Create the template for the Flask framework

This commit is contained in:
Charles Descamps 2025-06-03 16:50:03 -07:00
parent db65646400
commit c0f9876184
14 changed files with 266 additions and 0 deletions

35
app/__init__.py Normal file
View file

@ -0,0 +1,35 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
# Register blueprints for modular routes
from .routes.admin import admin_bp
from .routes.auth import auth_bp
from .routes.customer import customer_bp
from .routes.repair import repair_bp
from .routes.inventory import inventory_bp
from .routes.technician import technician_bp
from .routes.audit import audit_bp
app.register_blueprint(admin_bp)
app.register_blueprint(auth_bp)
app.register_blueprint(customer_bp)
app.register_blueprint(repair_bp)
app.register_blueprint(inventory_bp)
app.register_blueprint(technician_bp)
app.register_blueprint(audit_bp)
return app

0
app/forms.py Normal file
View file

67
app/models.py Normal file
View file

@ -0,0 +1,67 @@
from . import db
from flask_login import UserMixin
from datetime import datetime
# User roles: admin, technician, customer
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
role = db.Column(db.String(20), default='customer')
customer = db.relationship('Customer', uselist=False, backref='user')
class Customer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120))
email = db.Column(db.String(120), unique=True)
phone = db.Column(db.String(20))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
repairs = db.relationship('RepairJob', backref='customer')
class Technician(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
repairs = db.relationship('RepairJob', backref='technician')
class RepairJob(db.Model):
id = db.Column(db.Integer, primary_key=True)
tracking_number = db.Column(db.String(50), unique=True)
device = db.Column(db.String(120))
description = db.Column(db.Text)
status = db.Column(db.String(50), default='New')
customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'))
technician_id = db.Column(db.Integer, db.ForeignKey('technician.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
cost_estimate = db.Column(db.Float)
final_cost = db.Column(db.Float)
parts_used = db.relationship('RepairPart', backref='repair')
barcode_path = db.Column(db.String(256))
notes = db.relationship('RepairNote', backref='repair')
class RepairNote(db.Model):
id = db.Column(db.Integer, primary_key=True)
repair_id = db.Column(db.Integer, db.ForeignKey('repair_job.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
note = db.Column(db.Text)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
class RepairPart(db.Model):
id = db.Column(db.Integer, primary_key=True)
repair_id = db.Column(db.Integer, db.ForeignKey('repair_job.id'))
part_id = db.Column(db.Integer, db.ForeignKey('inventory_item.id'))
quantity = db.Column(db.Integer)
class InventoryItem(db.Model):
id = db.Column(db.Integer, primary_key=True)
part_name = db.Column(db.String(120))
quantity = db.Column(db.Integer)
low_stock_threshold = db.Column(db.Integer, default=1)
class AuditLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
action = db.Column(db.String(256))
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
details = db.Column(db.Text)

11
app/routes/audit.py Normal file
View file

@ -0,0 +1,11 @@
from flask import Blueprint, render_template
from flask_login import login_required
from app.models import AuditLog
audit_bp = Blueprint('audit', __name__, url_prefix='/audit')
@audit_bp.route('/log')
@login_required
def log():
logs = AuditLog.query.order_by(AuditLog.timestamp.desc()).all()
return render_template('audit/log.html', logs=logs)

11
app/routes/customer.py Normal file
View file

@ -0,0 +1,11 @@
from flask import Blueprint, render_template
from flask_login import login_required, current_user
from app.models import Customer
customer_bp = Blueprint('customer', __name__, url_prefix='/customers')
@customer_bp.route('/portal')
@login_required
def portal():
customer = Customer.query.filter_by(user_id=current_user.id).first_or_404()
return render_template('customers/portal.html', customer=customer)

38
app/routes/repair.py Normal file
View file

@ -0,0 +1,38 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from app.models import db, RepairJob, Customer, Technician, AuditLog
from app.utils.barcode import generate_barcode
repair_bp = Blueprint('repair', __name__, url_prefix='/repairs')
@repair_bp.route('/new', methods=['GET', 'POST'])
@login_required
def new_repair():
if request.method == 'POST':
# ... get form data ...
repair = RepairJob(
device=request.form['device'],
description=request.form['description'],
customer_id=request.form['customer_id'],
technician_id=request.form.get('technician_id'),
)
db.session.add(repair)
db.session.commit()
# Generate and save barcode image
barcode_path = generate_barcode(repair.id, repair.tracking_number)
repair.barcode_path = barcode_path
db.session.commit()
# Audit log
audit = AuditLog(user_id=current_user.id, action='Created repair', details=f"Repair ID {repair.id}")
db.session.add(audit)
db.session.commit()
flash('Repair job created!')
return redirect(url_for('repair.detail', repair_id=repair.id))
# ... render form ...
return render_template('repairs/new.html')
@repair_bp.route('/<int:repair_id>')
@login_required
def detail(repair_id):
repair = RepairJob.query.get_or_404(repair_id)
return render_template('repairs/detail.html', repair=repair)

26
app/templates/base.html Normal file
View file

@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}Repair Tracker{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}">
</head>
<body>
{% include 'navbar.html' %}
<div class="container-fluid mt-3">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">{{ messages[0] }}</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/custom.js') }}"></script>
</body>
</html>

0
app/utils.py Normal file
View file

16
app/utils/barcode.py Normal file
View file

@ -0,0 +1,16 @@
import os
import qrcode
from barcode import Code128
from barcode.writer import ImageWriter
from flask import current_app
def generate_barcode(repair_id, tracking_number):
# Generate barcode image
barcode = Code128(tracking_number, writer=ImageWriter())
barcode_path = os.path.join(current_app.config['UPLOAD_FOLDER'], f'barcode_{repair_id}.png')
barcode.save(barcode_path)
# Optionally, also generate QR code
qr = qrcode.make(tracking_number)
qr_path = os.path.join(current_app.config['UPLOAD_FOLDER'], f'qr_{repair_id}.png')
qr.save(qr_path)
return barcode_path