diff --git a/Projektarbeit-3/Vorgehensweise.md b/Projektarbeit-3/Vorgehensweise.md index 42b09c8..c986558 100644 --- a/Projektarbeit-3/Vorgehensweise.md +++ b/Projektarbeit-3/Vorgehensweise.md @@ -1,8 +1,8 @@ # Allgemeine Einführung ## Planung -Schon bei unserem erstem virtuellem Treffen war uns klar, dass wir den Fokus auf die Datenaufbereitung setzen müssen. Denn je nachdem, wie schnell wir von unserem Frontend auf die Daten zugreifen können, ergeben sich dann letztlich alle Anforderungen in puncto Design und Funktionsumfang. +Schon bei unserem erstem virtuellem Treffen war uns klar, dass wir den Fokus auf die Datenaufbereitung setzen müssen. Denn je nachdem, wie schnell wir von unserem Frontend auf die Daten zugreifen können, ergeben sich dann letztlich alle Anforderungen in Bezug auf Design und Funktionsumfang. -Daher haben wir strickt dem Plan verfolgt, dass das Backend schon in frühen Projekttagen abgeschlossen wird und man erst mit der Frontendimplementierung beginnt, wenn das Backend bereitgestellt wurde. +Daher haben wir strikt dem Plan verfolgt, dass das Backend schon in frühen Projekttagen abgeschlossen wird und man erst dann mit der Frontendimplementierung beginnt, wenn das Backend bereitgestellt wurde. Dadurch konnte man im Frontend mit minimalen bis keinen Performanzproblemen arbeiten und eine reibungslose Implementierung ohne Abhängigkeiten war gewährleistet. @@ -11,7 +11,7 @@ Wie sich aus unserem [Projektplan](Projektarbeit-3/projektplan) ableiten lässt, * Backend: Marcel Schwarz * Frontend: Tim Herbst -Dies haben wir durchweg über das gesamte Projekt beibehalten und durch tatkräftige gegenseitige Unterstützung sind wir auch schneller an unser Projektziel gelangt. +Dies haben wir durchweg über das gesamte Projekt beibehalten und durch tatkräftige gegenseitige Unterstützung sind wir auch schnell an unser Projektziel gelangt. ## zeitlicher Rahmen Folgender zeitlicher Rahmen ergab sich nun rückblickend für unser Projekt: @@ -24,7 +24,7 @@ Folgender zeitlicher Rahmen ergab sich nun rückblickend für unser Projekt: Verantwortlicher Abschluss in KW (20/21) - + 1 Backend Einlesen und konvertieren der OpenApi-Daten in benötigtes Format (Datenbank) @@ -35,7 +35,7 @@ Folgender zeitlicher Rahmen ergab sich nun rückblickend für unser Projekt: 2 Backend - Entwurf der Api-Schnittstellen unseres eigenen Backends) + Entwurf der Api-Schnittstellen unseres eigenen Backends 0,5 Marcel Schwarz 50 @@ -67,7 +67,7 @@ Folgender zeitlicher Rahmen ergab sich nun rückblickend für unser Projekt: 6 Frontend - Abfrage der Livedaten von der London Travelers API und anzeige für jeden BikePoint + Abfrage der Livedaten von der London Travelers API und Anzeige für jeden BikePoint 0,5 Tim Herbst 52 @@ -117,15 +117,16 @@ Folgender zeitlicher Rahmen ergab sich nun rückblickend für unser Projekt: Dokumentation Projektdokumentation und Präsentation 3 (1,5 + 1,5) - Marcel Schwarz, Tim Herbst + Tim Herbst, Marcel Schwarz 3 -// TODO: Abweichung vom Projektplan farblich Kennzeichnen +// TODO: Abweichung vom Projektplan durch druchstreichen Kennzeichnen -> sonst todo löschen +// Neue Spalte mit abweichung oder nicht -Generell haben wir akkurat und präzise die Personentage geschätzt und haben bei keinem geplanten Arbeitspaket die dafür vorgesehene Zeit überschritten. -Durch die enorme Performancesteigerung im Backend mussten wir mehr Zeit im Frontend für die dynamische Interaktion zwischen Tabelle und Mini-Map (Im Dashboard) einplanen. Dadurch hat sich aber der Zeitplan des gesamten Projektes nicht verändert. +Generell haben wir akkurat und präzise die Personentage geschätzt und bei keinem geplanten Arbeitspaket die dafür vorgesehene Zeit überschritten. +Durch die enorme Performancesteigerung im Backend mussten wir mehr Zeit im Frontend für die dynamische Interaktion zwischen Tabelle und Mini-Map (im Dashboard) einplanen. Dadurch hat sich aber der Zeitplan des gesamten Projektes nicht verändert. # Backend ## Architektur @@ -137,7 +138,7 @@ graph LR NGINX -->|Static Files| FS[Dateisystem] NGINX -->|/api| BACK[Python Backend] BACK -->|Dashboard, Historische Daten| DB[(Unsere Datenbank)] - BACK -->|Bikepoints| LONDON[London API] + BACK -->|Bikepoints, Verfügbarkeit| LONDON[London API] LONDON --> LON_DB[(Live Datenbank)] end @@ -147,11 +148,11 @@ graph LR TRANSFORMER --> DB end ``` -Die Architektur sieht vor, dass alle Requests über unser eigenes Backend abgewickelt werden. Dadurch müssen keine Drittanbieterimplementierungen in das Frontend und auch alle URLs zeigen auf einen Server. Weiter müssen die API Tokens für die London API nicht im Client-Code auftauchen, was weitere Sicherheit bietet. +Die Architektur sieht vor, dass alle Requests über unser eigenes Backend abgewickelt werden. Dadurch müssen keine Drittanbieterimplementierungen in das Frontend und auch alle URLs zeigen auf einen Server. Weiter müssen die API Tokens für die London API nicht im Client-Code auftauchen, was zusätzliche Sicherheit bietet. -Zuletzt können Filterungen der London-API-Daten bereits im Backend erledigt werden, was weitere Rechen- und Netzwerklast vom Benutzer nimmt. +Zuletzt können Filterungen der London-API-Daten bereits im Backend erledigt werden, was ebenfalls Rechen- und Netzwerklast vom Benutzer nimmt. -Das Backend bietet alle Endpoints über eine Swagger UI als Dokumentation an. Dort können alle Datenbeschreibungen abgerufen werden und Anfragen können vorab getestet werden. Die Spezifikation kann auch ganz einfach als openapi.json abgerufen werden, mehr dazu aber in einem späteren Kapitel. +Das Backend bietet alle Endpoints über eine Swagger UI als Dokumentation an. Dort können alle Datenbeschreibungen abgerufen und Anfragen können vorab getestet werden. Die Spezifikation kann auch ganz einfach als openapi.json heruntergeladen werden, mehr dazu aber in einem späteren Kapitel. ## Datenaufbereitung (Theorie) Zunächst war es wichtig, welche Daten wir für unsere Diagramme überhaupt auswerten müssen. Der [OpenData Export](https://cycling.data.tfl.gov.uk/) bietet zwei Arten von großen Datensätzen an. @@ -174,9 +175,9 @@ Bis auf die Bike Id haben wir alle Spalten in unsere eigene Datenhaltung überno Zusätzlich dazu wurden noch die Accidents und die Bikepoints aus der Live API gecached, dazu aber mehr in der Implementierung. **Anmerkung:** Der Datensatz der "Usage Stats" war sehr unsauber exportiert, weshalb ein einfaches Parsing nicht möglich war. Folgende Probleme sind mehrfach beim Import aufgetreten: -* Eine Datei wurde mehrfach mit anderen Namen hochgeladen. Daraus folgt, dass die unique Constraint der "Rental Id" Spalte fehlschlägt. +* Eine Datei wurde mehrfach mit anderem Namen hochgeladen. Daraus folgt, dass die unique Constraint der "Rental Id" Spalte fehlschlägt. * Spalten waren in den CSV-Dateien nicht immer gleich benannt, oder sogar gar nicht vorhanden. Sollte dies vorkommen wurden die Spalten beim Parsing umbenannt, oder die ganze Datei wurde weggelassen. -* Weiter wurden nachträglich Bikepoint Id geändert, was es nötig gemacht hat, dass die aktuellen Bikepoint Ids über den Namen angepasst werden mussten. +* Weiter wurden nachträglich Bikepoint Ids geändert, was es nötig gemacht hat, dass die aktuellen Bikepoint Ids über den Namen angeglichen werden mussten. **Anmerkung:** Der Datensatz der Accidents war ebenfalls fehlerhaft. Dort wurde jeder Unfall doppelt zurückgegeben. @@ -197,23 +198,23 @@ app = FastAPI() def root(): return {"message": "Hello World"} ``` -Der Output ist dann `{"message": "Hello World"}` +Der Output ist dann: `{"message": "Hello World"}` Die API kann dann über den eingebauten Gunicorn Server gestartet werden. Dieser wird dann später über einen Nginx Reverse-Proxy nach außen geöffnet. -Ein anderer Grund für die Nutzung von Python war jedoch auch die Interoperabilität mit SQLite, userer gewählten Datenbank-Engine. Python nutzt hierzu die C-Implementierung (lib-sqlite) von SQLite, was bestmögliche Performance bietet. +Ein anderer Grund für die Nutzung von Python war jedoch auch die Interoperabilität mit SQLite, userer gewählten Datenbank-Technologie. Python nutzt hierzu die C-Implementierung (lib-sqlite) von SQLite, was bestmögliche Performance bietet. ## Endpunkterklärung Da unser Backend nur Daten bereitstellt, sind auch nur GET-Endpoints implementiert worden. Auch Authentifizierung gibt es nicht. Die Endpunkte sind in zwei Gruppen unterteilt, "Local" und "Upstream". Die "Local" Endpoints greifen auf die Lokale Datenbank zu, wohingegen die "Upstream" Gruppe nur eine interne Weiterleitung ist. Dies ist wichtig, sollte etwas nicht funktionieren. Denn wenn ein mit "Upstream" markierter Endpunkt nicht funktioniert, muss es nicht zwingend an unserer Implementation liegen. -Die Upstream Endpoints sind: +Die upstream Endpoints sind: 1. **/api/latest/bikepoints** Liefert alle Bikepoints in einer Liste zurück 2. **/api/latest/bikepoints/:id** Liefert einen spezifischen Bikepoint zurück (reduziert Bandbreite und Ladezeit) -Die Lokalen Endpoints sind: +Die lokalen Endpoints sind: 1. **/api/latest/accidents** Liefert alle Accidents zurück 2. **/api/latest/accidents/:year** @@ -249,10 +250,10 @@ Programmatisch sieht das wie folgt aus (AppComponent.html): ``` ### Toolbar und Footer -Da Toolbar und Footer unabhängig der URL fast den gleichen Inhalt besitzen sind diese auf Top-Level Ebene in der App-Komponente gepflegt. +Da Toolbar und Footer unabhängig der URL fast den gleichen Inhalt besitzen, sind diese auf Top-Level Ebene in der App-Komponente gepflegt. ### Router-Outlet -Das Router-Outlet kommt mit dem AngularRoutingModule und man erhält dadurch von Haus aus eine sehr einfache Möglichkeit, eigene Routen und URL-Parameter zu definieren. +Das Router-Outlet kommt mit dem `AngularRoutingModule` und man erhält dadurch von Haus aus eine sehr einfache Möglichkeit, eigene Routen und URL-Parameter zu definieren. Im `AppRoutingModule` muss lediglich ein Array mit den gewünschten Routen definiert werden. Dies sieht wie folgt aus: ```typescript @@ -266,34 +267,34 @@ Mit der Angabe von **:id** im Pfad für die `DashboardComponent` können wir sp ## Hochzeit mit dem Backend ### Domänenobjekte im Frontend -Um das von uns erstellte Datenmodell im Frontend ohne Weiteres nutzen zu können, mussten wir erst einmal die wichtigsten Domänenobjekte im Frontend mappen. +Um das von uns erstellte Datenmodell im Frontend ohne Weiteres nutzen zu können, mussten wir zunächst die wichtigsten Domänenobjekte im Frontend mappen. #### Map-Bike-Point Der Map-Bike-Point wird vor allem für Map auf der Startseite und das PopUp benötigt. Das Domänenobjekt liefert folgende Informationen: - + - + - + - + - +
ideindeutiger Identifier vom Typ stringeindeutiger Identifier vom Typ string
commonNamesprechender Name der Station vom Typ stringDer Name der Station vom Typ string
latBreitengrad vom Typ numberBreitengrad vom Typ number
lonLängengrad vom Typ numberLängengrad vom Typ number
statusverschachteltes Objekt vom Typ BikePointStatusverschachteltes Objekt vom Typ BikePointStatus
-Das verschachtelte Objekt status vom Typ BikePointStatus hat wiederum folgende Felder: +Das Subobjekt `status` vom Typ `BikePointStatus` hat wiederum folgende Felder: @@ -309,7 +310,7 @@ Das verschachtelte Objekt status vom Typ BikePointStatus hat wiederum folgende F
NbBikes
-Programmatisch ist der Map-Bike-Point wie folgt abgebildet: +Programmatisch ist der `MapBikePoint` wie folgt implementiert: ```typescript export interface IMapBikePoint { id?: string; @@ -337,39 +338,39 @@ export class BikePointStatus { } ``` #### Dashboard-Common-Bike-Point -Der Dashboard-Common-Bike-Point ist in der Dashboard-Komponente und allen Kind-Komponenten zu finden. Folgende Informationen erhalten wir: +Der `DashboardCommonBikePoint` ist in der Dashboard-Komponente und allen Kindkomponenten zu finden. Folgende Informationen erhalten wir: - + - + - + - + - + - + - +
ideindeutigen Identifier vom Typ stringeindeutigen Identifier vom Typ string
colorFarbe für das Icon auf der Mini-Map vom Typ stringFarbe für das Icon auf der Mini-Map vom Typ string
commonNamesprechender Name der Station vom Typ stringsprechender Name der Station vom Typ string
latBreitengrad vom Typ numberBreitengrad vom Typ number
lonLängengrad vom Typ numberLängengrad vom Typ number
maxEndDatespätest-mögliche Datum in der eine Ausleihe enden kann, vom Typ stringspätest-mögliche Datum in der eine Ausleihe enden kann, vom Typ string
maxStartDatefrühest-mögliche Datum in der eine Ausleihe starten kann, vom Typ stringfrühest-mögliche Datum in der eine Ausleihe starten kann, vom Typ string
-Programmatisch ähnelt dieses Domänenobjekt dem Map-Bike-Point. +Programmatisch ähnelt dieses Domänenobjekt dem `MapBikePoint`. ### Methoden, welche mit dem Backend kommunizieren #### Allgemeiner Aufbau @@ -382,7 +383,7 @@ export const environment = { apiUrl: 'https://it-schwarz.net/api/' }; ``` -Somit ergibt sich dann bei einem Aufruf mit einem HTTP-GET folgende Architektur der URL: +Somit ergibt sich dann bei einem GET-Aufruf folgende Zusammensetzung der URL: ```typescript public exampleMethod(param: string): Promise { return this.client.get(environment.apiUrl + `urlToEndpoint/${param}/`).toPromise(); @@ -445,7 +446,7 @@ async initMapView(): Promise { In der `MapComponent.html` ist das div-Element mit der DOM-ID "map" ausschlaggebend. Über diese ID wird im Service ein Map-Objekt initialisiert und alle weiteren Layer darauffolgend geladen. ### Map-Initialisierung -Sowohl im Dashboard als auch auf der Startseite wird eine Map generiert. Beide nutzen die in der Vorlesung vorgestellten Daten von OpenStreetMap. Diese Daten werden bei beiden Maps als Base Layer gesetzt und sind nicht veränderlich. +Sowohl im Dashboard als auch auf der Startseite wird eine Map generiert. Beide nutzen die in der Vorlesung vorgestellten Daten von OpenStreetMap. Diese Daten werden bei beiden Maps als Base Layer gesetzt und sind unveränderlich. #### Mini-Map @@ -470,7 +471,7 @@ this.miniMap.addLayer(new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x })); ``` -Zu beachten ist wie schon erwähnt bei der Generierung des Map-Objektes die richtige DOM-ID zu übergeben. Zentriert wird die Map in der Startseite auf das Stadtzentrum, während die Mini-Map auf die Geo-Koordinaten des jeweiligen Bikepoints schaut. +Zu beachten ist, wie schon erwähnt bei der Generierung des Map-Objektes die richtige DOM-ID zu übergeben. Zentriert wird die Map in der Startseite auf das Stadtzentrum, während die Mini-Map die Geo-Koordinaten des jeweiligen Bikepoints anzeigt. Ein weiterer nennenswerter Punkt ist die initiale if-Bedingung in der `initDashBoardMap`. Diese ist nötig, da für jeden einzelnen Bikepoint eine Mini-Map geladen wird und falls diese schon gesetzt ist, wird sie mit den folgenden Code zurückgesetzt: