Files
pbt/models.py
T
zisco a52e233c31
Deploy PBT / deploy (push) Failing after 2m53s
Extend stops to DACH-region
2026-09-12 07:06:29 +02:00

100 lines
3.8 KiB
Python

from datetime import date
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False, index=True)
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(255), nullable=False)
trips = db.relationship(
"Trip", backref="user", lazy=True, cascade="all, delete-orphan"
)
def set_password(self, password: str) -> None:
self.password_hash = generate_password_hash(password)
def check_password(self, password: str) -> bool:
return check_password_hash(self.password_hash, password)
VERKEHRSMITTEL_OPTIONEN = [
"Bus",
"Straßenbahn",
"U-Bahn",
"S-Bahn",
"Regionalzug",
"Fernzug (ÖBB/Railjet/WESTbahn)",
"Sonstiges",
]
class Stop(db.Model):
"""A boardable public-transport stop ("Haltestelle") in Austria, Germany
or Switzerland (DACH).
Sourced from OpenStreetMap; see scripts/fetch_stops.py and stops_import.py.
"""
__tablename__ = "stops"
id = db.Column(db.Integer, primary_key=True)
osm_type = db.Column(db.String(8), nullable=False)
osm_id = db.Column(db.BigInteger, nullable=False)
name = db.Column(db.String(200), nullable=False)
# lowercased, accent-folded copy of name for diacritic-insensitive search
name_normalized = db.Column(db.String(200), nullable=False, index=True)
stop_type = db.Column(db.String(20), nullable=False, default="other")
# ISO 3166-1 alpha-2 of the fetch query that found this stop (AT/DE/CH) -
# disambiguates same-named stops across borders (several "Hauptbahnhof").
country = db.Column(db.String(2), nullable=False, default="AT")
municipality = db.Column(db.String(120))
latitude = db.Column(db.Float, nullable=False)
longitude = db.Column(db.Float, nullable=False)
# ";"-joined line refs serving this stop (e.g. "693" or "S2;U4;WLB"), from
# OSM route relations; empty when unknown. Used by the "Linie" autocomplete.
# Text, not String(n): big interchanges (Linz Hbf) exceed a few hundred chars.
lines = db.Column(db.Text, nullable=False, default="")
def line_list(self) -> list[str]:
return [ref for ref in self.lines.split(";") if ref]
__table_args__ = (
db.UniqueConstraint("osm_type", "osm_id", name="uq_stops_osm"),
)
class Trip(db.Model):
__tablename__ = "trips"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False, index=True)
transport_mode = db.Column(db.String(50), nullable=False)
line = db.Column(db.String(50))
# optional detail beyond the line, e.g. direction/departure time looked up
# in an external journey planner ("Kurs"); free text, nothing to match.
# Text, not String(n) - see the "lines" column truncation incident.
course = db.Column(db.Text)
# free-text label as entered; *_stop_id links to a known stop when matched
origin = db.Column(db.String(120), nullable=False)
origin_stop_id = db.Column(db.Integer, db.ForeignKey("stops.id"))
destination = db.Column(db.String(120), nullable=False)
destination_stop_id = db.Column(db.Integer, db.ForeignKey("stops.id"))
trip_date = db.Column(db.Date, nullable=False, default=date.today)
rating = db.Column(db.Integer, nullable=False) # 1-5
comment = db.Column(db.Text)
created_at = db.Column(db.DateTime, server_default=db.func.now())
origin_stop = db.relationship("Stop", foreign_keys=[origin_stop_id])
destination_stop = db.relationship("Stop", foreign_keys=[destination_stop_id])