226 lines
6.9 KiB
Python
226 lines
6.9 KiB
Python
import uuid
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
from pydantic import EmailStr
|
|
from sqlalchemy import JSON, DateTime, LargeBinary
|
|
from sqlmodel import Field, Relationship, SQLModel
|
|
|
|
|
|
def get_datetime_utc() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
# Shared properties
|
|
class UserBase(SQLModel):
|
|
email: EmailStr = Field(unique=True, index=True, max_length=255)
|
|
is_active: bool = True
|
|
is_superuser: bool = False
|
|
full_name: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
# Properties to receive via API on creation
|
|
class UserCreate(UserBase):
|
|
password: str = Field(min_length=8, max_length=128)
|
|
|
|
|
|
class UserRegister(SQLModel):
|
|
email: EmailStr = Field(max_length=255)
|
|
password: str = Field(min_length=8, max_length=128)
|
|
full_name: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
# Properties to receive via API on update, all are optional
|
|
class UserUpdate(UserBase):
|
|
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
|
|
password: str | None = Field(default=None, min_length=8, max_length=128)
|
|
|
|
|
|
class UserUpdateMe(SQLModel):
|
|
full_name: str | None = Field(default=None, max_length=255)
|
|
email: EmailStr | None = Field(default=None, max_length=255)
|
|
|
|
|
|
class UpdatePassword(SQLModel):
|
|
current_password: str = Field(min_length=8, max_length=128)
|
|
new_password: str = Field(min_length=8, max_length=128)
|
|
|
|
|
|
# Database model, database table inferred from class name
|
|
class User(UserBase, table=True):
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
hashed_password: str
|
|
created_at: datetime | None = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
locations: list["Location"] = Relationship(
|
|
back_populates="owner", cascade_delete=True
|
|
)
|
|
|
|
|
|
# Properties to return via API, id is always required
|
|
class UserPublic(UserBase):
|
|
id: uuid.UUID
|
|
created_at: datetime | None = None
|
|
|
|
|
|
class UsersPublic(SQLModel):
|
|
data: list[UserPublic]
|
|
count: int
|
|
|
|
|
|
# Shared properties
|
|
class LocationBase(SQLModel):
|
|
title: str = Field(min_length=1, max_length=255)
|
|
description: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
# Properties to receive on location creation
|
|
class LocationCreate(LocationBase):
|
|
pass
|
|
|
|
|
|
# Properties to receive on location update
|
|
class LocationUpdate(LocationBase):
|
|
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
|
|
|
|
|
|
# Database model, database table inferred from class name
|
|
class Location(LocationBase, table=True):
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
created_at: datetime | None = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
owner_id: uuid.UUID = Field(
|
|
foreign_key="user.id", nullable=False, ondelete="CASCADE"
|
|
)
|
|
owner: User | None = Relationship(back_populates="locations")
|
|
|
|
|
|
# Properties to return via API, id is always required
|
|
class LocationPublic(LocationBase):
|
|
id: uuid.UUID
|
|
owner_id: uuid.UUID
|
|
created_at: datetime | None = None
|
|
|
|
|
|
class LocationsPublic(SQLModel):
|
|
data: list[LocationPublic]
|
|
count: int
|
|
|
|
|
|
class TerminalMessageRaw(SQLModel, table=True):
|
|
__tablename__ = "terminal_message_raw"
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
device_id: str = Field(max_length=255, index=True)
|
|
topic: str = Field(max_length=255)
|
|
received_at: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
payload_bytes: bytes = Field(sa_type=LargeBinary) # type: ignore
|
|
payload_size: int = 0
|
|
payload_sha256: str = Field(default="", max_length=64, index=True)
|
|
qos: int = 0
|
|
retain: bool = False
|
|
parse_status: str = Field(default="pending", max_length=32, index=True)
|
|
parse_attempts: int = 0
|
|
next_retry_at: datetime | None = Field(
|
|
default=None,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
index=True,
|
|
)
|
|
parse_error: str | None = Field(default=None, max_length=1024)
|
|
created_at: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
|
|
|
|
class TerminalObservationEvent(SQLModel, table=True):
|
|
__tablename__ = "terminal_observation_event"
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
raw_id: uuid.UUID = Field(
|
|
foreign_key="terminal_message_raw.id", nullable=False, index=True
|
|
)
|
|
device_id: str = Field(max_length=255, index=True)
|
|
event_time: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
index=True,
|
|
)
|
|
event_type: str = Field(max_length=64, index=True)
|
|
payload_json: dict[str, Any] = Field(default_factory=dict, sa_type=JSON) # type: ignore
|
|
created_at: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
|
|
|
|
class TerminalDeviceState(SQLModel, table=True):
|
|
__tablename__ = "terminal_device_state"
|
|
|
|
device_id: str = Field(max_length=255, primary_key=True)
|
|
last_event_time: datetime | None = Field(
|
|
default=None, sa_type=DateTime(timezone=True)
|
|
) # type: ignore
|
|
fix_status: str | None = Field(default=None, max_length=64)
|
|
lat: float | None = None
|
|
lon: float | None = None
|
|
alt_m: float | None = None
|
|
satellites_used: int | None = None
|
|
satellite_signal_summary: dict[str, Any] | None = Field(default=None, sa_type=JSON) # type: ignore
|
|
rtcm_type_summary: dict[str, Any] | None = Field(default=None, sa_type=JSON) # type: ignore
|
|
updated_at: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
|
|
|
|
class TerminalRawArchiveManifest(SQLModel, table=True):
|
|
__tablename__ = "terminal_raw_archive_manifest"
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
device_id: str = Field(max_length=255, index=True)
|
|
range_start: datetime = Field(
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
index=True,
|
|
)
|
|
range_end: datetime = Field(
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
index=True,
|
|
)
|
|
file_path: str = Field(max_length=1024, unique=True)
|
|
file_size: int = 0
|
|
sha256: str = Field(max_length=64)
|
|
record_count: int = 0
|
|
created_at: datetime = Field(
|
|
default_factory=get_datetime_utc,
|
|
sa_type=DateTime(timezone=True), # type: ignore
|
|
)
|
|
|
|
|
|
# Generic message
|
|
class Message(SQLModel):
|
|
message: str
|
|
|
|
|
|
# JSON payload containing access token
|
|
class Token(SQLModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
# Contents of JWT token
|
|
class TokenPayload(SQLModel):
|
|
sub: str | None = None
|
|
|
|
|
|
class NewPassword(SQLModel):
|
|
token: str
|
|
new_password: str = Field(min_length=8, max_length=128)
|