diff --git a/app.py b/app.py index 7a9ceae..3fa5069 100644 --- a/app.py +++ b/app.py @@ -7,11 +7,18 @@ from fastapi.responses import FileResponse, HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from logfmter import Logfmter from pydantic_settings import BaseSettings from beetsstatistics import AlbumSort, BeetsStatistics, DBNotFoundError, DBQueryError -logger = logging.getLogger("uvicorn.error") +log_format = "%(asctime)s [%(levelname)-7s] [%(name)-12s] %(name)s - %(message)s" +date_format = "%H:%M:%S" +console_handler = logging.StreamHandler() +console_handler.setFormatter(Logfmter(keys=["at", "name", "asctime"])) +logging.basicConfig(level=logging.INFO, handlers=[console_handler]) + +logger = logging.getLogger("beets-statistics") class InitializationError(Exception): @@ -66,7 +73,13 @@ async def get_general_stats( format_count, lossless, lossy, unknown = beets_statistics.get_track_formats() recently_added_albums = beets_statistics.get_recently_added_albums() - except (DBQueryError, DBNotFoundError) as e: + except DBNotFoundError as e: + logger.error("Could not find database", exc_info=e) + raise HTTPException( + status_code=500, detail="Could not find database: {}".format(e) + ) + except DBQueryError as e: + logger.error("Could not query general statistics", exc_info=e) raise HTTPException( status_code=500, detail="Could not query general statistics: {}".format(e) ) diff --git a/beetsstatistics.py b/beetsstatistics.py index a50b38c..82e325d 100644 --- a/beetsstatistics.py +++ b/beetsstatistics.py @@ -63,6 +63,22 @@ def close(self): if self.connection: self.connection.close() + def convert_row_to_album(self, album): + return_album = Album() + return_album.id = album["album_id"] + return_album.mb_albumid = album["mb_albumid"] + return_album.title = album["album"] + return_album.tracks = album["tracks"] + return_album.track_total = album["tracktotal"] + return_album.complete_percentage = album["complete"] + return_album.album_artist = album["albumartist"] + return_album.genre = album["genre"] + return_album.year = album["year"] + return_album.original_year = album["original_year"] + return_album.barcode = album["barcode"] + # id |artpath|added |albumartist |albumartist_sort|albumartist_credit|albumartists|albumartists_sort |albumartists_credit|album |genre |style|discogs_albumid|discogs_artistid|discogs_labelid|year|month|day|disctotal|comp|mb_albumid|mb_albumartistid|albumtype|albumtypes|label |barcode |mb_releasegroupid |release_group_title |asin|catalognum|script|language|country|albumstatus|albumdisambig|releasegroupdisambig|rg_album_gain|rg_album_peak|r128_album_gain|original_year|original_month|original_day| + return return_album + def get_albums_from_db(self, sort_by: AlbumSort = AlbumSort.ARTIST): connection = self.get_db_connection() cursor = connection.cursor() @@ -92,19 +108,7 @@ def get_albums_from_db(self, sort_by: AlbumSort = AlbumSort.ARTIST): result = [] for album in albums: - return_album = Album() - return_album.id = album["album_id"] - return_album.mb_albumid = album["mb_albumid"] - return_album.title = album["album"] - return_album.tracks = album["tracks"] - return_album.track_total = album["tracktotal"] - return_album.complete_percentage = album["complete"] - return_album.album_artist = album["albumartist"] - return_album.genre = album["genre"] - return_album.year = album["year"] - return_album.original_year = album["original_year"] - return_album.barcode = album["barcode"] - # id |artpath|added |albumartist |albumartist_sort|albumartist_credit|albumartists|albumartists_sort |albumartists_credit|album |genre |style|discogs_albumid|discogs_artistid|discogs_labelid|year|month|day|disctotal|comp|mb_albumid|mb_albumartistid|albumtype|albumtypes|label |barcode |mb_releasegroupid |release_group_title |asin|catalognum|script|language|country|albumstatus|albumdisambig|releasegroupdisambig|rg_album_gain|rg_album_peak|r128_album_gain|original_year|original_month|original_day| + return_album = self.convert_row_to_album(album) result.append(return_album) return result @@ -406,21 +410,31 @@ def get_recently_added_albums(self): try: cursor = self.get_db_connection().cursor() query = """select - a.id, - a.album, - a.albumartist, - a.mb_albumid, - a.mb_year, - a.genre, - - - from albums a order by a.added desc + i.album_id, + i.album, + count(i.track) as tracks, + max(i.tracktotal) as tracktotal, + ifnull(round(count(i.track)/ cast(max(i.tracktotal) as float), 2) * 100, 0) as complete, + a.* + from + albums a + left join + items i on + i.album_id = a.id + where + album_id is not null + group by + i.album_id + order by a.added desc limit 10;""" cursor.execute(query) results = cursor.fetchall() cursor.close() - return results + + result_albums = [ self.convert_row_to_album(album) for album in results ] + + return result_albums except sqlite3.Error as e: raise DBQueryError from e diff --git a/pyproject.toml b/pyproject.toml index e08b064..a822c30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "humanize", "pydantic", "pydantic-settings", + "logfmter>=0.0.10", ] [tool.pytest.ini_options] diff --git a/uv.lock b/uv.lock index 146c02d..a97705b 100644 --- a/uv.lock +++ b/uv.lock @@ -10,6 +10,7 @@ dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "humanize" }, { name = "jinja2" }, + { name = "logfmter" }, { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -26,6 +27,7 @@ requires-dist = [ { name = "fastapi", extras = ["standard"] }, { name = "humanize" }, { name = "jinja2" }, + { name = "logfmter", specifier = ">=0.0.10" }, { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -264,6 +266,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "logfmter" +version = "0.0.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/32/8cef33e50f2181ea90b77bc504db382c8ae3e9c08c07ec4bea4e631f49b3/logfmter-0.0.10.tar.gz", hash = "sha256:5731d7b7cee6b0c552fd56b5eba6a0a163a32d6998371a77d75703e67aebaf99", size = 11464, upload-time = "2025-07-30T22:17:49.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/60/0d76d2337013937ca04f3861dd9924ff6f4058e02fb33870f581837f636a/logfmter-0.0.10-py3-none-any.whl", hash = "sha256:056280acccc027dd275b013cb3492dcb15fef02ad955b7a839242007685d0351", size = 8410, upload-time = "2025-07-30T22:17:48.027Z" }, +] + [[package]] name = "markdown-it-py" version = "3.0.0"