11import logging
22import os
33from contextlib import asynccontextmanager
4- from logging .handlers import RotatingFileHandler
54from pathlib import Path
65
76import asyncpg
87import orjson
9-
10- # from apscheduler import AsyncScheduler
11- # from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
12- # from apscheduler.eventbrokers.redis import RedisEventBroker
8+ import structlog
139from fastapi import Depends , FastAPI , Request
1410from fastapi .responses import HTMLResponse
1511from fastapi .templating import Jinja2Templates
16- from whenever ._whenever import Instant
1712
1813from app .api .health import router as health_router
1914from app .api .ml import router as ml_router
2217from app .api .stuff import router as stuff_router
2318from app .api .user import router as user_router
2419from app .config import settings as global_settings
25-
26- # from app.database import engine
2720from app .redis import get_redis
2821from app .services .auth import AuthBearer
22+ from whenever ._whenever import Instant
23+ from app .utils .logging import setup_structlog
2924
30- # from app.services.scheduler import SchedulerMiddleware
31- import structlog
32-
33- log_date = Instant .now ().py_datetime ().strftime ("%Y%m%d" )
34-
35- structlog .configure (
36- cache_logger_on_first_use = True ,
37- wrapper_class = structlog .make_filtering_bound_logger (logging .INFO ),
38- processors = [
39- structlog .contextvars .merge_contextvars ,
40- structlog .processors .add_log_level ,
41- structlog .processors .format_exc_info ,
42- structlog .processors .TimeStamper (fmt = "iso" , utc = True ),
43- structlog .processors .JSONRenderer (serializer = orjson .dumps ),
44- ],
45- # log per day and per process?
46- logger_factory = structlog .BytesLoggerFactory (
47- file = Path (f"cuul_{ log_date } _{ str (os .getpid ())} " ).with_suffix (".log" ).open ("wb" )
48- )
49- )
50-
51- logger = structlog .get_logger ()
5225
26+ logger = setup_structlog ()
5327templates = Jinja2Templates (directory = Path (__file__ ).parent .parent / "templates" )
5428
55-
5629@asynccontextmanager
57- async def lifespan (_app : FastAPI ):
58- # Load the redis connection
59- _app .redis = await get_redis ()
60-
61- _postgres_dsn = global_settings .postgres_url .unicode_string ()
62-
30+ async def lifespan (app : FastAPI ):
31+ app .redis = await get_redis ()
32+ postgres_dsn = global_settings .postgres_url .unicode_string ()
6333 try :
64- # TODO: cache with the redis connection
65- # Initialize the postgres connection pool
66- _app .postgres_pool = await asyncpg .create_pool (
67- dsn = _postgres_dsn ,
34+ app .postgres_pool = await asyncpg .create_pool (
35+ dsn = postgres_dsn ,
6836 min_size = 5 ,
6937 max_size = 20 ,
7038 )
71- logger .info ("Postgres pool created" , _app .postgres_pool .get_idle_size ())
39+ logger .info ("Postgres pool created" , idle_size = app .postgres_pool .get_idle_size ())
7240 yield
7341 finally :
74- # close redis connection and release the resources
75- await _app .redis .close ()
76- # close postgres connection pool and release the resources
77- await _app .postgres_pool .close ()
78-
79-
80- app = FastAPI (title = "Stuff And Nonsense API" , version = "0.19.0" , lifespan = lifespan )
81-
82- app .include_router (stuff_router )
83- app .include_router (nonsense_router )
84- app .include_router (shakespeare_router )
85- app .include_router (user_router )
86- app .include_router (ml_router , prefix = "/v1/ml" , tags = ["ML" ])
87-
88-
89- app .include_router (health_router , prefix = "/v1/public/health" , tags = ["Health, Public" ])
90- app .include_router (
91- health_router ,
92- prefix = "/v1/health" ,
93- tags = ["Health, Bearer" ],
94- dependencies = [Depends (AuthBearer ())],
95- )
42+ await app .redis .close ()
43+ await app .postgres_pool .close ()
44+
45+ def create_app () -> FastAPI :
46+ app = FastAPI (
47+ title = "Stuff And Nonsense API" ,
48+ version = "0.19.0" ,
49+ lifespan = lifespan ,
50+ )
51+ app .include_router (stuff_router )
52+ app .include_router (nonsense_router )
53+ app .include_router (shakespeare_router )
54+ app .include_router (user_router )
55+ app .include_router (ml_router , prefix = "/v1/ml" , tags = ["ML" ])
56+ app .include_router (health_router , prefix = "/v1/public/health" , tags = ["Health, Public" ])
57+ app .include_router (
58+ health_router ,
59+ prefix = "/v1/health" ,
60+ tags = ["Health, Bearer" ],
61+ dependencies = [Depends (AuthBearer ())],
62+ )
9663
64+ @app .get ("/index" , response_class = HTMLResponse )
65+ def get_index (request : Request ):
66+ return templates .TemplateResponse ("index.html" , {"request" : request })
9767
98- @app .get ("/index" , response_class = HTMLResponse )
99- def get_index (request : Request ):
100- return templates .TemplateResponse ("index.html" , {"request" : request })
68+ return app
10169
70+ app = create_app ()
10271
72+ # --- Unused/experimental code and TODOs ---
73+ # from apscheduler import AsyncScheduler
74+ # from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
75+ # from apscheduler.eventbrokers.redis import RedisEventBroker
76+ # from app.database import engine
77+ # from app.services.scheduler import SchedulerMiddleware
10378# _scheduler_data_store = SQLAlchemyDataStore(engine, schema="scheduler")
104- # _scheduler_event_broker = RedisEventBroker(
105- # client_or_url=global_settings.redis_url.unicode_string()
106- # )
79+ # _scheduler_event_broker = RedisEventBroker(client_or_url=global_settings.redis_url.unicode_string())
10780# _scheduler_himself = AsyncScheduler(_scheduler_data_store, _scheduler_event_broker)
108- #
10981# app.add_middleware(SchedulerMiddleware, scheduler=_scheduler_himself)
110-
111-
112- # TODO: every not GET meth should reset cache
113- # TODO: every scheduler task which needs to act on database should have access to connection pool via request - maybe ?
114- # TODO: https://stackoverflow.com/questions/16053364/make-sure-only-one-worker-launches-the-apscheduler-event-in-a-pyramid-web-app-ru
82+ # TODO: every non-GET method should reset cache
83+ # TODO: scheduler tasks needing DB should access connection pool via request
84+ # TODO: https://stackoverflow.com/questions/16053364/make-sure-only-one-worker-launches-the-apscheduler-event-in-a-pyramid-web-app-ru
0 commit comments