diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml
index 1ea6ee8..70ab75c 100644
--- a/.github/workflows/python-tests.yml
+++ b/.github/workflows/python-tests.yml
@@ -51,19 +51,15 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install flake8 black isort mypy
+ pip install ruff mypy
- - name: Lint with flake8
+ - name: Lint with ruff
run: |
- flake8 src/pyUSPTO --count --select=E9,F63,F7,F82 --show-source --statistics
+ ruff check src/
- - name: Check formatting with black
+ - name: Check formatting with ruff
run: |
- black --check src/
-
- - name: Check imports with isort
- run: |
- isort --check-only --profile black src/
+ ruff format --check src/
- name: Type check with mypy
run: |
diff --git a/.gitignore b/.gitignore
index 72e5097..a219980 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@ dist/
.claude/settings.local.json
.tox/
Python/
+run_integration_tests.py
+.plan.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef6dad3..cab588a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,28 @@ All notable changes to the pyUSPTO package will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.3.0] - TBD
+
+### Added
+
+- **PTAB API 3.0 Support**: New clients for PTAB trials, appeals, and interferences
+ - `PTABTrialsClient` - Search trial proceedings, documents, and decisions
+ - `PTABAppealsClient` - Search ex parte appeal decisions
+ - `PTABInterferencesClient` - Search interference decisions
+- New data models in `pyUSPTO.models.ptab` for PTAB responses:
+ - `PTABTrialProceeding`, `PTABAppealDecision`, `PTABInterferenceDecision`
+ - Supporting models for party data, metadata, and decision information
+- Configuration support for PTAB base URL in `USPTOConfig`
+- Comprehensive examples for all three PTAB clients (`examples/ptab_*.py`)
+- Additional convenience parameters for `PTABTrialsClient` search methods:
+ - `search_documents()`: petitioner name, inventor, patent details, real party in interest
+ - `search_decisions()`: trial type, patent/application numbers, status, party information, document category
+
+### Changed
+
+- Enhanced `PTABTrialsClient.search_documents()` with convenience parameters for petitioner, inventor, patent details
+- Enhanced `PTABTrialsClient.search_decisions()` with convenience parameters for trial type, status, and party information
+
## [0.2.2]
### Added
diff --git a/README.md b/README.md
index 867ebfb..1c93681 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# pyUSPTO
+
[](https://badge.fury.io/py/pyUSPTO)
[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
@@ -6,64 +7,253 @@
A Python client library for interacting with the United Stated Patent and Trademark Office (USPTO) [Open Data Portal](https://data.uspto.gov/home) APIs.
-This package provides clients for interacting with the USPTO Bulk Data API, the USPTO Patent Data API, and the USPTO Final Petition Decisions API.
+This package provides clients for interacting with the USPTO Bulk Data API, Patent Data API, Final Petition Decisions API, and PTAB (Patent Trial and Appeal Board) APIs.
> [!IMPORTANT]
> The USPTO is in the process of moving their API. This package is only concerned with the new API. The [old API](https://developer.uspto.gov/) will be retired at the end of 2025.
## Quick Start
-### Installation
-
**Requirements**: Python ≥3.10
```bash
pip install pyUSPTO
```
-Or install from source:
+> [!IMPORTANT]
+> You must have an API key for the [USPTO Open Data Portal API](https://data.uspto.gov/myodp/landing).
-```bash
-git clone https://github.com/DunlapCoddingPC/pyUSPTO.git
-cd pyUSPTO
-pip install -e .
-```
+```python
+from pyUSPTO import PatentDataClient
+# Initialize with your API key
+client = PatentDataClient(api_key="your_api_key_here")
-### Configuration Options
+# Search for patent applications
+results = client.search_applications(inventor_name_q="Smith", limit=10)
+print(f"Found {results.count} applications")
+```
-> [!IMPORTANT]
-> You must have an API key for the [USPTO Open Data Portal API](https://data.uspto.gov/myodp/landing).
+## Configuration
+
+All clients can be configured using one of three methods:
-There are multiple ways to configure the USPTO API clients:
+### Method 1: Direct API Key Initialization
+> [!NOTE]
+> This method is convenient for quick scripts but not recommended for production use. Consider using environment variables instead.
```python
-from pyUSPTO import PatentDataClient, FinalPetitionDecisionsClient
+from pyUSPTO import (
+ BulkDataClient,
+ PatentDataClient,
+ FinalPetitionDecisionsClient,
+ PTABTrialsClient,
+ PTABAppealsClient,
+ PTABInterferencesClient
+)
-# Method 1: Direct API key initialization
patent_client = PatentDataClient(api_key="your_api_key_here")
+bulk_client = BulkDataClient(api_key="your_api_key_here")
petition_client = FinalPetitionDecisionsClient(api_key="your_api_key_here")
+trials_client = PTABTrialsClient(api_key="your_api_key_here")
+appeals_client = PTABAppealsClient(api_key="your_api_key_here")
+interferences_client = PTABInterferencesClient(api_key="your_api_key_here")
+```
+
+### Method 2: Using USPTOConfig
+
+```python
+from pyUSPTO import (
+ BulkDataClient,
+ PatentDataClient,
+ FinalPetitionDecisionsClient,
+ PTABTrialsClient,
+ PTABAppealsClient,
+ PTABInterferencesClient
+)
-# Method 2: Using USPTOConfig with explicit parameters
from pyUSPTO.config import USPTOConfig
-config = USPTOConfig(
- api_key="your_api_key_here",
- bulk_data_base_url="https://api.uspto.gov",
- patent_data_base_url="https://api.uspto.gov",
- petition_decisions_base_url="https://api.uspto.gov"
+
+config = USPTOConfig(api_key="your_api_key_here")
+
+patent_client = PatentDataClient(config=config)
+bulk_client = BulkDataClient(config=config)
+petition_client = FinalPetitionDecisionsClient(config=config)
+trials_client = PTABTrialsClient(config=config)
+appeals_client = PTABAppealsClient(config=config)
+interferences_client = PTABInterferencesClient(config=config)
+```
+
+### Method 3: Environment Variables (Recommended)
+
+Set the environment variable in your shell:
+
+```bash
+export USPTO_API_KEY="your_api_key_here"
+```
+
+Then use it in your Python code:
+
+```python
+from pyUSPTO import (
+ BulkDataClient,
+ PatentDataClient,
+ FinalPetitionDecisionsClient,
+ PTABTrialsClient,
+ PTABAppealsClient,
+ PTABInterferencesClient
)
+from pyUSPTO.config import USPTOConfig
+
+# Load configuration from environment
+config = USPTOConfig.from_env()
+
patent_client = PatentDataClient(config=config)
+bulk_client = BulkDataClient(config=config)
petition_client = FinalPetitionDecisionsClient(config=config)
+trials_client = PTABTrialsClient(config=config)
+appeals_client = PTABAppealsClient(config=config)
+interferences_client = PTABInterferencesClient(config=config)
+```
+
+## API Usage Examples
+
+### Patent Data API
+
+```python
+# Search for applications by inventor name
+inventor_search = patent_client.search_applications(inventor_name_q="Smith")
+print(f"Found {inventor_search.count} applications with 'Smith' as inventor")
+# > Found 104926 applications with 'Smith' as inventor.
+```
+
+### Final Petition Decisions API
+
+```python
+# Search for petition decisions by date range
+decisions = petition_client.search_decisions(
+ decision_date_from_q="2023-01-01",
+ limit=10
+)
+print(f"Found {decisions.count} petition decisions since 2023")
+
+# Get a specific decision by ID
+decision = petition_client.get_decision_by_id("decision_id_here")
+print(f"Decision Type: {decision.decision_type_code}")
+print(f"Application: {decision.application_number_text}")
+```
+
+### PTAB (Patent Trial and Appeal Board) APIs
+
+The package provides three clients for accessing PTAB data:
+
+#### PTAB Trials API
+
+```python
+from pyUSPTO import PTABTrialsClient
+
+# Initialize client
+trials_client = PTABTrialsClient(api_key="your_api_key_here")
+
+# Search for IPR trial proceedings
+proceedings = trials_client.search_proceedings(
+ trial_type_code_q="IPR",
+ trial_status_category_q="Instituted",
+ petition_filing_date_from_q="2023-01-01",
+ limit=10
+)
+print(f"Found {proceedings.count} instituted IPR proceedings")
+
+# Search for trial documents with new convenience parameters
+documents = trials_client.search_documents(
+ trial_number_q="IPR2023-00001",
+ petitioner_party_name_q="Acme Corp",
+ patent_owner_name_q="XYZ Inc",
+ limit=5
+)
+
+# Search for trial decisions
+decisions = trials_client.search_decisions(
+ trial_type_code_q="IPR",
+ decision_type_category_q="Final Written Decision",
+ patent_number_q="US1234567",
+ decision_date_from_q="2023-01-01"
+)
+
+# Paginate through proceedings
+for proceeding in trials_client.paginate_proceedings(trial_type_code_q="IPR", limit=25):
+ print(f"Trial: {proceeding.trial_number}")
+```
+
+#### PTAB Appeals API
+
+```python
+from pyUSPTO import PTABAppealsClient
-# Method 3: Using environment variables (recommended for production)
-import os
-os.environ["USPTO_API_KEY"] = "your_api_key_here"
-config_from_env = USPTOConfig.from_env()
-patent_client = PatentDataClient(config=config_from_env)
-petition_client = FinalPetitionDecisionsClient(config=config_from_env)
+# Initialize client
+appeals_client = PTABAppealsClient(api_key="your_api_key_here")
+
+# Search for appeal decisions by technology center
+decisions = appeals_client.search_decisions(
+ technology_center_number_q="3600",
+ decision_type_category_q="Affirmed",
+ decision_date_from_q="2023-01-01",
+ limit=10
+)
+print(f"Found {decisions.count} affirmed decisions from TC 3600")
+
+# Search by application number
+decisions = appeals_client.search_decisions(
+ application_number_text_q="15/123456",
+ limit=5
+)
+
+# Paginate through decisions
+for decision in appeals_client.paginate_decisions(
+ technology_center_number_q="2100",
+ limit=25
+):
+ print(f"Appeal: {decision.appeal_number}")
```
+#### PTAB Interferences API
+
+```python
+from pyUSPTO import PTABInterferencesClient
+
+# Initialize client
+interferences_client = PTABInterferencesClient(api_key="your_api_key_here")
+
+# Search for interference decisions by outcome
+decisions = interferences_client.search_decisions(
+ interference_outcome_category_q="Priority to Senior Party",
+ decision_date_from_q="2022-01-01",
+ limit=10
+)
+print(f"Found {decisions.count} decisions awarding priority to senior party")
+
+# Search by party name
+decisions = interferences_client.search_decisions(
+ senior_party_name_q="Example Corp",
+ junior_party_name_q="Test Inc",
+ limit=5
+)
+
+# Paginate through decisions
+for decision in interferences_client.paginate_decisions(
+ decision_type_category_q="Final Decision",
+ limit=25
+):
+ print(f"Interference: {decision.interference_number}")
+```
+
+## Documentation
+
+Full documentation may be found on [Read the Docs](https://pyuspto.readthedocs.io/).
+
+## Advanced Topics
+
### Advanced HTTP Configuration
Control timeout behavior, retry logic, and connection pooling using `HTTPConfig`:
@@ -126,36 +316,11 @@ patent_client = PatentDataClient(config=patent_config)
petition_client = FinalPetitionDecisionsClient(config=petition_config)
```
-### Patent Data API
-
-```python
-# Search for applications by inventor name
-inventor_search = patent_client.search_applications(inventor_name_q="Smith")
-print(f"Found {inventor_search.count} applications with 'Smith' as inventor")
-# > Found 104926 applications with 'Smith' as inventor.
-```
-
-### Final Petition Decisions API
-
-```python
-# Search for petition decisions by date range
-decisions = petition_client.search_decisions(
- decision_date_from_q="2023-01-01",
- limit=10
-)
-print(f"Found {decisions.count} petition decisions since 2023")
-
-# Get a specific decision by ID
-decision = petition_client.get_decision_by_id("decision_id_here")
-print(f"Decision Type: {decision.decision_type_code}")
-print(f"Application: {decision.application_number_text}")
-```
-
-## Warning Control
+### Warning Control
The library uses Python's standard `warnings` module to report data parsing issues. This allows you to control how warnings are handled based on your needs.
-### Warning Categories
+**Warning Categories**
All warnings inherit from `USPTODataWarning`:
@@ -164,7 +329,7 @@ All warnings inherit from `USPTODataWarning`:
- `USPTOTimezoneWarning`: Timezone-related issues
- `USPTOEnumParseWarning`: Enum value parsing failures
-### Controlling Warnings
+**Controlling Warnings**
```python
import warnings
@@ -194,22 +359,9 @@ warnings.filterwarnings('always', category=USPTODataWarning)
The library's permissive parsing philosophy returns `None` for fields that cannot be parsed, allowing you to retrieve partial data even when some fields have issues. Warnings inform you when this happens without stopping execution.
-## Features
+## Data Models
-- Access to USPTO Bulk Data API, Patent Data API, and Final Petition Decisions API
-- Search for patent applications using various filters
-- Search and retrieve petition decisions with detailed information
-- Download files, documents, and petition decision documents from the APIs
-- Pagination support for large result sets
-- Full type annotations and comprehensive test coverage
-
-## Documentation
-
-Full documentation may be found on [Read the Docs](https://pyuspto.readthedocs.io/).
-
-### Data Models
-
-The library uses Python dataclasses to represent API responses. All data models include type annotations for attributes and methods, making them fully compatible with static type checkers.
+The library uses Python dataclasses to represent API responses. All data models include type annotations for attributes and methods, making them fully compatible with static type checkers.
#### Bulk Data API
@@ -239,6 +391,31 @@ The library uses Python dataclasses to represent API responses. All data models
- `DecisionTypeCode`: Enum for petition decision types
- `DocumentDirectionCategory`: Enum for document direction categories
+#### PTAB Trials API
+
+- `PTABTrialProceedingResponse`: Top-level response from the API
+- `PTABTrialProceeding`: Information about a PTAB trial proceeding (IPR, PGR, CBM, DER)
+- `PTABTrialDocument`: Document associated with a trial proceeding
+- `PTABTrialDecision`: Decision information for a trial proceeding
+- `RegularPetitionerData`, `RespondentData`, `DerivationPetitionerData`: Party data for different trial types
+- `PTABTrialMetaData`: Trial metadata and status information
+
+#### PTAB Appeals API
+
+- `PTABAppealResponse`: Top-level response from the API
+- `PTABAppealDecision`: Ex parte appeal decision information
+- `AppellantData`: Appellant information and application details
+- `PTABAppealMetaData`: Appeal metadata and filing information
+- `PTABAppealDocumentData`: Document and decision details
+
+#### PTAB Interferences API
+
+- `PTABInterferenceResponse`: Top-level response from the API
+- `PTABInterferenceDecision`: Interference proceeding decision information
+- `SeniorPartyData`, `JuniorPartyData`, `AdditionalPartyData`: Party data classes
+- `PTABInterferenceMetaData`: Interference metadata and status information
+- `PTABInterferenceDocumentData`: Document and outcome details
+
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo
index 4c27933..26e37b3 100644
--- a/docs/build/html/.buildinfo
+++ b/docs/build/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 41a512eb26efab7f3a442ee890fc7d4c
+config: c43a1a90993d7b5542e6aff794c219e2
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/build/html/.doctrees/api/clients.doctree b/docs/build/html/.doctrees/api/clients.doctree
index 788a88b..75ed98f 100644
Binary files a/docs/build/html/.doctrees/api/clients.doctree and b/docs/build/html/.doctrees/api/clients.doctree differ
diff --git a/docs/build/html/.doctrees/api/config.doctree b/docs/build/html/.doctrees/api/config.doctree
index a44ac64..f9f064c 100644
Binary files a/docs/build/html/.doctrees/api/config.doctree and b/docs/build/html/.doctrees/api/config.doctree differ
diff --git a/docs/build/html/.doctrees/api/exceptions.doctree b/docs/build/html/.doctrees/api/exceptions.doctree
index 8406a62..1aee399 100644
Binary files a/docs/build/html/.doctrees/api/exceptions.doctree and b/docs/build/html/.doctrees/api/exceptions.doctree differ
diff --git a/docs/build/html/.doctrees/api/index.doctree b/docs/build/html/.doctrees/api/index.doctree
index b4d61c9..c6da02c 100644
Binary files a/docs/build/html/.doctrees/api/index.doctree and b/docs/build/html/.doctrees/api/index.doctree differ
diff --git a/docs/build/html/.doctrees/api/models.doctree b/docs/build/html/.doctrees/api/models.doctree
index f7a7e17..53ca05f 100644
Binary files a/docs/build/html/.doctrees/api/models.doctree and b/docs/build/html/.doctrees/api/models.doctree differ
diff --git a/docs/build/html/.doctrees/api/warnings.doctree b/docs/build/html/.doctrees/api/warnings.doctree
new file mode 100644
index 0000000..a1550f5
Binary files /dev/null and b/docs/build/html/.doctrees/api/warnings.doctree differ
diff --git a/docs/build/html/.doctrees/development.doctree b/docs/build/html/.doctrees/development.doctree
index 8977260..74403b2 100644
Binary files a/docs/build/html/.doctrees/development.doctree and b/docs/build/html/.doctrees/development.doctree differ
diff --git a/docs/build/html/.doctrees/environment.pickle b/docs/build/html/.doctrees/environment.pickle
index 887d8d4..a7778b4 100644
Binary files a/docs/build/html/.doctrees/environment.pickle and b/docs/build/html/.doctrees/environment.pickle differ
diff --git a/docs/build/html/.doctrees/examples/bulk_data.doctree b/docs/build/html/.doctrees/examples/bulk_data.doctree
index 622021a..e821a0c 100644
Binary files a/docs/build/html/.doctrees/examples/bulk_data.doctree and b/docs/build/html/.doctrees/examples/bulk_data.doctree differ
diff --git a/docs/build/html/.doctrees/examples/ifw_example.doctree b/docs/build/html/.doctrees/examples/ifw_example.doctree
new file mode 100644
index 0000000..befdc27
Binary files /dev/null and b/docs/build/html/.doctrees/examples/ifw_example.doctree differ
diff --git a/docs/build/html/.doctrees/examples/index.doctree b/docs/build/html/.doctrees/examples/index.doctree
index 4522011..287181f 100644
Binary files a/docs/build/html/.doctrees/examples/index.doctree and b/docs/build/html/.doctrees/examples/index.doctree differ
diff --git a/docs/build/html/.doctrees/examples/patent_data.doctree b/docs/build/html/.doctrees/examples/patent_data.doctree
index d994ac9..00a19a6 100644
Binary files a/docs/build/html/.doctrees/examples/patent_data.doctree and b/docs/build/html/.doctrees/examples/patent_data.doctree differ
diff --git a/docs/build/html/.doctrees/examples/petition_decisions.doctree b/docs/build/html/.doctrees/examples/petition_decisions.doctree
new file mode 100644
index 0000000..da53a91
Binary files /dev/null and b/docs/build/html/.doctrees/examples/petition_decisions.doctree differ
diff --git a/docs/build/html/.doctrees/examples/ptab_appeals.doctree b/docs/build/html/.doctrees/examples/ptab_appeals.doctree
new file mode 100644
index 0000000..cd1ee50
Binary files /dev/null and b/docs/build/html/.doctrees/examples/ptab_appeals.doctree differ
diff --git a/docs/build/html/.doctrees/examples/ptab_interferences.doctree b/docs/build/html/.doctrees/examples/ptab_interferences.doctree
new file mode 100644
index 0000000..84b97a6
Binary files /dev/null and b/docs/build/html/.doctrees/examples/ptab_interferences.doctree differ
diff --git a/docs/build/html/.doctrees/examples/ptab_trials.doctree b/docs/build/html/.doctrees/examples/ptab_trials.doctree
new file mode 100644
index 0000000..66e2138
Binary files /dev/null and b/docs/build/html/.doctrees/examples/ptab_trials.doctree differ
diff --git a/docs/build/html/.doctrees/index.doctree b/docs/build/html/.doctrees/index.doctree
index 84e3817..cafb9f9 100644
Binary files a/docs/build/html/.doctrees/index.doctree and b/docs/build/html/.doctrees/index.doctree differ
diff --git a/docs/build/html/.doctrees/installation.doctree b/docs/build/html/.doctrees/installation.doctree
index 93a52fe..0725aca 100644
Binary files a/docs/build/html/.doctrees/installation.doctree and b/docs/build/html/.doctrees/installation.doctree differ
diff --git a/docs/build/html/.doctrees/quickstart.doctree b/docs/build/html/.doctrees/quickstart.doctree
index 0a4d996..aac4464 100644
Binary files a/docs/build/html/.doctrees/quickstart.doctree and b/docs/build/html/.doctrees/quickstart.doctree differ
diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html
index abd67e7..436801c 100644
--- a/docs/build/html/_modules/index.html
+++ b/docs/build/html/_modules/index.html
@@ -5,7 +5,7 @@
+[docs]
+ defsanitize_application_number(self,input_number:str)->str:
+"""Sanitize and validate a USPTO application number.
+
+ Application numbers are either:
+ - 8 digits (e.g., "16123456")
+ - Series code format: 2 digits + "/" + 6 digits (e.g., "08/123456")
+ - PCT format: "PCT/US2024/012345" → "PCTUS2412345"
+
+ This method removes common separators (commas, spaces) while preserving
+ the "/" in series code format. Args:
- api_key: Optional API key for authentication
- base_url: The base URL of the API, defaults to config.patent_data_base_url or "https://api.uspto.gov/api/v1/patent"
- config: Optional USPTOConfig instance
+ input_number: Raw application number input. May include commas,
+ spaces, or other formatting.
+
+ Returns:
+ str: Sanitized application number (either "NNNNNNNN" or "NN/NNNNNN").
+
+ Raises:
+ ValueError: If the format is invalid.
+
+ Examples:
+ >>> client.sanitize_application_number("16123456")
+ "16123456"
+ >>> client.sanitize_application_number("16,123,456")
+ "16123456"
+ >>> client.sanitize_application_number("08/123456")
+ "08/123456"
+ >>> client.sanitize_application_number("08/123,456")
+ "08/123456" """
- # Use config if provided, otherwise create default config
- self.config=configorUSPTOConfig(api_key=api_key)
+ ifnotinput_numberornotinput_number.strip():
+ raiseValueError("Application number cannot be empty")
- # Use provided API key or get from config
- api_key=api_keyorself.config.api_key
+ raw=input_number.strip()
- # Use provided base_url or get from config
- base_url=base_urlorself.config.patent_data_base_url
+ # --- NEW: Handle PCT formats ---
+ # Example: "PCT/US2024/012345" -> "PCTUS2412345"
+ ifraw.startswith("PCT"):
+ parts=raw.split("/")
+ iflen(parts)!=3:
+ raiseValueError(
+ f"Invalid PCT application format: {input_number}. "
+ "Expected PCT/CCYYYY/NNNNNN"
+ )
- super().__init__(api_key=api_key,base_url=base_url)
+ _,country_year,serial=parts
+ # country_year can be "US2024" or "US24"
+ country=country_year[:2]
-
-[docs]
- defget_patent_applications(
- self,params:Optional[Dict[str,Any]]=None
- )->PatentDataResponse:
-"""
- Get a list of patent applications using the search endpoint.
+ year_part=country_year[2:]
+ ifnotyear_part.isdigit():
+ raiseValueError(
+ f"Invalid PCT year in: {country_year}. Must be digits."
+ )
- Args:
- params: Optional query parameters including:
- - q: Search query string
- - sort: Field to sort by followed by sort order
- - offset: Position in dataset to start from
- - limit: Number of results to return
- - facets: List of fields to facet upon
- - fields: Fields to include in response
- - filters: Field filters
- - rangeFilters: Range filters
+ # Normalize:
+ # "2024" -> "24"
+ # "24" -> "24"
+ iflen(year_part)==4:
+ year=year_part[-2:]
+ eliflen(year_part)==2:
+ year=year_part
+ else:
+ raiseValueError(
+ f"Invalid PCT year length in: {country_year}. "
+ "Expected CCYYYY or CCYY."
+ )
- Returns:
- PatentDataResponse object containing the API response
- """
- result=self._make_request(
- method="GET",
- endpoint=self.ENDPOINTS["applications_search"],
- params=params,
- response_class=PatentDataResponse,
- )
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
+ # Serial must be digits only
+ ifnotserial.isdigit():
+ raiseValueError(f"Invalid PCT serial: {serial}. Must be numeric.")
+ returnf"PCT{country}{year}{serial}"
-
-[docs]
- defsearch_patent_applications_post(
- self,search_request:Dict[str,Any]
- )->PatentDataResponse:
-"""
- Search patent applications using POST method with JSON payload.
+ # Strip whitespace and remove commas/spaces
+ cleaned=raw.replace(",","").replace(" ","")
- Args:
- search_request: JSON payload with search parameters including:
- - q: Search query string
- - filters: Array of filter objects
- - rangeFilters: Array of range filter objects
- - sort: Array of sort objects
- - fields: Array of field names to include
- - pagination: Pagination object
- - facets: Array of facet field names
+ # Check if this is series code format (NN/NNNNNN)
+ if"/"incleaned:
+ parts=cleaned.split("/")
+ iflen(parts)!=2:
+ raiseValueError(
+ f"Invalid application number format: {input_number}. "
+ "Expected format: NNNNNNNN or NN/NNNNNN"
+ )
- Returns:
- PatentDataResponse object containing the API response
- """
- result=self._make_request(
- method="POST",
- endpoint=self.ENDPOINTS["applications_search"],
- json_data=search_request,
- response_class=PatentDataResponse,
- )
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
+ series,serial=parts
+ ifnotseries.isdigit()ornotserial.isdigit():
+ raiseValueError(
+ f"Invalid application number format: {input_number}. "
+ "Series and serial must be numeric."
+ )
+ iflen(series)!=2orlen(serial)!=6:
+ raiseValueError(
+ f"Invalid application number format: {input_number}. "
+ "Expected series code format: NN/NNNNNN (2 digits / 6 digits)"
+ )
-
-[docs]
- defdownload_patent_applications(
- self,params:Optional[Dict[str,Any]]=None,format:str="json"
- )->PatentDataResponse:
-"""
- Download patent data with specified format.
+ returncleaned
- Args:
- params: Optional query parameters
- format: Download format (json or csv)
+ # Standard 8-digit format
+ ifnotcleaned.isdigit():
+ raiseValueError(
+ f"Invalid application number format: {input_number}. "
+ "Must contain only digits."
+ )
- Returns:
- PatentDataResponse object containing the API response
+ iflen(cleaned)!=8:
+ raiseValueError(
+ f"Invalid application number format: {input_number}. "
+ "Expected 8 digits."
+ )
+
+ returncleaned
+
+
+ def_get_wrapper_from_response(
+ self,
+ response_data:PatentDataResponse,
+ application_number_for_validation:Optional[str]=None,
+ )->Optional[PatentFileWrapper]:
+"""Helper to extract a single PatentFileWrapper, optionally validating the app number."""
+ ifnotresponse_dataornotresponse_data.patent_file_wrapper_data_bag:
+ returnNone
+
+ wrapper=response_data.patent_file_wrapper_data_bag[0]
+
+ if(
+ application_number_for_validation
+ andwrapper.application_number_text
+ !=self.sanitize_application_number(application_number_for_validation)
+ ):
+ warnings.warn(
+ f"API returned application number '{wrapper.application_number_text}' "
+ f"but requested '{application_number_for_validation}'. "
+ f"This may indicate an API data inconsistency.",
+ USPTODataMismatchWarning,
+ stacklevel=2,
+ )
+ returnwrapper
+
+
+[docs]
+ defget_application_by_number(self,application_number:str
- )->PatentFileWrapper:
-"""
- Get a specific patent by application number.
+ )->Optional[PatentFileWrapper]:
+"""Retrieves the full details for a specific patent application by its number.
+
+ This method fetches comprehensive information for a single patent application
+ identified by its unique application number. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for the patent
+ application (e.g., "16123456" or "18/915,708"). The application
+ number will be automatically sanitized to remove commas and spaces. Returns:
- PatentFileWrapper object containing the patent data
+ Optional[PatentFileWrapper]: A `PatentFileWrapper` object representing
+ the complete file wrapper for the application if found. This object
+ contains all data sections related to the application, such as
+ metadata, addresses, assignments, attorney/agent data, continuity
+ data, PTA/PTE data, transactions, and associated documents.
+ Returns None if the application cannot be found or if the response
+ does not contain the expected data. """
- endpoint=self.ENDPOINTS["application_by_number"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_by_number"].format(
+ application_number=self.sanitize_application_number(application_number))
- data=self._make_request(method="GET",endpoint=endpoint)
-
- # Handling different response formats
- ifisinstance(data,dict):
- if"patentFileWrapperDataBag"indata:
- forwrapperindata["patentFileWrapperDataBag"]:
- ifwrapper.get("applicationNumberText")==application_number:
- returnPatentFileWrapper.from_dict(wrapper)
- raiseValueError(
- f"Patent with application number {application_number} not found in response"
- )
- else:
- # If response doesn't contain patentFileWrapperDataBag, assume it's a direct PatentFileWrapper
- returnPatentFileWrapper.from_dict(data)
- elifisinstance(data,PatentFileWrapper):
- returndata
- else:
- raiseTypeError(f"Unexpected response type: {type(data)}")
[docs]
- defget_application_metadata(self,application_number:str)->PatentDataResponse:
-"""
- Get metadata for a specific patent application.
+ defget_application_metadata(
+ self,application_number:str
+ )->Optional[ApplicationMetaData]:
+"""Retrieves key metadata for a specific patent application.
+
+ This method fetches the `ApplicationMetaData` component from the full
+ patent file wrapper. The metadata includes a wide range of information
+ such as application status, important dates (filing, grant, publication),
+ applicant and inventor details, classification data, and other core
+ identifying information for the application. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ metadata is being requested (e.g., "16123456" or "18/915,708").
+ The application number will be automatically sanitized. Returns:
- PatentDataResponse object containing the application metadata
+ Optional[ApplicationMetaData]: An `ApplicationMetaData` object
+ containing the core details of the patent application if found.
+ Returns None if the application cannot be found or if metadata
+ is not available in the response. """
- endpoint=self.ENDPOINTS["application_metadata"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_metadata"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]
- defget_application_adjustment(self,application_number:str)->PatentDataResponse:
-"""
- Get patent term adjustment data for an application.
+ defget_application_adjustment(
+ self,application_number:str
+ )->Optional[PatentTermAdjustmentData]:
+"""Retrieves patent term adjustment (PTA) data for a specific application.
+
+ This method fetches the `PatentTermAdjustmentData` component from the
+ full patent file wrapper. This data includes details on various delay
+ quantities (e.g., A, B, C delays, applicant delays), the total
+ calculated adjustment, and a history of PTA events that influenced the
+ term. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which PTA
+ data is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the adjustment data
+ Optional[PatentTermAdjustmentData]: A `PatentTermAdjustmentData`
+ object containing the PTA details if the application is found
+ and has such data. Returns None if the application cannot be
+ found or if PTA data is not available in the response. """
- endpoint=self.ENDPOINTS["application_adjustment"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_adjustment"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]
- defget_application_assignment(self,application_number:str)->PatentDataResponse:
-"""
- Get assignment data for an application.
+ defget_application_assignment(
+ self,application_number:str
+ )->Optional[List[Assignment]]:
+"""Retrieves a list of patent assignments for a specific application.
+
+ This method fetches the `assignment_bag` from the patent file wrapper,
+ which contains a list of `Assignment` objects. Each `Assignment` object
+ details an assignment including information such as reel and frame numbers,
+ recording dates, conveyance text, and details about the assignors and assignees. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ assignment data is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the assignment data
+ Optional[List[Assignment]]: A list of `Assignment` objects, each
+ representing a recorded assignment for the application. Returns
+ None if the application cannot be found, or if no assignment
+ data is available in the response. An empty list may be
+ returned if the application is found but has no recorded
+ assignments. """
- endpoint=self.ENDPOINTS["application_assignment"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_assignment"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]
- defget_application_attorney(self,application_number:str)->PatentDataResponse:
-"""
- Get attorney/agent data for an application.
+ defget_application_attorney(
+ self,application_number:str
+ )->Optional[RecordAttorney]:
+"""Retrieves data for the attorney(s) of record for a specific application.
+
+ This method fetches the `RecordAttorney` object associated with the
+ patent application. This object contains details about the attorney(s)
+ of record, including customer number correspondence data, power of attorney
+ information, and a list of listed attorneys. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ attorney data is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the attorney data
+ Optional[RecordAttorney]: A `RecordAttorney` object with details
+ about the attorney(s) of record if the application is found
+ and such data exists. Returns None if the application cannot
+ be found or if no attorney data is available in the response. """
- endpoint=self.ENDPOINTS["application_attorney"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_attorney"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]
- defget_application_continuity(self,application_number:str)->PatentDataResponse:
-"""
- Get continuity data for an application.
+ defget_application_continuity(
+ self,application_number:str
+ )->Optional[ApplicationContinuityData]:
+"""Retrieves continuity data (parent/child applications) for a specific application.
+
+ This method fetches the lineage of the specified application, returning an
+ `ApplicationContinuityData` object. This object consolidates lists of
+ `ParentContinuity` (applications to which the current one claims priority)
+ and `ChildContinuity` (applications claiming priority to the current one)
+ objects, each detailing the related application's key identifiers and status. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ continuity data is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the continuity data
+ Optional[ApplicationContinuityData]: An `ApplicationContinuityData`
+ object containing lists of parent and child continuity relationships.
+ Returns None if the application cannot be found or if the underlying
+ data to construct continuity is not available. The lists within
+ the returned object may be empty if no parent or child continuity
+ links exist. """
- endpoint=self.ENDPOINTS["application_continuity"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_continuity"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]defget_application_foreign_priority(self,application_number:str
- )->PatentDataResponse:
-"""
- Get foreign priority data for an application.
+ )->Optional[List[ForeignPriority]]:
+"""Retrieves a list of foreign priority claims for a specific application.
+
+ This method fetches the `foreign_priority_bag` from the patent file
+ wrapper. This bag contains a list of `ForeignPriority` objects, each
+ representing a claim to a foreign patent application's priority date.
+ Details include the IP office name, filing date, and application number
+ of the foreign priority application. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ foreign priority data is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the foreign priority data
+ Optional[List[ForeignPriority]]: A list of `ForeignPriority` objects,
+ each detailing a claimed foreign priority. Returns None if the
+ application cannot be found or if no foreign priority data is
+ available. An empty list may be returned if the application
+ is found but has no foreign priority claims. """
- endpoint=self.ENDPOINTS["application_foreign_priority"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_foreign_priority"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]defget_application_transactions(self,application_number:str
- )->PatentDataResponse:
-"""
- Get transaction data for an application.
+ )->Optional[List[EventData]]:
+"""Retrieves the transaction history (events) for a specific application.
+
+ This method fetches the `event_data_bag` from the patent file wrapper.
+ This bag contains a list of `EventData` objects, each representing a
+ single recorded event in the prosecution history of the patent application.
+ Events include details like an event code, a textual description, and
+ the date the event was recorded. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ transaction history is being requested (e.g., "16123456"). Returns:
- PatentDataResponse object containing the transaction data
+ Optional[List[EventData]]: A list of `EventData` objects, each
+ detailing a transaction or event in the application's history.
+ Returns None if the application cannot be found or if no
+ transaction data is available. An empty list may be returned if
+ the application is found but has no recorded transaction events. """
- endpoint=self.ENDPOINTS["application_transactions"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_transactions"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]
- defget_application_documents(self,application_number:str)->PatentDataResponse:
-"""
- Get document details for an application.
+ defget_application_documents(
+ self,
+ application_number:str,
+ document_codes:Optional[List[str]]=None,
+ official_date_from:Optional[str]=None,
+ official_date_to:Optional[str]=None,
+ )->DocumentBag:
+"""Retrieves metadata for documents associated with a specific application.
+
+ This method fetches a collection of document metadata related to the given
+ patent application. The result is a `DocumentBag` object, which is an
+ iterable collection of `Document` instances. Each `Document` object
+ contains metadata such as its identifier, official date, document code
+ and description, direction (incoming/outgoing), and available download
+ formats. Args:
- application_number: The application number
+ application_number (str): The USPTO application number for which
+ document metadata is being requested (e.g., "16123456").
+ document_codes (Optional[List[str]]): Filter by specific document type
+ codes. If provided, only documents with these codes will be returned.
+ Examples: ['ABST', 'CLM', 'SPEC', 'DRWD'].
+ official_date_from (Optional[str]): Filter documents from this date
+ (inclusive). Date format: YYYY-MM-DD (e.g., "2020-01-15").
+ official_date_to (Optional[str]): Filter documents to this date
+ (inclusive). Date format: YYYY-MM-DD (e.g., "2023-12-31"). Returns:
- PatentDataResponse object containing document details
+ DocumentBag: A `DocumentBag` object containing metadata for all
+ publicly available documents associated with the application
+ that match the provided filters. The bag will be empty if no
+ documents are found or if the API response indicates no documents.
+ It does not return None for "not found" cases; an empty collection
+ is returned instead. """
- endpoint=self.ENDPOINTS["application_documents"].format(
- application_number=application_number
+ endpoint=self.ENDPOINTS["get_application_documents"].format(
+ application_number=self.sanitize_application_number(application_number))
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
+
+ params={}
+ ifdocument_codes:
+ params["documentCodes"]=",".join(document_codes)
+ ifofficial_date_from:
+ params["officialDateFrom"]=official_date_from
+ ifofficial_date_to:
+ params["officialDateTo"]=official_date_to
+
+ result_dict=self._make_request(
+ method="GET",endpoint=endpoint,params=paramsifparamselseNone)
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
[docs]defget_application_associated_documents(self,application_number:str
- )->PatentDataResponse:
-"""
- Get associated documents metadata for an application.
+ )->Optional[PrintedPublication]:
+"""Retrieves metadata for Pre-Grant Publication and Grant documents.
- Args:
- application_number: The application number
-
- Returns:
- PatentDataResponse object containing the associated documents metadata
- """
- endpoint=self.ENDPOINTS["application_associated_documents"].format(
- application_number=application_number
- )
- result=self._make_request(
- method="GET",
- endpoint=endpoint,
- response_class=PatentDataResponse,
- )
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
-
-
-
-[docs]
- defdownload_application_document(
- self,application_number:str,document_id:str,destination:str
- )->str:
-"""
- Download a document for a patent application.
+ This method fetches metadata specifically for published documents associated
+ with the patent application, such as Pre-Grant Publications (PGPUBs)
+ and granted patent documents. It does not retrieve the prosecution
+ history documents (see `get_application_documents` for that).
+ The result is a `PrintedPublication` object, which holds
+ `PrintedMetaData` including file URIs and names. Download with download_archive. Args:
- application_number: The application number
- document_id: The document identifier
- destination: Directory where the file should be saved
+ application_number (str): The USPTO application number for which
+ associated PGPUB/Grant document metadata is being requested
+ (e.g., "16123456"). Returns:
- Path to the downloaded file
+ Optional[PrintedPublication]: A `PrintedPublication` object
+ containing `PrintedMetaData` for the Pre-Grant Publication
+ and/or the Grant document, if available. Returns None if the
+ application cannot be found or if no such associated document
+ metadata is available. The fields within the returned object
+ (`pgpub_document_meta_data`, `grant_document_meta_data`)
+ may themselves be None if a particular type of document
+ (e.g., PGPUB) does not exist for the application. """
- # This endpoint is at a different base URL level
- base_url_parts=self.base_url.split("/patent")
- base_url_root=base_url_parts[0]
- endpoint=self.ENDPOINTS["download_document"].format(
- application_number=application_number,document_id=document_id
+ endpoint=self.ENDPOINTS["get_application_associated_documents"].format(
+ application_number=self.sanitize_application_number(application_number))
-
- # Get the response with streaming enabled
- response=self._make_request(
- method="GET",endpoint=endpoint,stream=True,custom_base_url=base_url_root
+ response_data=self._make_request(
+ method="GET",endpoint=endpoint,response_class=PatentDataResponse)
+ assertisinstance(response_data,PatentDataResponse)
+ wrapper=self._get_wrapper_from_response(response_data,application_number)
+ returnPrintedPublication.from_wrapper(wrapper)ifwrapperelseNone
- # Ensure we have a Response object with iter_content
- importrequests
- ifnotisinstance(response,requests.Response):
- raiseTypeError("Expected a Response object for streaming download")
+
+[docs]
+ defpaginate_applications(self,**kwargs:Any)->Iterator[PatentFileWrapper]:
+"""Provides an iterator to easily paginate through patent application search results.
- ifnotos.path.exists(destination):
- os.makedirs(destination)
+ This method simplifies the process of fetching all patent applications
+ that match a given search query by automatically handling pagination.
+ It internally calls the `search_applications` method for GET requests,
+ batching results and yielding them one by one.
- # Get filename from Content-Disposition header if available
- content_disposition=response.headers.get("Content-Disposition")
- ifcontent_dispositionand"filename="incontent_disposition:
- filename_match=re.search(r'filename="(.+?)"',content_disposition)
- iffilename_match:
- filename=filename_match.group(1)
- else:
- filename=document_id
- else:
- filename=document_id
-
- file_path=os.path.join(destination,filename)
-
- withopen(file=file_path,mode="wb")asf:
- forchunkinresponse.iter_content(chunk_size=8192):
- f.write(chunk)
-
- returnfile_path
-
-
-
-[docs]
- defpaginate_patents(self,**kwargs:Any)->Iterator[PatentFileWrapper]:
-"""
- Paginate through all patents matching the search criteria.
+ All keyword arguments provided to this method (`**kwargs`) are passed
+ directly to the `search_applications` method to define the search
+ criteria. See the `search_applications` method for more details
+ on available parameters.
+ The `offset` and `limit` parameters are managed by the pagination logic;
+ setting them directly in `kwargs` might lead to unexpected behavior. Args:
- **kwargs: Keyword arguments to pass to search_patents
+ **kwargs (Any): Keyword arguments to be passed to the
+ `search_applications` method for constructing the search query.
+ These define the criteria for the patent applications to be
+ retrieved. Do not include `post_body`.
+
+ Returns:
+ Iterator[PatentFileWrapper]: An iterator that yields `PatentFileWrapper`
+ objects, allowing iteration over all matching patent applications
+ across multiple pages of results.
- Yields:
- PatentFileWrapper objects
+ Raises:
+ ValueError: If `post_body` is included in `kwargs`, as this
+ method only supports GET request parameters for pagination. """
+ if"post_body"inkwargs:
+ raiseValueError(
+ "paginate_applications uses GET requests and does not support 'post_body'. "
+ "Use keyword arguments for search criteria."
+ )
+
returnself.paginate_results(
- method_name="get_patent_applications",
+ method_name="search_applications",response_container_attr="patent_file_wrapper_data_bag",**kwargs,)
+[docs]
+ defget_status_codes(self,params:Optional[Dict[str,Any]]=None
- )->Dict[str,Any]:
-"""
- Get patent application status codes and descriptions.
+ )->StatusCodeSearchResponse:
+"""Retrieves USPTO patent application status codes and their descriptions.
+
+ This method fetches a list of defined USPTO patent application status codes
+ (e.g., codes for "Pending," "Abandoned," "Issued") using a GET request.
+ The request can be customized with query parameters to filter or paginate
+ the results if supported by the API endpoint. Args:
- params: Optional query parameters including:
- - q: Search query string
- - offset: Position in dataset to start from
- - limit: Number of results to return
+ params (Optional[Dict[str, Any]], optional): A dictionary of query
+ parameters to be sent with the GET request. These parameters can
+ be used to filter or control the output of the status codes
+ list. Defaults to None, which typically retrieves all available
+ status codes or the API's default set. Returns:
- Dictionary containing status codes and descriptions
+ StatusCodeSearchResponse: An object containing a count of matching
+ status codes, a `StatusCodeCollection` of the `StatusCode`
+ objects (code and description), and a request identifier. """
- result=self._make_request(
+ result_dict=self._make_request(method="GET",endpoint=self.ENDPOINTS["status_codes"],params=params)
- assertisinstance(result,dict)
- returnresult
+[docs]
+ defsearch_status_codes(self,search_request:Dict[str,Any]
- )->Dict[str,Any]:
-"""
- Search patent status codes using POST method with JSON payload.
+ )->StatusCodeSearchResponse:
+"""Searches USPTO patent application status codes using POST criteria.
+
+ Performs targeted searches for USPTO patent application status codes
+ (e.g., for "Pending," "Abandoned," "Issued") by sending a POST request
+ with a JSON body containing the `search_request` criteria. This method
+ is suited for more complex queries than the GET-based `get_status_codes`. Args:
- search_request: JSON payload with search parameters
+ search_request (Dict[str, Any]): A dictionary with search criteria,
+ sent as the JSON POST body. The structure must conform to USPTO
+ API requirements for this endpoint (e.g., for searching by code
+ or description keywords). Returns:
- Dictionary containing status codes and descriptions
+ StatusCodeSearchResponse: An object containing a count of matching
+ status codes, a `StatusCodeCollection` of the `StatusCode`
+ objects (code and description), and a request identifier. """
- result=self._make_request(
+ result_dict=self._make_request(method="POST",endpoint=self.ENDPOINTS["status_codes"],json_data=search_request,)
- assertisinstance(result,dict)
- returnresult
+[docs]
+ defdownload_document(
+ self,
+ document_format:DocumentFormat,
+ file_name:Optional[str]=None,
+ destination_path:Optional[str]=None,
+ overwrite:bool=False,
+ stream:bool=True,
+ )->str:
+"""Downloads a document in the specified format.
+
+ Args:
+ document_format: DocumentFormat object containing download URL and metadata
+ file_name: Optional filename. If not provided, extracted from URL
+ destination_path: Optional path - can be a directory OR a complete file path
+ overwrite: Whether to overwrite existing files. Default False
+ stream: Whether to stream the download. Default True for large files
+
+ Returns:
+ str: Path to the downloaded file
+
+ Raises:
+ ValueError: If document_format has no download URL
+ FileExistsError: If file exists and overwrite=False
+ """
+ # Validate we have a download URL
+ ifdocument_format.download_urlisNone:
+ raiseValueError("DocumentFormat must have a download_url")
+
+ # Get filename - either provided or extract from URL
+ iffile_nameisNone:
+ url_filename=document_format.download_url.split("/")[-1]
+ if"."inurl_filename:
+ file_name=url_filename
+ else:
+ extension=(
+ document_format.mime_type_identifier.lower()
+ ifdocument_format.mime_type_identifier
+ else"pdf"
+ )
+ file_name=f"document.{extension}"
+
+ # Determine final file path
+ ifdestination_pathisNone:
+ final_file_path=Path(file_name)
+ else:
+ # destination_path is ALWAYS treated as a directory path
+ destination_dir=Path(destination_path)
+ destination_dir.mkdir(parents=True,exist_ok=True)
+ final_file_path=destination_dir/file_name
+
+ # Download the file (overwrite check handled by base class)
+ returnself._download_file(
+ url=document_format.download_url,
+ file_path=final_file_path.as_posix(),
+ overwrite=overwrite,
+ )
+
+
+
+[docs]
+ defget_IFW_metadata(self,
- query:Optional[str]=None,application_number:Optional[str]=None,
+ publication_number:Optional[str]=None,patent_number:Optional[str]=None,
- inventor_name:Optional[str]=None,
- applicant_name:Optional[str]=None,
- assignee_name:Optional[str]=None,
- filing_date_from:Optional[str]=None,
- filing_date_to:Optional[str]=None,
- grant_date_from:Optional[str]=None,
- grant_date_to:Optional[str]=None,
- classification:Optional[str]=None,
- limit:Optional[int]=25,
- offset:Optional[int]=0,
- )->PatentDataResponse:
-"""
- Search for patents with various filters.
+ PCT_app_number:Optional[str]=None,
+ PCT_pub_number:Optional[str]=None,
+ )->Optional[PatentFileWrapper]:
+"""Retrieves complete patent file wrapper data using common identifiers.
+
+ This utility fetches the `PatentFileWrapper`, which contains comprehensive
+ IFW metadata, application details, and more. Provide only one
+ identifier if possible. If multiple are given, they are processed in the
+ order listed in the arguments, and the first successful match is returned. Args:
- query: Search text in all fields
- application_number: Filter by application number
- patent_number: Filter by patent number
- inventor_name: Filter by inventor name
- applicant_name: Filter by applicant name
- assignee_name: Filter by assignee name
- filing_date_from: Filter by filing date from (YYYY-MM-DD)
- filing_date_to: Filter by filing date to (YYYY-MM-DD)
- grant_date_from: Filter by grant date from (YYYY-MM-DD)
- grant_date_to: Filter by grant date to (YYYY-MM-DD)
- classification: Filter by CPC classification
- limit: Number of results to return (default 25)
- offset: Position in dataset to start from (default 0)
+ application_number (Optional[str], optional): USPTO application number
+ (e.g., "16123456"). Checked first (direct lookup).
+ patent_number (Optional[str], optional): USPTO patent number
+ (e.g., "11000000"). Checked second (uses search).
+ publication_number (Optional[str], optional): USPTO pre-grant
+ publication number (e.g., "20230123456"). Checked third (uses search).
+ PCT_app_number (Optional[str], optional): PCT application number.
+ Checked fourth (direct lookup, treated as USPTO app#).
+ PCT_pub_number (Optional[str], optional): PCT publication number
+ (e.g., "2023012345"). Checked fifth (uses search). Returns:
- PatentDataResponse object containing matching patents
+ Optional[PatentFileWrapper]: A `PatentFileWrapper` object with
+ comprehensive data if found using one of the identifiers,
+ otherwise None. """
- # Build the query string
- q_parts=[]ifapplication_number:
- q_parts.append(f"applicationNumberText:{application_number}")
-
+ returnself.get_application_by_number(application_number=application_number)ifpatent_number:
- q_parts.append(f"applicationMetaData.patentNumber:{patent_number}")
-
- ifinventor_name:
- q_parts.append(
- f"applicationMetaData.inventorBag.inventorNameText:{inventor_name}"
+ pdr=self.search_applications(patent_number_q=patent_number,limit=1)
+ ifpdr.patent_file_wrapper_data_bag:
+ returnpdr.patent_file_wrapper_data_bag[0]
+ ifpublication_number:
+ pdr=self.search_applications(
+ earliestPublicationNumber_q=publication_number,limit=1)
-
- ifapplicant_name:
- q_parts.append(f"applicationMetaData.firstApplicantName:{applicant_name}")
-
- ifassignee_name:
- q_parts.append(
- f"assignmentBag.assigneeBag.assigneeNameText:{assignee_name}"
+ ifpdr.patent_file_wrapper_data_bag:
+ returnpdr.patent_file_wrapper_data_bag[0]
+ ifPCT_app_number:
+ returnself.get_application_by_number(application_number=PCT_app_number)
+ ifPCT_pub_number:
+ pdr=self.search_applications(
+ pctPublicationNumber_q=PCT_pub_number,limit=1)
+ ifpdr.patent_file_wrapper_data_bag:
+ returnpdr.patent_file_wrapper_data_bag[0]
+ returnNone
- ifclassification:
- q_parts.append(f"applicationMetaData.cpcClassificationBag:{classification}")
- # Add date range filters
- range_filters=[]
+
+[docs]
+ defdownload_archive(
+ self,
+ printed_metadata:PrintedMetaData,
+ file_name:Optional[str]=None,
+ destination_path:Optional[str]=None,
+ overwrite:bool=False,
+ )->str:
+"""Downloads Printed Metadata (XML data). These are XML files of the patent as printed.
- iffiling_date_fromandfiling_date_to:
- range_filters.append(
- f"applicationMetaData.filingDate:[{filing_date_from} TO {filing_date_to}]"
- )
- eliffiling_date_from:
- range_filters.append(f"applicationMetaData.filingDate:>={filing_date_from}")
- eliffiling_date_to:
- range_filters.append(f"applicationMetaData.filingDate:<={filing_date_to}")
-
- ifgrant_date_fromandgrant_date_to:
- range_filters.append(
- f"applicationMetaData.grantDate:[{grant_date_from} TO {grant_date_to}]"
- )
- elifgrant_date_from:
- range_filters.append(f"applicationMetaData.grantDate:>={grant_date_from}")
- elifgrant_date_to:
- range_filters.append(f"applicationMetaData.grantDate:<={grant_date_to}")
+ Note:
+ See also `download_publication()` for a clearer method name with identical functionality.
+
+ Args:
+ printed_metadata: ArchiveMetaData object containing download URL and metadata
+ file_name: Optional filename. If not provided, uses xml_file_name from metadata
+ destination_path: Optional directory path to save the file
+ overwrite: Whether to overwrite existing files. Default False
- # Combine all query parts
- ifquery:
- q_parts.append(query)
+ Returns:
+ str: Path to the downloaded file
- q_parts.extend(range_filters)
+ Raises:
+ ValueError: If printed_metadata has no download URL
+ FileExistsError: If file exists and overwrite=False
+ """
+ # Validate we have a download URL
+ ifprinted_metadata.file_location_uriisNone:
+ raiseValueError("PrintedMetaData must have a file_location_uri")
+
+ # Get filename - either provided or from metadata
+ iffile_nameisNone:
+ ifprinted_metadata.xml_file_name:
+ file_name=printed_metadata.xml_file_name
+ else:
+ # Fallback: extract from URL
+ url_filename=printed_metadata.file_location_uri.split("/")[-1]
+ if"."inurl_filename:
+ file_name=url_filename
+ else:
+ # Last resort: use product identifier
+ product_id=printed_metadata.product_identifieror"patent_text"
+ file_name=f"{product_id}.xml"
+
+ # Determine final file path
+ ifdestination_pathisNone:
+ final_file_path=Path(file_name)
+ else:
+ destination_dir=Path(destination_path)
+ destination_dir.mkdir(parents=True,exist_ok=True)
+ final_file_path=destination_dir/file_name
+
+ # Check for existing file
+ iffinal_file_path.exists()andoverwriteisFalse:
+ raiseFileExistsError(
+ f"File already exists: {final_file_path}. Use overwrite=True to replace."
+ )
- # Build the final query string
- q=" AND ".join(q_parts)ifq_partselseNone
+ # Download the Printed Metadata
+ returnself._download_file(
+ url=printed_metadata.file_location_uri,file_path=final_file_path.as_posix()
+ )
- # Set up parameters
- params={}
- ifoffsetisnotNone:
- params["offset"]=str(offset)
+
+[docs]
+ defdownload_publication(
+ self,
+ printed_metadata:PrintedMetaData,
+ file_name:Optional[str]=None,
+ destination_path:Optional[str]=None,
+ overwrite:bool=False,
+ )->str:
+"""Download a publication XML file (grant or pre-grant publication).
- iflimitisnotNone:
- params["limit"]=str(limit)
+ This method downloads publication XML files from PrintedMetaData objects,
+ such as grant documents or pre-grant publications (pgpub). The filename
+ is automatically extracted from the metadata if not provided.
- ifq:
- params["q"]=q
+ Args:
+ printed_metadata: PrintedMetaData object containing the publication
+ download URL and filename information. Typically obtained from
+ `get_application_associated_documents()` or from PatentFileWrapper's
+ `grant_document_meta_data` or `pg_publication_document_meta_data`.
+ file_name: Optional custom filename. If not provided, uses the
+ `xml_file_name` from the metadata (e.g., "18915708_12307527.xml").
+ destination_path: Optional directory path where the file should be saved.
+ If not provided, saves to the current directory. The directory will
+ be created if it doesn't exist.
+ overwrite: Whether to overwrite an existing file at the destination.
+ Default is False, which raises FileExistsError if file exists.
- # Use the applications_search endpoint from ENDPOINTS
- result=self._make_request(
- method="GET",
- endpoint=self.ENDPOINTS["applications_search"],
- params=params,
- response_class=PatentDataResponse,
- )
- # Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
- assertisinstance(result,PatentDataResponse)
- returnresult
+ Returns:
+ str: Absolute path to the downloaded publication file.
+
+ Raises:
+ ValueError: If printed_metadata has no file_location_uri (download URL).
+ FileExistsError: If the file already exists and overwrite=False.
+
+ Examples:
+ Download grant XML to a specific directory (auto-filename):
+
+ >>> response = client.get_application_by_number("18/915,708")
+ >>> ifw = response
+ >>> grant_metadata = ifw.grant_document_meta_data
+ >>> path = client.download_publication(grant_metadata, destination_path="./downloads")
+ >>> print(path)
+ './downloads/18915708_12307527.xml'
+
+ Download pgpub XML with custom filename:
+
+ >>> pgpub_metadata = ifw.pg_publication_document_meta_data
+ >>> path = client.download_publication(
+ ... pgpub_metadata,
+ ... file_name="my_publication.xml",
+ ... destination_path="./downloads"
+ ... )
+ >>> print(path)
+ './downloads/my_publication.xml'
+
+ Download to current directory:
+
+ >>> path = client.download_publication(grant_metadata)
+ >>> print(path)
+ './18915708_12307527.xml'
+ """
+ returnself.download_archive(
+ printed_metadata=printed_metadata,
+ file_name=file_name,
+ destination_path=destination_path,
+ overwrite=overwrite,
+ )
Source code for pyUSPTO.clients.petition_decisions
+"""
+clients.petition_decisions - Client for USPTO Final Petition Decisions API
+
+This module provides a client for interacting with the USPTO Final Petition
+Decisions API. It allows you to search for and retrieve final agency petition
+decisions in publicly available patent applications and patents filed in 2001 or later.
+"""
+
+importwarnings
+frompathlibimportPath
+fromtypingimportAny,Dict,Iterator,List,Optional,Union
+
+importrequests
+
+frompyUSPTO.clients.baseimportBaseUSPTOClient
+frompyUSPTO.configimportUSPTOConfig
+frompyUSPTO.models.petition_decisionsimport(
+ DocumentDownloadOption,
+ PetitionDecision,
+ PetitionDecisionDownloadResponse,
+ PetitionDecisionResponse,
+)
+frompyUSPTO.warningsimportUSPTODataMismatchWarning
+
+
+
+[docs]
+classFinalPetitionDecisionsClient(BaseUSPTOClient[PetitionDecisionResponse]):
+"""Client for interacting with the USPTO Final Petition Decisions API.
+
+ This client provides methods to search for petition decisions, retrieve specific
+ decisions by ID, download decision data, and download associated documents.
+
+ Final petition decisions data are incrementally added to the USPTO Open Data Portal
+ on a monthly basis starting with data from 2022 and later.
+ """
+
+ ENDPOINTS={
+ "search_decisions":"api/v1/petition/decisions/search",
+ "get_decision_by_id":"api/v1/petition/decisions/{petitionDecisionRecordIdentifier}",
+ "download_decisions":"api/v1/petition/decisions/search/download",
+ }
+
+
+[docs]
+ def__init__(
+ self,
+ api_key:Optional[str]=None,
+ base_url:Optional[str]=None,
+ config:Optional[USPTOConfig]=None,
+ ):
+"""Initialize the FinalPetitionDecisionsClient.
+
+ Args:
+ api_key: Optional API key for authentication.
+ base_url: Optional base URL override for the API.
+ config: Optional USPTOConfig instance for configuration.
+ """
+ self.config=configorUSPTOConfig(api_key=api_key)
+ api_key_to_use=api_keyorself.config.api_key
+ effective_base_url=(
+ base_url
+ orself.config.petition_decisions_base_url
+ or"https://api.uspto.gov"
+ )
+ super().__init__(
+ api_key=api_key_to_use,base_url=effective_base_url,config=self.config
+ )
+
+
+ def_get_decision_from_response(
+ self,
+ response_data:PetitionDecisionResponse,
+ petition_decision_record_identifier_for_validation:Optional[str]=None,
+ )->Optional[PetitionDecision]:
+"""Helper to extract a single PetitionDecision from response.
+
+ Args:
+ response_data: The API response containing petition decisions.
+ petition_decision_record_identifier_for_validation: Optional identifier
+ to validate against the returned decision.
+
+ Returns:
+ Optional[PetitionDecision]: The first petition decision if found, None otherwise.
+ """
+ ifnotresponse_dataornotresponse_data.petition_decision_data_bag:
+ returnNone
+
+ decision=response_data.petition_decision_data_bag[0]
+
+ if(
+ petition_decision_record_identifier_for_validation
+ anddecision.petition_decision_record_identifier
+ !=petition_decision_record_identifier_for_validation
+ ):
+ warnings.warn(
+ f"API returned decision identifier '{decision.petition_decision_record_identifier}' "
+ f"but requested '{petition_decision_record_identifier_for_validation}'. "
+ f"This may indicate an API data inconsistency.",
+ USPTODataMismatchWarning,
+ stacklevel=2,
+ )
+ returndecision
+
+
+[docs]
+ defsearch_decisions(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ application_number_q:Optional[str]=None,
+ patent_number_q:Optional[str]=None,
+ inventor_name_q:Optional[str]=None,
+ applicant_name_q:Optional[str]=None,
+ invention_title_q:Optional[str]=None,
+ decision_type_code_q:Optional[str]=None,
+ decision_date_from_q:Optional[str]=None,
+ decision_date_to_q:Optional[str]=None,
+ petition_mail_date_from_q:Optional[str]=None,
+ petition_mail_date_to_q:Optional[str]=None,
+ technology_center_q:Optional[str]=None,
+ final_deciding_office_name_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PetitionDecisionResponse:
+"""Searches for final petition decisions.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ application_number_q: Filter by application number.
+ patent_number_q: Filter by patent number.
+ inventor_name_q: Filter by inventor name.
+ applicant_name_q: Filter by applicant name.
+ invention_title_q: Filter by invention title.
+ decision_type_code_q: Filter by decision type code.
+ decision_date_from_q: Filter decisions from this date (YYYY-MM-DD).
+ decision_date_to_q: Filter decisions to this date (YYYY-MM-DD).
+ petition_mail_date_from_q: Filter petition mail dates from (YYYY-MM-DD).
+ petition_mail_date_to_q: Filter petition mail dates to (YYYY-MM-DD).
+ technology_center_q: Filter by technology center.
+ final_deciding_office_name_q: Filter by deciding office name.
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PetitionDecisionResponse: Response containing matching petition decisions.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_decisions(query="applicationNumberText:17765301")
+
+ # Search with convenience parameters
+ >>> response = client.search_decisions(
+ ... applicant_name_q="ACME Corp",
+ ... decision_date_from_q="2022-01-01",
+ ... limit=50
+ ... )
+
+ # Search with POST body
+ >>> response = client.search_decisions(
+ ... post_body={"q": "technologyCenter:1700", "limit": 100}
+ ... )
+ """
+ endpoint=self.ENDPOINTS["search_decisions"]
+
+ ifpost_bodyisnotNone:
+ # POST request path
+ result=self._make_request(
+ method="POST",
+ endpoint=endpoint,
+ json_data=post_body,
+ params=additional_query_params,
+ response_class=PetitionDecisionResponse,
+ )
+ else:
+ # GET request path
+ params:Dict[str,Any]={}
+ final_q=query
+
+ # Build query from convenience parameters
+ iffinal_qisNone:
+ q_parts=[]
+ ifapplication_number_q:
+ q_parts.append(f"applicationNumberText:{application_number_q}")
+ ifpatent_number_q:
+ q_parts.append(f"patentNumber:{patent_number_q}")
+ ifinventor_name_q:
+ q_parts.append(f"inventorBag:{inventor_name_q}")
+ ifapplicant_name_q:
+ q_parts.append(f"firstApplicantName:{applicant_name_q}")
+ ifinvention_title_q:
+ q_parts.append(f"inventionTitle:{invention_title_q}")
+ ifdecision_type_code_q:
+ q_parts.append(f"decisionTypeCode:{decision_type_code_q}")
+ iftechnology_center_q:
+ q_parts.append(f"technologyCenter:{technology_center_q}")
+ iffinal_deciding_office_name_q:
+ q_parts.append(
+ f"finalDecidingOfficeName:{final_deciding_office_name_q}"
+ )
+
+ # Handle decision date range
+ ifdecision_date_from_qanddecision_date_to_q:
+ q_parts.append(
+ f"decisionDate:[{decision_date_from_q} TO {decision_date_to_q}]"
+ )
+ elifdecision_date_from_q:
+ q_parts.append(f"decisionDate:>={decision_date_from_q}")
+ elifdecision_date_to_q:
+ q_parts.append(f"decisionDate:<={decision_date_to_q}")
+
+ # Handle petition mail date range
+ ifpetition_mail_date_from_qandpetition_mail_date_to_q:
+ q_parts.append(
+ f"petitionMailDate:[{petition_mail_date_from_q} TO {petition_mail_date_to_q}]"
+ )
+ elifpetition_mail_date_from_q:
+ q_parts.append(f"petitionMailDate:>={petition_mail_date_from_q}")
+ elifpetition_mail_date_to_q:
+ q_parts.append(f"petitionMailDate:<={petition_mail_date_to_q}")
+
+ ifq_parts:
+ final_q=" AND ".join(q_parts)
+
+ # Add parameters
+ iffinal_qisnotNone:
+ params["q"]=final_q
+ ifsortisnotNone:
+ params["sort"]=sort
+ ifoffsetisnotNone:
+ params["offset"]=offset
+ iflimitisnotNone:
+ params["limit"]=limit
+ iffacetsisnotNone:
+ params["facets"]=facets
+ iffieldsisnotNone:
+ params["fields"]=fields
+ iffiltersisnotNone:
+ params["filters"]=filters
+ ifrange_filtersisnotNone:
+ params["rangeFilters"]=range_filters
+
+ ifadditional_query_params:
+ params.update(additional_query_params)
+
+ result=self._make_request(
+ method="GET",
+ endpoint=endpoint,
+ params=params,
+ response_class=PetitionDecisionResponse,
+ )
+
+ assertisinstance(result,PetitionDecisionResponse)
+ returnresult
+
+
+
+[docs]
+ defget_decision_by_id(
+ self,
+ petition_decision_record_identifier:str,
+ include_documents:Optional[bool]=None,
+ )->Optional[PetitionDecision]:
+"""Retrieves a specific petition decision by its record identifier.
+
+ Args:
+ petition_decision_record_identifier: The unique identifier for the petition
+ decision record (UUID format).
+ include_documents: Whether to include associated documents in the response.
+ If True, adds includeDocuments=true query parameter.
+
+ Returns:
+ Optional[PetitionDecision]: The petition decision if found, None otherwise.
+
+ Examples:
+ # Get decision without documents
+ >>> decision = client.get_decision_by_id(
+ ... "9f1a4a2b-eee1-58ec-a3aa-167c4075aed4"
+ ... )
+
+ # Get decision with documents
+ >>> decision = client.get_decision_by_id(
+ ... "34044333-4b40-515f-a684-2515325c57c5",
+ ... include_documents=True
+ ... )
+ """
+ endpoint=self.ENDPOINTS["get_decision_by_id"].format(
+ petitionDecisionRecordIdentifier=petition_decision_record_identifier
+ )
+
+ params={}
+ ifinclude_documentsisnotNone:
+ params["includeDocuments"]=str(include_documents).lower()
+
+ response_data=self._make_request(
+ method="GET",
+ endpoint=endpoint,
+ params=paramsifparamselseNone,
+ response_class=PetitionDecisionResponse,
+ )
+ assertisinstance(response_data,PetitionDecisionResponse)
+ returnself._get_decision_from_response(
+ response_data=response_data,
+ petition_decision_record_identifier_for_validation=petition_decision_record_identifier,
+ )
+
+
+
+[docs]
+ defdownload_decisions(
+ self,
+ format:str="json",
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=None,
+ limit:Optional[int]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ # Convenience query parameters
+ application_number_q:Optional[str]=None,
+ patent_number_q:Optional[str]=None,
+ inventor_name_q:Optional[str]=None,
+ applicant_name_q:Optional[str]=None,
+ decision_date_from_q:Optional[str]=None,
+ decision_date_to_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ # File save options (for CSV format)
+ file_name:Optional[str]=None,
+ destination_path:Optional[str]=None,
+ overwrite:bool=False,
+ )->Union[PetitionDecisionDownloadResponse,requests.Response,str]:
+"""Downloads petition decisions data in the specified format.
+
+ This endpoint is designed for bulk downloads of petition decisions data.
+ It supports JSON and CSV formats.
+
+ Args:
+ format: Download format, either "json" or "csv". Defaults to "json".
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ application_number_q: Filter by application number.
+ patent_number_q: Filter by patent number.
+ inventor_name_q: Filter by inventor name.
+ applicant_name_q: Filter by applicant name.
+ decision_date_from_q: Filter decisions from this date (YYYY-MM-DD).
+ decision_date_to_q: Filter decisions to this date (YYYY-MM-DD).
+ additional_query_params: Additional custom query parameters.
+ file_name: Optional filename for CSV downloads. Defaults to "petition_decisions.csv".
+ destination_path: Optional directory path to save CSV file. If None, returns Response.
+ overwrite: Whether to overwrite existing files. Default False.
+
+ Returns:
+ Union[PetitionDecisionDownloadResponse, requests.Response, str]:
+ - If format="json": Returns PetitionDecisionDownloadResponse
+ - If format="csv" and destination_path is None: Returns streaming Response
+ - If format="csv" and destination_path is set: Returns str path to saved file
+
+ Raises:
+ FileExistsError: If CSV file exists and overwrite=False
+
+ Examples:
+ # Download as JSON
+ >>> download = client.download_decisions(
+ ... format="json",
+ ... technology_center_q="1700",
+ ... limit=1000
+ ... )
+ >>> for decision in download.petition_decision_data:
+ ... print(decision.application_number_text)
+
+ # Download CSV and save to file
+ >>> file_path = client.download_decisions(
+ ... format="csv",
+ ... decision_date_from_q="2023-01-01",
+ ... destination_path="./downloads"
+ ... )
+ >>> print(f"Saved to: {file_path}")
+
+ # Download CSV as streaming response (advanced usage)
+ >>> response = client.download_decisions(format="csv")
+ >>> with open("decisions.csv", "wb") as f:
+ ... for chunk in response.iter_content(chunk_size=8192):
+ ... f.write(chunk)
+ """
+ endpoint=self.ENDPOINTS["download_decisions"]
+
+ params:Dict[str,Any]={"format":format}
+ final_q=query
+
+ # Build query from convenience parameters
+ iffinal_qisNone:
+ q_parts=[]
+ ifapplication_number_q:
+ q_parts.append(f"applicationNumberText:{application_number_q}")
+ ifpatent_number_q:
+ q_parts.append(f"patentNumber:{patent_number_q}")
+ ifinventor_name_q:
+ q_parts.append(f"inventorBag:{inventor_name_q}")
+ ifapplicant_name_q:
+ q_parts.append(f"firstApplicantName:{applicant_name_q}")
+
+ # Handle decision date range
+ ifdecision_date_from_qanddecision_date_to_q:
+ q_parts.append(
+ f"decisionDate:[{decision_date_from_q} TO {decision_date_to_q}]"
+ )
+ elifdecision_date_from_q:
+ q_parts.append(f"decisionDate:>={decision_date_from_q}")
+ elifdecision_date_to_q:
+ q_parts.append(f"decisionDate:<={decision_date_to_q}")
+
+ ifq_parts:
+ final_q=" AND ".join(q_parts)
+
+ # Add parameters
+ iffinal_qisnotNone:
+ params["q"]=final_q
+ ifsortisnotNone:
+ params["sort"]=sort
+ ifoffsetisnotNone:
+ params["offset"]=offset
+ iflimitisnotNone:
+ params["limit"]=limit
+ iffieldsisnotNone:
+ params["fields"]=fields
+ iffiltersisnotNone:
+ params["filters"]=filters
+ ifrange_filtersisnotNone:
+ params["rangeFilters"]=range_filters
+
+ ifadditional_query_params:
+ params.update(additional_query_params)
+
+ ifformat.lower()=="json":
+ # For JSON, parse the response
+ result_dict=self._make_request(
+ method="GET",endpoint=endpoint,params=params
+ )
+ assertisinstance(result_dict,dict)
+ returnPetitionDecisionDownloadResponse.from_dict(result_dict)
+ else:
+ # For CSV or other formats, get streaming response
+ result=self._make_request(
+ method="GET",endpoint=endpoint,params=params,stream=True
+ )
+ assertisinstance(result,requests.Response)
+
+ ifdestination_pathisnotNone:
+ # Save to file using the base class helper
+ frompathlibimportPath
+
+ # Determine filename
+ iffile_nameisNone:
+ file_name="petition_decisions.csv"
+
+ # Build full file path
+ destination_dir=Path(destination_path)
+ destination_dir.mkdir(parents=True,exist_ok=True)
+ final_file_path=destination_dir/file_name
+
+ # Save streaming response to file (overwrite check handled by base class)
+ returnself._save_response_to_file(
+ response=result,file_path=str(final_file_path),overwrite=overwrite
+ )
+ else:
+ # Return streaming response for manual handling
+ returnresult
+
+
+
+[docs]
+ defpaginate_decisions(self,**kwargs:Any)->Iterator[PetitionDecision]:
+"""Provides an iterator to paginate through petition decision search results.
+
+ This method simplifies fetching all petition decisions matching a search query
+ by automatically handling pagination. It internally calls the search_decisions
+ method for GET requests, batching results and yielding them one by one.
+
+ All keyword arguments are passed directly to search_decisions to define the
+ search criteria. The offset and limit parameters are managed by the pagination
+ logic; setting them directly in kwargs might lead to unexpected behavior.
+
+ Args:
+ **kwargs: Keyword arguments passed to search_decisions for constructing
+ the search query. Do not include post_body.
+
+ Returns:
+ Iterator[PetitionDecision]: An iterator yielding PetitionDecision objects,
+ allowing iteration over all matching petition decisions across multiple
+ pages of results.
+
+ Raises:
+ ValueError: If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+ Examples:
+ # Paginate through all decisions for a technology center
+ >>> for decision in client.paginate_decisions(technology_center_q="1700"):
+ ... print(f"{decision.application_number_text}: {decision.decision_type_code}")
+
+ # Paginate with date range
+ >>> for decision in client.paginate_decisions(
+ ... decision_date_from_q="2023-01-01",
+ ... decision_date_to_q="2023-12-31"
+ ... ):
+ ... process_decision(decision)
+ """
+ if"post_body"inkwargs:
+ raiseValueError(
+ "paginate_decisions uses GET requests and does not support 'post_body'. "
+ "Use keyword arguments for search criteria."
+ )
+
+ returnself.paginate_results(
+ method_name="search_decisions",
+ response_container_attr="petition_decision_data_bag",
+ **kwargs,
+ )
+
+
+
+[docs]
+ defdownload_petition_document(
+ self,
+ download_option:DocumentDownloadOption,
+ file_name:Optional[str]=None,
+ destination_path:Optional[str]=None,
+ overwrite:bool=False,
+ )->str:
+"""Downloads a petition decision document in the specified format.
+
+ Args:
+ download_option: DocumentDownloadOption object containing the download
+ URL and metadata.
+ file_name: Optional filename for the downloaded file. If not provided,
+ it will be extracted from the URL or generated based on the MIME type.
+ destination_path: Optional directory path where the file should be saved.
+ If not provided, saves to the current directory.
+ overwrite: Whether to overwrite an existing file. Defaults to False.
+
+ Returns:
+ str: The absolute path to the downloaded file.
+
+ Raises:
+ ValueError: If download_option has no download URL.
+ FileExistsError: If the file exists and overwrite=False.
+
+ Examples:
+ # Download first document from a decision
+ >>> decision = client.get_decision_by_id(
+ ... "34044333-4b40-515f-a684-2515325c57c5",
+ ... include_documents=True
+ ... )
+ >>> if decision.document_bag:
+ ... doc = decision.document_bag[0]
+ ... if doc.download_option_bag:
+ ... # Download PDF version
+ ... pdf_option = next(
+ ... opt for opt in doc.download_option_bag
+ ... if opt.mime_type_identifier == "PDF"
+ ... )
+ ... path = client.download_petition_document(
+ ... pdf_option,
+ ... destination_path="./downloads"
+ ... )
+ ... print(f"Downloaded to: {path}")
+ """
+ ifdownload_option.download_urlisNone:
+ raiseValueError("DocumentDownloadOption must have a download_url")
+
+ # Determine filename
+ iffile_nameisNone:
+ url_filename=download_option.download_url.split("/")[-1]
+ if"."inurl_filename:
+ file_name=url_filename
+ else:
+ # Generate filename from MIME type
+ extension=(
+ download_option.mime_type_identifier.lower()
+ ifdownload_option.mime_type_identifier
+ else"pdf"
+ )
+ file_name=f"document.{extension}"
+
+ # Determine final file path
+ ifdestination_pathisNone:
+ final_file_path=Path(file_name)
+ else:
+ destination_dir=Path(destination_path)
+ destination_dir.mkdir(parents=True,exist_ok=True)
+ final_file_path=destination_dir/file_name
+
+ # Check for existing file
+ iffinal_file_path.exists()andoverwriteisFalse:
+ raiseFileExistsError(
+ f"File already exists: {final_file_path}. Use overwrite=True to replace."
+ )
+
+ # Download the file
+ returnself._download_file(
+ url=download_option.download_url,file_path=final_file_path.as_posix()
+ )
+"""
+clients.ptab_appeals - Client for USPTO PTAB Appeals API
+
+This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Appeals API. It allows you to search for ex parte appeal decisions.
+"""
+
+fromtypingimportAny,Dict,Iterator,Optional
+
+frompyUSPTO.clients.baseimportBaseUSPTOClient
+frompyUSPTO.configimportUSPTOConfig
+frompyUSPTO.models.ptabimportPTABAppealDecision,PTABAppealResponse
+
+
+
+[docs]
+classPTABAppealsClient(BaseUSPTOClient[PTABAppealResponse]):
+"""Client for interacting with the USPTO PTAB Appeals API.
+
+ This client provides methods to search for ex parte appeal decisions from the
+ Patent Trial and Appeal Board.
+
+ Appeals data includes decisions on patent application appeals from the examiner
+ to the PTAB.
+ """
+
+ ENDPOINTS={
+ "search_decisions":"api/v1/patent/appeals/decisions/search",
+ }
+
+
+[docs]
+ def__init__(
+ self,
+ api_key:Optional[str]=None,
+ base_url:Optional[str]=None,
+ config:Optional[USPTOConfig]=None,
+ ):
+"""Initialize the PTABAppealsClient.
+
+ Args:
+ api_key: Optional API key for authentication.
+ base_url: Optional base URL override for the API.
+ config: Optional USPTOConfig instance for configuration.
+ """
+ self.config=configorUSPTOConfig(api_key=api_key)
+ api_key_to_use=api_keyorself.config.api_key
+ effective_base_url=(
+ base_urlorself.config.ptab_base_urlor"https://api.uspto.gov"
+ )
+ super().__init__(
+ api_key=api_key_to_use,base_url=effective_base_url,config=self.config
+ )
+
+
+
+[docs]
+ defsearch_decisions(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ appeal_number_q:Optional[str]=None,
+ application_number_text_q:Optional[str]=None,
+ appellant_name_q:Optional[str]=None,
+ requestor_name_q:Optional[str]=None,
+ decision_type_category_q:Optional[str]=None,
+ decision_date_from_q:Optional[str]=None,
+ decision_date_to_q:Optional[str]=None,
+ technology_center_number_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PTABAppealResponse:
+"""Searches for PTAB appeal decisions.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ appeal_number_q: Filter by appeal number.
+ application_number_text_q: Filter by application number.
+ appellant_name_q: Filter by appellant name.
+ requestor_name_q: Filter by requestor name.
+ decision_type_category_q: Filter by decision type category.
+ decision_date_from_q: Filter decisions from this date (YYYY-MM-DD).
+ decision_date_to_q: Filter decisions to this date (YYYY-MM-DD).
+ technology_center_number_q: Filter by technology center number.
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PTABAppealResponse: Response containing matching appeal decisions.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_decisions(query="appealNumber:2023-001234")
+
+ # Search with convenience parameters
+ >>> response = client.search_decisions(
+ ... technology_center_number_q="3600",
+ ... decision_date_from_q="2023-01-01",
+ ... limit=50
+ ... )
+
+ # Search with POST body
+ >>> response = client.search_decisions(
+ ... post_body={"q": "decisionTypeCategory:Affirmed", "limit": 100}
+ ... )
+ """
+ endpoint=self.ENDPOINTS["search_decisions"]
+
+ ifpost_bodyisnotNone:
+ # POST request path
+ result=self._make_request(
+ method="POST",
+ endpoint=endpoint,
+ json_data=post_body,
+ params=additional_query_params,
+ response_class=PTABAppealResponse,
+ )
+ else:
+ # GET request path
+ params:Dict[str,Any]={}
+ final_q=query
+
+ # Build query from convenience parameters
+ iffinal_qisNone:
+ q_parts=[]
+ ifappeal_number_q:
+ q_parts.append(f"appealNumber:{appeal_number_q}")
+ ifapplication_number_text_q:
+ q_parts.append(f"applicationNumberText:{application_number_text_q}")
+ ifappellant_name_q:
+ q_parts.append(f"appellantName:{appellant_name_q}")
+ ifrequestor_name_q:
+ q_parts.append(f"requestorName:{requestor_name_q}")
+ ifdecision_type_category_q:
+ q_parts.append(f"decisionTypeCategory:{decision_type_category_q}")
+ iftechnology_center_number_q:
+ q_parts.append(
+ f"technologyCenterNumber:{technology_center_number_q}"
+ )
+
+ # Handle decision date range
+ ifdecision_date_from_qanddecision_date_to_q:
+ q_parts.append(
+ f"decisionDate:[{decision_date_from_q} TO {decision_date_to_q}]"
+ )
+ elifdecision_date_from_q:
+ q_parts.append(f"decisionDate:>={decision_date_from_q}")
+ elifdecision_date_to_q:
+ q_parts.append(f"decisionDate:<={decision_date_to_q}")
+
+ ifq_parts:
+ final_q=" AND ".join(q_parts)
+
+ # Add parameters
+ iffinal_qisnotNone:
+ params["q"]=final_q
+ ifsortisnotNone:
+ params["sort"]=sort
+ ifoffsetisnotNone:
+ params["offset"]=offset
+ iflimitisnotNone:
+ params["limit"]=limit
+ iffacetsisnotNone:
+ params["facets"]=facets
+ iffieldsisnotNone:
+ params["fields"]=fields
+ iffiltersisnotNone:
+ params["filters"]=filters
+ ifrange_filtersisnotNone:
+ params["rangeFilters"]=range_filters
+
+ ifadditional_query_params:
+ params.update(additional_query_params)
+
+ result=self._make_request(
+ method="GET",
+ endpoint=endpoint,
+ params=params,
+ response_class=PTABAppealResponse,
+ )
+
+ assertisinstance(result,PTABAppealResponse)
+ returnresult
+
+
+
+[docs]
+ defpaginate_decisions(self,**kwargs:Any)->Iterator[PTABAppealDecision]:
+"""Provides an iterator to paginate through appeal decision search results.
+
+ This method simplifies fetching all appeal decisions matching a search query
+ by automatically handling pagination. It internally calls the search_decisions
+ method for GET requests, batching results and yielding them one by one.
+
+ All keyword arguments are passed directly to search_decisions to define the
+ search criteria. The offset and limit parameters are managed by the pagination
+ logic; setting them directly in kwargs might lead to unexpected behavior.
+
+ Args:
+ **kwargs: Keyword arguments passed to search_decisions for constructing
+ the search query. Do not include post_body.
+
+ Returns:
+ Iterator[PTABAppealDecision]: An iterator yielding PTABAppealDecision objects,
+ allowing iteration over all matching decisions across multiple pages of results.
+
+ Raises:
+ ValueError: If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+ Examples:
+ # Paginate through all decisions for a technology center
+ >>> for decision in client.paginate_decisions(technology_center_number_q="3600"):
+ ... print(f"{decision.appeal_meta_data.appeal_number}: "
+ ... f"{decision.decision_data.decision_type_category}")
+
+ # Paginate with date range
+ >>> for decision in client.paginate_decisions(
+ ... decision_date_from_q="2023-01-01",
+ ... decision_date_to_q="2023-12-31"
+ ... ):
+ ... process_decision(decision)
+ """
+ if"post_body"inkwargs:
+ raiseValueError(
+ "paginate_decisions uses GET requests and does not support 'post_body'. "
+ "Use keyword arguments for search criteria."
+ )
+
+ returnself.paginate_results(
+ method_name="search_decisions",
+ response_container_attr="patent_appeal_data_bag",
+ **kwargs,
+ )
Source code for pyUSPTO.clients.ptab_interferences
+"""
+clients.ptab_interferences - Client for USPTO PTAB Interferences API
+
+This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Interferences API. It allows you to search for patent interference decisions.
+"""
+
+fromtypingimportAny,Dict,Iterator,Optional
+
+frompyUSPTO.clients.baseimportBaseUSPTOClient
+frompyUSPTO.configimportUSPTOConfig
+frompyUSPTO.models.ptabimportPTABInterferenceDecision,PTABInterferenceResponse
+
+
+
+[docs]
+classPTABInterferencesClient(BaseUSPTOClient[PTABInterferenceResponse]):
+"""Client for interacting with the USPTO PTAB Interferences API.
+
+ This client provides methods to search for patent interference decisions from the
+ Patent Trial and Appeal Board.
+
+ Interference proceedings are used to determine priority of invention when two or
+ more parties claim the same patentable invention.
+ """
+
+ ENDPOINTS={
+ "search_decisions":"api/v1/patent/interferences/decisions/search",
+ }
+
+
+[docs]
+ def__init__(
+ self,
+ api_key:Optional[str]=None,
+ base_url:Optional[str]=None,
+ config:Optional[USPTOConfig]=None,
+ ):
+"""Initialize the PTABInterferencesClient.
+
+ Args:
+ api_key: Optional API key for authentication.
+ base_url: Optional base URL override for the API.
+ config: Optional USPTOConfig instance for configuration.
+ """
+ self.config=configorUSPTOConfig(api_key=api_key)
+ api_key_to_use=api_keyorself.config.api_key
+ effective_base_url=(
+ base_urlorself.config.ptab_base_urlor"https://api.uspto.gov"
+ )
+ super().__init__(
+ api_key=api_key_to_use,base_url=effective_base_url,config=self.config
+ )
+
+
+
+[docs]
+ defsearch_decisions(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ interference_number_q:Optional[str]=None,
+ senior_party_application_number_q:Optional[str]=None,
+ junior_party_application_number_q:Optional[str]=None,
+ senior_party_name_q:Optional[str]=None,
+ junior_party_name_q:Optional[str]=None,
+ real_party_in_interest_q:Optional[str]=None,
+ interference_outcome_category_q:Optional[str]=None,
+ decision_type_category_q:Optional[str]=None,
+ decision_date_from_q:Optional[str]=None,
+ decision_date_to_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PTABInterferenceResponse:
+"""Searches for PTAB interference decisions.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ interference_number_q: Filter by interference number.
+ senior_party_application_number_q: Filter by senior party application number.
+ junior_party_application_number_q: Filter by junior party application number.
+ senior_party_name_q: Filter by senior party name.
+ junior_party_name_q: Filter by junior party name.
+ real_party_in_interest_q: Filter by Real Party in Interest.
+ interference_outcome_category_q: Filter by interference outcome category.
+ decision_type_category_q: Filter by decision type category.
+ decision_date_from_q: Filter decisions from this date (YYYY-MM-DD).
+ decision_date_to_q: Filter decisions to this date (YYYY-MM-DD).
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PTABInterferenceResponse: Response containing matching interference decisions.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_decisions(query="interferenceNumber:106123")
+
+ # Search with convenience parameters
+ >>> response = client.search_decisions(
+ ... interference_outcome_category_q="Priority to Senior Party",
+ ... decision_date_from_q="2020-01-01",
+ ... limit=50
+ ... )
+
+ # Search with POST body
+ >>> response = client.search_decisions(
+ ... post_body={"q": "decisionTypeCategory:Final Decision", "limit": 100}
+ ... )
+ """
+ endpoint=self.ENDPOINTS["search_decisions"]
+
+ ifpost_bodyisnotNone:
+ # POST request path
+ result=self._make_request(
+ method="POST",
+ endpoint=endpoint,
+ json_data=post_body,
+ params=additional_query_params,
+ response_class=PTABInterferenceResponse,
+ )
+ else:
+ # GET request path
+ params:Dict[str,Any]={}
+ final_q=query
+
+ # Build query from convenience parameters
+ iffinal_qisNone:
+ q_parts=[]
+ ifinterference_number_q:
+ q_parts.append(f"interferenceNumber:{interference_number_q}")
+ ifsenior_party_application_number_q:
+ q_parts.append(
+ f"seniorPartyData.applicationNumberText:{senior_party_application_number_q}"
+ )
+ ifjunior_party_application_number_q:
+ q_parts.append(
+ f"juniorPartyData.applicationNumberText:{junior_party_application_number_q}"
+ )
+ ifsenior_party_name_q:
+ q_parts.append(
+ f'seniorPartyData.patentOwnerName:"{senior_party_name_q}" OR seniorPartyData.inventorName:"{senior_party_name_q}" OR seniorPartyData.realPartyInInterestName:"{senior_party_name_q}"'
+ )
+ ifjunior_party_name_q:
+ q_parts.append(
+ f'juniorPartyData.patentOwnerName:"{junior_party_name_q}" OR juniorPartyData.inventorName:"{junior_party_name_q}" OR juniorPartyData.realPartyInInterestName:"{junior_party_name_q}"'
+ )
+ ifreal_party_in_interest_q:
+ q_parts.append(
+ f'seniorPartyData.realPartyInInterestName:"{real_party_in_interest_q}" OR juniorPartyData.realPartyInInterestName:"{real_party_in_interest_q}"'
+ )
+
+ ifinterference_outcome_category_q:
+ q_parts.append(
+ f'documentData.interferenceOutcomeCategory:"{interference_outcome_category_q}"'
+ )
+ ifdecision_type_category_q:
+ q_parts.append(
+ f'documentData.decisionTypeCategory:"{decision_type_category_q}"'
+ )
+
+ # Handle decision date range
+ ifdecision_date_from_qanddecision_date_to_q:
+ q_parts.append(
+ f"documentData.decisionIssueDate:[{decision_date_from_q} TO {decision_date_to_q}]"
+ )
+ elifdecision_date_from_q:
+ q_parts.append(
+ f"documentData.decisionIssueDate:>={decision_date_from_q}"
+ )
+ elifdecision_date_to_q:
+ q_parts.append(
+ f"documentData.decisionIssueDate:<={decision_date_to_q}"
+ )
+
+ ifq_parts:
+ final_q=" AND ".join(q_parts)
+
+ # Add parameters
+ iffinal_qisnotNone:
+ params["q"]=final_q
+ ifsortisnotNone:
+ params["sort"]=sort
+ ifoffsetisnotNone:
+ params["offset"]=offset
+ iflimitisnotNone:
+ params["limit"]=limit
+ iffacetsisnotNone:
+ params["facets"]=facets
+ iffieldsisnotNone:
+ params["fields"]=fields
+ iffiltersisnotNone:
+ params["filters"]=filters
+ ifrange_filtersisnotNone:
+ params["rangeFilters"]=range_filters
+
+ ifadditional_query_params:
+ params.update(additional_query_params)
+
+ result=self._make_request(
+ method="GET",
+ endpoint=endpoint,
+ params=params,
+ response_class=PTABInterferenceResponse,
+ )
+
+ assertisinstance(result,PTABInterferenceResponse)
+ returnresult
+
+
+
+[docs]
+ defpaginate_decisions(self,**kwargs:Any)->Iterator[PTABInterferenceDecision]:
+"""Provides an iterator to paginate through interference decision search results.
+
+ This method simplifies fetching all interference decisions matching a search query
+ by automatically handling pagination. It internally calls the search_decisions
+ method for GET requests, batching results and yielding them one by one.
+
+ All keyword arguments are passed directly to search_decisions to define the
+ search criteria. The offset and limit parameters are managed by the pagination
+ logic; setting them directly in kwargs might lead to unexpected behavior.
+
+ Args:
+ **kwargs: Keyword arguments passed to search_decisions for constructing
+ the search query. Do not include post_body.
+
+ Returns:
+ Iterator[PTABInterferenceDecision]: An iterator yielding PTABInterferenceDecision
+ objects, allowing iteration over all matching decisions across multiple pages
+ of results.
+
+ Raises:
+ ValueError: If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+ Examples:
+ # Paginate through all interference decisions
+ >>> for decision in client.paginate_decisions():
+ ... print(f"{decision.interference_meta_data.interference_number}: "
+ ... f"{decision.document_data.interference_outcome_category}")
+
+ # Paginate with date range
+ >>> for decision in client.paginate_decisions(
+ ... decision_date_from_q="2020-01-01",
+ ... decision_date_to_q="2023-12-31"
+ ... ):
+ ... process_decision(decision)
+ """
+ if"post_body"inkwargs:
+ raiseValueError(
+ "paginate_decisions uses GET requests and does not support 'post_body'. "
+ "Use keyword arguments for search criteria."
+ )
+
+ returnself.paginate_results(
+ method_name="search_decisions",
+ response_container_attr="patent_interference_data_bag",
+ **kwargs,
+ )
+"""
+clients.ptab_trials - Client for USPTO PTAB Trials API
+
+This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Trials API. It allows you to search for trial proceedings,
+documents, and decisions.
+"""
+
+fromtypingimportAny,Dict,Iterator,List,Optional,Union
+
+frompyUSPTO.clients.baseimportBaseUSPTOClient
+frompyUSPTO.configimportUSPTOConfig
+frompyUSPTO.models.ptabimport(
+ PTABTrialDocumentResponse,
+ PTABTrialProceeding,
+ PTABTrialProceedingResponse,
+)
+
+
+
+[docs]
+classPTABTrialsClient(
+ BaseUSPTOClient[Union[PTABTrialProceedingResponse,PTABTrialDocumentResponse]]
+):
+"""Client for interacting with the USPTO PTAB Trials API.
+
+ This client provides methods to search for trial proceedings, trial documents,
+ and trial decisions from the Patent Trial and Appeal Board.
+
+ Trial proceedings data includes IPR (Inter Partes Review), PGR (Post-Grant Review),
+ CBM (Covered Business Method), and DER (Derivation) proceedings.
+ """
+
+ ENDPOINTS={
+ "search_proceedings":"api/v1/patent/trials/proceedings/search",
+ "search_documents":"api/v1/patent/trials/documents/search",
+ "search_decisions":"api/v1/patent/trials/decisions/search",
+ }
+
+
+[docs]
+ def__init__(
+ self,
+ api_key:Optional[str]=None,
+ base_url:Optional[str]=None,
+ config:Optional[USPTOConfig]=None,
+ ):
+"""Initialize the PTABTrialsClient.
+
+ Args:
+ api_key: Optional API key for authentication.
+ base_url: Optional base URL override for the API.
+ config: Optional USPTOConfig instance for configuration.
+ """
+ self.config=configorUSPTOConfig(api_key=api_key)
+ api_key_to_use=api_keyorself.config.api_key
+ effective_base_url=(
+ base_urlorself.config.ptab_base_urlor"https://api.uspto.gov"
+ )
+ super().__init__(
+ api_key=api_key_to_use,base_url=effective_base_url,config=self.config
+ )
+[docs]
+ defsearch_proceedings(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ trial_number_q:Optional[str]=None,
+ patent_owner_name_q:Optional[str]=None,
+ petitioner_real_party_in_interest_name_q:Optional[str]=None,
+ respondent_name_q:Optional[str]=None,
+ trial_type_code_q:Optional[str]=None,
+ trial_status_category_q:Optional[str]=None,
+ petition_filing_date_from_q:Optional[str]=None,
+ petition_filing_date_to_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PTABTrialProceedingResponse:
+"""Searches for PTAB trial proceedings.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ trial_number_q: Filter by trial number (e.g., "IPR2023-00001").
+ patent_owner_name_q: Filter by patent owner name.
+ petitioner_real_party_in_interest_name_q: Filter by petitioner real party in interest.
+ respondent_name_q: Filter by respondent name.
+ trial_type_code_q: Filter by trial type code (e.g., "IPR", "PGR", "CBM", "DER").
+ trial_status_category_q: Filter by trial status category.
+ petition_filing_date_from_q: Filter proceedings from this date (YYYY-MM-DD).
+ petition_filing_date_to_q: Filter proceedings to this date (YYYY-MM-DD).
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PTABTrialProceedingResponse: Response containing matching trial proceedings.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_proceedings(query="trialNumber:IPR2023-00001")
+
+ # Search with convenience parameters
+ >>> response = client.search_proceedings(
+ ... trial_type_code_q="IPR",
+ ... petition_filing_date_from_q="2023-01-01",
+ ... limit=50
+ ... )
+ """
+ q_parts=[]
+ iftrial_number_q:
+ q_parts.append(f"trialNumber:{trial_number_q}")
+ ifpatent_owner_name_q:
+ q_parts.append(f'patentOwnerData.patentOwnerName:"{patent_owner_name_q}"')
+ ifpetitioner_real_party_in_interest_name_q:
+ q_parts.append(
+ f'regularPetitionerData.realPartyInInterestName:"{petitioner_real_party_in_interest_name_q}"'
+ )
+ ifrespondent_name_q:
+ q_parts.append(f'respondentData.patentOwnerName:"{respondent_name_q}"')
+ iftrial_type_code_q:
+ q_parts.append(f"trialMetaData.trialTypeCode:{trial_type_code_q}")
+ iftrial_status_category_q:
+ q_parts.append(
+ f'trialMetaData.trialStatusCategory:"{trial_status_category_q}"'
+ )
+
+ ifpetition_filing_date_from_qandpetition_filing_date_to_q:
+ q_parts.append(
+ f"trialMetaData.petitionFilingDate:[{petition_filing_date_from_q} TO {petition_filing_date_to_q}]"
+ )
+ elifpetition_filing_date_from_q:
+ q_parts.append(
+ f"trialMetaData.petitionFilingDate:>={petition_filing_date_from_q}"
+ )
+ elifpetition_filing_date_to_q:
+ q_parts.append(
+ f"trialMetaData.petitionFilingDate:<={petition_filing_date_to_q}"
+ )
+
+ returnself._perform_search(
+ endpoint_key="search_proceedings",
+ response_class=PTABTrialProceedingResponse,
+ query=query,
+ query_parts=q_parts,
+ post_body=post_body,
+ sort=sort,
+ offset=offset,
+ limit=limit,
+ facets=facets,
+ fields=fields,
+ filters=filters,
+ range_filters=range_filters,
+ additional_params=additional_query_params,
+ )# type: ignore
+
+
+
+[docs]
+ defsearch_documents(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ trial_number_q:Optional[str]=None,
+ document_category_q:Optional[str]=None,
+ document_type_name_q:Optional[str]=None,
+ filing_date_from_q:Optional[str]=None,
+ filing_date_to_q:Optional[str]=None,
+ petitioner_real_party_in_interest_name_q:Optional[str]=None,
+ inventor_name_q:Optional[str]=None,
+ real_party_in_interest_name_q:Optional[str]=None,
+ patent_number_q:Optional[str]=None,
+ patent_owner_name_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PTABTrialDocumentResponse:
+"""Searches for PTAB trial documents.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ trial_number_q: Filter by trial number.
+ document_category_q: Filter by document category (e.g., "Petition") DOCUMENTED BUT NOT IN API.
+ document_type_name_q: Filter by document type name (description).
+ filing_date_from_q: Filter documents from this date (YYYY-MM-DD).
+ filing_date_to_q: Filter documents to this date (YYYY-MM-DD).
+ petitioner_real_party_in_interest_name_q: Filter by petitioner real party in interest.
+ inventor_name_q: Filter by inventor name.
+ real_party_in_interest_name_q: Filter by real party in interest (generic).
+ patent_number_q: Filter by patent number.
+ patent_owner_name_q: Filter by patent owner name.
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PTABTrialDocumentResponse: Response containing matching trial documents.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_documents(query="trialNumber:IPR2023-00001")
+
+ # Search with convenience parameters
+ >>> response = client.search_documents(
+ ... document_category_q="Paper",
+ ... filing_date_from_q="2023-01-01",
+ ... limit=50
+ ... )
+ """
+ q_parts=[]
+ iftrial_number_q:
+ q_parts.append(f"trialNumber:{trial_number_q}")
+ ifdocument_category_q:
+ q_parts.append(f'documentData.documentCategory:"{document_category_q}"')
+ ifdocument_type_name_q:
+ q_parts.append(
+ f'documentData.documentTypeDescriptionText:"{document_type_name_q}"'
+ )
+ ifpetitioner_real_party_in_interest_name_q:
+ q_parts.append(
+ f'regularPetitionerData.realPartyInInterestName:"{petitioner_real_party_in_interest_name_q}"'
+ )
+ ifinventor_name_q:
+ q_parts.append(f'patentOwnerData.inventorName:"{inventor_name_q}"')
+ ifreal_party_in_interest_name_q:
+ q_parts.append(
+ f'regularPetitionerData.realPartyInInterestName:"{real_party_in_interest_name_q}"'
+ )
+ ifpatent_number_q:
+ q_parts.append(f"patentOwnerData.patentNumber:{patent_number_q}")
+ ifpatent_owner_name_q:
+ q_parts.append(f'patentOwnerData.patentOwnerName:"{patent_owner_name_q}"')
+
+ iffiling_date_from_qandfiling_date_to_q:
+ q_parts.append(
+ f"documentData.documentFilingDate:[{filing_date_from_q} TO {filing_date_to_q}]"
+ )
+ eliffiling_date_from_q:
+ q_parts.append(f"documentData.documentFilingDate:>={filing_date_from_q}")
+ eliffiling_date_to_q:
+ q_parts.append(f"documentData.documentFilingDate:<={filing_date_to_q}")
+
+ returnself._perform_search(
+ endpoint_key="search_documents",
+ response_class=PTABTrialDocumentResponse,
+ query=query,
+ query_parts=q_parts,
+ post_body=post_body,
+ sort=sort,
+ offset=offset,
+ limit=limit,
+ facets=facets,
+ fields=fields,
+ filters=filters,
+ range_filters=range_filters,
+ additional_params=additional_query_params,
+ )# type: ignore
+
+
+
+[docs]
+ defsearch_decisions(
+ self,
+ query:Optional[str]=None,
+ sort:Optional[str]=None,
+ offset:Optional[int]=0,
+ limit:Optional[int]=25,
+ facets:Optional[str]=None,
+ fields:Optional[str]=None,
+ filters:Optional[str]=None,
+ range_filters:Optional[str]=None,
+ post_body:Optional[Dict[str,Any]]=None,
+ # Convenience query parameters
+ trial_number_q:Optional[str]=None,
+ decision_type_category_q:Optional[str]=None,
+ document_type_description_q:Optional[str]=None,
+ decision_date_from_q:Optional[str]=None,
+ decision_date_to_q:Optional[str]=None,
+ trial_type_code_q:Optional[str]=None,
+ patent_number_q:Optional[str]=None,
+ application_number_q:Optional[str]=None,
+ patent_owner_name_q:Optional[str]=None,
+ trial_status_category_q:Optional[str]=None,
+ real_party_in_interest_name_q:Optional[str]=None,
+ document_category_q:Optional[str]=None,
+ additional_query_params:Optional[Dict[str,Any]]=None,
+ )->PTABTrialDocumentResponse:
+"""Searches for PTAB trial decisions.
+
+ This method can perform either a GET request using query parameters or a POST
+ request if post_body is specified. When using GET, you can provide either a
+ direct query string or use convenience parameters that will be automatically
+ combined into a query.
+
+ Args:
+ query: Direct query string in USPTO search syntax.
+ sort: Sort order for results.
+ offset: Number of records to skip (pagination).
+ limit: Maximum number of records to return.
+ facets: Facet configuration string.
+ fields: Specific fields to return.
+ filters: Filter configuration string.
+ range_filters: Range filter configuration string.
+ post_body: Optional POST body for complex queries.
+ trial_number_q: Filter by trial number.
+ decision_type_category_q: Filter by decision type category.
+ document_type_description_q: Filter by "*[description]*".
+ decision_date_from_q: Filter decisions from this date (YYYY-MM-DD).
+ decision_date_to_q: Filter decisions to this date (YYYY-MM-DD).
+ trial_type_code_q: Filter by trial type code (e.g., "IPR", "PGR", "CBM", "DER").
+ patent_number_q: Filter by patent number.
+ application_number_q: Filter by application number.
+ patent_owner_name_q: Filter by patent owner name.
+ trial_status_category_q: Filter by trial status category.
+ real_party_in_interest_name_q: Filter by real party in interest name.
+ document_category_q: Filter by document category.
+ additional_query_params: Additional custom query parameters.
+
+ Returns:
+ PTABTrialDocumentResponse: Response containing matching trial decisions.
+
+ Examples:
+ # Search with direct query
+ >>> response = client.search_decisions(query="trialNumber:IPR2023-00001")
+
+ # Search with convenience parameters
+ >>> response = client.search_decisions(
+ ... decision_type_category_q="Final Written Decision",
+ ... decision_date_from_q="2023-01-01",
+ ... limit=50
+ ... )
+ """
+ q_parts=[]
+ iftrial_number_q:
+ q_parts.append(f"trialNumber:{trial_number_q}")
+ ifdecision_type_category_q:
+ q_parts.append(
+ f'decisionData.decisionTypeCategory:"{decision_type_category_q}"'
+ )
+ ifdocument_type_description_q:
+ q_parts.append(
+ f'documentData.documentTypeDescriptionText:"*{document_type_description_q}*"'
+ )
+ iftrial_type_code_q:
+ q_parts.append(f"trialMetaData.trialTypeCode:{trial_type_code_q}")
+ ifpatent_number_q:
+ q_parts.append(f"patentOwnerData.patentNumber:{patent_number_q}")
+ ifapplication_number_q:
+ q_parts.append(
+ f"patentOwnerData.applicationNumberText:{application_number_q}"
+ )
+ ifpatent_owner_name_q:
+ q_parts.append(f'patentOwnerData.patentOwnerName:"{patent_owner_name_q}"')
+ iftrial_status_category_q:
+ q_parts.append(
+ f'trialMetaData.trialStatusCategory:"{trial_status_category_q}"'
+ )
+ ifreal_party_in_interest_name_q:
+ q_parts.append(
+ f'regularPetitionerData.realPartyInInterestName:"{real_party_in_interest_name_q}"'
+ )
+ ifdocument_category_q:
+ q_parts.append(f'documentData.documentCategory:"{document_category_q}"')
+
+ ifdecision_date_from_qanddecision_date_to_q:
+ q_parts.append(
+ f"decisionData.decisionIssueDate:[{decision_date_from_q} TO {decision_date_to_q}]"
+ )
+ elifdecision_date_from_q:
+ q_parts.append(f"decisionData.decisionIssueDate:>={decision_date_from_q}")
+ elifdecision_date_to_q:
+ q_parts.append(f"decisionData.decisionIssueDate:<={decision_date_to_q}")
+
+ returnself._perform_search(
+ endpoint_key="search_decisions",
+ response_class=PTABTrialDocumentResponse,
+ query=query,
+ query_parts=q_parts,
+ post_body=post_body,
+ sort=sort,
+ offset=offset,
+ limit=limit,
+ facets=facets,
+ fields=fields,
+ filters=filters,
+ range_filters=range_filters,
+ additional_params=additional_query_params,
+ )# type: ignore
+
+
+
+[docs]
+ defpaginate_proceedings(self,**kwargs:Any)->Iterator[PTABTrialProceeding]:
+"""Provides an iterator to paginate through trial proceeding search results."""
+ if"post_body"inkwargs:
+ raiseValueError(
+ "paginate_proceedings uses GET requests and does not support 'post_body'. "
+ "Use keyword arguments for search criteria."
+ )
+
+ returnself.paginate_results(
+ method_name="search_proceedings",
+ response_container_attr="patent_trial_proceeding_data_bag",
+ **kwargs,
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pyUSPTO/config.html b/docs/build/html/_modules/pyUSPTO/config.html
index 819a396..8c9b251 100644
--- a/docs/build/html/_modules/pyUSPTO/config.html
+++ b/docs/build/html/_modules/pyUSPTO/config.html
@@ -5,7 +5,7 @@
- pyUSPTO.config — pyUSPTO 0.1.4.dev0+ga92fa00.d20250320 documentation
+ pyUSPTO.config — pyUSPTO 0.2.3.dev1+g2c5d89b38.d20251125 documentation
@@ -13,7 +13,7 @@
-
+
@@ -79,60 +79,89 @@
Source code for pyUSPTO.config
"""config - Configuration management for USPTO API clients
-This module provides configuration management for USPTO API clients.
+This module provides configuration management for USPTO API clients,
+including API keys, base URLs, and HTTP transport settings."""importosfromtypingimportOptional
+frompyUSPTO.http_configimportHTTPConfig
+
[docs]classUSPTOConfig:
-"""Configuration for USPTO API clients."""
+"""Configuration for USPTO API clients.
+
+ Manages API-level configuration (keys, URLs) and optionally
+ accepts HTTP transport configuration via HTTPConfig.
+ """
[docs]def__init__(self,api_key:Optional[str]=None,
- bulk_data_base_url:str="https://api.uspto.gov/api/v1/datasets",
- patent_data_base_url:str="https://api.uspto.gov/api/v1/patent",
+ bulk_data_base_url:str="https://api.uspto.gov",
+ patent_data_base_url:str="https://api.uspto.gov",
+ petition_decisions_base_url:str="https://api.uspto.gov",
+ ptab_base_url:str="https://api.uspto.gov",
+ http_config:Optional[HTTPConfig]=None,
+ include_raw_data:bool=False,):
-"""
- Initialize the USPTOConfig.
+"""Initialize the USPTOConfig. Args: api_key: API key for authentication, defaults to USPTO_API_KEY environment variable bulk_data_base_url: Base URL for the Bulk Data API patent_data_base_url: Base URL for the Patent Data API
+ petition_decisions_base_url: Base URL for the Final Petition Decisions API
+ ptab_base_url: Base URL for the PTAB (Patent Trial and Appeal Board) API
+ http_config: Optional HTTPConfig for request handling (uses defaults if None)
+ include_raw_data: If True, store raw JSON in response objects for debugging (default: False) """# Use environment variable only if api_key is None, not if it's an empty stringself.api_key=(api_keyifapi_keyisnotNoneelseos.environ.get("USPTO_API_KEY"))self.bulk_data_base_url=bulk_data_base_url
- self.patent_data_base_url=patent_data_base_url
+ self.patent_data_base_url=patent_data_base_url
+ self.petition_decisions_base_url=petition_decisions_base_url
+ self.ptab_base_url=ptab_base_url
+
+ # Use provided HTTPConfig or create default
+ self.http_config=http_configifhttp_configisnotNoneelseHTTPConfig()
+
+ # Control whether to include raw JSON data in response objects
+ self.include_raw_data=include_raw_data
[docs]@classmethoddeffrom_env(cls)->"USPTOConfig":
-"""
- Create a USPTOConfig from environment variables.
+"""Create a USPTOConfig from environment variables. Returns:
- USPTOConfig instance
+ USPTOConfig instance with values from environment """returncls(api_key=os.environ.get("USPTO_API_KEY"),bulk_data_base_url=os.environ.get(
- "USPTO_BULK_DATA_BASE_URL","https://api.uspto.gov/api/v1/datasets"
+ "USPTO_BULK_DATA_BASE_URL","https://api.uspto.gov"),patent_data_base_url=os.environ.get(
- "USPTO_PATENT_DATA_BASE_URL","https://api.uspto.gov/api/v1/patent"
+ "USPTO_PATENT_DATA_BASE_URL","https://api.uspto.gov"
+ ),
+ petition_decisions_base_url=os.environ.get(
+ "USPTO_PETITION_DECISIONS_BASE_URL","https://api.uspto.gov"
+ ),
+ ptab_base_url=os.environ.get(
+ "USPTO_PTAB_BASE_URL","https://api.uspto.gov"),
+ # Also read HTTP config from environment
+ http_config=HTTPConfig.from_env(),)
Built with Sphinx using a
diff --git a/docs/build/html/_modules/pyUSPTO/exceptions.html b/docs/build/html/_modules/pyUSPTO/exceptions.html
index 071202b..587cf1e 100644
--- a/docs/build/html/_modules/pyUSPTO/exceptions.html
+++ b/docs/build/html/_modules/pyUSPTO/exceptions.html
@@ -5,7 +5,7 @@
- pyUSPTO.exceptions — pyUSPTO 0.1.4.dev0+ga92fa00.d20250320 documentation
+ pyUSPTO.exceptions — pyUSPTO 0.2.3.dev1+g2c5d89b38.d20251125 documentation
@@ -13,7 +13,7 @@
-
+
@@ -79,27 +79,112 @@
Source code for pyUSPTO.exceptions
"""exceptions - Exception classes for USPTO API clients
-This module provides exception classes for USPTO API clients.
+This module provides exception classes for USPTO API errors that correspond to
+the various response types from the USPTO API. It also includes helper
+structures and functions for creating these exceptions."""
-fromtypingimportOptional
+fromdataclassesimportasdict,dataclass
+fromtypingimportTYPE_CHECKING,Optional,Type,Union
+# To avoid circular imports if requests is type-hinted directly,
+# use TYPE_CHECKING guard or a string literal for the type hint.
+ifTYPE_CHECKING:
+ importrequests# requests.exceptions.HTTPError
+
+# --- Exception Classes (largely unchanged) ---
[docs]classUSPTOApiError(Exception):
-"""Base exception for USPTO API errors."""
-
- def__init__(self,message:str,status_code:Optional[int]=None):
+"""Base exception for USPTO API errors.
+ This is the parent class for all USPTO API-specific exceptions. It includes
+ information about the status code, API's short error message, detailed error
+ information, and request identifier from the API response.
+ """
+
+ DEFAULT_UNKNOWN_MESSAGE="UNK USPTO API ERROR"
+
+
+[docs]
+ def__init__(
+ self,
+ message:str,# Primary client-facing message for the exception context
+ status_code:Optional[int]=None,
+ api_short_error:Optional[
+ str
+ ]=None,# From API 'error' or 'message' (for 413) field
+ error_details:Optional[
+ Union[str,dict]
+ ]=None,# From API 'errorDetails' or 'detailedMessage' field
+ request_identifier:Optional[str]=None,
+ ):
+"""
+ Initializes the USPTOApiError.
+ Args:
+ message: The primary message for the exception (often client-generated context).
+ status_code: The HTTP status code from the API response (e.g., 400, 403).
+ api_short_error: The short error description from the API (e.g., "Bad Request", "Forbidden").
+ error_details: The detailed error message or structure from the API.
+ request_identifier: The request identifier from the API response, if available.
+ """
+ effective_message=messageifmessageelseself.DEFAULT_UNKNOWN_MESSAGE
+ super().__init__(effective_message)self.status_code=status_code
- super().__init__(message)
+
+
+ @property
+ defmessage(self)->str:
+"""
+ Provides direct access to the primary exception message.
+ This refers to the first argument passed to the exception,
+ which is conventionally the main human-readable message.
+ """
+ returnstr(object=self.args[0])
+
+
+[docs]
+@dataclass
+classAPIErrorArgs:
+"""Data structure to hold arguments for API exception constructors."""
+
+ message:str
+ status_code:Optional[int]=None
+ api_short_error:Optional[str]=None
+ error_details:Optional[Union[str,dict]]=None
+ request_identifier:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_http_error(
+ cls,
+ http_error:"requests.exceptions.HTTPError",# String literal for type hint
+ client_operation_message:str,
+ )->"APIErrorArgs":
+"""
+ Creates an APIErrorArgs instance by parsing a requests.exceptions.HTTPError.
+
+ Args:
+ http_error: The HTTPError object from the requests library.
+ client_operation_message: A message describing the client operation that failed.
+
+ Returns:
+ An instance of APIErrorArgs populated with details from the HTTPError.
+ """
+ status_code=http_error.response.status_code
+
+ api_short_error_from_response=None
+ error_details_from_response=None
+ request_identifier_from_response=None
+
+ try:
+ error_data=http_error.response.json()
+ ifstatus_code==413:
+ api_short_error_from_response=error_data.get("message")
+ error_details_from_response=error_data.get("detailedMessage")
+ else:
+ api_short_error_from_response=error_data.get("error")
+ error_details_from_response=error_data.get("errorDetails")
+ request_identifier_from_response=error_data.get("requestIdentifier")
+ exceptValueError:# If response.json() fails (e.g., not JSON)
+ pass# Values remain None
+
+ # Fallback for api_short_error if not found in JSON response
+ ifnotapi_short_error_from_responseandhttp_error.response.reason:
+ api_short_error_from_response=http_error.response.reason
+
+ # Fallback for error_details if not found in JSON and response text is available
+ ifnoterror_details_from_responseandhttp_error.response.text:
+ # Avoid setting very long HTML pages as error_details if JSON parsing failed
+ if(
+ "content-type"inhttp_error.response.headers
+ and"application/json"
+ notinhttp_error.response.headers.get("content-type","").lower()
+ ):
+ iflen(http_error.response.text)>500:# Heuristic for "too long"
+ error_details_from_response=f"Non-JSON error response (status {status_code}). Check response text."
+ else:
+ error_details_from_response=http_error.response.text
+ elif(
+ http_error.response.text
+ ):# If it might have been JSON but parsing failed
+ error_details_from_response=http_error.response.text
+
+ returncls(
+ message=client_operation_message,
+ status_code=status_code,
+ api_short_error=api_short_error_from_response,
+ error_details=error_details_from_response,
+ request_identifier=request_identifier_from_response,
+ )
+
+
+
+[docs]
+ @classmethod
+ deffrom_request_exception(
+ cls,
+ request_exception:"requests.exceptions.RequestException",# String for type hint
+ client_operation_message:Optional[str]=None,
+ )->"APIErrorArgs":
+"""
+ Creates an APIErrorArgs instance from a generic requests.exceptions.RequestException
+ (e.g., ConnectionError, Timeout) that is not an HTTPError.
+ """
+ message_prefix=client_operation_messageor"API request failed"
+ returncls(
+ message=f"{message_prefix} due to a network or request issue: {str(request_exception)}"
+ # status_code, api_short_error, etc., will be None
+ )
+
+
+
+
+
+[docs]
+defget_api_exception(error_args:APIErrorArgs)->USPTOApiError:
+"""
+ Determines and instantiates the appropriate USPTOApiError subclass
+ based on the status code in error_args.
+
+ Args:
+ error_args: An instance of APIErrorArgs containing all necessary
+ information to construct the exception.
+
+ Returns:
+ An instance of a USPTOApiError subclass.
+ """
+ status_code=error_args.status_code
+ exception_class:Type[USPTOApiError]
+
+ matchstatus_code:
+ case400:
+ exception_class=USPTOApiBadRequestError
+ case401|403:
+ exception_class=USPTOApiAuthError
+ case404:
+ exception_class=USPTOApiNotFoundError
+ case413:
+ exception_class=USPTOApiPayloadTooLargeError
+ case429:
+ exception_class=USPTOApiRateLimitError
+ case_ifstatus_codeisnotNoneandstatus_code>=500:
+ exception_class=USPTOApiServerError
+ case(
+ _
+ ):# Default for other errors or if status_code is None (e.g. network error)
+ exception_class=USPTOApiError
+
+ returnexception_class(**asdict(error_args))
+"""
+http_config - HTTP client configuration for USPTO API requests
+
+This module provides configuration for HTTP transport-level settings including
+timeouts, retries, connection pooling, and custom headers.
+"""
+
+importos
+fromdataclassesimportdataclass,field
+fromtypingimportDict,List,Optional
+
+
+
+[docs]
+@dataclass
+classHTTPConfig:
+"""HTTP client configuration for request handling.
+
+ This class separates transport-level HTTP concerns from API-level
+ configuration, allowing fine-grained control over request behavior.
+
+ Attributes:
+ timeout: Read timeout in seconds for requests (default: 30.0)
+ connect_timeout: Connection establishment timeout in seconds (default: 10.0)
+ max_retries: Maximum number of retry attempts (default: 3)
+ backoff_factor: Exponential backoff multiplier for retries (default: 1.0)
+ retry_status_codes: HTTP status codes that trigger retries
+ pool_connections: Number of connection pools to cache (default: 10)
+ pool_maxsize: Maximum number of connections per pool (default: 10)
+ custom_headers: Additional headers to include in all requests
+ """
+
+ # Timeout configuration
+ timeout:Optional[float]=30.0
+ connect_timeout:Optional[float]=10.0
+
+ # Retry configuration
+ max_retries:int=3
+ backoff_factor:float=1.0
+ retry_status_codes:List[int]=field(
+ default_factory=lambda:[429,500,502,503,504]
+ )
+
+ # Connection pooling
+ pool_connections:int=10
+ pool_maxsize:int=10
+
+ # Custom headers (User-Agent, tracking, etc.)
+ custom_headers:Optional[Dict[str,str]]=None
+
+
+[docs]
+ @classmethod
+ deffrom_env(cls)->"HTTPConfig":
+"""Create HTTPConfig from environment variables.
+
+ Environment variables:
+ USPTO_REQUEST_TIMEOUT: Request timeout in seconds
+ USPTO_CONNECT_TIMEOUT: Connection timeout in seconds
+ USPTO_MAX_RETRIES: Maximum retry attempts
+ USPTO_BACKOFF_FACTOR: Retry backoff factor
+ USPTO_POOL_CONNECTIONS: Connection pool size
+ USPTO_POOL_MAXSIZE: Max connections per pool
+
+ Returns:
+ HTTPConfig instance with values from environment or defaults
+ """
+ returncls(
+ timeout=float(os.environ.get("USPTO_REQUEST_TIMEOUT","30.0")),
+ connect_timeout=float(os.environ.get("USPTO_CONNECT_TIMEOUT","10.0")),
+ max_retries=int(os.environ.get("USPTO_MAX_RETRIES","3")),
+ backoff_factor=float(os.environ.get("USPTO_BACKOFF_FACTOR","1.0")),
+ pool_connections=int(os.environ.get("USPTO_POOL_CONNECTIONS","10")),
+ pool_maxsize=int(os.environ.get("USPTO_POOL_MAXSIZE","10")),
+ )
+
+
+
+[docs]
+ defget_timeout_tuple(self)->tuple[Optional[float],Optional[float]]:
+"""Get timeout as tuple for requests library.
+
+ Returns:
+ Tuple of (connect_timeout, read_timeout) for requests
+ """
+ return(self.connect_timeout,self.timeout)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pyUSPTO/models/bulk_data.html b/docs/build/html/_modules/pyUSPTO/models/bulk_data.html
index 3b6b714..5b321ae 100644
--- a/docs/build/html/_modules/pyUSPTO/models/bulk_data.html
+++ b/docs/build/html/_modules/pyUSPTO/models/bulk_data.html
@@ -5,7 +5,7 @@
- pyUSPTO.models.bulk_data — pyUSPTO 0.1.4.dev0+ga92fa00.d20250320 documentation
+ pyUSPTO.models.bulk_data — pyUSPTO 0.2.3.dev1+g2c5d89b38.d20251125 documentation
@@ -13,7 +13,7 @@
-
+
@@ -82,7 +82,8 @@
Source code for pyUSPTO.models.bulk_data
This module provides data models for the USPTO Open Data Portal (ODP) Bulk Data API."""
-fromdataclassesimportdataclass
+importjson
+fromdataclassesimportdataclass,fieldfromtypingimportAny,Dict,List,Optional
@@ -201,22 +202,40 @@
Source code for pyUSPTO.models.bulk_data
[docs]@dataclassclassBulkDataResponse:
-"""Top-level response from the bulk data API."""
+"""Top-level response from the bulk data API.
+
+ Attributes:
+ count: The number of bulk data products in the response.
+ bulk_data_product_bag: List of bulk data products.
+ raw_data: Optional raw JSON data from the API response (for debugging).
+ """count:intbulk_data_product_bag:List[BulkDataProduct]
+ raw_data:Optional[str]=field(default=None,compare=False,repr=False)
[docs]@classmethod
- deffrom_dict(cls,data:Dict[str,Any])->"BulkDataResponse":
-"""Create a BulkDataResponse object from a dictionary."""
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"BulkDataResponse":
+"""Create a BulkDataResponse object from a dictionary.
+
+ Args:
+ data: Dictionary containing API response data.
+ include_raw_data: If True, store the raw JSON for debugging.
+
+ Returns:
+ BulkDataResponse: An instance of BulkDataResponse.
+ """returncls(count=data.get("count",0),bulk_data_product_bag=[BulkDataProduct.from_dict(product)forproductindata.get("bulkDataProductBag",[])],
+ raw_data=json.dumps(data)ifinclude_raw_dataelseNone,)
Built with Sphinx using a
diff --git a/docs/build/html/_modules/pyUSPTO/models/patent_data.html b/docs/build/html/_modules/pyUSPTO/models/patent_data.html
index 3e74975..de7fd30 100644
--- a/docs/build/html/_modules/pyUSPTO/models/patent_data.html
+++ b/docs/build/html/_modules/pyUSPTO/models/patent_data.html
@@ -5,7 +5,7 @@
- pyUSPTO.models.patent_data — pyUSPTO 0.1.4.dev0+ga92fa00.d20250320 documentation
+ pyUSPTO.models.patent_data — pyUSPTO 0.2.3.dev1+g2c5d89b38.d20251125 documentation
@@ -13,7 +13,7 @@
-
+
@@ -79,63 +79,427 @@
Source code for pyUSPTO.models.patent_data
"""models.patent_data - Data models for USPTO patent data API
-This module provides data models for the 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."""
-fromdataclassesimportdataclass,field
-fromtypingimportAny,Dict,List,Optional
+importcsv
+importio
+importjson
+importwarnings
+fromdataclassesimportasdict,dataclass,field
+fromdatetimeimportdate,datetime
+fromenumimportEnum
+fromtypingimportAny,Dict,Iterator,List,Optional,Union
+
+# Import utility functions from models.utils module
+frompyUSPTO.models.utilsimport(
+ 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,
+)
+frompyUSPTO.warningsimportUSPTOEnumParseWarning
+
+
+# --- Enums for Categorical Data ---
+
+[docs]
+classDirectionCategory(Enum):
+"""Represents the direction of a document relative to the USPTO (e.g., INCOMING, OUTGOING)."""
+
+ INCOMING="INCOMING"
+ OUTGOING="OUTGOING"
+
+
+
+
+[docs]
+classActiveIndicator(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":
+ ifisinstance(value,str):
+ val_upper=value.upper()
+ ifval_upper=="Y":
+ returncls.YES
+ ifval_upper=="N":
+ returncls.NO
+ ifval_upper=="TRUE":
+ returncls.TRUE
+ ifval_upper=="FALSE":
+ returncls.FALSE
+ ifval_upper=="ACTIVE":
+ returncls.ACTIVE
+ returnsuper()._missing_(value=value)# type: ignore[no-any-return]
+
+
+
+# --- Data Models ---
+
+[docs]
+@dataclass(frozen=True)
+classDocumentFormat:
+"""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:
+ returnf"DocumentFormat(mime_type={self.mime_type_identifier}, pages={self.page_total_quantity})"
+
+
+[docs]
+ @classmethod
+ deffrom_dict(cls,data:Dict[str,Any])->"DocumentFormat":
+"""Creates a `DocumentFormat` instance from a dictionary representation.
-
-[docs]
-@dataclass
-classPatentDataResponse:
-"""Top-level response from the patent data API."""
+ 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.
- count:int
- patent_file_wrapper_data_bag:List["PatentFileWrapper"]
+ Args:
+ data (Dict[str, Any]): A dictionary containing the data for a
+ `DocumentFormat`. Expected keys from the API are
+ "mimeTypeIdentifier", "downloadUrl", and "pageTotalQuantity".
-
-[docs]
+ Returns:
+ DocumentFormat: An instance of `DocumentFormat` initialized with
+ data from the input dictionary.
+ """
+
+ returncls(
+ mime_type_identifier=data.get("mimeTypeIdentifier"),
+ download_url=data.get("downloadUrl"),
+ page_total_quantity=data.get("pageTotalQuantity"),
+ )
+
+
+
+[docs]
+ defto_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)
+classDocument:
+"""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")ifself.official_dateelse"No date"
+ )
+ returnf"Document {self.document_identifier} ({self.document_code}): {self.document_code_description_text} - {date_str}"
+
+ def__repr__(self)->str:
+ returnf"Document(id={self.document_identifier}, code={self.document_code}, date={self.official_date.strftime('%Y-%m-%d')ifself.official_dateelse'None'})"
+
+
+[docs]@classmethod
- deffrom_dict(cls,data:Dict[str,Any])->"PatentDataResponse":
-"""Create a PatentDataResponse object from a dictionary."""
+ deffrom_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)
+ forfindata.get("downloadOptionBag",[])
+ ifisinstance(f,dict)
+ ]
+ dir_val=data.get("documentDirectionCategory")
+ dir_cat=None
+ ifdir_val:
+ try:
+ dir_cat=DirectionCategory(dir_val)
+ exceptValueError:
+ warnings.warn(
+ f"Unknown document direction category '{dir_val}'",
+ category=USPTOEnumParseWarning,
+ stacklevel=2,
+ )returncls(
- count=data.get("count",0),
- patent_file_wrapper_data_bag=[
- PatentFileWrapper.from_dict(data=wrapper)
- forwrapperindata.get("patentFileWrapperDataBag",[])
- ],
+ 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]defto_dict(self)->Dict[str,Any]:
-"""Convert the PatentDataResponse object to a dictionary."""
+"""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)
+ ifself.official_date
+ elseNone
+ ),
+ "documentIdentifier":self.document_identifier,
+ "documentCode":self.document_code,
+ "documentCodeDescriptionText":self.document_code_description_text,
+ "documentDirectionCategory":(
+ self.direction_category.valueifself.direction_categoryelseNone
+ ),
+ "downloadOptionBag":[df.to_dict()fordfinself.document_formats],
+ }return{
- "count":self.count,
- "patentFileWrapperDataBag":[
- # If PatentFileWrapper had a to_dict method, we would use it here
- # For now, we'll just return a basic representation
- {
- "applicationNumberText":wrapper.application_number_text,
- # Add other fields as needed
- }
- forwrapperinself.patent_file_wrapper_data_bag
- ],
- # Add other fields that might be in the API response but not in our model
- "documentBag":[],# Empty placeholder for document bag
+ k:v
+ fork,vind.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)}
+
+[docs]
+classDocumentBag:
+"""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)
+[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)
+ ifcount==0:
+ return"DocumentBag(0 documents)"
+
+ # Count unique document codes
+ doc_codes:Dict[str,int]={}
+ fordocinself._documents:
+ code=doc.document_codeor"Unknown"
+ doc_codes[code]=doc_codes.get(code,0)+1
+
+ # Format summary
+ ifcount==1:
+ code=self._documents[0].document_codeor"Unknown"
+ returnf"DocumentBag(1 document: {code})"
+
+ # Show top 3 most common document codes
+ sorted_codes=sorted(doc_codes.items(),key=lambdax:x[1],reverse=True)
+ top_codes=sorted_codes[:3]
+ code_summary=", ".join(f"{code} ({cnt})"forcode,cntintop_codes)
+
+ iflen(sorted_codes)>3:
+ remaining=len(sorted_codes)-3
+ returnf"DocumentBag({count} documents: {code_summary}, +{remaining} more types)"
+ else:
+ returnf"DocumentBag({count} documents: {code_summary})"
+
+
+
+[docs]
+ def__repr__(self)->str:
+"""Returns a detailed string representation for debugging.
+
+ Returns:
+ str: Detailed representation of the DocumentBag.
+ """
+ returnf"DocumentBag(documents={self._documents!r})"
+
+
+
+[docs]
+ @classmethod
+ deffrom_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)forddindocs_dataifisinstance(dd,dict)]
+ ifisinstance(docs_data,list)
+ else[]
+ )
+ returncls(documents=docs)
+
+
+
+[docs]
+ defto_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()fordocinself._documents]}
+
+
+
+
[docs]
-@dataclass
+@dataclass(frozen=True)classAddress:
-"""Represents an address in the patent data API."""
+"""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]=Nonename_line_two_text:Optional[str]=None
@@ -151,12 +515,24 @@
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Address":
-"""Create an Address object from a dictionary."""
+"""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`.
+ """returncls(name_line_one_text=data.get("nameLineOneText"),name_line_two_text=data.get("nameLineTwoText"),
@@ -172,16 +548,56 @@
+[docs]
+ defto_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:vfork,vin_dict.items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classTelecommunication:
-"""Represents telecommunication information."""
+"""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]=Noneextension_number:Optional[str]=None
@@ -191,21 +607,57 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Telecommunication":
-"""Create a Telecommunication object from a dictionary."""
+"""Creates a `Telecommunication` instance from a dictionary.
+
+ Args:
+ data (Dict[str, Any]): Dictionary with telecommunication data.
+
+ Returns:
+ Telecommunication: An instance of `Telecommunication`.
+ """returncls(telecommunication_number=data.get("telecommunicationNumber"),extension_number=data.get("extensionNumber"),telecom_type_code=data.get("telecomTypeCode"),)
+
+
+
+[docs]
+ defto_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:vfork,vin_dict.items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classPerson:
-"""Base class for person-related data."""
+"""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]=Nonemiddle_name:Optional[str]=None
@@ -215,29 +667,45 @@
+[docs]
+ defto_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):vfork,vinasdict(self).items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classApplicant(Person):
-"""Represents an applicant in the patent data."""
+"""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]=Nonecorrespondence_address_bag:List[Address]=field(default_factory=list)
@@ -246,35 +714,70 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Applicant":
-"""Create an Applicant object from a dictionary."""
- person=Person.from_dict(data=data)
- addresses=[]
- if"correspondenceAddressBag"indata:
- addresses=[
- Address.from_dict(data=addr)
- foraddrindata.get("correspondenceAddressBag",[])
- ]
-
+"""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)
+ foraindata.get("correspondenceAddressBag",[])
+ ifisinstance(a,dict)
+ ]returncls(
- first_name=person.first_name,
- middle_name=person.middle_name,
- last_name=person.last_name,
- name_prefix=person.name_prefix,
- name_suffix=person.name_suffix,
- preferred_name=person.preferred_name,
- country_code=person.country_code,
+ **pf,applicant_name_text=data.get("applicantNameText"),
- correspondence_address_bag=addresses,
+ correspondence_address_bag=addrs,)
+
+
+
+[docs]
+ defto_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()forainself.correspondence_address_bag
+ ],
+ }
+ )
+ return{
+ k:v
+ fork,vind.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)
+ }
[docs]
-@dataclass
+@dataclass(frozen=True)classInventor(Person):
-"""Represents an inventor in the patent data."""
+"""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]=Nonecorrespondence_address_bag:List[Address]=field(default_factory=list)
@@ -283,35 +786,73 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Inventor":
-"""Create an Inventor object from a dictionary."""
- person=Person.from_dict(data=data)
- addresses=[]
- if"correspondenceAddressBag"indata:
- addresses=[
- Address.from_dict(data=addr)
- foraddrindata.get("correspondenceAddressBag",[])
- ]
-
+"""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)
+ foraindata.get("correspondenceAddressBag",[])
+ ifisinstance(a,dict)
+ ]returncls(
- first_name=person.first_name,
- middle_name=person.middle_name,
- last_name=person.last_name,
- name_prefix=person.name_prefix,
- name_suffix=person.name_suffix,
- preferred_name=person.preferred_name,
- country_code=person.country_code,
+ **pf,inventor_name_text=data.get("inventorNameText"),
- correspondence_address_bag=addresses,
+ correspondence_address_bag=addrs,)
+
+
+
+[docs]
+ defto_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()forainself.correspondence_address_bag
+ ],
+ }
+ )
+ return{
+ k:v
+ fork,vind.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)
+ }
[docs]
-@dataclass
+@dataclass(frozen=True)classAttorney(Person):
-"""Represents an attorney in the patent data."""
+"""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]=Noneactive_indicator:Optional[str]=None
@@ -323,45 +864,79 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Attorney":
-"""Create an Attorney object from a dictionary."""
- person=Person.from_dict(data=data)
- addresses=[]
- if"attorneyAddressBag"indata:
- addresses=[
- Address.from_dict(data=addr)
- foraddrindata.get("attorneyAddressBag",[])
- ]
-
- telecom_addresses=[]
- if"telecommunicationAddressBag"indata:
- telecom_addresses=[
- Telecommunication.from_dict(data=telecom)
- fortelecomindata.get("telecommunicationAddressBag",[])
- ]
-
+"""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)
+ foraindata.get("attorneyAddressBag",[])
+ ifisinstance(a,dict)
+ ]
+ telecoms=[
+ Telecommunication.from_dict(t)
+ fortindata.get("telecommunicationAddressBag",[])
+ ifisinstance(t,dict)
+ ]returncls(
- first_name=person.first_name,
- middle_name=person.middle_name,
- last_name=person.last_name,
- name_prefix=person.name_prefix,
- name_suffix=person.name_suffix,
- preferred_name=person.preferred_name,
- country_code=person.country_code,
+ **pf,registration_number=data.get("registrationNumber"),active_indicator=data.get("activeIndicator"),registered_practitioner_category=data.get("registeredPractitionerCategory"),
- attorney_address_bag=addresses,
- telecommunication_address_bag=telecom_addresses,
+ attorney_address_bag=addrs,
+ telecommunication_address_bag=telecoms,)
+
+
+
+[docs]
+ defto_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()forainself.attorney_address_bag],
+ "telecommunicationAddressBag":[
+ t.to_dict()fortinself.telecommunication_address_bag
+ ],
+ }
+ )
+ return{
+ k:v
+ fork,vind.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)
+ }
[docs]
-@dataclass
+@dataclass(frozen=True)classEntityStatus:
-"""Represents entity status data."""
+"""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]=Nonebusiness_entity_status_category:Optional[str]=None
@@ -370,20 +945,50 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"EntityStatus":
-"""Create an EntityStatus object from a dictionary."""
+"""Creates an `EntityStatus` instance from a dictionary.
+
+ Args:
+ data (Dict[str, Any]): Dictionary with entity status data.
+
+ Returns:
+ EntityStatus: An instance of `EntityStatus`.
+ """returncls(small_entity_status_indicator=data.get("smallEntityStatusIndicator"),business_entity_status_category=data.get("businessEntityStatusCategory"),)
+
+
+
+[docs]
+ defto_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
+@dataclass(frozen=True)classCustomerNumberCorrespondence:
-"""Represents customer number correspondence data."""
+"""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]=Noneorganization_standard_name:Optional[str]=None
@@ -394,46 +999,76 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"CustomerNumberCorrespondence":
-"""Create a CustomerNumberCorrespondence object from a dictionary."""
- addresses=[]
- if"powerOfAttorneyAddressBag"indata:
- power_of_attorney_bag=data.get("powerOfAttorneyAddressBag",[])
- # Ensure we only process dictionary objects
- addresses=[
- Address.from_dict(data=addr)
- foraddrinpower_of_attorney_bag
- ifisinstance(addr,dict)
- ]
-
- telecom_addresses=[]
- if"telecommunicationAddressBag"indata:
- telecom_bag=data.get("telecommunicationAddressBag",[])
- # Ensure we only process dictionary objects
- telecom_addresses=[
- Telecommunication.from_dict(data=telecom)
- fortelecomintelecom_bag
- ifisinstance(telecom,dict)
- ]
-
+"""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)
+ foraindata.get("powerOfAttorneyAddressBag",[])
+ ifisinstance(a,dict)
+ ]
+ telecoms=[
+ Telecommunication.from_dict(t)
+ fortindata.get("telecommunicationAddressBag",[])
+ ifisinstance(t,dict)
+ ]returncls(patron_identifier=data.get("patronIdentifier"),organization_standard_name=data.get("organizationStandardName"),
- power_of_attorney_address_bag=addresses,
- telecommunication_address_bag=telecom_addresses,
+ power_of_attorney_address_bag=addrs,
+ telecommunication_address_bag=telecoms,)
[docs]
-@dataclass
+@dataclass(frozen=True)classRecordAttorney:
-"""Represents record attorney data."""
+"""Represents information about the attorney(s) of record for a patent application.
- customer_number_correspondence_data:List[CustomerNumberCorrespondence]=field(
- default_factory=list
- )
+ 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]=Nonepower_of_attorney_bag:List[Attorney]=field(default_factory=list)attorney_bag:List[Attorney]=field(default_factory=list)
@@ -441,67 +1076,119 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"RecordAttorney":
-"""Create a RecordAttorney object from a dictionary."""
- customer_correspondence=[]
- if"customerNumberCorrespondenceData"indata:
- correspondence_data=data.get("customerNumberCorrespondenceData",[])
- # Ensure we only process dictionary objects
- customer_correspondence=[
- CustomerNumberCorrespondence.from_dict(corr)
- forcorrincorrespondence_data
- ifisinstance(corr,dict)
- ]
-
- power_attorneys=[]
- if"powerOfAttorneyBag"indata:
- power_attorneys=[
- Attorney.from_dict(data=attorney)
- forattorneyindata.get("powerOfAttorneyBag",[])
- ]
-
- attorneys=[]
- if"attorneyBag"indata:
- attorneys=[
- Attorney.from_dict(data=attorney)
- forattorneyindata.get("attorneyBag",[])
- ]
-
+"""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)
+ ifisinstance(cust_corr_data,dict)
+ elseNone
+ )
+ poa_bag=[
+ Attorney.from_dict(a)
+ foraindata.get("powerOfAttorneyBag",[])
+ ifisinstance(a,dict)
+ ]
+ att_bag=[
+ Attorney.from_dict(a)
+ foraindata.get("attorneyBag",[])
+ ifisinstance(a,dict)
+ ]returncls(
- customer_number_correspondence_data=customer_correspondence,
- power_of_attorney_bag=power_attorneys,
- attorney_bag=attorneys,
+ customer_number_correspondence_data=cust_corr,
+ power_of_attorney_bag=poa_bag,
+ attorney_bag=att_bag,)
+
+
+
+[docs]
+ defto_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()
+ ifself.customer_number_correspondence_data
+ elseNone
+ ),
+ "powerOfAttorneyBag":[p.to_dict()forpinself.power_of_attorney_bag],
+ "attorneyBag":[a.to_dict()forainself.attorney_bag],
+ }
+ return{k:vfork,vind.items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classAssignor:
-"""Represents an assignor in an assignment."""
+"""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[str]=None
+ execution_date:Optional[date]=None
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Assignor":
-"""Create an Assignor object from a dictionary."""
+"""Creates an `Assignor` instance from a dictionary.
+
+ Args:
+ data (Dict[str, Any]): Dictionary with assignor data.
+
+ Returns:
+ Assignor: An instance of `Assignor`.
+ """returncls(assignor_name=data.get("assignorName"),
- execution_date=data.get("executionDate"),
+ execution_date=parse_to_date(data.get("executionDate")),)
+
+
+
+[docs]
+ defto_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
+@dataclass(frozen=True)classAssignee:
-"""Represents an assignee in an assignment."""
+"""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]=Noneassignee_address:Optional[Address]=None
@@ -510,130 +1197,314 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"Assignee":
-"""Create an Assignee object from a dictionary."""
- address=None
- if"assigneeAddress"indataanddata.get("assigneeAddress")isnotNone:
- address=Address.from_dict(data=data.get("assigneeAddress",{}))
+"""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)ifisinstance(addr_data,dict)elseNonereturncls(
- assignee_name_text=data.get("assigneeNameText"),assignee_address=address
+ assignee_name_text=data.get("assigneeNameText"),assignee_address=addr)
[docs]
-@dataclass
+@dataclass(frozen=True)classAssignment:
-"""Represents an assignment in the patent data."""
+"""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]=Noneframe_number:Optional[int]=Nonereel_and_frame_number:Optional[str]=None
+ page_total_quantity:Optional[int]=Noneassignment_document_location_uri:Optional[str]=None
- assignment_received_date:Optional[str]=None
- assignment_recorded_date:Optional[str]=None
- assignment_mailed_date:Optional[str]=None
+ assignment_received_date:Optional[date]=None
+ assignment_recorded_date:Optional[date]=None
+ assignment_mailed_date:Optional[date]=Noneconveyance_text:Optional[str]=None
+ image_available_status_code:Optional[bool]=None
+ attorney_docket_number:Optional[str]=Noneassignor_bag:List[Assignor]=field(default_factory=list)assignee_bag:List[Assignee]=field(default_factory=list)
- correspondence_address_bag:List[Address]=field(default_factory=list)
+ correspondence_address:Optional[Address]=None
+ domestic_representative:Optional[Address]=None
[docs]
-@dataclass
+@dataclass(frozen=True)classForeignPriority:
-"""Represents foreign priority information."""
+"""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[str]=None
+ filing_date:Optional[date]=Noneapplication_number_text:Optional[str]=None
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"ForeignPriority":
-"""Create a ForeignPriority object from a dictionary."""
+"""Creates a `ForeignPriority` instance from a dictionary.
+
+ Args:
+ data (Dict[str, Any]): Dictionary with foreign priority data.
+
+ Returns:
+ ForeignPriority: An instance of `ForeignPriority`.
+ """returncls(ip_office_name=data.get("ipOfficeName"),
- filing_date=data.get("filingDate"),
+ filing_date=parse_to_date(data.get("filingDate")),application_number_text=data.get("applicationNumberText"),)
+
+
+
+[docs]
+ defto_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
+@dataclass(frozen=True)classContinuity:
-"""Base class for continuity information."""
+"""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]=Noneapplication_number_text:Optional[str]=None
- filing_date:Optional[str]=None
+ filing_date:Optional[date]=Nonestatus_code:Optional[int]=Nonestatus_description_text:Optional[str]=Nonepatent_number:Optional[str]=Noneclaim_parentage_type_code:Optional[str]=None
- claim_parentage_type_code_description_text:Optional[str]=None
+ claim_parentage_type_code_description_text:Optional[str]=None
+
+ @property
+ defis_aia(self)->Optional[bool]:
+"""Returns True if the application is AIA, False if pre-AIA, None if unknown."""
+ returnself.first_inventor_to_file_indicator
+
+ @property
+ defis_pre_aia(self)->Optional[bool]:
+"""Returns True if the application is pre-AIA, False if AIA, None if unknown."""
+ ifself.first_inventor_to_file_indicatorisNone:
+ returnNone
+ returnnotself.first_inventor_to_file_indicator
+
+
+[docs]
+ defto_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
+ fork,vinasdict(self).items()
+ ifvisnotNoneandnotk.startswith("is_")
+ }
+
[docs]
-@dataclass
+@dataclass(frozen=True)classParentContinuity(Continuity):
-"""Represents parent continuity information."""
+"""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]=Noneparent_patent_number:Optional[str]=Noneparent_application_status_description_text:Optional[str]=None
- parent_application_filing_date:Optional[str]=None
+ parent_application_filing_date:Optional[date]=Noneparent_application_number_text:Optional[str]=Nonechild_application_number_text:Optional[str]=None
@@ -641,7 +1512,15 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"ParentContinuity":
-"""Create a ParentContinuity object from a dictionary."""
+"""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"))returncls(first_inventor_to_file_indicator=data.get("firstInventorToFileIndicator"),parent_application_status_code=data.get("parentApplicationStatusCode"),
@@ -649,42 +1528,87 @@
Source code for pyUSPTO.models.patent_data
parent_application_status_description_text=data.get("parentApplicationStatusDescriptionText"),
- parent_application_filing_date=data.get("parentApplicationFilingDate"),
+ 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"),
- # Map parent-specific fields to base class fields
+ application_number_text=data.get("parentApplicationNumberText"),
+ filing_date=p_filing_date,status_code=data.get("parentApplicationStatusCode"),status_description_text=data.get("parentApplicationStatusDescriptionText"),
- filing_date=data.get("parentApplicationFilingDate"),
- application_number_text=data.get("parentApplicationNumberText"),patent_number=data.get("parentPatentNumber"),)
+
+
+
+[docs]
+ defto_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:vfork,vin_dict.items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classChildContinuity(Continuity):
-"""Represents child continuity information."""
+"""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]=Noneparent_application_number_text:Optional[str]=Nonechild_application_number_text:Optional[str]=Nonechild_application_status_description_text:Optional[str]=None
- child_application_filing_date:Optional[str]=None
+ child_application_filing_date:Optional[date]=Nonechild_patent_number:Optional[str]=None
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"ChildContinuity":
-"""Create a ChildContinuity object from a dictionary."""
+"""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"))returncls(first_inventor_to_file_indicator=data.get("firstInventorToFileIndicator"),child_application_status_code=data.get("childApplicationStatusCode"),
@@ -693,73 +1617,163 @@
Source code for pyUSPTO.models.patent_data
child_application_status_description_text=data.get("childApplicationStatusDescriptionText"),
- child_application_filing_date=data.get("childApplicationFilingDate"),
+ 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"),
- # Map child-specific fields to base class fields
+ application_number_text=data.get("childApplicationNumberText"),
+ filing_date=c_filing_date,status_code=data.get("childApplicationStatusCode"),status_description_text=data.get("childApplicationStatusDescriptionText"),
- filing_date=data.get("childApplicationFilingDate"),
- application_number_text=data.get("childApplicationNumberText"),patent_number=data.get("childPatentNumber"),)
+
+
+
+[docs]
+ defto_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:vfork,vin_dict.items()ifvisnotNone}
[docs]
-@dataclass
+@dataclass(frozen=True)classPatentTermAdjustmentHistoryData:
-"""Represents patent term adjustment history data."""
+"""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[str]=None
+ event_date:Optional[date]=Noneapplicant_day_delay_quantity:Optional[float]=Noneevent_description_text:Optional[str]=Noneevent_sequence_number:Optional[float]=None
- ip_office_day_delay_quantity:Optional[float]=Noneoriginating_event_sequence_number:Optional[float]=Nonepta_pte_code:Optional[str]=None
+ ip_office_day_delay_quantity:Optional[float]=None
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"PatentTermAdjustmentHistoryData":
-"""Create a PatentTermAdjustmentHistoryData object from a dictionary."""
+"""Creates a `PatentTermAdjustmentHistoryData` instance from a dictionary.
+
+ Args:
+ data (Dict[str, Any]): Dictionary with PTA history event data.
+
+ Returns:
+ PatentTermAdjustmentHistoryData: An instance of `PatentTermAdjustmentHistoryData`.
+ """returncls(
- event_date=data.get("eventDate"),
+ 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"),
- ip_office_day_delay_quantity=data.get("ipOfficeDayDelayQuantity"),originating_event_sequence_number=data.get("originatingEventSequenceNumber"),pta_pte_code=data.get("ptaPTECode"),
+ ip_office_day_delay_quantity=data.get("ipOfficeDayDelayQuantity"),)
[docs]
-@dataclass
+@dataclass(frozen=True)classPatentTermAdjustmentData:
-"""Represents patent term adjustment data."""
+"""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]=Noneadjustment_total_quantity:Optional[float]=Noneapplicant_day_delay_quantity:Optional[float]=Noneb_delay_quantity:Optional[float]=Nonec_delay_quantity:Optional[float]=None
- filing_date:Optional[str]=None
- grant_date:Optional[str]=Nonenon_overlapping_day_quantity:Optional[float]=Noneoverlapping_day_quantity:Optional[float]=None
- ip_office_day_delay_quantity:Optional[float]=None
+ non_overlapping_day_delay_quantity:Optional[float]=None
+ ip_office_adjustment_delay_quantity:Optional[float]=Nonepatent_term_adjustment_history_data_bag:List[PatentTermAdjustmentHistoryData]=(field(default_factory=list))
@@ -768,104 +1782,246 @@
Source code for pyUSPTO.models.patent_data
[docs]@classmethoddeffrom_dict(cls,data:Dict[str,Any])->"PatentTermAdjustmentData":
-"""Create a PatentTermAdjustmentData object from a dictionary."""
- history_data=[]
- if"patentTermAdjustmentHistoryDataBag"indata:
- history_data=[
- PatentTermAdjustmentHistoryData.from_dict(history)
- forhistoryindata.get("patentTermAdjustmentHistoryDataBag",[])
- ]
-
+"""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)
+ forhindata.get("patentTermAdjustmentHistoryDataBag",[])
+ ifisinstance(h,dict)
+ ]returncls(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"),
- filing_date=data.get("filingDate"),
- grant_date=data.get("grantDate"),non_overlapping_day_quantity=data.get("nonOverlappingDayQuantity"),overlapping_day_quantity=data.get("overlappingDayQuantity"),
- ip_office_day_delay_quantity=data.get("ipOfficeDayDelayQuantity"),
- patent_term_adjustment_history_data_bag=history_data,
+ non_overlapping_day_delay_quantity=data.get(
+ "nonOverlappingDayDelayQuantity"
+ ),
+ ip_office_adjustment_delay_quantity=data.get(
+ "ipOfficeAdjustmentDelayQuantity"
+ ),
+ patent_term_adjustment_history_data_bag=history,)
+
+
+
+[docs]
+ defto_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()forhinself.patent_term_adjustment_history_data_bag
+ ]
+ return{
+ to_camel_case(k):v
+ fork,vind.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)
+ }
-
-[docs]
-@dataclass
-classEvent:
-"""Represents an event in the patent data."""
+
+[docs]
+@dataclass(frozen=True)
+classEventData:
+"""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]=Noneevent_description_text:Optional[str]=None
- event_date:Optional[str]=None
+ event_date:Optional[date]=None
-
+[docs]@classmethod
- deffrom_dict(cls,data:Dict[str,Any])->"Event":
-"""Create an Event object from a dictionary."""
+ deffrom_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`.
+ """returncls(event_code=data.get("eventCode"),event_description_text=data.get("eventDescriptionText"),
- event_date=data.get("eventDate"),
+ event_date=parse_to_date(data.get("eventDate")),)
+
+
+
+[docs]
+ defto_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):vfork,vind.items()ifvisnotNone}
+[docs]
+@dataclass(frozen=True)
+classPrintedMetaData:
+"""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]=Noneproduct_identifier:Optional[str]=Nonefile_location_uri:Optional[str]=None
- file_create_date_time:Optional[str]=None
+ file_create_date_time:Optional[datetime]=Nonexml_file_name:Optional[str]=None
-
+[docs]@classmethod
- deffrom_dict(cls,data:Dict[str,Any])->"DocumentMetaData":
-"""Create a DocumentMetaData object from a dictionary."""
+ deffrom_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`.
+ """returncls(zip_file_name=data.get("zipFileName"),product_identifier=data.get("productIdentifier"),file_location_uri=data.get("fileLocationURI"),
- file_create_date_time=data.get("fileCreateDateTime"),
+ file_create_date_time=parse_to_datetime_utc(data.get("fileCreateDateTime")),xml_file_name=data.get("xmlFileName"),)
+
+
+
+[docs]
+ defto_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]={}
+ ifself.zip_file_nameisnotNone:
+ final_dict["zipFileName"]=self.zip_file_name
+ ifself.product_identifierisnotNone:
+ final_dict["productIdentifier"]=self.product_identifier
+ ifself.file_location_uriisnotNone:
+ final_dict["fileLocationURI"]=self.file_location_uri
+ ifself.file_create_date_timeisnotNone:
+ final_dict["fileCreateDateTime"]=serialize_datetime_as_naive(
+ self.file_create_date_time
+ )
+ ifself.xml_file_nameisnotNone:
+ final_dict["xmlFileName"]=self.xml_file_name
+ returnfinal_dict
[docs]
-@dataclass
+@dataclass(frozen=True)classApplicationMetaData:
-"""Represents application metadata."""
+"""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]=Noneentity_status_data:Optional[EntityStatus]=None
- publication_date_bag:List[str]=field(default_factory=list)
+ 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[str]=None
+ first_inventor_to_file_indicator:Optional[bool]=Nonefirst_applicant_name:Optional[str]=Nonefirst_inventor_name:Optional[str]=Noneapplication_confirmation_number:Optional[int]=None
- application_status_date:Optional[str]=None
+ application_status_date:Optional[date]=Noneapplication_status_description_text:Optional[str]=None
- filing_date:Optional[str]=None
- effective_filing_date:Optional[str]=None
- grant_date:Optional[str]=None
+ filing_date:Optional[date]=None
+ effective_filing_date:Optional[date]=None
+ grant_date:Optional[date]=Nonegroup_art_unit_number:Optional[str]=Noneapplication_type_code:Optional[str]=Noneapplication_type_label_name:Optional[str]=None
@@ -874,65 +2030,91 @@
Source code for pyUSPTO.models.patent_data
patent_number:Optional[str]=Noneapplication_status_code:Optional[int]=Noneearliest_publication_number:Optional[str]=None
- earliest_publication_date:Optional[str]=None
+ earliest_publication_date:Optional[date]=Nonepct_publication_number:Optional[str]=None
- pct_publication_date:Optional[str]=None
- international_registration_publication_date:Optional[str]=None
+ pct_publication_date:Optional[date]=None
+ international_registration_publication_date:Optional[date]=Noneinternational_registration_number:Optional[str]=Noneexaminer_name_text:Optional[str]=None
- class_field:Optional[str]=None# 'class' is a reserved keyword
+ class_field:Optional[str]=Nonesubclass:Optional[str]=Noneuspc_symbol_text:Optional[str]=Nonecustomer_number:Optional[int]=Nonecpc_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
+ defis_aia(self)->Optional[bool]:
+"""Returns True if the application is AIA, False if pre-AIA, None if unknown."""
+ returnself.first_inventor_to_file_indicator
+
+ @property
+ defis_pre_aia(self)->Optional[bool]:
+"""Returns True if the application is pre-AIA, False if AIA, None if unknown."""
+ ifself.first_inventor_to_file_indicatorisNone:
+ returnNone
+ returnnotself.first_inventor_to_file_indicator
[docs]
-@dataclass
+@dataclass(frozen=True)classPatentFileWrapper:
-"""Represents a patent file wrapper."""
+"""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]=Noneapplication_meta_data:Optional[ApplicationMetaData]=None
@@ -978,112 +2261,634 @@
+[docs]
+@dataclass(frozen=True)
+classPatentDataResponse:
+"""Represents the overall response from a patent data API request.
- child_continuities=[]
- if"childContinuityBag"indata:
- child_continuities=[
- ChildContinuity.from_dict(data=continuity)
- forcontinuityindata.get("childContinuityBag",[])
- ]
+ It typically includes a count of the results and a list of PatentFileWrapper objects,
+ each containing detailed data for a patent application.
- patent_term=None
- if(
- "patentTermAdjustmentData"indata
- anddata.get("patentTermAdjustmentData")isnotNone
- ):
- patent_term=PatentTermAdjustmentData.from_dict(
- data=data.get("patentTermAdjustmentData",{})
+ 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
+ deffrom_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)
+ forwindata.get("patentFileWrapperDataBag",[])
+ ifisinstance(w,dict)
+ ]
+ returncls(
+ count=data.get("count",0),
+ patent_file_wrapper_data_bag=wrappers,
+ request_identifier=data.get("requestIdentifier"),
+ raw_data=json.dumps(data)ifinclude_raw_dataelseNone,
+ )
+[docs]
+@dataclass(frozen=True)
+classStatusCode:
+"""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."""
+ returnf"{self.code}: {self.description}"
+
+
+
+[docs]
+ @classmethod
+ deffrom_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"indata:
+ returncls(
+ code=data.get("code"),
+ description=data.get("description"),)
+ else:
+ returncls(
+ code=data.get("applicationStatusCode"),
+ description=data.get("applicationStatusDescriptionText"),
+ )
+
+
+
+[docs]
+ defto_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]
+classStatusCodeCollection:
+"""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)
+[docs]
+ deffind_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.
+ """
+ forstatusinself._status_codes:
+ ifstatus.code==code_to_find:
+ returnstatus
+ returnNone
+
+
+
+[docs]
+ defsearch_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
+ forsinself._status_codes
+ ifs.descriptionandtext.lower()ins.description.lower()
+ ]
+ returnStatusCodeCollection(status_codes=matching)
+
+
+
+[docs]
+ defto_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()forscinself._status_codes]
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classStatusCodeSearchResponse:
+"""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
+ deffrom_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)forcdincodes_jsonifisinstance(cd,dict)]
+ ifisinstance(codes_json,list)
+ else[]
+ )
+ collection=StatusCodeCollection(parsed_codes)returncls(
- application_number_text=data.get("applicationNumberText"),
- application_meta_data=application_meta,
- correspondence_address_bag=addresses,
- assignment_bag=assignments,
- record_attorney=record_atty,
- foreign_priority_bag=foreign_priorities,
- parent_continuity_bag=parent_continuities,
- child_continuity_bag=child_continuities,
- patent_term_adjustment_data=patent_term,
- event_data_bag=events,
- pgpub_document_meta_data=pgpub_meta,
- grant_document_meta_data=grant_meta,
- last_ingestion_date_time=data.get("lastIngestionDateTime"),
+ count=data.get("count",0),
+ status_code_bag=collection,
+ request_identifier=data.get("requestIdentifier"),
+ )
+
+
+
+[docs]
+ defto_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
+ fork,vin_dict.items()
+ ifvisnotNoneand(notisinstance(v,list)orv)
+ }
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classApplicationContinuityData:
+"""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
+ deffrom_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`.
+ """
+ returncls(
+ parent_continuity_bag=wrapper.parent_continuity_bag,
+ child_continuity_bag=wrapper.child_continuity_bag,)
+
+
+
+[docs]
+ defto_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()forpcinself.parent_continuity_bag],
+ "childContinuityBag":[cc.to_dict()forccinself.child_continuity_bag],
+ }
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classPrintedPublication:
+"""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
+ deffrom_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`.
+ """
+ returncls(
+ pgpub_document_meta_data=wrapper.pgpub_document_meta_data,
+ grant_document_meta_data=wrapper.grant_document_meta_data,
+ )
+
+
+
+[docs]
+ defto_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()
+ ifself.pgpub_document_meta_data
+ elseNone
+ ),
+ "grantDocumentMetaData":(
+ self.grant_document_meta_data.to_dict()
+ ifself.grant_document_meta_data
+ elseNone
+ ),
+ }
+"""
+models.ptab - Data models for USPTO PTAB (Patent Trial and Appeal Board) APIs
+
+This module provides data models, primarily using frozen dataclasses, for
+representing responses from the USPTO PTAB APIs. These models cover:
+- Patent trial proceedings (IPR, PGR, CBM, DER)
+- Trial documents and decisions
+- Appeal decisions
+- Interference decisions
+"""
+
+importjson
+fromdataclassesimportasdict,dataclass,field
+fromdatetimeimportdate,datetime
+fromtypingimportAny,Dict,List,Optional
+
+try:
+ fromtypingimportSelf
+exceptImportError:
+ fromtyping_extensionsimportSelf
+
+# Import parsing utilities from models utils module
+frompyUSPTO.models.utilsimport(
+ parse_to_date,
+ parse_to_datetime_utc,
+ serialize_date,
+ serialize_datetime_as_iso,
+ serialize_datetime_as_naive,
+ to_camel_case,
+)
+
+
+@dataclass(frozen=True)
+classPartyData:
+"""Base class for all party data models across PTAB endpoints.
+
+ Attributes:
+ application_number_text: Application number.
+ counsel_name: Name of counsel.
+ grant_date: Patent grant date.
+ group_art_unit_number: Art unit number.
+ inventor_name: Name of inventor.
+ patent_number: Patent number.
+ technology_center_number: Technology center number.
+ real_party_in_interest_name: Real party in interest name.
+ patent_owner_name: Patent owner name.
+ publication_date: Publication date (if applicable).
+ publication_number: Publication number (if applicable).
+ """
+
+ application_number_text:Optional[str]=None
+ counsel_name:Optional[str]=None
+ grant_date:Optional[date]=None
+ group_art_unit_number:Optional[str]=None
+ inventor_name:Optional[str]=None
+ real_party_in_interest_name:Optional[str]=None
+ patent_number:Optional[str]=None
+ patent_owner_name:Optional[str]=None
+ technology_center_number:Optional[str]=None
+ publication_date:Optional[date]=None
+ publication_number:Optional[str]=None
+
+ @classmethod
+ deffrom_dict(cls,data:Dict[str,Any],include_raw_data:bool=False)->Self:
+"""Creates a PartyData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing party data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ PartyData: A populated PartyData instance.
+ """
+ returncls(
+ application_number_text=data.get("applicationNumberText"),
+ counsel_name=data.get("counselName"),
+ grant_date=parse_to_date(data.get("grantDate")),
+ group_art_unit_number=data.get("groupArtUnitNumber"),
+ inventor_name=data.get("inventorName"),
+ real_party_in_interest_name=data.get("realPartyInInterestName"),
+ patent_number=data.get("patentNumber"),
+ patent_owner_name=data.get("patentOwnerName"),
+ technology_center_number=data.get("technologyCenterNumber"),
+ publication_date=parse_to_date(data.get("publicationDate")),
+ publication_number=data.get("publicationNumber"),
+ )
+
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the PartyData instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+ fork,vinasdict(self).items():
+ ifvisnotNone:
+ ifisinstance(v,date):
+ result[to_camel_case(k)]=serialize_date(v)
+ else:
+ result[to_camel_case(k)]=v
+ returnresult
+
+
+# ============================================================================
+# TRIAL PROCEEDINGS MODELS
+# ============================================================================
+
+
+
+[docs]
+@dataclass(frozen=True)
+classTrialMetaData:
+"""Trial metadata including status, dates, and download URI.
+
+ Attributes:
+ petition_filing_date: Date the petition was filed.
+ accorded_filing_date: The filing date accorded to the petition.
+ trial_last_modified_date_time: Last modification timestamp.
+ trial_last_modified_date: Last modification date.
+ trial_status_category: Status of the trial (e.g., "Institution Denied", "Instituted").
+ trial_type_code: Type of trial (IPR, PGR, CBM, DER).
+ file_download_uri: URI to download ZIP of all trial documents.
+ termination_date: Date the trial was terminated.
+ latest_decision_date: Date of the most recent decision.
+ institution_decision_date: Date of the institution decision.
+ """
+
+ petition_filing_date:Optional[date]=None
+ accorded_filing_date:Optional[date]=None
+ trial_last_modified_date_time:Optional[datetime]=None
+ trial_last_modified_date:Optional[date]=None
+ trial_status_category:Optional[str]=None
+ trial_type_code:Optional[str]=None
+ file_download_uri:Optional[str]=None
+ termination_date:Optional[date]=None
+ latest_decision_date:Optional[date]=None
+ institution_decision_date:Optional[date]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"TrialMetaData":
+"""Creates a TrialMetaData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing trial metadata from API response.
+ include_raw_data: Ignored for this model (no raw_data field).
+
+ Returns:
+ TrialMetaData: An instance of TrialMetaData.
+ """
+ # Handle aliases
+ file_download_uri=data.get("fileDownloadURI")ordata.get("downloadURI")
+ returncls(
+ petition_filing_date=parse_to_date(data.get("petitionFilingDate")),
+ accorded_filing_date=parse_to_date(data.get("accordedFilingDate")),
+ trial_last_modified_date_time=parse_to_datetime_utc(
+ data.get("trialLastModifiedDateTime")
+ ),
+ trial_last_modified_date=parse_to_date(data.get("trialLastModifiedDate")),
+ trial_status_category=data.get("trialStatusCategory"),
+ trial_type_code=data.get("trialTypeCode"),
+ file_download_uri=file_download_uri,
+ termination_date=parse_to_date(data.get("terminationDate")),
+ latest_decision_date=parse_to_date(data.get("latestDecisionDate")),
+ institution_decision_date=parse_to_date(
+ data.get("institutionDecisionDate")
+ ),
+ )
+[docs]
+@dataclass(frozen=True)
+classPatentOwnerData(PartyData):
+"""Party data for a patent owner in PTAB trial proceedings.
+
+ Inherits all attributes from PartyData. Used in IPR, PGR, CBM,
+ and DER proceedings to represent the patent holder.
+ """
+
+ pass
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classRegularPetitionerData:
+"""Regular petitioner information.
+
+ Attributes:
+ counsel_name: Name of counsel.
+ real_party_in_interest_name: Real party in interest name.
+ """
+
+ counsel_name:Optional[str]=None
+ real_party_in_interest_name:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"RegularPetitionerData":
+"""Creates a RegularPetitionerData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing petitioner data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ RegularPetitionerData: An instance of RegularPetitionerData.
+ """
+ returncls(
+ counsel_name=data.get("counselName"),
+ real_party_in_interest_name=data.get("realPartyInInterestName"),
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the RegularPetitionerData instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+
+ ifself.counsel_nameisnotNone:
+ result["counselName"]=self.counsel_name
+ ifself.real_party_in_interest_nameisnotNone:
+ result["realPartyInInterestName"]=self.real_party_in_interest_name
+
+ returnresult
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classRespondentData(PartyData):
+"""Respondent party data in derivation proceedings.
+
+ Inherits all attributes from PartyData. Used in DER proceedings
+ to represent the responding party.
+ """
+
+ pass
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classDerivationPetitionerData(PartyData):
+"""Derivation petitioner data in derivation proceedings.
+
+ Inherits all attributes from PartyData. Used in DER proceedings
+ to represent the petitioning party claiming derivation.
+ """
+
+ pass
+[docs]
+@dataclass(frozen=True)
+classTrialDocumentData:
+"""Metadata for a document in a PTAB trial.
+
+ Attributes:
+ document_category: Category of the document.
+ document_filing_date: Filing date.
+ document_identifier: Unique ID.
+ document_name: Filename.
+ document_number: Document number in the proceeding.
+ document_size_quantity: Size in bytes.
+ document_ocr_text: OCR text content.
+ document_title_text: Title of the document.
+ document_type_description_text: Description of document type.
+ file_download_uri: URL to download the file.
+ filing_party_category: Who filed (e.g., "Petitioner").
+ mime_type_identifier: MIME type (e.g., "application/pdf").
+ document_status: Public status.
+ """
+
+ # document_category: Optional[str] = None # Removed: Documented but not in API.
+ document_filing_date:Optional[date]=None
+ document_identifier:Optional[str]=None
+ document_name:Optional[str]=None
+ document_number:Optional[str]=None
+ document_size_quantity:Optional[int]=None
+ document_ocr_text:Optional[str]=None
+ document_title_text:Optional[str]=None
+ document_type_description_text:Optional[str]=None
+ file_download_uri:Optional[str]=None
+ filing_party_category:Optional[str]=None
+ # mime_type_identifier: Optional[str] = None # Removed: Documented but not in API.
+ # document_status: Optional[str] = None # Removed: Documented but not in API.
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"TrialDocumentData":
+"""Creates a TrialDocumentData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing document data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ TrialDocumentData: An instance of TrialDocumentData.
+ """
+ # Handle aliases
+ file_download_uri=data.get("fileDownloadURI")ordata.get("downloadURI")
+ returncls(
+ # document_category=data.get("documentCategory"), # Removed: Documented but not in API.
+ document_filing_date=parse_to_date(data.get("documentFilingDate")),
+ document_identifier=data.get("documentIdentifier"),
+ document_name=data.get("documentName"),
+ document_number=data.get("documentNumber"),
+ document_size_quantity=data.get("documentSizeQuantity"),
+ document_ocr_text=data.get("documentOCRText"),
+ document_title_text=data.get("documentTitleText"),
+ document_type_description_text=data.get("documentTypeDescriptionText"),
+ file_download_uri=file_download_uri,
+ filing_party_category=data.get("filingPartyCategory"),
+ # mime_type_identifier=data.get("mimeTypeIdentifier"),
+ # document_status=data.get("documentStatus"),
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the TrialDocumentData instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+
+ # Removed: Documented but not in API.
+ # if self.document_category is not None:
+ # result["documentCategory"] = self.document_category
+ ifself.document_filing_dateisnotNone:
+ result["documentFilingDate"]=serialize_date(self.document_filing_date)
+ ifself.document_identifierisnotNone:
+ result["documentIdentifier"]=self.document_identifier
+ ifself.document_nameisnotNone:
+ result["documentName"]=self.document_name
+ ifself.document_numberisnotNone:
+ result["documentNumber"]=self.document_number
+ ifself.document_size_quantityisnotNone:
+ result["documentSizeQuantity"]=self.document_size_quantity
+ ifself.document_ocr_textisnotNone:
+ result["documentOCRText"]=self.document_ocr_text# Uppercase OCR
+ ifself.document_title_textisnotNone:
+ result["documentTitleText"]=self.document_title_text
+ ifself.document_type_description_textisnotNone:
+ result["documentTypeDescriptionText"]=self.document_type_description_text
+ ifself.file_download_uriisnotNone:
+ result["fileDownloadURI"]=self.file_download_uri# Uppercase URI
+ ifself.filing_party_categoryisnotNone:
+ result["filingPartyCategory"]=self.filing_party_category
+ # Removed: Documented but not in API.
+ # if self.mime_type_identifier is not None:
+ # result["mimeTypeIdentifier"] = self.mime_type_identifier
+ # if self.document_status is not None:
+ # result["documentStatus"] = self.document_status
+
+ returnresult
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classTrialDecisionData:
+"""Metadata for a decision in a PTAB trial.
+
+ Attributes:
+ statute_and_rule_bag: List of applicable statutes and rules.
+ decision_issue_date: Date issued.
+ decision_type_category: Type of decision (e.g. "Final Written Decision").
+ issue_type_bag: List of issues addressed.
+ trial_outcome_category: Outcome (e.g., "Denied").
+ """
+
+ statute_and_rule_bag:List[str]=field(default_factory=list)
+ decision_issue_date:Optional[date]=None
+ decision_type_category:Optional[str]=None
+ issue_type_bag:List[str]=field(default_factory=list)
+ trial_outcome_category:Optional[str]=None
+
+
+[docs]
+@dataclass(frozen=True)
+classAppealMetaData:
+"""Appeal metadata.
+
+ Attributes:
+ appeal_filing_date: Date the appeal was filed.
+ appeal_last_modified_date: Last modification date.
+ appeal_last_modified_date_time: Last modification timestamp.
+ application_type_category: Type of application.
+ docket_notice_mailed_date: Date the docket notice was mailed.
+ file_download_uri: URI to download ZIP of appeal documents.
+ """
+
+ appeal_filing_date:Optional[date]=None
+ appeal_last_modified_date:Optional[date]=None
+ appeal_last_modified_date_time:Optional[datetime]=None
+ application_type_category:Optional[str]=None
+ docket_notice_mailed_date:Optional[date]=None
+ file_download_uri:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"AppealMetaData":
+"""Creates an AppealMetaData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing appeal metadata from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ AppealMetaData: An instance of AppealMetaData.
+ """
+ # Handle aliases
+ file_download_uri=data.get("fileDownloadURI")ordata.get("downloadURI")
+ returncls(
+ appeal_filing_date=parse_to_date(data.get("appealFilingDate")),
+ appeal_last_modified_date=parse_to_date(data.get("appealLastModifiedDate")),
+ appeal_last_modified_date_time=parse_to_datetime_utc(
+ data.get("appealLastModifiedDateTime")
+ ),
+ application_type_category=data.get("applicationTypeCategory"),
+ docket_notice_mailed_date=parse_to_date(data.get("docketNoticeMailedDate")),
+ file_download_uri=file_download_uri,
+ )
+[docs]
+@dataclass(frozen=True)
+classAppellantData(PartyData):
+"""Appellant party data in PTAB appeals.
+
+ Inherits all attributes from PartyData. Used in appeal proceedings
+ to represent the party appealing an examiner decision.
+ """
+
+ pass
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classRequestorData:
+"""Third party requestor information.
+
+ Attributes:
+ third_party_name: Name of the third party.
+ """
+
+ third_party_name:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"RequestorData":
+"""Creates a RequestorData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing requestor data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ RequestorData: An instance of RequestorData.
+ """
+ returncls(
+ third_party_name=data.get("thirdPartyName"),
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the RequestorData instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+ fork,vinasdict(self).items():
+ ifvisnotNone:
+ result[to_camel_case(k)]=v
+ returnresult
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classAppealDocumentData:
+"""Appeal document metadata.
+
+ Attributes:
+ document_filing_date: Date the document was filed.
+ document_identifier: Unique identifier for the document.
+ document_name: Name of the document.
+ document_size_quantity: Size of the document in bytes.
+ document_ocr_text: Full OCR text of the document.
+ document_type_description_text: Description of the document type.
+ file_download_uri: URI to download the document.
+ """
+
+ document_filing_date:Optional[date]=None
+ document_identifier:Optional[str]=None
+ document_name:Optional[str]=None
+ document_size_quantity:Optional[int]=None
+ document_ocr_text:Optional[str]=None
+ document_type_description_text:Optional[str]=None
+ file_download_uri:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"AppealDocumentData":
+"""Creates an AppealDocumentData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing document data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ AppealDocumentData: An instance of AppealDocumentData.
+ """
+ # Handle aliases
+ file_download_uri=data.get("fileDownloadURI")ordata.get("downloadURI")
+ doc_type=data.get("documentTypeDescriptionText")ordata.get(
+ "documentTypeCategory"
+ )
+
+ returncls(
+ document_filing_date=parse_to_date(data.get("documentFilingDate")),
+ document_identifier=data.get("documentIdentifier"),
+ document_name=data.get("documentName"),
+ document_size_quantity=data.get("documentSizeQuantity"),
+ document_ocr_text=data.get("documentOCRText"),
+ document_type_description_text=doc_type,
+ file_download_uri=file_download_uri,
+ )
+[docs]
+@dataclass(frozen=True)
+classDecisionData:
+"""Appeal decision information.
+
+ Attributes:
+ appeal_outcome_category: Outcome of the appeal.
+ statute_and_rule_bag: List of applicable statutes and rules.
+ decision_issue_date: Date the decision was issued.
+ decision_type_category: Type of decision.
+ issue_type_bag: List of issue types.
+ """
+
+ appeal_outcome_category:Optional[str]=None
+ statute_and_rule_bag:List[str]=field(default_factory=list)
+ decision_issue_date:Optional[date]=None
+ decision_type_category:Optional[str]=None
+ issue_type_bag:List[str]=field(default_factory=list)
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"DecisionData":
+"""Creates a DecisionData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing decision data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ DecisionData: An instance of DecisionData.
+ """
+ returncls(
+ appeal_outcome_category=data.get("appealOutcomeCategory"),
+ statute_and_rule_bag=data.get("statuteAndRuleBag",[]),
+ decision_issue_date=parse_to_date(data.get("decisionIssueDate")),
+ decision_type_category=data.get("decisionTypeCategory"),
+ issue_type_bag=data.get("issueTypeBag",[]),
+ )
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"PTABAppealDecision":
+"""Creates a PTABAppealDecision instance from a dictionary.
+
+ Args:
+ data: Dictionary containing appeal decision data from API response.
+ include_raw_data: Whether to include raw JSON data in the instance.
+
+ Returns:
+ PTABAppealDecision: An instance of PTABAppealDecision.
+ """
+ # Parse nested objects
+ appeal_meta=data.get("appealMetaData")
+ appeal_meta_data=(
+ AppealMetaData.from_dict(appeal_meta)ifappeal_metaelseNone
+ )
+
+ # Handle potential typo 'appelantData' vs 'appellantData'
+ appellant=data.get("appellantData")ordata.get("appelantData")
+ appellant_data=AppellantData.from_dict(appellant)ifappellantelseNone
+
+ requestor=data.get("requestorData")
+ requestor_data=RequestorData.from_dict(requestor)ifrequestorelseNone
+
+ document=data.get("documentData")
+ document_data=AppealDocumentData.from_dict(document)ifdocumentelseNone
+
+ decision=data.get("decisionData")
+ decision_data=DecisionData.from_dict(decision)ifdecisionelseNone
+
+ returncls(
+ appeal_number=data.get("appealNumber"),
+ last_modified_date_time=parse_to_datetime_utc(
+ data.get("lastModifiedDateTime")
+ ),
+ appeal_document_category=data.get("appealDocumentCategory"),
+ appeal_meta_data=appeal_meta_data,
+ appellant_data=appellant_data,
+ requestor_data=requestor_data,
+ document_data=document_data,
+ decision_data=decision_data,
+ raw_data=dataifinclude_raw_dataelseNone,
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the PTABAppealDecision instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+
+ # Manually process each field to preserve nested objects
+ ifself.appeal_numberisnotNone:
+ result["appealNumber"]=self.appeal_number
+ ifself.last_modified_date_timeisnotNone:
+ result["lastModifiedDateTime"]=serialize_datetime_as_naive(
+ self.last_modified_date_time
+ )
+ ifself.appeal_document_categoryisnotNone:
+ result["appealDocumentCategory"]=self.appeal_document_category
+ ifself.appeal_meta_dataisnotNone:
+ result["appealMetaData"]=self.appeal_meta_data.to_dict()
+ ifself.appellant_dataisnotNone:
+ result["appellantData"]=self.appellant_data.to_dict()
+ ifself.requestor_dataisnotNone:
+ result["requestorData"]=self.requestor_data.to_dict()
+ ifself.document_dataisnotNone:
+ result["documentData"]=self.document_data.to_dict()
+ ifself.decision_dataisnotNone:
+ result["decisionData"]=self.decision_data.to_dict()
+
+ returnresult
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classPTABAppealResponse:
+"""Response container for PTAB appeals search.
+
+ Attributes:
+ count: Total number of matching results.
+ request_identifier: UUID for the API request.
+ patent_appeal_data_bag: List of appeal decisions.
+ raw_data: Raw JSON response data (if include_raw_data=True).
+ """
+
+ count:int=0
+ request_identifier:Optional[str]=None
+ patent_appeal_data_bag:List[PTABAppealDecision]=field(default_factory=list)
+ raw_data:Optional[Dict[str,Any]]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"PTABAppealResponse":
+"""Creates a PTABAppealResponse instance from a dictionary.
+
+ Args:
+ data: Dictionary containing response data from API.
+ include_raw_data: Whether to include raw JSON data in the instance.
+
+ Returns:
+ PTABAppealResponse: An instance of PTABAppealResponse.
+ """
+ appeals_data=data.get("patentAppealDataBag",[])
+ appeals=[
+ PTABAppealDecision.from_dict(item,include_raw_data=include_raw_data)
+ foriteminappeals_data
+ ]
+
+ returncls(
+ count=data.get("count",0),
+ request_identifier=data.get("requestIdentifier"),
+ patent_appeal_data_bag=appeals,
+ raw_data=dataifinclude_raw_dataelseNone,
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the PTABAppealResponse instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+
+ # Manually process each field
+ ifself.countisnotNone:
+ result["count"]=self.count
+ ifself.request_identifierisnotNone:
+ result["requestIdentifier"]=self.request_identifier
+ if(
+ self.patent_appeal_data_bagisnotNone
+ andlen(self.patent_appeal_data_bag)>0
+ ):
+ result["patentAppealDataBag"]=[
+ decision.to_dict()fordecisioninself.patent_appeal_data_bag
+ ]
+
+ returnresult
+[docs]
+@dataclass(frozen=True)
+classSeniorPartyData(PartyData):
+"""Senior party information in PTAB interference proceedings.
+
+ Inherits all attributes from PartyData. Represents the party with
+ the earlier effective filing date in an interference.
+ """
+
+ pass
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classJuniorPartyData(PartyData):
+"""Junior party information in PTAB interference proceedings.
+
+ Inherits all attributes from PartyData. Represents the party with
+ the later effective filing date in an interference.
+ """
+
+ pass
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classAdditionalPartyData:
+"""Additional party information in an interference.
+
+ Attributes:
+ application_number_text: Application number.
+ inventor_name: Name of inventor.
+ patent_number: Patent number.
+ additional_party_name: Name of additional party.
+ """
+
+ application_number_text:Optional[str]=None
+ inventor_name:Optional[str]=None
+ patent_number:Optional[str]=None
+ additional_party_name:Optional[str]=None
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"AdditionalPartyData":
+"""Creates an AdditionalPartyData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing additional party data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ AdditionalPartyData: An instance of AdditionalPartyData.
+ """
+ returncls(
+ application_number_text=data.get("applicationNumberText"),
+ inventor_name=data.get("inventorName"),
+ patent_number=data.get("patentNumber"),
+ additional_party_name=data.get("additionalPartyName"),
+ )
+
+
+
+[docs]
+ defto_dict(self)->Dict[str,Any]:
+"""Converts the AdditionalPartyData instance to a dictionary.
+
+ Returns:
+ Dict[str, Any]: Dictionary with camelCase keys and None values filtered.
+ """
+ result:Dict[str,Any]={}
+
+ ifself.application_number_textisnotNone:
+ result["applicationNumberText"]=self.application_number_text
+ ifself.inventor_nameisnotNone:
+ result["inventorName"]=self.inventor_name
+ ifself.patent_numberisnotNone:
+ result["patentNumber"]=self.patent_number
+ ifself.additional_party_nameisnotNone:
+ result["additionalPartyName"]=self.additional_party_name
+
+ returnresult
+
+
+
+
+
+[docs]
+@dataclass(frozen=True)
+classInterferenceDocumentData:
+"""Interference document metadata.
+
+ Attributes:
+ document_identifier: Unique identifier for the document.
+ document_name: Name of the document.
+ document_size_quantity: Size of the document in bytes.
+ document_ocr_text: Full OCR text of the document.
+ document_title_text: Title of the document.
+ interference_outcome_category: Outcome of the interference.
+ document_filing_date: Date the document was filed.
+ decision_issue_date: Date the decision was issued.
+ decision_type_category: Type of decision.
+ file_download_uri: URI to download the document.
+ statute_and_rule_bag: List of applicable statutes and rules.
+ issue_type_bag: List of issues addressed.
+ """
+
+ document_identifier:Optional[str]=None
+ document_name:Optional[str]=None
+ document_size_quantity:Optional[int]=None
+ document_ocr_text:Optional[str]=None
+ document_title_text:Optional[str]=None
+ interference_outcome_category:Optional[str]=None
+ document_filing_date:Optional[date]=None
+ decision_issue_date:Optional[date]=None
+ decision_type_category:Optional[str]=None
+ file_download_uri:Optional[str]=None
+ statute_and_rule_bag:List[str]=field(default_factory=list)
+ issue_type_bag:List[str]=field(default_factory=list)
+
+
+[docs]
+ @classmethod
+ deffrom_dict(
+ cls,data:Dict[str,Any],include_raw_data:bool=False
+ )->"InterferenceDocumentData":
+"""Creates an InterferenceDocumentData instance from a dictionary.
+
+ Args:
+ data: Dictionary containing document data from API response.
+ include_raw_data: Ignored for this model.
+
+ Returns:
+ InterferenceDocumentData: An instance of InterferenceDocumentData.
+ """
+ # Handle aliases
+ file_download_uri=data.get("fileDownloadURI")ordata.get("downloadURI")
+
+ returncls(
+ document_identifier=data.get("documentIdentifier"),
+ document_name=data.get("documentName"),
+ document_size_quantity=data.get("documentSizeQuantity"),
+ document_ocr_text=data.get("documentOCRText"),
+ document_title_text=data.get("documentTitleText"),
+ interference_outcome_category=data.get("interferenceOutcomeCategory"),
+ document_filing_date=parse_to_date(data.get("documentFilingDate")),
+ decision_issue_date=parse_to_date(data.get("decisionIssueDate")),
+ decision_type_category=data.get("decisionTypeCategory"),
+ file_download_uri=file_download_uri,
+ statute_and_rule_bag=data.get("statuteAndRuleBag",[]),
+ issue_type_bag=data.get("issueTypeBag",[]),
+ )
+"""
+models.utils - Utility functions for USPTO data models
+
+This module provides utility functions for parsing, serializing, and converting
+data used across USPTO API data models. These utilities handle date/datetime
+conversions, boolean string representations, and string transformations.
+"""
+
+importwarnings
+fromdatetimeimportdate,datetime,timezone,tzinfo
+fromtypingimportOptional
+fromzoneinfoimportZoneInfo,ZoneInfoNotFoundError
+
+frompyUSPTO.warningsimport(
+ USPTOBooleanParseWarning,
+ USPTODateParseWarning,
+ USPTOTimezoneWarning,
+)
+
+# --- Timezone and Parsing Utilities ---
+ASSUMED_NAIVE_TIMEZONE_STR="America/New_York"
+try:
+ ASSUMED_NAIVE_TIMEZONE:Optional[tzinfo]=ZoneInfo(ASSUMED_NAIVE_TIMEZONE_STR)
+exceptZoneInfoNotFoundError:
+ warnings.warn(
+ f"Timezone '{ASSUMED_NAIVE_TIMEZONE_STR}' not found. "
+ f"Naive datetimes will be treated as UTC.",
+ category=USPTOTimezoneWarning,
+ stacklevel=1,
+ )
+ ASSUMED_NAIVE_TIMEZONE=timezone.utc
+
+
+
+[docs]
+defparse_to_date(date_str:Optional[str],fmt:str="%Y-%m-%d")->Optional[date]:
+"""Parses a string representation of a date into a date object.
+
+ Args:
+ date_str (Optional[str]): The string to parse as a date.
+ fmt (str, optional): The expected strptime format string for parsing
+ the date. Defaults to "%Y-%m-%d".
+
+ Returns:
+ Optional[date]: A date object if parsing is successful and `date_str`
+ is not None. Returns None if `date_str` is None or if parsing fails.
+
+ Warns:
+ USPTODateParseWarning: If the date string cannot be parsed.
+ """
+
+ ifnotdate_str:
+ returnNone
+ try:
+ returndatetime.strptime(date_str,fmt).date()
+ exceptValueError:
+ warnings.warn(
+ f"Could not parse date string '{date_str}' with format '{fmt}'",
+ category=USPTODateParseWarning,
+ stacklevel=2,
+ )
+ returnNone
+
+
+
+
+[docs]
+defparse_to_datetime_utc(datetime_str:Optional[str])->Optional[datetime]:
+"""Parses a string representation of a datetime into a UTC datetime object.
+
+ Attempts to parse ISO format strings. If the input string contains timezone
+ information, it's used. If the string is a naive datetime (no timezone),
+ it's assumed to be in the `ASSUMED_NAIVE_TIMEZONE_STR` (e.g., "America/New_York")
+ and then converted to UTC.
+
+ Args:
+ datetime_str (Optional[str]): The string to parse as a datetime.
+ Supports ISO 8601 format, including those ending with "Z".
+
+ Returns:
+ Optional[datetime]: A timezone-aware datetime object in UTC if parsing
+ is successful and `datetime_str` is not None. Returns None if
+ `datetime_str` is None or if parsing/conversion fails.
+
+ Warns:
+ USPTODateParseWarning: If the datetime string cannot be parsed.
+ USPTOTimezoneWarning: If timezone localization fails.
+ """
+
+ ifnotdatetime_str:
+ returnNone
+ dt_obj:Optional[datetime]=None
+ parsed_successfully=False
+ ifisinstance(datetime_str,str):
+ try:
+ ifdatetime_str.endswith("Z"):
+ dt_obj=datetime.fromisoformat(datetime_str.replace("Z","+00:00"))
+ # Normalize offsets like -0500 → -05:00 for Python <3.11
+ elif(
+ len(datetime_str)>5
+ and(datetime_str[-5]in"+-")
+ anddatetime_str[-3]!=":"
+ ):
+ datetime_str=(
+ datetime_str[:-5]+datetime_str[-5:-2]+":"+datetime_str[-2:]
+ )
+ dt_obj=datetime.fromisoformat(datetime_str)
+ else:
+ dt_obj=datetime.fromisoformat(datetime_str)
+ parsed_successfully=True
+ exceptValueError:
+ pass
+
+ ifnotparsed_successfullyordt_objisNone:
+ warnings.warn(
+ f"Could not parse datetime string '{datetime_str}' with any known format",
+ category=USPTODateParseWarning,
+ stacklevel=2,
+ )
+ returnNone
+ ifdt_obj.tzinfoisNoneordt_obj.tzinfo.utcoffset(dt_obj)isNone:
+ ifASSUMED_NAIVE_TIMEZONE:
+ try:
+ aware_dt=dt_obj.replace(tzinfo=ASSUMED_NAIVE_TIMEZONE)
+ returnaware_dt.astimezone(timezone.utc)
+ exceptExceptionase:
+ warnings.warn(
+ f"Error localizing naive datetime '{datetime_str}': {e}",
+ category=USPTOTimezoneWarning,
+ stacklevel=2,
+ )
+ ifASSUMED_NAIVE_TIMEZONE==timezone.utc:
+ returndt_obj.replace(tzinfo=timezone.utc)
+ returnNone
+ else:
+ returndt_obj.astimezone(timezone.utc)
+
+
+
+
+[docs]
+defserialize_date(d:Optional[date])->Optional[str]:
+"""Serializes a date object into an ISO 8601 string (YYYY-MM-DD).
+
+ Args:
+ d (Optional[date]): The date object to serialize.
+
+ Returns:
+ Optional[str]: The date as an ISO 8601 formatted string, or None
+ if the input is None.
+ """
+ returnd.isoformat()ifdelseNone
+
+
+
+
+[docs]
+defserialize_datetime_as_iso(dt:Optional[datetime])->Optional[str]:
+"""Serializes a datetime object to a local-timezone ISO 8601 string.
+
+ If the input datetime object is timezone-aware, it is converted to the
+ assumed local timezone defined by `ASSUMED_NAIVE_TIMEZONE`.
+ If it is naive (lacks timezone information), it is first assigned that
+ assumed local timezone.
+
+ The resulting datetime is formatted as:
+ YYYY-MM-DDTHH:MM:SS.000±HHMM
+ (e.g., "2024-12-10T00:00:00.000-0500")
+
+ Args:
+ dt (Optional[datetime]): The datetime object to serialize.
+ Can be naive or timezone-aware.
+
+ Returns:
+ Optional[str]: The datetime formatted in the assumed local timezone,
+ or None if the input `dt` is None.
+ """
+ ifnotdt:
+ returnNone
+
+ ifdt.tzinfoisNoneordt.tzinfo.utcoffset(dt)isNone:
+ dt=dt.replace(tzinfo=ASSUMED_NAIVE_TIMEZONE)
+
+ dt_local=dt.astimezone(ASSUMED_NAIVE_TIMEZONE)
+ returndt_local.strftime("%Y-%m-%dT%H:%M:%S.000%z")
+[docs]
+defparse_yn_to_bool(value:Optional[str])->Optional[bool]:
+"""Converts a 'Y'/'N' (case-insensitive) string to a boolean.
+
+ Args:
+ value (Optional[str]): The string value to convert. Expected to be
+ 'Y', 'y', 'N', or 'n'.
+
+ Returns:
+ Optional[bool]: True if `value` is 'Y' or 'y', False if `value` is
+ 'N' or 'n'. Returns None if `value` is None or any other string.
+
+ Warns:
+ USPTOBooleanParseWarning: If the value is not 'Y' or 'N'.
+ """
+
+ ifvalueisNone:
+ returnNone
+ ifvalue=="":
+ returnNone
+ ifvalue.upper()=="Y":
+ returnTrue
+ ifvalue.upper()=="N":
+ returnFalse
+ warnings.warn(
+ f"Unexpected value for Y/N boolean string: '{value}'. Treating as None.",
+ category=USPTOBooleanParseWarning,
+ stacklevel=2,
+ )
+ returnNone
+
+
+
+
+[docs]
+defserialize_bool_to_yn(value:Optional[bool])->Optional[str]:
+"""Converts a boolean value to its 'Y'/'N' string representation.
+
+ Args:
+ value (Optional[bool]): The boolean value to convert.
+
+ Returns:
+ Optional[str]: "Y" if `value` is True, "N" if `value` is False.
+ Returns None if `value` is None.
+ """
+
+ ifvalueisNone:
+ returnNone
+ return"Y"ifvalueelse"N"
+
+
+
+
+[docs]
+defto_camel_case(snake_str:str)->str:
+"""Converts a snake_case string to lowerCamelCase.
+
+ For example, "example_snake_string" becomes "exampleSnakeString".
+
+ Args:
+ snake_str (str): The input string in snake_case.
+
+ Returns:
+ str: The converted string in lowerCamelCase.
+ """
+ parts=snake_str.split("_")
+ returnparts[0]+"".join(x.title()forxinparts[1:])
+"""
+warnings - Warning classes for pyUSPTO data parsing issues
+
+This module defines custom warning categories for different types of
+data parsing issues encountered when working with USPTO API responses.
+These warnings follow Python's standard warning framework and can be
+controlled using warnings.filterwarnings().
+
+Example:
+ # Suppress all pyUSPTO data warnings
+ import warnings
+ from pyUSPTO.warnings import USPTODataWarning
+ warnings.filterwarnings('ignore', category=USPTODataWarning)
+
+ # Turn specific warnings into errors (strict mode)
+ warnings.filterwarnings('error', category=USPTODateParseWarning)
+"""
+
+
+
+[docs]
+classUSPTODataWarning(UserWarning):
+"""Base warning class for USPTO data parsing issues.
+
+ All pyUSPTO data-related warnings inherit from this class,
+ allowing users to filter all data warnings at once.
+ """
+
+ pass
+
+
+
+
+[docs]
+classUSPTODateParseWarning(USPTODataWarning):
+"""Warning for date/datetime string parsing failures.
+
+ Raised when a date or datetime string from the API cannot be
+ parsed into a Python date/datetime object. The field will be
+ set to None.
+ """
+
+ pass
+
+
+
+
+[docs]
+classUSPTOBooleanParseWarning(USPTODataWarning):
+"""Warning for Y/N boolean string parsing failures.
+
+ Raised when a string that should be 'Y' or 'N' has an unexpected
+ value. The field will be set to None.
+ """
+
+ pass
+
+
+
+
+[docs]
+classUSPTOTimezoneWarning(USPTODataWarning):
+"""Warning for timezone-related issues.
+
+ Raised when timezone data is not available or timezone conversion
+ fails. Falls back to UTC timezone.
+ """
+
+ pass
+
+
+
+
+[docs]
+classUSPTOEnumParseWarning(USPTODataWarning):
+"""Warning for enum value parsing failures.
+
+ Raised when an API response contains a value that doesn't match
+ any defined enum member. The field will be set to None.
+ """
+
+ pass
+
+
+
+
+[docs]
+classUSPTODataMismatchWarning(USPTODataWarning):
+"""Warning for data validation mismatches.
+
+ Raised when the API returns data that doesn't match the requested
+ identifier (e.g., requesting application 12345678 but receiving 87654321).
+ This indicates a potential API inconsistency or data integrity issue.
+ """
+
+ pass
Download a publication XML file (grant or pre-grant publication).
+
This method downloads publication XML files from PrintedMetaData objects,
+such as grant documents or pre-grant publications (pgpub). The filename
+is automatically extracted from the metadata if not provided.
printed_metadata (PrintedMetaData) – PrintedMetaData object containing the publication
+download URL and filename information. Typically obtained from
+get_application_associated_documents() or from PatentFileWrapper’s
+grant_document_meta_data or pg_publication_document_meta_data.
+
file_name (str | None) – Optional custom filename. If not provided, uses the
+xml_file_name from the metadata (e.g., “18915708_12307527.xml”).
+
destination_path (str | None) – Optional directory path where the file should be saved.
+If not provided, saves to the current directory. The directory will
+be created if it doesn’t exist.
+
overwrite (bool) – Whether to overwrite an existing file at the destination.
+Default is False, which raises FileExistsError if file exists.
Retrieves complete patent file wrapper data using common identifiers.
+
This utility fetches the PatentFileWrapper, which contains comprehensive
+IFW metadata, application details, and more. Provide only one
+identifier if possible. If multiple are given, they are processed in the
+order listed in the arguments, and the first successful match is returned.
Parameters:
-
download_request (Dict[str, Any]) – JSON payload with download parameters including format
+
+
application_number (Optional[str], optional) – USPTO application number
+(e.g., “16123456”). Checked first (direct lookup).
+
patent_number (Optional[str], optional) – USPTO patent number
+(e.g., “11000000”). Checked second (uses search).
+
publication_number (Optional[str], optional) – USPTO pre-grant
+publication number (e.g., “20230123456”). Checked third (uses search).
Get patent term adjustment data for an application.
+
Retrieves patent term adjustment (PTA) data for a specific application.
+
This method fetches the PatentTermAdjustmentData component from the
+full patent file wrapper. This data includes details on various delay
+quantities (e.g., A, B, C delays, applicant delays), the total
+calculated adjustment, and a history of PTA events that influenced the
+term.
object containing the PTA details if the application is found
+and has such data. Returns None if the application cannot be
+found or if PTA data is not available in the response.
-
Returns:
-
PatentDataResponse object containing the adjustment data
Retrieves a list of patent assignments for a specific application.
+
This method fetches the assignment_bag from the patent file wrapper,
+which contains a list of Assignment objects. Each Assignment object
+details an assignment including information such as reel and frame numbers,
+recording dates, conveyance text, and details about the assignors and assignees.
representing a recorded assignment for the application. Returns
+None if the application cannot be found, or if no assignment
+data is available in the response. An empty list may be
+returned if the application is found but has no recorded
+assignments.
-
Returns:
-
PatentDataResponse object containing the assignment data
Get associated documents metadata for an application.
+
Retrieves metadata for Pre-Grant Publication and Grant documents.
+
This method fetches metadata specifically for published documents associated
+with the patent application, such as Pre-Grant Publications (PGPUBs)
+and granted patent documents. It does not retrieve the prosecution
+history documents (see get_application_documents for that).
+The result is a PrintedPublication object, which holds
+PrintedMetaData including file URIs and names. Download with download_archive.
application_number (str) – The USPTO application number for which
+associated PGPUB/Grant document metadata is being requested
+(e.g., “16123456”).
+
+
Returns:
+
+
A PrintedPublication object
containing PrintedMetaData for the Pre-Grant Publication
+and/or the Grant document, if available. Returns None if the
+application cannot be found or if no such associated document
+metadata is available. The fields within the returned object
+(pgpub_document_meta_data, grant_document_meta_data)
+may themselves be None if a particular type of document
+(e.g., PGPUB) does not exist for the application.
Retrieves data for the attorney(s) of record for a specific application.
+
This method fetches the RecordAttorney object associated with the
+patent application. This object contains details about the attorney(s)
+of record, including customer number correspondence data, power of attorney
+information, and a list of listed attorneys.
about the attorney(s) of record if the application is found
+and such data exists. Returns None if the application cannot
+be found or if no attorney data is available in the response.
-
Returns:
-
PatentDataResponse object containing the attorney data
Retrieves the full details for a specific patent application by its number.
+
This method fetches comprehensive information for a single patent application
+identified by its unique application number.
+
+
Parameters:
+
application_number (str) – The USPTO application number for the patent
+application (e.g., “16123456” or “18/915,708”). The application
+number will be automatically sanitized to remove commas and spaces.
+
+
Returns:
+
+
A PatentFileWrapper object representing
the complete file wrapper for the application if found. This object
+contains all data sections related to the application, such as
+metadata, addresses, assignments, attorney/agent data, continuity
+data, PTA/PTE data, transactions, and associated documents.
+Returns None if the application cannot be found or if the response
+does not contain the expected data.
Retrieves continuity data (parent/child applications) for a specific application.
+
This method fetches the lineage of the specified application, returning an
+ApplicationContinuityData object. This object consolidates lists of
+ParentContinuity (applications to which the current one claims priority)
+and ChildContinuity (applications claiming priority to the current one)
+objects, each detailing the related application’s key identifiers and status.
object containing lists of parent and child continuity relationships.
+Returns None if the application cannot be found or if the underlying
+data to construct continuity is not available. The lists within
+the returned object may be empty if no parent or child continuity
+links exist.
-
Returns:
-
PatentDataResponse object containing the continuity data
Retrieves metadata for documents associated with a specific application.
+
This method fetches a collection of document metadata related to the given
+patent application. The result is a DocumentBag object, which is an
+iterable collection of Document instances. Each Document object
+contains metadata such as its identifier, official date, document code
+and description, direction (incoming/outgoing), and available download
+formats.
application_number (str) – The USPTO application number for which
+document metadata is being requested (e.g., “16123456”).
+
document_codes (Optional[List[str]]) – Filter by specific document type
+codes. If provided, only documents with these codes will be returned.
+Examples: [‘ABST’, ‘CLM’, ‘SPEC’, ‘DRWD’].
+
official_date_from (Optional[str]) – Filter documents from this date
+(inclusive). Date format: YYYY-MM-DD (e.g., “2020-01-15”).
+
official_date_to (Optional[str]) – Filter documents to this date
+(inclusive). Date format: YYYY-MM-DD (e.g., “2023-12-31”).
publicly available documents associated with the application
+that match the provided filters. The bag will be empty if no
+documents are found or if the API response indicates no documents.
+It does not return None for “not found” cases; an empty collection
+is returned instead.
Retrieves a list of foreign priority claims for a specific application.
+
This method fetches the foreign_priority_bag from the patent file
+wrapper. This bag contains a list of ForeignPriority objects, each
+representing a claim to a foreign patent application’s priority date.
+Details include the IP office name, filing date, and application number
+of the foreign priority application.
each detailing a claimed foreign priority. Returns None if the
+application cannot be found or if no foreign priority data is
+available. An empty list may be returned if the application
+is found but has no foreign priority claims.
-
Returns:
-
PatentDataResponse object containing the foreign priority data
Retrieves key metadata for a specific patent application.
+
This method fetches the ApplicationMetaData component from the full
+patent file wrapper. The metadata includes a wide range of information
+such as application status, important dates (filing, grant, publication),
+applicant and inventor details, classification data, and other core
+identifying information for the application.
application_number (str) – The USPTO application number for which
+metadata is being requested (e.g., “16123456” or “18/915,708”).
+The application number will be automatically sanitized.
containing the core details of the patent application if found.
+Returns None if the application cannot be found or if metadata
+is not available in the response.
-
Returns:
-
PatentDataResponse object containing the application metadata
Retrieves the transaction history (events) for a specific application.
+
This method fetches the event_data_bag from the patent file wrapper.
+This bag contains a list of EventData objects, each representing a
+single recorded event in the prosecution history of the patent application.
+Events include details like an event code, a textual description, and
+the date the event was recorded.
detailing a transaction or event in the application’s history.
+Returns None if the application cannot be found or if no
+transaction data is available. An empty list may be returned if
+the application is found but has no recorded transaction events.
-
Returns:
-
PatentDataResponse object containing the transaction data
Fetches a dataset of patent applications based on search criteria, always requesting JSON format.
+For GET, parameters align with OpenAPI for /api/v1/patent/applications/search/download.
+For POST, post_body should conform to PatentDownloadRequest schema.
Retrieves USPTO patent application status codes and their descriptions.
+
This method fetches a list of defined USPTO patent application status codes
+(e.g., codes for “Pending,” “Abandoned,” “Issued”) using a GET request.
+The request can be customized with query parameters to filter or paginate
+the results if supported by the API endpoint.
Parameters:
-
params (Optional[Dict[str, Any]]) – Optional query parameters including:
-- q: Search query string
-- sort: Field to sort by followed by sort order
-- offset: Position in dataset to start from
-- limit: Number of results to return
-- facets: List of fields to facet upon
-- fields: Fields to include in response
-- filters: Field filters
-- rangeFilters: Range filters
+
params (Optional[Dict[str, Any]], optional) – A dictionary of query
+parameters to be sent with the GET request. These parameters can
+be used to filter or control the output of the status codes
+list. Defaults to None, which typically retrieves all available
+status codes or the API’s default set.
Provides an iterator to easily paginate through patent application search results.
+
This method simplifies the process of fetching all patent applications
+that match a given search query by automatically handling pagination.
+It internally calls the search_applications method for GET requests,
+batching results and yielding them one by one.
+
All keyword arguments provided to this method (**kwargs) are passed
+directly to the search_applications method to define the search
+criteria. See the search_applications method for more details
+on available parameters.
+The offset and limit parameters are managed by the pagination logic;
+setting them directly in kwargs might lead to unexpected behavior.
**kwargs (Any) – Keyword arguments to be passed to the
+search_applications method for constructing the search query.
+These define the criteria for the patent applications to be
+retrieved. Do not include post_body.
This method removes common separators (commas, spaces) while preserving
+the “/” in series code format.
Parameters:
-
params (Optional[Dict[str, Any]]) – Optional query parameters including:
-- q: Search query string
-- offset: Position in dataset to start from
-- limit: Number of results to return
+
input_number (str) – Raw application number input. May include commas,
+spaces, or other formatting.
Searches USPTO patent application status codes using POST criteria.
+
Performs targeted searches for USPTO patent application status codes
+(e.g., for “Pending,” “Abandoned,” “Issued”) by sending a POST request
+with a JSON body containing the search_request criteria. This method
+is suited for more complex queries than the GET-based get_status_codes.
Parameters:
-
**kwargs (Any) – Keyword arguments to pass to search_patents
+
search_request (Dict[str, Any]) – A dictionary with search criteria,
+sent as the JSON POST body. The structure must conform to USPTO
+API requirements for this endpoint (e.g., for searching by code
+or description keywords).
-
Yields:
-
PatentFileWrapper objects
+
Returns:
+
+
An object containing a count of matching
status codes, a StatusCodeCollection of the StatusCode
+objects (code and description), and a request identifier.
clients.petition_decisions - Client for USPTO Final Petition Decisions API
+
This module provides a client for interacting with the USPTO Final Petition
+Decisions API. It allows you to search for and retrieve final agency petition
+decisions in publicly available patent applications and patents filed in 2001 or later.
Client for interacting with the USPTO Final Petition Decisions API.
+
This client provides methods to search for petition decisions, retrieve specific
+decisions by ID, download decision data, and download associated documents.
+
Final petition decisions data are incrementally added to the USPTO Open Data Portal
+on a monthly basis starting with data from 2022 and later.
search_request (Dict[str, Any]) – JSON payload with search parameters including:
-- q: Search query string
-- filters: Array of filter objects
-- rangeFilters: Array of range filter objects
-- sort: Array of sort objects
-- fields: Array of field names to include
-- pagination: Pagination object
-- facets: Array of facet field names
+
+
api_key (str | None) – Optional API key for authentication.
+
base_url (str | None) – Optional base URL override for the API.
+
config (USPTOConfig | None) – Optional USPTOConfig instance for configuration.
# Download as JSON
+>>> download = client.download_decisions(
+… format=”json”,
+… technology_center_q=”1700”,
+… limit=1000
+… )
+>>> for decision in download.petition_decision_data:
+… print(decision.application_number_text)
+
# Download CSV and save to file
+>>> file_path = client.download_decisions(
+… format=”csv”,
+… decision_date_from_q=”2023-01-01”,
+… destination_path=”./downloads”
+… )
+>>> print(f”Saved to: {file_path}”)
+
# Download CSV as streaming response (advanced usage)
+>>> response = client.download_decisions(format=”csv”)
+>>> with open(“decisions.csv”, “wb”) as f:
+… for chunk in response.iter_content(chunk_size=8192):
+… f.write(chunk)
Downloads a petition decision document in the specified format.
Parameters:
-
search_request (Dict[str, Any]) – JSON payload with search parameters
+
+
download_option (DocumentDownloadOption) – DocumentDownloadOption object containing the download
+URL and metadata.
+
file_name (str | None) – Optional filename for the downloaded file. If not provided,
+it will be extracted from the URL or generated based on the MIME type.
+
destination_path (str | None) – Optional directory path where the file should be saved.
+If not provided, saves to the current directory.
+
overwrite (bool) – Whether to overwrite an existing file. Defaults to False.
Provides an iterator to paginate through petition decision search results.
+
This method simplifies fetching all petition decisions matching a search query
+by automatically handling pagination. It internally calls the search_decisions
+method for GET requests, batching results and yielding them one by one.
+
All keyword arguments are passed directly to search_decisions to define the
+search criteria. The offset and limit parameters are managed by the pagination
+logic; setting them directly in kwargs might lead to unexpected behavior.
+
+
Parameters:
+
**kwargs (Any) – Keyword arguments passed to search_decisions for constructing
+the search query. Do not include post_body.
+
+
Returns:
+
+
An iterator yielding PetitionDecision objects,
allowing iteration over all matching petition decisions across multiple
+pages of results.
+
+
+
+
+
Return type:
+
Iterator[PetitionDecision]
+
+
Raises:
+
ValueError – If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+
+
+
Examples
+
# Paginate through all decisions for a technology center
+>>> for decision in client.paginate_decisions(technology_center_q=”1700”):
+… print(f”{decision.application_number_text}: {decision.decision_type_code}”)
+
# Paginate with date range
+>>> for decision in client.paginate_decisions(
+… decision_date_from_q=”2023-01-01”,
+… decision_date_to_q=”2023-12-31”
+… ):
+… process_decision(decision)
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
# Search with POST body
+>>> response = client.search_decisions(
+… post_body={“q”: “technologyCenter:1700”, “limit”: 100}
+… )
+
+
+
+
+
+
clients.ptab_appeals - Client for USPTO PTAB Appeals API
+
This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Appeals API. It allows you to search for ex parte appeal decisions.
Provides an iterator to paginate through appeal decision search results.
+
This method simplifies fetching all appeal decisions matching a search query
+by automatically handling pagination. It internally calls the search_decisions
+method for GET requests, batching results and yielding them one by one.
+
All keyword arguments are passed directly to search_decisions to define the
+search criteria. The offset and limit parameters are managed by the pagination
+logic; setting them directly in kwargs might lead to unexpected behavior.
+
+
Parameters:
+
**kwargs (Any) – Keyword arguments passed to search_decisions for constructing
+the search query. Do not include post_body.
+
+
Returns:
+
+
An iterator yielding PTABAppealDecision objects,
allowing iteration over all matching decisions across multiple pages of results.
ValueError – If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+
+
+
Examples
+
# Paginate through all decisions for a technology center
+>>> for decision in client.paginate_decisions(technology_center_number_q=”3600”):
+… print(f”{decision.appeal_meta_data.appeal_number}: ”
+… f”{decision.decision_data.decision_type_category}”)
+
# Paginate with date range
+>>> for decision in client.paginate_decisions(
+… decision_date_from_q=”2023-01-01”,
+… decision_date_to_q=”2023-12-31”
+… ):
+… process_decision(decision)
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
# Search with POST body
+>>> response = client.search_decisions(
+… post_body={“q”: “decisionTypeCategory:Affirmed”, “limit”: 100}
+… )
+
+
+
+
+
+
clients.ptab_interferences - Client for USPTO PTAB Interferences API
+
This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Interferences API. It allows you to search for patent interference decisions.
Provides an iterator to paginate through interference decision search results.
+
This method simplifies fetching all interference decisions matching a search query
+by automatically handling pagination. It internally calls the search_decisions
+method for GET requests, batching results and yielding them one by one.
+
All keyword arguments are passed directly to search_decisions to define the
+search criteria. The offset and limit parameters are managed by the pagination
+logic; setting them directly in kwargs might lead to unexpected behavior.
+
+
Parameters:
+
**kwargs (Any) – Keyword arguments passed to search_decisions for constructing
+the search query. Do not include post_body.
+
+
Returns:
+
+
An iterator yielding PTABInterferenceDecision
objects, allowing iteration over all matching decisions across multiple pages
+of results.
ValueError – If post_body is included in kwargs, as this method only
+ supports GET request parameters for pagination.
+
+
+
+
Examples
+
# Paginate through all interference decisions
+>>> for decision in client.paginate_decisions():
+… print(f”{decision.interference_meta_data.interference_number}: ”
+… f”{decision.document_data.interference_outcome_category}”)
+
# Paginate with date range
+>>> for decision in client.paginate_decisions(
+… decision_date_from_q=”2020-01-01”,
+… decision_date_to_q=”2023-12-31”
+… ):
+… process_decision(decision)
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
# Search with direct query
+>>> response = client.search_decisions(query=”interferenceNumber:106123”)
+
# Search with convenience parameters
+>>> response = client.search_decisions(
+… interference_outcome_category_q=”Priority to Senior Party”,
+… decision_date_from_q=”2020-01-01”,
+… limit=50
+… )
+
# Search with POST body
+>>> response = client.search_decisions(
+… post_body={“q”: “decisionTypeCategory:Final Decision”, “limit”: 100}
+… )
+
+
+
+
+
+
clients.ptab_trials - Client for USPTO PTAB Trials API
+
This module provides a client for interacting with the USPTO PTAB (Patent Trial
+and Appeal Board) Trials API. It allows you to search for trial proceedings,
+documents, and decisions.
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
This method can perform either a GET request using query parameters or a POST
+request if post_body is specified. When using GET, you can provide either a
+direct query string or use convenience parameters that will be automatically
+combined into a query.
+
+
Parameters:
+
+
query (str | None) – Direct query string in USPTO search syntax.
USPTO_REQUEST_TIMEOUT: Request timeout in seconds
+USPTO_CONNECT_TIMEOUT: Connection timeout in seconds
+USPTO_MAX_RETRIES: Maximum retry attempts
+USPTO_BACKOFF_FACTOR: Retry backoff factor
+USPTO_POOL_CONNECTIONS: Connection pool size
+USPTO_POOL_MAXSIZE: Max connections per pool
exceptions - Exception classes for USPTO API clients
-
This module provides exception classes for USPTO API clients.
+
This module provides exception classes for USPTO API errors that correspond to
+the various response types from the USPTO API. It also includes helper
+structures and functions for creating these exceptions.
Base exception for USPTO API errors.
+This is the parent class for all USPTO API-specific exceptions. It includes
+information about the status code, API’s short error message, detailed error
+information, and request identifier from the API response.
Initializes the USPTOApiError.
+:type message: str
+:param message: The primary message for the exception (often client-generated context).
+:type status_code: int | None
+:param status_code: The HTTP status code from the API response (e.g., 400, 403).
+:type api_short_error: str | None
+:param api_short_error: The short error description from the API (e.g., “Bad Request”, “Forbidden”).
+:type error_details: str | dict | None
+:param error_details: The detailed error message or structure from the API.
+:type request_identifier: str | None
+:param request_identifier: The request identifier from the API response, if available.
Provides direct access to the primary exception message.
+This refers to the first argument passed to the exception,
+which is conventionally the main human-readable message.
models.patent_data - Data models for USPTO patent data API
-
This module provides data models for the 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.
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.
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.
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:
+
Dictionary representation of the application metadata.
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.
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.
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.
+
+
Parameters:
+
data (Dict[str, Any]) – A dictionary containing the data for a
+DocumentFormat. Expected keys from the API are
+“mimeTypeIdentifier”, “downloadUrl”, and “pageTotalQuantity”.
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:
+
+
A dictionary representation of the DocumentFormat
instance with keys “mimeTypeIdentifier”, “downloadUrl”, and
+“pageTotalQuantity”.
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.
models.ptab - Data models for USPTO PTAB (Patent Trial and Appeal Board) APIs
+
This module provides data models, primarily using frozen dataclasses, for
+representing responses from the USPTO PTAB APIs. These models cover:
+- Patent trial proceedings (IPR, PGR, CBM, DER)
+- Trial documents and decisions
+- Appeal decisions
+- Interference decisions
Individual trial document or decision record from PTAB document/decision search APIs.
+
Used by search_documents() and search_decisions() endpoints. Contains document-specific
+metadata (documentData) or decision information (decisionData), plus trial context.
+Differs from PTABTrialProceeding which represents the entire proceeding rather than
+individual documents within it.
models.utils - Utility functions for USPTO data models
+
This module provides utility functions for parsing, serializing, and converting
+data used across USPTO API data models. These utilities handle date/datetime
+conversions, boolean string representations, and string transformations.
Parses a string representation of a datetime into a UTC datetime object.
+
Attempts to parse ISO format strings. If the input string contains timezone
+information, it’s used. If the string is a naive datetime (no timezone),
+it’s assumed to be in the ASSUMED_NAIVE_TIMEZONE_STR (e.g., “America/New_York”)
+and then converted to UTC.
+
+
Parameters:
+
datetime_str (Optional[str]) – The string to parse as a datetime.
+Supports ISO 8601 format, including those ending with “Z”.
+
+
Returns:
+
+
A timezone-aware datetime object in UTC if parsing
is successful and datetime_str is not None. Returns None if
+datetime_str is None or if parsing/conversion fails.
+
+
+
+
+
Return type:
+
Optional[datetime]
+
+
Warns:
+
+
USPTODateParseWarning – If the datetime string cannot be parsed.
+
USPTOTimezoneWarning – If timezone localization fails.
Serializes a datetime object to a local-timezone ISO 8601 string.
+
If the input datetime object is timezone-aware, it is converted to the
+assumed local timezone defined by ASSUMED_NAIVE_TIMEZONE.
+If it is naive (lacks timezone information), it is first assigned that
+assumed local timezone.
+
+
The resulting datetime is formatted as:
YYYY-MM-DDTHH:MM:SS.000±HHMM
+
+
+
(e.g., “2024-12-10T00:00:00.000-0500”)
+
+
Parameters:
+
dt (Optional[datetime]) – The datetime object to serialize.
+Can be naive or timezone-aware.
+
+
Returns:
+
+
The datetime formatted in the assumed local timezone,
warnings - Warning classes for pyUSPTO data parsing issues
+
This module defines custom warning categories for different types of
+data parsing issues encountered when working with USPTO API responses.
+These warnings follow Python’s standard warning framework and can be
+controlled using warnings.filterwarnings().
+
+
Example
+
# Suppress all pyUSPTO data warnings
+import warnings
+from pyUSPTO.warnings import USPTODataWarning
+warnings.filterwarnings(‘ignore’, category=USPTODataWarning)
+
# Turn specific warnings into errors (strict mode)
+warnings.filterwarnings(‘error’, category=USPTODateParseWarning)
Raised when the API returns data that doesn’t match the requested
+identifier (e.g., requesting application 12345678 but receiving 87654321).
+This indicates a potential API inconsistency or data integrity issue.
1"""
+ 2Example usage of pyUSPTO for IFW data
+ 3
+ 4This example demonstrates how to use the PatentDataClient to interact with the USPTO Patent Data API.
+ 5It shows how to retrieve IFW based on various identifying values.
+ 6"""
+ 7
+ 8importos
+ 9frommultiprocessingimportValue
+10
+11frompyUSPTO.clients.patent_dataimportPatentDataClient
+12
+13api_key=os.environ.get("USPTO_API_KEY","YOUR_API_KEY_HERE")
+14ifapi_key=="YOUR_API_KEY_HERE":
+15raiseValueError(
+16"WARNING: API key is not set. Please replace 'YOUR_API_KEY_HERE' or set USPTO_API_KEY environment variable."
+17)
+18
+19client=PatentDataClient(api_key=api_key)
+20
+21
+22print("\nBeginning API requests with configured client:")
+23
+24print("\nGet IFW Based on Application Number ->")
+25app_no_ifw=client.get_IFW_metadata(application_number="14412875")
+26ifapp_no_ifwandapp_no_ifw.application_meta_data:
+27print(app_no_ifw.application_meta_data.invention_title)
+28print(" - IFW Found based on App No")
+29
+30
+31print("\nGet IFW Based on Patent Number ->")
+32pat_no_ifw=client.get_IFW_metadata(patent_number="10765880")
+33ifpat_no_ifwandpat_no_ifw.application_meta_data:
+34print(pat_no_ifw.application_meta_data.invention_title)
+35print(" - IFW Found based on Pat No")
+36
+37
+38print("\nGet IFW Based on Publication Number ->")
+39pub_no_ifw=client.get_IFW_metadata(publication_number="*20150157873*")
+40ifpub_no_ifwandpub_no_ifw.application_meta_data:
+41print(pub_no_ifw.application_meta_data.invention_title)
+42print(" - IFW Found based on Pub No")
+43
+44
+45print("\nGet IFW Based on PCT App Number ->")
+46pct_app_no_ifw=client.get_IFW_metadata(PCT_app_number="PCTUS0812705")
+47ifpct_app_no_ifwandpct_app_no_ifw.application_meta_data:
+48print(pct_app_no_ifw.application_meta_data.invention_title)
+49print(" - IFW Found based on PCT App No")
+50
+51
+52print("\nGet IFW Based on PCT Pub Number ->")
+53pct_pub_no_ifw=client.get_IFW_metadata(PCT_pub_number="*2009064413*")
+54ifpct_pub_no_ifwandpct_pub_no_ifw.application_meta_data:
+55print(pct_pub_no_ifw.application_meta_data.invention_title)
+56print(" - IFW Found based on PCT Pub No")
+57
+58print("Now let's download the Patent Publication Text -->")
+59ifapp_no_ifwandapp_no_ifw.pgpub_document_meta_data:
+60pgpub_archive=app_no_ifw.pgpub_document_meta_data
+61print(pgpub_archive)
+62download_path="./download-example"
+63file_path=client.download_archive(
+64printed_metadata=pgpub_archive,destination_path=download_path,overwrite=True
+65)
+66print(f"-Downloaded document to: {file_path}")
+67
+68print("Now let's download the Patent Grant Text -->")
+69ifapp_no_ifwandapp_no_ifw.grant_document_meta_data:
+70grant_archive=app_no_ifw.grant_document_meta_data
+71print(grant_archive)
+72download_path="./download-example"
+73file_path=client.download_archive(
+74printed_metadata=grant_archive,destination_path=download_path,overwrite=True
+75)
+76print(f"-Downloaded document to: {file_path}")
+