support-ticker/app/main.py

137 lines
3.1 KiB
Python
Raw Normal View History

2023-08-30 12:24:25 +02:00
import tomllib
from datetime import datetime
from pathlib import Path
import json
import enum
from pydantic import BaseModel, SecretStr, constr
import secrets
from fastapi import FastAPI, HTTPException, Security
from fastapi.openapi.models import APIKey
2023-08-30 12:24:25 +02:00
from fastapi.security.api_key import APIKeyHeader
from starlette.status import HTTP_403_FORBIDDEN
import sys
class Settings(BaseModel):
api_key: SecretStr
path_support_reports: Path
with open(Path(__file__).parent / 'settings.toml', 'r') as f:
settings_dict = tomllib.loads(f.read())
SETTINGS = Settings(**settings_dict)
####################
# - Utilities
####################
2023-08-30 12:24:25 +02:00
api_key_header = APIKeyHeader(
name="access_token",
auto_error=False,
)
async def g_api_key(
api_key_header: APIKey = Security(api_key_header),
) -> APIKey:
if api_key_header and secrets.compare_digest(
str(api_key_header),
2023-08-30 12:24:25 +02:00
SETTINGS.api_key.get_secret_value(),
):
return api_key_header
raise HTTPException(
status_code=HTTP_403_FORBIDDEN,
detail="Wrong API key.",
)
####################
# - Types
####################
2023-08-30 12:24:25 +02:00
class PyInstSupTag(enum.StrEnum):
INST_WIN_OFFICIAL = enum.auto()
INST_WIN_WINSTORE = enum.auto()
INST_MAC_OFFICIAL = enum.auto()
INST_MAC_HOMEBREW = enum.auto()
INST_LIN_OFFICIAL = enum.auto()
INST_LIN_PKG = enum.auto()
COURSE_MATH1A = enum.auto()
COURSE_INTROPROG = enum.auto()
ENV_PIP = enum.auto()
ENV_CONDA = enum.auto()
IDE_VSCODE = enum.auto()
IDE_PYCHARM = enum.auto()
IDE_SPYDER = enum.auto()
IDE_NOTEPADPP = enum.auto()
class ClockOption(enum.IntEnum):
CLOCK_IN = enum.auto()
CLOCK_OUT = enum.auto()
class AttendanceEntry(BaseModel):
at: datetime
status: ClockOption
class SupportEntry(BaseModel):
study_id: constr(pattern = r'^s[0-9]{6}$')
tags: list[PyInstSupTag]
description: str
####################
# - FastAPI App
####################
app = FastAPI(
2023-08-30 12:24:25 +02:00
#prefix="/v1",
dependencies=[Security(g_api_key)],
)
2023-08-30 12:24:25 +02:00
####################
# - Support
####################
@app.get("/report/support")
async def g_support_entry() -> list[SupportEntry]:
"""Report an instance of granted Python Installation support."""
with open(SETTINGS.path_support_reports, 'r') as f:
return [
SupportEntry(support_entry_snippet)
for support_entry_snippet in f.readlines()
]
@app.post("/report/support")
2023-08-30 12:24:25 +02:00
async def mk_support_entry(
support_entry: SupportEntry,
) -> bool:
2023-08-30 12:24:25 +02:00
"""Print granted Python Support."""
with open(SETTINGS.path_support_reports, 'a') as f:
print(
json.dumps(support_entry.model_dump()),
file = f,
)
return True
####################
# - Hours
####################
@app.get("/report/hours")
async def g_hours_entry() -> list[SupportEntry]:
"""Print support attendance record."""
with open(SETTINGS.path_support_reports, 'r') as f:
return [
SupportEntry(support_entry_snippet)
for support_entry_snippet in f.readlines()
]
@app.post("/report/hours")
async def mk_hours_entry(
attendance_entry: AttendanceEntry,
) -> bool:
"""Clock in / out to Python support."""
with open(SETTINGS.path_support_reports, 'a') as f:
print(
json.dumps(attendance_entry.model_dump()),
file = f,
)
return True