commit 8a8d2f02fa0164c06879111db19e6d815c588507 Author: ijuanes Date: Wed Jun 3 10:43:17 2026 +0100 Initial commit: Tablet lending system with user-tablet many-to-many relationship - Database schema: tablets, users, loans (junction table) - CLI app: minimal_app.py - Web apps: app.py (Flask), simple_app.py (http.server) - Features: loan management, search, user loans view, project notes - Git structure: CONTRIBUTING.md, .gitignore Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec5f329 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info/ + +# Virtual environments +.venv/ + +# Database files +*.db +*.db-journal + +# Project-specific +notes_development/ +.vibe/ + +# IDE/Editor +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Python +.python-version diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c37e12e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,82 @@ +# Contributing Guide + +## Git Workflow + +### Branch Strategy + +| Branch Type | Naming | Purpose | +|-------------|--------|---------| +| `main` | `main` | Production-ready code | +| `develop` | `develop` | Integration branch for features | +| Feature | `feature/` | New functionality | +| Hotfix | `hotfix/` | Urgent bug fixes | + +### Workflow + +```bash +# Clone repository +git clone +cd GestionTablets + +# Setup develop branch +git checkout -b develop + +# Create feature branch +git checkout -b feature/my-feature +git push origin feature/my-feature + +# Make changes, commit +git add . +git commit -m "feat: add my feature" +git push origin feature/my-feature + +# Create Pull Request to develop +``` + +### Commit Message Format + +``` +type(scope): description + +[optional body] + +[optional footer] +``` + +**Types**: +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `refactor`: Code refactoring +- `chore`: Maintenance tasks +- `test`: Test-related changes + +**Examples**: +- `feat(loans): add user loans view page` +- `fix(tablet): validate serial number uniqueness` +- `docs: update README with database schema` +- `refactor(app): extract loan logic to service` + +### Pull Request Process + +1. Target `develop` branch (or `main` for hotfixes) +2. Include clear description of changes +3. Reference related issues +4. Wait for code review +5. Squash merge preferred + +### Reverting Changes + +```bash +# Discard unstaged changes +git checkout -- file.txt + +# Unstage file +git reset HEAD file.txt + +# Revert to previous commit (safe) +git revert HEAD + +# Hard reset to commit (destructive) +git reset --hard +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..58fab68 --- /dev/null +++ b/README.md @@ -0,0 +1,158 @@ +# Tablet Lending and Return Management System + +A simple SQLite-based system for managing tablet lending and returns. + +## Features + +- **Tablet Management**: Add, track, and manage tablets in inventory +- **User Management**: Register users who can borrow tablets +- **Loan Tracking**: Track which tablets are loaned to which users +- **Return Management**: Record when tablets are returned +- **History Tracking**: Complete history of all loans and returns +- **User Loans View**: See all tablets loaned to each user (demonstrates one-to-many relationship) +- **Project Notes**: Markdown editor for development documentation + +## Files + +- `minimal_app.py` - Interactive command-line application +- `test_app.py` - Test script that demonstrates functionality +- `tablets.db` - SQLite database (created automatically) +- `simple_app.py` - Web-based version (requires Flask) +- `app.py` - Alternative web version (requires Flask) + +## Quick Start + +### 1. Run the test to see it in action + +```bash +python3 test_app.py +``` + +This will: +- Create the database +- Add sample tablets and users +- Demonstrate loan and return operations +- Show the complete workflow + +### 2. Run the interactive application + +```bash +python3 minimal_app.py +``` + +This provides a menu-driven interface for: +- Adding tablets +- Adding users +- Loaning tablets +- Returning tablets +- Viewing inventory and loan status + +### 3. Database Structure + +The system uses three main tables with a **many-to-many relationship** between users and tablets: + +**tablets** +- `id`: Primary key +- `brand`: Tablet brand +- `model`: Tablet model +- `serial_number`: Unique serial number +- `status`: 'available' or 'loaned' + +**users** +- `id`: Primary key +- `name`: User's name +- `identification`: Unique identification + +**loans** (junction table) +- `id`: Primary key +- `tablet_id`: Foreign key to tablets +- `user_id`: Foreign key to users +- `loan_date`: When the tablet was loaned +- `return_date`: When the tablet was returned (NULL if still active) +- `status`: 'active' or 'returned' + +> **Note**: The loans table enables a many-to-many relationship. One user can loan **multiple tablets** (one-to-many from user to loans), and each tablet can be loaned to different users over time. The `status` field on tablets ensures a device can only be loaned to one user at a time. + +## Requirements + +- Python 3.x (built-in sqlite3 module) +- No additional dependencies needed for the command-line version +- Flask required for web versions (optional) + +## Usage Examples + +### Adding a tablet +``` +python3 -c " +import sqlite3 +conn = sqlite3.connect('tablets.db') +cursor = conn.cursor() +cursor.execute('INSERT INTO tablets (brand, model, serial_number, status) VALUES (?, ?, ?, ?)', + ('Microsoft', 'Surface Pro', 'SN004', 'available')) +conn.commit() +conn.close() +print('Tablet added!') +" +``` + +### Adding a user +``` +python3 -c " +import sqlite3 +conn = sqlite3.connect('tablets.db') +cursor = conn.cursor() +cursor.execute('INSERT INTO users (name, identification) VALUES (?, ?)', + ('Alice Brown', 'ID004')) +conn.commit() +conn.close() +print('User added!') +" +``` + +### Querying available tablets +``` +python3 -c " +import sqlite3 +conn = sqlite3.connect('tablets.db') +cursor = conn.cursor() +cursor.execute('SELECT brand, model, serial_number FROM tablets WHERE status = ?', ('available',)) +for row in cursor.fetchall(): + print(f'{row[0]} {row[1]} ({row[2]})') +conn.close() +" +``` + +## Web Version (Optional) + +If you want to use the web interface: + +1. Install Flask: +```bash +pip install flask +``` + +2. Run the web application: +```bash +python3 app.py +``` + +3. Open your browser to: http://localhost:5000 + +### Web Features + +- **Home**: View available tablets and active loans +- **Loan Tablet**: Loan a tablet to a user (with search for large datasets) +- **User Loans**: View all tablets loaned to each user - demonstrates the **one-to-many relationship** (one user, multiple tablets) +- **Loan History**: Complete history of all loan and return transactions +- **Project Management**: Markdown editor for development notes (saved to `notes_development/project_notes.md`) + +## Database Backup + +To backup your data: +```bash +cp tablets.db tablets_backup_$(date +%Y%m%d).db +``` + +## License + +This is a simple educational project. Feel free to use and modify it as needed. \ No newline at end of file diff --git a/RUNNING.md b/RUNNING.md new file mode 100644 index 0000000..9c37f3d --- /dev/null +++ b/RUNNING.md @@ -0,0 +1,57 @@ +# Tablet Management System - Running + +✅ **Status**: Application is running successfully! + +## Access Information + +- **Web Interface**: [http://localhost:5000](http://localhost:5000) +- **Port**: 5000 +- **Framework**: Flask 3.1.3 +- **Database**: SQLite (`tablets.db`) + +## How to Use + +### Web Interface +1. Open your browser to: [http://localhost:5000](http://localhost:5000) +2. Use the navigation menu to: + - **Home**: View available tablets and active loans + - **Add Tablet**: Add new tablets to inventory + - **Add User**: Register new users + - **Loan Tablet**: Loan a tablet to a user + - **Loan History**: View complete loan history + +### Command Line (Alternative) +If you prefer command line: +```bash +# Run the interactive application +python3 minimal_app.py + +# Run the test/demo +python3 test_app.py +``` + +## Current Data +The system already contains test data: +- **3 Tablets**: Samsung Galaxy Tab S7, Apple iPad Pro, Lenovo Tab P11 +- **3 Users**: John Doe, Jane Smith, Bob Johnson +- **2 Loans**: One active, one returned + +## Stopping the Server +To stop the Flask application: +```bash +pkill -f "python app.py" +``` + +## Technical Details +- **Backend**: SQLite 3 +- **Frontend**: Flask with HTML/CSS +- **Port**: 5000 (configurable in app.py) +- **Virtual Environment**: `.venv/` (created with uv) + +## Troubleshooting +If you have issues: +1. Check if the server is running: `ps aux | grep app.py` +2. Check the database: `sqlite3 tablets.db` +3. Restart the server: `source .venv/bin/activate && python app.py` + +Enjoy managing your tablets! 📱💻 \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..a5332b7 --- /dev/null +++ b/app.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python3 +""" +Simple Tablet Lending and Return Management Web Application +with SQLite backend +""" + +from flask import Flask, render_template, request, redirect, url_for, flash +import sqlite3 +import os +from datetime import datetime + +app = Flask(__name__) +app.secret_key = 'your_secret_key_here' + +# 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) + ) + ''') + + conn.commit() + +@app.route('/') +def index(): + """Main page showing available tablets and active loans""" + 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() + + return render_template('index.html', + available_tablets=available_tablets, + active_loans=active_loans) + +@app.route('/add_tablet', methods=['GET', 'POST']) +def add_tablet(): + """Add a new tablet to inventory""" + if request.method == 'POST': + brand = request.form['brand'] + model = request.form['model'] + serial_number = request.form['serial_number'] + notes = request.form['notes'] + + 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() + flash('Tablet added successfully!', 'success') + except sqlite3.IntegrityError: + flash('Error: Serial number already exists!', 'error') + + return redirect(url_for('index')) + + return render_template('add_tablet.html') + +@app.route('/add_user', methods=['GET', 'POST']) +def add_user(): + """Add a new user""" + if request.method == 'POST': + name = request.form['name'] + email = request.form['email'] + phone = request.form['phone'] + identification = request.form['identification'] + + 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() + flash('User added successfully!', 'success') + except sqlite3.IntegrityError: + flash('Error: Identification already exists!', 'error') + + return redirect(url_for('index')) + + return render_template('add_user.html') + +@app.route('/loan_tablet', methods=['GET', 'POST']) +def loan_tablet(): + """Loan a tablet to a user""" + if request.method == 'POST': + tablet_id = request.form['tablet_id'] + user_id = request.form['user_id'] + + 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() + flash('Tablet loaned successfully!', 'success') + + return redirect(url_for('index')) + + # Get data for form + 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() + + return render_template('loan_tablet.html', + available_tablets=available_tablets, + users=users) + +@app.route('/return_tablet/') +def return_tablet(loan_id): + """Return a loaned tablet""" + 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() + flash('Tablet returned successfully!', 'success') + else: + flash('Error: Loan not found!', 'error') + + return redirect(url_for('index')) + +@app.route('/history') +def history(): + """Show loan history""" + 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() + + return render_template('history.html', loans=loans) + + +@app.route('/project_management') +def project_management(): + """Project management page with markdown editor for development notes""" + return render_template('project_management.html') + + +@app.route('/get_notes/') +def get_notes(filename): + """Serve notes file content""" + try: + with open(filename, 'r', encoding='utf-8') as f: + content = f.read() + return content, 200, { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'no-cache, no-store, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0' + } + except FileNotFoundError: + return '', 404 + except Exception as e: + return str(e), 500 + + +@app.route('/save_notes', methods=['POST']) +def save_notes(): + """Save markdown notes to file""" + data = request.get_json() + content = data.get('content', '') + filename = data.get('filename', 'notes_development/project_notes.md') + + try: + # Ensure directory exists + os.makedirs(os.path.dirname(filename), exist_ok=True) + + # Write content to file + with open(filename, 'w', encoding='utf-8') as f: + f.write(content) + + return {'status': 'success', 'message': 'Notes saved successfully'} + except Exception as e: + return {'status': 'error', 'message': str(e)}, 500 + + +@app.route('/user_loans') +def user_loans(): + """ + Show all loans grouped by user. + This demonstrates the one-to-many relationship: one user can loan multiple tablets. + """ + with get_db() as conn: + cursor = conn.cursor() + + # Get all users + cursor.execute("SELECT * FROM users") + users = cursor.fetchall() + + # Get all loans with tablet and user details + cursor.execute(''' + SELECT l.id, l.tablet_id, l.user_id, l.loan_date, l.return_date, l.status, + t.brand, t.model, t.serial_number, + u.name, u.identification + FROM loans l + JOIN tablets t ON l.tablet_id = t.id + JOIN users u ON l.user_id = u.id + ORDER BY u.name, l.loan_date DESC + ''') + loans = cursor.fetchall() + + # Group loans by user + user_loans_map = {} + for loan in loans: + user_id = loan['user_id'] + if user_id not in user_loans_map: + user_loans_map[user_id] = { + 'user': None, + 'loans': [] + } + user_loans_map[user_id]['loans'].append(loan) + + # Attach user info + for user in users: + if user['id'] in user_loans_map: + user_loans_map[user['id']]['user'] = user + + # Filter out users with no loans (optional - keep all users) + # Convert to list for template + user_loans_list = [] + for user in users: + user_data = user_loans_map.get(user['id'], {'user': user, 'loans': []}) + user_loans_list.append(user_data) + + return render_template('user_loans.html', users_with_loans=user_loans_list) + + +if __name__ == '__main__': + # Initialize database + init_db() + + # Create templates directory if it doesn't exist + if not os.path.exists('templates'): + os.makedirs('templates') + + app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/basic_server.py b/basic_server.py new file mode 100644 index 0000000..b2731e4 --- /dev/null +++ b/basic_server.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Basic HTTP server to serve the tablet management system +""" + +from http.server import SimpleHTTPRequestHandler, HTTPServer +import os + +class MyHandler(SimpleHTTPRequestHandler): + def do_GET(self): + if self.path == '/': + self.path = '/index.html' + return super().do_GET() + +def run_server(): + port = 8080 + server_address = ('', port) + httpd = HTTPServer(server_address, MyHandler) + + # Create a simple index file if it doesn't exist + if not os.path.exists('index.html'): + with open('index.html', 'w') as f: + f.write(''' + + + Tablet Management System + + + +

Tablet Management System

+ +
+

Welcome to the Tablet Management System!

+

This system is running with a SQLite backend.

+ +

Available Commands:

+
    +
  • Test the system: python3 test_app.py
  • +
  • Run interactive mode: python3 minimal_app.py
  • +
  • Check database: sqlite3 tablets.db
  • +
+ +

Current Status:

+

✓ Database: tablets.db

+

✓ Web server: Running on port 8080

+

✓ System: Ready for use

+
+ +

Quick Test Results:

+
Running tests...
+ + + +''') + + print(f"Server running on http://localhost:{port}") + print("Serving current directory") + print("Press Ctrl+C to stop the server") + + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nServer stopped") + +if __name__ == '__main__': + run_server() \ No newline at end of file diff --git a/check_flask.py b/check_flask.py new file mode 100644 index 0000000..6ea9046 --- /dev/null +++ b/check_flask.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +""" +Check if Flask is available and provide installation instructions +""" + +try: + import flask + print("✓ Flask is available!") + print("You can run the web version:") + print(" python3 simple_app.py") + print("Then open: http://localhost:8000") +except ImportError: + print("✗ Flask is not installed") + print("\nTo install Flask:") + print(" pip install flask") + print("\nOr use the system package manager:") + print(" sudo apt install python3-flask") + print("\nYou can still use the command-line version:") + print(" python3 minimal_app.py") + print(" python3 test_app.py") \ No newline at end of file diff --git a/debug_server.py b/debug_server.py new file mode 100644 index 0000000..7645070 --- /dev/null +++ b/debug_server.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +from http.server import HTTPServer, BaseHTTPRequestHandler + +class DebugHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.end_headers() + self.wfile.write(b'Hello from Tablet Management Server!') + +def run(): + try: + server = HTTPServer(('', 8000), DebugHandler) + print("Debug server running on port 8000") + server.serve_forever() + except Exception as e: + print(f"Error: {e}") + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..099519d --- /dev/null +++ b/index.html @@ -0,0 +1,47 @@ + + + + Tablet Management System + + + +

Tablet Management System

+ +
+

Welcome to the Tablet Management System!

+

This system is running with a SQLite backend.

+ +

Available Commands:

+
    +
  • Test the system: python3 test_app.py
  • +
  • Run interactive mode: python3 minimal_app.py
  • +
  • Check database: sqlite3 tablets.db
  • +
+ +

Current Status:

+

✓ Database: tablets.db

+

✓ Web server: Running on port 8080

+

✓ System: Ready for use

+
+ +

Quick Test Results:

+
Running tests...
+ + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..494f954 --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from gestiontablets!") + + +if __name__ == "__main__": + main() diff --git a/minimal_app.py b/minimal_app.py new file mode 100644 index 0000000..ff0295a --- /dev/null +++ b/minimal_app.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +""" +Minimal Tablet Lending and Return Management System +Using only built-in Python modules +""" + +import sqlite3 +from datetime import datetime + +def init_db(): + """Initialize database with required tables""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + # Create tables + 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' + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + identification TEXT UNIQUE + ) + ''') + + 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) + ) + ''') + + conn.commit() + conn.close() + +def add_tablet(brand, model, serial_number): + """Add a new tablet to inventory""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + try: + cursor.execute(''' + INSERT INTO tablets (brand, model, serial_number, status) + VALUES (?, ?, ?, 'available') + ''', (brand, model, serial_number)) + conn.commit() + print(f"✓ Added tablet: {brand} {model} ({serial_number})") + except sqlite3.IntegrityError: + print(f"✗ Error: Serial number {serial_number} already exists") + finally: + conn.close() + +def add_user(name, identification): + """Add a new user""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + try: + cursor.execute(''' + INSERT INTO users (name, identification) + VALUES (?, ?) + ''', (name, identification)) + conn.commit() + print(f"✓ Added user: {name} ({identification})") + except sqlite3.IntegrityError: + print(f"✗ Error: Identification {identification} already exists") + finally: + conn.close() + +def loan_tablet(tablet_id, user_id): + """Loan a tablet to a user""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + # Check if tablet is available + cursor.execute("SELECT status FROM tablets WHERE id = ?", (tablet_id,)) + result = cursor.fetchone() + + if result and result[0] == 'available': + # 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() + print(f"✓ Tablet {tablet_id} loaned to user {user_id}") + else: + print(f"✗ Error: Tablet {tablet_id} is not available for loan") + + conn.close() + +def return_tablet(loan_id): + """Return a loaned tablet""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + # Get loan information + cursor.execute("SELECT tablet_id FROM loans WHERE id = ? AND status = 'active'", (loan_id,)) + loan = cursor.fetchone() + + if loan: + tablet_id = loan[0] + + # 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() + print(f"✓ Tablet returned for loan {loan_id}") + else: + print(f"✗ Error: Loan {loan_id} not found or already returned") + + conn.close() + +def show_available_tablets(): + """Show available tablets""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + cursor.execute("SELECT id, brand, model, serial_number FROM tablets WHERE status = 'available'") + tablets = cursor.fetchall() + + print("\n=== Available Tablets ===") + if tablets: + for tablet in tablets: + print(f"ID: {tablet[0]}, {tablet[1]} {tablet[2]} ({tablet[3]})") + else: + print("No available tablets") + + conn.close() + +def show_active_loans(): + """Show active loans""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + 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' + ''') + loans = cursor.fetchall() + + print("\n=== Active Loans ===") + if loans: + for loan in loans: + print(f"Loan ID: {loan[0]}, Tablet: {loan[1]} {loan[2]} ({loan[3]}), Borrower: {loan[4]}, Loan Date: {loan[5]}") + else: + print("No active loans") + + conn.close() + +def show_loan_history(): + """Show complete loan history""" + conn = sqlite3.connect('tablets.db') + 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() + + print("\n=== Loan History ===") + if loans: + for loan in loans: + return_date = loan[6] or 'Not returned' + print(f"Loan ID: {loan[0]}, Tablet: {loan[1]} {loan[2]} ({loan[3]}), Borrower: {loan[4]}") + print(f" Loan Date: {loan[5]}, Return Date: {return_date}, Status: {loan[7]}") + else: + print("No loan history") + + conn.close() + +def main(): + """Main menu""" + init_db() + + print("=== Tablet Lending and Return Management System ===") + print("Using SQLite database: tablets.db") + + while True: + print("\nMenu:") + print("1. Add Tablet") + print("2. Add User") + print("3. Loan Tablet") + print("4. Return Tablet") + print("5. Show Available Tablets") + print("6. Show Active Loans") + print("7. Show Loan History") + print("8. Exit") + + choice = input("Enter your choice (1-8): ") + + if choice == '1': + print("\n=== Add Tablet ===") + brand = input("Brand: ") + model = input("Model: ") + serial_number = input("Serial Number: ") + add_tablet(brand, model, serial_number) + + elif choice == '2': + print("\n=== Add User ===") + name = input("Name: ") + identification = input("Identification: ") + add_user(name, identification) + + elif choice == '3': + print("\n=== Loan Tablet ===") + show_available_tablets() + show_users() + + tablet_id = input("Enter Tablet ID to loan: ") + user_id = input("Enter User ID: ") + + try: + loan_tablet(int(tablet_id), int(user_id)) + except ValueError: + print("✗ Error: Invalid ID format") + + elif choice == '4': + print("\n=== Return Tablet ===") + show_active_loans() + + loan_id = input("Enter Loan ID to return: ") + + try: + return_tablet(int(loan_id)) + except ValueError: + print("✗ Error: Invalid Loan ID format") + + elif choice == '5': + show_available_tablets() + + elif choice == '6': + show_active_loans() + + elif choice == '7': + show_loan_history() + + elif choice == '8': + print("Goodbye!") + break + + else: + print("✗ Invalid choice. Please try again.") + +def show_users(): + """Show available users""" + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + cursor.execute("SELECT id, name, identification FROM users") + users = cursor.fetchall() + + print("\n=== Available Users ===") + if users: + for user in users: + print(f"ID: {user[0]}, {user[1]} ({user[2]})") + else: + print("No users available") + + conn.close() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e409926 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "gestiontablets" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.14" +dependencies = [] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cc35792 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask==2.3.3 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..47fe889 --- /dev/null +++ b/setup.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Setup script for Tablet Management System +""" + +import subprocess +import sys +import os + +def install_dependencies(): + """Install required dependencies""" + try: + # Try to install Flask using pip + subprocess.check_call([sys.executable, '-m', 'ensurepip', '--upgrade']) + subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--upgrade', 'pip']) + subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'Flask==2.3.3']) + print("✓ Dependencies installed successfully") + return True + except subprocess.CalledProcessError as e: + print(f"✗ Failed to install dependencies: {e}") + return False + except Exception as e: + print(f"✗ Error during installation: {e}") + return False + +def check_dependencies(): + """Check if required dependencies are available""" + try: + import flask + import sqlite3 + print("✓ All dependencies are available") + return True + except ImportError as e: + print(f"✗ Missing dependency: {e}") + return False + +def main(): + print("Setting up Tablet Management System...") + + # Check if dependencies are already available + if not check_dependencies(): + print("Installing required dependencies...") + if not install_dependencies(): + print("\nError: Could not install dependencies.") + print("Please install Flask manually:") + print(" pip install Flask==2.3.3") + print("or") + print(" python3 -m pip install Flask==2.3.3") + return False + + print("\nSetup complete!") + print("\nTo run the application:") + print(" python3 app.py") + print("\nThe application will be available at: http://localhost:5000") + + return True + +if __name__ == '__main__': + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/simple_app.py b/simple_app.py new file mode 100644 index 0000000..f50478d --- /dev/null +++ b/simple_app.py @@ -0,0 +1,555 @@ +#!/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) + ) + ''') + + 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("

404 Not Found

") + + 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("

404 Not Found

") + + 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""" + + + + Tablet Management System + + + +

Tablet Management System

+ + +

Available Tablets

+ + + + + + + + + + + """ + + if available_tablets: + for tablet in available_tablets: + html += f""" + + + + + + + """ + else: + html += "" + + html += """ + +
BrandModelSerial NumberNotes
{tablet['brand']}{tablet['model']}{tablet['serial_number']}{tablet['notes'] or '-'}
No available tablets.
+ +

Active Loans

+ + + + + + + + + + + + """ + + if active_loans: + for loan in active_loans: + html += f""" + + + + + + + + """ + else: + html += "" + + html += """ + +
TabletSerial NumberBorrowerLoan DateActions
{loan['brand']} {loan['model']}{loan['serial_number']}{loan['name']}{loan['loan_date']}Return
No active loans.
+ + + """ + + self._send_html(html) + + def _handle_add_tablet_form(self): + html = """ + + + + Add Tablet + + + +

Add New Tablet

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + """ + 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("

Error: Serial number already exists!

Try again

") + + def _handle_add_user_form(self): + html = """ + + + + Add User + + + +

Add New User

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + """ + 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("

Error: Identification already exists!

Try again

") + + 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 = """ + + + + Loan Tablet + + + +

Loan Tablet

+
+
+ + +
+
+ + +
+
+ +
+
+ + + """ + 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 = """ + + + + Loan History + + + +

Loan History

+ + + + + + + + + + + + + + + """ + + if loans: + for loan in loans: + html += f""" + + + + + + + + + """ + else: + html += "" + + html += """ + +
TabletSerial NumberBorrowerLoan DateReturn DateStatus
{loan['brand']} {loan['model']}{loan['serial_number']}{loan['name']}{loan['loan_date']}{loan['return_date'] or '-'}{loan['status']}
No loan history available.
+ + + """ + + 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() \ No newline at end of file diff --git a/templates/add_tablet.html b/templates/add_tablet.html new file mode 100644 index 0000000..8199daf --- /dev/null +++ b/templates/add_tablet.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block content %} +

Add New Tablet

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/add_user.html b/templates/add_user.html new file mode 100644 index 0000000..2bcd10b --- /dev/null +++ b/templates/add_user.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block content %} +

Add New User

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..629882b --- /dev/null +++ b/templates/base.html @@ -0,0 +1,153 @@ + + + + + + Tablet Management System + + + +
+

Tablet Management System

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + + + + {% block content %}{% endblock %} +
+ + diff --git a/templates/history.html b/templates/history.html new file mode 100644 index 0000000..8a2fe7a --- /dev/null +++ b/templates/history.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} + +{% block content %} +
+

Loan History

+ {% if loans %} + + + + + + + + + + + + + {% for loan in loans %} + + + + + + + + + {% endfor %} + +
TabletSerial NumberBorrowerLoan DateReturn DateStatus
{{ loan.brand }} {{ loan.model }}{{ loan.serial_number }}{{ loan.name }}{{ loan.loan_date }}{{ loan.return_date or '-' }}{{ loan.status }}
+ {% else %} +

No loan history available.

+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..8dfcdb6 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,63 @@ +{% extends "base.html" %} + +{% block content %} +
+

Available Tablets

+ {% if available_tablets %} + + + + + + + + + + + {% for tablet in available_tablets %} + + + + + + + {% endfor %} + +
BrandModelSerial NumberNotes
{{ tablet.brand }}{{ tablet.model }}{{ tablet.serial_number }}{{ tablet.notes or '-' }}
+ {% else %} +

No available tablets.

+ {% endif %} +
+ +
+

Active Loans

+ {% if active_loans %} + + + + + + + + + + + + {% for loan in active_loans %} + + + + + + + + {% endfor %} + +
TabletSerial NumberBorrowerLoan DateActions
{{ loan.brand }} {{ loan.model }}{{ loan.serial_number }}{{ loan.name }}{{ loan.loan_date }} + Return +
+ {% else %} +

No active loans.

+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/templates/loan_tablet.html b/templates/loan_tablet.html new file mode 100644 index 0000000..b9f2527 --- /dev/null +++ b/templates/loan_tablet.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} + +{% block content %} +

Loan Tablet

+
+ +
+ + + +
+ + +
+ + + +
+ +
+ +
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/templates/project_management.html b/templates/project_management.html new file mode 100644 index 0000000..7ca8005 --- /dev/null +++ b/templates/project_management.html @@ -0,0 +1,196 @@ +{% extends "base.html" %} + +{% block content %} +

Project Management - Development Notes

+ +
+
+
+ + + +
+ +
+
+

Editor

+ +
+ +
+

Preview

+
+
+
+
+
+ + + + + +{% endblock %} diff --git a/templates/user_loans.html b/templates/user_loans.html new file mode 100644 index 0000000..8c10045 --- /dev/null +++ b/templates/user_loans.html @@ -0,0 +1,157 @@ +{% extends "base.html" %} + +{% block content %} +

User Loans

+

+ Relationship: One user can loan multiple tablets (one-to-many). + This page demonstrates the many-to-many relationship between users and tablets via the loans table. +

+ +
+ {% for user_data in users_with_loans %} + {% set user = user_data.user %} + {% set loans = user_data.loans %} + +
+
+

{{ user.name }} ({{ user.identification }})

+ {{ loans|length }} tablet{{ 's' if loans|length != 1 else '' }} loaned +
+ + {% if loans %} + + + + + + + + + + + + {% for loan in loans %} + + + + + + + + {% endfor %} + +
TabletSerial NumberLoan DateReturn DateStatus
{{ loan.brand }} {{ loan.model }}{{ loan.serial_number }}{{ loan.loan_date }}{{ loan.return_date or '-' }} + + {{ loan.status }} + +
+ {% else %} +

No loans recorded for this user.

+ {% endif %} +
+ {% endfor %} + + {% if users_with_loans|length == 0 %} +

No users found. Add users and loan tablets to see data here.

+ {% endif %} +
+ + +{% endblock %} diff --git a/test_app.py b/test_app.py new file mode 100644 index 0000000..0fcb8ac --- /dev/null +++ b/test_app.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +""" +Test script for the Tablet Management System +""" + +import sqlite3 +from datetime import datetime + +def test_tablet_management(): + """Test the tablet management system functionality""" + + # Initialize database + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + + # Create tables + 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' + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + identification TEXT UNIQUE + ) + ''') + + 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) + ) + ''') + + conn.commit() + + print("=== Testing Tablet Management System ===") + + # Test 1: Add tablets + print("\n1. Adding tablets...") + tablets = [ + ("Samsung", "Galaxy Tab S7", "SN001"), + ("Apple", "iPad Pro", "SN002"), + ("Lenovo", "Tab P11", "SN003") + ] + + for brand, model, serial in tablets: + try: + cursor.execute(''' + INSERT INTO tablets (brand, model, serial_number, status) + VALUES (?, ?, ?, 'available') + ''', (brand, model, serial)) + print(f" ✓ Added: {brand} {model} ({serial})") + except sqlite3.IntegrityError: + print(f" ✗ Duplicate: {serial}") + + conn.commit() + + # Test 2: Add users + print("\n2. Adding users...") + users = [ + ("John Doe", "ID001"), + ("Jane Smith", "ID002"), + ("Bob Johnson", "ID003") + ] + + for name, identification in users: + try: + cursor.execute(''' + INSERT INTO users (name, identification) + VALUES (?, ?) + ''', (name, identification)) + print(f" ✓ Added: {name} ({identification})") + except sqlite3.IntegrityError: + print(f" ✗ Duplicate: {identification}") + + conn.commit() + + # Test 3: Show available tablets + print("\n3. Available tablets:") + cursor.execute("SELECT id, brand, model, serial_number FROM tablets WHERE status = 'available'") + available_tablets = cursor.fetchall() + + for tablet in available_tablets: + print(f" ID {tablet[0]}: {tablet[1]} {tablet[2]} ({tablet[3]})") + + # Test 4: Loan tablets + print("\n4. Loaning tablets...") + + # Loan tablet 1 to user 1 + tablet_id = 1 + user_id = 1 + + cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet_id,)) + 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)) + print(f" ✓ Loaned tablet {tablet_id} to user {user_id}") + + # Loan tablet 2 to user 2 + tablet_id = 2 + user_id = 2 + + cursor.execute("UPDATE tablets SET status = 'loaned' WHERE id = ?", (tablet_id,)) + 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)) + print(f" ✓ Loaned tablet {tablet_id} to user {user_id}") + + conn.commit() + + # Test 5: Show active loans + print("\n5. 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() + + for loan in active_loans: + print(f" Loan {loan[0]}: {loan[1]} {loan[2]} ({loan[3]}) to {loan[4]} on {loan[5]}") + + # Test 6: Return a tablet + print("\n6. Returning tablet...") + loan_id = 1 + + cursor.execute("SELECT tablet_id FROM loans WHERE id = ?", (loan_id,)) + loan = cursor.fetchone() + + if loan: + tablet_id = loan[0] + 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)) + cursor.execute("UPDATE tablets SET status = 'available' WHERE id = ?", (tablet_id,)) + print(f" ✓ Returned tablet for loan {loan_id}") + + conn.commit() + + # Test 7: Show loan history + print("\n7. Complete loan history:") + 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() + + for loan in loans: + return_date = loan[6] or 'Not returned' + print(f" Loan {loan[0]}: {loan[1]} {loan[2]} ({loan[3]}) to {loan[4]}") + print(f" Loan: {loan[5]}, Return: {return_date}, Status: {loan[7]}") + + # Test 8: Show final status + print("\n8. Final status:") + + # Available tablets + cursor.execute("SELECT COUNT(*) FROM tablets WHERE status = 'available'") + available_count = cursor.fetchone()[0] + print(f" Available tablets: {available_count}") + + # Loaned tablets + cursor.execute("SELECT COUNT(*) FROM tablets WHERE status = 'loaned'") + loaned_count = cursor.fetchone()[0] + print(f" Loaned tablets: {loaned_count}") + + # Active loans + cursor.execute("SELECT COUNT(*) FROM loans WHERE status = 'active'") + active_loans_count = cursor.fetchone()[0] + print(f" Active loans: {active_loans_count}") + + # Returned loans + cursor.execute("SELECT COUNT(*) FROM loans WHERE status = 'returned'") + returned_loans_count = cursor.fetchone()[0] + print(f" Returned loans: {returned_loans_count}") + + conn.close() + + print("\n=== Test completed successfully! ===") + print("\nYou can now run the interactive application:") + print(" python3 minimal_app.py") + print("\nOr use the database directly with SQLite:") + print(" sqlite3 tablets.db") + +if __name__ == '__main__': + test_tablet_management() \ No newline at end of file diff --git a/working_server.py b/working_server.py new file mode 100644 index 0000000..96b4dd8 --- /dev/null +++ b/working_server.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +""" +Working web server for tablet management system +""" + +from http.server import HTTPServer, BaseHTTPRequestHandler +import sqlite3 +from urllib.parse import urlparse, parse_qs +import json + +class TabletHandler(BaseHTTPRequestHandler): + def _set_headers(self, status=200, content_type='text/html'): + self.send_response(status) + self.send_header('Content-type', content_type) + self.end_headers() + + def do_GET(self): + parsed = urlparse(self.path) + + if parsed.path == '/': + self._serve_main_page() + elif parsed.path == '/api/tablets': + self._serve_tablets() + elif parsed.path == '/api/users': + self._serve_users() + elif parsed.path == '/api/loans': + self._serve_loans() + else: + self._set_headers(404) + self.wfile.write(b'404 Not Found') + + def _serve_main_page(self): + html = ''' + + + Tablet Management System + + + +
+

Tablet Management System

+

SQLite backend - Port 8000

+ +
+

Available Tablets

+
+
+ +
+

Active Loans

+
+
+ +
+

System Info

+

✓ Database: tablets.db

+

✓ Status: Operational

+

Port: 8000

+
+
+ + + +''' + + self._set_headers() + self.wfile.write(html.encode('utf-8')) + + def _serve_tablets(self): + try: + conn = sqlite3.connect('tablets.db') + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute('SELECT * FROM tablets') + tablets = [dict(row) for row in cursor.fetchall()] + conn.close() + + self._set_headers(content_type='application/json') + self.wfile.write(json.dumps(tablets).encode('utf-8')) + except Exception as e: + self._set_headers(500) + self.wfile.write(json.dumps({'error': str(e)}).encode('utf-8')) + + def _serve_users(self): + try: + conn = sqlite3.connect('tablets.db') + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute('SELECT * FROM users') + users = [dict(row) for row in cursor.fetchall()] + conn.close() + + self._set_headers(content_type='application/json') + self.wfile.write(json.dumps(users).encode('utf-8')) + except Exception as e: + self._set_headers(500) + self.wfile.write(json.dumps({'error': str(e)}).encode('utf-8')) + + def _serve_loans(self): + try: + conn = sqlite3.connect('tablets.db') + cursor = conn.cursor() + cursor.execute(''' + SELECT l.id, l.tablet_id, l.user_id, l.loan_date, l.return_date, l.status, + t.brand || ' ' || t.model as tablet, + u.name as user + FROM loans l + JOIN tablets t ON l.tablet_id = t.id + JOIN users u ON l.user_id = u.id + ''') + + loans = [] + for row in cursor.fetchall(): + loans.append({ + 'id': row[0], + 'tablet_id': row[1], + 'user_id': row[2], + 'loan_date': row[3], + 'return_date': row[4], + 'status': row[5], + 'tablet': row[6], + 'user': row[7] + }) + conn.close() + + self._set_headers(content_type='application/json') + self.wfile.write(json.dumps(loans).encode('utf-8')) + except Exception as e: + self._set_headers(500) + self.wfile.write(json.dumps({'error': str(e)}).encode('utf-8')) + +def run_server(): + server_address = ('', 8000) + httpd = HTTPServer(server_address, TabletHandler) + + print("Tablet Management System - Web Interface") + print("========================================") + print("Server running on: http://localhost:8000") + print("Database: tablets.db (SQLite)") + print("Press Ctrl+C to stop the server") + print() + + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nServer stopped") + +if __name__ == '__main__': + run_server() \ No newline at end of file