text: import re
  
  from common import *
  
  
  def partTwo(instr: str) -> int:
      input_list = parse(instr)
  
      passports = [Passport(x) for x in input_list]
  
      valid_passports = 0
      for passport in passports:
          if passport.validate():
              valid_passports += 1
  
      return valid_passports
  
  
  class Passport:
      byr: str  # Birth year
      iyr: str  # Issue year
      eyr: str  # Expiration year
      hgt: str  # Height
      hcl: str  # Hair colour
      ecl: str  # Eye colour
      pid: str  # Passport ID
      cid: str  # Country ID
  
      def __init__(self, instr: str) -> None:
          self.byr = self._extract_field("byr", instr)
          self.iyr = self._extract_field("iyr", instr)
          self.eyr = self._extract_field("eyr", instr)
          self.hgt = self._extract_field("hgt", instr)
          self.hcl = self._extract_field("hcl", instr)
          self.ecl = self._extract_field("ecl", instr)
          self.pid = self._extract_field("pid", instr)
          self.cid = self._extract_field("cid", instr)
  
      def _extract_field(self, field: str, instr: str) -> str:
          matches = re.search(field + r":([^ ]+)", instr)
          if matches is None:
              return ""
  
          return matches.group(1)
  
      def validate(self) -> bool:
          # byr (Birth Year) - four digits; at least 1920 and at most 2002.
          if self.byr == "":
              return False
          if not (1920 <= int(self.byr) <= 2002):
              return False
  
          # iyr (Issue Year) - four digits; at least 2010 and at most 2020.
          if self.iyr == "":
              return False
          if not (2010 <= int(self.iyr) <= 2020):
              return False
  
          # eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
          if self.eyr == "":
              return False
          if not (2020 <= int(self.eyr) <= 2030):
              return False
  
          # hgt (Height) - a number followed by either cm or in:
          #     If cm, the number must be at least 150 and at most 193.
          #     If in, the number must be at least 59 and at most 76.
          if self.hgt == "":
              return False
          if "cm" in self.hgt:
              if not (150 <= int(self.hgt.strip("cm")) <= 193):
                  return False
          elif "in" in self.hgt:
              if not (59 <= int(self.hgt.strip("in")) <= 76):
                  return False
          else:
              return False
  
          # hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
          if self.hcl == "":
              return False
          if self.hcl[0] == "#":
              if re.match(r"#[0-9a-f]{6}", self.hcl) is None:
                  return False
          else:
              return False
  
          # ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
          if self.ecl == "":
              return False
          if self.ecl not in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]:
              return False
  
          # pid (Passport ID) - a nine-digit number, including leading zeroes.
          if len(self.pid) == 9:
              try:
                  int(self.pid)
              except ValueError:
                  return False
          else:
              return False
  
          return True
  