Skip to content

Commit 12ab92c

Browse files
committed
Changed max line length for python
1 parent 05e5f3e commit 12ab92c

8 files changed

Lines changed: 321 additions & 287 deletions

File tree

backend/app/core/security.py

Lines changed: 66 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,66 @@
1-
"""Security utilities for creating and validating JWT tokens."""
2-
3-
from datetime import datetime, timedelta, timezone
4-
from typing import Any, Dict, Optional
5-
6-
from fastapi import Depends, HTTPException, status
7-
from fastapi.security import OAuth2PasswordBearer
8-
from jose import JWTError, jwt
9-
10-
from app.core.config import settings
11-
12-
ALGORITHM = "HS256"
13-
14-
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
15-
16-
17-
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
18-
"""Create a signed JWT access token."""
19-
20-
to_encode = data.copy()
21-
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES))
22-
to_encode.update({"exp": expire})
23-
return jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=ALGORITHM)
24-
25-
26-
def create_refresh_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
27-
"""Create a signed JWT refresh token."""
28-
29-
to_encode = data.copy()
30-
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS))
31-
to_encode.update({"exp": expire, "type": "refresh"})
32-
return jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=ALGORITHM)
33-
34-
35-
def verify_token(token: str) -> Optional[Dict[str, Any]]:
36-
"""Decode a JWT token and return its payload, if valid."""
37-
38-
try:
39-
return jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[ALGORITHM])
40-
except JWTError:
41-
return None
42-
43-
44-
def get_current_user(token: str = Depends(oauth2_scheme)) -> str:
45-
"""Return the username encoded in the access token."""
46-
47-
payload = verify_token(token)
48-
if not payload:
49-
raise HTTPException(
50-
status_code=status.HTTP_401_UNAUTHORIZED,
51-
detail="Invalid or expired token.",
52-
headers={"WWW-Authenticate": "Bearer"},
53-
)
54-
55-
username = payload.get("sub")
56-
if not username:
57-
raise HTTPException(
58-
status_code=status.HTTP_401_UNAUTHORIZED,
59-
detail="Invalid token payload.",
60-
)
61-
62-
return username
1+
"""Security utilities for creating and validating JWT tokens."""
2+
3+
from datetime import datetime, timedelta, timezone
4+
from typing import Any, Dict, Optional
5+
6+
from fastapi import Depends, HTTPException, status
7+
from fastapi.security import OAuth2PasswordBearer
8+
from jose import JWTError, jwt
9+
10+
from app.core.config import settings
11+
12+
ALGORITHM = "HS256"
13+
14+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
15+
16+
17+
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
18+
"""Create a signed JWT access token."""
19+
20+
to_encode = data.copy()
21+
expire = datetime.now(timezone.utc) + (
22+
expires_delta or timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
23+
)
24+
to_encode.update({"exp": expire})
25+
return jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=ALGORITHM)
26+
27+
28+
def create_refresh_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
29+
"""Create a signed JWT refresh token."""
30+
31+
to_encode = data.copy()
32+
expire = datetime.now(timezone.utc) + (
33+
expires_delta or timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
34+
)
35+
to_encode.update({"exp": expire, "type": "refresh"})
36+
return jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=ALGORITHM)
37+
38+
39+
def verify_token(token: str) -> Optional[Dict[str, Any]]:
40+
"""Decode a JWT token and return its payload, if valid."""
41+
42+
try:
43+
return jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[ALGORITHM])
44+
except JWTError:
45+
return None
46+
47+
48+
def get_current_user(token: str = Depends(oauth2_scheme)) -> str:
49+
"""Return the username encoded in the access token."""
50+
51+
payload = verify_token(token)
52+
if not payload:
53+
raise HTTPException(
54+
status_code=status.HTTP_401_UNAUTHORIZED,
55+
detail="Invalid or expired token.",
56+
headers={"WWW-Authenticate": "Bearer"},
57+
)
58+
59+
username = payload.get("sub")
60+
if not username:
61+
raise HTTPException(
62+
status_code=status.HTTP_401_UNAUTHORIZED,
63+
detail="Invalid token payload.",
64+
)
65+
66+
return username

backend/app/models/paper.py

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
1-
"""SQLAlchemy model and enumerations for scholarly papers."""
2-
3-
from datetime import date, datetime
4-
from typing import TYPE_CHECKING, List, Optional
5-
6-
from sqlalchemy import JSON, BigInteger, Date, DateTime, String, Text
7-
from sqlalchemy import Enum as SqlEnum
8-
from sqlalchemy.orm import Mapped, mapped_column, relationship
9-
from sqlalchemy.sql import func
10-
11-
from app.constants.database_constants import PaperSource, PaperType
12-
from app.core.database import Base
13-
14-
15-
class Paper(Base):
16-
"""Database representation of a scholarly paper."""
17-
18-
__tablename__ = "paper"
19-
20-
paper_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
21-
doi: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
22-
source: Mapped[PaperSource] = mapped_column(SqlEnum(PaperSource, name="paper_source"), nullable=False)
23-
paper_type: Mapped[PaperType] = mapped_column(
24-
SqlEnum(PaperType, name="paper_type"),
25-
nullable=False,
26-
default=PaperType.PREPRINT,
27-
server_default=PaperType.PREPRINT.value,
28-
)
29-
title: Mapped[str] = mapped_column(String(512), nullable=False)
30-
authors: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
31-
abstract: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
32-
published_at: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
33-
pdf_url: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
34-
url: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
35-
fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
36-
37-
project_links: Mapped[List["ProjectPaper"]] = relationship(
38-
"ProjectPaper", back_populates="paper", cascade="all, delete-orphan"
39-
)
40-
projects: Mapped[List["Project"]] = relationship("Project", secondary="project_paper", back_populates="papers")
41-
42-
43-
if TYPE_CHECKING:
44-
from .project import Project
45-
from .project_paper import ProjectPaper
1+
"""SQLAlchemy model and enumerations for scholarly papers."""
2+
3+
from datetime import date, datetime
4+
from typing import TYPE_CHECKING, List, Optional
5+
6+
from sqlalchemy import JSON, BigInteger, Date, DateTime, String, Text
7+
from sqlalchemy import Enum as SqlEnum
8+
from sqlalchemy.orm import Mapped, mapped_column, relationship
9+
from sqlalchemy.sql import func
10+
11+
from app.constants.database_constants import PaperSource, PaperType
12+
from app.core.database import Base
13+
14+
15+
class Paper(Base):
16+
"""Database representation of a scholarly paper."""
17+
18+
__tablename__ = "paper"
19+
20+
paper_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
21+
doi: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
22+
source: Mapped[PaperSource] = mapped_column(
23+
SqlEnum(PaperSource, name="paper_source"), nullable=False
24+
)
25+
paper_type: Mapped[PaperType] = mapped_column(
26+
SqlEnum(PaperType, name="paper_type"),
27+
nullable=False,
28+
default=PaperType.PREPRINT,
29+
server_default=PaperType.PREPRINT.value,
30+
)
31+
title: Mapped[str] = mapped_column(String(512), nullable=False)
32+
authors: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
33+
abstract: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
34+
published_at: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
35+
pdf_url: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
36+
url: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
37+
fetched_at: Mapped[datetime] = mapped_column(
38+
DateTime(timezone=True), nullable=False, server_default=func.now()
39+
)
40+
41+
project_links: Mapped[List["ProjectPaper"]] = relationship(
42+
"ProjectPaper", back_populates="paper", cascade="all, delete-orphan"
43+
)
44+
projects: Mapped[List["Project"]] = relationship(
45+
"Project", secondary="project_paper", back_populates="papers"
46+
)
47+
48+
49+
if TYPE_CHECKING:
50+
from .project import Project
51+
from .project_paper import ProjectPaper

backend/app/models/project.py

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
1-
"""SQLAlchemy model for projects managed in the application."""
2-
3-
from datetime import datetime
4-
from typing import TYPE_CHECKING, List
5-
6-
from sqlalchemy import BigInteger, DateTime, ForeignKey, String
7-
from sqlalchemy.orm import Mapped, mapped_column, relationship
8-
from sqlalchemy.sql import func
9-
10-
from app.core.database import Base
11-
12-
13-
class Project(Base):
14-
"""Database representation of a user project."""
15-
16-
__tablename__ = "project"
17-
18-
project_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
19-
created_by: Mapped[int] = mapped_column(BigInteger, ForeignKey("user.user_id"), nullable=False, index=True)
20-
project_name: Mapped[str] = mapped_column(String(255), nullable=False)
21-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
22-
23-
creator: Mapped["User"] = relationship("User", back_populates="projects")
24-
project_papers: Mapped[List["ProjectPaper"]] = relationship(
25-
"ProjectPaper", back_populates="project", cascade="all, delete-orphan"
26-
)
27-
papers: Mapped[List["Paper"]] = relationship("Paper", secondary="project_paper", back_populates="projects")
28-
29-
30-
if TYPE_CHECKING:
31-
from .paper import Paper
32-
from .project_paper import ProjectPaper
33-
from .user import User
1+
"""SQLAlchemy model for projects managed in the application."""
2+
3+
from datetime import datetime
4+
from typing import TYPE_CHECKING, List
5+
6+
from sqlalchemy import BigInteger, DateTime, ForeignKey, String
7+
from sqlalchemy.orm import Mapped, mapped_column, relationship
8+
from sqlalchemy.sql import func
9+
10+
from app.core.database import Base
11+
12+
13+
class Project(Base):
14+
"""Database representation of a user project."""
15+
16+
__tablename__ = "project"
17+
18+
project_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
19+
created_by: Mapped[int] = mapped_column(
20+
BigInteger, ForeignKey("user.user_id"), nullable=False, index=True
21+
)
22+
project_name: Mapped[str] = mapped_column(String(255), nullable=False)
23+
created_at: Mapped[datetime] = mapped_column(
24+
DateTime(timezone=True), nullable=False, server_default=func.now()
25+
)
26+
27+
creator: Mapped["User"] = relationship("User", back_populates="projects")
28+
project_papers: Mapped[List["ProjectPaper"]] = relationship(
29+
"ProjectPaper", back_populates="project", cascade="all, delete-orphan"
30+
)
31+
papers: Mapped[List["Paper"]] = relationship(
32+
"Paper", secondary="project_paper", back_populates="projects"
33+
)
34+
35+
36+
if TYPE_CHECKING:
37+
from .paper import Paper
38+
from .project_paper import ProjectPaper
39+
from .user import User
Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,49 @@
1-
"""Association table model linking projects and papers."""
2-
3-
from datetime import datetime
4-
from typing import TYPE_CHECKING
5-
6-
from sqlalchemy import BigInteger, DateTime, ForeignKey, UniqueConstraint
7-
from sqlalchemy.orm import Mapped, mapped_column, relationship
8-
from sqlalchemy.sql import func
9-
10-
from app.core.database import Base
11-
12-
13-
class ProjectPaper(Base):
14-
"""Associative entity connecting projects with papers."""
15-
16-
__tablename__ = "project_paper"
17-
18-
# Ensure a paper can only be linked once to the same project.
19-
__table_args__ = (
20-
UniqueConstraint(
21-
"project_id",
22-
"paper_id",
23-
name="uq_project_paper_project_id_paper_id",
24-
),
25-
)
26-
27-
project_paper_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
28-
29-
project_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("project.project_id"), nullable=False, index=True)
30-
31-
paper_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("paper.paper_id"), nullable=False, index=True)
32-
33-
added_at: Mapped[datetime] = mapped_column(
34-
DateTime(timezone=True),
35-
nullable=False,
36-
server_default=func.now(),
37-
)
38-
39-
project: Mapped["Project"] = relationship("Project", back_populates="project_papers")
40-
paper: Mapped["Paper"] = relationship("Paper", back_populates="project_links")
41-
42-
43-
if TYPE_CHECKING:
44-
from .paper import Paper
45-
from .project import Project
1+
"""Association table model linking projects and papers."""
2+
3+
from datetime import datetime
4+
from typing import TYPE_CHECKING
5+
6+
from sqlalchemy import BigInteger, DateTime, ForeignKey, UniqueConstraint
7+
from sqlalchemy.orm import Mapped, mapped_column, relationship
8+
from sqlalchemy.sql import func
9+
10+
from app.core.database import Base
11+
12+
13+
class ProjectPaper(Base):
14+
"""Associative entity connecting projects with papers."""
15+
16+
__tablename__ = "project_paper"
17+
18+
# Ensure a paper can only be linked once to the same project.
19+
__table_args__ = (
20+
UniqueConstraint(
21+
"project_id",
22+
"paper_id",
23+
name="uq_project_paper_project_id_paper_id",
24+
),
25+
)
26+
27+
project_paper_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
28+
29+
project_id: Mapped[int] = mapped_column(
30+
BigInteger, ForeignKey("project.project_id"), nullable=False, index=True
31+
)
32+
33+
paper_id: Mapped[int] = mapped_column(
34+
BigInteger, ForeignKey("paper.paper_id"), nullable=False, index=True
35+
)
36+
37+
added_at: Mapped[datetime] = mapped_column(
38+
DateTime(timezone=True),
39+
nullable=False,
40+
server_default=func.now(),
41+
)
42+
43+
project: Mapped["Project"] = relationship("Project", back_populates="project_papers")
44+
paper: Mapped["Paper"] = relationship("Paper", back_populates="project_links")
45+
46+
47+
if TYPE_CHECKING:
48+
from .paper import Paper
49+
from .project import Project

0 commit comments

Comments
 (0)