Update database schema

This commit is contained in:
2023-04-14 20:39:04 +02:00
parent 28ce38779c
commit d5c8c224df
4 changed files with 142 additions and 40 deletions

View File

@ -359,7 +359,6 @@ CREATE TABLE area_commitment (
area INTEGER NOT NULL, area INTEGER NOT NULL,
sortid TEXT NOT NULL, sortid TEXT NOT NULL,
attrid TEXT,
cultid TEXT NOT NULL, cultid TEXT NOT NULL,
CONSTRAINT pk_area_commitment PRIMARY KEY (vnr, kgnr, gstnr), CONSTRAINT pk_area_commitment PRIMARY KEY (vnr, kgnr, gstnr),
@ -369,9 +368,6 @@ CREATE TABLE area_commitment (
CONSTRAINT fk_area_commitment_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid) CONSTRAINT fk_area_commitment_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE RESTRICT, ON DELETE RESTRICT,
CONSTRAINT fk_area_commitment_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_area_commitment_wine_cultivation FOREIGN KEY (cultid) REFERENCES wine_cultivation (cultid) CONSTRAINT fk_area_commitment_wine_cultivation FOREIGN KEY (cultid) REFERENCES wine_cultivation (cultid)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE RESTRICT, ON DELETE RESTRICT,
@ -380,6 +376,21 @@ CREATE TABLE area_commitment (
ON DELETE RESTRICT ON DELETE RESTRICT
) STRICT; ) STRICT;
CREATE TABLE area_commitment_attribute (
vnr INTEGER NOT NULL,
kgnr INTEGER NOT NULL,
gstnr TEXT NOT NULL,
attrid TEXT NOT NULL,
CONSTRAINT pk_area_commitment_attribute PRIMARY KEY (vnr, kgnr, gstnr, attrid),
CONSTRAINT fk_area_commitment_attribute_area_commitment FOREIGN KEY (vnr, kgnr, gstnr) REFERENCES area_commitment (vnr, kgnr, gstnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_area_commitment_attribute_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
---------------------------------------------------------------- ----------------------------------------------------------------
@ -400,7 +411,7 @@ CREATE TABLE season (
CREATE TABLE modifier ( CREATE TABLE modifier (
year INTEGER NOT NULL, year INTEGER NOT NULL,
mnr INTEGER NOT NULL, modid TEXT NOT NULL CHECK (modid REGEXP '^[A-Z]+$'),
name TEXT NOT NULL, name TEXT NOT NULL,
abs INTEGER, abs INTEGER,
@ -409,7 +420,7 @@ CREATE TABLE modifier (
standard INTEGER NOT NULL CHECK (standard IN (TRUE, FALSE)), standard INTEGER NOT NULL CHECK (standard IN (TRUE, FALSE)),
quick_select INTEGER NOT NULL CHECK (quick_select IN (TRUE, FALSE)), quick_select INTEGER NOT NULL CHECK (quick_select IN (TRUE, FALSE)),
CONSTRAINT pk_modifier PRIMARY KEY (year, mnr), CONSTRAINT pk_modifier PRIMARY KEY (year, modid),
CONSTRAINT fk_modifier_season FOREIGN KEY (year) REFERENCES season (year) CONSTRAINT fk_modifier_season FOREIGN KEY (year) REFERENCES season (year)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE CASCADE, ON DELETE CASCADE,
@ -459,8 +470,6 @@ CREATE TABLE delivery_part (
dpnr INTEGER NOT NULL, dpnr INTEGER NOT NULL,
sortid TEXT NOT NULL, sortid TEXT NOT NULL,
attrid TEXT DEFAULT NULL,
weight INTEGER NOT NULL, weight INTEGER NOT NULL,
kmw REAL NOT NULL, kmw REAL NOT NULL,
qualid TEXT NOT NULL, qualid TEXT NOT NULL,
@ -474,7 +483,7 @@ CREATE TABLE delivery_part (
spl_check INTEGER NOT NULL CHECK (spl_check IN (TRUE, FALSE)) DEFAULT FALSE, spl_check INTEGER NOT NULL CHECK (spl_check IN (TRUE, FALSE)) DEFAULT FALSE,
hand_picked INTEGER CHECK (hand_picked IN (TRUE, FALSE)) DEFAULT NULL, hand_picked INTEGER CHECK (hand_picked IN (TRUE, FALSE)) DEFAULT NULL,
lesemaschine INTEGER CHECK (lesemaschine IN (True, FALSE)) DEFAULT NULL, lesewagen INTEGER CHECK (lesewagen IN (True, FALSE)) DEFAULT NULL,
temperature REAL DEFAULT NULL, temperature REAL DEFAULT NULL,
acid REAL DEFAULT NULL, acid REAL DEFAULT NULL,
@ -491,9 +500,6 @@ CREATE TABLE delivery_part (
CONSTRAINT fk_delivery_part_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid) CONSTRAINT fk_delivery_part_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE RESTRICT, ON DELETE RESTRICT,
CONSTRAINT fk_delivery_part_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_delivery_part_wine_quality FOREIGN KEY (qualid) REFERENCES wine_quality (qualid) CONSTRAINT fk_delivery_part_wine_quality FOREIGN KEY (qualid) REFERENCES wine_quality (qualid)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE RESTRICT, ON DELETE RESTRICT,
@ -534,17 +540,32 @@ BEGIN
) WHERE (year, did, dpnr) = (NEW.year, NEW.did, NEW.dpnr); ) WHERE (year, did, dpnr) = (NEW.year, NEW.did, NEW.dpnr);
END; END;
CREATE TABLE delivery_part_attribute (
year INTEGER NOT NULL,
did INTEGER NOT NULL,
dpnr INTEGER NOT NULL,
attrid TEXT NOT NULL,
CONSTRAINT pk_delivery_part_attribute PRIMARY KEY (year, did, dpnr, attrid),
CONSTRAINT fk_delivery_part_attribute_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_delivery_part_attribute_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
CREATE TABLE delivery_part_modifier ( CREATE TABLE delivery_part_modifier (
year INTEGER NOT NULL, year INTEGER NOT NULL,
did INTEGER NOT NULL, did INTEGER NOT NULL,
dpnr INTEGER NOT NULL, dpnr INTEGER NOT NULL,
mnr INTEGER NOT NULL, modid TEXT NOT NULL,
CONSTRAINT pk_delivery_part_modifier PRIMARY KEY (year, did, dpnr, mnr), CONSTRAINT pk_delivery_part_modifier PRIMARY KEY (year, did, dpnr, modid),
CONSTRAINT fk_delivery_part_modifier_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr) CONSTRAINT fk_delivery_part_modifier_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE CASCADE, ON DELETE CASCADE,
CONSTRAINT fk_delivery_part_modifier_modifier FOREIGN KEY (year, mnr) REFERENCES modifier (year, mnr) CONSTRAINT fk_delivery_part_modifier_modifier FOREIGN KEY (year, modid) REFERENCES modifier (year, modid)
ON UPDATE CASCADE ON UPDATE CASCADE
ON DELETE RESTRICT ON DELETE RESTRICT
) STRICT; ) STRICT;

View File

@ -4,3 +4,62 @@ SELECT plz, p.dest AS bestimmungsort, g.name AS gemeinde, g.gkz, o.name AS ort,
FROM AT_gem g FROM AT_gem g
JOIN AT_ort o ON o.gkz = g.gkz JOIN AT_ort o ON o.gkz = g.gkz
JOIN AT_plz_dest p ON p.okz = o.okz; JOIN AT_plz_dest p ON p.okz = o.okz;
CREATE VIEW v_delivery AS
SELECT p.year, p.did, p.dpnr,
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
m.mgnr, m.family_name, m.given_name,
p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr,
GROUP_CONCAT(DISTINCT a.attrid) as attributes, GROUP_CONCAT(DISTINCT o.modid) as modifiers,
d.comment, p.comment as part_comment
FROM delivery_part p
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN delivery_part_attribute a ON (a.year, a.did, a.dpnr) = (p.year, p.did, p.dpnr)
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
GROUP BY p.year, p.did, p.dpnr
ORDER BY p.year, p.did, p.dpnr;
CREATE VIEW v_stat_season AS
SELECT year,
SUM(weight) AS sum,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year
ORDER BY year;
CREATE VIEW v_stat_sort AS
SELECT year, sortid,
SUM(weight) as sum,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid
ORDER BY year, sortid;
CREATE VIEW v_stat_attr AS
SELECT year, attributes,
SUM(weight) as sum,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, attributes
ORDER BY year, LENGTH(attributes) DESC, attributes;
CREATE VIEW v_stat_sort_attr AS
SELECT year, sortid, attributes,
SUM(weight) as sum,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid, attributes
ORDER BY year, sortid, LENGTH(attributes) DESC, attributes;

View File

@ -14,8 +14,8 @@ import csv
DIR: str DIR: str
TABLES = ['branch', 'wb_gl', 'wb_kg', 'wb_rd', 'wine_attribute', 'wine_cultivation', TABLES = ['branch', 'wb_gl', 'wb_kg', 'wb_rd', 'wine_attribute', 'wine_cultivation',
'member', 'member_billing_address', 'contract', 'area_commitment', 'member', 'member_billing_address', 'contract', 'area_commitment', 'area_commitment_attribute',
'season', 'modifier', 'delivery', 'delivery_part', 'delivery_part_modifier', ] 'season', 'modifier', 'delivery', 'delivery_part', 'delivery_part_attribute', 'delivery_part_modifier', ]
# 'payment_variant', 'delivery_payment', 'member_payment'] # 'payment_variant', 'delivery_payment', 'member_payment']

View File

@ -331,6 +331,7 @@ def migrate_attributes(in_dir: str, out_dir: str) -> None:
f.write(csv.format_row(a['SANR'], a['Attribut'], int(a['KgProHa']))) f.write(csv.format_row(a['SANR'], a['Attribut'], int(a['KgProHa'])))
if WG == 'MATZEN': if WG == 'MATZEN':
f.write(csv.format_row('M', 'Matzen', 10000)) f.write(csv.format_row('M', 'Matzen', 10000))
f.write(csv.format_row('HU', 'Huber', 10000))
def migrate_cultivations(in_dir: str, out_dir: str) -> None: def migrate_cultivations(in_dir: str, out_dir: str) -> None:
@ -649,9 +650,12 @@ def migrate_contracts(in_dir: str, out_dir: str) -> None:
invalid(mgnr, 'GstNr.', f'{kgnr:05}-{nr_str}') invalid(mgnr, 'GstNr.', f'{kgnr:05}-{nr_str}')
return [] return []
with open(f'{out_dir}/contract.csv', 'w+') as f_c, open(f'{out_dir}/area_commitment.csv', 'w+') as f_fb: with open(f'{out_dir}/contract.csv', 'w+') as f_c, \
open(f'{out_dir}/area_commitment.csv', 'w+') as f_fb, \
open(f'{out_dir}/area_commitment_attribute.csv', 'w+') as f_attr:
f_c.write('vnr;mgnr;year_from;year_to\n') f_c.write('vnr;mgnr;year_from;year_to\n')
f_fb.write('vnr;kgnr;gstnr;rdnr;area;sortid;attrid;cultid\n') f_fb.write('vnr;kgnr;gstnr;rdnr;area;sortid;cultid\n')
f_attr.write('vnr;kgnr;gstnr;attrid\n')
for fb in csv.parse_dict(f'{in_dir}/TFlaechenbindungen.csv'): for fb in csv.parse_dict(f'{in_dir}/TFlaechenbindungen.csv'):
if fb['Von'] is None and fb['Bis'] is None: if fb['Von'] is None and fb['Bis'] is None:
@ -682,8 +686,10 @@ def migrate_contracts(in_dir: str, out_dir: str) -> None:
a = area - gst_area * (len(gstnrs) - 1) if i == 0 else gst_area a = area - gst_area * (len(gstnrs) - 1) if i == 0 else gst_area
rdnr = REED_MAP[fb['RNR']][1] if fb['RNR'] else None rdnr = REED_MAP[fb['RNR']][1] if fb['RNR'] else None
f_fb.write(csv.format_row( f_fb.write(csv.format_row(
vnr, kgnr, gstnr, rdnr, a, fb['SNR'], fb['SANR'], CULTIVATION_MAP[fb['BANR']] vnr, kgnr, gstnr, rdnr, a, fb['SNR'], CULTIVATION_MAP[fb['BANR']]
)) ))
if fb['SANR']:
f_attr.write(csv.format_row(vnr, kgnr, gstnr, fb['SANR']))
def fix_deliveries(deliveries: Iterable[Dict[str, Any]]) -> Iterable[Tuple[str, List[int], datetime.date]]: def fix_deliveries(deliveries: Iterable[Dict[str, Any]]) -> Iterable[Tuple[str, List[int], datetime.date]]:
@ -744,15 +750,24 @@ def migrate_deliveries(in_dir: str, out_dir: str) -> None:
seasons = {} seasons = {}
branches = {} branches = {}
for mod in modifiers.values():
name: str = mod['Bezeichnung']
if WG == 'MATZEN':
mod['id'] = name[-1] if name.startswith('Klasse') else 'TB' if name == 'Treuebonus' else 'PZS'
else:
raise NotImplementedError()
deliveries = list(csv.parse_dict(f'{in_dir}/TLieferungen.csv')) deliveries = list(csv.parse_dict(f'{in_dir}/TLieferungen.csv'))
delivery_dict = {d['LINR']: d for d in deliveries} delivery_dict = {d['LINR']: d for d in deliveries}
fixed = fix_deliveries(deliveries) fixed = fix_deliveries(deliveries)
with open(f'{out_dir}/delivery.csv', 'w+') as f_delivery, \ with open(f'{out_dir}/delivery.csv', 'w+') as f_delivery, \
open(f'{out_dir}/delivery_part.csv', 'w+') as f_part: open(f'{out_dir}/delivery_part.csv', 'w+') as f_part, \
open(f'{out_dir}/delivery_part_attribute.csv', 'w+') as f_attr:
f_delivery.write('year;did;date;time;zwstid;lnr;lsnr;mgnr;comment\n') f_delivery.write('year;did;date;time;zwstid;lnr;lsnr;mgnr;comment\n')
f_part.write('year;did;dpnr;sortid;attrid;weight;kmw;qualid;hkid;kgnr;rdnr;gerebelt;manual_weighing;spl_check;' f_part.write('year;did;dpnr;sortid;weight;kmw;qualid;hkid;kgnr;rdnr;gerebelt;manual_weighing;spl_check;'
'hand_picked;lesemaschine;temperature;acid;scale_id;weighing_id;comment\n') 'hand_picked;lesewagen;temperature;acid;scale_id;weighing_id;comment\n')
f_attr.write('year;did;dpnr;attrid\n')
for lsnr, linrs, date in fixed: for lsnr, linrs, date in fixed:
if date.year not in seasons: if date.year not in seasons:
@ -778,6 +793,7 @@ def migrate_deliveries(in_dir: str, out_dir: str) -> None:
lnr = branches[znr][date] lnr = branches[znr][date]
comments = [] comments = []
attributes = set()
for dpnr, linr in enumerate(linrs, start=1): for dpnr, linr in enumerate(linrs, start=1):
d = delivery_dict[linr] d = delivery_dict[linr]
delivery_map[linr] = (date.year, snr, dpnr) delivery_map[linr] = (date.year, snr, dpnr)
@ -786,28 +802,32 @@ def migrate_deliveries(in_dir: str, out_dir: str) -> None:
oe = d['OechsleOriginal'] or d['Oechsle'] oe = d['OechsleOriginal'] or d['Oechsle']
kmw = GRADATION_MAP[oe] kmw = GRADATION_MAP[oe]
sortid, attrid = d['SNR'].upper(), d['SANR'] or None sortid = d['SNR'].upper()
if d['SANR']:
attributes.add(d['SANR'])
if len(sortid) != 2: if len(sortid) != 2:
attrid = sortid[-1] attributes.add(sortid[2:])
sortid = sortid[:2] sortid = sortid[:2]
if WG == 'MATZEN': if WG == 'MATZEN':
if sortid == 'HU': if sortid == 'HU':
# Gr.Veltliner (Huber) # Gr.Veltliner (Huber)
sortid = 'GV' sortid = 'GV'
attributes.add('HU')
elif sortid == 'SV': elif sortid == 'SV':
# FIXME probably Sortenverschnitt? # FIXME probably Sortenverschnitt?
sortid = 'SW' sortid = 'SW'
elif sortid == 'WC': elif sortid == 'WC':
# WEIßBURGUNDER/CHARDONNAY # WEIBURGUNDER/CHARDONNAY
sortid = 'SW' sortid = 'SW'
if attrid == 'H': if 'H' in attributes:
attrid = 'HK' attributes.remove('H')
elif attrid == 'W': attributes.add('HK')
attrid = None if 'W' in attributes:
attributes.remove('W')
if d['SNR'] != sortid: if d['SNR'] != sortid:
print(f'{d["SNR"]}/{d["SANR"]} -> {sortid}/{attrid}') print(f'{d["SNR"]}/{d["SANR"]} -> {sortid}/{attributes}')
kgnr, rdnr = None, None kgnr, rdnr = None, None
if d['GNR']: if d['GNR']:
@ -851,32 +871,34 @@ def migrate_deliveries(in_dir: str, out_dir: str) -> None:
comments.append(comment) comments.append(comment)
f_part.write(csv.format_row( f_part.write(csv.format_row(
date.year, snr, dpnr, sortid, attrid, int(d['Gewicht']), kmw, QUAL_MAP[d['QSNR']], HKID, kgnr, rdnr, date.year, snr, dpnr, sortid, int(d['Gewicht']), kmw, QUAL_MAP[d['QSNR']], HKID, kgnr, rdnr,
d['Gerebelt'] or False, d['Handwiegung'] or False, d['Spaetlese-Ueberpruefung'] or False, d['Gerebelt'] or False, d['Handwiegung'] or False, d['Spaetlese-Ueberpruefung'] or False,
hand, lesemaschine, d['Temperatur'], acid, scale_id, weighing_id, comment hand, lesemaschine, d['Temperatur'], acid, scale_id, weighing_id, comment
)) ))
for attrid in attributes:
f_attr.write(csv.format_row(date.year, snr, dpnr, attrid))
f_delivery.write(csv.format_row( f_delivery.write(csv.format_row(
date.year, snr, date, d['Uhrzeit'], BRANCH_MAP[d['ZNR']], lnr, lsnr, d['MGNR'], date.year, snr, date, d['Uhrzeit'], BRANCH_MAP[d['ZNR']], lnr, lsnr, d['MGNR'],
'; '.join(comments) or None '; '.join(comments) or None
)) ))
with open(f'{out_dir}/delivery_part_modifier.csv', 'w+') as f_part_mod: with open(f'{out_dir}/delivery_part_modifier.csv', 'w+') as f_part_mod:
f_part_mod.write('year;did;dpnr;mnr\n') f_part_mod.write('year;did;dpnr;modid\n')
for m in csv.parse_dict(f'{in_dir}/TLieferungAbschlag.csv'): for m in csv.parse_dict(f'{in_dir}/TLieferungAbschlag.csv'):
if m['LINR'] not in delivery_map: if m['LINR'] not in delivery_map:
continue continue
nid = delivery_map[m['LINR']] nid = delivery_map[m['LINR']]
f_part_mod.write(csv.format_row(nid[0], nid[1], nid[2], m['ASNR'])) f_part_mod.write(csv.format_row(nid[0], nid[1], nid[2], modifiers[m['ASNR']]['id']))
with open(f'{out_dir}/season.csv', 'w+') as f_season, open(f'{out_dir}/modifier.csv', 'w+') as f_mod: with open(f'{out_dir}/season.csv', 'w+') as f_season, open(f'{out_dir}/modifier.csv', 'w+') as f_mod:
f_season.write('year;currency;precision;start_date;end_date\n') f_season.write('year;currency;precision;start_date;end_date\n')
f_mod.write('year;mnr;name;abs;rel;standard;quick_select\n') f_mod.write('year;modid;name;abs;rel;standard;quick_select\n')
for y, s in seasons.items(): for y, s in seasons.items():
f_season.write(csv.format_row(y, s['currency'], s['precision'], s['start'], s['end'])) f_season.write(csv.format_row(y, s['currency'], s['precision'], s['start'], s['end']))
for m in modifiers.values(): for m in modifiers.values():
abs_v = int(m['AZAS'] * pow(10, s['precision'])) if m['AZAS'] is not None else None abs_v = int(m['AZAS'] * pow(10, s['precision'])) if m['AZAS'] is not None else None
f_mod.write(csv.format_row( f_mod.write(csv.format_row(
y, m['ASNR'], m['Bezeichnung'], abs_v, m['AZASProzent'], m['Standard'], m['Schnellauswahl'] y, m['id'], m['Bezeichnung'], abs_v, m['AZASProzent'], m['Standard'], m['Schnellauswahl']
)) ))