Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

11 changed files with 24 additions and 115 deletions

View file

@ -1,6 +1,6 @@
.*
/.dockerignore
/.gitignore
/inventory.sqlite3
/README.md
*.pyc
Dockerfile
/contrib/

View file

@ -1,4 +1,4 @@
FROM python:3.12-alpine3.19
FROM python:3.10-alpine
WORKDIR /app

View file

@ -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']}")

22
crud.py
View file

@ -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):

View file

@ -5,7 +5,9 @@ from sqlalchemy.orm import sessionmaker
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)

33
main.py
View file

@ -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}
@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")

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -43,9 +43,6 @@
<label for="hidden">Hide by default</label>
<input type="checkbox" id="hidden">
<label for="last_updated">Last updated</label>
<input id="last_updated" type="text" disabled>
</form>
<button id="delete" class="btn red" autocomplete="off">Delete</button>

View file

@ -39,7 +39,6 @@ function fillForm() {
document.getElementById('content').value = item.content;
document.getElementById('note').value = item.note;
document.getElementById('hidden').checked = item.hidden;
document.getElementById('last_updated').value = formatTimestamp(item.last_updated);
formCoordsToMap();
}
@ -173,9 +172,3 @@ function formCoordsToMap() {
coordsToMap(coords_bl, coords_tr);
}
}
function formatTimestamp(ts) {
const date = new Date(ts * 1000);
// using Swedish format as a hack to get an iso formatted date
return date.toLocaleDateString("sv", {timeZone: "UTC"}).replace(/\-\d+$/,'')
}