Initial commit
This commit is contained in:
290
skills/uspto-database/scripts/patent_search.py
Normal file
290
skills/uspto-database/scripts/patent_search.py
Normal file
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
USPTO PatentSearch API Helper
|
||||
|
||||
Provides functions for searching and retrieving patent data using the USPTO
|
||||
PatentSearch API (ElasticSearch-based system, replaced legacy PatentsView in May 2025).
|
||||
|
||||
Requires:
|
||||
- requests library: pip install requests
|
||||
- USPTO API key from https://account.uspto.gov/api-manager/
|
||||
|
||||
Environment variables:
|
||||
USPTO_API_KEY - Your USPTO API key
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
from typing import Dict, List, Optional, Any
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class PatentSearchClient:
|
||||
"""Client for USPTO PatentSearch API."""
|
||||
|
||||
BASE_URL = "https://search.patentsview.org/api/v1"
|
||||
|
||||
def __init__(self, api_key: Optional[str] = None):
|
||||
"""
|
||||
Initialize client with API key.
|
||||
|
||||
Args:
|
||||
api_key: USPTO API key (if not provided, uses USPTO_API_KEY env var)
|
||||
"""
|
||||
self.api_key = api_key or os.getenv("USPTO_API_KEY")
|
||||
if not self.api_key:
|
||||
raise ValueError("API key required. Set USPTO_API_KEY environment variable or pass to constructor.")
|
||||
|
||||
self.headers = {
|
||||
"X-Api-Key": self.api_key,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
def _request(self, endpoint: str, query: Dict, fields: Optional[List[str]] = None,
|
||||
sort: Optional[List[Dict]] = None, options: Optional[Dict] = None) -> Dict:
|
||||
"""
|
||||
Make a request to the PatentSearch API.
|
||||
|
||||
Args:
|
||||
endpoint: API endpoint (e.g., "patent", "inventor")
|
||||
query: Query dictionary
|
||||
fields: List of fields to return
|
||||
sort: Sort specification
|
||||
options: Pagination and other options
|
||||
|
||||
Returns:
|
||||
API response as dictionary
|
||||
"""
|
||||
url = f"{self.BASE_URL}/{endpoint}"
|
||||
|
||||
data = {"q": query}
|
||||
if fields:
|
||||
data["f"] = fields
|
||||
if sort:
|
||||
data["s"] = sort
|
||||
if options:
|
||||
data["o"] = options
|
||||
|
||||
response = requests.post(url, headers=self.headers, json=data)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def search_patents(self, query: Dict, fields: Optional[List[str]] = None,
|
||||
sort: Optional[List[Dict]] = None, page: int = 1,
|
||||
per_page: int = 100) -> Dict:
|
||||
"""
|
||||
Search for patents.
|
||||
|
||||
Args:
|
||||
query: Query dictionary (see PatentSearch API docs for syntax)
|
||||
fields: Fields to return (defaults to essential fields)
|
||||
sort: Sort specification
|
||||
page: Page number
|
||||
per_page: Results per page (max 1000)
|
||||
|
||||
Returns:
|
||||
Search results with patents array
|
||||
|
||||
Example:
|
||||
# Search by keyword
|
||||
results = client.search_patents({
|
||||
"patent_abstract": {"_text_all": ["machine", "learning"]}
|
||||
})
|
||||
|
||||
# Search by date range
|
||||
results = client.search_patents({
|
||||
"patent_date": {"_gte": "2024-01-01", "_lte": "2024-12-31"}
|
||||
})
|
||||
"""
|
||||
if fields is None:
|
||||
fields = [
|
||||
"patent_number", "patent_title", "patent_date",
|
||||
"patent_abstract", "assignee_organization",
|
||||
"inventor_name", "cpc_subclass_id"
|
||||
]
|
||||
|
||||
if sort is None:
|
||||
sort = [{"patent_date": "desc"}]
|
||||
|
||||
options = {"page": page, "per_page": min(per_page, 1000)}
|
||||
|
||||
return self._request("patent", query, fields, sort, options)
|
||||
|
||||
def get_patent(self, patent_number: str) -> Optional[Dict]:
|
||||
"""
|
||||
Get details for a specific patent by number.
|
||||
|
||||
Args:
|
||||
patent_number: Patent number (with or without commas)
|
||||
|
||||
Returns:
|
||||
Patent data dictionary or None if not found
|
||||
"""
|
||||
# Remove commas from patent number
|
||||
patent_number = patent_number.replace(",", "")
|
||||
|
||||
query = {"patent_number": patent_number}
|
||||
fields = [
|
||||
"patent_number", "patent_title", "patent_date", "patent_abstract",
|
||||
"patent_type", "inventor_name", "assignee_organization",
|
||||
"cpc_subclass_id", "cited_patent_number", "citedby_patent_number"
|
||||
]
|
||||
|
||||
result = self._request("patent", query, fields)
|
||||
|
||||
if result.get("patents"):
|
||||
return result["patents"][0]
|
||||
return None
|
||||
|
||||
def search_by_inventor(self, inventor_name: str, **kwargs) -> Dict:
|
||||
"""
|
||||
Search patents by inventor name.
|
||||
|
||||
Args:
|
||||
inventor_name: Inventor name (use _text_phrase for exact match)
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
Search results
|
||||
"""
|
||||
query = {"inventor_name": {"_text_phrase": inventor_name}}
|
||||
return self.search_patents(query, **kwargs)
|
||||
|
||||
def search_by_assignee(self, assignee_name: str, **kwargs) -> Dict:
|
||||
"""
|
||||
Search patents by assignee/company name.
|
||||
|
||||
Args:
|
||||
assignee_name: Assignee/company name
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
Search results
|
||||
"""
|
||||
query = {"assignee_organization": {"_text_any": assignee_name.split()}}
|
||||
return self.search_patents(query, **kwargs)
|
||||
|
||||
def search_by_classification(self, cpc_code: str, **kwargs) -> Dict:
|
||||
"""
|
||||
Search patents by CPC classification code.
|
||||
|
||||
Args:
|
||||
cpc_code: CPC subclass code (e.g., "H04N", "G06F")
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
Search results
|
||||
"""
|
||||
query = {"cpc_subclass_id": cpc_code}
|
||||
return self.search_patents(query, **kwargs)
|
||||
|
||||
def search_by_date_range(self, start_date: str, end_date: str, **kwargs) -> Dict:
|
||||
"""
|
||||
Search patents by date range.
|
||||
|
||||
Args:
|
||||
start_date: Start date (YYYY-MM-DD)
|
||||
end_date: End date (YYYY-MM-DD)
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
Search results
|
||||
"""
|
||||
query = {
|
||||
"patent_date": {
|
||||
"_gte": start_date,
|
||||
"_lte": end_date
|
||||
}
|
||||
}
|
||||
return self.search_patents(query, **kwargs)
|
||||
|
||||
def advanced_search(self, keywords: List[str], assignee: Optional[str] = None,
|
||||
start_date: Optional[str] = None, end_date: Optional[str] = None,
|
||||
cpc_codes: Optional[List[str]] = None, **kwargs) -> Dict:
|
||||
"""
|
||||
Perform advanced search with multiple criteria.
|
||||
|
||||
Args:
|
||||
keywords: List of keywords to search in abstract/title
|
||||
assignee: Assignee/company name
|
||||
start_date: Start date (YYYY-MM-DD)
|
||||
end_date: End date (YYYY-MM-DD)
|
||||
cpc_codes: List of CPC classification codes
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
Search results
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
# Keyword search in abstract
|
||||
if keywords:
|
||||
conditions.append({
|
||||
"patent_abstract": {"_text_all": keywords}
|
||||
})
|
||||
|
||||
# Assignee filter
|
||||
if assignee:
|
||||
conditions.append({
|
||||
"assignee_organization": {"_text_any": assignee.split()}
|
||||
})
|
||||
|
||||
# Date range
|
||||
if start_date and end_date:
|
||||
conditions.append({
|
||||
"patent_date": {"_gte": start_date, "_lte": end_date}
|
||||
})
|
||||
|
||||
# CPC classification
|
||||
if cpc_codes:
|
||||
conditions.append({
|
||||
"cpc_subclass_id": cpc_codes
|
||||
})
|
||||
|
||||
query = {"_and": conditions} if len(conditions) > 1 else conditions[0]
|
||||
|
||||
return self.search_patents(query, **kwargs)
|
||||
|
||||
|
||||
def main():
|
||||
"""Command-line interface for patent search."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage:")
|
||||
print(" python patent_search.py <patent_number>")
|
||||
print(" python patent_search.py --inventor <name>")
|
||||
print(" python patent_search.py --assignee <company>")
|
||||
print(" python patent_search.py --keywords <word1> <word2> ...")
|
||||
sys.exit(1)
|
||||
|
||||
client = PatentSearchClient()
|
||||
|
||||
try:
|
||||
if sys.argv[1] == "--inventor":
|
||||
results = client.search_by_inventor(" ".join(sys.argv[2:]))
|
||||
elif sys.argv[1] == "--assignee":
|
||||
results = client.search_by_assignee(" ".join(sys.argv[2:]))
|
||||
elif sys.argv[1] == "--keywords":
|
||||
query = {"patent_abstract": {"_text_all": sys.argv[2:]}}
|
||||
results = client.search_patents(query)
|
||||
else:
|
||||
# Assume patent number
|
||||
patent = client.get_patent(sys.argv[1])
|
||||
if patent:
|
||||
results = {"patents": [patent], "count": 1, "total_hits": 1}
|
||||
else:
|
||||
print(f"Patent {sys.argv[1]} not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Print results
|
||||
print(json.dumps(results, indent=2))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
253
skills/uspto-database/scripts/peds_client.py
Normal file
253
skills/uspto-database/scripts/peds_client.py
Normal file
@@ -0,0 +1,253 @@
|
||||
#!/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()
|
||||
263
skills/uspto-database/scripts/trademark_client.py
Normal file
263
skills/uspto-database/scripts/trademark_client.py
Normal file
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
USPTO Trademark API Helper
|
||||
|
||||
Provides functions for searching and retrieving trademark data using USPTO
|
||||
Trademark Status & Document Retrieval (TSDR) API.
|
||||
|
||||
Requires:
|
||||
- requests library: pip install requests
|
||||
- USPTO API key from https://account.uspto.gov/api-manager/
|
||||
|
||||
Environment variables:
|
||||
USPTO_API_KEY - Your USPTO API key
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
|
||||
class TrademarkClient:
|
||||
"""Client for USPTO Trademark APIs."""
|
||||
|
||||
TSDR_BASE_URL = "https://tsdrapi.uspto.gov/ts/cd"
|
||||
ASSIGNMENT_BASE_URL = "https://assignment-api.uspto.gov/trademark"
|
||||
|
||||
def __init__(self, api_key: Optional[str] = None):
|
||||
"""
|
||||
Initialize client with API key.
|
||||
|
||||
Args:
|
||||
api_key: USPTO API key (if not provided, uses USPTO_API_KEY env var)
|
||||
"""
|
||||
self.api_key = api_key or os.getenv("USPTO_API_KEY")
|
||||
if not self.api_key:
|
||||
raise ValueError("API key required. Set USPTO_API_KEY environment variable or pass to constructor.")
|
||||
|
||||
self.headers = {"X-Api-Key": self.api_key}
|
||||
|
||||
def get_trademark_by_serial(self, serial_number: str) -> Optional[Dict]:
|
||||
"""
|
||||
Get trademark information by serial number.
|
||||
|
||||
Args:
|
||||
serial_number: Trademark serial number (e.g., "87654321")
|
||||
|
||||
Returns:
|
||||
Trademark data dictionary or None if not found
|
||||
"""
|
||||
url = f"{self.TSDR_BASE_URL}/casedocs/sn{serial_number}/info.json"
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
return None
|
||||
raise
|
||||
|
||||
def get_trademark_by_registration(self, registration_number: str) -> Optional[Dict]:
|
||||
"""
|
||||
Get trademark information by registration number.
|
||||
|
||||
Args:
|
||||
registration_number: Trademark registration number (e.g., "5678901")
|
||||
|
||||
Returns:
|
||||
Trademark data dictionary or None if not found
|
||||
"""
|
||||
url = f"{self.TSDR_BASE_URL}/casedocs/rn{registration_number}/info.json"
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
return None
|
||||
raise
|
||||
|
||||
def get_trademark_status(self, serial_or_registration: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current status summary for a trademark.
|
||||
|
||||
Args:
|
||||
serial_or_registration: Serial or registration number
|
||||
|
||||
Returns:
|
||||
Status summary dictionary with:
|
||||
- mark_text: Text of the mark
|
||||
- status: Current status
|
||||
- filing_date: Application filing date
|
||||
- registration_number: Registration number if registered
|
||||
- registration_date: Registration date if registered
|
||||
"""
|
||||
# Try serial number first
|
||||
data = self.get_trademark_by_serial(serial_or_registration)
|
||||
|
||||
# If not found, try registration number
|
||||
if not data:
|
||||
data = self.get_trademark_by_registration(serial_or_registration)
|
||||
|
||||
if not data:
|
||||
return {}
|
||||
|
||||
tm = data.get('TradeMarkAppln', {})
|
||||
|
||||
return {
|
||||
'mark_text': tm.get('MarkVerbalElementText'),
|
||||
'status': tm.get('MarkCurrentStatusExternalDescriptionText'),
|
||||
'status_date': tm.get('MarkCurrentStatusDate'),
|
||||
'filing_date': tm.get('ApplicationDate'),
|
||||
'application_number': tm.get('ApplicationNumber'),
|
||||
'registration_number': tm.get('RegistrationNumber'),
|
||||
'registration_date': tm.get('RegistrationDate'),
|
||||
'mark_drawing_code': tm.get('MarkDrawingCode'),
|
||||
'is_registered': tm.get('RegistrationNumber') is not None
|
||||
}
|
||||
|
||||
def get_goods_and_services(self, serial_or_registration: str) -> List[Dict]:
|
||||
"""
|
||||
Get goods and services classification for a trademark.
|
||||
|
||||
Args:
|
||||
serial_or_registration: Serial or registration number
|
||||
|
||||
Returns:
|
||||
List of goods/services entries with classes
|
||||
"""
|
||||
data = self.get_trademark_by_serial(serial_or_registration)
|
||||
if not data:
|
||||
data = self.get_trademark_by_registration(serial_or_registration)
|
||||
|
||||
if not data:
|
||||
return []
|
||||
|
||||
tm = data.get('TradeMarkAppln', {})
|
||||
return tm.get('GoodsAndServices', [])
|
||||
|
||||
def get_owner_info(self, serial_or_registration: str) -> List[Dict]:
|
||||
"""
|
||||
Get owner/applicant information for a trademark.
|
||||
|
||||
Args:
|
||||
serial_or_registration: Serial or registration number
|
||||
|
||||
Returns:
|
||||
List of owner entries
|
||||
"""
|
||||
data = self.get_trademark_by_serial(serial_or_registration)
|
||||
if not data:
|
||||
data = self.get_trademark_by_registration(serial_or_registration)
|
||||
|
||||
if not data:
|
||||
return []
|
||||
|
||||
tm = data.get('TradeMarkAppln', {})
|
||||
return tm.get('Owners', [])
|
||||
|
||||
def get_prosecution_history(self, serial_or_registration: str) -> List[Dict]:
|
||||
"""
|
||||
Get prosecution history for a trademark.
|
||||
|
||||
Args:
|
||||
serial_or_registration: Serial or registration number
|
||||
|
||||
Returns:
|
||||
List of prosecution events
|
||||
"""
|
||||
data = self.get_trademark_by_serial(serial_or_registration)
|
||||
if not data:
|
||||
data = self.get_trademark_by_registration(serial_or_registration)
|
||||
|
||||
if not data:
|
||||
return []
|
||||
|
||||
tm = data.get('TradeMarkAppln', {})
|
||||
return tm.get('ProsecutionHistoryEntry', [])
|
||||
|
||||
def check_trademark_health(self, serial_or_registration: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Check trademark health and identify issues.
|
||||
|
||||
Args:
|
||||
serial_or_registration: Serial or registration number
|
||||
|
||||
Returns:
|
||||
Health check dictionary with alerts and status
|
||||
"""
|
||||
status = self.get_trademark_status(serial_or_registration)
|
||||
|
||||
if not status:
|
||||
return {'error': 'Trademark not found'}
|
||||
|
||||
current_status = status.get('status', '').upper()
|
||||
alerts = []
|
||||
|
||||
# Check for problematic statuses
|
||||
if 'ABANDON' in current_status:
|
||||
alerts.append('⚠️ ABANDONED - Mark is no longer active')
|
||||
elif 'CANCELLED' in current_status:
|
||||
alerts.append('⚠️ CANCELLED - Registration cancelled')
|
||||
elif 'EXPIRED' in current_status:
|
||||
alerts.append('⚠️ EXPIRED - Registration has expired')
|
||||
elif 'SUSPENDED' in current_status:
|
||||
alerts.append('⏸️ SUSPENDED - Examination suspended')
|
||||
elif 'PUBLISHED' in current_status:
|
||||
alerts.append('📢 PUBLISHED - In opposition period')
|
||||
elif 'REGISTERED' in current_status:
|
||||
alerts.append('✅ ACTIVE - Mark is registered and active')
|
||||
elif 'PENDING' in current_status:
|
||||
alerts.append('⏳ PENDING - Application under examination')
|
||||
|
||||
return {
|
||||
'mark': status.get('mark_text'),
|
||||
'status': current_status,
|
||||
'status_date': status.get('status_date'),
|
||||
'alerts': alerts,
|
||||
'needs_attention': len([a for a in alerts if '⚠️' in a]) > 0
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""Command-line interface for trademark search."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage:")
|
||||
print(" python trademark_client.py <serial_or_registration_number>")
|
||||
print(" python trademark_client.py --status <number>")
|
||||
print(" python trademark_client.py --health <number>")
|
||||
print(" python trademark_client.py --goods <number>")
|
||||
sys.exit(1)
|
||||
|
||||
client = TrademarkClient()
|
||||
|
||||
try:
|
||||
if sys.argv[1] == "--status":
|
||||
result = client.get_trademark_status(sys.argv[2])
|
||||
elif sys.argv[1] == "--health":
|
||||
result = client.check_trademark_health(sys.argv[2])
|
||||
elif sys.argv[1] == "--goods":
|
||||
result = client.get_goods_and_services(sys.argv[2])
|
||||
else:
|
||||
# Get full trademark data
|
||||
result = client.get_trademark_by_serial(sys.argv[1])
|
||||
if not result:
|
||||
result = client.get_trademark_by_registration(sys.argv[1])
|
||||
|
||||
if result:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
print(f"Trademark {sys.argv[1]} not 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()
|
||||
Reference in New Issue
Block a user