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
6 changes: 4 additions & 2 deletions backend/app/glossary/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ def list_glossary_controller(db: Session):
return [GlossaryResponse.model_validate(glossary) for glossary in glossaries]


def list_glossary_records_controller(db: Session, glossary_id: int):
records = GlossaryQuery(db).list_glossary_records(glossary_id)
def list_glossary_records_controller(
db: Session, glossary_id: int, page: int, page_records: int
):
records = GlossaryQuery(db).list_glossary_records(glossary_id, page, page_records)
return [GlossaryRecordSchema.model_validate(record) for record in records]


Expand Down
4 changes: 4 additions & 0 deletions backend/app/glossary/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class Glossary(Base):
upload_time: Mapped[datetime] = mapped_column(default=datetime.now)
created_by: Mapped[int] = mapped_column(ForeignKey("user.id"))

@property
def records_count(self):
return len(self.records)

records: Mapped[list["GlossaryRecord"]] = relationship(
back_populates="glossary",
cascade="all, delete-orphan",
Expand Down
4 changes: 3 additions & 1 deletion backend/app/glossary/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ def get_glossary_records_for_segment(
def list_glossary(self) -> list[Glossary]:
return self.db.query(Glossary).order_by(Glossary.id).all()

def list_glossary_records(self, glossary_id: int):
def list_glossary_records(self, glossary_id: int, page: int, page_records: int):
return (
self.db.query(GlossaryRecord)
.filter(GlossaryRecord.glossary_id == glossary_id) # type: ignore
.order_by(GlossaryRecord.id)
.offset(page * page_records)
.limit(page_records)
.all()
)

Expand Down
1 change: 1 addition & 0 deletions backend/app/glossary/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class GlossaryResponse(IdentifiedTimestampedModel):
upload_time: datetime.datetime
created_by_user: ShortUser
name: str
records_count: int
model_config = ConfigDict(from_attributes=True)


Expand Down
17 changes: 13 additions & 4 deletions backend/app/routers/glossary.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Annotated
from typing import Annotated, Final

from fastapi import (
APIRouter,
BackgroundTasks,
Depends,
HTTPException,
Query,
UploadFile,
status,
)
Expand Down Expand Up @@ -144,12 +145,20 @@ def delete_glossary(glossary_id: int, db: Session = Depends(get_db)):

@router.get(
"/{glossary_id}/records",
description="Get list glossary record ",
description="Get list glossary record",
response_model=list[GlossaryRecordSchema],
status_code=status.HTTP_200_OK,
)
def list_records(glossary_id: int, db: Session = Depends(get_db)):
return list_glossary_records_controller(db, glossary_id)
def list_records(
glossary_id: int,
db: Session = Depends(get_db),
page: Annotated[int | None, Query(ge=0)] = None,
):
page_records: Final = 100
if not page:
page = 0

return list_glossary_records_controller(db, glossary_id, page, page_records)


@router.post(
Expand Down
66 changes: 66 additions & 0 deletions backend/tests/routers/test_routers_glossary.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,40 @@ def test_get_glossary_retrieve(user_logged_client: TestClient, session: Session)
assert response_json["id"] == glossary_1.id
assert response_json["processing_status"] == glossary_1.processing_status
assert response_json["created_by_user"]["id"] == glossary_1.created_by
assert response_json["records_count"] == 0


def test_get_glossary_retrieve_with_records(
user_logged_client: TestClient, session: Session
):
"""GET /glossary/{glossary_id}/"""

glossary_1 = GlossaryQuery(session).create_glossary(
user_id=1, glossary=GlossarySchema(name="Glossary name")
)
record_scheme = GlossaryRecordCreate(
comment="Comment",
source="Test",
target="Тест",
)
for _ in range(140): # two pages with 100 records per page
GlossaryQuery(session).create_glossary_record(
user_id=1,
record=record_scheme,
glossary_id=glossary_1.id,
)

path = app.url_path_for("retrieve_glossary", **{"glossary_id": glossary_1.id})

response = user_logged_client.get(path)
response_json = response.json()

assert response.status_code == status.HTTP_200_OK

assert response_json["id"] == glossary_1.id
assert response_json["processing_status"] == glossary_1.processing_status
assert response_json["created_by_user"]["id"] == glossary_1.created_by
assert response_json["records_count"] == 140


def test_update_glossary(user_logged_client: TestClient, session: Session):
Expand Down Expand Up @@ -143,6 +177,38 @@ def test_list_glossary_records(user_logged_client: TestClient, session: Session)
assert resp_rec["glossary_id"] == record.glossary_id


def test_list_glossary_records_paged(user_logged_client: TestClient, session: Session):
"""GET /glossary/{glossary_id}/records/"""

glossary = GlossaryQuery(session).create_glossary(
user_id=1, glossary=GlossarySchema(name="Glossary name")
)
record_scheme = GlossaryRecordCreate(
comment="Comment",
source="Test",
target="Тест",
)

for _ in range(140): # two pages with 100 records per page
record = GlossaryQuery(session).create_glossary_record(
user_id=1,
record=record_scheme,
glossary_id=glossary.id,
)

path = app.url_path_for("list_records", **{"glossary_id": glossary.id})

response = user_logged_client.get(path, params={"page": 1})
resp_records = response.json()

assert resp_records[0]["created_by_user"]["id"] == record.created_by
assert resp_records[0]["id"] == 101 # 100th record
assert resp_records[0]["comment"] == record.comment
assert resp_records[0]["source"] == record.source
assert resp_records[0]["target"] == record.target
assert resp_records[0]["glossary_id"] == record.glossary_id


def test_update_glossary_record(user_logged_client: TestClient, session: Session):
"""PUT /glossary/records/{record_id}/"""
expected_user_id = 1
Expand Down
60 changes: 32 additions & 28 deletions frontend/mocks/glossaryMocks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {http, HttpResponse} from 'msw'
import {faker} from '@faker-js/faker'
import {fakerRU} from '@faker-js/faker'

import {
createGlossaryRecord,
deleteGlossary,
Expand All @@ -16,38 +19,31 @@ import {GlossaryRecordUpdate} from '../src/client/schemas/GlossaryRecordUpdate'
import {GlossaryResponse} from '../src/client/schemas/GlossaryResponse'
import {GlossarySchema} from '../src/client/schemas/GlossarySchema'

const glossarySegments: GlossaryRecordSchema[] = new Array(125)
.fill(null)
.map((_, idx) => {
return {
id: idx + 1,
glossary_id: 51,
created_at: faker.date.recent().toISOString().split('.')[0],
updated_at: faker.date.recent().toISOString().split('.')[0],
source: faker.commerce.productName(),
target: fakerRU.commerce.productName(),
comment: fakerRU.commerce.productDescription(),
created_by_user: defaultUser,
}
})

const glossaries: GlossaryResponse[] = [
{
id: 51,
name: 'Some glossary',
created_at: '2024-12-03T12:31:22',
updated_at: '2024-12-03T16:32:22',
created_at: faker.date.recent().toISOString().split('.')[0],
updated_at: faker.date.recent().toISOString().split('.')[0],
processing_status: 'done',
upload_time: '2024-12-03T12:32:05',
created_by_user: defaultUser,
},
]

const glossarySegments: GlossaryRecordSchema[] = [
{
id: 1,
glossary_id: 51,
created_at: '2024-12-03T12:31:22',
updated_at: '2024-12-03T12:31:22',
source: 'Some source',
target: 'Some target',
comment: 'This is a comment',
created_by_user: defaultUser,
},
{
id: 2,
glossary_id: 51,
created_at: '2024-12-03T12:31:22',
updated_at: '2024-12-03T12:31:22',
source: 'Another source',
target: 'Another target',
comment: 'Some comment from another segment',
upload_time: faker.date.recent().toISOString().split('.')[0],
created_by_user: defaultUser,
records_count: glossarySegments.length,
},
]

Expand Down Expand Up @@ -94,8 +90,16 @@ export const glossaryMocks = [
}
}
),
http.get<{id: string}>('http://localhost:8000/glossary/:id/records', () =>
HttpResponse.json<AwaitedReturnType<typeof listRecords>>(glossarySegments)
http.get<{id: string}>(
'http://localhost:8000/glossary/:id/records',
({request}) => {
const searchParams = new URL(request.url).searchParams
const page = Number(searchParams.get('page') ?? '0')

return HttpResponse.json<AwaitedReturnType<typeof listRecords>>(
glossarySegments.slice(page * 100, page * 100 + 100)
)
}
),
http.post<{id: string}, GlossaryRecordCreate>(
'http://localhost:8000/glossary/:id/records',
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"homepage": "https://github.com/ArchiDevil/hat#readme",
"devDependencies": {
"@faker-js/faker": "^10.0.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.21",
Expand Down
9 changes: 9 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/src/client/schemas/GlossaryResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export interface GlossaryResponse {
upload_time: string
created_by_user: ShortUser
name: string
records_count: number
}
4 changes: 2 additions & 2 deletions frontend/src/client/services/GlossaryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const updateGlossary = async (glossary_id: number, content: GlossarySchem
export const deleteGlossary = async (glossary_id: number): Promise<StatusMessage> => {
return await api.delete<StatusMessage>(`/glossary/${glossary_id}`)
}
export const listRecords = async (glossary_id: number): Promise<GlossaryRecordSchema[]> => {
return await api.get<GlossaryRecordSchema[]>(`/glossary/${glossary_id}/records`)
export const listRecords = async (glossary_id: number, page?: number | null): Promise<GlossaryRecordSchema[]> => {
return await api.get<GlossaryRecordSchema[]>(`/glossary/${glossary_id}/records`, {query: {page}})
}
export const createGlossaryRecord = async (glossary_id: number, content: GlossaryRecordCreate): Promise<GlossaryRecordSchema> => {
return await api.post<GlossaryRecordSchema>(`/glossary/${glossary_id}/records`, content)
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/stores/current_glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export const useCurrentGlossaryStore = defineStore('current_glossary', {
actions: {
async loadGlossary(glossaryId: number) {
this.glossary = await retrieveGlossary(glossaryId)
this.records = await listRecords(glossaryId)
this.records = await listRecords(glossaryId, 0)
},
async loadRecords(page: number | undefined) {
if (!this.glossary) throw new Error('No glossary loaded')
this.records = await listRecords(this.glossary?.id, page ?? 0)
},
},
})
Expand Down
Loading