diff --git a/.dockerignore b/.dockerignore index 4bc1268..0fdd12c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ -.* +/.dockerignore +/.gitignore /inventory.sqlite3 /README.md *.pyc Dockerfile -/contrib/ diff --git a/.forgejo/workflows/deployment-pull-request.yaml b/.forgejo/workflows/deployment-pull-request.yaml deleted file mode 100644 index bef68b9..0000000 --- a/.forgejo/workflows/deployment-pull-request.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: deployment-on-pull-request -on: - pull_request: - pull_request_target: - types: - - opened - - reopened - - synchronize - -jobs: - deployment: - runs-on: docker - container: - image: node:bookworm - steps: - - uses: actions/checkout@v4 - - name: Build inventory - uses: https://codeberg.org/umglurf/kaniko-action@main - with: - cache: false - push: 'false' - context: /workspace/snoopy/in-c4/ diff --git a/.forgejo/workflows/deployment.yaml b/.forgejo/workflows/deployment.yaml deleted file mode 100644 index 391e59c..0000000 --- a/.forgejo/workflows/deployment.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: deployment -on: - push: - branches: - - 'main' - -jobs: - deployment: - runs-on: docker - container: - image: node:bookworm - steps: - - uses: actions/checkout@v4 - - name: Build inventory - uses: https://codeberg.org/umglurf/kaniko-action@main - with: - cache: true - cache_repo: git.koeln.ccc.de/${{ github.repository }}/cache - push: 'true' - context: /workspace/snoopy/in-c4/ - credentials: | - git.koeln.ccc.de=snoopy:${{ secrets.REGISTRY_WRITE }} - destinations: | - git.koeln.ccc.de/${{ github.repository }}/in-c4:latest diff --git a/.gitignore b/.gitignore index 03899e1..53c9ebf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/data/* +/inventory.sqlite3 *.pyc diff --git a/Dockerfile b/Dockerfile index 9cfa1aa..228fbb6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-alpine3.19 +FROM python:3.10-alpine WORKDIR /app diff --git a/contrib/bulk_create.py b/contrib/bulk_create.py deleted file mode 100755 index 3d7fbf5..0000000 --- a/contrib/bulk_create.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 - -import sys - -import requests - -BASE = "https://in.ccc.ac" -COUNT = 0 -START_ID = 1 - - -session = requests.Session() -# session.auth = (":)", ":)") - -res = session.get(f"{BASE}/api/items") -res.raise_for_status() -ids = res.json().keys() -kiste_ids = {int(i.lstrip("K")) for i in ids if i.startswith("K") and i != "K"} -print("existing ids:", repr(kiste_ids), file=sys.stderr) - -current_id = START_ID -new_ids = [] -while len(new_ids) < COUNT: - if current_id not in kiste_ids: - new_ids.append(current_id) - current_id += 1 - -print("created:", file=sys.stderr) -if len(new_ids) == 0: - print("(none)", file=sys.stderr) - - -for i in new_ids: - j = { - "id": f"K{i}", - "is_in": None, - "coords_bl": None, - "coords_tr": None, - "type": "Kiste", - "name": None, - "content": None, - "note": "Bitte Name, Ort, Inhalt eintragen", - "hidden": False, - } - res = session.put(f"{BASE}/api/items/{j['id']}", json=j) - res.raise_for_status() - print(f"{j['id']},{BASE}/form.html?id={j['id']}") diff --git a/crud.py b/crud.py index 277c881..f5e1f39 100644 --- a/crud.py +++ b/crud.py @@ -1,32 +1,18 @@ -from enum import Enum - -from sqlalchemy import DECIMAL, cast from sqlalchemy.orm import Session +from sqlalchemy import DECIMAL, cast -import models -import schemas - +import models, schemas def get_items(db: Session) -> list[models.Item]: return db.query(models.Item).all() def put_item(db: Session, id: str, item: schemas.Item): - updated = bool( - db.query(models.Item).filter(models.Item.id == id).update(item.dict()) - ) + updated = bool(db.query(models.Item).filter(models.Item.id == id).update(item.dict())) if not updated: db.add(models.Item(**item.dict())) db.commit() - if updated: - return PutItemResult.UPDATED - else: - return PutItemResult.ADDED - - -class PutItemResult(Enum): - ADDED = 1 - UPDATED = 2 + return updated def delete_item(db: Session, id: str): diff --git a/database.py b/database.py index 7759e10..ac9f8a6 100644 --- a/database.py +++ b/database.py @@ -2,10 +2,12 @@ from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -SQLALCHEMY_DATABASE_URL = "sqlite:///./data/inventory.sqlite3" +SQLALCHEMY_DATABASE_URL = "sqlite:///./inventory.sqlite3" engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}, echo=True + SQLALCHEMY_DATABASE_URL, + connect_args = {"check_same_thread": False}, + echo=True ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/main.py b/main.py index eb66394..ecaeea6 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,9 @@ -import re -from calendar import timegm -from datetime import date - from fastapi import Depends, FastAPI, Response from fastapi.staticfiles import StaticFiles from sqlalchemy import event -from sqlalchemy.engine import Engine from sqlalchemy.orm import Session +from sqlalchemy.engine import Engine +import re import crud from database import SessionLocal, engine @@ -17,7 +14,6 @@ Base.metadata.create_all(bind=engine) app = FastAPI() - def get_db(): db = SessionLocal() try: @@ -25,7 +21,6 @@ def get_db(): finally: db.close() - @event.listens_for(Engine, "connect") def _set_sqlite_pragma(conn, _): cursor = conn.cursor() @@ -36,35 +31,21 @@ def _set_sqlite_pragma(conn, _): @app.get("/api/items", response_model=dict[str, Item]) async def list_items(db: Session = Depends(get_db)): # natural sort by id - natsort = lambda item: [ - int(t) if t.isdigit() else t.lower() for t in re.split(r"(\d+)", item.id) - ] + natsort = lambda item: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', item.id)] items = crud.get_items(db) items = sorted(items, key=natsort) - return {i.id: i for i in items} + return {i.id:i for i in items} -@app.put( - "/api/items/{id}", - status_code=201, - responses={201: {"description": "created"}, 204: {"description": "updated"}}, -) +@app.put("/api/items/{id}") async def put_item(id: str, item: Item, db: Session = Depends(get_db)): - if item.last_updated is None: - item.last_updated = month_timestamp() - if crud.put_item(db, id, item) == crud.PutItemResult.UPDATED: - return Response(b"", status_code=204) - return Response(b"", status_code=201) + if crud.put_item(db, id, item): + return Response(b'', status_code=204) + return Response(b'', status_code=201) @app.delete("/api/items/{id}", status_code=204) async def delete_item(id: str, db: Session = Depends(get_db)): crud.delete_item(db, id) - -def month_timestamp() -> int: - """Provides the timestamp of the current month's beginning (for improved privacy)""" - return timegm(date.today().replace(day=1).timetuple()) - - app.mount("/", StaticFiles(directory="static", html=True), name="static") diff --git a/models.py b/models.py index 1df2c2b..d7618a6 100644 --- a/models.py +++ b/models.py @@ -1,8 +1,7 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text +from sqlalchemy import Column, ForeignKey, String, Text, Boolean from database import Base - class Item(Base): __tablename__ = "items" @@ -15,4 +14,3 @@ class Item(Base): content = Column(Text) note = Column(Text) hidden = Column(Boolean, nullable=False) - last_updated = Column(Integer) diff --git a/requirements.txt b/requirements.txt index 92fc06b..54b2b3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -fastapi>=0.109.0 -sqlalchemy>=2.0.25 -uvicorn>=0.27.0.post1 +fastapi>=0.79.0 +sqlalchemy>=1.4.39 +uvicorn>=0.17.6 diff --git a/schemas.py b/schemas.py index 30f7545..218fe94 100644 --- a/schemas.py +++ b/schemas.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Field +from pydantic import BaseModel class Item(BaseModel): @@ -11,7 +11,6 @@ class Item(BaseModel): content: str | None note: str | None hidden: bool - last_updated: int | None = Field(None) class Config: - from_attributes = True + orm_mode = True diff --git a/static/80x80.svg b/static/80x80.svg new file mode 100644 index 0000000..d0124e9 --- /dev/null +++ b/static/80x80.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/c4.css b/static/c4.css deleted file mode 100644 index abeae82..0000000 --- a/static/c4.css +++ /dev/null @@ -1,122 +0,0 @@ -body { - color: #f0f0f0; - background-color: #333; -} - -h1, h1 a { - color: #dfaa37; /* C4 Yellow */ -} - -#search.failed { - color: #200; - background-color: #ec6d5f; -} - -#map { - border: 1px solid #202020; - background-color: #4d4d4d; - max-width: 1440px; - max-height: 50vh; - aspect-ratio: 2 / 1; - box-shadow: 0px 6px 12px #000c; -} - -#mapnote { - background-color: #ccc; - color: #333; - border-color: #202020; - padding: 0.25em 0.5em; - position: relative; - top: -1px; - box-shadow: 0px 6px 12px #000c; -} - -#mapnote:empty { - display: none; -} - -#itemcount { - display: inline-block; - margin-right: 1ch; -} - -.result { - background-color: #202020; - border-color: #4d4d4d; -} - -.result:focus { - background-color: #202020; - border-color: #dfaa37; - outline-color: #dfaa37; -} - -.result a { - color: #dfaa37; -} - -.note { - background-color: #333; - font-size: smaller; -} - -#knowntypes button { - color: #220; - background-color: #dfaa37; - border: 1px solid #220; - border-radius: 4px; - padding: 0.2em 0.4em; - margin: 0.2em; - font-weight: bolder; - cursor: pointer; -} - -#knowntypes button:hover, #knowntypes button:focus { - background-color: #f5c251; -} - -.btn { - box-shadow: 0 3px 6px -1px rgba(0,0,0,0.3); -} - -.btn.primary { - color: #220; - background-color: #dfaa37; -} - -.btn.primary:hover,.btn.primary:focus { - background-color: #f5c251; -} - -.btn.grn { - color: #020; - background-color: #9f6; -} - -.btn.grn:hover ,.btn.grn:focus { - background-color: #3f0; -} - -.btn.red { - color: #200; - background-color: #ec6d5f; -} - -.btn.red:hover, .btn.red:focus { - background-color: #ff7667; -} - -@keyframes blinker { - 0% { opacity: 1; } - 40% { opacity: 1; } - 90% { opacity: 0; } - 100% { opacity: 1; } -} - -#mapgrid { - outline: 2px solid #333; - background-color: #dfaa37; - border-radius: min(4px, 25%); - animation: blinker 1s infinite; -} - diff --git a/static/form.html b/static/form.html index 44d5c16..a5de3f2 100644 --- a/static/form.html +++ b/static/form.html @@ -3,7 +3,6 @@