fix imports (w/o backend.) and fix Items route (had wrong content)
This commit is contained in:
parent
e702418221
commit
2d11a362ea
7 changed files with 169 additions and 206 deletions
|
|
@ -4,7 +4,7 @@ from uuid import UUID as UUID_t
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import re, ast, operator
|
import re, ast, operator
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from backend import models
|
import models
|
||||||
|
|
||||||
ALLOWED_NAMES = {"days", "nights"}
|
ALLOWED_NAMES = {"days", "nights"}
|
||||||
ALLOWED_NODES = (
|
ALLOWED_NODES = (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
from uuid import UUID
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from backend.database import Base, engine
|
from database import Base, engine
|
||||||
from backend.routes import items, tags, trips, trip_items
|
from routes import items, tags, trips, trip_items, dev_seed
|
||||||
|
|
||||||
|
|
||||||
# Create tables (for MVP without Alembic)
|
# Create tables (for MVP without Alembic)
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from sqlalchemy import (
|
||||||
)
|
)
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from .database import Base
|
from database import Base
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
class User(Base):
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,31 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from backend.database import get_db
|
from database import get_db
|
||||||
from backend import models
|
import models
|
||||||
from backend.schemas import TripCreate, TripOut, TripUpdate, TripRegenerationResult
|
from schemas import ItemCreate, ItemOut
|
||||||
from backend.crud import generate_trip_items
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/trips", tags=["trips"])
|
router = APIRouter(prefix="/items", tags=["items"])
|
||||||
|
|
||||||
@router.get("/", response_model=list[TripOut])
|
@router.get("/", response_model=list[ItemOut])
|
||||||
def list_trips(db: Session = Depends(get_db)):
|
def list_items(db: Session = Depends(get_db)):
|
||||||
trips = (
|
items = (
|
||||||
db.query(models.Trip)
|
db.query(models.Item)
|
||||||
.options(
|
.options(joinedload(models.Item.tags).joinedload(models.ItemTag.tag))
|
||||||
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
|
|
||||||
joinedload(models.Trip.marked_tags).joinedload(models.TripTagMarked.tag),
|
|
||||||
)
|
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
# pydantic can handle relationships if orm_mode
|
||||||
return [
|
return [
|
||||||
TripOut(
|
models.Item(
|
||||||
id=t.id,
|
id=it.id,
|
||||||
name=t.name,
|
user_id=it.user_id,
|
||||||
start_date=t.start_date,
|
name=it.name,
|
||||||
end_date=t.end_date,
|
tags=it.tags,
|
||||||
selected_tags=[st.tag for st in t.selected_tags],
|
) for it in items
|
||||||
marked_tags=[mt.tag for mt in t.marked_tags],
|
|
||||||
)
|
|
||||||
for t in trips
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@router.post("/", response_model=TripOut)
|
@router.post("/", response_model=ItemOut)
|
||||||
def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
|
def create_item(payload: ItemCreate, db: Session = Depends(get_db)):
|
||||||
user = db.query(models.User).first()
|
user = db.query(models.User).first()
|
||||||
if not user:
|
if not user:
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
@ -39,85 +33,31 @@ def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
|
||||||
db.add(user)
|
db.add(user)
|
||||||
db.flush()
|
db.flush()
|
||||||
|
|
||||||
trip = models.Trip(user_id=user.id, name=payload.name, start_date=payload.start_date, end_date=payload.end_date)
|
item = models.Item(user_id=user.id, name=payload.name)
|
||||||
db.add(trip)
|
|
||||||
db.flush()
|
|
||||||
|
|
||||||
# attach selected & marked
|
if payload.tag_ids:
|
||||||
if payload.selected_tag_ids:
|
links = []
|
||||||
for tid in payload.selected_tag_ids:
|
tags = db.query(models.Tag).filter(models.Tag.id.in_(payload.tag_ids), models.Tag.user_id == user.id).all()
|
||||||
db.add(models.TripTagSelected(trip_id=trip.id, tag_id=tid))
|
for t in tags:
|
||||||
if payload.marked_tag_ids:
|
links.append(models.ItemTag(item=item, tag=t))
|
||||||
for tid in payload.marked_tag_ids:
|
item.tags = links
|
||||||
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.add(item)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
db.refresh(item)
|
||||||
|
|
||||||
# reload with relationships
|
# eager load for response
|
||||||
trip = (
|
item = (
|
||||||
db.query(models.Trip)
|
db.query(models.Item)
|
||||||
.options(
|
.options(joinedload(models.Item.tags).joinedload(models.ItemTag.tag))
|
||||||
joinedload(models.Trip.selected_tags).joinedload(models.TripTagSelected.tag),
|
.get(item.id)
|
||||||
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],
|
|
||||||
)
|
)
|
||||||
|
return item
|
||||||
|
|
||||||
@router.put("/{trip_id}/reconfigure", response_model=TripRegenerationResult)
|
@router.delete("/{item_id}", status_code=204)
|
||||||
def reconfigure_trip(trip_id: UUID, payload: TripUpdate, db: Session = Depends(get_db)):
|
def delete_item(item_id: UUID, db: Session = Depends(get_db)):
|
||||||
trip = db.get(models.Trip, trip_id)
|
item = db.get(models.Item, item_id)
|
||||||
if not trip:
|
if not item:
|
||||||
raise HTTPException(status_code=404, detail="Trip not found")
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
db.delete(item)
|
||||||
# update base fields
|
db.commit()
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from backend.database import get_db
|
from database import get_db
|
||||||
from backend import models
|
import models
|
||||||
from backend.schemas import TagCreate, TagOut
|
from schemas import TagCreate, TagOut
|
||||||
|
|
||||||
router = APIRouter(prefix="/tags", tags=["tags"])
|
router = APIRouter(prefix="/tags", tags=["tags"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from backend.database import get_db
|
from database import get_db
|
||||||
from backend import models
|
import models
|
||||||
from backend.schemas import TripItemOut
|
from schemas import TripItemOut
|
||||||
|
|
||||||
router = APIRouter(prefix="/trip-items", tags=["trip-items"])
|
router = APIRouter(prefix="/trip-items", tags=["trip-items"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,123 @@
|
||||||
import re
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from uuid import UUID, uuid4
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from fastapi import APIRouter, Depends
|
from uuid import UUID
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
|
||||||
from schemas import TripCreate, TripItemOut, TripOut
|
|
||||||
from models import Item, Trip, TripItem
|
|
||||||
from database import get_db
|
from database import get_db
|
||||||
from sqlalchemy import select
|
import models
|
||||||
from sqlalchemy.orm import selectinload
|
from schemas import TripCreate, TripOut, TripUpdate, TripRegenerationResult
|
||||||
from config import FIXED_USER_ID
|
from crud import generate_trip_items
|
||||||
|
|
||||||
router = APIRouter(prefix="/trips", tags=["Trips"])
|
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.get("/", response_model=list[TripOut])
|
@router.get("/", response_model=list[TripOut])
|
||||||
async def get_trips(db: AsyncSession = Depends(get_db)):
|
def list_trips(db: Session = Depends(get_db)):
|
||||||
user_id = FIXED_USER_ID
|
trips = (
|
||||||
result = await db.execute(select(Trip).where(Trip.user_id == user_id))
|
db.query(models.Trip)
|
||||||
trips = result.scalars().all()
|
.options(
|
||||||
return trips
|
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:
|
@router.post("/", response_model=TripOut)
|
||||||
def replacer(match):
|
def create_trip(payload: TripCreate, db: Session = Depends(get_db)):
|
||||||
expr = match.group(1)
|
user = db.query(models.User).first()
|
||||||
expr = expr.replace("days", str(days)).replace("nights", str(nights))
|
if not user:
|
||||||
try:
|
from uuid import uuid4
|
||||||
return str(eval(expr))
|
user = models.User(id=uuid4(), name="Demo")
|
||||||
except:
|
db.add(user)
|
||||||
return match.group(0)
|
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,
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue