jgrivolla commited on
Commit
3b0c90a
·
verified ·
1 Parent(s): db0bf65

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +89 -55
  2. index.html +86 -110
app.py CHANGED
@@ -7,41 +7,70 @@
7
  import pprint
8
  import json
9
  import pymysql.cursors
10
- from fastapi import FastAPI
11
- from fastapi.responses import JSONResponse
 
 
12
  import os
13
 
14
- # Connect to the database
15
- connection = pymysql.connect(host=os.environ['MURMEL_DB_HOST'],
16
- user=os.environ['MURMEL_DB_USER'],
17
- password=os.environ['MURMEL_DB_PASSWORD'],
18
- database='murmel',
19
- cursorclass=pymysql.cursors.DictCursor)
 
 
 
 
20
 
21
  app = FastAPI()
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  @app.get("/groups")
25
  def get_groups():
26
- connection.ping(reconnect=True)
27
- with connection.cursor() as cursor:
28
- # Read a single record
29
- sql = "SELECT Gruppe, idGruppe FROM `gruppe` WHERE aktuell is True ORDER BY idGruppe ASC;"
30
- cursor.execute(sql, ())
31
- result = cursor.fetchall()
32
- #pprint.pprint(result)
33
- return result
34
 
35
  @app.get("/students/{idGruppe}")
36
  def get_students(idGruppe):
37
- connection.ping(reconnect=True)
38
- with connection.cursor() as cursor:
39
- #sql = "SELECT Gruppe, idGruppe FROM `gruppe` WHERE idGruppe=%s ORDER BY name ASC;"
40
- sql = "select concat(`ki`.`Vorname`,' ',`ki`.`Nachnamen`) AS `Kind`, `ki`.`idKind` AS `idKind` from (((`kind` `ki` join `gruppe` `gr`) join `kind_x_gruppe_x_schuljahr` `kgs`) join `schuljahr` `sch`) where `ki`.`idKind` = `kgs`.`x_kind` and `sch`.`idschuljahr` = `kgs`.`x_schuljahr` and `gr`.`idGruppe` = `kgs`.`x_gruppe` and `sch`.`aktuell` = 1 and (`gr`.`Gruppe` = %s or `gr`.`idGruppe` = %s) order by 1"
41
- cursor.execute(sql, (idGruppe,idGruppe))
42
- result = cursor.fetchall()
43
- #pprint.pprint(result)
44
- return result
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  @app.get("/return/{idBuch}")
47
  def rueckgabe(idBuch, grund='rueckgabe'):
@@ -56,21 +85,16 @@ def rueckgabe(idBuch, grund='rueckgabe'):
56
  - int: 0 if the book was not found or already returned.
57
 
58
  """
59
- connection.ping(reconnect=True)
60
- with connection.cursor() as cursor:
61
- # check if the book is already returned
62
- sql = "SELECT `idBuch`, `idKind`, `ausleihe`, `rueckgabe`, `rueckGrund` FROM `ausleihe` WHERE `idBuch` = %s AND `rueckgabe` is NULL;"
63
- cursor.execute(sql, (idBuch,))
64
- result = cursor.fetchall()
65
- if len(result) == 0:
66
- return 0
67
- # return the book
68
- sql = "UPDATE `ausleihe` SET `rueckgabe` = NOW(), `rueckGrund` = %s WHERE `idBuch` = %s AND `rueckgabe` is NULL;"
69
- cursor.execute(sql, (grund, idBuch))
70
- connection.commit()
71
- #pprint.pprint(result)
72
- # return if the book was returned or not and who had it before
73
- return result
74
 
75
  @app.get("/borrow/{idBuch}/{idKind}")
76
  def ausleihe(idBuch, idKind):
@@ -82,17 +106,23 @@ def ausleihe(idBuch, idKind):
82
  - idKind (int): The ID of the child borrowing the book.
83
 
84
  Returns:
85
- - rueckgabe_result (str): The result of the book return operation, indicating if the book was returned or not and who had it before, or if there was an error.
86
  """
87
- rueckgabe_result = rueckgabe(idBuch, grund="neu-ausleihe") # Buch kann nicht durch andere ausgeliehen sein
 
 
 
 
 
 
 
 
 
 
88
  sql = "INSERT INTO `ausleihe` (`idBuch`, `idKind`, `ausleihe`) VALUES (%s, %s, NOW());"
89
- connection.ping(reconnect=True)
90
- with connection.cursor() as cursor:
91
- cursor.execute(sql, (idBuch, idKind))
92
- connection.commit()
93
- #pprint.pprint(result)
94
- # return if the book was returned or not and who had it before or if there was an error
95
- return rueckgabe_result
96
 
97
  @app.get("/borrowed/{idKind}")
98
  def ausgeliehen(idKind):
@@ -105,17 +135,21 @@ def ausgeliehen(idKind):
105
  Returns:
106
  list: A list of tuples containing the book ID and the borrowing date for each book that is currently borrowed by the child.
107
  """
108
- connection.ping(reconnect=True)
109
- with connection.cursor() as cursor:
110
- sql = "SELECT `idBuch`, `ausleihe` FROM `ausleihe` WHERE `idKind` = %s AND `rueckgabe` IS NULL;"
111
- cursor.execute(sql, (idKind,))
112
- result = cursor.fetchall()
113
- #pprint.pprint(result)
114
  return result
115
 
 
 
 
 
 
 
116
 
117
  # run the app
118
  if __name__ == '__main__':
119
- app.run(host='localhost', port=5000)
 
120
 
121
  # %%
 
7
  import pprint
8
  import json
9
  import pymysql.cursors
10
+ from fastapi import FastAPI, HTTPException
11
+ from fastapi.responses import JSONResponse, HTMLResponse
12
+ import time
13
+ from dbutils.pooled_db import PooledDB
14
  import os
15
 
16
+ # Create a connection pool
17
+ db_config = {
18
+ 'host': os.environ['MURMEL_DB_HOST'],
19
+ 'user': os.environ['MURMEL_DB_USER'],
20
+ 'password': os.environ['MURMEL_DB_PASSWORD'],
21
+ 'database': 'murmel',
22
+ 'cursorclass': pymysql.cursors.DictCursor
23
+ }
24
+
25
+ pool = PooledDB(pymysql, maxconnections=5, **db_config)
26
 
27
  app = FastAPI()
28
 
29
+ def execute_query(sql, params=None, max_retries=3, retry_delay=1):
30
+ for attempt in range(max_retries):
31
+ try:
32
+ connection = pool.connection()
33
+ with connection.cursor() as cursor:
34
+ cursor.execute(sql, params or ())
35
+ result = cursor.fetchall()
36
+ connection.commit()
37
+ return result
38
+ except pymysql.OperationalError as e:
39
+ if attempt == max_retries - 1:
40
+ raise HTTPException(status_code=500, detail="Database connection error")
41
+ time.sleep(retry_delay)
42
+ finally:
43
+ if 'connection' in locals():
44
+ connection.close()
45
 
46
  @app.get("/groups")
47
  def get_groups():
48
+ sql = "SELECT Gruppe, idGruppe FROM `gruppe` WHERE aktuell is True ORDER BY idGruppe ASC;"
49
+ return execute_query(sql)
 
 
 
 
 
 
50
 
51
  @app.get("/students/{idGruppe}")
52
  def get_students(idGruppe):
53
+ if idGruppe == 'all':
54
+ sql = """
55
+ SELECT DISTINCT concat(`ki`.`Vorname`,' ',`ki`.`Nachnamen`) AS `Kind`, `ki`.`idKind` AS `idKind`
56
+ FROM `kind` `ki`
57
+ JOIN `kind_x_gruppe_x_schuljahr` `kgs` ON `ki`.`idKind` = `kgs`.`x_kind`
58
+ JOIN `schuljahr` `sch` ON `sch`.`idschuljahr` = `kgs`.`x_schuljahr`
59
+ WHERE `sch`.`aktuell` = 1
60
+ ORDER BY 1
61
+ """
62
+ return execute_query(sql)
63
+ else:
64
+ sql = """
65
+ SELECT concat(`ki`.`Vorname`,' ',`ki`.`Nachnamen`) AS `Kind`, `ki`.`idKind` AS `idKind`
66
+ FROM `kind` `ki`
67
+ JOIN `kind_x_gruppe_x_schuljahr` `kgs` ON `ki`.`idKind` = `kgs`.`x_kind`
68
+ JOIN `schuljahr` `sch` ON `sch`.`idschuljahr` = `kgs`.`x_schuljahr`
69
+ JOIN `gruppe` `gr` ON `gr`.`idGruppe` = `kgs`.`x_gruppe`
70
+ WHERE `sch`.`aktuell` = 1 AND (`gr`.`Gruppe` = %s OR `gr`.`idGruppe` = %s)
71
+ ORDER BY 1
72
+ """
73
+ return execute_query(sql, (idGruppe, idGruppe))
74
 
75
  @app.get("/return/{idBuch}")
76
  def rueckgabe(idBuch, grund='rueckgabe'):
 
85
  - int: 0 if the book was not found or already returned.
86
 
87
  """
88
+ sql = "SELECT `idBuch`, `idKind`, `ausleihe`, `rueckgabe`, `rueckGrund` FROM `ausleihe` WHERE `idBuch` = %s AND `rueckgabe` is NULL;"
89
+ result = execute_query(sql, (idBuch,))
90
+ if len(result) == 0:
91
+ return 0
92
+ # return the book
93
+ sql = "UPDATE `ausleihe` SET `rueckgabe` = NOW(), `rueckGrund` = %s WHERE `idBuch` = %s AND `rueckgabe` is NULL;"
94
+ execute_query(sql, (grund, idBuch))
95
+ #pprint.pprint(result)
96
+ # return if the book was returned or not and who had it before
97
+ return result
 
 
 
 
 
98
 
99
  @app.get("/borrow/{idBuch}/{idKind}")
100
  def ausleihe(idBuch, idKind):
 
106
  - idKind (int): The ID of the child borrowing the book.
107
 
108
  Returns:
109
+ - dict: A dictionary containing the result of the borrowing operation.
110
  """
111
+ rueckgabe_result = rueckgabe(idBuch, grund="neu-ausleihe")
112
+
113
+ message = "Buch erfolgreich ausgeliehen"
114
+ if rueckgabe_result:
115
+ # Get the name of the previous borrower
116
+ prev_borrower_id = rueckgabe_result[0]['idKind']
117
+ sql = "SELECT CONCAT(Vorname, ' ', Nachnamen) AS full_name FROM kind WHERE idKind = %s;"
118
+ prev_borrower_name = execute_query(sql, (prev_borrower_id,))[0]['full_name']
119
+ message += f". Zuvor ausgeliehen von {prev_borrower_name}"
120
+
121
+ # Insert new borrowing record
122
  sql = "INSERT INTO `ausleihe` (`idBuch`, `idKind`, `ausleihe`) VALUES (%s, %s, NOW());"
123
+ execute_query(sql, (idBuch, idKind))
124
+
125
+ return {"message": message}
 
 
 
 
126
 
127
  @app.get("/borrowed/{idKind}")
128
  def ausgeliehen(idKind):
 
135
  Returns:
136
  list: A list of tuples containing the book ID and the borrowing date for each book that is currently borrowed by the child.
137
  """
138
+ sql = "SELECT `idBuch`, `ausleihe` FROM `ausleihe` WHERE `idKind` = %s AND `rueckgabe` IS NULL;"
139
+ result = execute_query(sql, (idKind,))
140
+ #pprint.pprint(result)
 
 
 
141
  return result
142
 
143
+ @app.get("/", response_class=HTMLResponse)
144
+ async def read_root():
145
+ with open("index.html", "r") as f:
146
+ content = f.read()
147
+ return HTMLResponse(content=content)
148
+
149
 
150
  # run the app
151
  if __name__ == '__main__':
152
+ import uvicorn
153
+ uvicorn.run(app, host='localhost', port=5000)
154
 
155
  # %%
index.html CHANGED
@@ -1,142 +1,118 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Library App</title>
 
 
 
 
 
7
  </head>
8
  <body>
9
- <h1>Library App</h1>
 
 
 
 
10
 
11
- <!-- Borrow a Book -->
12
- <section id="borrow-section">
13
- <h2>Borrow a Book</h2>
14
- <button onclick="loadGroups('borrow')">Select Group</button>
15
- <div id="borrow-controls" style="display: none;">
16
- <select id="group-select-borrow" onchange="loadStudents('borrow')"></select>
17
- <select id="student-select-borrow"></select>
18
- <input type="text" id="book-id-borrow" placeholder="Enter Book ID">
19
- <button onclick="borrowBook()">Borrow Book</button>
20
- </div>
21
- <div id="borrow-result"></div>
22
- </section>
23
 
24
- <!-- Return Books -->
25
- <section id="return-section">
26
- <h2>Return Books</h2>
27
- <input type="text" id="book-id-return" placeholder="Enter Book ID">
28
- <button onclick="returnBook()">Return Book</button>
29
- <div id="return-result"></div>
30
- </section>
31
-
32
- <!-- List Outstanding Books -->
33
- <section id="list-section">
34
- <h2>List Outstanding Books</h2>
35
- <button onclick="loadGroups('list')">Select Group</button>
36
- <div id="list-controls" style="display: none;">
37
- <select id="group-select-list" onchange="loadStudents('list')"></select>
38
- <select id="student-select-list"></select>
39
- <button onclick="listOutstandingBooks()">Show Outstanding Books</button>
40
- </div>
41
- <div id="list-result"></div>
42
- </section>
43
-
44
- <!-- Inventory -->
45
- <section id="inventory-section">
46
- <h2>Inventory</h2>
47
- <input type="text" id="book-id-inventory" placeholder="Enter Book ID">
48
- <button onclick="inventoryBook()">Inventory Book</button>
49
- <div id="inventory-result"></div>
50
- </section>
51
 
52
  <script>
53
- const apiUrl = 'http://localhost:8000'; // Replace with your actual API URL
 
54
 
55
- // Load groups for selection
56
- function loadGroups(mode) {
57
- fetch(`${apiUrl}/groups`)
58
  .then(response => response.json())
59
  .then(groups => {
60
- let groupSelect = document.getElementById(`group-select-${mode}`);
61
- groupSelect.innerHTML = '<option value="">Select Group</option>';
 
 
 
 
 
62
  groups.forEach(group => {
63
- groupSelect.innerHTML += `<option value="${group.id}">${group.name}</option>`;
 
 
 
64
  });
65
- document.getElementById(`${mode}-controls`).style.display = 'block';
66
  });
67
  }
68
 
69
- // Load students based on selected group
70
- function loadStudents(mode) {
71
- const groupId = document.getElementById(`group-select-${mode}`).value;
72
- if (groupId) {
73
- fetch(`${apiUrl}/students/${groupId}`)
74
- .then(response => response.json())
75
- .then(students => {
76
- let studentSelect = document.getElementById(`student-select-${mode}`);
77
- studentSelect.innerHTML = '<option value="">Select Student</option>';
78
- students.forEach(student => {
79
- studentSelect.innerHTML += `<option value="${student.id}">${student.name}</option>`;
80
- });
81
- });
82
- }
83
- }
84
 
85
- // Borrow a book
86
- function borrowBook() {
87
- const studentId = document.getElementById('student-select-borrow').value;
88
- const bookId = document.getElementById('book-id-borrow').value;
89
- if (studentId && bookId) {
90
- fetch(`${apiUrl}/borrow/${bookId}/${studentId}`)
91
- .then(response => response.json())
92
- .then(result => {
93
- document.getElementById('borrow-result').innerText = JSON.stringify(result);
 
 
 
94
  });
95
- } else {
96
- alert('Please select a student and enter a book ID.');
97
- }
98
  }
99
 
100
- // Return a book
101
  function returnBook() {
102
- const bookId = document.getElementById('book-id-return').value;
103
- if (bookId) {
104
- fetch(`${apiUrl}/return/${bookId}?grund=rueckgabe`)
105
- .then(response => response.json())
106
- .then(result => {
107
- document.getElementById('return-result').innerText = JSON.stringify(result);
108
- });
109
- } else {
110
- alert('Please enter a book ID.');
111
  }
 
 
 
 
 
 
 
 
 
 
112
  }
113
 
114
- // List outstanding books
115
- function listOutstandingBooks() {
116
- const studentId = document.getElementById('student-select-list').value;
117
- if (studentId) {
118
- fetch(`${apiUrl}/borrowed/${studentId}`)
119
- .then(response => response.json())
120
- .then(result => {
121
- document.getElementById('list-result').innerText = JSON.stringify(result);
122
- });
123
- } else {
124
- alert('Please select a student.');
125
  }
 
 
 
 
 
 
 
 
 
 
126
  }
127
 
128
- // Inventory a book
129
- function inventoryBook() {
130
- const bookId = document.getElementById('book-id-inventory').value;
131
- if (bookId) {
132
- fetch(`${apiUrl}/return/${bookId}?grund=inventory`)
133
- .then(response => response.json())
134
- .then(result => {
135
- document.getElementById('inventory-result').innerText = JSON.stringify(result);
136
- });
137
- } else {
138
- alert('Please enter a book ID.');
139
- }
140
  }
141
  </script>
142
  </body>
 
1
  <!DOCTYPE html>
2
+ <html lang="de">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Bibliotheksverwaltung</title>
7
+ <style>
8
+ body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
9
+ select, input, button { margin: 10px 0; padding: 5px; }
10
+ #message { margin-top: 20px; font-weight: bold; }
11
+ </style>
12
  </head>
13
  <body>
14
+ <h1>Bibliotheksverwaltung</h1>
15
+
16
+ <h2>Buch zurückgeben</h2>
17
+ <input type="text" id="returnBookId" placeholder="Buchnummer eingeben">
18
+ <button onclick="returnBook()">Buch zurückgeben</button>
19
 
20
+ <h2>Buch ausleihen</h2>
21
+ <select id="groupSelect" onchange="loadStudents()">
22
+ <option value="">Gruppe auswählen</option>
23
+ </select>
24
+ <select id="studentSelect">
25
+ <option value="">Schüler auswählen</option>
26
+ </select>
27
+ <input type="text" id="borrowBookId" placeholder="Buchnummer eingeben">
28
+ <button onclick="borrowBook()">Buch ausleihen</button>
 
 
 
29
 
30
+ <div id="message"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  <script>
33
+ // Load groups when the page loads
34
+ window.onload = loadGroups;
35
 
36
+ function loadGroups() {
37
+ fetch('/groups')
 
38
  .then(response => response.json())
39
  .then(groups => {
40
+ const select = document.getElementById('groupSelect');
41
+ // Add "Alle Gruppen" option
42
+ const allGroupsOption = document.createElement('option');
43
+ allGroupsOption.value = "all";
44
+ allGroupsOption.textContent = "Alle Gruppen";
45
+ select.appendChild(allGroupsOption);
46
+ // Add other groups
47
  groups.forEach(group => {
48
+ const option = document.createElement('option');
49
+ option.value = group.idGruppe;
50
+ option.textContent = group.Gruppe;
51
+ select.appendChild(option);
52
  });
 
53
  });
54
  }
55
 
56
+ function loadStudents() {
57
+ const groupId = document.getElementById('groupSelect').value;
58
+ if (!groupId) return;
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ let url = groupId === 'all' ? '/students/all' : `/students/${groupId}`;
61
+
62
+ fetch(url)
63
+ .then(response => response.json())
64
+ .then(students => {
65
+ const select = document.getElementById('studentSelect');
66
+ select.innerHTML = '<option value="">Schüler auswählen</option>';
67
+ students.forEach(student => {
68
+ const option = document.createElement('option');
69
+ option.value = student.idKind;
70
+ option.textContent = student.Kind;
71
+ select.appendChild(option);
72
  });
73
+ });
 
 
74
  }
75
 
 
76
  function returnBook() {
77
+ const bookId = document.getElementById('returnBookId').value;
78
+ if (!bookId) {
79
+ setMessage('Bitte geben Sie eine Buchnummer ein');
80
+ return;
 
 
 
 
 
81
  }
82
+
83
+ fetch(`/return/${bookId}`)
84
+ .then(response => response.json())
85
+ .then(result => {
86
+ if (result === 0) {
87
+ setMessage('Buch nicht gefunden oder bereits zurückgegeben');
88
+ } else {
89
+ setMessage('Buch erfolgreich zurückgegeben');
90
+ }
91
+ });
92
  }
93
 
94
+ function borrowBook() {
95
+ const studentId = document.getElementById('studentSelect').value;
96
+ const bookId = document.getElementById('borrowBookId').value;
97
+
98
+ if (!studentId || !bookId) {
99
+ setMessage('Bitte wählen Sie einen Schüler aus und geben Sie eine Buchnummer ein');
100
+ return;
 
 
 
 
101
  }
102
+
103
+ fetch(`/borrow/${bookId}/${studentId}`)
104
+ .then(response => response.json())
105
+ .then(result => {
106
+ if (result.message) {
107
+ setMessage(result.message);
108
+ } else {
109
+ setMessage('Fehler beim Ausleihen des Buches: ' + JSON.stringify(result));
110
+ }
111
+ });
112
  }
113
 
114
+ function setMessage(msg) {
115
+ document.getElementById('message').textContent = msg;
 
 
 
 
 
 
 
 
 
 
116
  }
117
  </script>
118
  </body>