diff options
author | Wolfgang Müller | 2024-11-29 16:02:17 +0100 |
---|---|---|
committer | Wolfgang Müller | 2025-01-19 16:10:05 +0100 |
commit | 261ceaa057742fc70c52885021221d7a89c28af7 (patch) | |
tree | 6256a4968a98320c999f41c7c4eeed5a9ed03802 /src | |
parent | 9dea5cbc01e2d447d77d5fb205acb1d17fe9c55f (diff) | |
download | hircine-261ceaa057742fc70c52885021221d7a89c28af7.tar.gz |
backend: Add basic statistics query endpoint
For now we simply collect totals for all scrapers, models, and comic
associations. These should be sufficient to compile some basic but still
interesting statistics.
Diffstat (limited to 'src')
-rw-r--r-- | src/hircine/api/query/__init__.py | 2 | ||||
-rw-r--r-- | src/hircine/api/query/resolvers.py | 29 | ||||
-rw-r--r-- | src/hircine/api/responses.py | 30 | ||||
-rw-r--r-- | src/hircine/db/ops.py | 8 |
4 files changed, 68 insertions, 1 deletions
diff --git a/src/hircine/api/query/__init__.py b/src/hircine/api/query/__init__.py index 2e07c71..7bc4b7d 100644 --- a/src/hircine/api/query/__init__.py +++ b/src/hircine/api/query/__init__.py @@ -24,6 +24,7 @@ from .resolvers import ( every, scrape_comic, single, + statistics, ) @@ -52,3 +53,4 @@ class Query: world: rp.WorldResponse = query(single(models.World)) worlds: FilterResult[World] = query(every(models.World)) scrape_comic: rp.ScrapeComicResponse = query(scrape_comic) + statistics: rp.Statistics = query(statistics) diff --git a/src/hircine/api/query/resolvers.py b/src/hircine/api/query/resolvers.py index 6609cc1..389a200 100644 --- a/src/hircine/api/query/resolvers.py +++ b/src/hircine/api/query/resolvers.py @@ -10,10 +10,13 @@ import hircine.plugins as plugins from hircine.api.filters import Input as FilterInput from hircine.api.inputs import Pagination from hircine.api.responses import ( + ComicTotals, IDNotFoundError, ScraperError, ScraperNotAvailableError, ScraperNotFoundError, + Statistics, + Totals, ) from hircine.api.sort import Input as SortInput from hircine.api.types import ( @@ -144,3 +147,29 @@ async def scrape_comic(id: int, scraper: str): ) except ScrapeError as e: return ScraperError(error=str(e)) + + +async def statistics(): + async with db.session() as s: + total = Totals( + archives=await ops.count(s, models.Archive), + artists=await ops.count(s, models.Artist), + characters=await ops.count(s, models.Character), + circles=await ops.count(s, models.Circle), + comic=ComicTotals( + artists=await ops.count(s, models.ComicArtist), + characters=await ops.count(s, models.ComicCharacter), + circles=await ops.count(s, models.ComicCircle), + tags=await ops.count(s, models.ComicTag), + worlds=await ops.count(s, models.ComicWorld), + ), + comics=await ops.count(s, models.Comic), + images=await ops.count(s, models.Image), + namespaces=await ops.count(s, models.Namespace), + pages=await ops.count(s, models.Page), + scrapers=len(plugins.get_scrapers()), + tags=await ops.count(s, models.Tag), + worlds=await ops.count(s, models.World), + ) + + return Statistics(total=total) diff --git a/src/hircine/api/responses.py b/src/hircine/api/responses.py index 99d5113..883705b 100644 --- a/src/hircine/api/responses.py +++ b/src/hircine/api/responses.py @@ -147,6 +147,36 @@ class ScraperNotAvailableError(Error): return f"Scraper {self.scraper} not available for comic ID {self.comic_id}" +@strawberry.type +class ComicTotals: + artists: int + characters: int + circles: int + tags: int + worlds: int + + +@strawberry.type +class Totals: + archives: int + artists: int + characters: int + circles: int + comics: int + comic: ComicTotals + images: int + namespaces: int + pages: int + scrapers: int + tags: int + worlds: int + + +@strawberry.type +class Statistics: + total: Totals + + AddComicResponse = Annotated[ Union[ AddComicSuccess, diff --git a/src/hircine/db/ops.py b/src/hircine/db/ops.py index 91c830d..8cc5ddc 100644 --- a/src/hircine/db/ops.py +++ b/src/hircine/db/ops.py @@ -1,7 +1,7 @@ import random from collections import defaultdict -from sqlalchemy import delete, func, null, select, text, tuple_ +from sqlalchemy import delete, func, literal_column, null, select, text, tuple_ from sqlalchemy.orm import contains_eager, undefer from sqlalchemy.orm.util import identity_key from strawberry import UNSET @@ -204,3 +204,9 @@ async def delete_all(session, model, ids): result = await session.execute(delete(model).where(model.id.in_(ids))) return result.rowcount + + +async def count(session, model): + sql = select(func.count(literal_column("1"))).select_from(model) + + return (await session.execute(sql)).scalar_one() |