astrololo: ekrany i eksport jako moduły, katalog z rejestracji (3/5)
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4m46s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m32s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4m46s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m32s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
Trzeci z pięciu kroków. Zmieniłem jego zakres wobec planu i warto wiedzieć dlaczego: pierwotnie miała to być deduplikacja pięciu kopii link_crypto.py, ale po zrobieniu PR 2 widać, że astroklienta blokuje co innego — ekrany, których nie ma mieć, siedzą wewnątrz jednego main.py. Deduplikacja kryptografii jest realnym długiem, ale niczego nie blokuje. PODZIAŁ. main.py (1023 linie) rozpadł się na podstawa.py (wspólne obiekty i pomocnicy), jedenaście modułów w app/ekrany/ i main.py, który jest już samym ZŁOŻENIEM: lista importów JEST definicją produktu. Podział zrobiony mechanicznie, z osobnym sprawdzeniem, że żadna sekcja nie wołała pomocnika z innej (nie wołała). KATALOG Z REJESTRACJI. Dotąd wszystkie funkcje były wypisane w features.py, więc obraz produktu, który części z nich nie ma, i tak niósł ich nazwy — spis funkcji, których nie ma jak włączyć. Teraz ekran zgłasza siebie, swoje trasy i swoje zasoby przy imporcie własnego modułu, a features.py nie wymienia ani jednego ekranu. Kolejność w nawigacji jest jawna (`kolejnosc`), żeby nie rządziła nią kolejność importów. To samo dotyczy nawigacji administratora: odsyłacz do ekranu kont był wpisany na sztywno w base.html, więc w węższym produkcie zostawał martwy link i nazwa ekranu, którego nie ma. EKSPORT JAKO MODUŁ. Zgodnie z ustaleniem eksport jest funkcją administracyjną, więc musi dać się usunąć. app/moduly/eksport/ zabiera arkusz, trasę PDF-a i akcję formularza. „Można, ale nie temu kontu" i „nie ma takiej możliwości" to dwie różne gwarancje, a eksport wynosi najwięcej treści baz naraz. MOST ODKRYWA MODUŁY. Skoro modułów jest więcej niż jeden, most nie może ich znać z nazwy — nazwa nieobecnego modułu jechałaby do obrazu, w którym go nie ma. Przechodzi więc po podkatalogach app/moduly/ i pyta każdy, co wnosi. Katalog generowania przeniesiony z app/dodatki na app/moduly/dodatki. ZNALEZIONE PRZY OKAZJI. Po wydzieleniu eksportu okazało się, że jego ścieżki SZCZĘŚLIWEJ nie sprawdzał żaden test — badano wyłącznie odmowę dla konta bez uprawnienia. Moduł dostaje zależności z wywołania montującego, więc brak jednej z nich wyszedłby dopiero przy pierwszym kliknięciu. Dopisany test funkcjonalny (realny arkusz, sprawdzany aż do nagłówka ZIP-a) i brakujące zależności. Cztery komentarze w plikach współdzielonych wymieniały zakładkę „Skompiluj", w tym wheelzoom.js ze wzmianką o „przyszłej zakładce" — to samo zgłoszenie, które audyt podnosił wcześniej. TEST ZŁOŻENIA. Buduje węższy produkt NAPRAWDĘ: kopiuje drzewo, usuwa cztery ekrany i oba moduły, uruchamia aplikację w OSOBNYM PROCESIE (importy są zapamiętywane, więc sprawdzanie tego w procesie, który moduł już zaimportował, dawałoby wynik fałszywie pozytywny) i sprawdza, że wstaje, że zachowane ekrany oddają 200, że usunięte oddają 404 (nie 403 i nie 500), że katalog opisuje ten obraz, i że w nawigacji nie ma martwych odsyłaczy. Napisałem najpierw ostrzejszy test — „nazwa ekranu nie pada poza jego modułem" — i go wyrzuciłem: zgłaszał wzmianki o Horoskopie w plikach współdzielonych, choć astroklient Horoskop MA. Ślad ma znaczenie wyłącznie wobec konkretnego złożenia, więc sprawdzenie należy do produktu, nie do mechanizmu. Testy: presentation 368, logic 342, data 37, render 41. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Zapamiętana interpretacja natalna (PRE-23).
|
||||
//
|
||||
// Odpowiednik predictions.js, tylko że natalna jest JEDNA — dotyczy momentu
|
||||
// urodzenia, nie wybranego okresu. Ponowne wygenerowanie podmienia poprzednią.
|
||||
//
|
||||
// Po co magazyn: zakładka „Skompiluj" składa raport z trzech kawałków liczonych
|
||||
// w różnych miejscach (horoskop, interpretacja natalna, predykcje okresowe).
|
||||
// Bez zapamiętania trzeba by je generować od nowa na jednej stronie.
|
||||
//
|
||||
// localStorage, tak jak reszta stanu (PRE-21/22) — serwer zostaje bezstanowy.
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var KEY = 'astrololo.natal.v1';
|
||||
|
||||
function read() {
|
||||
try {
|
||||
var v = JSON.parse(localStorage.getItem(KEY) || 'null');
|
||||
return (v && typeof v === 'object' && v.text) ? v : null;
|
||||
} catch (e) {
|
||||
return null; // uszkodzony wpis nie może zablokować strony
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
try { localStorage.removeItem(KEY); } catch (e) { /* trudno */ }
|
||||
}
|
||||
|
||||
function val(sel) {
|
||||
var el = document.querySelector(sel);
|
||||
return el ? el.value.trim() : '';
|
||||
}
|
||||
|
||||
function save() {
|
||||
var text = val('#horoscopeText');
|
||||
if (!text) return false; // nie ma czego zapamiętać
|
||||
var entry = {
|
||||
text: text,
|
||||
person: val('input[name=person]'),
|
||||
date: val('input[name=date]'),
|
||||
time: val('input[name=time]'),
|
||||
tz_offset: val('input[name=tz_offset]'),
|
||||
lat: val('input[name=lat]'),
|
||||
lon: val('input[name=lon]'),
|
||||
saved_at: new Date().toISOString()
|
||||
};
|
||||
try {
|
||||
localStorage.setItem(KEY, JSON.stringify(entry));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false; // przepełniony magazyn — zgłaszamy wyżej
|
||||
}
|
||||
}
|
||||
|
||||
function note(msg) {
|
||||
var el = document.getElementById('natalNote');
|
||||
if (el) el.textContent = msg;
|
||||
}
|
||||
|
||||
function saveCurrent() {
|
||||
var text = val('#horoscopeText');
|
||||
if (!text) return;
|
||||
if (save()) {
|
||||
note('Interpretacja natalna zapamiętana — trafi do raportu (zakładka Skompiluj).');
|
||||
} else {
|
||||
// Interpretacje bywają długie; cicha strata byłaby najgorsza.
|
||||
note('Nie udało się zapamiętać — magazyn przeglądarki pełny. '
|
||||
+ 'Usuń starsze predykcje i spróbuj ponownie.');
|
||||
}
|
||||
}
|
||||
|
||||
function ready(fn) {
|
||||
if (document.readyState !== 'loading') fn();
|
||||
else document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
|
||||
ready(function () {
|
||||
if (!document.getElementById('natalNote')) return; // nie ta zakładka
|
||||
|
||||
// 1) interpretacja policzona przez okno postępu (strumień)
|
||||
document.addEventListener('astrololo:horoscope', function (e) {
|
||||
if (!e.detail || e.detail.profile === 'natal') saveCurrent();
|
||||
});
|
||||
|
||||
// 2) wariant bez strumienia: strona wróciła z gotowym wynikiem po zwykłym
|
||||
// POST. Zapis nadpisuje jeden slot, więc odświeżenie niczego nie mnoży.
|
||||
if (document.querySelector('#horoscopeText')) saveCurrent();
|
||||
|
||||
var status = read();
|
||||
if (status) {
|
||||
note('W raporcie jest zapamiętana interpretacja natalna z '
|
||||
+ String(status.saved_at).slice(0, 10) + '.');
|
||||
}
|
||||
});
|
||||
|
||||
window.astrololoNatal = { read: read, clear: clear, key: KEY };
|
||||
})();
|
||||
Reference in New Issue
Block a user