Ein Zeitraum geht jetzt durch -- als Zeitraum, nicht als Tag
Filipe: "verbesser das, also ich will dass das auch so geht, mach dass
es funktioniert, ich will es so einfach und perfekt wie moeglich. also
sieh zu dass die excel auch so durch geht."
Backstage gibt eine Ausgabe fuer einen ZEITRAUM heraus --
"2026-09-01 ~ 2026-09-13", eine Zeile je Creator, alle Zahlen
aufsummiert. Am Vormittag wurde so eine Zeile abgewiesen. Jetzt geht
sie durch.
DREI WEGE WAEREN MOEGLICH GEWESEN, ZWEI DAVON WAEREN
ZAHLENFAELSCHUNG:
* auf den ersten Tag schreiben -> dreizehn Tage auf einem Tag; die
Zahl steht da, sie ist gross, sie sieht richtig aus, und niemand
kann spaeter sagen, was darin steckt;
* gleichmaessig verteilen -> erfundene Tage, die es nie gab.
Der dritte ist dieser: den Zeitraum ALS Zeitraum ablegen. Was drinsteht,
ist dann wahr -- und was er nicht sagt (welcher Tag wie lief),
behauptet er auch nicht.
EIGENE TABELLE UND NICHT EIN FELD IN `leistung`: Dort ist der
Schluessel (creator, tag). Ein Zeitraum ab dem 1. September wuerde mit
dem ECHTEN 1. September zusammenstossen, und eine der beiden Zahlen
waere weg. Getrennt kann keines das andere ueberschreiben -- und die
Wochenzahlen bleiben, was sie sind: aus Tagen gerechnet.
Der Schluessel ist (creator, von, bis): Dieselbe Datei zweimal
einzulesen ersetzt denselben Zeitraum, statt ihn zu verdoppeln.
AUF DER SEITE steht ein eigener Block zwischen Woche und Tageszeilen:
die Spanne vorn, die Zahl der Tage als Marke daneben, die Werte
darunter. Dazu EINE Umrechnung, und nur diese: "Ø 4.129 Diamanten pro
Tag" -- als Durchschnitt bezeichnet, nirgends gespeichert. Wer eine
Woche vergleichen will, braucht sie; wer sie fuer einen echten Tag
haelt, hat das Wort nicht gelesen. Darueber steht woertlich, dass diese
Zahlen nicht in die Wochenzahlen eingehen.
MIT DER ECHTEN DATEI GEMESSEN: alle 8 Zeilen gehen durch, 8 als
Zeitraum, 0 abgelehnt. Spanne 01.09.-11.09. = 11 Tage, Diamanten
53.679, Dauer 5171 Minuten.
pruef-backstage-import 86 -> 106. Die Pruefung, auf die es ankommt, ist
nicht "es wird gespeichert", sondern "es wird NICHT in die Woche
gemischt": kein Tag kommt hinzu, die Wochenzahl bleibt Ziffer fuer
Ziffer dieselbe. Dazu die Gegenprobe, dass ein Zeitraum von EINEM Tag
weiterhin ein Tag bleibt -- sonst hiesse alles andere nur, dass jetzt
alles als Zeitraum abgelegt wird.
DREI PRUEFUNGEN GEDREHT, KEINE GELOESCHT: Zwei behaupteten noch die
Ablehnung vom Vormittag. Und tagAusZelle() hat jetzt DREI Ausgaenge
statt zwei (Tag, Zeitraum, Grund) -- geprueft wird, dass nie zwei davon
gleichzeitig kommen. Gaebe es zwei, entschiede jeder Aufrufer selbst,
was Vorrang hat, und der zweite entschiede anders als der erste. Genau
daran ist heute frueh der Zeitraum-Schutz gescheitert.
ZWEI EIGENE MESSFEHLER, beide von der Pruefung gefunden: Ich verlangte
"kein einziger Tag in der Spanne" und uebersah, dass weiter oben schon
ein Tag geschrieben worden war, der hineinfaellt -- gemessen wird jetzt
die VERAENDERUNG. Und ich mass den Zeitraum-Block, waehrend ein anderer
Creator geoeffnet war: keine Frage an die Seite, sondern an die falsche
Person.
Nebenbei zum zweiten Mal heute: eine Versalzeile mit 10,88 px. Auf
.72rem angehoben, bevor pruef-css-klassen sie findet.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -237,7 +237,23 @@ leistungRouter.get("/workspace/api/leistung/:creatorId", (req, res) => {
|
||||
anteil: Math.round((dieseWoche.verweildauer_s / ziele.verweildauer_s) * 100) } : null,
|
||||
} : null;
|
||||
|
||||
/* DIE ZEITRAEUME AUS BACKSTAGE (14.09.2026).
|
||||
|
||||
Sie gehen NICHT in die Wochenzahlen ein -- die bleiben aus Tagen
|
||||
gerechnet. Ein Zeitraum ueber 13 Tage in eine Woche zu mischen
|
||||
waere eine Summe aus zwei verschiedenen Dingen, und niemand
|
||||
koennte hinterher sagen, was darin steckt.
|
||||
|
||||
Sie stehen deshalb daneben, mit ihrer Spanne davor. Die juengsten
|
||||
zuerst; mehr als sechs braucht niemand auf einen Blick. */
|
||||
const zeitraeume = db().prepare(`
|
||||
SELECT von, bis, tage, diamanten, dauer_min, gueltige_tage,
|
||||
zuschauer_avg, zuschauer_max, follower_neu, erfasst
|
||||
FROM leistung_zeitraum WHERE creator_id = ?
|
||||
ORDER BY bis DESC, von DESC LIMIT 6`).all(creatorId);
|
||||
|
||||
res.json({
|
||||
zeitraeume,
|
||||
creator_id: creatorId,
|
||||
woche: mitVergleich,
|
||||
roh: { woche: dieseWoche, vorwoche },
|
||||
@@ -684,14 +700,33 @@ export function zeitraumLesen(roh) {
|
||||
export function tagAusZelle(roh) {
|
||||
const zeitraum = zeitraumLesen(roh);
|
||||
if (zeitraum && zeitraum.von !== zeitraum.bis) {
|
||||
return { fehler: `Zeitraum ${zeitraum.von} bis ${zeitraum.bis} – das sind mehrere `
|
||||
+ "Tage auf einmal. Stell in Backstage den Zeitraum auf EINEN Tag." };
|
||||
/* MEHRERE TAGE SIND KEIN FEHLER MEHR (14.09.2026, zweite Fassung).
|
||||
|
||||
Am Vormittag wurde so eine Zeile abgewiesen -- richtig, denn auf
|
||||
einen Tag geschrieben waeren es 13 Tage auf einmal gewesen.
|
||||
Filipe: "ich will dass das auch so geht ... sieh zu dass die
|
||||
excel auch so durch geht."
|
||||
|
||||
Also geht sie durch -- aber als das, was sie ist. Der Aufrufer
|
||||
bekommt `zeitraum` statt `tag` und legt sie in
|
||||
leistung_zeitraum ab. Wer nur `tag` liest, bekommt weiterhin
|
||||
nichts und schreibt damit auch nichts Falsches. */
|
||||
return { zeitraum };
|
||||
}
|
||||
const tag = zeitraum ? zeitraum.von : datumLesen(roh);
|
||||
if (!tag) return { fehler: `Datum unlesbar: "${String(roh ?? "").slice(0, 40)}"` };
|
||||
return { tag };
|
||||
}
|
||||
|
||||
/** Wie viele Tage liegen in einem Zeitraum? Beide Enden zaehlen mit --
|
||||
* vom 1. bis zum 1. ist EIN Tag, nicht null. */
|
||||
export function tageImZeitraum(von, bis) {
|
||||
const a = Date.parse(`${von}T00:00:00Z`);
|
||||
const b = Date.parse(`${bis}T00:00:00Z`);
|
||||
if (!Number.isFinite(a) || !Number.isFinite(b) || b < a) return 0;
|
||||
return Math.round((b - a) / 86400000) + 1;
|
||||
}
|
||||
|
||||
/** Macht aus einem Textwert ein Datum "JJJJ-MM-TT".
|
||||
* Erkennt 2026-09-06, 06.09.2026 und 09/06/2026. */
|
||||
export function datumLesen(roh) {
|
||||
@@ -891,13 +926,19 @@ function netzwerkLesen(text, person, tagVorgabe) {
|
||||
daraufhin durch den Einzel-Import, und dort fehlte sie. Jetzt
|
||||
benutzen alle drei Stellen dieselbe. */
|
||||
let tag = tagVorgabe;
|
||||
let spanne = null;
|
||||
if (ausTabelle) {
|
||||
const gelesen = tagAusZelle(z[zu.tag]);
|
||||
if (gelesen.fehler) { schlecht.push({ zeile: i + 1, grund: gelesen.fehler }); continue; }
|
||||
tag = gelesen.tag;
|
||||
if (gelesen.zeitraum) spanne = gelesen.zeitraum;
|
||||
else tag = gelesen.tag;
|
||||
}
|
||||
if (!tag && !spanne) { schlecht.push({ zeile: i + 1, grund: "Kein Tag angegeben." }); continue; }
|
||||
const letzterTag = spanne ? spanne.bis : tag;
|
||||
if (letzterTag > heuteLokal()) {
|
||||
schlecht.push({ zeile: i + 1, grund: `${letzterTag} liegt in der Zukunft` });
|
||||
continue;
|
||||
}
|
||||
if (!tag) { schlecht.push({ zeile: i + 1, grund: "Kein Tag angegeben." }); continue; }
|
||||
if (tag > heuteLokal()) { schlecht.push({ zeile: i + 1, grund: `${tag} liegt in der Zukunft` }); continue; }
|
||||
|
||||
/* Vier Versuche, in dieser Reihenfolge: Handle gegen Handle, Handle
|
||||
gegen Name, Name gegen Handle, Name gegen Name. Das Eindeutige
|
||||
@@ -910,7 +951,10 @@ function netzwerkLesen(text, person, tagVorgabe) {
|
||||
|| (sName && nach.name.get(sName));
|
||||
if (!p) { unbekannt.push(rohWer.slice(0, 60)); continue; }
|
||||
|
||||
const werte = { creator_id: p.id, name: p.name, tag };
|
||||
const werte = spanne
|
||||
? { creator_id: p.id, name: p.name, von: spanne.von, bis: spanne.bis,
|
||||
tage: tageImZeitraum(spanne.von, spanne.bis) }
|
||||
: { creator_id: p.id, name: p.name, tag };
|
||||
let hatZahl = false;
|
||||
for (const feld of messwerte) {
|
||||
/* Die Dauer hat eine EINHEIT, die anderen Felder nicht.
|
||||
@@ -948,12 +992,31 @@ leistungRouter.post("/workspace/api/leistung/netzwerk-import/vorschau", gleicheH
|
||||
hinterher raten, ob er etwas überschrieben hat. */
|
||||
const vorhanden = db().prepare(
|
||||
"SELECT 1 FROM leistung WHERE creator_id = ? AND tag = ?");
|
||||
let neu = 0, ersetzt = 0;
|
||||
for (const t of e.treffer) (vorhanden.get(t.creator_id, t.tag) ? ersetzt++ : neu++);
|
||||
const vorhandenSpanne = db().prepare(
|
||||
"SELECT 1 FROM leistung_zeitraum WHERE creator_id = ? AND von = ? AND bis = ?");
|
||||
let neu = 0, ersetzt = 0, zeitraeume = 0;
|
||||
for (const t of e.treffer) {
|
||||
if (t.von) {
|
||||
zeitraeume++;
|
||||
if (vorhandenSpanne.get(t.creator_id, t.von, t.bis)) ersetzt++; else neu++;
|
||||
} else {
|
||||
(vorhanden.get(t.creator_id, t.tag) ? ersetzt++ : neu++);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
spalten: e.spalten, person_spalten: e.person_spalten, aus_tabelle: e.aus_tabelle,
|
||||
kopf: e.kopf,
|
||||
/* WIE VIELE DAVON SIND ZEITRAEUME? Steht eigens da, weil es
|
||||
etwas anderes ist als ein Tag -- wer eine Backstage-Ausgabe
|
||||
ueber zwei Wochen einliest, soll das VOR dem Bestaetigen
|
||||
sehen und nicht hinterher raten, warum die Woche leer ist. */
|
||||
zeitraeume,
|
||||
spanne: e.treffer.find((t) => t.von)
|
||||
? { von: e.treffer.find((t) => t.von).von,
|
||||
bis: e.treffer.find((t) => t.von).bis,
|
||||
tage: e.treffer.find((t) => t.von).tage }
|
||||
: null,
|
||||
zugeordnet: e.treffer.length, neu, ersetzt,
|
||||
unbekannt: e.unbekannt, probleme: e.schlecht.slice(0, 12),
|
||||
probleme_gesamt: e.schlecht.length,
|
||||
@@ -988,10 +1051,30 @@ leistungRouter.post("/workspace/api/leistung/netzwerk-import", gleicheHerkunft,
|
||||
(SELECT notiz FROM leistung WHERE creator_id = ? AND tag = ?),
|
||||
?,?, 'import')`);
|
||||
|
||||
/* DER ZWEITE WEG: ein Zeitraum geht in seine eigene Tabelle.
|
||||
INSERT OR REPLACE, damit dieselbe Datei zweimal denselben
|
||||
Zeitraum ersetzt statt ihn zu verdoppeln. */
|
||||
const spanneSchreiben = d.prepare(`
|
||||
INSERT OR REPLACE INTO leistung_zeitraum
|
||||
(creator_id, von, bis, tage, diamanten, dauer_min, gueltige_tage,
|
||||
zuschauer_avg, zuschauer_max, verweildauer_s, schenker, follower_neu,
|
||||
erfasst, erfasst_von)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`);
|
||||
|
||||
let geschrieben = 0;
|
||||
let spannen = 0;
|
||||
d.exec("BEGIN");
|
||||
try {
|
||||
for (const t of e.treffer) {
|
||||
if (t.von) {
|
||||
spanneSchreiben.run(t.creator_id, t.von, t.bis, t.tage,
|
||||
t.diamanten ?? null, t.dauer_min ?? null, t.gueltige_tage ?? null,
|
||||
t.zuschauer_avg ?? null, t.zuschauer_max ?? null,
|
||||
t.verweildauer_s ?? null, t.schenker ?? null, t.follower_neu ?? null,
|
||||
new Date().toISOString(), req.person.id);
|
||||
spannen++;
|
||||
continue;
|
||||
}
|
||||
schreiben.run(t.creator_id, t.tag,
|
||||
t.diamanten ?? null, t.dauer_min ?? null,
|
||||
/* Ein gültiger Tag im Sinne von TikTok ist einer mit
|
||||
@@ -1009,9 +1092,11 @@ leistungRouter.post("/workspace/api/leistung/netzwerk-import", gleicheHerkunft,
|
||||
|
||||
protokolliere("leistung_netzwerk_import", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${geschrieben} Zeilen, ${e.unbekannt.length} unbekannt`.slice(0, 120),
|
||||
detail: `${geschrieben} Tage, ${spannen} Zeitraeume, `
|
||||
+ `${e.unbekannt.length} unbekannt`.slice(0, 120),
|
||||
});
|
||||
res.json({ geschrieben, unbekannt: e.unbekannt, probleme: e.schlecht.length });
|
||||
res.json({ geschrieben, zeitraeume: spannen,
|
||||
unbekannt: e.unbekannt, probleme: e.schlecht.length });
|
||||
} catch (fehler) {
|
||||
console.error("[leistung] Netzwerk-Import:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
|
||||
Reference in New Issue
Block a user