2025-12-26 18:53:14 +01:00
|
|
|
import logging
|
2025-12-20 04:31:02 +01:00
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
from sqlmodel import (
|
2026-02-07 02:48:27 +01:00
|
|
|
Field,
|
2026-02-22 16:41:42 +01:00
|
|
|
Sequence,
|
2026-02-14 18:48:46 +01:00
|
|
|
Session,
|
2026-02-22 16:41:42 +01:00
|
|
|
SQLModel,
|
2026-02-14 18:48:46 +01:00
|
|
|
select,
|
2025-12-20 04:31:02 +01:00
|
|
|
)
|
|
|
|
|
|
2026-02-22 16:41:42 +01:00
|
|
|
|
2025-12-26 18:53:14 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
2025-12-20 04:31:02 +01:00
|
|
|
|
|
|
|
|
|
2026-01-08 12:06:12 +01:00
|
|
|
class TeilchenCreate(SQLModel):
|
2025-12-20 04:31:02 +01:00
|
|
|
description: str | None
|
2025-12-26 18:53:14 +01:00
|
|
|
name: str = Field(index=True)
|
2025-12-20 04:31:02 +01:00
|
|
|
number: int = Field(default=1)
|
2026-01-08 12:06:12 +01:00
|
|
|
text: str
|
2025-12-26 18:53:14 +01:00
|
|
|
tags: str | None
|
2025-12-20 04:31:02 +01:00
|
|
|
|
|
|
|
|
|
2025-12-26 18:53:14 +01:00
|
|
|
class Teilchen(TeilchenCreate, table=True):
|
2026-02-20 21:28:11 +01:00
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid7, primary_key=True)
|
2025-12-26 18:53:14 +01:00
|
|
|
|
2025-12-20 04:31:02 +01:00
|
|
|
|
2026-02-07 02:47:36 +01:00
|
|
|
async def make_teilchen_input(text: str) -> TeilchenCreate | None:
|
2026-02-14 18:49:24 +01:00
|
|
|
"""Constructor to create new Teilchen from user input.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
text: The whole input string as provided by the user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Returns new Teilchen instance if enough parts could be extracted.
|
|
|
|
|
Returns `None` otherwise.
|
|
|
|
|
"""
|
2026-02-07 02:46:01 +01:00
|
|
|
import re
|
2026-02-22 16:41:42 +01:00
|
|
|
|
2026-02-07 02:46:01 +01:00
|
|
|
from natsort import natsorted
|
|
|
|
|
|
2026-02-19 20:42:50 +01:00
|
|
|
text = text.strip()
|
|
|
|
|
|
2025-12-26 18:53:14 +01:00
|
|
|
if not text:
|
|
|
|
|
logger.error("Empty text.")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
name_end = text.find(".")
|
2026-02-19 20:42:50 +01:00
|
|
|
if name_end == -1: # "." not in text
|
|
|
|
|
logger.warning("Missing period '.' from text.")
|
|
|
|
|
logger.debug("Could not find period symbol in input: %s", text)
|
|
|
|
|
return None
|
|
|
|
|
else:
|
|
|
|
|
name = text[0:name_end]
|
|
|
|
|
|
2025-12-26 18:53:14 +01:00
|
|
|
if not name:
|
2026-02-19 20:44:18 +01:00
|
|
|
logger.warning("Could not extract name.")
|
2026-02-14 18:49:05 +01:00
|
|
|
logger.debug("Could not extract name from input: %s", text)
|
2025-12-26 18:53:14 +01:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
description_start = text.find('"', name_end + 1)
|
|
|
|
|
description_end = text.find('"', description_start + 1)
|
2026-02-07 02:46:44 +01:00
|
|
|
if description_end > description_start:
|
2026-02-20 21:27:34 +01:00
|
|
|
description = text[description_start + 1 : description_end]
|
2026-02-07 02:46:44 +01:00
|
|
|
else:
|
|
|
|
|
description = ""
|
2025-12-26 18:53:14 +01:00
|
|
|
|
|
|
|
|
tags = re.findall(r"#\w+", text.lower())
|
|
|
|
|
if not tags:
|
2026-02-14 18:49:05 +01:00
|
|
|
logger.info("Could not extract tags.")
|
|
|
|
|
logger.debug("Could not extract tags from input: %s", text)
|
2025-12-26 18:53:14 +01:00
|
|
|
|
2026-02-14 18:49:05 +01:00
|
|
|
teilchen = TeilchenCreate(
|
2025-12-26 18:53:14 +01:00
|
|
|
name=name,
|
|
|
|
|
description=description,
|
|
|
|
|
tags=" ".join(natsorted(tags)),
|
|
|
|
|
text=text,
|
|
|
|
|
)
|
2026-02-14 18:49:05 +01:00
|
|
|
logger.debug("Created new Teilchen: %r", teilchen)
|
|
|
|
|
|
|
|
|
|
return teilchen
|
2026-02-07 02:52:13 +01:00
|
|
|
|
|
|
|
|
|
2026-02-22 18:08:39 +01:00
|
|
|
async def load_initial_data(engine) -> Sequence[Teilchen]:
|
2026-02-22 20:41:40 +01:00
|
|
|
"""Retrieve all Teilchen records from the database.
|
2026-02-14 18:48:46 +01:00
|
|
|
|
2026-02-22 20:41:40 +01:00
|
|
|
Args:
|
|
|
|
|
engine (sqlalchemy.Engine): the engine or connection or whatever
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
List of Teilchen, potentially empty
|
|
|
|
|
"""
|
2026-02-14 18:48:46 +01:00
|
|
|
with Session(engine) as session:
|
|
|
|
|
statement = select(
|
|
|
|
|
Teilchen.id, Teilchen.name, Teilchen.description, Teilchen.number, Teilchen.tags
|
2026-02-16 21:11:58 +01:00
|
|
|
) # ty:ignore[no-matching-overload]
|
2026-02-14 18:48:46 +01:00
|
|
|
all_teilchen = session.exec(statement).all()
|
2026-02-22 20:42:42 +01:00
|
|
|
|
|
|
|
|
logger.debug("Loading initial data: found %s records", len(all_teilchen))
|
2026-02-14 18:48:46 +01:00
|
|
|
return all_teilchen
|
2026-02-22 16:41:42 +01:00
|
|
|
|
|
|
|
|
|
2026-02-22 18:08:39 +01:00
|
|
|
async def add_to_database(tc: TeilchenCreate, engine) -> Teilchen:
|
2026-02-22 20:41:40 +01:00
|
|
|
"""Add given data as a new record into the database.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
engine (sqlalchemy.Engine): the engine or connection or whatever
|
|
|
|
|
tc: Teilchen data (no `id` yet)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The newly created Teilchen (this time with `id`)
|
|
|
|
|
"""
|
2026-02-22 20:42:42 +01:00
|
|
|
logger.debug("received: %s", str(tc))
|
2026-02-22 16:41:42 +01:00
|
|
|
with Session(engine) as session:
|
|
|
|
|
teilchen = Teilchen.model_validate(tc)
|
|
|
|
|
session.add(teilchen)
|
|
|
|
|
session.commit()
|
|
|
|
|
session.refresh(teilchen)
|
|
|
|
|
|
2026-02-22 20:42:42 +01:00
|
|
|
logger.debug("created: %s", str(teilchen))
|
2026-02-22 16:41:42 +01:00
|
|
|
return teilchen
|