Files
shopdb-flask/shopdb/core/models/application.py
cproudlock 9c8b2c9c9e
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / frontend (push) Has been cancelled
DB review safe-fix: naive-UTC timestamp defaults (drop db.func.now)
DB review found four DateTime columns defaulting to db.func.now() (MySQL
session-timezone wall clock) while the rest of the schema stores naive UTC, so
one schema mixed two clocks and to_dict() labelled the local values UTC with a
'Z' suffix. Switch application.dateadded, computers.installeddate,
knowledgebase.lastupdated (default + onupdate), and slides.uploadeddate to the
module-level naive-UTC _utcnow callable already used elsewhere (apitoken.py).
ORM-side default only - no column-type change, no data migration; affects
new/updated rows.

Targeted tests pass (154); naming + pyflakes green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:09:29 -04:00

96 lines
3.5 KiB
Python

"""Application tracking models."""
from datetime import datetime, timezone
from shopdb.extensions import db
from .base import BaseModel
def _utcnow():
# naive UTC to match the other DB DateTime columns (stored without tzinfo)
return datetime.now(timezone.utc).replace(tzinfo=None)
# SupportTeam / SupportTeamContact live in supportteam.py; imported by the
# models package so the Application.supportteam relationship resolves.
class Application(BaseModel):
"""Application catalog."""
__tablename__ = 'applications'
appid = db.Column(db.Integer, primary_key=True)
appname = db.Column(db.String(100), unique=True, nullable=False)
appdescription = db.Column(db.String(255))
supportteamid = db.Column(db.Integer, db.ForeignKey('supportteams.supportteamid'))
isinstallable = db.Column(db.Boolean, default=False)
applicationnotes = db.Column(db.Text)
installpath = db.Column(db.String(255))
applicationlink = db.Column(db.String(512))
documentationpath = db.Column(db.String(512))
ishidden = db.Column(db.Boolean, default=False)
isprinter = db.Column(db.Boolean, default=False)
islicenced = db.Column(db.Boolean, default=False)
isrequired = db.Column(
db.Boolean, default=False,
comment='Required on all PCs (drives the software-compliance report)'
)
image = db.Column(db.String(255))
# Relationships
supportteam = db.relationship('SupportTeam', back_populates='applications')
versions = db.relationship('AppVersion', back_populates='application', lazy='dynamic')
def to_dict(self):
"""Serialize, flattening the support team + its active contacts.
Emits supportteamname, teamurl, and the team's active contacts so the
frontend needs a single call to render the Support card.
"""
result = super().to_dict()
team = self.supportteam
result['supportteamname'] = team.teamname if team else None
result['teamurl'] = team.teamurl if team else None
result['contacts'] = [
{'name': c.name, 'sso': c.sso}
for c in team.active_contacts()
] if team else []
return result
def __repr__(self):
return f"<Application {self.appname}>"
class AppVersion(db.Model):
"""Application version tracking."""
__tablename__ = 'appversions'
appversionid = db.Column(db.Integer, primary_key=True)
appid = db.Column(db.Integer, db.ForeignKey('applications.appid'), nullable=False)
version = db.Column(db.String(50), nullable=False)
releasedate = db.Column(db.Date)
notes = db.Column(db.String(255))
dateadded = db.Column(db.DateTime, default=_utcnow)
isactive = db.Column(db.Boolean, default=True)
# Relationships
application = db.relationship('Application', back_populates='versions')
# Unique constraint on app + version
__table_args__ = (
db.UniqueConstraint('appid', 'version', name='uq_app_version'),
)
def to_dict(self):
"""Convert to dictionary."""
return {
'appversionid': self.appversionid,
'appid': self.appid,
'version': self.version,
'releasedate': self.releasedate.isoformat() if self.releasedate else None,
'notes': self.notes,
'dateadded': self.dateadded.isoformat() + 'Z' if self.dateadded else None,
'isactive': self.isactive
}
def __repr__(self):
return f"<AppVersion {self.application.appname if self.application else self.appid} v{self.version}>"