254 lines
8.3 KiB
Python
254 lines
8.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
USPTO Patent Examination Data System (PEDS) Helper
|
|
|
|
Provides functions for retrieving patent examination data using the
|
|
uspto-opendata-python library.
|
|
|
|
Requires:
|
|
- uspto-opendata-python: pip install uspto-opendata-python
|
|
|
|
Note: This script provides a simplified interface to PEDS data.
|
|
For full functionality, use the uspto-opendata-python library directly.
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
from typing import Dict, List, Optional, Any
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from uspto.peds import PEDSClient as OriginalPEDSClient
|
|
HAS_USPTO_LIB = True
|
|
except ImportError:
|
|
HAS_USPTO_LIB = False
|
|
print("Warning: uspto-opendata-python not installed.", file=sys.stderr)
|
|
print("Install with: pip install uspto-opendata-python", file=sys.stderr)
|
|
|
|
|
|
class PEDSHelper:
|
|
"""Helper class for accessing PEDS data."""
|
|
|
|
def __init__(self):
|
|
"""Initialize PEDS client."""
|
|
if not HAS_USPTO_LIB:
|
|
raise ImportError("uspto-opendata-python library required")
|
|
self.client = OriginalPEDSClient()
|
|
|
|
def get_application(self, application_number: str) -> Optional[Dict]:
|
|
"""
|
|
Get patent application data by application number.
|
|
|
|
Args:
|
|
application_number: Application number (e.g., "16123456")
|
|
|
|
Returns:
|
|
Application data dictionary with:
|
|
- title: Application title
|
|
- filing_date: Filing date
|
|
- status: Current status
|
|
- transactions: List of prosecution events
|
|
- inventors: List of inventors
|
|
- assignees: List of assignees
|
|
"""
|
|
try:
|
|
result = self.client.get_application(application_number)
|
|
return self._format_application_data(result)
|
|
except Exception as e:
|
|
print(f"Error retrieving application {application_number}: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
def get_patent(self, patent_number: str) -> Optional[Dict]:
|
|
"""
|
|
Get patent data by patent number.
|
|
|
|
Args:
|
|
patent_number: Patent number (e.g., "11234567")
|
|
|
|
Returns:
|
|
Patent data dictionary
|
|
"""
|
|
try:
|
|
result = self.client.get_patent(patent_number)
|
|
return self._format_application_data(result)
|
|
except Exception as e:
|
|
print(f"Error retrieving patent {patent_number}: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
def get_transaction_history(self, application_number: str) -> List[Dict]:
|
|
"""
|
|
Get transaction history for an application.
|
|
|
|
Args:
|
|
application_number: Application number
|
|
|
|
Returns:
|
|
List of transactions with date, code, and description
|
|
"""
|
|
app_data = self.get_application(application_number)
|
|
if app_data and 'transactions' in app_data:
|
|
return app_data['transactions']
|
|
return []
|
|
|
|
def get_office_actions(self, application_number: str) -> List[Dict]:
|
|
"""
|
|
Get office actions for an application.
|
|
|
|
Args:
|
|
application_number: Application number
|
|
|
|
Returns:
|
|
List of office actions with dates and types
|
|
"""
|
|
transactions = self.get_transaction_history(application_number)
|
|
|
|
# Filter for office action transaction codes
|
|
oa_codes = ['CTNF', 'CTFR', 'AOPF', 'NOA']
|
|
|
|
office_actions = [
|
|
trans for trans in transactions
|
|
if trans.get('code') in oa_codes
|
|
]
|
|
|
|
return office_actions
|
|
|
|
def get_status_summary(self, application_number: str) -> Dict[str, Any]:
|
|
"""
|
|
Get a summary of application status.
|
|
|
|
Args:
|
|
application_number: Application number
|
|
|
|
Returns:
|
|
Dictionary with status summary:
|
|
- current_status: Current application status
|
|
- filing_date: Filing date
|
|
- status_date: Status date
|
|
- is_patented: Boolean indicating if patented
|
|
- patent_number: Patent number if granted
|
|
- pendency_days: Days since filing
|
|
"""
|
|
app_data = self.get_application(application_number)
|
|
if not app_data:
|
|
return {}
|
|
|
|
filing_date = app_data.get('filing_date')
|
|
if filing_date:
|
|
filing_dt = datetime.strptime(filing_date, '%Y-%m-%d')
|
|
pendency_days = (datetime.now() - filing_dt).days
|
|
else:
|
|
pendency_days = None
|
|
|
|
return {
|
|
'current_status': app_data.get('app_status'),
|
|
'filing_date': filing_date,
|
|
'status_date': app_data.get('app_status_date'),
|
|
'is_patented': app_data.get('patent_number') is not None,
|
|
'patent_number': app_data.get('patent_number'),
|
|
'issue_date': app_data.get('patent_issue_date'),
|
|
'pendency_days': pendency_days,
|
|
'title': app_data.get('title'),
|
|
'inventors': app_data.get('inventors', []),
|
|
'assignees': app_data.get('assignees', [])
|
|
}
|
|
|
|
def analyze_prosecution(self, application_number: str) -> Dict[str, Any]:
|
|
"""
|
|
Analyze prosecution history.
|
|
|
|
Args:
|
|
application_number: Application number
|
|
|
|
Returns:
|
|
Dictionary with prosecution analysis:
|
|
- total_office_actions: Count of office actions
|
|
- rejections: Count of rejections
|
|
- allowance: Boolean if allowed
|
|
- response_count: Count of applicant responses
|
|
- examination_duration: Days from filing to allowance/abandonment
|
|
"""
|
|
transactions = self.get_transaction_history(application_number)
|
|
app_summary = self.get_status_summary(application_number)
|
|
|
|
if not transactions:
|
|
return {}
|
|
|
|
analysis = {
|
|
'total_office_actions': 0,
|
|
'non_final_rejections': 0,
|
|
'final_rejections': 0,
|
|
'allowance': False,
|
|
'responses': 0,
|
|
'abandonment': False
|
|
}
|
|
|
|
for trans in transactions:
|
|
code = trans.get('code', '')
|
|
if code == 'CTNF':
|
|
analysis['non_final_rejections'] += 1
|
|
analysis['total_office_actions'] += 1
|
|
elif code == 'CTFR':
|
|
analysis['final_rejections'] += 1
|
|
analysis['total_office_actions'] += 1
|
|
elif code in ['AOPF', 'OA']:
|
|
analysis['total_office_actions'] += 1
|
|
elif code == 'NOA':
|
|
analysis['allowance'] = True
|
|
elif code == 'WRIT':
|
|
analysis['responses'] += 1
|
|
elif code == 'ABND':
|
|
analysis['abandonment'] = True
|
|
|
|
analysis['status'] = app_summary.get('current_status')
|
|
analysis['pendency_days'] = app_summary.get('pendency_days')
|
|
|
|
return analysis
|
|
|
|
def _format_application_data(self, raw_data: Dict) -> Dict:
|
|
"""Format raw PEDS data into cleaner structure."""
|
|
# This is a placeholder - actual implementation depends on
|
|
# the structure returned by uspto-opendata-python
|
|
return raw_data
|
|
|
|
|
|
def main():
|
|
"""Command-line interface for PEDS data."""
|
|
if len(sys.argv) < 2:
|
|
print("Usage:")
|
|
print(" python peds_client.py <application_number>")
|
|
print(" python peds_client.py --patent <patent_number>")
|
|
print(" python peds_client.py --status <application_number>")
|
|
print(" python peds_client.py --analyze <application_number>")
|
|
sys.exit(1)
|
|
|
|
if not HAS_USPTO_LIB:
|
|
print("Error: uspto-opendata-python library not installed")
|
|
print("Install with: pip install uspto-opendata-python")
|
|
sys.exit(1)
|
|
|
|
helper = PEDSHelper()
|
|
|
|
try:
|
|
if sys.argv[1] == "--patent":
|
|
result = helper.get_patent(sys.argv[2])
|
|
elif sys.argv[1] == "--status":
|
|
result = helper.get_status_summary(sys.argv[2])
|
|
elif sys.argv[1] == "--analyze":
|
|
result = helper.analyze_prosecution(sys.argv[2])
|
|
else:
|
|
result = helper.get_application(sys.argv[1])
|
|
|
|
if result:
|
|
print(json.dumps(result, indent=2))
|
|
else:
|
|
print("No data found", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
print(f"Error: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|