Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

# add your model's MetaData object here
# for 'autogenerate' support
from app import schema
target_metadata = schema.Base.metadata
from app.db import Base

target_metadata = Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
Expand Down
55 changes: 55 additions & 0 deletions backend/alembic/versions/6d107741a92e_add_glossary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""add glossary

Revision ID: 6d107741a92e
Revises: b3e764c93fac
Create Date: 2024-07-17 01:01:34.146777

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# pylint: disable=E1101

revision: str = "6d107741a92e"
down_revision: Union[str, None] = "b3e764c93fac"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.create_table(
"glossary_document",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_by", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(
["created_by"],
["user.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"glossary_record",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.Column("document_id", sa.Integer(), nullable=False),
sa.Column("comment", sa.String(), nullable=False),
sa.Column("source", sa.String(), nullable=False),
sa.Column("target", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["document_id"],
["glossary_document.id"],
),
sa.PrimaryKeyConstraint("id"),
)


def downgrade() -> None:
op.drop_table("glossary_record")
op.drop_table("glossary_document")
48 changes: 21 additions & 27 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.db import Base
from app.glossary.models import GlossaryDocument, GlossaryRecord
from app.schema import (
DocumentTask,
TmxDocument,
TmxRecord,
User,
XliffDocument,
XliffRecord,
)

from app.routers import auth, tmx, user, users, xliff


def create_app():
app = FastAPI()

# TODO: it would be nice to make it debug-only
origins = [
"http://localhost:5173",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(auth.router)
app.include_router(tmx.router)
app.include_router(xliff.router)
app.include_router(user.router)
app.include_router(users.router)

return app
__all__ = [
"Base",
"DocumentTask",
"TmxDocument",
"TmxRecord",
"User",
"XliffDocument",
"XliffRecord",
"GlossaryDocument",
"GlossaryRecord",
]
3 changes: 1 addition & 2 deletions backend/app/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@

from app import models, schema
from app.db import get_db
from app.settings import Settings, get_settings
from app.settings import settings


def get_current_user_id(
settings: Annotated[Settings, Depends(get_settings)],
session: Annotated[str | None, Cookie(include_in_schema=False)] = None,
) -> int:
if not session:
Expand Down
29 changes: 7 additions & 22 deletions backend/app/db.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,16 @@
from sqlalchemy import Engine, create_engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

from app.settings import get_settings
from app.settings import settings

engine: Engine | None = None
SessionLocal: sessionmaker | None = None
engine = create_engine(settings.database_url)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


def init_connection(connection_url: str):
global engine, SessionLocal
engine = create_engine(connection_url)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


def close_connection():
global engine
if engine:
engine.dispose()
engine = None
Base = declarative_base()


def get_db():
if not engine:
init_connection(get_settings().database_url)

assert SessionLocal
db: Session = SessionLocal()
db = SessionLocal()
try:
yield db
finally:
Expand Down
Empty file.
41 changes: 41 additions & 0 deletions backend/app/glossary/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from datetime import UTC, datetime
from typing import TYPE_CHECKING

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.db import Base
from app.schema import User

if TYPE_CHECKING:
from app.schema import User


class GlossaryDocument(Base):
__tablename__ = "glossary_document"
id: Mapped[int] = mapped_column(primary_key=True)
created_by: Mapped[int] = mapped_column(ForeignKey("user.id"))
created_at: Mapped[datetime] = mapped_column(default=datetime.now(UTC))
updated_at: Mapped[datetime] = mapped_column(default=datetime.now(UTC))

records: Mapped[list["GlossaryRecord"]] = relationship(
back_populates="document",
cascade="all, delete-orphan",
order_by="GlossaryRecord.id",
)
user: Mapped["User"] = relationship(back_populates="glossaries")


class GlossaryRecord(Base):
__tablename__ = "glossary_record"

id: Mapped[int] = mapped_column(primary_key=True)
created_at: Mapped[datetime] = mapped_column(default=datetime.now(UTC))
updated_at: Mapped[datetime] = mapped_column(default=datetime.now(UTC))

document_id: Mapped[int] = mapped_column(ForeignKey("glossary_document.id"))
comment: Mapped[str] = mapped_column()
source: Mapped[str] = mapped_column()
target: Mapped[str] = mapped_column()

document: Mapped["GlossaryDocument"] = relationship(back_populates="records")
6 changes: 2 additions & 4 deletions backend/app/routers/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import UTC, datetime, timedelta
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Response, status
from itsdangerous import URLSafeTimedSerializer
Expand All @@ -9,17 +8,16 @@
from app.auth import has_user_role
from app.db import get_db
from app.security import password_hasher
from app.settings import Settings, get_settings
from app.settings import settings

router = APIRouter(prefix="/auth", tags=["auth"])


@router.post("/login")
def login(
data: models.AuthFields,
db: Annotated[Session, Depends(get_db)],
settings: Annotated[Settings, Depends(get_settings)],
response: Response,
db: Session = Depends(get_db),
) -> models.StatusMessage:
if not data.password:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
Expand Down
13 changes: 10 additions & 3 deletions backend/app/schema.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from datetime import UTC, datetime
from typing import TYPE_CHECKING

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.db import Base

class Base(DeclarativeBase):
pass
if TYPE_CHECKING:
from app.glossary.models import GlossaryDocument


class TmxDocument(Base):
Expand Down Expand Up @@ -90,3 +92,8 @@ class User(Base):
xliffs: Mapped[list["XliffDocument"]] = relationship(
back_populates="user", cascade="all, delete-orphan", order_by="XliffDocument.id"
)
glossaries: Mapped[list["GlossaryDocument"]] = relationship(
back_populates="user",
cascade="all, delete-orphan",
order_by="GlossaryDocument.id",
)
11 changes: 7 additions & 4 deletions backend/app/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from functools import lru_cache
from typing import Literal

from pydantic_settings import BaseSettings

Expand All @@ -11,8 +11,11 @@ class Settings(BaseSettings):
translation_api: str = "https://translate.api.cloud.yandex.net"
secret_key: str = "secret-key"
domain_name: str | None = None
env: Literal["DEV", "PROD"] = "DEV"
origins: tuple[str, ...] = (
"http://localhost:5173",
"http://localhost:8000",
)


@lru_cache
def get_settings():
return Settings()
settings = Settings()
6 changes: 3 additions & 3 deletions backend/app/translators/yandex.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pydantic import BaseModel, PositiveInt, ValidationError

from app.models import MachineTranslationSettings
from app.settings import get_settings
from app.settings import settings


class YandexTranslatorResponse(BaseModel):
Expand Down Expand Up @@ -59,7 +59,7 @@ def get_iam_token(oauth_token: str):
iam_token (str): An IAM token from Yandex Translator API.
"""
response = requests.post(
f"{get_settings().iam_api}/iam/v1/tokens",
f"{settings.iam_api}/iam/v1/tokens",
json={"yandexPassportOauthToken": oauth_token},
timeout=15,
)
Expand Down Expand Up @@ -89,7 +89,7 @@ def translate_batch(lines: list[str], iam_token: str, folder_id: str) -> list[st
}

response = requests.post(
f"{get_settings().translation_api}/translate/v2/translate",
f"{settings.translation_api}/translate/v2/translate",
json=json_data,
headers=headers,
timeout=15,
Expand Down
3 changes: 0 additions & 3 deletions backend/asgi.py

This file was deleted.

29 changes: 29 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.routers import auth, tmx, user, users, xliff
from app.settings import settings

ROUTERS = (auth, tmx, user, users, xliff)


def create_app():
fastapi = FastAPI()

fastapi.add_middleware(
CORSMiddleware,
allow_origins=settings.origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
register_routers(fastapi)
return fastapi


def register_routers(fastapi: FastAPI):
for router in ROUTERS:
fastapi.include_router(router.router)


app = create_app()
2 changes: 1 addition & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fastapi==0.110.*
fastapi==0.111.*
python-multipart==0.0.9
httpx==0.26.0
pydantic_settings==2.2.1
Expand Down
2 changes: 1 addition & 1 deletion backend/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
alembic upgrade head

# then run the app
exec hypercorn -b 0.0.0.0:8000 --workers 4 --access-logfile - --error-logfile - asgi:app
exec hypercorn -b 0.0.0.0:8000 --workers 4 --access-logfile - --error-logfile - main:app
Loading