Wintersemester 2024/25 (2)
Wintersemester 2024/25, zweiter Termin. 9 ECTS: alle Aufgaben, 120 Punkte. 6 ECTS: nur A1 bis A6, 80 Punkte.
A1: Grundlagen (22 Punkte)
A1a (4 Punkte)
Die C-Funktion fopen benötigt als Parameter neben dem Dateinamen einen Mode String, der angibt, welche Operationen auf einer Datei ausgeführt werden sollen.
Geben Sie für die folgenden Szenarien an, welcher Mode String im Quelltext der Programme beim Aufruf von fopen verwendet werden muss, um die angegebene Aufgabe durchzuführen. Entscheiden Sie sich jeweils für genau einen der in der Vorlesung behandelten Mode Strings, also r, w oder a.
Hinweis: Falls Ihnen die Parameter von fopen bzw. das Konzept des Mode Strings nicht mehr bekannt sind, können Sie die Details dazu im CS50-Manual nachlesen, das oben auf der Seite verlinkt ist.
A1b (3 Punkte)
Analysieren Sie folgenden Pseudocode. Die Syntax und das Verhalten der Sprachkonstrukte sind an C angelehnt. Auf geschweifte Klammern wird verzichtet. Die Einrückungen drücken aus, welche Teile des Codes innerhalb von Schleifen und Bedingungen stehen. FOR und IF und alle Operatoren funktionieren wie in C. AND ist der boolesche UND-Operator (&& in C). Die Pseudocode-Funktion AUSGABE gibt die Werte der dahinterstehenden Variablen ohne Trennzeichen dazwischen, also direkt hintereinander innerhalb von einer Zeile aus, gefolgt von einen Zeilenumbruch. Hinweis: Es ist zur Beantwortung nicht nötig, das Programm in C-Code zu übersetzen und zu kompilieren.
x = 0
count = 0
FOR (a = '0'; (a <= '9') AND (x == 0); a++):
FOR (b = '0'; (b <= '9') AND (x == 0); b++):
FOR (c = '0'; (c <= '9') AND (x == 0); c++):
IF a == c:
AUSGABE a, b, c
count++
IF count == 3:
x = 1A1c (4 Punkte)
Wir haben in der Vorlesung drei Kriterien betrachtet, nach denen Codequalität bewertet werden kann. Nennen Sie diese mit je einem Stichwort. Es ist nicht nötig, die Kriterien zu erklären.
Nehmen Sie an, in Ihrer main-Funktion wird die Funktion process() aufgerufen. Sie haben an der Stelle in main, an der process() aufgerufen wird, in VS Code einen Breakpoint gesetzt und das Programm gestartet. Der Debugger hat das Programm an Ihrem Breakpoint angehalten. Wie heißt die Debugger-Funktion, mit der Sie den Debugger instruieren, die Ausführung fortzusetzen und bei der ersten Instruktion in process() wieder anzuhalten?
A1d (7 Punkte)
Beantworten Sie folgende Fragen zur Kommandozeile. Die Befehle werden in der angegebenen Reihenfolge ausgeführt. Zu Beginn befinden Sie sich im Verzeichnis /home/user.
projects wechseln, der sich im aktuellen Verzeichnis befindet.foo anlegen.foo befinden. Dort möchten Sie eine Datei namens “bar.c” über die Kommandozeile erstellen und direkt in VS Code öffnen (das Terminal läuft in der Ihnen bekannten CS50-Umgebung).bar auf Speicherlecks überprüfen.A1e (4 Punkte)
In der Vorlesung haben Sie verschiedene Stellenwertsysteme kennengelernt, wie das Binärsystem, das Hexadezimalsystem und das Dezimalsystem. Viele mesoamerikanische Kulturen, darunter auch die Maya, verwendeten das sogenannte Vigesimalsystem, ein Stellenwertsystem zur Basis 20. Um eine Stelle darzustellen, werden zwei verschiedene Zeichen verwendet: Runde Punkte (Wert: 1) und rechteckige Blöcke (Wert: 5). Der Wert an der jeweiligen Stelle entspricht der Summe der Werte seiner Zeichen. Bei der Notation einer Stelle werden immer so wenig Zeichen wie möglich verwendet. Es werden also nie mehr als vier Punkte pro Stelle notiert.
Die einzelnen Stellen werden von unten nach oben geschrieben, d.h. die niedrigstwertige Stelle steht immer ganz unten. Die Zahlen 24 und 13 werden in diesem Stellenwertsystem beispielsweise folgendermaßen dargestellt, wobei die dünne horizontale Linie die zwei Stellen voneinander trennt.

Schreiben Sie Zahlen im Vigesimalsystem auf. Verwenden sie dabei folgenden Notation:
- Das Zeichen
-(Bindestrich, links neben der rechten Shift-Taste) für einen Block. - Das Zeichen
.(Punkt, ebenfalls neben der rechten Shift-Taste) für einen Punkt. - Die Ziffer
0, wenn Sie an der entsprechenden Stelle weder einen Block noch einen Punkt notieren möchten. - Schreiben Sie jeden Block in eine eigene Zeile. Fassen Sie alle Punkte direkt hintereinander in einer eigenen Zeile zusammen.
- Trennen Sie die einzelnen Stellen durch Leerzeilen.
Die Zahl 24 würden Sie also folgendermaßen notieren:
.
....A2: Grundlagen der C-Programmierung (17 Punkte)
A2a (2 Punkte)
Beantworten Sie folgende Fragen.
Mit welcher Funktion können Sie in C die Länge eines Strings ermitteln? Es reicht, den Namen der Funktion anzugeben (ohne Klammern und ohne Parameter)
Mit welcher Funktion können Sie in C zwei Strings miteinander vergleichen? Es reicht, den Namen der Funktion anzugeben (ohne Klammern und ohne Parameter)
A2b (4 Punkte)
Ein wichtiger Datentyp in C ist int.
Betrachten Sie folgenden Programmcode.
#include <stdio.h>
typedef struct foo
{
int value;
char bar[5];
} foo;
int main()
{
printf("Ein int benötigt %lu Bytes.\n", sizeof(int));
printf("Fünf chars benötigen %lu Bytes.\n", sizeof(char) * 5);
printf("%lu + %lu sollte %lu sein.\n",
sizeof(int), sizeof(char) * 5, sizeof(foo));
}Nach dem Kompilieren und Ausführen wird die folgende Ausgabe erzeugt:
Ein int benötigt 4 Bytes.
Fünf chars benötigen 5 Bytes.
4 + 5 sollte 12 sein.A2c (5 Punkte)
Betrachten Sie das folgende C-Programm (Sie haben es bereits in der Vorlesung gesehen) und ermitteln Sie drei Fehler im Quellcode.
Geben Sie jeweils die Zeile an, in der der Fehler auftritt (Inhalt der Zeile), und wie Sie den Fehler beheben würden (am besten mit der erforderlichen Anpassung der Codezeile, ersatzweise mit einer Erklärung). Achten Sie dabei darauf, dass der Code auch nach Ihren Änderungen noch das bewirkt, was intendiert war.
Empfehlung: Nutzen Sie die Fehlermeldungen, die make ausgibt, wenn Sie versuchen, das Programm zu kompilieren.
#include <stdlib.h>
#include <stdio.h>
int main()
{
int n = 5;
int *z = NULL;
int *array = malloc(5);
for (int i = 0; i <= n; i++)
{
array[i] = i * 2;
*z = array[i];
}
free(array);
printf("%d\n", *z);
return 0;
}A2d (6 Punkte)
Schreiben Sie ein Programm, das einen Integer auf der Konsole entgegennimmt und den doppelten Wert ausgibt. Sie können dazu die CS50-Bibliothek benutzen.
Die Ausgabe des Programms soll zum Beispiel wie folgt aussehen:
Geben Sie einen Integer ein: 3
Der doppelte Wert von 3 ist 6.A3: Chiffre (12 Punkte)
In dieser Aufgabe implementieren Sie ein Kommandozeilenprogramm in C, das einen Text verschlüsselt. Dieses Verfahren funktioniert so ähnlich wie die Ihnen bekannte Caesar-Chiffre. Der wichtigste Unterschied: Die Klartext-Zeichen werden nicht um einen konstanten Wert verschoben, sondern unterschiedlich weit. Wie weit ein Zeichen verschoben wird, ergibt sich aus einem Schlüsselwort, das genauso lang ist wie der Klartext.
Das Verfahren lässt sich wie folgt veranschaulichen:
H A L L O
7 0 11 11 14
B I B E R
1 8 1 4 17
------------------
I I M P F
8 8 12 15 5Das Programm arbeitet wie folgt:
- Es ordnet dem Klartext und dem Schlüsselwort wie folgt buchstabenweise Zahlen zu: A = 0, B = 1, C = 2, …, Z = 25.
- Es addiert die Zahlen, die an der gleichen Position stehen. Im Beispiel etwa
7 + 1 = 8und ermittelt den zugehörigen Chiffretext-Buchstaben (hier:I). - Um sicherzustellen, dass der Chiffretext aus Buchstaben besteht, wird der Modulo-Operator verwendet (
% 26). Im Beispiel sieht man den Effekt beim letzten Zeichen:(14 + 17) % 26 = 31 % 26 = 5.
Tipp: Für die Ausgabe ist folgende Konstruktion hilfreich: 'A' + 8 = 'I' etc.
Die Aufgabenstellung finden Sie im Verzeichnis exam-files/chiffre. Für diese Aufgabe steht Ihnen check50 zur Verfügung. Um check50 zu verwenden, führen Sie folgenden Befehl in dem Verzeichnis aus, in dem sich die Datei chiffre.c befindet:
check50 checks/chiffre
Zuvor sollten Sie Ihr Programm kompilieren.
A3a (7 Punkte)
Prüfen Sie zunächst die Anzahl der Argumente sowie deren Inhalt.
Wenn weniger oder mehr als 2 Argumente übergeben werden, dann soll das Programm mit dem Rückgabewert 2 terminieren und
Usage: ./chiffre <plaintext> <key>\n
ausgeben.
Prüfen Sie anschließend, ob beide Argumente ausschließlich aus Großbuchstaben bestehen. Sollte dies nicht der Fall sein, geben Sie den String
Both arguments need to be uppercase strings!\n
aus und terminieren das Programm wieder mit dem Rückgabewert 2. Verwenden Sie für Ihre Implementierung die Funktion is_uppercase_string(string arg).
Prüfen Sie zuletzt, ob beide übergebene Strings gleich lang sind. Implementieren Sie dazu die Funktion same_length(string arg_1, string arg_2). Wenn dies nicht der Fall ist, geben Sie den String
Both arguments need to be uppercase strings and of the same length!\n
aus und beenden das Programm mit dem Rückgabewert 2.
A3b (5 Punkte)
Implementieren Sie nun die Verschlüsselung für die Chiffre. Dazu implementieren Sie die Funktion encrypt(char a, char b). Wenden Sie dann die Funktion encrypt() Zeichen für Zeichen auf den Klartext und das Schlüsselwort an. Das Ergebnis speichern Sie im String result und geben es auf der Konsole aus.
A4: Datenrettung (10 Punkte)
Dominik hat versehentlich wichtige Daten gelöscht. Glücklicherweise hat er den Inhalt des Datenträgers gesichert, indem er eine vollständige Kopie des Dateisystems (“Image”) erstellt hat. Ihre Aufgabe ist es, aus diesem Image zwei Dateien wiederherzustellen.
Bearbeitungshinweis: Das Folgende beschreibt das verwendete FAT16-Dateisystem. Konzentrieren Sie sich auf die für die Dateiwiederherstellung relevanten Teile. Schauen Sie sich zuerst die konkreten Aufgaben und den Code in fat1.c und fat2.c an.
Das Image nutzt FAT16, aufgebaut aus: Boot Sector, File Allocation Tables (FATs), Root Directory, Data Area.

Boot Sector: Enthält Basisparameter des Dateisystems, wie Sektorgröße, Clustergröße (in Sektoren) und Startpositionen der anderen Bereiche. Diese Informationen sind oft zum Berechnen von Offsets nötig.
Data Area: Hier liegen die eigentlichen Datei-Inhalte, aufgeteilt in nummerierte Blöcke fester Größe, genannt Cluster. Ein Cluster ist die kleinste Einheit für die Dateispeicherung und besteht aus einem oder mehreren Sektoren (der kleinsten Einheit, die die Hardware lesen/schreiben kann).
Root Directory: Enthält Einträge für Dateien und Verzeichnisse im Stammverzeichnis. Für die Wiederherstellung sind entscheidend:
- Der Dateiname und Erweiterung.
- Die Nummer des ersten Clusters, in dem der Dateiinhalt beginnt.
- Die exakte Dateigröße in Bytes (wichtig, um das Ende der Datei im letzten Cluster zu bestimmen).
File Allocation Table (FAT): Das Kernstück, das die Cluster einer Datei miteinander verkettet.
Für jeden Cluster in der Data Area gibt es einen Eintrag in der FAT (bei FAT16: 16 Bit pro Eintrag).
Funktionsweise: Der Index des FAT-Eintrags entspricht der Cluster-Nummer, auf den sich der FAT-Eintrag bezieht. Der Wert in einem FAT-Eintrag enthält je nach Fall folgende Daten:
- Nächster Cluster: Eine Cluster-Nummer (z.B. Wert 12 im Eintrag für Cluster 5) verweist auf den nächsten Datenblock (Cluster 12).
- Letzter Cluster (EOC - End of Chain): Ein spezieller Wert (typisch:
0xFFF8-0xFFFF) markiert das Ende der Dateikette. - Freier Cluster: Der Wert
0x0000bedeutet, der Cluster ist ungenutzt.
Anders als bei klassischen verketteten Listen in der Programmierung (wo ein Element Daten und einen expliziten Pointer auf das nächste Element enthält), funktioniert die FAT durch implizite Referenzierung:
- Der Index im FAT-Array ist gleichzeitig die Nummer des Clusters
- Der Wert an dieser Stelle in der FAT ist die Nummer des nächsten Clusters in der Liste
- Die Daten selbst sind nicht Teil der Liste, sondern liegen getrennt in den Clustern der Data Area
Beispiel zum Nachverfolgen einer Datei (“Urlaub.jpg”, 9500 Bytes):
- Root Directory: Liefert Startcluster = 3, Größe = 9500 Bytes.
- FAT: Enthält den Wert 7. Nächster Cluster ist 7.
- FAT: Enthält den Wert 11. Nächster Cluster ist 11.
- FAT: Enthält 0xFFFF (end of chain). Cluster 11 ist der letzte.
- Ergebnis: Die Datei besteht aus den Clustern 3, 7, 11 (in dieser Reihenfolge).
- Lesen der Daten: Angenommen, Clustergröße = 4096 Bytes (4 KB):
Normalerweise gibt es zwei identische Kopien der FAT (FAT1 und FAT2). Die zweite FAT dient als Backup, falls die erste beschädigt wird.
Zusammenfassend für die Wiederherstellung: Das Root Directory liefert den Startcluster und die Gesamtgröße einer Datei. Die FAT zeigt, wie die nachfolgenden Cluster der Datei verkettet sind, bis zum End-of-Chain-Marker. Die eigentlichen Daten befinden sich in den entsprechenden Clustern der Data Area.
Aufgaben
Um diese Aufgabe zu bearbeiten, müssen Sie die Dateien im Verzeichnis exam-files/fat bearbeiten.
Das folgende Bild zeigt beispielhaft wie die Situation im Image aussehen könnte.

A4a (4 Punkte)
Ihre erste Aufgabe ist es, einige Metadaten aus dem Boot Sector zu extrahieren:
- OEM Identifier: Das Betriebssystem, unter dem der Datenträger formatiert wurde.
- Reserved Sectors: Die Anzahl der für das Dateisystem reservierten Sektoren, die nicht frei verwendet werden können.
- Number of FATs: Die Anzahl der FATs.
- Bytes per Sector: Die Anzahl der Bytes pro Sektor.
- Sectors per Cluster: Die Anzahl der Sektoren pro Cluster.
Vervollständigen Sie dazu das Programm fat1.c.
A4b (6 Punkte)
Die Datei fat2.c enthält bereits Code, der die Einträge aus dem Root Directory liest und über die einzelnen Cluster einer Datei iteriert.
Vervollständigen Sie dazu die Funktion extract_file, sodass diese den Inhalt einer Datei aus dem Image liest und unter dem originalen Dateinamen im aktuellen Verzeichnis speichert. Achten Sie darauf, dass alle allokierten Ressourcen (Dateien, Speicher) ordnungsgemäß freigegeben werden.
A5: Sortieren und verkettete Listen (9 Punkte)
A5a (3 Punkte)
Gegeben sei ein Array mit 8 Elementen. Dieses Array soll sortiert werden. Dazu wird eine MergeSort-Funktion verwendet, die Merge Sort rekursiv implementiert.
Die verwendete MergeSort-Funktion ist wie folgt implementiert:
Prüfe, ob Basisfall vorliegt (einzelnes Element)
Sonst:
- Teile Array in zwei Hälften
- Sortiere Hälften jeweils durch Aufruf von MergeSort
- Führe sortierte Hälften zusammen
A5b (6 Punkte)
Neben der Ihnen bekannten einfach verketteten Liste gibt es auch doppelt verkettete Listen. Diese lassen sich mit folgender Datenstruktur implementieren:
typedef struct list_node {
int value;
struct list_node *next;
struct list_node *prev;
};Mit dieser Datenstruktur wurde eine sortierte doppelt verkettete Liste erstellt, die bereits folgende Elemente in aufsteigender Reihenfolge enthält: 10, 25, 40, 55.
Im Speicher wird dies wie folgt abgebildet (Speicheradressen sind vierstellig):
// Pointer auf den Listenanfang
struct list_node* head = 0x1000;
// Pointer auf das Listenende
struct list_node* tail = 0x4000;
0x1000: [10, 0x2000, NULL] // Erstes Element (prev ist NULL)
0x2000: [25, 0x3000, 0x1000] // Zweites Element
0x3000: [40, 0x4000, 0x2000] // Drittes Element
0x4000: [55, NULL, 0x3000] // Letztes Element (next ist NULL)
Fügen Sie die Werte 30 und 60 in die sortierte doppelt verkettete Liste ein.
Legen Sie dazu neue Knoten mit von Ihnen gewählten vierstelligen Speicheradressen an. In Ihrer Antwort geben Sie die neuen Knoten in der gleichen Schreibweise an wie in der Aufgabenstellung, also Speicheradresse gefolgt vom Inhalt des Knotens. Ihre Antwort muss auch alle notwendigen Änderungen an bestehenden Knoten dokumentieren (auch hier Angabe der Speicheradresse und des veränderten Inhalts). Geben Sie außerdem an, wie sich der head- und tail-Pointer nach den Einfügeoperationen verändert haben.
Achten Sie darauf, dass alle Verbindungen in beide Richtungen korrekt gesetzt werden und die Liste nach dem Einfügen weiterhin aufsteigend sortiert ist.
A6: Structs und Pointer (10 Punkte)
A6a (3 Punkte)
Gegeben sei ein Integer int i sowie die Pointer int *p und int *q, die beide auf i zeigen.
Geben Sie alle Zuweisungen an, die gültig sind. Gültig bedeutet, dass der Code kompiliert und dass Inhalt und Zweck der Variablen gleich bleiben.
Antworten Sie, indem Sie die Buchstaben der einzelnen Zuweisunge durch Komma getrennt eintragen. Also z. B. a, d, g, wenn sie der Meinung sind, dass dies gültige Zuweisungen sind.
(a) p = i; (d) p = &q; (g) p = *q;
(b) *p = &i; (e) p = *&q; (h) *p = q;
(c) &p = q; (f) p = q; (i) *p = *q;A6b (7 Punkte)
Gegeben sind folgende Strukturen für die Implementierung eines Stacks:
typedef struct Node
{
int value;
struct Node *next;
} Node;
typedef struct Stack
{
Node *top;
} Stack;Implementieren Sie eine Funktion void push(Stack *s, int value), um ein Element auf den Stack zu legen. Dabei müssen Sie prüfen, ob der übergebene Pointer gültig ist, also nicht NULL ist. Falls der Stack leer ist, hat der Pointer Node *top den Wert NULL. Beim letzten Element des Stacks hat struct Node *next den Wert NULL. Im Verzeichnis stack befindet sich die Datei stack.c mit einem bereits generierten Stack, die sie verwenden können, um Ihre Implementierung zu testen.
B1: Python und OOP (25 Punkte)
B1a (6 Punkte)
Schreiben Sie ein Python-Programm, das einen Wert in Meilen entgegennimmt und diesen in Kilometer umrechnet.
Runden Sie Ihre Ausgabe auf 2 Dezimalstellen (also z.B. 5.12).
Nutzen Sie folgendes Umrechnungsverhältnis: 1 Meile = 1,609344 Kilometer.
Verwenden Sie in dieser Aufgabe nicht die cs50-Bibliothek. Wandeln Sie stattdessen die eingelesene Zeichenkette str mit der Funktion float(str) in eine Fließkommazahl um.
Die Ausgabe des Programms sollte für die Eingabe 23.0 wie folgt aussehen (es können auch Ganzzahlen eingegeben werden):
Distanz in Meilen: 23.0
23.0 Meilen sind ungefähr 37.01 Kilometer.B1b (6 Punkte)
In dieser Aufgabe dürfen Sie die cs50-Bibliothek verwenden. Schreiben Sie ein Programm, das einen String über die Eingabeaufforderung entgegennimmt. Das Programm soll ermitteln, wie oft jedes Zeichen im eingegebenen String vorkommt. Berücksichtigen Sie dabei alle eingegebenen Zeichen, einschließlich Leerzeichen, und unterscheiden Sie zwischen Groß- und Kleinbuchstaben. Das Programm soll die Häufigkeiten in einem Dictionary letters speichern und am Ende des Programm das Dictionary mit print(letters) ausgeben.
Die Ausgabe soll wie folgt aussehen:
Eingabe: Hallo
{'H': 1, 'a': 1, 'l': 2, 'o': 1}B1c (6 Punkte)
Gegeben ist folgender C-Code:
#include <stdio.h>
int main(int argc, char *argv[])
{
typedef struct {
char* name;
int age;
} person;
person people[2];
people[0].name = "Alice";
people[0].age = 25;
people[1].name = "Bob";
people[1].age = 30;
for (int i = 0; i < 2; i++)
{
printf("Person %i: Name: %s, Age: %i\n",
(i + 1), people[i].name, people[i].age);
}
}Wandeln Sie dieses C-Programm in ein entsprechendes Python-Programm um.
Programmieren Sie objektorientiert, d.h. verwenden Sie eine Klasse Person mit den Attributen name und age. Sie müssen keine Methoden oder Properties verwenden.
Speichern Sie die zwei Person-Objekte in einer Liste. Iterieren Sie über die Liste, um die gleichen Daten auszugeben wie im C-Programm.
B1d (7 Punkte)
Schreiben Sie ein Python-Programm, das eine ASCII-Leiter basierend auf zwei Parametern n und m ausgibt:
Der Parameter n bestimmt zwei Eigenschaften:
- Die Breite der Sprossen (die horizontalen Verbindungsstücke, bestehend aus “-"-Zeichen)
- Die Länge der Holmstücke (die vertikalen Teile zwischen den Sprossen, bestehend aus “#"-Zeichen)
Der Parameter m gibt an, wie viele Zwischenräume (Sprossen mit den dazugehörigen Holmstücken) die Leiter haben soll.
Die Bereiche innerhalb der Leiter sind mit Leerzeichen ausgefüllt.
Die Holme (die senkrechten Teile) bestehen aus
#-SymbolenDie Verbindung zwischen Sprossen und Holmen wird durch
+dargestellt.
Lesen Sie die Parameter n und m nacheinander über die input()-Funktion oder eine geeignete Funktion aus der cs50-Bibliothek von der Konsole ein.
Hier einige Beispiele zur Veranschaulichung:
n=3, m=3 n=1, m=5 n=5, m=2
+---+ +-+ +-----+
# # # # # #
# # +-+ # #
# # # # # #
+---+ +-+ # #
# # # # # #
# # +-+ +-----+
# # # # # #
+---+ +-+ # #
# # # # # #
# # +-+ # #
# # # #
+---+ +-----+B2: Budget Tracker und Debugging (15 Punkte)
In dieser Aufgabe entwickeln Sie Teile einer Flask-Anwendung zur Verwaltung von Ausgaben. Ihre Aufgabe wird es sein, den Login und eine Ausgabenübersicht mit Test-Daten zu implementieren.
Für diese Aufgabe stellen wir Ihnen Quellcode zur Verfügung, den Sie vervollständigen müssen. Wechseln Sie dazu im Terminal in das Verzeichnis exam-files/budget, das sich in Ihrem Homeverzeichnis /home/student befindet.
Damit Code-Änderungen vom Flask-Server automatisch berücksichtigt werden, müssen Sie den Server wie folgt starten:
flask --debug run
Um die Anwendung im Browser zu öffnen, klicken Sie bei gedrückter Strg-Taste auf die URL (Running on …), die Flask nach dem Start anzeigt. Um Flask zu beenden, verwenden Sie die Tastenkombination Strg-C.
B2a (5 Punkte)
Zunächst implementieren Sie die Login-Funktion. Hierfür müssen Sie die Funktion für die “/"-Route implementieren (index). Implementieren Sie folgende Fälle:
Fall 1: GET-Requests: Die Funktion soll das Template login.html rendern.
Fall 2: POST-Requests: Die Funktion soll zunächst prüfen, ob die in das Formular eingegebenen Daten korrekt sind. Dazu müssen Sie die Formularfelder username und password auslesen und mit den Angaben im users-Dictionary abgleichen. Dort stehen die korrekten Daten. Die Keys sind die Benutzernamen. Als Value wird jeweils ein Dictionary gespeichert, in dem unter anderem das Passwort abgelegt ist.
Fall 2a: Es ist kein Eintrag für den eingegebenen Benutzernamen vorhanden oder das eingegebene Passwort stimmt nicht mit dem hinterlegtem Passwort überein. In diesem Fall leiten Sie den Browser auf / um. Dies bewirkt, dass der Browser die Seite erneut mit einem GET-Request abruft und das Login-Formular anzeigt.
Fall 2b: Der Benutzer existiert und das Passwort ist korrekt. In diesem Fall weisen Sie der Session-Variablen username den im Formular eingegebenen Benutzernamen zu. Beispiel: session["username"] = <...>. Danach leiten Sie den Browser nach /home um.
B2b (5 Punkte)
In dieser Teilaufgabe sollen Sie die Testdaten, die im Dictionary users hinterlegt sind, auslesen und im Template home.html rendern.
Hinweis: Wenn Sie die Teilaufgabe B2a nicht lösen konnten, entfernen Sie den Decorator @login_required über show_expenses() und setzen Sie den User mit folgender Codezeile statisch auf den Testuser: user = users['test'].
Lesen Sie dazu den User-Eintrag für den aktuell angemeldeten Benutzer aus dem users-Dictionary aus. Der aktuell angemeldete Benutzer steht in der Session-Variablen, die Sie in Teilaufgabe B2a verwendet haben. Übergeben Sie anschließend items und das budget des Users an das Template und rendern Sie dieses.
Das Template müssen Sie folgendermaßen vervollständigen:
- Iterieren Sie mit einer Schleife über die einzelnen
itemsund geben Sie sowohldescriptionals auchpricein der Tabelle aus. - Geben Sie anschließend noch
budgetim div-Element hinter dem Text “Total Budget:” aus. - Fügen Sie schließlich ein weiteres div-Element hinzu, das einen Link mit dem Text “Logout” enthält, über den sich der Benutzer abmelden kann. Verwenden Sie die dafür vorgesehene Route.
B2c (5 Punkte)
In der Anwendung gibt es noch die Route /signup, allerdings ohne Programmlogik im Flask-Backend.
Wird das Account-anlegen-Formular unvollständig oder inkorrekt ausgefüllt, erscheint vor dem Absenden eine Fehlermeldung. Dazu werden die Formularfelder in der Datei validation.js clientseitig validiert.
Das Skript validation.js enthält zwei Fehler.
Hinweis: Zur Fehlersuche können Sie die Konsole und den Debugger in den Developer Tools des Browsers verwenden. Diese können Sie mit F12 öffnen.
Diese Seite als Markdown: ansehen herunterladen