fix imports (w/o backend.) and fix Items route (had wrong content)

This commit is contained in:
Felix Zett 2025-08-14 16:55:00 +02:00
parent e702418221
commit 2d11a362ea
7 changed files with 169 additions and 206 deletions

View file

@ -4,7 +4,7 @@ from uuid import UUID as UUID_t
from datetime import date
import re, ast, operator
from sqlalchemy.orm import Session, joinedload
from backend import models
import models
ALLOWED_NAMES = {"days", "nights"}
ALLOWED_NODES = (

View file

@ -1,7 +1,9 @@
from uuid import UUID
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.database import Base, engine
from backend.routes import items, tags, trips, trip_items
from database import Base, engine
from routes import items, tags, trips, trip_items, dev_seed
# Create tables (for MVP without Alembic)
Base.metadata.create_all(bind=engine)

View file

@ -4,7 +4,7 @@ from sqlalchemy import (
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from .database import Base
from database import Base
class User(Base):

View file

@ -1,37 +1,31 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session, joinedload
from uuid import UUID
from backend.database import get_db
from backend import models
from backend.schemas import TripCreate, TripOut, TripUpdate, TripRegenerationResult
from backend.crud import generate_trip_items
from database import get_db
import models
from schemas import ItemCreate, ItemOut
router = APIRouter(prefix="/trips", tags=["trips"])
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/", response_model=list[TripOut])
def list_trips(db: Session = Depends(get_db)):
trips = (
db.query(models.Trip)
.options(
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
joinedload(models.Trip.marked_tags).joinedload(models.TripTagMarked.tag),
)
@router.get("/", response_model=list[ItemOut])
def list_items(db: Session = Depends(get_db)):
items = (
db.query(models.Item)
.options(joinedload(models.Item.tags).joinedload(models.ItemTag.tag))
.all()
)
# pydantic can handle relationships if orm_mode
return [
TripOut(
id=t.id,
name=t.name,
start_date=t.start_date,
end_date=t.end_date,
selected_tags=[st.tag for st in t.selected_tags],
marked_tags=[mt.tag for mt in t.marked_tags],
)
for t in trips
models.Item(
id=it.id,
user_id=it.user_id,
name=it.name,
tags=it.tags,
) for it in items
]
@router.post("/", response_model=TripOut)
def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
@router.post("/", response_model=ItemOut)
def create_item(payload: ItemCreate, db: Session = Depends(get_db)):
user = db.query(models.User).first()
if not user:
from uuid import uuid4
@ -39,85 +33,31 @@ def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
db.add(user)
db.flush()
trip = models.Trip(user_id=user.id, name=payload.name, start_date=payload.start_date, end_date=payload.end_date)
db.add(trip)
db.flush()
item = models.Item(user_id=user.id, name=payload.name)
# attach selected & marked
if payload.selected_tag_ids:
for tid in payload.selected_tag_ids:
db.add(models.TripTagSelected(trip_id=trip.id, tag_id=tid))
if payload.marked_tag_ids:
for tid in payload.marked_tag_ids:
db.add(models.TripTagMarked(trip_id=trip.id, tag_id=tid))
db.flush()
# generate items per rules
created_ids, _ = generate_trip_items(
db,
trip=trip,
selected_tag_ids=payload.selected_tag_ids,
marked_tag_ids=payload.marked_tag_ids,
)
if payload.tag_ids:
links = []
tags = db.query(models.Tag).filter(models.Tag.id.in_(payload.tag_ids), models.Tag.user_id == user.id).all()
for t in tags:
links.append(models.ItemTag(item=item, tag=t))
item.tags = links
db.add(item)
db.commit()
db.refresh(item)
# reload with relationships
trip = (
db.query(models.Trip)
.options(
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
joinedload(models.Trip.marked_tags).joinedload(models.TripTagMarked.tag),
)
.get(trip.id)
)
return TripOut(
id=trip.id,
name=trip.name,
start_date=trip.start_date,
end_date=trip.end_date,
selected_tags=[st.tag for st in trip.selected_tags],
marked_tags=[mt.tag for mt in trip.marked_tags],
# eager load for response
item = (
db.query(models.Item)
.options(joinedload(models.Item.tags).joinedload(models.ItemTag.tag))
.get(item.id)
)
return item
@router.put("/{trip_id}/reconfigure", response_model=TripRegenerationResult)
def reconfigure_trip(trip_id: UUID, payload: TripUpdate, db: Session = Depends(get_db)):
trip = db.get(models.Trip, trip_id)
if not trip:
raise HTTPException(status_code=404, detail="Trip not found")
# update base fields
if payload.name is not None:
trip.name = payload.name
if payload.start_date is not None:
trip.start_date = payload.start_date
if payload.end_date is not None:
trip.end_date = payload.end_date
db.flush()
# update selected/marked join tables if provided
if payload.selected_tag_ids is not None:
# replace all
db.query(models.TripTagSelected).filter_by(trip_id=trip.id).delete()
for tid in payload.selected_tag_ids:
db.add(models.TripTagSelected(trip_id=trip.id, tag_id=tid))
if payload.marked_tag_ids is not None:
db.query(models.TripTagMarked).filter_by(trip_id=trip.id).delete()
for tid in payload.marked_tag_ids:
db.add(models.TripTagMarked(trip_id=trip.id, tag_id=tid))
db.flush()
# read back lists
sel_ids = [row.tag_id for row in db.query(models.TripTagSelected).filter_by(trip_id=trip.id).all()]
mrk_ids = [row.tag_id for row in db.query(models.TripTagMarked).filter_by(trip_id=trip.id).all()]
created_ids, deleted_checked = generate_trip_items(
db, trip=trip, selected_tag_ids=sel_ids, marked_tag_ids=mrk_ids
)
@router.delete("/{item_id}", status_code=204)
def delete_item(item_id: UUID, db: Session = Depends(get_db)):
item = db.get(models.Item, item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
db.delete(item)
db.commit()
return {
"trip_id": trip.id,
"deleted_checked_trip_item_ids": deleted_checked,
"created_trip_item_ids": created_ids,
}

View file

@ -1,9 +1,9 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from uuid import UUID
from backend.database import get_db
from backend import models
from backend.schemas import TagCreate, TagOut
from database import get_db
import models
from schemas import TagCreate, TagOut
router = APIRouter(prefix="/tags", tags=["tags"])

View file

@ -1,9 +1,9 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session, joinedload
from uuid import UUID
from backend.database import get_db
from backend import models
from backend.schemas import TripItemOut
from database import get_db
import models
from schemas import TripItemOut
router = APIRouter(prefix="/trip-items", tags=["trip-items"])

View file

@ -1,102 +1,123 @@
import re
from uuid import UUID, uuid4
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from schemas import TripCreate, TripItemOut, TripOut
from models import Item, Trip, TripItem
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session, joinedload
from uuid import UUID
from database import get_db
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from config import FIXED_USER_ID
import models
from schemas import TripCreate, TripOut, TripUpdate, TripRegenerationResult
from crud import generate_trip_items
router = APIRouter(prefix="/trips", tags=["Trips"])
@router.post("/", response_model=TripOut)
async def create_trip(trip: TripCreate, db: AsyncSession = Depends(get_db)):
user_id = FIXED_USER_ID
db_trip = Trip(
id=uuid4(),
name=trip.name,
user_id=user_id,
start_date=trip.start_date,
end_date=trip.end_date,
selected_tags=trip.selected_tags,
marked_tags=trip.marked_tags
)
await db.commit() # damit ID vorhanden ist
db.add(db_trip)
# Tage berechnen
days = (trip.end_date - trip.start_date).days + 1
nights = days - 1
# relevante Items
result = await db.execute(select(Item).where(Item.user_id == user_id).options(selectinload(Item.tags)))
all_items = result.scalars().all()
trip_items = []
for item in all_items:
item_tag_names = [tag.name for tag in item.tags]
if not set(item_tag_names).issubset(set(trip.selected_tags)):
continue
item_text = replace_placeholders(item.name, days, nights)
# markierte Tags matchen?
matching_marked = set(item_tag_names) & set(trip.marked_tags)
if matching_marked:
for tag in matching_marked:
trip_items.append(TripItem(
id=uuid4(),
trip_id=db_trip.id,
item_id=item.id,
tag=tag,
name=item.name,
calculated_label=item_text,
checked=False
))
else:
trip_items.append(TripItem(
id=uuid4(),
trip_id=db_trip.id,
item_id=item.id,
tag=None,
name=item.name,
calculated_label=item_text,
checked=False
))
db.add_all(trip_items)
await db.commit()
# return {"status": "trip created", "trip_id": str(db_trip.id)}
return db_trip
@router.get("/{trip_id}/items", response_model=list[TripItemOut])
async def get_trip_items(trip_id: UUID, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(TripItem).where(TripItem.trip_id == trip_id).options(selectinload(TripItem.item)))
items = result.scalars().all()
return [TripItemOut.model_validate(item) for item in items]
router = APIRouter(prefix="/trips", tags=["trips"])
@router.get("/", response_model=list[TripOut])
async def get_trips(db: AsyncSession = Depends(get_db)):
user_id = FIXED_USER_ID
result = await db.execute(select(Trip).where(Trip.user_id == user_id))
trips = result.scalars().all()
return trips
def list_trips(db: Session = Depends(get_db)):
trips = (
db.query(models.Trip)
.options(
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
joinedload(models.Trip.marked_tags).joinedload(models.TripTagMarked.tag),
)
.all()
)
return [
TripOut(
id=t.id,
name=t.name,
start_date=t.start_date,
end_date=t.end_date,
selected_tags=[st.tag for st in t.selected_tags],
marked_tags=[mt.tag for mt in t.marked_tags],
)
for t in trips
]
def replace_placeholders(text: str, days: int, nights: int) -> str:
def replacer(match):
expr = match.group(1)
expr = expr.replace("days", str(days)).replace("nights", str(nights))
try:
return str(eval(expr))
except:
return match.group(0)
@router.post("/", response_model=TripOut)
def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
user = db.query(models.User).first()
if not user:
from uuid import uuid4
user = models.User(id=uuid4(), name="Demo")
db.add(user)
db.flush()
return re.sub(r"{(.*?)}", replacer, text)
trip = models.Trip(user_id=user.id, name=payload.name, start_date=payload.start_date, end_date=payload.end_date)
db.add(trip)
db.flush()
# attach selected & marked
if payload.selected_tag_ids:
for tid in payload.selected_tag_ids:
db.add(models.TripTagSelected(trip_id=trip.id, tag_id=tid))
if payload.marked_tag_ids:
for tid in payload.marked_tag_ids:
db.add(models.TripTagMarked(trip_id=trip.id, tag_id=tid))
db.flush()
# generate items per rules
created_ids, _ = generate_trip_items(
db,
trip=trip,
selected_tag_ids=payload.selected_tag_ids,
marked_tag_ids=payload.marked_tag_ids,
)
db.commit()
# reload with relationships
trip = (
db.query(models.Trip)
.options(
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
joinedload(models.Trip.marked_tags).joinedload(models.TripTagMarked.tag),
)
.get(trip.id)
)
return TripOut(
id=trip.id,
name=trip.name,
start_date=trip.start_date,
end_date=trip.end_date,
selected_tags=[st.tag for st in trip.selected_tags],
marked_tags=[mt.tag for mt in trip.marked_tags],
)
@router.put("/{trip_id}/reconfigure", response_model=TripRegenerationResult)
def reconfigure_trip(trip_id: UUID, payload: TripUpdate, db: Session = Depends(get_db)):
trip = db.get(models.Trip, trip_id)
if not trip:
raise HTTPException(status_code=404, detail="Trip not found")
# update base fields
if payload.name is not None:
trip.name = payload.name
if payload.start_date is not None:
trip.start_date = payload.start_date
if payload.end_date is not None:
trip.end_date = payload.end_date
db.flush()
# update selected/marked join tables if provided
if payload.selected_tag_ids is not None:
# replace all
db.query(models.TripTagSelected).filter_by(trip_id=trip.id).delete()
for tid in payload.selected_tag_ids:
db.add(models.TripTagSelected(trip_id=trip.id, tag_id=tid))
if payload.marked_tag_ids is not None:
db.query(models.TripTagMarked).filter_by(trip_id=trip.id).delete()
for tid in payload.marked_tag_ids:
db.add(models.TripTagMarked(trip_id=trip.id, tag_id=tid))
db.flush()
# read back lists
sel_ids = [row.tag_id for row in db.query(models.TripTagSelected).filter_by(trip_id=trip.id).all()]
mrk_ids = [row.tag_id for row in db.query(models.TripTagMarked).filter_by(trip_id=trip.id).all()]
created_ids, deleted_checked = generate_trip_items(
db, trip=trip, selected_tag_ids=sel_ids, marked_tag_ids=mrk_ids
)
db.commit()
return {
"trip_id": trip.id,
"deleted_checked_trip_item_ids": deleted_checked,
"created_trip_item_ids": created_ids,
}