forked from 0xWheatyz/SPARC
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecc2c37bcd |
@@ -34,17 +34,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
ruff check SPARC/ tests/
|
ruff check SPARC/ tests/
|
||||||
|
|
||||||
- name: Install Node.js and frontend dependencies
|
|
||||||
shell: sh
|
|
||||||
run: |
|
|
||||||
apk add --no-cache nodejs npm
|
|
||||||
cd frontend && npm ci
|
|
||||||
|
|
||||||
- name: Run TypeScript type check
|
|
||||||
shell: sh
|
|
||||||
run: |
|
|
||||||
cd frontend && npx tsc --noEmit
|
|
||||||
|
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
shell: sh
|
shell: sh
|
||||||
env:
|
env:
|
||||||
|
|||||||
+21
-11
@@ -108,12 +108,10 @@ class CompanyAnalyzer:
|
|||||||
def analyze_single_patent(self, patent_id: str, company_name: str) -> str:
|
def analyze_single_patent(self, patent_id: str, company_name: str) -> str:
|
||||||
"""Analyze a single patent by ID.
|
"""Analyze a single patent by ID.
|
||||||
|
|
||||||
Prerequisite:
|
If the patent PDF is not already on disk, this method attempts to
|
||||||
The patent PDF must already exist at ``patents/{patent_id}.pdf``
|
download it automatically by looking up the PDF link in the database
|
||||||
before calling this method. PDFs are downloaded automatically when
|
cache. If the link is not cached either, a ``FileNotFoundError`` is
|
||||||
using the batch analysis pipeline (``analyze_company`` or the
|
raised with instructions on how to obtain the PDF.
|
||||||
``/analyze/batch`` API endpoint). For standalone usage, download
|
|
||||||
the PDF manually or call ``SERP.save_patents()`` first.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
patent_id: Publication ID of the patent (e.g. "US-11234567-B2")
|
patent_id: Publication ID of the patent (e.g. "US-11234567-B2")
|
||||||
@@ -123,7 +121,7 @@ class CompanyAnalyzer:
|
|||||||
Analysis of the specific patent's innovation quality
|
Analysis of the specific patent's innovation quality
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If the patent PDF is not found at the expected path.
|
FileNotFoundError: If the patent PDF cannot be found or downloaded.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
logger.info("Analyzing patent %s for %s...", patent_id, company_name)
|
logger.info("Analyzing patent %s for %s...", patent_id, company_name)
|
||||||
@@ -131,10 +129,22 @@ class CompanyAnalyzer:
|
|||||||
patent_path = f"patents/{patent_id}.pdf"
|
patent_path = f"patents/{patent_id}.pdf"
|
||||||
|
|
||||||
if not os.path.exists(patent_path):
|
if not os.path.exists(patent_path):
|
||||||
raise FileNotFoundError(
|
# Attempt to download the PDF automatically from cached metadata
|
||||||
f"Patent PDF not found at '{patent_path}'. "
|
cached = self.db.get_cached_patent(patent_id)
|
||||||
f"Download the PDF first using SERP.save_patents() or the batch analysis pipeline."
|
pdf_link = cached.get("pdf_link") if cached else None
|
||||||
)
|
|
||||||
|
if pdf_link:
|
||||||
|
logger.info("PDF not on disk; downloading %s from cached link", patent_id)
|
||||||
|
patent = SERP.save_patents(
|
||||||
|
Patent(patent_id=patent_id, pdf_link=pdf_link)
|
||||||
|
)
|
||||||
|
patent_path = patent.pdf_path
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"Patent PDF not found at '{patent_path}' and no download link is "
|
||||||
|
f"cached for '{patent_id}'. Run a company analysis first to populate "
|
||||||
|
f"the cache, or call SERP.save_patents() with the patent's PDF link."
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sections = SERP.parse_patent_pdf(patent_path)
|
sections = SERP.parse_patent_pdf(patent_path)
|
||||||
|
|||||||
@@ -429,6 +429,38 @@ async def analyze_company(
|
|||||||
return _convert_result(result)
|
return _convert_result(result)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/analyze/patent/{patent_id}",
|
||||||
|
tags=["Analysis"],
|
||||||
|
)
|
||||||
|
async def analyze_single_patent(
|
||||||
|
patent_id: str,
|
||||||
|
company_name: str = Query(description="Company name for analysis context"),
|
||||||
|
_: UserResponse = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Analyze a single patent by its publication ID.
|
||||||
|
|
||||||
|
If the patent PDF is not already cached locally, the system will attempt
|
||||||
|
to download it automatically from a previously cached link. If no link
|
||||||
|
is available, a 404 error is returned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
patent_id: Patent publication ID (e.g. "US-11234567-B2")
|
||||||
|
company_name: Company name for analysis context
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Analysis text for the patent
|
||||||
|
"""
|
||||||
|
if not _analyzer:
|
||||||
|
raise HTTPException(status_code=503, detail="Analyzer not initialized")
|
||||||
|
|
||||||
|
try:
|
||||||
|
analysis = _analyzer.analyze_single_patent(patent_id, company_name)
|
||||||
|
return {"patent_id": patent_id, "company_name": company_name, "analysis": analysis}
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise HTTPException(status_code=404, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/analyze/batch",
|
"/analyze/batch",
|
||||||
response_model=BatchAnalysisResponse,
|
response_model=BatchAnalysisResponse,
|
||||||
|
|||||||
Generated
+4
-4
@@ -10,7 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.51.0",
|
"@tanstack/react-query": "^5.51.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^0.400.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.24.0",
|
"react-router-dom": "^6.24.0",
|
||||||
@@ -3452,9 +3452,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "1.7.0",
|
"version": "0.400.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.400.0.tgz",
|
||||||
"integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==",
|
"integrity": "sha512-rpp7pFHh3Xd93KHixNgB0SqThMHpYNzsGUu69UaQbSZ75Q/J3m5t6EhKyMT3m4w2WOxmJ2mY0tD3vebnXqQryQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
|||||||
@@ -7,13 +7,12 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"typecheck": "tsc --noEmit",
|
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.51.0",
|
"@tanstack/react-query": "^5.51.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^0.400.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.24.0",
|
"react-router-dom": "^6.24.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user