Initial release

This commit is contained in:
Francesco Carmelo Capria 2025-06-21 18:15:33 +02:00
commit ae5e4b8873
52 changed files with 17572 additions and 0 deletions

View file

@ -0,0 +1,233 @@
from fastapi import Depends, APIRouter, Query, HTTPException
from sqlalchemy import select, or_, and_, asc, desc
from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List, Optional
from app.db import Publication, get_db, Keyword, Author, User
from app.schemas import PublicationOut, UserPublicationOut
from app.users import current_active_user
# Create router for publication endpoints
router = APIRouter()
# Endpoint to delete a publication
@router.delete("/publications/{publication_id}")
async def delete_publication(
publication_id: int,
user: User = Depends(current_active_user),
db: AsyncSession = Depends(get_db)
):
"""
Delete a publication owned by the current user
"""
# Find the publication with relations
result = await db.execute(
select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
).where(
and_(
Publication.id == publication_id,
Publication.user_id == user.id # Security: only user's own publications
)
)
)
publication = result.scalar_one_or_none()
if not publication:
raise HTTPException(
status_code=404,
detail="Publication not found or you don't have permission to delete it"
)
publication_title = publication.title
# Delete the publication (many-to-many relations are deleted automatically)
await db.delete(publication)
await db.commit()
print(f"🗑️ Publication deleted: '{publication_title}' (ID: {publication_id}) by user {user.email}")
return {"message": f"Publication '{publication_title}' successfully deleted"}
# Endpoint for user publications with sorting
@router.get("/users/me/publications", response_model=List[UserPublicationOut])
async def get_user_publications(
order_by: Optional[str] = Query("date_desc",
description="Sort by: date_asc, date_desc, title_asc, title_desc"),
user: User = Depends(current_active_user),
db: AsyncSession = Depends(get_db)
):
"""
Returns all publications uploaded by the current user with sorting
"""
stmt = select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
).where(
Publication.user_id == user.id
)
# Sorting management
if order_by == "date_asc":
stmt = stmt.order_by(asc(Publication.upload_date))
elif order_by == "date_desc":
stmt = stmt.order_by(desc(Publication.upload_date))
elif order_by == "title_asc":
stmt = stmt.order_by(asc(Publication.title))
elif order_by == "title_desc":
stmt = stmt.order_by(desc(Publication.title))
else:
# Default: descending by date (most recent first)
stmt = stmt.order_by(desc(Publication.upload_date))
result = await db.execute(stmt)
publications = result.scalars().all()
print(f"🔍 User {user.email} (ID: {user.id}) has {len(publications)} publications (sorted by: {order_by})")
return publications
# Search publications endpoint
@router.get("/publications", response_model=List[PublicationOut])
async def get_publications(
search: Optional[str] = Query(None,
description="Search by title, author or keyword. For multiple keywords use spaces: 'keyword1 keyword2'"),
order_by: Optional[str] = Query("date_desc",
description="Sort by: date_asc, date_desc, title_asc, title_desc"),
db: AsyncSession = Depends(get_db)
):
"""
🔍 ADVANCED SEARCH SYSTEM WITH KEYWORDS
Search function with priority and sorting:
1. Keywords (highest priority) - supports multiple search with spaces
2. Authors (medium priority)
3. Title (lowest priority)
Keywords are the core of the search system!
"""
print(f"🔍 Search: '{search}' | Sort by: {order_by}")
# If no search query, return all sorted
if search is None or not search.strip():
stmt = select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
)
# Sorting management
if order_by == "date_asc":
stmt = stmt.order_by(asc(Publication.upload_date))
elif order_by == "date_desc":
stmt = stmt.order_by(desc(Publication.upload_date))
elif order_by == "title_asc":
stmt = stmt.order_by(asc(Publication.title))
elif order_by == "title_desc":
stmt = stmt.order_by(desc(Publication.title))
else:
# Default: descending by date
stmt = stmt.order_by(desc(Publication.upload_date))
result = await db.execute(stmt)
return result.scalars().all()
search_term = search.strip()
# Split search string into individual keywords
search_keywords = [kw.strip().lower() for kw in search_term.split() if kw.strip()]
print(f"🔍 Keywords to search: {search_keywords}")
# SET to track already found IDs
found_publication_ids = set()
final_results = []
# 🎯 1. SEARCH BY KEYWORDS (highest priority) - MULTIPLE SEARCH
if search_keywords:
print("🔍 Step 1: Searching by multiple keywords...")
# Create conditions for each keyword
keyword_conditions = []
for keyword in search_keywords:
keyword_pattern = f"%{keyword}%"
keyword_conditions.append(
Publication.keywords.any(Keyword.name.ilike(keyword_pattern))
)
# Publication must have ALL keywords (AND)
keyword_query = select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
).where(
and_(*keyword_conditions) # All conditions must be true
)
keyword_result = await db.execute(keyword_query)
keyword_publications = keyword_result.scalars().all()
for pub in keyword_publications:
if pub.id not in found_publication_ids:
final_results.append(pub)
found_publication_ids.add(pub.id)
pub_keywords = [k.name for k in pub.keywords]
print(f" ✅ Found by keywords: {pub.title} (keywords: {pub_keywords})")
# 📝 2. SEARCH BY AUTHORS (medium priority) - uses complete string
print("🔍 Step 2: Searching by authors...")
author_pattern = f"%{search_term}%"
author_query = select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
).join(Publication.authors).where(
Author.name.ilike(author_pattern)
)
author_result = await db.execute(author_query)
author_publications = author_result.scalars().all()
for pub in author_publications:
if pub.id not in found_publication_ids:
final_results.append(pub)
found_publication_ids.add(pub.id)
pub_authors = [a.name for a in pub.authors]
print(f" ✅ Found by author: {pub.title} (authors: {pub_authors})")
# 📰 3. SEARCH BY TITLE (lowest priority) - uses complete string
print("🔍 Step 3: Searching by title...")
title_pattern = f"%{search_term}%"
title_query = select(Publication).options(
selectinload(Publication.authors),
selectinload(Publication.keywords)
).where(
Publication.title.ilike(title_pattern)
)
title_result = await db.execute(title_query)
title_publications = title_result.scalars().all()
for pub in title_publications:
if pub.id not in found_publication_ids:
final_results.append(pub)
found_publication_ids.add(pub.id)
print(f" ✅ Found by title: {pub.title}")
# Apply sorting to final results
print(f"🔍 Applying sorting: {order_by}")
if order_by == "date_asc":
final_results.sort(key=lambda x: x.upload_date)
elif order_by == "date_desc":
final_results.sort(key=lambda x: x.upload_date, reverse=True)
elif order_by == "title_asc":
final_results.sort(key=lambda x: x.title.lower())
elif order_by == "title_desc":
final_results.sort(key=lambda x: x.title.lower(), reverse=True)
else:
# Default: descending by date
final_results.sort(key=lambda x: x.upload_date, reverse=True)
print(f"🔍 Total results found: {len(final_results)}")
return final_results