Datumsspalten auf Date-Typ umgestellt und Normalisierungsmigration

Modelle: startdatum (Projekt), datum und wiedervorlage_datum (CallLog)
von String(20) auf Date umgestellt. columns2dict serialisiert diese nun
über isoformat() als yyyy-MM-dd. Die String-Normalisierung in
columns2dict wurde entfernt, da sie nicht mehr benötigt wird.

Migration V01.08: Normalisiert bestehende String-Datumswerte (z.B.
'2026-08-3') auf ISO-Format yyyy-MM-dd mittels printf/substr, da
SQLite's date() einstellige Komponenten nicht erkennt. Leere Strings
werden zu NULL. V01.07 ist Platzhalter für die fehlgeschlagene V01.06.

Client: toDateInputValue normalisiert ebenfalls String-Datumswerte
als Fallback für Route-Params (Neuanlage).

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
gg committed 2026-09-04 16:57:17 +02:00
1 parent 93827dde0f
commit 461953a7e9
3 files changed
+107 -5

No files matched your search

+4 -4
View File
@@ -12,7 +12,7 @@ erfolgt ausschließlich über __bind_key__.
import datetime import datetime
import decimal import decimal
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Index, Integer, String from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, Index, Integer, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from db import dbapp from db import dbapp
@@ -145,7 +145,7 @@ class Projekt(dbapp.Model):
projektname = Column(String(200), nullable=False) projektname = Column(String(200), nullable=False)
zweck = Column(String(2000), nullable=True) zweck = Column(String(2000), nullable=True)
verantwortlicher = Column(String(200), nullable=True) verantwortlicher = Column(String(200), nullable=True)
startdatum = Column(String(20), nullable=True) startdatum = Column(Date, nullable=True)
kunde = relationship('Kunde') kunde = relationship('Kunde')
@@ -214,14 +214,14 @@ class CallLog(dbapp.Model):
) )
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False) projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False)
datum = Column(String(20), nullable=True) datum = Column(Date, nullable=True)
uhrzeit_beginn = Column(String(10), nullable=True) uhrzeit_beginn = Column(String(10), nullable=True)
dauer = Column(String(10), nullable=True) dauer = Column(String(10), nullable=True)
betreff = Column(String(200), nullable=True) betreff = Column(String(200), nullable=True)
name_ansprechpartner = Column(String(200), nullable=True) name_ansprechpartner = Column(String(200), nullable=True)
telefon = Column(String(100), nullable=True) telefon = Column(String(100), nullable=True)
notiz = Column(String(2000), nullable=True) notiz = Column(String(2000), nullable=True)
wiedervorlage_datum = Column(String(20), nullable=True) wiedervorlage_datum = Column(Date, nullable=True)
erledigt = Column(Boolean, nullable=False, default=False) erledigt = Column(Boolean, nullable=False, default=False)
projekt = relationship('Projekt') projekt = relationship('Projekt')
+92
View File
@@ -108,6 +108,98 @@ def _db_V01_05():
session.commit() session.commit()
def _db_V01_07():
"""
Platzhalter für die fehlgeschlagene Migration V01.06. Die eigentliche
Normalisierung erfolgt in V01.08, da V01.06 bereits (fehlerhaft)
durchlief und die Systeminfo auf 01.06 aktualisierte.
"""
pass
def _db_V01_08():
"""
Normalisiert bestehende Datumswerte in den Spalten 'datum' und
'wiedervorlage_datum' (call_logs) sowie 'startdatum' (projekte) auf
das ISO-Format yyyy-MM-dd. Diese Spalten waren bisher String-Spalten
und können Werte ohne führende Nullen (z.B. '2026-08-3') enthalten,
die von SQLite's date()-Funktion nicht erkannt werden. Daher wird
printf('%04d-%02d-%02d', ...) zur Normalisierung verwendet, das auch
einstellige Monate/Tage korrekt auffüllt. Leere Strings werden zu
NULL konvertiert, da der Date-Typ keine leeren Strings akzeptiert.
Alle Updates werden vor dem Update der Systeminfo committet, um
'database is locked' zu vermeiden.
"""
from sqlalchemy import text
# Normalisierungs-Ausdruck: splittet den Datum-String am '-' und
# formatiert die Komponenten mit führenden Nullen.
# _norm(col) = printf('%04d-%02d-%02d',
# cast(substr(col, 1, instr(col, '-')-1) as integer),
# cast(substr(col, instr(col, '-')+1, instr(substr(col, instr(col, '-')+1), '-')-1) as integer),
# cast(substr(col, instr(col, '-')+instr(substr(col, instr(col, '-')+1), '-')+1) as integer)
# )
# Da dieser Ausdruck sehr lang ist, wird er pro Spalte inline gesetzt.
# call_logs.datum
session.execute(text(
"UPDATE call_logs SET datum = "
"printf('%04d-%02d-%02d', "
" cast(substr(datum, 1, instr(datum, '-')-1) as integer), "
" cast(substr(datum, instr(datum, '-')+1, "
" instr(substr(datum, instr(datum, '-')+1), '-')-1) as integer), "
" cast(substr(datum, "
" instr(datum, '-')+instr(substr(datum, instr(datum, '-')+1), '-')+1"
" ) as integer)) "
"WHERE datum IS NOT NULL AND datum != '' "
"AND instr(datum, '-') > 0"
))
session.execute(text(
"UPDATE call_logs SET datum = NULL WHERE datum = ''"
))
# call_logs.wiedervorlage_datum
session.execute(text(
"UPDATE call_logs SET wiedervorlage_datum = "
"printf('%04d-%02d-%02d', "
" cast(substr(wiedervorlage_datum, 1, instr(wiedervorlage_datum, '-')-1) as integer), "
" cast(substr(wiedervorlage_datum, instr(wiedervorlage_datum, '-')+1, "
" instr(substr(wiedervorlage_datum, instr(wiedervorlage_datum, '-')+1), '-')-1) as integer), "
" cast(substr(wiedervorlage_datum, "
" instr(wiedervorlage_datum, '-')+instr(substr(wiedervorlage_datum, instr(wiedervorlage_datum, '-')+1), '-')+1"
" ) as integer)) "
"WHERE wiedervorlage_datum IS NOT NULL AND wiedervorlage_datum != '' "
"AND instr(wiedervorlage_datum, '-') > 0"
))
session.execute(text(
"UPDATE call_logs SET wiedervorlage_datum = NULL WHERE wiedervorlage_datum = ''"
))
# projekte.startdatum
session.execute(text(
"UPDATE projekte SET startdatum = "
"printf('%04d-%02d-%02d', "
" cast(substr(startdatum, 1, instr(startdatum, '-')-1) as integer), "
" cast(substr(startdatum, instr(startdatum, '-')+1, "
" instr(substr(startdatum, instr(startdatum, '-')+1), '-')-1) as integer), "
" cast(substr(startdatum, "
" instr(startdatum, '-')+instr(substr(startdatum, instr(startdatum, '-')+1), '-')+1"
" ) as integer)) "
"WHERE startdatum IS NOT NULL AND startdatum != '' "
"AND instr(startdatum, '-') > 0"
))
session.execute(text(
"UPDATE projekte SET startdatum = NULL WHERE startdatum = ''"
))
# Daten-Updates committen, bevor die Systeminfo aktualisiert wird.
session.commit()
# Systeminfo erst nach dem Commit der Daten-Updates aktualisieren.
_updatesysteminfo('01.08')
session.commit()
def checkupdates(): def checkupdates():
""" """
Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle
+11 -1
View File
@@ -118,10 +118,20 @@ export const Textarea:m.Component<iTextarea> = {
} }
export function toDateInputValue(date: Date|string): string { export function toDateInputValue(date: Date|string): string {
console.log(date, typeof(date), (date instanceof Date))
let result: string = null let result: string = null
if (typeof date === "string") { if (typeof date === "string") {
// String-Datumswerte (aus der DB) auf das vom HTML-Date-Input
// geforderte Format yyyy-MM-dd normalisieren (führende Nullen).
if (date === "") {
result = ""
} else {
const parts = date.split("-")
if (parts.length === 3) {
result = [parts[0], parts[1].padStart(2, "0"), parts[2].padStart(2, "0")].join("-")
} else {
result = date result = date
}
}
} else if (date instanceof Date && date != null) { } else if (date instanceof Date && date != null) {
result = [date.getFullYear(), (date.getMonth() + 1).toString().padStart(2, "0"), date.getDate().toString().padStart(2, "0")].join("-") result = [date.getFullYear(), (date.getMonth() + 1).toString().padStart(2, "0"), date.getDate().toString().padStart(2, "0")].join("-")
} }