2026-06-03 10:43:17 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
Simple Tablet Lending and Return Management System
|
|
|
|
|
Using only built-in Python modules (no Flask required)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import sqlite3
|
|
|
|
|
import os
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
|
|
|
from urllib.parse import parse_qs, urlparse
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
# Database setup
|
|
|
|
|
DATABASE = 'tablets.db'
|
|
|
|
|
|
|
|
|
|
def get_db():
|
|
|
|
|
"""Get database connection"""
|
|
|
|
|
conn = sqlite3.connect(DATABASE)
|
|
|
|
|
conn.row_factory = sqlite3.Row
|
|
|
|
|
return conn
|
|
|
|
|
|
|
|
|
|
def init_db():
|
|
|
|
|
"""Initialize database with required tables"""
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
# Create tablets table
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
CREATE TABLE IF NOT EXISTS tablets (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
brand TEXT NOT NULL,
|
|
|
|
|
model TEXT NOT NULL,
|
|
|
|
|
serial_number TEXT UNIQUE NOT NULL,
|
|
|
|
|
status TEXT DEFAULT 'available',
|
|
|
|
|
notes TEXT
|
|
|
|
|
)
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
# Create users table
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
email TEXT,
|
|
|
|
|
phone TEXT,
|
|
|
|
|
identification TEXT UNIQUE
|
|
|
|
|
)
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
# Create loans table
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
CREATE TABLE IF NOT EXISTS loans (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
tablet_id INTEGER NOT NULL,
|
|
|
|
|
user_id INTEGER NOT NULL,
|
|
|
|
|
loan_date TEXT NOT NULL,
|
|
|
|
|
return_date TEXT,
|
|
|
|
|
status TEXT DEFAULT 'active',
|
|
|
|
|
FOREIGN KEY (tablet_id) REFERENCES tablets (id),
|
|
|
|
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
|
|
|
|
)
|
|
|
|
|
''')
|
|
|
|
|
|
2026-06-09 23:54:38 +01:00
|
|
|
# Create non_loanable_devices table
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
CREATE TABLE IF NOT EXISTS non_loanable_devices (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
brand TEXT NOT NULL,
|
|
|
|
|
model TEXT NOT NULL,
|
|
|
|
|
serial_number TEXT UNIQUE NOT NULL,
|
|
|
|
|
device_type TEXT NOT NULL,
|
|
|
|
|
location TEXT,
|
|
|
|
|
status TEXT DEFAULT 'available',
|
|
|
|
|
notes TEXT,
|
|
|
|
|
purchase_date TEXT,
|
|
|
|
|
purchase_cost REAL
|
|
|
|
|
)
|
|
|
|
|
''')
|
|
|
|
|
|
2026-06-03 10:43:17 +01:00
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
|
|
|
|
def _set_headers(self, content_type="text/html"):
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
self.send_header('Content-type', content_type)
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
|
|
def _send_html(self, content):
|
|
|
|
|
self._set_headers()
|
|
|
|
|
self.wfile.write(content.encode('utf-8'))
|
|
|
|
|
|
|
|
|
|
def _send_json(self, data):
|
|
|
|
|
self._set_headers(content_type="application/json")
|
|
|
|
|
self.wfile.write(json.dumps(data).encode('utf-8'))
|
|
|
|
|
|
|
|
|
|
def _parse_form_data(self):
|
|
|
|
|
content_length = int(self.headers['Content-Length'])
|
|
|
|
|
post_data = self.rfile.read(content_length)
|
|
|
|
|
return parse_qs(post_data.decode('utf-8'))
|
|
|
|
|
|
|
|
|
|
def do_GET(self):
|
|
|
|
|
parsed_path = urlparse(self.path)
|
|
|
|
|
|
|
|
|
|
if parsed_path.path == '/':
|
|
|
|
|
self._handle_index()
|
|
|
|
|
elif parsed_path.path == '/add_tablet':
|
|
|
|
|
self._handle_add_tablet_form()
|
|
|
|
|
elif parsed_path.path == '/add_user':
|
|
|
|
|
self._handle_add_user_form()
|
|
|
|
|
elif parsed_path.path == '/loan_tablet':
|
|
|
|
|
self._handle_loan_tablet_form()
|
|
|
|
|
elif parsed_path.path == '/history':
|
|
|
|
|
self._handle_history()
|
|
|
|
|
elif parsed_path.path.startswith('/return_tablet/'):
|
|
|
|
|
self._handle_return_tablet(parsed_path.path)
|
|
|
|
|
else:
|
|
|
|
|
self._send_html("<h1>404 Not Found</h1>")
|
|
|
|
|
|
|
|
|
|
def do_POST(self):
|
|
|
|
|
parsed_path = urlparse(self.path)
|
|
|
|
|
|
|
|
|
|
if parsed_path.path == '/add_tablet':
|
|
|
|
|
self._handle_add_tablet()
|
|
|
|
|
elif parsed_path.path == '/add_user':
|
|
|
|
|
self._handle_add_user()
|
|
|
|
|
elif parsed_path.path == '/loan_tablet':
|
|
|
|
|
self._handle_loan_tablet()
|
|
|
|
|
else:
|
|
|
|
|
self._send_html("<h1>404 Not Found</h1>")
|
|
|
|
|
|
|
|
|
|
def _handle_index(self):
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
# Get available tablets
|
|
|
|
|
cursor.execute("SELECT * FROM tablets WHERE status = 'available'")
|
|
|
|
|
available_tablets = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
# Get active loans
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
SELECT l.id, t.brand, t.model, t.serial_number, u.name, l.loan_date
|
|
|
|
|
FROM loans l
|
|
|
|
|
JOIN tablets t ON l.tablet_id = t.id
|
|
|
|
|
JOIN users u ON l.user_id = u.id
|
|
|
|
|
WHERE l.status = 'active'
|
|
|
|
|
''')
|
|
|
|
|
active_loans = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
html = f"""
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Tablet Management System</title>
|
|
|
|
|
<style>
|
|
|
|
|
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
|
|
|
|
h1 {{ color: #333; }}
|
|
|
|
|
.nav {{ margin-bottom: 20px; }}
|
|
|
|
|
.nav a {{ margin-right: 15px; text-decoration: none; color: #4CAF50; }}
|
|
|
|
|
table {{ width: 100%; border-collapse: collapse; margin-bottom: 20px; }}
|
|
|
|
|
th, td {{ padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }}
|
|
|
|
|
th {{ background-color: #4CAF50; color: white; }}
|
|
|
|
|
.btn {{ padding: 5px 10px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 3px; }}
|
|
|
|
|
.btn-danger {{ background-color: #f44336; }}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Tablet Management System</h1>
|
|
|
|
|
<div class="nav">
|
|
|
|
|
<a href="/">Home</a>
|
|
|
|
|
<a href="/add_tablet">Add Tablet</a>
|
|
|
|
|
<a href="/add_user">Add User</a>
|
|
|
|
|
<a href="/loan_tablet">Loan Tablet</a>
|
|
|
|
|
<a href="/history">Loan History</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2>Available Tablets</h2>
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Brand</th>
|
|
|
|
|
<th>Model</th>
|
|
|
|
|
<th>Serial Number</th>
|
|
|
|
|
<th>Notes</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if available_tablets:
|
|
|
|
|
for tablet in available_tablets:
|
|
|
|
|
html += f"""
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{tablet['brand']}</td>
|
|
|
|
|
<td>{tablet['model']}</td>
|
|
|
|
|
<td>{tablet['serial_number']}</td>
|
|
|
|
|
<td>{tablet['notes'] or '-'}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
"""
|
|
|
|
|
else:
|
|
|
|
|
html += "<tr><td colspan='4'>No available tablets.</td></tr>"
|
|
|
|
|
|
|
|
|
|
html += """
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
<h2>Active Loans</h2>
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Tablet</th>
|
|
|
|
|
<th>Serial Number</th>
|
|
|
|
|
<th>Borrower</th>
|
|
|
|
|
<th>Loan Date</th>
|
|
|
|
|
<th>Actions</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if active_loans:
|
|
|
|
|
for loan in active_loans:
|
|
|
|
|
html += f"""
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{loan['brand']} {loan['model']}</td>
|
|
|
|
|
<td>{loan['serial_number']}</td>
|
|
|
|
|
<td>{loan['name']}</td>
|
|
|
|
|
<td>{loan['loan_date']}</td>
|
|
|
|
|
<td><a href='/return_tablet/{loan['id']}' class='btn btn-danger'>Return</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
"""
|
|
|
|
|
else:
|
|
|
|
|
html += "<tr><td colspan='5'>No active loans.</td></tr>"
|
|
|
|
|
|
|
|
|
|
html += """
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
self._send_html(html)
|
|
|
|
|
|
|
|
|
|
def _handle_add_tablet_form(self):
|
|
|
|
|
html = """
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Add Tablet</title>
|
|
|
|
|
<style>
|
|
|
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
|
|
|
.form-group { margin-bottom: 15px; }
|
|
|
|
|
label { display: block; margin-bottom: 5px; }
|
|
|
|
|
input, textarea { width: 100%; padding: 8px; }
|
|
|
|
|
.btn { padding: 8px 16px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Add New Tablet</h1>
|
|
|
|
|
<form method="POST" action="/add_tablet">
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="brand">Brand:</label>
|
|
|
|
|
<input type="text" id="brand" name="brand" required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="model">Model:</label>
|
|
|
|
|
<input type="text" id="model" name="model" required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="serial_number">Serial Number:</label>
|
|
|
|
|
<input type="text" id="serial_number" name="serial_number" required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="notes">Notes:</label>
|
|
|
|
|
<textarea id="notes" name="notes"></textarea>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<button type="submit" class="btn">Add Tablet</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
"""
|
|
|
|
|
self._send_html(html)
|
|
|
|
|
|
|
|
|
|
def _handle_add_tablet(self):
|
|
|
|
|
form_data = self._parse_form_data()
|
|
|
|
|
brand = form_data['brand'][0]
|
|
|
|
|
model = form_data['model'][0]
|
|
|
|
|
serial_number = form_data['serial_number'][0]
|
|
|
|
|
notes = form_data.get('notes', [''])[0]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
INSERT INTO tablets (brand, model, serial_number, status, notes)
|
|
|
|
|
VALUES (?, ?, ?, 'available', ?)
|
|
|
|
|
''', (brand, model, serial_number, notes))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
self.send_response(303)
|
|
|
|
|
self.send_header('Location', '/')
|
|
|
|
|
self.end_headers()
|
|
|
|
|
except sqlite3.IntegrityError:
|
|
|
|
|
self._send_html("<h1>Error: Serial number already exists!</h1><p><a href='/add_tablet'>Try again</a></p>")
|
|
|
|
|
|
|
|
|
|
def _handle_add_user_form(self):
|
|
|
|
|
html = """
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Add User</title>
|
|
|
|
|
<style>
|
|
|
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
|
|
|
.form-group { margin-bottom: 15px; }
|
|
|
|
|
label { display: block; margin-bottom: 5px; }
|
|
|
|
|
input { width: 100%; padding: 8px; }
|
|
|
|
|
.btn { padding: 8px 16px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Add New User</h1>
|
|
|
|
|
<form method="POST" action="/add_user">
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="name">Name:</label>
|
|
|
|
|
<input type="text" id="name" name="name" required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="email">Email:</label>
|
|
|
|
|
<input type="email" id="email" name="email">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="phone">Phone:</label>
|
|
|
|
|
<input type="text" id="phone" name="phone">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="identification">Identification:</label>
|
|
|
|
|
<input type="text" id="identification" name="identification" required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<button type="submit" class="btn">Add User</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
"""
|
|
|
|
|
self._send_html(html)
|
|
|
|
|
|
|
|
|
|
def _handle_add_user(self):
|
|
|
|
|
form_data = self._parse_form_data()
|
|
|
|
|
name = form_data['name'][0]
|
|
|
|
|
email = form_data.get('email', [''])[0]
|
|
|
|
|
phone = form_data.get('phone', [''])[0]
|
|
|
|
|
identification = form_data['identification'][0]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
INSERT INTO users (name, email, phone, identification)
|
|
|
|
|
VALUES (?, ?, ?, ?)
|
|
|
|
|
''', (name, email, phone, identification))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
self.send_response(303)
|
|
|
|
|
self.send_header('Location', '/')
|
|
|
|
|
self.end_headers()
|
|
|
|
|
except sqlite3.IntegrityError:
|
|
|
|
|
self._send_html("<h1>Error: Identification already exists!</h1><p><a href='/add_user'>Try again</a></p>")
|
|
|
|
|
|
|
|
|
|
def _handle_loan_tablet_form(self):
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("SELECT * FROM tablets WHERE status = 'available'")
|
|
|
|
|
available_tablets = cursor.fetchall()
|
|
|
|
|
cursor.execute("SELECT * FROM users")
|
|
|
|
|
users = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
html = """
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Loan Tablet</title>
|
|
|
|
|
<style>
|
|
|
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
|
|
|
.form-group { margin-bottom: 15px; }
|
|
|
|
|
label { display: block; margin-bottom: 5px; }
|
|
|
|
|
select { width: 100%; padding: 8px; }
|
|
|
|
|
.btn { padding: 8px 16px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Loan Tablet</h1>
|
|
|
|
|
<form method="POST" action="/loan_tablet">
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="tablet_id">Tablet:</label>
|
|
|
|
|
<select id="tablet_id" name="tablet_id" required>
|
|
|
|
|
<option value="">Select a tablet</option>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
for tablet in available_tablets:
|
|
|
|
|
html += f"<option value='{tablet['id']}'>{tablet['brand']} {tablet['model']} ({tablet['serial_number']})</option>"
|
|
|
|
|
|
|
|
|
|
html += """
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label for="user_id">User:</label>
|
|
|
|
|
<select id="user_id" name="user_id" required>
|
|
|
|
|
<option value="">Select a user</option>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
for user in users:
|
|
|
|
|
html += f"<option value='{user['id']}'>{user['name']} ({user['identification']})</option>"
|
|
|
|
|
|
|
|
|
|
html += """
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<button type="submit" class="btn">Loan Tablet</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
"""
|
|
|
|
|
self._send_html(html)
|
|
|
|
|
|
|
|
|
|
def _handle_loan_tablet(self):
|
|
|
|
|
form_data = self._parse_form_data()
|
|
|
|
|
tablet_id = form_data['tablet_id'][0]
|
|
|
|
|
user_id = form_data['user_id'][0]
|
|
|
|
|
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
# Update tablet status
|
|
|
|
|
cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet_id,))
|
|
|
|
|
|
|
|
|
|
# Create loan record
|
|
|
|
|
loan_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
INSERT INTO loans (tablet_id, user_id, loan_date, status)
|
|
|
|
|
VALUES (?, ?, ?, 'active')
|
|
|
|
|
''', (tablet_id, user_id, loan_date))
|
|
|
|
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
self.send_response(303)
|
|
|
|
|
self.send_header('Location', '/')
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
|
|
def _handle_return_tablet(self, path):
|
|
|
|
|
loan_id = path.split('/')[-1]
|
|
|
|
|
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
# Get loan information
|
|
|
|
|
cursor.execute("SELECT tablet_id FROM loans WHERE id = ?", (loan_id,))
|
|
|
|
|
loan = cursor.fetchone()
|
|
|
|
|
|
|
|
|
|
if loan:
|
|
|
|
|
tablet_id = loan['tablet_id']
|
|
|
|
|
|
|
|
|
|
# Update loan status and return date
|
|
|
|
|
return_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
UPDATE loans SET status = 'returned', return_date = ? WHERE id = ?
|
|
|
|
|
''', (return_date, loan_id))
|
|
|
|
|
|
|
|
|
|
# Update tablet status
|
|
|
|
|
cursor.execute("UPDATE tablets SET status = 'available' WHERE id = ?", (tablet_id,))
|
|
|
|
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
self.send_response(303)
|
|
|
|
|
self.send_header('Location', '/')
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
|
|
def _handle_history(self):
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
SELECT l.id, t.brand, t.model, t.serial_number, u.name,
|
|
|
|
|
l.loan_date, l.return_date, l.status
|
|
|
|
|
FROM loans l
|
|
|
|
|
JOIN tablets t ON l.tablet_id = t.id
|
|
|
|
|
JOIN users u ON l.user_id = u.id
|
|
|
|
|
ORDER BY l.loan_date DESC
|
|
|
|
|
''')
|
|
|
|
|
loans = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
html = """
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Loan History</title>
|
|
|
|
|
<style>
|
|
|
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
|
|
|
h1 { color: #333; }
|
|
|
|
|
.nav { margin-bottom: 20px; }
|
|
|
|
|
.nav a { margin-right: 15px; text-decoration: none; color: #4CAF50; }
|
|
|
|
|
table { width: 100%; border-collapse: collapse; }
|
|
|
|
|
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
|
|
|
|
|
th { background-color: #4CAF50; color: white; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Loan History</h1>
|
|
|
|
|
<div class="nav">
|
|
|
|
|
<a href="/">Home</a>
|
|
|
|
|
<a href="/add_tablet">Add Tablet</a>
|
|
|
|
|
<a href="/add_user">Add User</a>
|
|
|
|
|
<a href="/loan_tablet">Loan Tablet</a>
|
|
|
|
|
<a href="/history">Loan History</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Tablet</th>
|
|
|
|
|
<th>Serial Number</th>
|
|
|
|
|
<th>Borrower</th>
|
|
|
|
|
<th>Loan Date</th>
|
|
|
|
|
<th>Return Date</th>
|
|
|
|
|
<th>Status</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if loans:
|
|
|
|
|
for loan in loans:
|
|
|
|
|
html += f"""
|
|
|
|
|
<tr>
|
|
|
|
|
<td>{loan['brand']} {loan['model']}</td>
|
|
|
|
|
<td>{loan['serial_number']}</td>
|
|
|
|
|
<td>{loan['name']}</td>
|
|
|
|
|
<td>{loan['loan_date']}</td>
|
|
|
|
|
<td>{loan['return_date'] or '-'}</td>
|
|
|
|
|
<td>{loan['status']}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
"""
|
|
|
|
|
else:
|
|
|
|
|
html += "<tr><td colspan='6'>No loan history available.</td></tr>"
|
|
|
|
|
|
|
|
|
|
html += """
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
self._send_html(html)
|
|
|
|
|
|
|
|
|
|
def run_server():
|
|
|
|
|
"""Run the HTTP server"""
|
|
|
|
|
server_address = ('', 8000)
|
|
|
|
|
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
|
|
|
|
|
print(f"Server running on http://localhost:8000")
|
|
|
|
|
print("Press Ctrl+C to stop the server")
|
|
|
|
|
httpd.serve_forever()
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
# Initialize database
|
|
|
|
|
init_db()
|
|
|
|
|
|
|
|
|
|
# Run the server
|
|
|
|
|
run_server()
|