from typing import Optional from datetime import date from enum import Enum from pydantic import BaseModel class JobType(Enum): FULL_TIME = ( "fulltime", "períodointegral", "estágio/trainee", "cunormăîntreagă", "tiempocompleto", "vollzeit", "voltijds", "tempointegral", "全职", "plnýúvazek", "fuldtid", "دوامكامل", "kokopäivätyö", "tempsplein", "vollzeit", "πλήρηςαπασχόληση", "teljesmunkaidő", "tempopieno", "tempsplein", "heltid", "jornadacompleta", "pełnyetat", "정규직", "100%", "全職", "งานประจำ", "tamzamanlı", "повназайнятість", "toànthờigian", ) PART_TIME = ("parttime", "teilzeit", "částečnýúvazek", "deltid") CONTRACT = ("contract", "contractor") TEMPORARY = ("temporary",) INTERNSHIP = ( "internship", "prácticas", "ojt(onthejobtraining)", "praktikum", "praktik", ) PER_DIEM = ("perdiem",) NIGHTS = ("nights",) OTHER = ("other",) SUMMER = ("summer",) VOLUNTEER = ("volunteer",) class Country(Enum): """ Gets the subdomain for Indeed and Glassdoor. The second item in the tuple is the subdomain for Indeed The third item in the tuple is the subdomain (and tld if there's a ':' separator) for Glassdoor """ ARGENTINA = ("argentina", "ar", "com.ar") AUSTRALIA = ("australia", "au", "com.au") AUSTRIA = ("austria", "at", "at") BAHRAIN = ("bahrain", "bh") BELGIUM = ("belgium", "be", "fr:be") BRAZIL = ("brazil", "br", "com.br") CANADA = ("canada", "ca", "ca") CHILE = ("chile", "cl") CHINA = ("china", "cn") COLOMBIA = ("colombia", "co") COSTARICA = ("costa rica", "cr") CZECHREPUBLIC = ("czech republic,czechia", "cz") DENMARK = ("denmark", "dk") ECUADOR = ("ecuador", "ec") EGYPT = ("egypt", "eg") FINLAND = ("finland", "fi") FRANCE = ("france", "fr", "fr") GERMANY = ("germany", "de", "de") GREECE = ("greece", "gr") HONGKONG = ("hong kong", "hk", "com.hk") HUNGARY = ("hungary", "hu") INDIA = ("india", "in", "co.in") INDONESIA = ("indonesia", "id") IRELAND = ("ireland", "ie", "ie") ISRAEL = ("israel", "il") ITALY = ("italy", "it", "it") JAPAN = ("japan", "jp") KUWAIT = ("kuwait", "kw") LUXEMBOURG = ("luxembourg", "lu") MALAYSIA = ("malaysia", "malaysia") MEXICO = ("mexico", "mx", "com.mx") MOROCCO = ("morocco", "ma") NETHERLANDS = ("netherlands", "nl", "nl") NEWZEALAND = ("new zealand", "nz", "co.nz") NIGERIA = ("nigeria", "ng") NORWAY = ("norway", "no") OMAN = ("oman", "om") PAKISTAN = ("pakistan", "pk") PANAMA = ("panama", "pa") PERU = ("peru", "pe") PHILIPPINES = ("philippines", "ph") POLAND = ("poland", "pl") PORTUGAL = ("portugal", "pt") QATAR = ("qatar", "qa") ROMANIA = ("romania", "ro") SAUDIARABIA = ("saudi arabia", "sa") SINGAPORE = ("singapore", "sg", "sg") SOUTHAFRICA = ("south africa", "za") SOUTHKOREA = ("south korea", "kr") SPAIN = ("spain", "es", "es") SWEDEN = ("sweden", "se") SWITZERLAND = ("switzerland", "ch", "de:ch") TAIWAN = ("taiwan", "tw") THAILAND = ("thailand", "th") TURKEY = ("turkey", "tr") UKRAINE = ("ukraine", "ua") UNITEDARABEMIRATES = ("united arab emirates", "ae") UK = ("uk,united kingdom", "uk", "co.uk") USA = ("usa,us,united states", "www", "com") URUGUAY = ("uruguay", "uy") VENEZUELA = ("venezuela", "ve") VIETNAM = ("vietnam", "vn") # internal for ziprecruiter US_CANADA = ("usa/ca", "www") # internal for linkedin WORLDWIDE = ("worldwide", "www") @property def indeed_domain_value(self): return self.value[1] @property def glassdoor_domain_value(self): if len(self.value) == 3: subdomain, _, domain = self.value[2].partition(":") if subdomain and domain: return f"{subdomain}.glassdoor.{domain}" else: return f"www.glassdoor.{self.value[2]}" else: raise Exception(f"Glassdoor is not available for {self.name}") def get_url(self): return f"https://{self.glassdoor_domain_value}/" @classmethod def from_string(cls, country_str: str): """Convert a string to the corresponding Country enum.""" country_str = country_str.strip().lower() for country in cls: country_names = country.value[0].split(',') if country_str in country_names: return country valid_countries = [country.value for country in cls] raise ValueError( f"Invalid country string: '{country_str}'. Valid countries are: {', '.join([country[0] for country in valid_countries])}" ) class Location(BaseModel): country: Country | None = None city: Optional[str] = None state: Optional[str] = None def display_location(self) -> str: location_parts = [] if self.city: location_parts.append(self.city) if self.state: location_parts.append(self.state) if self.country and self.country not in (Country.US_CANADA, Country.WORLDWIDE): country_name = self.country.value[0] if "," in country_name: country_name = country_name.split(",")[0] if country_name in ("usa", "uk"): location_parts.append(country_name.upper()) else: location_parts.append(country_name.title()) return ", ".join(location_parts) class CompensationInterval(Enum): YEARLY = "yearly" MONTHLY = "monthly" WEEKLY = "weekly" DAILY = "daily" HOURLY = "hourly" @classmethod def get_interval(cls, pay_period): return cls[pay_period].value if pay_period in cls.__members__ else None class Compensation(BaseModel): interval: Optional[CompensationInterval] = None min_amount: int | None = None max_amount: int | None = None currency: Optional[str] = "USD" class JobPost(BaseModel): title: str company_name: str job_url: str location: Optional[Location] description: str | None = None company_url: str | None = None job_type: list[JobType] | None = None compensation: Compensation | None = None date_posted: date | None = None benefits: str | None = None emails: list[str] | None = None num_urgent_words: int | None = None is_remote: bool | None = None # company_industry: str | None = None class JobResponse(BaseModel): jobs: list[JobPost] = []