Spis treści

Gabinet dentystyczny - zapis zmian i podsumowanie

Dziś wpis o ostatnim etapie implementacji Gabinetu oraz podsumowanie całego eksperymentu. Jak teraz wygląda aplikacja? Czego się nauczyłam? Zapraszam.

Wysyłanie zmian

Dziś piszę kod, który zamieni klikanie na diagramie w zapisy do bazy danych na serwerze.

Wysyłanie zdarzeń

Mój rysowacz diagramu (TeethDrawer) jest również kontrolerem (w znaczeniu MVC) - reaguje na zdarzenia kliknięć (i koloruje odpowiednie fragmenty SVG). Chcę, aby każde kliknięcie - zmiana stanu - była wysyłana na serwer, ale nie chcę, aby wysyłaniem zajmował się rysowacz, ponieważ on powinien zajmować się jedynie rysowaniem. Wysyłaniem żądań na serwer będzie zajmował się wysyłacz (Sender).

W jaki sposób rysowacz powiadomi wysyłacza, że dokonała się zmiana? Rysowacz i wysyłacz wspólnie mają dostęp do obiektu emitera zdarzeń, który może “emitować” zdarzenia oraz rejestrować callbacki do obsługi tych zdarzeń. Rysowacz będzie wysyłał zdarzenia update używając emitera, a wysyłacz zarejestruje się w emiterze jako obserwator zdarzeń update.

Reakcja na kliknięcie w rysowaczu obsługiwana jest przez metodę onclick, która wywołuje updateState:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  onclick(v) {
    return (e) => this.updateState(v)  
  }

  updateState(v) {
    let {toothside, toothnum} = v
    let switcher = this.teeth.get(toothnum)
    if (v.i !== undefined) {
      stateid = switcher.updatePart(toothside)
    } else {
      stateid = switcher.updateWhole()
    }

    this.es.emit("update", 
      {toothnum, stateid, toothside})
  }

Klasa Sender

Klasa Sender otrzymuje w konstruktorze obiekt es - emiter zdarzeń - w którym rejestruje ona swoją metodę sendUpdate. Metoda ta zostanie wywołana (z parametrami przekazanymi przez stronę wysyłającą event) w reakcji na event update i spodziewa się mapy {toothnum, stateid, toothside}:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Sender {
  constructor(pid, vid, es) {
    this.pid = pid
    this.vid = vid
    this.es = es
    es.on("update", this.sendUpdate)
  }
  sendUpdate(data) {
    console.log("sendUpdate: data = ", data)
    let {toothnum, stateid, toothside} = data
    (async () => {
      const rawResponse = await fetch('/api/visits/%{this.vid}/add', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        VisitId: this.vid, 
        PatientId: this.pid,
        StateId: stateid,
        ToothNum: toothnum,
        Toothside: toothside})
    });
    const content = await rawResponse.json();
    console.log(content)
    })();
  }
}

Kompatybilność

Nie używam żadnej biblioteki do wysyłania żądań HTTP. Korzystam z funkcji fetch WebAPI (patrz: Fetch API). Wszyscy dentyści chcący korzystać z mojego programu powinni zainstalować sobie porządną przeglądarkę, bo IE tej funkcji nie wspiera.

Aktualizacja

Dziś rano napisałam kod, który aktualizuje diagram - rysuje listę zmian aż do bieżącej wizyty (włącznie). Dzięki temu mam już przepływ danych w obie strony: klikanie na diagramie jest serializowane do serii rekordów w tabeli change, a po załadowaniu danych bieżącej wizyty widoczne są wszystkie zmiany nie późniejsze niż czas utworzenia wizyty.

Zapytanie do wyciągania serii zmian, które są w tej właśnie kolejności “aplikowane” do diagramu:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
with
	pv(id, patient_id, vdatetime) as (
		select id, patient_id, vdatetime
		from visit
		where visit.id = ?)
select
	c.id, c.visit_id, v.patient_id, c.state_id, c.tooth_num, c.tooth_side
from
	change c
left join
	visit v
on
	c.visit_id = v.id
join
	pv
on
	v.patient_id = pv.patient_id
where
	v.vdatetime <= pv.vdatetime
order by
	v.vdatetime asc,
	c.time asc

Screenshoty

Tak wygląda przykładowo seria wizyt Pszczółki Mai:

Seria screenów prezentujących kolejne wizyty pacjentki Pszczółki Mai w gabinecie Spokojny Ząb

Podsumowanie

Myślę, że mój program posiada już wszystkie funkcje potrzebne minimalistycznemu stomatologowi. Można w nim:

  • Dodawać i edytować pacjentów
  • Dodawać i edytować wizyty
  • Zapisywać zmiany w uzębieniu

Niestety:

  • programu nikt nie używa, więc
  • ma pewnie mnóstwo błędów
  • nie posiada żadnych testów
  • nie ma sprawdzania danych w formularzach
  • program nie udostępnia operacji undo
  • ani nie posiada żadnego mechanizmu backupowania bazy

Jednym słowem - jest to jedynie POC, częściowo działający prototyp.

Czego się nauczyłam - negatywy

  1. pisanie aplikacji frontendowych jest jednak strasznie nudne
  2. przełączanie sie między językami (które się słabo zna) jest nieprzyjemne - zmiana kontekstu (czytaj: paradygmatów, sposobów iteracji,rodzajów kolekcji, mechanizmu obsługi błędów etc) wymaga dużo energii i jest wyczerpujące
  3. używanie w projekcie bibliotek, których się dobrze nie zna jest dość karkołomne, więc dobrze najpierw sobie sprawdzić “na boczku”, jak wszystko działa
  4. niemałą trudność sprawiło mi trzymanie się wersji “minimalistycznej” i powstrzymywanie się przed wielkimi refaktoryzacjami - gdzieś tam w głowie zawsze majaczą jakieś pomysły na lepsze mechanizmy czy efektywniejsze struktury danych

Czego się nauczyłam - pozytywy

  1. posiadanie pełnej kontroli nad kodem to potężnie motywujące uczucie
  2. Go jest super szybkie, serwer wstaje w ciągu milisekund
  3. nauczyłam się podstaw Go i napisałam backend aplikacji webowej
  4. nauczyłam się biblioteki svgjs

Zakończenie

Koniec zabawy, zamykam projekt. Czas na inne wyzwania!

Jakie? O tym w następnym wpisie.

Zapraszam już niedługo.

(Źródła: dent)