TigerJython4Kids | HomeTurtlegrafikRobotikDatenbanken |
DU LERNST HIER... |
wie ein Online-Reservationssystem funktioniert. Die Bestellung von Karten für Konzerte erfolgt heute fast ausnahmslos über das Internet. Dabei wird dem Kunden gewöhnlich eine aktuelle Sitzplatzbelegung gezeigt, damit dieser per Mausklick die gewünschten Sitzplätze reservieren kann. Der Belegungsplan ist in praktisch allen Fällen in einer Datenbank auf einem Datenbankserver gespeichert und der Kunde greift über einen Web-Browser auf die Datenbank zu. |
MUSTERBEISPIEL |
from gturtle import * import time # --------------- Callback executors ------------ def apply(): if selected == []: setStatusText("Please make a seat selection.") return print("Selected seats:", selected) for seat in selected: occupied.append(seat) renew() setStatusText("Your reservation is confirmed. All reservations updated.") def renew(): for seat in range(1, 16): setPos(toSeatPos(seat)) if seat in occupied: drawImage("sprites/seat_2.png") else: drawImage("sprites/seat_0.png") selected.clear() # -------------- Mouse callback -------------------- def onMouseHit(x, y): if isRightMouseButton(): apply() return if y < -20 or y > 20 or x < -300 or x > 300: return seat = toSeat(x) setPos(toSeatPos(seat)) if seat in occupied: setStatusText("This seat is occupied.") return if seat not in selected: drawImage("sprites/seat_1.png") selected.append(seat) else: drawImage("sprites/seat_0.png") selected.remove(seat) # ----------------- Coordinate convertions --------- def toSeatPos(seat): return -320 + seat * 40, 0 def toLabelPos(seat): return -322 + seat * 40, -40 def toSeat(x): return int((x + 300) / 40 + 1) # --------------- main ----------------------------- selected = [] occupied = [] makeTurtle(mouseHit = onMouseHit) setTitle("Multiuser Seat Reservation") addStatusBar(20) ht() pu() for seat in range(1, 16): setPos(toSeatPos(seat)) drawImage("sprites/seat_0.png") setPos(toLabelPos(seat)) label(str(seat), adjust = "c") setStatusText("Left-click to select a free seat. Right-click for reservation.") while not isDisposed(): time.sleep(1) |
2. Teilaufgabe: DatenbankmanipulationenZuerst entwirfst du das Konzept der Datenbank mit der Tabellenstruktur, indem du dir überlegst, welche Informationen in welchen Tabellenfeldern abgespeichert werden. In einfachen Fällen genügt eine einzige Tabelle, in den meisten Fällen ist es aber angebracht, mehrere miteinander verknüpfte Tabellen zu verwenden. Bei deinem Reservationssystem ist es ziemlich offensichtlich, zwei Tabellen zu verwenden, eine Personentabelle customer mit den Kundendaten (Felder personid, name, firstname, creditcard) und eine Tabelle reservation mit der Sitzplatz-Belegung (Felder: seatnum, booked, custid).
booked kann die Werte 'N' (frei) und 'Y' (reserviert) annehmen. Ist ein Sitzplatz nicht reserviert, so wird als custid 0 eingetragen. Um die Aufgabe zu vereinfachen, betrachtest du vorerst nur die Tabelle reservation und erstellst und initialisierst diese mit einem einfachen Programm für die 16 Sitze in der Datenbank casino. from dbtable import * reservation = DbTable("seatnum", "booked", "custid") for seat in range(1, 16): reservation.insert(seat, 'N', 0) print(reservation) reservation.save("casino") Der Ablauf einer Reservation geht folgendermassen vor sich: Ein Benutzer A fragt nach der aktuellen Belegung der Sitzplätze. Diese Information wird aus der Datenbank geholt und grafisch darstellt. Während einer gewissen Bearbeitungszeit wählt A mit linken Mausklicks einen oder mehrere freie Plätze und bestätigt diese Wahl am Ende durch einen rechten Mausklick. Er wird nun aufgefordert, seine Personendaten einzugeben (falls er noch nicht Kunde ist) und die Reservation wird in der Datenbank vollzogen. Bei diesem Ablauf kann sich aber folgendes Problem ergeben: Falls während der Bearbeitungszeit von A ein zweiter Benutzer B ebenfalls eine Reservation vornimmt, erhält dieser zu Beginn die gleiche aktuelle Sitzplatz-Belegung wie A und es kann durchaus sein, dass A und B dieselben Plätze reservieren möchten. Je nachdem, wer weniger lang überlegt, sagen wir A, erhält dieser die Plätze, aber B merkt davon nichts, da ja die Datenbank von sich aus keine Rückmeldungen an B machen kann. Wenn B seine Wahl bestätigt, gibt es einen schwerwiegenden Zugriffskonflikt für die gemeinsam gewählten Sitze. Was ist zu tun, um den Zugriffskonflikt zu vermeiden? Eine Lösung besteht darin, dass bei der Bestätigungsoperation mit dem rechten Mausklick das Programm nachmals prüft, ob die Plätze immer noch frei sind. Ist dies nicht der Fall, so erhält der Benutzer eine Rückmeldung, dass die Plätze in der Zwischenzeit leider bereits vergeben wurden. Im folgenden Programm ist dieser Mechanismus implementiert, was allerdings einen Zusatzaufwand mit sich bringt. Das Programm schreibt bei der Bestätigungsoperation sogar aus, welche der Plätze bereits vergeben wurden. from gturtle import * from dbtable import * import time # ---------------- Database management ------------- def makeReservation(): # Get all reserved seats reserved = [] reservation = DbTable() reservation.restore("casino") for row in reservation.select(): if row[1] == 'Y': reserved.append(row[0]) # Check if some selected seats are already reserved conflict = [] for seat in selected: if seat in reserved: conflict.append(seat) if conflict != []: return conflict # No conflict-> make reservation for seat in selected: reservation.update(seatnum = seat, _booked = 'Y') reservation.save("casino") return [] # --------------- Callback executors ------------ def apply(): if selected == []: setStatusText("Please make a seat selection.") return taken = makeReservation() if taken == []: setStatusText("Your reservation is confirmed. All reservations updated.") renew() else: setStatusText("Seat(s) " + str(taken) + " already taken. Left-click to deselect.") def renew(): reservation = DbTable() reservation.restore("casino") for row in reservation.select(): if row[1] == 'Y': occupied.append(row[0]) for seat in range(1, 16): setPos(toSeatPos(seat)) if seat in occupied: drawImage("sprites/seat_2.png") else: drawImage("sprites/seat_0.png") selected.clear() # -------------- Mouse callback -------------------- def onMouseHit(x, y): if isRightMouseButton(): apply() return if y < -20 or y > 20 or x < -300 or x > 300: return seat = toSeat(x) setPos(toSeatPos(seat)) if seat in occupied: setStatusText("This seat is occupied.") return if seat not in selected: drawImage("sprites/seat_1.png") selected.append(seat) else: drawImage("sprites/seat_0.png") selected.remove(seat) # ----------------- Coordinate convertions --------- def toSeatPos(seat): return -320 + seat * 40, 0 def toLabelPos(seat): return -322 + seat * 40, -40 def toSeat(x): return int((x + 300) / 40 + 1) # --------------- main ----------------------------- selected = [] occupied = [] makeTurtle(mouseHit = onMouseHit) setTitle("Multiuser Seat Reservation") addStatusBar(20) ht() pu() for seat in range(1, 16): setPos(toSeatPos(seat)) drawImage("sprites/seat_0.png") setPos(toLabelPos(seat)) label(str(seat), adjust = "c") renew() setStatusText("Left-click to select a free seat. Right-click for reservation.") while not isDisposed(): time.sleep(1) |
MERKE DIR... |
Der Zugriffskonflikt bei Mehrbenutzer-Datenbanken kann so gelöst werden, das vor dem Update der Datenbank nochmals geprüft wird, ob in der Zwischenzeit ein anderer Benutzer die Daten verändert hat. |
ZUM SELBST LÖSEN |
|
Die Übereinstimmung der Verfahren ist gross: Mit einem Browser erfolgt sowohl die Abfrage der Datenbankinformation als auch das Speichern neuer Daten mit einem HTTP-Request Nach jedem Request wird die Verbindung mit dem Webserver wieder geschlossen, genau gleich wie bei restore() und save() bezüglich der Datenbank-Datei.