chore: save session state – feature/phase-1 ready to implement
- Update session-context.md with exact resume point for next session - Update settings.local.json with broader git permissions - feature/grundstruktur merged to develop - PAT authentication configured Version: 0.2.3
This commit is contained in:
0
backend/app/models/__init__.py
Normal file
0
backend/app/models/__init__.py
Normal file
66
backend/app/models/bed.py
Normal file
66
backend/app/models/bed.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import enum
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, Numeric, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class LocationType(str, enum.Enum):
|
||||
SONNIG = "sonnig"
|
||||
HALBSCHATTEN = "halbschatten"
|
||||
SCHATTEN = "schatten"
|
||||
|
||||
|
||||
class SoilType(str, enum.Enum):
|
||||
NORMAL = "normal"
|
||||
SANDIG = "sandig"
|
||||
LEHMIG = "lehmig"
|
||||
HUMUSREICH = "humusreich"
|
||||
|
||||
|
||||
class Bed(Base):
|
||||
__tablename__ = "beds"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
tenant_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("tenants.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
width_m: Mapped[float] = mapped_column(Numeric(5, 2), nullable=False)
|
||||
length_m: Mapped[float] = mapped_column(Numeric(5, 2), nullable=False)
|
||||
location: Mapped[LocationType] = mapped_column(
|
||||
Enum(LocationType, name="location_type"),
|
||||
nullable=False,
|
||||
)
|
||||
soil_type: Mapped[SoilType] = mapped_column(
|
||||
Enum(SoilType, name="soil_type"),
|
||||
nullable=False,
|
||||
default=SoilType.NORMAL,
|
||||
)
|
||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
# Relationships
|
||||
tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="beds") # noqa: F821
|
||||
plantings: Mapped[list["BedPlanting"]] = relationship( # noqa: F821
|
||||
"BedPlanting", back_populates="bed", cascade="all, delete-orphan"
|
||||
)
|
||||
133
backend/app/models/plant.py
Normal file
133
backend/app/models/plant.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import enum
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
Enum,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class NutrientDemand(str, enum.Enum):
|
||||
SCHWACH = "schwach"
|
||||
MITTEL = "mittel"
|
||||
STARK = "stark"
|
||||
|
||||
|
||||
class WaterDemand(str, enum.Enum):
|
||||
WENIG = "wenig"
|
||||
MITTEL = "mittel"
|
||||
VIEL = "viel"
|
||||
|
||||
|
||||
class CompatibilityRating(str, enum.Enum):
|
||||
GUT = "gut"
|
||||
NEUTRAL = "neutral"
|
||||
SCHLECHT = "schlecht"
|
||||
|
||||
|
||||
class PlantFamily(Base):
|
||||
__tablename__ = "plant_families"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
|
||||
latin_name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
|
||||
|
||||
# Relationships
|
||||
plants: Mapped[list["Plant"]] = relationship("Plant", back_populates="family")
|
||||
|
||||
|
||||
class Plant(Base):
|
||||
__tablename__ = "plants"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
tenant_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
ForeignKey("tenants.id", ondelete="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
family_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("plant_families.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
latin_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
nutrient_demand: Mapped[NutrientDemand] = mapped_column(
|
||||
Enum(NutrientDemand, name="nutrient_demand"),
|
||||
nullable=False,
|
||||
)
|
||||
water_demand: Mapped[WaterDemand] = mapped_column(
|
||||
Enum(WaterDemand, name="water_demand"),
|
||||
nullable=False,
|
||||
)
|
||||
spacing_cm: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
sowing_start_month: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
sowing_end_month: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
rest_years: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
# Relationships
|
||||
tenant: Mapped["Tenant | None"] = relationship( # noqa: F821
|
||||
"Tenant", back_populates="plants"
|
||||
)
|
||||
family: Mapped["PlantFamily"] = relationship("PlantFamily", back_populates="plants")
|
||||
plantings: Mapped[list["BedPlanting"]] = relationship( # noqa: F821
|
||||
"BedPlanting", back_populates="plant"
|
||||
)
|
||||
compatibility_a: Mapped[list["PlantCompatibility"]] = relationship(
|
||||
"PlantCompatibility",
|
||||
foreign_keys="PlantCompatibility.plant_id_a",
|
||||
back_populates="plant_a",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
compatibility_b: Mapped[list["PlantCompatibility"]] = relationship(
|
||||
"PlantCompatibility",
|
||||
foreign_keys="PlantCompatibility.plant_id_b",
|
||||
back_populates="plant_b",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
|
||||
class PlantCompatibility(Base):
|
||||
__tablename__ = "plant_compatibilities"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("plant_id_a", "plant_id_b", name="uq_plant_compatibility"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
plant_id_a: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("plants.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
plant_id_b: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("plants.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
rating: Mapped[CompatibilityRating] = mapped_column(
|
||||
Enum(CompatibilityRating, name="compatibility_rating"),
|
||||
nullable=False,
|
||||
)
|
||||
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
plant_a: Mapped["Plant"] = relationship(
|
||||
"Plant", foreign_keys=[plant_id_a], back_populates="compatibility_a"
|
||||
)
|
||||
plant_b: Mapped["Plant"] = relationship(
|
||||
"Plant", foreign_keys=[plant_id_b], back_populates="compatibility_b"
|
||||
)
|
||||
40
backend/app/models/planting.py
Normal file
40
backend/app/models/planting.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import uuid
|
||||
from datetime import date, datetime, timezone
|
||||
|
||||
from sqlalchemy import Date, DateTime, ForeignKey, Integer, Numeric, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class BedPlanting(Base):
|
||||
__tablename__ = "bed_plantings"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
bed_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("beds.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
plant_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("plants.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
area_m2: Mapped[float | None] = mapped_column(Numeric(5, 2), nullable=True)
|
||||
count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
planted_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
||||
removed_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
# Relationships
|
||||
bed: Mapped["Bed"] = relationship("Bed", back_populates="plantings") # noqa: F821
|
||||
plant: Mapped["Plant"] = relationship("Plant", back_populates="plantings") # noqa: F821
|
||||
40
backend/app/models/tenant.py
Normal file
40
backend/app/models/tenant.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class Tenant(Base):
|
||||
__tablename__ = "tenants"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
slug: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
# Relationships
|
||||
user_tenants: Mapped[list["UserTenant"]] = relationship( # noqa: F821
|
||||
"UserTenant", back_populates="tenant", cascade="all, delete-orphan"
|
||||
)
|
||||
beds: Mapped[list["Bed"]] = relationship( # noqa: F821
|
||||
"Bed", back_populates="tenant", cascade="all, delete-orphan"
|
||||
)
|
||||
plants: Mapped[list["Plant"]] = relationship( # noqa: F821
|
||||
"Plant", back_populates="tenant"
|
||||
)
|
||||
71
backend/app/models/user.py
Normal file
71
backend/app/models/user.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import enum
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, String, UniqueConstraint, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class TenantRole(str, enum.Enum):
|
||||
READ_ONLY = "READ_ONLY"
|
||||
READ_WRITE = "READ_WRITE"
|
||||
TENANT_ADMIN = "TENANT_ADMIN"
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
primary_key=True, default=uuid.uuid4, index=True
|
||||
)
|
||||
email: Mapped[str] = mapped_column(
|
||||
String(255), unique=True, nullable=False, index=True
|
||||
)
|
||||
hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
full_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
is_superadmin: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
# Relationships
|
||||
user_tenants: Mapped[list["UserTenant"]] = relationship(
|
||||
"UserTenant", back_populates="user", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
|
||||
class UserTenant(Base):
|
||||
__tablename__ = "user_tenants"
|
||||
__table_args__ = (UniqueConstraint("user_id", "tenant_id", name="uq_user_tenant"),)
|
||||
|
||||
user_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
tenant_id: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("tenants.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
role: Mapped[TenantRole] = mapped_column(
|
||||
Enum(TenantRole, name="tenant_role"),
|
||||
nullable=False,
|
||||
default=TenantRole.READ_ONLY,
|
||||
)
|
||||
|
||||
# Relationships
|
||||
user: Mapped["User"] = relationship("User", back_populates="user_tenants")
|
||||
tenant: Mapped["Tenant"] = relationship( # noqa: F821
|
||||
"Tenant", back_populates="user_tenants"
|
||||
)
|
||||
Reference in New Issue
Block a user