init
This commit is contained in:
17
app/models/__init__.py
Normal file
17
app/models/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from app.models.user import User
|
||||
from app.models.activity import Activity
|
||||
from app.models.zone import Zone
|
||||
from app.models.zone_history import ZoneHistory
|
||||
from app.models.friendship import Friendship
|
||||
from app.models.score import Score
|
||||
from app.models.notification import Notification
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
"Activity",
|
||||
"Zone",
|
||||
"ZoneHistory",
|
||||
"Friendship",
|
||||
"Score",
|
||||
"Notification",
|
||||
]
|
||||
19
app/models/activity.py
Normal file
19
app/models/activity.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Activity(SQLModel, table=True):
|
||||
__tablename__ = "activities"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
user_id: uuid.UUID = Field(foreign_key="users.id", index=True)
|
||||
type: str = Field(max_length=16) # run | cycle | walk | hike
|
||||
started_at: datetime | None = None
|
||||
ended_at: datetime | None = None
|
||||
distance_m: float | None = None
|
||||
raw_gpx: str | None = None # deleted after 30 days (privacy)
|
||||
status: str = Field(
|
||||
default="pending", max_length=16
|
||||
) # pending | completed | failed
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
11
app/models/friendship.py
Normal file
11
app/models/friendship.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Friendship(SQLModel, table=True):
|
||||
__tablename__ = "friendships"
|
||||
|
||||
user_id: uuid.UUID = Field(foreign_key="users.id", primary_key=True)
|
||||
friend_id: uuid.UUID = Field(foreign_key="users.id", primary_key=True)
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
17
app/models/notification.py
Normal file
17
app/models/notification.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field, Column
|
||||
from sqlalchemy import JSON
|
||||
|
||||
|
||||
class Notification(SQLModel, table=True):
|
||||
__tablename__ = "notifications"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
user_id: uuid.UUID = Field(foreign_key="users.id", index=True)
|
||||
type: str = Field(
|
||||
max_length=32
|
||||
) # zone_captured | leaderboard_change | streak_risk | friend_joined | raid_weekend
|
||||
payload: dict = Field(default_factory=dict, sa_column=Column(JSON))
|
||||
sent_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
read_at: datetime | None = None
|
||||
15
app/models/score.py
Normal file
15
app/models/score.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import uuid
|
||||
from datetime import date, datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Score(SQLModel, table=True):
|
||||
__tablename__ = "scores"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
user_id: uuid.UUID = Field(foreign_key="users.id", index=True)
|
||||
date: date
|
||||
base_pts: int = 0
|
||||
bonus_pts: int = 0
|
||||
total_pts: int = 0
|
||||
streak_days: int = 0
|
||||
15
app/models/user.py
Normal file
15
app/models/user.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
username: str = Field(max_length=32, unique=True, index=True)
|
||||
email: str = Field(unique=True, index=True)
|
||||
password_hash: str
|
||||
avatar_url: str | None = None
|
||||
fcm_token: str | None = None
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
19
app/models/zone.py
Normal file
19
app/models/zone.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Zone(SQLModel, table=True):
|
||||
__tablename__ = "zones"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
owner_id: uuid.UUID = Field(foreign_key="users.id", index=True)
|
||||
activity_id: uuid.UUID = Field(foreign_key="activities.id")
|
||||
# Polygon stored as WKT text for SQLite compatibility.
|
||||
# For PostgreSQL/PostGIS, a migration can add a GEOMETRY column.
|
||||
polygon_wkt: str # e.g. "POLYGON((lon lat, lon lat, ...))"
|
||||
area_m2: float
|
||||
defense_level: int = Field(default=1)
|
||||
defense_runs: int = Field(default=0)
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
last_reinforced_at: datetime | None = None
|
||||
14
app/models/zone_history.py
Normal file
14
app/models/zone_history.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class ZoneHistory(SQLModel, table=True):
|
||||
__tablename__ = "zone_history"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
zone_id: uuid.UUID = Field(foreign_key="zones.id", index=True)
|
||||
from_owner_id: uuid.UUID = Field(foreign_key="users.id")
|
||||
to_owner_id: uuid.UUID = Field(foreign_key="users.id")
|
||||
changed_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
cause: str = Field(max_length=16) # capture | merge
|
||||
Reference in New Issue
Block a user