Source code for pyUSPTO.models.patent_data

"""
models.patent_data - Data models for USPTO patent data API

This module provides Pydantic-style data models, primarily using frozen
dataclasses, for representing responses from the USPTO Patent Data API.
It aims to offer more Pythonic representations (e.g., Enums, native
date/datetime objects) of the API's JSON data. Models cover aspects like
application metadata, party information (applicants, inventors, attorneys),
document details, continuity, assignments, and more.
"""

import csv
import io
import json
import warnings
from dataclasses import asdict, dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import Any, Dict, Iterator, List, Optional, Union

# Import utility functions from models.utils module
from pyUSPTO.models.utils import (
    ASSUMED_NAIVE_TIMEZONE,
    ASSUMED_NAIVE_TIMEZONE_STR,
    parse_to_date,
    parse_to_datetime_utc,
    parse_yn_to_bool,
    serialize_bool_to_yn,
    serialize_date,
    serialize_datetime_as_iso,
    serialize_datetime_as_naive,
    to_camel_case,
)
from pyUSPTO.warnings import USPTOEnumParseWarning


# --- Enums for Categorical Data ---
[docs] class DirectionCategory(Enum): """Represents the direction of a document relative to the USPTO (e.g., INCOMING, OUTGOING).""" INCOMING = "INCOMING" OUTGOING = "OUTGOING"
[docs] class ActiveIndicator(Enum): """Represents an active or inactive status, often used for practitioners or entities. This Enum is designed to flexibly parse common string representations of active/inactive or true/false states (e.g., "Y", "N", "true", "false", "Active") into standardized Enum members. """ YES = "Y" NO = "N" TRUE = "true" FALSE = "false" ACTIVE = "Active" @classmethod def _missing_(cls, value: Any) -> "ActiveIndicator": if isinstance(value, str): val_upper = value.upper() if val_upper == "Y": return cls.YES if val_upper == "N": return cls.NO if val_upper == "TRUE": return cls.TRUE if val_upper == "FALSE": return cls.FALSE if val_upper == "ACTIVE": return cls.ACTIVE return super()._missing_(value=value) # type: ignore[no-any-return]
# --- Data Models ---
[docs] @dataclass(frozen=True) class DocumentFormat: """Represents an available download format for a specific document. Attributes: mime_type_identifier: The MIME type of the downloadable file (e.g., "PDF"). download_url: The URL from which the document format can be downloaded. page_total_quantity: The total number of pages in this document format. """ mime_type_identifier: Optional[str] = None download_url: Optional[str] = None page_total_quantity: Optional[int] = None def __str__(self) -> str: return ( f"{self.mime_type_identifier} format with {self.page_total_quantity} pages" ) def __repr__(self) -> str: return f"DocumentFormat(mime_type={self.mime_type_identifier}, pages={self.page_total_quantity})"
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "DocumentFormat": """Creates a `DocumentFormat` instance from a dictionary representation. This factory method is typically used to construct `DocumentFormat` objects from data parsed from an API JSON response. It maps dictionary keys (expected in camelCase) to the class attributes. Args: data (Dict[str, Any]): A dictionary containing the data for a `DocumentFormat`. Expected keys from the API are "mimeTypeIdentifier", "downloadUrl", and "pageTotalQuantity". Returns: DocumentFormat: An instance of `DocumentFormat` initialized with data from the input dictionary. """ return cls( mime_type_identifier=data.get("mimeTypeIdentifier"), download_url=data.get("downloadUrl"), page_total_quantity=data.get("pageTotalQuantity"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `DocumentFormat` instance to a dictionary. This method serializes the `DocumentFormat` object into a dictionary, mapping the instance's attributes to camelCase keys. This is typically useful for generating JSON representations compatible with API expectations. Returns: Dict[str, Any]: A dictionary representation of the `DocumentFormat` instance with keys "mimeTypeIdentifier", "downloadUrl", and "pageTotalQuantity". """ return { "mimeTypeIdentifier": self.mime_type_identifier, "downloadUrl": self.download_url, "pageTotalQuantity": self.page_total_quantity, }
[docs] @dataclass(frozen=True) class Document: """Represents a single document associated with a patent application. This includes metadata such as its identifier, official date, code, description, direction (incoming/outgoing), and available download formats. Attributes: application_number_text: The application number this document belongs to. official_date: The official date of the document. document_identifier: A unique identifier for this document. document_code: A code representing the type of document. document_code_description_text: A textual description of the document code. direction_category: The direction of the document (e.g., INCOMING, OUTGOING). document_formats: A list of available download formats for this document. """ application_number_text: Optional[str] = None official_date: Optional[datetime] = None document_identifier: Optional[str] = None document_code: Optional[str] = None document_code_description_text: Optional[str] = None direction_category: Optional[DirectionCategory] = None document_formats: List[DocumentFormat] = field(default_factory=list) def __str__(self) -> str: date_str = ( self.official_date.strftime("%Y-%m-%d") if self.official_date else "No date" ) return f"Document {self.document_identifier} ({self.document_code}): {self.document_code_description_text} - {date_str}" def __repr__(self) -> str: return f"Document(id={self.document_identifier}, code={self.document_code}, date={self.official_date.strftime('%Y-%m-%d') if self.official_date else 'None'})"
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Document": """Creates a `Document` instance from a dictionary representation. Maps API JSON keys (camelCase) to class attributes, parsing nested objects like `DocumentFormat` and `DirectionCategory`. Args: data (Dict[str, Any]): A dictionary containing document data, typically from an API response. Returns: Document: An instance of `Document`. """ dl_formats = [ DocumentFormat.from_dict(f) for f in data.get("downloadOptionBag", []) if isinstance(f, dict) ] dir_val = data.get("documentDirectionCategory") dir_cat = None if dir_val: try: dir_cat = DirectionCategory(dir_val) except ValueError: warnings.warn( f"Unknown document direction category '{dir_val}'", category=USPTOEnumParseWarning, stacklevel=2, ) return cls( application_number_text=data.get("applicationNumberText"), official_date=parse_to_datetime_utc(data.get("officialDate")), document_identifier=data.get("documentIdentifier"), document_code=data.get("documentCode"), document_code_description_text=data.get("documentCodeDescriptionText"), direction_category=dir_cat, document_formats=dl_formats, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Document` instance to a dictionary for API compatibility. Serializes attributes to camelCase keys and handles nested objects. Omits keys with None values or empty lists. Returns: Dict[str, Any]: A dictionary representation of the `Document`. """ d = { "applicationNumberText": self.application_number_text, "officialDate": ( serialize_datetime_as_iso(self.official_date) if self.official_date else None ), "documentIdentifier": self.document_identifier, "documentCode": self.document_code, "documentCodeDescriptionText": self.document_code_description_text, "documentDirectionCategory": ( self.direction_category.value if self.direction_category else None ), "downloadOptionBag": [df.to_dict() for df in self.document_formats], } return { k: v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] class DocumentBag: """A collection of Document objects associated with a patent application. Provides iterable access and standard collection methods like `len` and `getitem`. This class is immutable by convention after initialization. Attributes: documents (tuple[Document, ...]): An immutable tuple of `Document` objects. """
[docs] def __init__(self, documents: List[Document]): """Initializes the DocumentBag with a list of documents. Args: documents (List[Document]): A list of `Document` instances. """ self._documents = tuple(documents)
@property def documents(self) -> tuple[Document, ...]: """Provides access to the tuple of documents.""" return self._documents def __iter__(self) -> Iterator[Document]: return iter(self._documents) def __len__(self) -> int: return len(self._documents) def __getitem__(self, index: int) -> Document: return self._documents[index]
[docs] def __str__(self) -> str: """Returns a string representation showing document count and summary. Returns: str: Human-readable summary of the DocumentBag. """ count = len(self._documents) if count == 0: return "DocumentBag(0 documents)" # Count unique document codes doc_codes: Dict[str, int] = {} for doc in self._documents: code = doc.document_code or "Unknown" doc_codes[code] = doc_codes.get(code, 0) + 1 # Format summary if count == 1: code = self._documents[0].document_code or "Unknown" return f"DocumentBag(1 document: {code})" # Show top 3 most common document codes sorted_codes = sorted(doc_codes.items(), key=lambda x: x[1], reverse=True) top_codes = sorted_codes[:3] code_summary = ", ".join(f"{code} ({cnt})" for code, cnt in top_codes) if len(sorted_codes) > 3: remaining = len(sorted_codes) - 3 return f"DocumentBag({count} documents: {code_summary}, +{remaining} more types)" else: return f"DocumentBag({count} documents: {code_summary})"
[docs] def __repr__(self) -> str: """Returns a detailed string representation for debugging. Returns: str: Detailed representation of the DocumentBag. """ return f"DocumentBag(documents={self._documents!r})"
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "DocumentBag": """Creates a `DocumentBag` instance from a dictionary representation. Expects a dictionary with a "documentBag" key containing a list of document data dictionaries. Args: data (Dict[str, Any]): A dictionary, typically from an API response, containing the document bag. Returns: DocumentBag: An instance of `DocumentBag`. """ docs_data = data.get("documentBag", []) docs = ( [Document.from_dict(dd) for dd in docs_data if isinstance(dd, dict)] if isinstance(docs_data, list) else [] ) return cls(documents=docs)
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `DocumentBag` instance to a dictionary. Serializes the collection into a dictionary with a "documentBag" key, containing a list of `Document` dictionaries. Returns: Dict[str, Any]: A dictionary representation of the `DocumentBag`. """ return {"documentBag": [doc.to_dict() for doc in self._documents]}
[docs] @dataclass(frozen=True) class Address: """Represents a postal address with fields for street, city, region, country, and postal code. It can be used for various entities like applicants, inventors, or correspondence. Attributes: name_line_one_text: First line of the name (e.g., company name). name_line_two_text: Second line of the name. address_line_one_text: First line of the street address. address_line_two_text: Second line of the street address. address_line_three_text: Third line of the street address. address_line_four_text: Fourth line of the street address. geographic_region_name: Name of the geographic region (e.g., state, province). geographic_region_code: Code for the geographic region. postal_code: Postal or ZIP code. city_name: Name of the city. country_code: Two-letter country code (e.g., "US"). country_name: Full name of the country (e.g., "United States"). postal_address_category: Category of the address (e.g., "MAILING_ADDRESS"). correspondent_name_text: Name of the correspondent at this address. country_or_state_code: Country or state code. ict_state_code: International code for the state/region (USPTO format). ict_country_code: International code for the country (USPTO format). """ name_line_one_text: Optional[str] = None name_line_two_text: Optional[str] = None address_line_one_text: Optional[str] = None address_line_two_text: Optional[str] = None address_line_three_text: Optional[str] = None address_line_four_text: Optional[str] = None geographic_region_name: Optional[str] = None geographic_region_code: Optional[str] = None postal_code: Optional[str] = None city_name: Optional[str] = None country_code: Optional[str] = None country_name: Optional[str] = None postal_address_category: Optional[str] = None correspondent_name_text: Optional[str] = None country_or_state_code: Optional[str] = None ict_state_code: Optional[str] = None ict_country_code: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Address": """Creates an `Address` instance from a dictionary representation. Maps camelCase keys from API data to class attributes. Args: data (Dict[str, Any]): Dictionary containing address data. Returns: Address: An instance of `Address`. """ return cls( name_line_one_text=data.get("nameLineOneText"), name_line_two_text=data.get("nameLineTwoText"), address_line_one_text=data.get("addressLineOneText"), address_line_two_text=data.get("addressLineTwoText"), address_line_three_text=data.get("addressLineThreeText"), address_line_four_text=data.get("addressLineFourText"), geographic_region_name=data.get("geographicRegionName"), geographic_region_code=data.get("geographicRegionCode"), postal_code=data.get("postalCode"), city_name=data.get("cityName"), country_code=data.get("countryCode"), country_name=data.get("countryName"), postal_address_category=data.get("postalAddressCategory"), correspondent_name_text=data.get("correspondentNameText"), country_or_state_code=data.get("countryOrStateCode"), ict_state_code=data.get("ictStateCode"), ict_country_code=data.get("ictCountryCode"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Address` instance to a dictionary with camelCase keys. Returns: Dict[str, Any]: A dictionary representation of the address. """ _dict = { "nameLineOneText": self.name_line_one_text, "nameLineTwoText": self.name_line_two_text, "addressLineOneText": self.address_line_one_text, "addressLineTwoText": self.address_line_two_text, "addressLineThreeText": self.address_line_three_text, "addressLineFourText": self.address_line_four_text, "geographicRegionName": self.geographic_region_name, "geographicRegionCode": self.geographic_region_code, "postalCode": self.postal_code, "cityName": self.city_name, "countryCode": self.country_code, "countryName": self.country_name, "postalAddressCategory": self.postal_address_category, "correspondentNameText": self.correspondent_name_text, "countryOrStateCode": self.country_or_state_code, "ictStateCode": self.ict_state_code, "ictCountryCode": self.ict_country_code, } # Filter out None values to match API behavior return {k: v for k, v in _dict.items() if v is not None}
[docs] @dataclass(frozen=True) class Telecommunication: """Represents telecommunication details, such as phone or fax numbers. Attributes: telecommunication_number: The main number (e.g., phone number). extension_number: Any extension associated with the number. telecom_type_code: A code indicating the type of telecommunication (e.g., "TEL", "FAX"). """ telecommunication_number: Optional[str] = None extension_number: Optional[str] = None telecom_type_code: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Telecommunication": """Creates a `Telecommunication` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with telecommunication data. Returns: Telecommunication: An instance of `Telecommunication`. """ return cls( telecommunication_number=data.get("telecommunicationNumber"), extension_number=data.get("extensionNumber"), telecom_type_code=data.get("telecomTypeCode"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Telecommunication` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ _dict = { "telecommunicationNumber": self.telecommunication_number, "extensionNumber": self.extension_number, "telecomTypeCode": self.telecom_type_code, } # Filter out None values to match API behavior return {k: v for k, v in _dict.items() if v is not None}
[docs] @dataclass(frozen=True) class Person: """A base data class representing a person with common name and country attributes. This class is typically inherited by more specific types like Applicant, Inventor, or Attorney. Attributes: first_name: The first name of the person. middle_name: The middle name or initial of the person. last_name: The last name or surname of the person. name_prefix: A prefix for the name (e.g., "Dr.", "Mr."). name_suffix: A suffix for the name (e.g., "Jr.", "PhD"). preferred_name: The person's preferred name, if different. country_code: The country code associated with the person (e.g., citizenship). """ first_name: Optional[str] = None middle_name: Optional[str] = None last_name: Optional[str] = None name_prefix: Optional[str] = None name_suffix: Optional[str] = None preferred_name: Optional[str] = None country_code: Optional[str] = None @classmethod def _extract_person_fields(cls, data: Dict[str, Any]) -> Dict[str, Any]: return { "first_name": data.get("firstName"), "middle_name": data.get("middleName"), "last_name": data.get("lastName"), "name_prefix": data.get("namePrefix"), "name_suffix": data.get("nameSuffix"), "preferred_name": data.get("preferredName"), "country_code": data.get("countryCode"), }
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Person` instance to a dictionary with camelCase keys. Omits attributes that are None. Returns: Dict[str, Any]: A dictionary representation of the person. """ return {to_camel_case(k): v for k, v in asdict(self).items() if v is not None}
[docs] @dataclass(frozen=True) class Applicant(Person): """Represents an applicant for a patent, inheriting from Person. Includes applicant-specific name text and a list of correspondence addresses. Attributes: applicant_name_text: The full name of the applicant as a single string. correspondence_address_bag: A list of `Address` objects for the applicant. """ applicant_name_text: Optional[str] = None correspondence_address_bag: List[Address] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Applicant": """Creates an `Applicant` instance from a dictionary. Inherits person fields and adds applicant-specific fields. Args: data (Dict[str, Any]): Dictionary with applicant data. Returns: Applicant: An instance of `Applicant`. """ pf = Person._extract_person_fields(data) addrs = [ Address.from_dict(a) for a in data.get("correspondenceAddressBag", []) if isinstance(a, dict) ] return cls( **pf, applicant_name_text=data.get("applicantNameText"), correspondence_address_bag=addrs, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Applicant` instance to a dictionary. Includes inherited person fields and applicant-specific fields, using camelCase keys and omitting None values or empty lists. Returns: Dict[str, Any]: Dictionary representation of the applicant. """ d = super().to_dict() d.update( { "applicantNameText": self.applicant_name_text, "correspondenceAddressBag": [ a.to_dict() for a in self.correspondence_address_bag ], } ) return { k: v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class Inventor(Person): """Represents an inventor for a patent application, inheriting from Person. Includes inventor-specific name text and a list of correspondence addresses. Attributes: inventor_name_text: The full name of the inventor as a single string. correspondence_address_bag: A list of `Address` objects for the inventor. """ inventor_name_text: Optional[str] = None correspondence_address_bag: List[Address] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Inventor": """Creates an `Inventor` instance from a dictionary. Inherits person fields and adds inventor-specific fields. Args: data (Dict[str, Any]): Dictionary with inventor data. Returns: Inventor: An instance of `Inventor`. """ pf = Person._extract_person_fields(data) addrs = [ Address.from_dict(a) for a in data.get("correspondenceAddressBag", []) if isinstance(a, dict) ] return cls( **pf, inventor_name_text=data.get("inventorNameText"), correspondence_address_bag=addrs, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Inventor` instance to a dictionary. Includes inherited person fields and inventor-specific fields, using camelCase keys and omitting None values or empty lists. Returns: Dict[str, Any]: Dictionary representation of the inventor. """ d = super().to_dict() d.update( { "inventorNameText": self.inventor_name_text, "correspondenceAddressBag": [ a.to_dict() for a in self.correspondence_address_bag ], } ) return { k: v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class Attorney(Person): """Represents an attorney or agent associated with a patent application, inheriting from Person. Includes registration number, active status, practitioner category, addresses, and telecommunication details. Attributes: registration_number: The attorney's USPTO registration number. active_indicator: Indicates if the attorney is currently active (e.g., "Y", "N"). registered_practitioner_category: Category of the practitioner (e.g., "ATTORNEY", "AGENT"). attorney_address_bag: List of `Address` objects for the attorney. telecommunication_address_bag: List of `Telecommunication` objects for the attorney. """ registration_number: Optional[str] = None active_indicator: Optional[str] = None registered_practitioner_category: Optional[str] = None attorney_address_bag: List[Address] = field(default_factory=list) telecommunication_address_bag: List[Telecommunication] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Attorney": """Creates an `Attorney` instance from a dictionary. Inherits person fields and adds attorney-specific details. Args: data (Dict[str, Any]): Dictionary with attorney data. Returns: Attorney: An instance of `Attorney`. """ pf = Person._extract_person_fields(data) addrs = [ Address.from_dict(a) for a in data.get("attorneyAddressBag", []) if isinstance(a, dict) ] telecoms = [ Telecommunication.from_dict(t) for t in data.get("telecommunicationAddressBag", []) if isinstance(t, dict) ] return cls( **pf, registration_number=data.get("registrationNumber"), active_indicator=data.get("activeIndicator"), registered_practitioner_category=data.get("registeredPractitionerCategory"), attorney_address_bag=addrs, telecommunication_address_bag=telecoms, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Attorney` instance to a dictionary. Includes inherited person fields and attorney-specific fields, using camelCase keys and omitting None values or empty lists. Returns: Dict[str, Any]: Dictionary representation of the attorney. """ d = super().to_dict() d.update( { "registrationNumber": self.registration_number, "activeIndicator": self.active_indicator, "registeredPractitionerCategory": self.registered_practitioner_category, "attorneyAddressBag": [a.to_dict() for a in self.attorney_address_bag], "telecommunicationAddressBag": [ t.to_dict() for t in self.telecommunication_address_bag ], } ) return { k: v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class EntityStatus: """Represents the entity status of an applicant (e.g., small entity status). Attributes: small_entity_status_indicator: Boolean indicating if the applicant qualifies for small entity status. business_entity_status_category: String category of the business entity status (e.g., "Undiscounted"). """ small_entity_status_indicator: Optional[bool] = None business_entity_status_category: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "EntityStatus": """Creates an `EntityStatus` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with entity status data. Returns: EntityStatus: An instance of `EntityStatus`. """ return cls( small_entity_status_indicator=data.get("smallEntityStatusIndicator"), business_entity_status_category=data.get("businessEntityStatusCategory"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `EntityStatus` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ return { "smallEntityStatusIndicator": self.small_entity_status_indicator, "businessEntityStatusCategory": self.business_entity_status_category, }
[docs] @dataclass(frozen=True) class CustomerNumberCorrespondence: """Represents correspondence data associated with a USPTO customer number. Includes patron identifier, organization name, power of attorney addresses, and telecommunication details. Attributes: patron_identifier: The USPTO customer number. organization_standard_name: The name of the organization associated with the customer number. power_of_attorney_address_bag: List of `Address` objects for power of attorney. telecommunication_address_bag: List of `Telecommunication` objects. """ patron_identifier: Optional[int] = None organization_standard_name: Optional[str] = None power_of_attorney_address_bag: List[Address] = field(default_factory=list) telecommunication_address_bag: List[Telecommunication] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "CustomerNumberCorrespondence": """Creates a `CustomerNumberCorrespondence` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with customer number correspondence data. Returns: CustomerNumberCorrespondence: An instance of `CustomerNumberCorrespondence`. """ addrs = [ Address.from_dict(a) for a in data.get("powerOfAttorneyAddressBag", []) if isinstance(a, dict) ] telecoms = [ Telecommunication.from_dict(t) for t in data.get("telecommunicationAddressBag", []) if isinstance(t, dict) ] return cls( patron_identifier=data.get("patronIdentifier"), organization_standard_name=data.get("organizationStandardName"), power_of_attorney_address_bag=addrs, telecommunication_address_bag=telecoms, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `CustomerNumberCorrespondence` instance to a dictionary. Omits keys with None values or empty lists. Returns: Dict[str, Any]: Dictionary representation. """ d = { "patronIdentifier": self.patron_identifier, "organizationStandardName": self.organization_standard_name, "powerOfAttorneyAddressBag": [ a.to_dict() for a in self.power_of_attorney_address_bag ], "telecommunicationAddressBag": [ t.to_dict() for t in self.telecommunication_address_bag ], } return { k: v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class RecordAttorney: """Represents information about the attorney(s) of record for a patent application. Contains customer number correspondence data, power of attorney information, and listed attorneys. Attributes: customer_number_correspondence_data: `CustomerNumberCorrespondence` object with customer number details. power_of_attorney_bag: List of `Attorney` objects named in a power of attorney. attorney_bag: List of `Attorney` objects listed as attorneys of record. """ customer_number_correspondence_data: Optional[CustomerNumberCorrespondence] = None power_of_attorney_bag: List[Attorney] = field(default_factory=list) attorney_bag: List[Attorney] = field(default_factory=list)
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "RecordAttorney": """Creates a `RecordAttorney` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with record attorney data. Returns: RecordAttorney: An instance of `RecordAttorney`. """ cust_corr_data = data.get("customerNumberCorrespondenceData") cust_corr = ( CustomerNumberCorrespondence.from_dict(cust_corr_data) if isinstance(cust_corr_data, dict) else None ) poa_bag = [ Attorney.from_dict(a) for a in data.get("powerOfAttorneyBag", []) if isinstance(a, dict) ] att_bag = [ Attorney.from_dict(a) for a in data.get("attorneyBag", []) if isinstance(a, dict) ] return cls( customer_number_correspondence_data=cust_corr, power_of_attorney_bag=poa_bag, attorney_bag=att_bag, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `RecordAttorney` instance to a dictionary. Omits keys with None values. Includes empty lists to match API behavior. Returns: Dict[str, Any]: Dictionary representation. """ d = { "customerNumberCorrespondenceData": ( self.customer_number_correspondence_data.to_dict() if self.customer_number_correspondence_data else None ), "powerOfAttorneyBag": [p.to_dict() for p in self.power_of_attorney_bag], "attorneyBag": [a.to_dict() for a in self.attorney_bag], } return {k: v for k, v in d.items() if v is not None}
[docs] @dataclass(frozen=True) class Assignor: """Represents an assignor in a patent assignment. Attributes: assignor_name: The name of the assigning party. execution_date: The date the assignment was executed. """ assignor_name: Optional[str] = None execution_date: Optional[date] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Assignor": """Creates an `Assignor` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with assignor data. Returns: Assignor: An instance of `Assignor`. """ return cls( assignor_name=data.get("assignorName"), execution_date=parse_to_date(data.get("executionDate")), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Assignor` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ return { "assignorName": self.assignor_name, "executionDate": serialize_date(self.execution_date), }
[docs] @dataclass(frozen=True) class Assignee: """Represents an assignee in a patent assignment. Attributes: assignee_name_text: The name of the party receiving the assignment. assignee_address: The `Address` of the assignee. """ assignee_name_text: Optional[str] = None assignee_address: Optional[Address] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Assignee": """Creates an `Assignee` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with assignee data. Returns: Assignee: An instance of `Assignee`. """ addr_data = data.get("assigneeAddress") addr = Address.from_dict(addr_data) if isinstance(addr_data, dict) else None return cls( assignee_name_text=data.get("assigneeNameText"), assignee_address=addr )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Assignee` instance to a dictionary. Omits keys with None values. Returns: Dict[str, Any]: Dictionary representation. """ d = { "assigneeNameText": self.assignee_name_text, "assigneeAddress": ( self.assignee_address.to_dict() if self.assignee_address else None ), } return {k: v for k, v in d.items() if v is not None}
[docs] @dataclass(frozen=True) class Assignment: """Represents a patent assignment, detailing the transfer of rights. Includes information about the reel and frame, document location, dates, conveyance text, and bags of assignors, assignees, correspondence address, and domestic representative. Attributes: reel_number: Reel number for the assignment record. frame_number: Frame number for the assignment record. reel_and_frame_number: Combined reel and frame number. page_total_quantity: Total number of pages in the assignment document. assignment_document_location_uri: URI for the assignment document. assignment_received_date: Date the assignment was received by USPTO. assignment_recorded_date: Date the assignment was recorded by USPTO. assignment_mailed_date: Date the assignment notification was mailed. conveyance_text: Text describing the nature of the conveyance. image_available_status_code: Code to indicate the availability of the image. attorney_docket_number: Attorney docket number for the assignment. assignor_bag: List of `Assignor` objects. assignee_bag: List of `Assignee` objects. correspondence_address: `Address` object for correspondence (single object). domestic_representative: `Address` object for the domestic representative. """ reel_number: Optional[int] = None frame_number: Optional[int] = None reel_and_frame_number: Optional[str] = None page_total_quantity: Optional[int] = None assignment_document_location_uri: Optional[str] = None assignment_received_date: Optional[date] = None assignment_recorded_date: Optional[date] = None assignment_mailed_date: Optional[date] = None conveyance_text: Optional[str] = None image_available_status_code: Optional[bool] = None attorney_docket_number: Optional[str] = None assignor_bag: List[Assignor] = field(default_factory=list) assignee_bag: List[Assignee] = field(default_factory=list) correspondence_address: Optional[Address] = None domestic_representative: Optional[Address] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Assignment": """Creates an `Assignment` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with assignment data. Returns: Assignment: An instance of `Assignment`. """ assignors = [ Assignor.from_dict(a) for a in data.get("assignorBag", []) if isinstance(a, dict) ] assignees = [ Assignee.from_dict(a) for a in data.get("assigneeBag", []) if isinstance(a, dict) ] # Parse correspondence address (single object, not bag) corr_addr_data = data.get("correspondenceAddress") corr_addr = ( Address.from_dict(corr_addr_data) if isinstance(corr_addr_data, dict) else None ) # Parse domestic representative dom_rep_data = data.get("domesticRepresentative") dom_rep = ( Address.from_dict(dom_rep_data) if isinstance(dom_rep_data, dict) else None ) return cls( reel_number=data.get("reelNumber"), frame_number=data.get("frameNumber"), reel_and_frame_number=data.get("reelAndFrameNumber"), page_total_quantity=data.get("pageTotalQuantity"), assignment_document_location_uri=data.get("assignmentDocumentLocationURI"), assignment_received_date=parse_to_date(data.get("assignmentReceivedDate")), assignment_recorded_date=parse_to_date(data.get("assignmentRecordedDate")), assignment_mailed_date=parse_to_date(data.get("assignmentMailedDate")), conveyance_text=data.get("conveyanceText"), image_available_status_code=data.get("imageAvailableStatusCode"), attorney_docket_number=data.get("attorneyDocketNumber"), assignor_bag=assignors, assignee_bag=assignees, correspondence_address=corr_addr, domestic_representative=dom_rep, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Assignment` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ _dict = { "reelNumber": self.reel_number, "frameNumber": self.frame_number, "reelAndFrameNumber": self.reel_and_frame_number, "pageTotalQuantity": self.page_total_quantity, "assignmentDocumentLocationURI": self.assignment_document_location_uri, "assignmentReceivedDate": serialize_date(self.assignment_received_date), "assignmentRecordedDate": serialize_date(self.assignment_recorded_date), "assignmentMailedDate": serialize_date(self.assignment_mailed_date), "conveyanceText": self.conveyance_text, "imageAvailableStatusCode": self.image_available_status_code, "attorneyDocketNumber": self.attorney_docket_number, "assignorBag": [a.to_dict() for a in self.assignor_bag], "assigneeBag": [a.to_dict() for a in self.assignee_bag], "correspondenceAddress": ( self.correspondence_address.to_dict() if self.correspondence_address else None ), "domesticRepresentative": ( self.domestic_representative.to_dict() if self.domestic_representative else None ), } # Filter out None values to match API behavior return {k: v for k, v in _dict.items() if v is not None}
[docs] @dataclass(frozen=True) class ForeignPriority: """Represents a foreign priority claim for a patent application. Attributes: ip_office_name: The name of the intellectual property office of the priority application. filing_date: The filing date of the priority application. application_number_text: The application number of the priority application. """ ip_office_name: Optional[str] = None filing_date: Optional[date] = None application_number_text: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ForeignPriority": """Creates a `ForeignPriority` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with foreign priority data. Returns: ForeignPriority: An instance of `ForeignPriority`. """ return cls( ip_office_name=data.get("ipOfficeName"), filing_date=parse_to_date(data.get("filingDate")), application_number_text=data.get("applicationNumberText"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `ForeignPriority` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ return { "ipOfficeName": self.ip_office_name, "filingDate": serialize_date(self.filing_date), "applicationNumberText": self.application_number_text, }
[docs] @dataclass(frozen=True) class Continuity: """Base class representing continuity data for a patent application. This includes details about the application's relationship to other applications (parent/child), its filing status under AIA (America Invents Act), and key identifiers. Attributes: first_inventor_to_file_indicator: Boolean indicating if the application is under First-Inventor-to-File provisions. application_number_text: The application number of the related (parent or child) application. filing_date: The filing date of the related application. status_code: The status code of the related application. status_description_text: The status description of the related application. patent_number: The patent number if the related application is granted. claim_parentage_type_code: Code indicating the type of continuity claim (e.g., "CON", "DIV"). claim_parentage_type_code_description_text: Description of the continuity claim type. """ first_inventor_to_file_indicator: Optional[bool] = None application_number_text: Optional[str] = None filing_date: Optional[date] = None status_code: Optional[int] = None status_description_text: Optional[str] = None patent_number: Optional[str] = None claim_parentage_type_code: Optional[str] = None claim_parentage_type_code_description_text: Optional[str] = None @property def is_aia(self) -> Optional[bool]: """Returns True if the application is AIA, False if pre-AIA, None if unknown.""" return self.first_inventor_to_file_indicator @property def is_pre_aia(self) -> Optional[bool]: """Returns True if the application is pre-AIA, False if AIA, None if unknown.""" if self.first_inventor_to_file_indicator is None: return None return not self.first_inventor_to_file_indicator
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `Continuity` instance to a dictionary. Omits attributes that are None and property-derived fields. Keys are converted to camelCase. Returns: Dict[str, Any]: A dictionary representation of the continuity data. """ return { to_camel_case(k): v for k, v in asdict(self).items() if v is not None and not k.startswith("is_") }
[docs] @dataclass(frozen=True) class ParentContinuity(Continuity): """Represents a parent application in a patent application's continuity chain. Inherits from Continuity and adds specific fields for parent application details. Attributes: parent_application_status_code: Status code of the parent application. parent_patent_number: Patent number of the parent application, if granted. parent_application_status_description_text: Status description of the parent application. parent_application_filing_date: Filing date of the parent application. parent_application_number_text: Application number of the parent application. child_application_number_text: Application number of the child (current) application. """ parent_application_status_code: Optional[int] = None parent_patent_number: Optional[str] = None parent_application_status_description_text: Optional[str] = None parent_application_filing_date: Optional[date] = None parent_application_number_text: Optional[str] = None child_application_number_text: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ParentContinuity": """Creates a `ParentContinuity` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with parent continuity data. Returns: ParentContinuity: An instance of `ParentContinuity`. """ p_filing_date = parse_to_date(data.get("parentApplicationFilingDate")) return cls( first_inventor_to_file_indicator=data.get("firstInventorToFileIndicator"), parent_application_status_code=data.get("parentApplicationStatusCode"), parent_patent_number=data.get("parentPatentNumber"), parent_application_status_description_text=data.get( "parentApplicationStatusDescriptionText" ), parent_application_filing_date=p_filing_date, parent_application_number_text=data.get("parentApplicationNumberText"), child_application_number_text=data.get("childApplicationNumberText"), claim_parentage_type_code=data.get("claimParentageTypeCode"), claim_parentage_type_code_description_text=data.get( "claimParentageTypeCodeDescriptionText" ), application_number_text=data.get("parentApplicationNumberText"), filing_date=p_filing_date, status_code=data.get("parentApplicationStatusCode"), status_description_text=data.get("parentApplicationStatusDescriptionText"), patent_number=data.get("parentPatentNumber"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `ParentContinuity` instance to a dictionary. Maps attributes to specific camelCase keys expected by the API for parent continuity. Filters out None values to match the API response structure. Returns: Dict[str, Any]: Dictionary representation. """ _dict = { "firstInventorToFileIndicator": self.first_inventor_to_file_indicator, "parentApplicationStatusCode": self.parent_application_status_code, "parentPatentNumber": self.parent_patent_number, "parentApplicationStatusDescriptionText": self.parent_application_status_description_text, "parentApplicationFilingDate": serialize_date( self.parent_application_filing_date ), "parentApplicationNumberText": self.parent_application_number_text, "childApplicationNumberText": self.child_application_number_text, "claimParentageTypeCode": self.claim_parentage_type_code, "claimParentageTypeCodeDescriptionText": self.claim_parentage_type_code_description_text, } return {k: v for k, v in _dict.items() if v is not None}
[docs] @dataclass(frozen=True) class ChildContinuity(Continuity): """Represents a child application in a patent application's continuity chain. Inherits from Continuity and adds specific fields for child application details. Attributes: child_application_status_code: Status code of the child application. parent_application_number_text: Application number of the parent (current) application. child_application_number_text: Application number of the child application. child_application_status_description_text: Status description of the child application. child_application_filing_date: Filing date of the child application. child_patent_number: Patent number of the child application, if granted. """ child_application_status_code: Optional[int] = None parent_application_number_text: Optional[str] = None child_application_number_text: Optional[str] = None child_application_status_description_text: Optional[str] = None child_application_filing_date: Optional[date] = None child_patent_number: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ChildContinuity": """Creates a `ChildContinuity` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with child continuity data. Returns: ChildContinuity: An instance of `ChildContinuity`. """ c_filing_date = parse_to_date(data.get("childApplicationFilingDate")) return cls( first_inventor_to_file_indicator=data.get("firstInventorToFileIndicator"), child_application_status_code=data.get("childApplicationStatusCode"), parent_application_number_text=data.get("parentApplicationNumberText"), child_application_number_text=data.get("childApplicationNumberText"), child_application_status_description_text=data.get( "childApplicationStatusDescriptionText" ), child_application_filing_date=c_filing_date, child_patent_number=data.get("childPatentNumber"), claim_parentage_type_code=data.get("claimParentageTypeCode"), claim_parentage_type_code_description_text=data.get( "claimParentageTypeCodeDescriptionText" ), application_number_text=data.get("childApplicationNumberText"), filing_date=c_filing_date, status_code=data.get("childApplicationStatusCode"), status_description_text=data.get("childApplicationStatusDescriptionText"), patent_number=data.get("childPatentNumber"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `ChildContinuity` instance to a dictionary. Maps attributes to specific camelCase keys expected by the API for child continuity. Filters out None values to match the API response structure. Returns: Dict[str, Any]: Dictionary representation. """ _dict = { "childApplicationStatusCode": self.child_application_status_code, "parentApplicationNumberText": self.parent_application_number_text, "childApplicationNumberText": self.child_application_number_text, "childApplicationStatusDescriptionText": self.child_application_status_description_text, "childApplicationFilingDate": serialize_date( self.child_application_filing_date ), "firstInventorToFileIndicator": self.first_inventor_to_file_indicator, "childPatentNumber": self.child_patent_number, "claimParentageTypeCode": self.claim_parentage_type_code, "claimParentageTypeCodeDescriptionText": self.claim_parentage_type_code_description_text, } return {k: v for k, v in _dict.items() if v is not None}
[docs] @dataclass(frozen=True) class PatentTermAdjustmentHistoryData: """Represents a single entry in the patent term adjustment (PTA) history for an application. Details specific events, dates, and day quantities affecting the patent term. Attributes: event_date: Date of the PTA event. applicant_day_delay_quantity: Number of days of delay attributable to the applicant for this event. event_description_text: Textual description of the PTA event. event_sequence_number: Sequence number of this event in the PTA history. originating_event_sequence_number: Sequence number of an event that originated this event. pta_pte_code: Code indicating if the event relates to PTA or Patent Term Extension (PTE). ip_office_day_delay_quantity: Number of days of IP office delay used in adjustment calculation for this event. """ event_date: Optional[date] = None applicant_day_delay_quantity: Optional[float] = None event_description_text: Optional[str] = None event_sequence_number: Optional[float] = None originating_event_sequence_number: Optional[float] = None pta_pte_code: Optional[str] = None ip_office_day_delay_quantity: Optional[float] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "PatentTermAdjustmentHistoryData": """Creates a `PatentTermAdjustmentHistoryData` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with PTA history event data. Returns: PatentTermAdjustmentHistoryData: An instance of `PatentTermAdjustmentHistoryData`. """ return cls( event_date=parse_to_date(data.get("eventDate")), applicant_day_delay_quantity=data.get("applicantDayDelayQuantity"), event_description_text=data.get("eventDescriptionText"), event_sequence_number=data.get("eventSequenceNumber"), originating_event_sequence_number=data.get( "originatingEventSequenceNumber" ), pta_pte_code=data.get("ptaPTECode"), ip_office_day_delay_quantity=data.get("ipOfficeDayDelayQuantity"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PatentTermAdjustmentHistoryData` instance to a dictionary. Omits keys with None values. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ final_dict: Dict[str, Any] = {} if self.event_date is not None: final_dict["eventDate"] = serialize_date(self.event_date) if self.applicant_day_delay_quantity is not None: final_dict["applicantDayDelayQuantity"] = self.applicant_day_delay_quantity if self.event_description_text is not None: final_dict["eventDescriptionText"] = self.event_description_text if self.event_sequence_number is not None: final_dict["eventSequenceNumber"] = self.event_sequence_number if self.originating_event_sequence_number is not None: final_dict["originatingEventSequenceNumber"] = ( self.originating_event_sequence_number ) if self.pta_pte_code is not None: final_dict["ptaPTECode"] = self.pta_pte_code if self.ip_office_day_delay_quantity is not None: final_dict["ipOfficeDayDelayQuantity"] = self.ip_office_day_delay_quantity return final_dict
[docs] @dataclass(frozen=True) class PatentTermAdjustmentData: """Represents the overall patent term adjustment (PTA) data for an application. Includes various delay quantities (A, B, C, applicant, IP office), total adjustment, and a history of PTA events. Attributes: a_delay_quantity: Number of days of 'A' delay. adjustment_total_quantity: Total calculated PTA in days. applicant_day_delay_quantity: Total days of delay attributable to the applicant. b_delay_quantity: Number of days of 'B' delay. c_delay_quantity: Number of days of 'C' delay. non_overlapping_day_quantity: Number of non-overlapping delay days. overlapping_day_quantity: Number of overlapping delay days. non_overlapping_day_delay_quantity: Number of non-overlapping delay days specifically for delay calculation. ip_office_adjustment_delay_quantity: Days of IP office delay used in adjustment calculation. patent_term_adjustment_history_data_bag: List of `PatentTermAdjustmentHistoryData` events. """ a_delay_quantity: Optional[float] = None adjustment_total_quantity: Optional[float] = None applicant_day_delay_quantity: Optional[float] = None b_delay_quantity: Optional[float] = None c_delay_quantity: Optional[float] = None non_overlapping_day_quantity: Optional[float] = None overlapping_day_quantity: Optional[float] = None non_overlapping_day_delay_quantity: Optional[float] = None ip_office_adjustment_delay_quantity: Optional[float] = None patent_term_adjustment_history_data_bag: List[PatentTermAdjustmentHistoryData] = ( field(default_factory=list) )
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "PatentTermAdjustmentData": """Creates a `PatentTermAdjustmentData` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with PTA data. Returns: PatentTermAdjustmentData: An instance of `PatentTermAdjustmentData`. """ history = [ PatentTermAdjustmentHistoryData.from_dict(h) for h in data.get("patentTermAdjustmentHistoryDataBag", []) if isinstance(h, dict) ] return cls( a_delay_quantity=data.get("aDelayQuantity"), adjustment_total_quantity=data.get("adjustmentTotalQuantity"), applicant_day_delay_quantity=data.get("applicantDayDelayQuantity"), b_delay_quantity=data.get("bDelayQuantity"), c_delay_quantity=data.get("cDelayQuantity"), non_overlapping_day_quantity=data.get("nonOverlappingDayQuantity"), overlapping_day_quantity=data.get("overlappingDayQuantity"), non_overlapping_day_delay_quantity=data.get( "nonOverlappingDayDelayQuantity" ), ip_office_adjustment_delay_quantity=data.get( "ipOfficeAdjustmentDelayQuantity" ), patent_term_adjustment_history_data_bag=history, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PatentTermAdjustmentData` instance to a dictionary. Omits keys with None values or empty lists, and converts field names to camelCase. Returns: Dict[str, Any]: Dictionary representation. """ d = asdict(self) d["patentTermAdjustmentHistoryDataBag"] = [ h.to_dict() for h in self.patent_term_adjustment_history_data_bag ] return { to_camel_case(k): v for k, v in d.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class EventData: """Represents a single event in the transaction history of a patent application. Attributes: event_code: A code identifying the type of event. event_description_text: A textual description of the event. event_date: The date the event was recorded. """ event_code: Optional[str] = None event_description_text: Optional[str] = None event_date: Optional[date] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "EventData": """Creates an `EventData` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with event data. Returns: EventData: An instance of `EventData`. """ return cls( event_code=data.get("eventCode"), event_description_text=data.get("eventDescriptionText"), event_date=parse_to_date(data.get("eventDate")), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `EventData` instance to a dictionary. Omits keys with None values and converts field names to camelCase. Returns: Dict[str, Any]: Dictionary representation. """ d = asdict(self) d["eventDate"] = serialize_date(self.event_date) return {to_camel_case(k): v for k, v in d.items() if v is not None}
[docs] @dataclass(frozen=True) class PrintedMetaData: """Represents metadata for a specific archive file, such as a PGPUB or Grant XML file. Attributes: zip_file_name: The name of the ZIP archive. product_identifier: An identifier for the data product (e.g., "APPXML", "PTGRXML"). file_location_uri: The URI where the document file can be accessed. file_create_date_time: The creation timestamp of the document file (UTC). xml_file_name: The name of the XML file within the ZIP archive. """ zip_file_name: Optional[str] = None product_identifier: Optional[str] = None file_location_uri: Optional[str] = None file_create_date_time: Optional[datetime] = None xml_file_name: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "PrintedMetaData": """Creates a `PrintedMetaData` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with printed metadata. Returns: PrintedMetaData: An instance of `PrintedMetaData`. """ return cls( zip_file_name=data.get("zipFileName"), product_identifier=data.get("productIdentifier"), file_location_uri=data.get("fileLocationURI"), file_create_date_time=parse_to_datetime_utc(data.get("fileCreateDateTime")), xml_file_name=data.get("xmlFileName"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PrintedMetaData` instance to a dictionary. Omits keys with None values. Serializes datetime to ISO format with 'Z'. Returns: Dict[str, Any]: Dictionary representation with camelCase keys. """ final_dict: Dict[str, Any] = {} if self.zip_file_name is not None: final_dict["zipFileName"] = self.zip_file_name if self.product_identifier is not None: final_dict["productIdentifier"] = self.product_identifier if self.file_location_uri is not None: final_dict["fileLocationURI"] = self.file_location_uri if self.file_create_date_time is not None: final_dict["fileCreateDateTime"] = serialize_datetime_as_naive( self.file_create_date_time ) if self.xml_file_name is not None: final_dict["xmlFileName"] = self.xml_file_name return final_dict
[docs] @dataclass(frozen=True) class ApplicationMetaData: """Represents the metadata associated with a patent application. This class holds a wide range of information including application status, dates (filing, grant, publication), applicant and inventor details, classification data, and other identifying information. Attributes: national_stage_indicator: Indicates if the application is a national stage entry. entity_status_data: `EntityStatus` object detailing applicant's entity status. publication_date_bag: List of publication dates. publication_sequence_number_bag: List of publication sequence numbers. publication_category_bag: List of publication categories. docket_number: Applicant's or attorney's docket number. first_inventor_to_file_indicator: Boolean indicating if under First-Inventor-to-File. first_applicant_name: Name of the first listed applicant. first_inventor_name: Name of the first listed inventor. application_confirmation_number: USPTO confirmation number for the application. application_status_date: Date the current application status was set. application_status_description_text: Textual description of the current application status. filing_date: Official filing date of the application. effective_filing_date: Effective filing date, considering priority claims. grant_date: Date the patent was granted, if applicable. group_art_unit_number: USPTO Group Art Unit number. application_type_code: Code for the application type. application_type_label_name: Label for the application type (e.g., "Utility"). application_type_category: Category of the application type. invention_title: Title of the invention. patent_number: USPTO patent number, if granted. application_status_code: Numeric code for the application status. earliest_publication_number: Number of the earliest pre-grant publication. earliest_publication_date: Date of the earliest pre-grant publication. pct_publication_number: PCT publication number, if applicable. pct_publication_date: PCT publication date, if applicable. international_registration_publication_date: Date of international registration publication. international_registration_number: International registration number. examiner_name_text: Name of the patent examiner. class_field: USPC main classification. (Named `class_field` to avoid keyword clash). subclass: USPC subclass. uspc_symbol_text: Full USPC classification symbol. customer_number: USPTO customer number associated with the application. cpc_classification_bag: List of CPC classification symbols. applicant_bag: List of `Applicant` objects. inventor_bag: List of `Inventor` objects. raw_data: Raw JSON string of the data used to create this instance (for debugging). """ national_stage_indicator: Optional[bool] = None entity_status_data: Optional[EntityStatus] = None publication_date_bag: List[date] = field(default_factory=list) publication_sequence_number_bag: List[str] = field(default_factory=list) publication_category_bag: List[str] = field(default_factory=list) docket_number: Optional[str] = None first_inventor_to_file_indicator: Optional[bool] = None first_applicant_name: Optional[str] = None first_inventor_name: Optional[str] = None application_confirmation_number: Optional[int] = None application_status_date: Optional[date] = None application_status_description_text: Optional[str] = None filing_date: Optional[date] = None effective_filing_date: Optional[date] = None grant_date: Optional[date] = None group_art_unit_number: Optional[str] = None application_type_code: Optional[str] = None application_type_label_name: Optional[str] = None application_type_category: Optional[str] = None invention_title: Optional[str] = None patent_number: Optional[str] = None application_status_code: Optional[int] = None earliest_publication_number: Optional[str] = None earliest_publication_date: Optional[date] = None pct_publication_number: Optional[str] = None pct_publication_date: Optional[date] = None international_registration_publication_date: Optional[date] = None international_registration_number: Optional[str] = None examiner_name_text: Optional[str] = None class_field: Optional[str] = None subclass: Optional[str] = None uspc_symbol_text: Optional[str] = None customer_number: Optional[int] = None cpc_classification_bag: List[str] = field(default_factory=list) applicant_bag: List[Applicant] = field(default_factory=list) inventor_bag: List[Inventor] = field(default_factory=list) raw_data: Optional[str] = field(default=None, compare=False) @property def is_aia(self) -> Optional[bool]: """Returns True if the application is AIA, False if pre-AIA, None if unknown.""" return self.first_inventor_to_file_indicator @property def is_pre_aia(self) -> Optional[bool]: """Returns True if the application is pre-AIA, False if AIA, None if unknown.""" if self.first_inventor_to_file_indicator is None: return None return not self.first_inventor_to_file_indicator
[docs] @classmethod def from_dict( cls, data: Dict[str, Any], include_raw_data: bool = False ) -> "ApplicationMetaData": """Creates an `ApplicationMetaData` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with application metadata. include_raw_data (bool): If True, store the raw JSON for debugging. Returns: ApplicationMetaData: An instance of `ApplicationMetaData`. """ entity = ( EntityStatus.from_dict(data["entityStatusData"]) if isinstance(data.get("entityStatusData"), dict) else None ) app_bag = [ Applicant.from_dict(a) for a in data.get("applicantBag", []) if isinstance(a, dict) ] inv_bag = [ Inventor.from_dict(i) for i in data.get("inventorBag", []) if isinstance(i, dict) ] pub_dates_str = data.get("publicationDateBag", []) pub_dates = ( [parse_to_date(d) for d in pub_dates_str if isinstance(d, str)] if isinstance(pub_dates_str, list) else [] ) fitf_indicator_str = data.get("firstInventorToFileIndicator") fitf_indicator_bool = parse_yn_to_bool(fitf_indicator_str) return cls( national_stage_indicator=data.get("nationalStageIndicator"), entity_status_data=entity, publication_date_bag=[d for d in pub_dates if d is not None], publication_sequence_number_bag=data.get( "publicationSequenceNumberBag", [] ), publication_category_bag=data.get("publicationCategoryBag", []), docket_number=data.get("docketNumber"), first_inventor_to_file_indicator=fitf_indicator_bool, first_applicant_name=data.get("firstApplicantName"), first_inventor_name=data.get("firstInventorName"), application_confirmation_number=data.get("applicationConfirmationNumber"), application_status_date=parse_to_date(data.get("applicationStatusDate")), application_status_description_text=data.get( "applicationStatusDescriptionText" ), filing_date=parse_to_date(data.get("filingDate")), effective_filing_date=parse_to_date(data.get("effectiveFilingDate")), grant_date=parse_to_date(data.get("grantDate")), group_art_unit_number=data.get("groupArtUnitNumber"), application_type_code=data.get("applicationTypeCode"), application_type_label_name=data.get("applicationTypeLabelName"), application_type_category=data.get("applicationTypeCategory"), invention_title=data.get("inventionTitle"), patent_number=data.get("patentNumber"), application_status_code=data.get("applicationStatusCode"), earliest_publication_number=data.get("earliestPublicationNumber"), earliest_publication_date=parse_to_date( data.get("earliestPublicationDate") ), pct_publication_number=data.get("pctPublicationNumber"), pct_publication_date=parse_to_date(data.get("pctPublicationDate")), international_registration_publication_date=parse_to_date( data.get("internationalRegistrationPublicationDate") ), international_registration_number=data.get( "internationalRegistrationNumber" ), examiner_name_text=data.get("examinerNameText"), class_field=data.get("class"), subclass=data.get("subclass"), uspc_symbol_text=data.get("uspcSymbolText"), customer_number=data.get("customerNumber"), cpc_classification_bag=data.get("cpcClassificationBag", []), applicant_bag=app_bag, inventor_bag=inv_bag, raw_data=json.dumps(data) if include_raw_data else None, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `ApplicationMetaData` instance to a dictionary. Serializes attributes to camelCase keys suitable for API interaction or storage. Omits keys with None values or empty lists. Handles date and boolean serialization. Returns: Dict[str, Any]: Dictionary representation of the application metadata. """ d = asdict(self) d.pop("raw_data", None) d.pop("is_aia", None) d.pop("is_pre_aia", None) date_fields = [ "application_status_date", "filing_date", "effective_filing_date", "grant_date", "earliest_publication_date", "pct_publication_date", "international_registration_publication_date", ] for field_name in date_fields: camel_key = to_camel_case(field_name) d[camel_key] = serialize_date(getattr(self, field_name, None)) if field_name != camel_key: d.pop(field_name, None) d["publicationDateBag"] = [ serialize_date(dt) for dt in self.publication_date_bag if dt ] if ( "publication_date_bag" in d and "publication_date_bag" != "publicationDateBag" ): d.pop("publication_date_bag") d["firstInventorToFileIndicator"] = serialize_bool_to_yn( self.first_inventor_to_file_indicator ) if ( "first_inventor_to_file_indicator" in d and "first_inventor_to_file_indicator" != "firstInventorToFileIndicator" ): d.pop("first_inventor_to_file_indicator") if self.entity_status_data: d["entityStatusData"] = self.entity_status_data.to_dict() else: d.pop("entityStatusData", None) d.pop("entity_status_data", None) d["applicantBag"] = [a.to_dict() for a in self.applicant_bag] if "applicant_bag" in d and "applicant_bag" != "applicantBag": d.pop("applicant_bag") d["inventorBag"] = [i.to_dict() for i in self.inventor_bag] if "inventor_bag" in d and "inventor_bag" != "inventorBag": d.pop("inventor_bag") if "class_field" in d: d["class"] = d.pop("class_field") if "class" in d and d["class"] is None: d.pop("class", None) final_data = {} for key_snake, v_obj in d.items(): k_camel = to_camel_case(snake_str=key_snake) if key_snake in [ "firstInventorToFileIndicator", "publicationDateBag", "entityStatusData", "applicantBag", "inventorBag", ] or any(to_camel_case(df) == key_snake for df in date_fields): k_camel = key_snake if v_obj is not None: if isinstance(v_obj, list) and not v_obj: continue final_data[k_camel] = v_obj return final_data
[docs] @dataclass(frozen=True) class PatentFileWrapper: """Represents the complete file wrapper for a single patent application. This is a top-level object containing all data sections related to an application, such as metadata, addresses, assignments, attorney information, continuity data, PTA data, transaction events, and associated document metadata. Attributes: application_number_text: The primary application number. application_meta_data: Comprehensive `ApplicationMetaData`. correspondence_address_bag: List of `Address` objects for correspondence. assignment_bag: List of `Assignment` records. record_attorney: Information about the `RecordAttorney`. foreign_priority_bag: List of `ForeignPriority` claims. parent_continuity_bag: List of `ParentContinuity` records. child_continuity_bag: List of `ChildContinuity` records. patent_term_adjustment_data: `PatentTermAdjustmentData` details. event_data_bag: List of `EventData` (transaction history). pgpub_document_meta_data: `PrintedMetaData` for Pre-Grant Publication. grant_document_meta_data: `PrintedMetaData` for the granted patent. last_ingestion_date_time: Timestamp of when this data was last ingested by the API (UTC). """ application_number_text: Optional[str] = None application_meta_data: Optional[ApplicationMetaData] = None correspondence_address_bag: List[Address] = field(default_factory=list) assignment_bag: List[Assignment] = field(default_factory=list) record_attorney: Optional[RecordAttorney] = None foreign_priority_bag: List[ForeignPriority] = field(default_factory=list) parent_continuity_bag: List[ParentContinuity] = field(default_factory=list) child_continuity_bag: List[ChildContinuity] = field(default_factory=list) patent_term_adjustment_data: Optional[PatentTermAdjustmentData] = None event_data_bag: List[EventData] = field(default_factory=list) pgpub_document_meta_data: Optional[PrintedMetaData] = None grant_document_meta_data: Optional[PrintedMetaData] = None last_ingestion_date_time: Optional[datetime] = None
[docs] @classmethod def from_dict( cls, data: Dict[str, Any], include_raw_data: bool = False ) -> "PatentFileWrapper": """Creates a `PatentFileWrapper` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with patent file wrapper data. include_raw_data (bool): If True, store the raw JSON for debugging. Returns: PatentFileWrapper: An instance of `PatentFileWrapper`. """ amd_json = data.get("applicationMetaData") amd = ( ApplicationMetaData.from_dict(amd_json, include_raw_data=include_raw_data) if isinstance(amd_json, dict) else None ) corr_addrs = [ Address.from_dict(a) for a in data.get("correspondenceAddressBag", []) if isinstance(a, dict) ] assigns = [ Assignment.from_dict(a) for a in data.get("assignmentBag", []) if isinstance(a, dict) ] rec_att_json = data.get("recordAttorney") rec_att = ( RecordAttorney.from_dict(rec_att_json) if isinstance(rec_att_json, dict) else None ) f_pris = [ ForeignPriority.from_dict(fp) for fp in data.get("foreignPriorityBag", []) if isinstance(fp, dict) ] p_conts = [ ParentContinuity.from_dict(pc) for pc in data.get("parentContinuityBag", []) if isinstance(pc, dict) ] c_conts = [ ChildContinuity.from_dict(cc) for cc in data.get("childContinuityBag", []) if isinstance(cc, dict) ] pta_json = data.get("patentTermAdjustmentData") pta = ( PatentTermAdjustmentData.from_dict(pta_json) if isinstance(pta_json, dict) else None ) evts = [ EventData.from_dict(e) for e in data.get("eventDataBag", []) if isinstance(e, dict) ] pgpub_json = data.get("pgpubDocumentMetaData") pgpub = ( PrintedMetaData.from_dict(pgpub_json) if isinstance(pgpub_json, dict) else None ) grant_json = data.get("grantDocumentMetaData") grant = ( PrintedMetaData.from_dict(grant_json) if isinstance(grant_json, dict) else None ) return cls( application_number_text=data.get("applicationNumberText"), application_meta_data=amd, correspondence_address_bag=corr_addrs, assignment_bag=assigns, record_attorney=rec_att, foreign_priority_bag=f_pris, parent_continuity_bag=p_conts, child_continuity_bag=c_conts, patent_term_adjustment_data=pta, event_data_bag=evts, pgpub_document_meta_data=pgpub, grant_document_meta_data=grant, last_ingestion_date_time=parse_to_datetime_utc( data.get("lastIngestionDateTime") ), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PatentFileWrapper` instance to a dictionary. Omits keys with None values or empty lists. Serializes nested objects. Returns: Dict[str, Any]: Dictionary representation. """ _dict = { "applicationNumberText": self.application_number_text, "applicationMetaData": ( self.application_meta_data.to_dict() if self.application_meta_data else None ), "correspondenceAddressBag": [ a.to_dict() for a in self.correspondence_address_bag ], "assignmentBag": [a.to_dict() for a in self.assignment_bag], "recordAttorney": ( self.record_attorney.to_dict() if self.record_attorney else None ), "foreignPriorityBag": [fp.to_dict() for fp in self.foreign_priority_bag], "parentContinuityBag": [pc.to_dict() for pc in self.parent_continuity_bag], "childContinuityBag": [cc.to_dict() for cc in self.child_continuity_bag], "patentTermAdjustmentData": ( self.patent_term_adjustment_data.to_dict() if self.patent_term_adjustment_data else None ), "eventDataBag": [e.to_dict() for e in self.event_data_bag], "pgpubDocumentMetaData": ( self.pgpub_document_meta_data.to_dict() if self.pgpub_document_meta_data else None ), "grantDocumentMetaData": ( self.grant_document_meta_data.to_dict() if self.grant_document_meta_data else None ), "lastIngestionDateTime": ( serialize_datetime_as_naive(self.last_ingestion_date_time) if self.last_ingestion_date_time else None ), } return { k: v for k, v in _dict.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class PatentDataResponse: """Represents the overall response from a patent data API request. It typically includes a count of the results and a list of PatentFileWrapper objects, each containing detailed data for a patent application. Attributes: count: The total number of patent applications found matching the query. patent_file_wrapper_data_bag: A list of `PatentFileWrapper` objects. request_identifier: An identifier for the API request, if provided. raw_data: Optional raw JSON data from the API response (for debugging). """ count: int patent_file_wrapper_data_bag: List[PatentFileWrapper] = field(default_factory=list) request_identifier: Optional[str] = None raw_data: Optional[str] = field(default=None, compare=False, repr=False)
[docs] @classmethod def from_dict( cls, data: Dict[str, Any], include_raw_data: bool = False ) -> "PatentDataResponse": """Creates a `PatentDataResponse` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with API response data. include_raw_data (bool): If True, store the raw JSON for debugging. Returns: PatentDataResponse: An instance of `PatentDataResponse`. """ wrappers = [ PatentFileWrapper.from_dict(w, include_raw_data=include_raw_data) for w in data.get("patentFileWrapperDataBag", []) if isinstance(w, dict) ] return cls( count=data.get("count", 0), patent_file_wrapper_data_bag=wrappers, request_identifier=data.get("requestIdentifier"), raw_data=json.dumps(data) if include_raw_data else None, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PatentDataResponse` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation. """ _dict = { "count": self.count, "patentFileWrapperDataBag": [ w.to_dict() for w in self.patent_file_wrapper_data_bag ], "requestIdentifier": self.request_identifier, } return { k: v for k, v in _dict.items() if v is not None and (not isinstance(v, list) or v) }
[docs] def to_csv(self) -> str: """Converts the patent data in this response to a CSV formatted string. The CSV will contain key metadata fields for each application, such as invention title, application number, filing date, status, etc. Returns: str: A string containing the data in CSV format. """ headers = [ "inventionTitle", "applicationNumberText", "filingDate", "applicationTypeLabelName", "publicationCategoryBag", "applicationStatusDescriptionText", "applicationStatusDate", "firstInventorName", ] output = io.StringIO() writer = csv.writer(output) writer.writerow(headers) if not self.patent_file_wrapper_data_bag: return output.getvalue() for wrapper in self.patent_file_wrapper_data_bag: if not wrapper.application_meta_data: continue meta = wrapper.application_meta_data pub_category_str = ( "|".join(meta.publication_category_bag) if meta.publication_category_bag else "" ) row_data = [ meta.invention_title or "", wrapper.application_number_text or "", serialize_date(meta.filing_date) or "", meta.application_type_label_name or "", pub_category_str, meta.application_status_description_text or "", serialize_date(meta.application_status_date) or "", meta.first_inventor_name or "", ] writer.writerow(row_data) return output.getvalue()
[docs] @dataclass(frozen=True) class StatusCode: """Represents a USPTO application status code and its textual description. Attributes: code: The numeric status code. description: The textual description of the status code. """ code: Optional[int] = None description: Optional[str] = None
[docs] def __str__(self) -> str: """Returns a user-friendly string representation of the status code.""" return f"{self.code}: {self.description}"
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "StatusCode": """Creates a `StatusCode` instance from a dictionary. Handles two possible key sets from the API for status information. Args: data (Dict[str, Any]): Dictionary with status code data. Returns: StatusCode: An instance of `StatusCode`. """ if "code" in data: return cls( code=data.get("code"), description=data.get("description"), ) else: return cls( code=data.get("applicationStatusCode"), description=data.get("applicationStatusDescriptionText"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `StatusCode` instance to a dictionary. Uses keys "applicationStatusCode" and "applicationStatusDescriptionText" for consistency with some API response parts. Returns: Dict[str, Any]: Dictionary representation. """ return { "applicationStatusCode": self.code, "applicationStatusDescriptionText": self.description, }
[docs] class StatusCodeCollection: """A collection of StatusCode objects. Provides iterable access and helper methods to find or filter status codes. This class is immutable by convention after initialization. Attributes: status_codes (tuple[StatusCode, ...]): An immutable tuple of `StatusCode` objects. """
[docs] def __init__(self, status_codes: List[StatusCode]): """Initializes the StatusCodeCollection with a list of status codes. Args: status_codes (List[StatusCode]): A list of `StatusCode` instances. """ self._status_codes: tuple[StatusCode, ...] = tuple(status_codes)
def __iter__(self) -> Iterator[StatusCode]: return iter(self._status_codes) def __len__(self) -> int: return len(self._status_codes) def __getitem__(self, index: int) -> StatusCode: return self._status_codes[index] def __str__(self) -> str: return f"StatusCodeCollection with {len(self)} status codes." def __repr__(self) -> str: if not self._status_codes: return "StatusCodeCollection(empty)" if len(self._status_codes) <= 3: codes = ", ".join(str(s.code) for s in self._status_codes) return f"StatusCodeCollection({len(self)} status codes: {codes})" else: first_codes = ", ".join(str(s.code) for s in self._status_codes[:3]) return f"StatusCodeCollection({len(self)} status codes: {first_codes}, ...)"
[docs] def find_by_code(self, code_to_find: int) -> Optional[StatusCode]: """Finds a status code by its numeric code. Args: code_to_find (int): The numeric status code to search for. Returns: Optional[StatusCode]: The `StatusCode` object if found, otherwise None. """ for status in self._status_codes: if status.code == code_to_find: return status return None
[docs] def search_by_description(self, text: str) -> "StatusCodeCollection": """Searches for status codes by a case-insensitive text match in their description. Args: text (str): The text to search for within status code descriptions. Returns: StatusCodeCollection: A new collection containing matching status codes. """ matching = [ s for s in self._status_codes if s.description and text.lower() in s.description.lower() ] return StatusCodeCollection(status_codes=matching)
[docs] def to_dict(self) -> List[Dict[str, Any]]: """Converts the collection of status codes to a list of dictionaries. Returns: List[Dict[str, Any]]: A list where each item is the dictionary representation of a `StatusCode`. """ return [sc.to_dict() for sc in self._status_codes]
[docs] @dataclass(frozen=True) class StatusCodeSearchResponse: """Represents the response from a search query for patent application status codes. Attributes: count: The total number of status codes found matching the query. status_code_bag: A `StatusCodeCollection` of the `StatusCode` objects returned. request_identifier: An identifier for the API request, if provided. """ count: int status_code_bag: StatusCodeCollection request_identifier: Optional[str] = None
[docs] @classmethod def from_dict(cls, data: Dict[str, Any]) -> "StatusCodeSearchResponse": """Creates a `StatusCodeSearchResponse` instance from a dictionary. Args: data (Dict[str, Any]): Dictionary with API response data for status codes. Returns: StatusCodeSearchResponse: An instance of `StatusCodeSearchResponse`. """ codes_json = data.get("statusCodeBag", []) parsed_codes = ( [StatusCode.from_dict(cd) for cd in codes_json if isinstance(cd, dict)] if isinstance(codes_json, list) else [] ) collection = StatusCodeCollection(parsed_codes) return cls( count=data.get("count", 0), status_code_bag=collection, request_identifier=data.get("requestIdentifier"), )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `StatusCodeSearchResponse` instance to a dictionary. Omits keys with None values or empty lists. Returns: Dict[str, Any]: Dictionary representation. """ _dict = { "count": self.count, "statusCodeBag": self.status_code_bag.to_dict(), "requestIdentifier": self.request_identifier, } return { k: v for k, v in _dict.items() if v is not None and (not isinstance(v, list) or v) }
[docs] @dataclass(frozen=True) class ApplicationContinuityData: """Holds parent and child continuity application data for a specific patent application. This class consolidates lists of ParentContinuity and ChildContinuity objects, representing the lineage of an application. Attributes: parent_continuity_bag: List of `ParentContinuity` objects. child_continuity_bag: List of `ChildContinuity` objects. """ parent_continuity_bag: List[ParentContinuity] = field(default_factory=list) child_continuity_bag: List[ChildContinuity] = field(default_factory=list)
[docs] @classmethod def from_wrapper(cls, wrapper: PatentFileWrapper) -> "ApplicationContinuityData": """Creates an `ApplicationContinuityData` instance from a `PatentFileWrapper`. Extracts parent and child continuity bags from the wrapper. Args: wrapper (PatentFileWrapper): The patent file wrapper containing continuity data. Returns: ApplicationContinuityData: An instance of `ApplicationContinuityData`. """ return cls( parent_continuity_bag=wrapper.parent_continuity_bag, child_continuity_bag=wrapper.child_continuity_bag, )
[docs] def to_dict( self, ) -> Dict[str, Any]: """Converts the `ApplicationContinuityData` instance to a dictionary. Returns: Dict[str, Any]: Dictionary representation with "parentContinuityBag" and "childContinuityBag" keys. """ return { "parentContinuityBag": [pc.to_dict() for pc in self.parent_continuity_bag], "childContinuityBag": [cc.to_dict() for cc in self.child_continuity_bag], }
[docs] @dataclass(frozen=True) class PrintedPublication: """Holds metadata for associated documents like Pre-Grant Publications (PGPUB) and Grant documents for a specific patent application. Attributes: pgpub_document_meta_data: `PrintedMetaData` for the Pre-Grant Publication, if any. grant_document_meta_data: `PrintedMetaData` for the Grant document, if any. """ pgpub_document_meta_data: Optional[PrintedMetaData] = None grant_document_meta_data: Optional[PrintedMetaData] = None
[docs] @classmethod def from_wrapper(cls, wrapper: PatentFileWrapper) -> "PrintedPublication": """Creates a `PrintedPublication` instance from a `PatentFileWrapper`. Extracts PGPUB and Grant document metadata from the wrapper. Args: wrapper (PatentFileWrapper): The patent file wrapper. Returns: PrintedPublication: An instance of `PrintedPublication`. """ return cls( pgpub_document_meta_data=wrapper.pgpub_document_meta_data, grant_document_meta_data=wrapper.grant_document_meta_data, )
[docs] def to_dict(self) -> Dict[str, Any]: """Converts the `PrintedPublication` instance to a dictionary. Omits keys if their corresponding metadata is None. Returns: Dict[str, Any]: Dictionary representation. """ return { "pgpubDocumentMetaData": ( self.pgpub_document_meta_data.to_dict() if self.pgpub_document_meta_data else None ), "grantDocumentMetaData": ( self.grant_document_meta_data.to_dict() if self.grant_document_meta_data else None ), }