Skip to content

Commit 69148f5

Browse files
author
Tobias Geilen
committed
adjusted design and added miising projects functionality
1 parent 2e284f7 commit 69148f5

10 files changed

Lines changed: 573 additions & 97 deletions

File tree

backend/app/repositories/project_repository.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,3 @@ def remove_paper(db: Session, project_id: int, paper_id: int, user_id: int) -> P
104104
db.refresh(project)
105105
return project
106106

107-

backend/app/routes/project_routes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,3 @@ def remove_paper_from_project(
129129
return ProjectService.remove_paper_from_project(
130130
db, current_username, project_id, paper_id
131131
)
132-
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
from fastapi import APIRouter, status
1+
from fastapi import APIRouter, Depends, status
2+
from sqlalchemy.orm import Session
23

4+
from app.core.database import get_db
35
from app.schemas.search_dto import SearchRequest, SearchResponse
46
from app.services.search_service import SearchService
57

@@ -12,10 +14,12 @@
1214
status_code=status.HTTP_200_OK,
1315
summary="Search for papers",
1416
)
15-
def search(request: SearchRequest) -> SearchResponse:
17+
def search(request: SearchRequest, db: Session = Depends(get_db)) -> SearchResponse:
1618
"""
17-
Returns a list of papers that match the search query
19+
Returns a list of papers that match the search query.
20+
21+
Currently, this returns the 5 most recently fetched papers, regardless
22+
of the query string.
1823
"""
1924

20-
papers = SearchService.search_papers(request.query)
21-
return SearchResponse.model_validate(papers)
25+
return SearchService.search_papers(db, request.query)

backend/app/schemas/search_dto.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Paper(BaseModel):
1010
"""
1111

1212
paper_id: int
13-
doi: str
13+
doi: Optional[str] # DOI is not always reliable or present in the source data
1414
source: str
1515
paper_type: str
1616
title: str

backend/app/services/project_service.py

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,36 @@ def get_project_with_papers(
4949
detail="Project not found.",
5050
)
5151

52-
papers = [PaperSummary.model_validate(p) for p in project.papers]
52+
papers: list[PaperSummary] = []
53+
for paper in project.papers:
54+
authors_value = None
55+
if isinstance(paper.authors, dict):
56+
authors_value = paper.authors
57+
elif isinstance(paper.authors, list):
58+
formatted = []
59+
for item in paper.authors:
60+
if isinstance(item, (list, tuple)) and item:
61+
last = item[0] or ""
62+
first = item[1] if len(item) > 1 else ""
63+
middle = item[2] if len(item) > 2 else ""
64+
name = " ".join(part for part in [first, middle, last] if part)
65+
if name:
66+
formatted.append(name)
67+
if formatted:
68+
authors_value = {
69+
str(idx): name for idx, name in enumerate(formatted)
70+
}
71+
papers.append(
72+
PaperSummary(
73+
paper_id=paper.paper_id,
74+
title=paper.title,
75+
authors=authors_value,
76+
abstract=paper.abstract,
77+
published_at=paper.published_at,
78+
url=paper.paper_id_external,
79+
pdf_url=None,
80+
)
81+
)
5382
return ProjectWithPapersResponse(
5483
project=ProjectResponse.model_validate(project),
5584
papers=papers,
@@ -111,7 +140,36 @@ def add_paper_to_project(
111140
detail=str(exc),
112141
) from exc
113142

114-
papers = [PaperSummary.model_validate(p) for p in project.papers]
143+
papers: list[PaperSummary] = []
144+
for paper in project.papers:
145+
authors_value = None
146+
if isinstance(paper.authors, dict):
147+
authors_value = paper.authors
148+
elif isinstance(paper.authors, list):
149+
formatted = []
150+
for item in paper.authors:
151+
if isinstance(item, (list, tuple)) and item:
152+
last = item[0] or ""
153+
first = item[1] if len(item) > 1 else ""
154+
middle = item[2] if len(item) > 2 else ""
155+
name = " ".join(part for part in [first, middle, last] if part)
156+
if name:
157+
formatted.append(name)
158+
if formatted:
159+
authors_value = {
160+
str(idx): name for idx, name in enumerate(formatted)
161+
}
162+
papers.append(
163+
PaperSummary(
164+
paper_id=paper.paper_id,
165+
title=paper.title,
166+
authors=authors_value,
167+
abstract=paper.abstract,
168+
published_at=paper.published_at,
169+
url=paper.paper_id_external,
170+
pdf_url=None,
171+
)
172+
)
115173
return ProjectWithPapersResponse(
116174
project=ProjectResponse.model_validate(project),
117175
papers=papers,
@@ -133,10 +191,38 @@ def remove_paper_from_project(
133191
detail=str(exc),
134192
) from exc
135193

136-
papers = [PaperSummary.model_validate(p) for p in project.papers]
194+
papers: list[PaperSummary] = []
195+
for paper in project.papers:
196+
authors_value = None
197+
if isinstance(paper.authors, dict):
198+
authors_value = paper.authors
199+
elif isinstance(paper.authors, list):
200+
formatted = []
201+
for item in paper.authors:
202+
if isinstance(item, (list, tuple)) and item:
203+
last = item[0] or ""
204+
first = item[1] if len(item) > 1 else ""
205+
middle = item[2] if len(item) > 2 else ""
206+
name = " ".join(part for part in [first, middle, last] if part)
207+
if name:
208+
formatted.append(name)
209+
if formatted:
210+
authors_value = {
211+
str(idx): name for idx, name in enumerate(formatted)
212+
}
213+
papers.append(
214+
PaperSummary(
215+
paper_id=paper.paper_id,
216+
title=paper.title,
217+
authors=authors_value,
218+
abstract=paper.abstract,
219+
published_at=paper.published_at,
220+
url=paper.paper_id_external,
221+
pdf_url=None,
222+
)
223+
)
137224
return ProjectWithPapersResponse(
138225
project=ProjectResponse.model_validate(project),
139226
papers=papers,
140227
)
141228

142-
Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,100 @@
11
import datetime
22

3-
from app.core.deps import get_openai_provider, get_specter2_query_embedder
4-
from app.schemas.search_dto import Paper, SearchResponse
3+
from sqlalchemy import desc
4+
from sqlalchemy.orm import Session
5+
6+
from app.models.paper import Paper as PaperModel
7+
from app.schemas.search_dto import Paper as PaperSchema, SearchResponse
8+
9+
# from app.core.deps import get_openai_provider, get_specter2_query_embedder
510

611

712
class SearchService:
813
"""
9-
Service for managing search requests
14+
Service for managing search requests.
15+
16+
NOTE:
17+
-----
18+
For now, we just return the 5 most recently fetched papers from the
19+
database (ordered by `fetched_at DESC`) to establish the connection
20+
between the API and the frontend.
21+
22+
To restore or extend the original behavior that uses OpenAI + SPECTER2
23+
embeddings, uncomment and adapt the code in `search_papers` below.
1024
"""
1125

1226
@staticmethod
13-
def search_papers(query: str) -> SearchResponse:
27+
def search_papers(db: Session, query: str) -> SearchResponse:
1428
"""
15-
Returns a list of matching papers based on the query
29+
Return the 5 most recent papers from the database.
30+
31+
The `query` parameter is currently ignored; it's kept for future
32+
use when more advanced search logic is implemented.
1633
"""
17-
openai_provider = get_openai_provider()
18-
keywords = openai_provider.extract_keywords(query)
19-
20-
embedder = get_specter2_query_embedder()
21-
embeddings = embedder.embed_batch(keywords)
22-
23-
# Additionally embedd the original query -> could also be used for searching
24-
embeddings.append(embedder.embed_one(query))
25-
26-
# Call search module to find matching papers in vector db
27-
# Needs to be implemented in the future
28-
29-
# Mock paper as we cannot implement search as of now
30-
example_paper = Paper(
31-
paper_id=1,
32-
doi="some doi",
33-
source="arXiv",
34-
paper_type="conference paper",
35-
title="Deep Reinforcement Learning for Multi-Agent Voting with Approval Preferences",
36-
authors={"main": "Usain Bolt"},
37-
abstract="Very good paper about DRL and social choice.",
38-
published_at=datetime.date.today(),
39-
pdf_url="some url",
40-
url="some url",
41-
fetched_at=datetime.datetime.now(),
34+
35+
# --- Original implementation (kept for easy reactivation) ---
36+
# openai_provider = get_openai_provider()
37+
# keywords = openai_provider.extract_keywords(query)
38+
#
39+
# embedder = get_specter2_query_embedder()
40+
# embeddings = embedder.embed_batch(keywords)
41+
#
42+
# # Additionally embed the original query -> could also be used for searching
43+
# embeddings.append(embedder.embed_one(query))
44+
#
45+
# TODO: Use `embeddings` to perform a vector similarity search against
46+
# the `paper.embedding` column (pgvector) and return real results.
47+
48+
# Simple placeholder: return the 5 most recently fetched papers
49+
papers_db = (
50+
db.query(PaperModel)
51+
.order_by(desc(PaperModel.fetched_at))
52+
.limit(10) # fetch a few more and filter below
53+
.all()
4254
)
4355

44-
return SearchResponse(papers=[example_paper])
56+
# Manually map ORM objects to schema to handle slight type/field
57+
# differences (e.g. authors JSON shape, missing URLs, nullable DOIs).
58+
paper_schemas: list[PaperSchema] = []
59+
for p in papers_db:
60+
# Only require a title; DOI and URLs may be missing or unreliable.
61+
if not p.title:
62+
continue
63+
64+
authors_value = None
65+
if isinstance(p.authors, dict):
66+
authors_value = p.authors
67+
elif isinstance(p.authors, list):
68+
# Example format: [["Last", "First", "Middle"], ...]
69+
formatted = []
70+
for item in p.authors:
71+
if isinstance(item, (list, tuple)) and item:
72+
last = item[0] or ""
73+
first = item[1] if len(item) > 1 else ""
74+
middle = item[2] if len(item) > 2 else ""
75+
name = " ".join(part for part in [first, middle, last] if part)
76+
if name:
77+
formatted.append(name)
78+
if formatted:
79+
authors_value = {str(idx): name for idx, name in enumerate(formatted)}
80+
81+
paper_schemas.append(
82+
PaperSchema(
83+
paper_id=p.paper_id,
84+
doi=p.doi,
85+
source=str(getattr(p.source, "value", p.source)),
86+
paper_type=str(getattr(p.paper_type, "value", p.paper_type)),
87+
title=p.title,
88+
authors=authors_value,
89+
abstract=p.abstract,
90+
published_at=p.published_at,
91+
pdf_url=None,
92+
url=None,
93+
fetched_at=p.fetched_at,
94+
)
95+
)
96+
97+
if len(paper_schemas) >= 5:
98+
break
99+
100+
return SearchResponse(papers=paper_schemas)

0 commit comments

Comments
 (0)