ci: add ruff linting and mypy type checking
Add ruff and mypy to dev dependencies with configuration in pyproject.toml. Add a lint CI job that runs ruff check, ruff format --check, and mypy. Auto-fix import sorting and formatting across the codebase. Exclude alembic/versions from linting (auto-generated migrations). Ignore B008 (FastAPI Depends pattern) and RUF001 (unicode in user-facing strings). 21 ruff lint errors and 50 mypy errors remain for manual review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
399276ea69
commit
bf04c77aa9
137 changed files with 3066 additions and 1900 deletions
|
|
@ -18,7 +18,7 @@ from app.services.domains.portability.publication_import import (
|
|||
_upsert_imported_publication,
|
||||
)
|
||||
from app.services.domains.portability.scholar_import import _upsert_imported_scholars
|
||||
from app.services.domains.portability.types import ImportExportError, ImportedPublicationInput
|
||||
from app.services.domains.portability.types import ImportedPublicationInput, ImportExportError
|
||||
|
||||
|
||||
async def import_user_data(
|
||||
|
|
@ -58,8 +58,8 @@ async def import_user_data(
|
|||
|
||||
__all__ = [
|
||||
"EXPORT_SCHEMA_VERSION",
|
||||
"MAX_IMPORT_SCHOLARS",
|
||||
"MAX_IMPORT_PUBLICATIONS",
|
||||
"MAX_IMPORT_SCHOLARS",
|
||||
"ImportExportError",
|
||||
"ImportedPublicationInput",
|
||||
"export_user_data",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import select
|
||||
|
|
@ -11,7 +11,7 @@ from app.services.domains.portability.constants import EXPORT_SCHEMA_VERSION
|
|||
|
||||
|
||||
def _exported_at_iso() -> str:
|
||||
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
||||
return datetime.now(UTC).replace(microsecond=0).isoformat()
|
||||
|
||||
|
||||
def _serialize_export_scholar(profile: ScholarProfile) -> dict[str, Any]:
|
||||
|
|
@ -58,9 +58,7 @@ async def export_user_data(
|
|||
user_id: int,
|
||||
) -> dict[str, Any]:
|
||||
scholars_result = await db_session.execute(
|
||||
select(ScholarProfile)
|
||||
.where(ScholarProfile.user_id == user_id)
|
||||
.order_by(ScholarProfile.id.asc())
|
||||
select(ScholarProfile).where(ScholarProfile.user_id == user_id).order_by(ScholarProfile.id.asc())
|
||||
)
|
||||
publication_result = await db_session.execute(
|
||||
select(
|
||||
|
|
|
|||
|
|
@ -104,6 +104,4 @@ def _validate_import_sizes(
|
|||
if len(scholars) > MAX_IMPORT_SCHOLARS:
|
||||
raise ImportExportError(f"Import exceeds max scholars ({MAX_IMPORT_SCHOLARS}).")
|
||||
if len(publications) > MAX_IMPORT_PUBLICATIONS:
|
||||
raise ImportExportError(
|
||||
f"Import exceeds max publications ({MAX_IMPORT_PUBLICATIONS})."
|
||||
)
|
||||
raise ImportExportError(f"Import exceeds max publications ({MAX_IMPORT_PUBLICATIONS}).")
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ from sqlalchemy import select
|
|||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.models import Publication, ScholarProfile, ScholarPublication
|
||||
from app.services.domains.ingestion.application import build_publication_url, normalize_title
|
||||
from app.services.domains.doi.normalize import normalize_doi
|
||||
from app.services.domains.ingestion.application import build_publication_url, normalize_title
|
||||
from app.services.domains.portability.normalize import (
|
||||
_normalize_citation_count,
|
||||
_normalize_optional_text,
|
||||
|
|
@ -22,9 +22,7 @@ async def _find_publication_by_cluster(
|
|||
*,
|
||||
cluster_id: str,
|
||||
) -> Publication | None:
|
||||
result = await db_session.execute(
|
||||
select(Publication).where(Publication.cluster_id == cluster_id)
|
||||
)
|
||||
result = await db_session.execute(select(Publication).where(Publication.cluster_id == cluster_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
|
|
@ -33,9 +31,7 @@ async def _find_publication_by_fingerprint(
|
|||
*,
|
||||
fingerprint_sha256: str,
|
||||
) -> Publication | None:
|
||||
result = await db_session.execute(
|
||||
select(Publication).where(Publication.fingerprint_sha256 == fingerprint_sha256)
|
||||
)
|
||||
result = await db_session.execute(select(Publication).where(Publication.fingerprint_sha256 == fingerprint_sha256))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ async def _load_user_scholar_map(
|
|||
*,
|
||||
user_id: int,
|
||||
) -> dict[str, ScholarProfile]:
|
||||
result = await db_session.execute(
|
||||
select(ScholarProfile).where(ScholarProfile.user_id == user_id)
|
||||
)
|
||||
result = await db_session.execute(select(ScholarProfile).where(ScholarProfile.user_id == user_id))
|
||||
profiles = list(result.scalars().all())
|
||||
return {profile.scholar_id: profile for profile in profiles}
|
||||
|
||||
|
|
@ -70,9 +68,7 @@ async def _upsert_imported_scholars(
|
|||
for item in scholars:
|
||||
try:
|
||||
scholar_id = scholar_service.validate_scholar_id(str(item["scholar_id"]))
|
||||
display_name = scholar_service.normalize_display_name(
|
||||
str(item.get("display_name") or "")
|
||||
)
|
||||
display_name = scholar_service.normalize_display_name(str(item.get("display_name") or ""))
|
||||
override_url = scholar_service.normalize_profile_image_url(
|
||||
_normalize_optional_text(item.get("profile_image_override_url"))
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue