first test with local claude code
This commit is contained in:
commit
34daffbbee
5 changed files with 574 additions and 0 deletions
285
app.py
Normal file
285
app.py
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
import os
|
||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, session
|
||||
from datetime import datetime
|
||||
import sqlite3
|
||||
import uuid
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'your_secret_key_here'
|
||||
|
||||
DB_PATH = '/home/jimmy/inspectiontool/db.sqlite3'
|
||||
|
||||
# Initialize database
|
||||
def init_db():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS inspections (id INTEGER PRIMARY KEY AUTOINCREMENT, inspector_name TEXT NOT NULL, location TEXT NOT NULL, inspection_date TEXT NOT NULL, installation_name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP)")
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS checklist_items (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)")
|
||||
cursor.execute("INSERT OR IGNORE INTO checklist_items (name) VALUES ('Check electrical wiring'), ('Check plumbing'), ('Check fire safety'), ('Check electrical safety'), ('Check water pressure'), ('Check drainage'), ('Check structural integrity'), ('Check emergency exits'), ('Check fire extinguishers'), ('Check ventilation')")
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS inspection_photos (id INTEGER PRIMARY KEY AUTOINCREMENT, inspection_id INTEGER NOT NULL, photo_path TEXT NOT NULL, comment TEXT, resolved INTEGER DEFAULT 0, uploaded_at TEXT DEFAULT CURRENT_TIMESTAMP)")
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'user', logo_path TEXT)")
|
||||
|
||||
# Add default admin user
|
||||
hashed_password = generate_password_hash('admin')
|
||||
cursor.execute("INSERT OR IGNORE INTO users (username, password_hash, role) VALUES ('admin', ?, 'admin')", (hashed_password,))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
init_db()
|
||||
|
||||
# Set up static folders
|
||||
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'static', 'uploads')
|
||||
app.config['PDF_FOLDER'] = os.path.join(app.root_path, 'static', 'pdf')
|
||||
app.config['LOGO_FOLDER'] = os.path.join(app.root_path, 'static', 'logo')
|
||||
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
os.makedirs(app.config['PDF_FOLDER'], exist_ok=True)
|
||||
os.makedirs(app.config['LOGO_FOLDER'], exist_ok=True)
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
|
||||
user = cursor.fetchone()
|
||||
if user and check_password_hash(user[2], password):
|
||||
session['user_id'] = user[0]
|
||||
session['username'] = user[1]
|
||||
session['role'] = user[3]
|
||||
# Store logo path for current user in session
|
||||
cursor.execute("SELECT logo_path FROM users WHERE username = ?", (username,))
|
||||
logo_path = cursor.fetchone()[0] if cursor.fetchone() else None
|
||||
session['user_logo_path'] = logo_path
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash('Invalid username or password')
|
||||
return render_template('login.html')
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
role = request.form.get('role', 'user')
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
|
||||
if cursor.fetchone():
|
||||
flash('Username already exists')
|
||||
return redirect(url_for('register'))
|
||||
hashed_password = generate_password_hash(password)
|
||||
cursor.execute("INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)", (username, hashed_password, role))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
flash('Registration successful!')
|
||||
return redirect(url_for('login'))
|
||||
return render_template('register.html')
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
session.pop('user_id', None)
|
||||
session.pop('username', None)
|
||||
session.pop('role', None)
|
||||
session.pop('user_logo_path', None)
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route('/admin')
|
||||
def admin():
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
return render_template('admin.html')
|
||||
|
||||
@app.route('/admin/users')
|
||||
def admin_users():
|
||||
if 'user_id' not in session or session['role'] != 'admin':
|
||||
return redirect(url_for('index'))
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users")
|
||||
users = cursor.fetchall()
|
||||
conn.close()
|
||||
return render_template('admin_users.html', users=users)
|
||||
|
||||
@app.route('/admin/users/<int:user_id>', methods=['DELETE'])
|
||||
def delete_user(user_id):
|
||||
if 'user_id' not in session or session['role'] != 'admin':
|
||||
return redirect(url_for('index'))
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM users WHERE id = ?", (user_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return redirect(url_for('admin_users'))
|
||||
|
||||
@app.route('/admin/logo', methods=['GET', 'POST'])
|
||||
def admin_logo():
|
||||
if 'user_id' not in session or session['role'] != 'admin':
|
||||
return redirect(url_for('index'))
|
||||
if request.method == 'POST':
|
||||
if 'logo' in request.files:
|
||||
file = request.files['logo']
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(url_for('admin_users'))
|
||||
filename = f'logo_{uuid.uuid4().hex}.png'
|
||||
logo_path = os.path.join(app.config['LOGO_FOLDER'], filename)
|
||||
file.save(logo_path)
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("UPDATE users SET logo_path = ? WHERE username = 'admin'", (logo_path,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
flash('Logo uploaded successfully')
|
||||
return redirect(url_for('admin_users'))
|
||||
return render_template('admin_logo.html')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
inspections = conn.execute("SELECT * FROM inspections ORDER BY inspection_date DESC").fetchall()
|
||||
conn.close()
|
||||
return render_template('index.html', inspections=inspections)
|
||||
|
||||
@app.route('/new', methods=['GET', 'POST'])
|
||||
def new_inspection():
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
if request.method == 'POST':
|
||||
inspector_name = request.form['inspector_name']
|
||||
location = request.form['location']
|
||||
inspection_date = request.form['inspection_date']
|
||||
installation_name = request.form['installation_name']
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("INSERT INTO inspections (inspector_name, location, inspection_date, installation_name) VALUES (?, ?, ?, ?)",
|
||||
(inspector_name, location, inspection_date, installation_name))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return redirect(url_for('view_inspection', id=cursor.lastrowid))
|
||||
|
||||
return render_template('new_inspection.html')
|
||||
|
||||
@app.route('/view/<int:id>')
|
||||
def view_inspection(id):
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT * FROM inspections WHERE id = ?", (id,))
|
||||
inspection = cursor.fetchone()
|
||||
|
||||
cursor.execute("SELECT * FROM inspection_photos WHERE inspection_id = ?", (id,))
|
||||
photos = cursor.fetchall()
|
||||
|
||||
cursor.execute("SELECT * FROM checklist_items")
|
||||
checklist_items = cursor.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
return render_template('inspection.html', inspection=inspection, photos=photos, checklist_items=checklist_items)
|
||||
|
||||
@app.route('/upload/<int:id>', methods=['POST'])
|
||||
def upload_photo(id):
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
if 'photo' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(url_for('view_inspection', id=id))
|
||||
|
||||
file = request.files['photo']
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(url_for('view_inspection', id=id))
|
||||
|
||||
filename = f'photo_{uuid.uuid4().hex}.jpg'
|
||||
photo_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
file.save(photo_path)
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("INSERT INTO inspection_photos (inspection_id, photo_path, comment) VALUES (?, ?, ?)",
|
||||
(id, filename, request.form.get('comment', '')))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
flash('Photo uploaded successfully')
|
||||
return redirect(url_for('view_inspection', id=id))
|
||||
|
||||
@app.route('/generate_pdf/<int:id>', methods=['GET'])
|
||||
def generate_pdf(id):
|
||||
if 'user_id' not in session:
|
||||
return redirect(url_for('login'))
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT * FROM inspections WHERE id = ?", (id,))
|
||||
inspection = cursor.fetchone()
|
||||
|
||||
cursor.execute("SELECT * FROM inspection_photos WHERE inspection_id = ?", (id,))
|
||||
photos = cursor.fetchall()
|
||||
|
||||
cursor.execute("SELECT * FROM checklist_items")
|
||||
checklist_items = cursor.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
|
||||
from reportlab.lib import utils
|
||||
from reportlab.lib.utils import ImageReader
|
||||
|
||||
pdf_path = os.path.join(app.config['PDF_FOLDER'], f'report_{id}.pdf')
|
||||
doc = SimpleDocTemplate(pdf_path, pagesize=letter)
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
elements = []
|
||||
elements.append(Paragraph(f"Inspection Report - {inspection[1]}", styles['Heading1']))
|
||||
elements.append(Spacer(1, 12))
|
||||
elements.append(Paragraph(f"Inspector: {inspection[1]}", styles['Normal']))
|
||||
elements.append(Paragraph(f"Location: {inspection[2]}", styles['Normal']))
|
||||
elements.append(Paragraph(f"Date: {inspection[3]}", styles['Normal']))
|
||||
elements.append(Paragraph(f"Installation: {inspection[4]}", styles['Normal']))
|
||||
elements.append(Spacer(1, 12))
|
||||
|
||||
elements.append(Paragraph("Checklist", styles['Heading2']))
|
||||
for item in checklist_items:
|
||||
elements.append(Paragraph(f"- {item[1]}", styles['Normal']))
|
||||
elements.append(Spacer(1, 12))
|
||||
|
||||
elements.append(Paragraph("Photos", styles['Heading2']))
|
||||
for photo in photos:
|
||||
elements.append(Paragraph(f"Photo {photo[0]}: {photo[2]}", styles['Normal']))
|
||||
elements.append(Spacer(1, 4))
|
||||
|
||||
# Add logo if available
|
||||
logo_path = session.get('user_logo_path')
|
||||
if logo_path:
|
||||
try:
|
||||
logo = ImageReader(logo_path)
|
||||
logo_width = 100
|
||||
logo_height = 100
|
||||
elements.append(Paragraph(f"Logo: {logo_path}", styles['Normal']))
|
||||
# Add logo to PDF
|
||||
doc.build(elements)
|
||||
except Exception as e:
|
||||
print(f"Error adding logo: {e}")
|
||||
else:
|
||||
doc.build(elements)
|
||||
|
||||
return send_from_directory(app.config['PDF_FOLDER'], f'report_{id}.pdf', as_attachment=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
44
db_schema.sql
Normal file
44
db_schema.sql
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
CREATE TABLE inspections (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
inspector_name TEXT NOT NULL,
|
||||
location TEXT NOT NULL,
|
||||
inspection_date TEXT NOT NULL,
|
||||
installation_name TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE checklist_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO checklist_items (name) VALUES
|
||||
('Check electrical wiring'),
|
||||
('Check plumbing'),
|
||||
('Check fire safety'),
|
||||
('Check electrical safety'),
|
||||
('Check water pressure'),
|
||||
('Check drainage'),
|
||||
('Check structural integrity'),
|
||||
('Check emergency exits'),
|
||||
('Check fire extinguishers'),
|
||||
('Check ventilation');
|
||||
|
||||
CREATE TABLE inspection_photos (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
inspection_id INTEGER NOT NULL,
|
||||
photo_path TEXT NOT NULL,
|
||||
comment TEXT,
|
||||
resolved INTEGER DEFAULT 0,
|
||||
uploaded_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (inspection_id) REFERENCES inspections(id)
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
logo_path TEXT
|
||||
);
|
||||
44
templates/index.html
Normal file
44
templates/index.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Inspection Reports</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #f2f2f2; }
|
||||
a { text-decoration: none; color: #0066cc; }
|
||||
a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Inspection Reports</h1>
|
||||
<a href="{{ url_for('new_inspection') }}">Create New Inspection</a>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Inspector</th>
|
||||
<th>Location</th>
|
||||
<th>Date</th>
|
||||
<th>Installation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for inspection in inspections %}
|
||||
<tr>
|
||||
<td>{{ inspection.id }}</td>
|
||||
<td>{{ inspection.inspector_name }}</td>
|
||||
<td>{{ inspection.location }}</td>
|
||||
<td>{{ inspection.inspection_date }}</td>
|
||||
<td>{{ inspection.installation_name }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('view_inspection', id=inspection.id) }}">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
107
templates/inspection.html
Normal file
107
templates/inspection.html
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Inspection Report - {{ inspection.inspector_name }}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.header { border-bottom: 2px solid #000; padding-bottom: 10px; margin-bottom: 20px; }
|
||||
.header h1 { color: #0066cc; }
|
||||
.report-section { margin-bottom: 20px; }
|
||||
.checklist { margin-left: 20px; }
|
||||
.checklist-item { margin-bottom: 5px; }
|
||||
.photo-section { margin-top: 20px; }
|
||||
.photo-container { border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; }
|
||||
.photo { max-width: 300px; height: auto; }
|
||||
.photo-comment { margin-top: 5px; font-size: 0.9em; }
|
||||
.checkbox-container { display: flex; align-items: center; }
|
||||
.checkbox { margin-right: 5px; }
|
||||
.summary-section { margin-top: 20px; }
|
||||
.summary-buttons { display: flex; gap: 10px; margin-top: 10px; }
|
||||
.btn { background: #0066cc; color: white; padding: 5px 10px; border: none; cursor: pointer; }
|
||||
.btn:hover { background: #0055aa; }
|
||||
.download-btn { background: #333; color: white; padding: 5px 10px; border: none; cursor: pointer; }
|
||||
.download-btn:hover { background: #555; }
|
||||
.update-note { color: #888; margin-top: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Inspection Report</h1>
|
||||
<p><strong>Inspector:</strong> {{ inspection.inspector_name }}<br>
|
||||
<strong>Location:</strong> {{ inspection.location }}<br>
|
||||
<strong>Date:</strong> {{ inspection.inspection_date }}<br>
|
||||
<strong>Installation:</strong> {{ inspection.installation_name }}</p>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>Checklist</h2>
|
||||
<ul class="checklist">
|
||||
{% for item in checklist_items %}
|
||||
<li class="checklist-item">
|
||||
<input type="checkbox" class="checklist-item-checkbox" data-item-id="{{ item.id }}" data-inspection-id="{{ inspection.id }}"> {{ item.name }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>Photos</h2>
|
||||
<div class="photo-section">
|
||||
{% for photo in photos %}
|
||||
<div class="photo-container">
|
||||
<img src="{{ url_for('static', filename=photo.photo_path) }}" class="photo" alt="Photo {{ loop.index }}">
|
||||
<div class="photo-comment">{{ photo.comment or 'No comment' }}</div>
|
||||
<div class="checkbox-container">
|
||||
<input type="checkbox" class="resolved-checkbox" data-photo-id="{{ photo.id }}" data-inspection-id="{{ inspection.id }}" data-resolved="{{ photo.resolved }}">
|
||||
<label>Resolved</label>
|
||||
</div>
|
||||
<div class="update-note">Updated: {{ photo.uploaded_at }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>Summary</h2>
|
||||
<textarea name="summary" rows="4" cols="50" placeholder="Enter summary text here">{{ inspection.summary or '' }}</textarea>
|
||||
<div class="summary-buttons">
|
||||
<button class="btn" onclick="toggleStatus('ok')">Ok for operation</button>
|
||||
<button class="btn" onclick="toggleStatus('minor')">Minor remarks</button>
|
||||
<button class="btn" onclick="toggleStatus('major')">Major remarks</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="report-section">
|
||||
<h2>Download Report</h2>
|
||||
<a href="{{ url_for('generate_pdf', id=inspection.id) }}" class="download-btn">Download as PDF</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const checklistItems = document.querySelectorAll('.checklist-item-checkbox');
|
||||
const photoCheckboxes = document.querySelectorAll('.resolved-checkbox');
|
||||
|
||||
checklistItems.forEach(item => {
|
||||
item.addEventListener('change', function() {
|
||||
const inspectionId = this.getAttribute('data-inspection-id');
|
||||
const itemId = this.getAttribute('data-item-id');
|
||||
// Update the database here (implementation would go here)
|
||||
});
|
||||
});
|
||||
|
||||
photoCheckboxes.forEach(box => {
|
||||
box.addEventListener('change', function() {
|
||||
const photoId = this.getAttribute('data-photo-id');
|
||||
const inspectionId = this.getAttribute('data-inspection-id');
|
||||
const resolved = this.checked ? 1 : 0;
|
||||
// Update the database here (implementation would go here)
|
||||
});
|
||||
});
|
||||
|
||||
function toggleStatus(status) {
|
||||
// Implementation for toggling status
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
94
tests/test_app.py
Normal file
94
tests/test_app.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from flask import json
|
||||
from app import app
|
||||
import sqlite3
|
||||
from io import BytesIO
|
||||
|
||||
class TestApp(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Use a temporary test database
|
||||
self.test_db_path = os.path.join(os.getcwd(), 'test.db')
|
||||
app.config['TESTING'] = True
|
||||
app.config['DB_PATH'] = self.test_db_path
|
||||
self.client = app.test_client()
|
||||
|
||||
# Initialize the database
|
||||
conn = sqlite3.connect(self.test_db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS inspections (id INTEGER PRIMARY KEY AUTOINCREMENT, inspector_name TEXT NOT NULL, location TEXT NOT NULL, inspection_date TEXT NOT NULL, installation_name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP)")
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS checklist_items (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)")
|
||||
cursor.execute("INSERT OR IGNORE INTO checklist_items (name) VALUES ('Check electrical wiring'), ('Check plumbing'), ('Check fire safety'), ('Check electrical safety'), ('Check water pressure'), ('Check drainage'), ('Check structural integrity'), ('Check emergency exits'), ('Check fire extinguishers'), ('Check ventilation')")
|
||||
cursor.execute("CREATE TABLE IF NOT EXISTS inspection_photos (id INTEGER PRIMARY KEY AUTOINCREMENT, inspection_id INTEGER NOT NULL, photo_path TEXT NOT NULL, comment TEXT, resolved INTEGER DEFAULT 0, uploaded_at TEXT DEFAULT CURRENT_TIMESTAMP)")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def tearDown(self):
|
||||
# Clean up the test database
|
||||
if os.path.exists(self.test_db_path):
|
||||
os.remove(self.test_db_path)
|
||||
|
||||
def test_create_inspection(self):
|
||||
response = self.client.post('/new', data={
|
||||
'inspector_name': 'Test Inspector',
|
||||
'location': 'Test Location',
|
||||
'inspection_date': '2026-02-23',
|
||||
'installation_name': 'Test Installation'
|
||||
}, follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'Test Inspector', response.data)
|
||||
self.assertIn(b'Test Location', response.data)
|
||||
self.assertIn(b'Test Installation', response.data)
|
||||
|
||||
def test_view_inspection(self):
|
||||
# Create an inspection
|
||||
response = self.client.post('/new', data={
|
||||
'inspector_name': 'Test Inspector',
|
||||
'location': 'Test Location',
|
||||
'inspection_date': '2026-02-23',
|
||||
'installation_name': 'Test Installation'
|
||||
}, follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# View the inspection
|
||||
response = self.client.get('/view/1')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'Inspection Report', response.data)
|
||||
|
||||
def test_upload_photo(self):
|
||||
# Create an inspection
|
||||
response = self.client.post('/new', data={
|
||||
'inspector_name': 'Test Inspector',
|
||||
'location': 'Test Location',
|
||||
'inspection_date': '2026-02-23',
|
||||
'installation_name': 'Test Installation'
|
||||
}, follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Upload a photo
|
||||
with BytesIO(b'test photo data') as photo:
|
||||
response = self.client.post('/upload/1', data={
|
||||
'photo': (photo, 'test.jpg'),
|
||||
'comment': 'Test comment'
|
||||
}, follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b'Photo uploaded successfully', response.data)
|
||||
|
||||
def test_generate_pdf(self):
|
||||
# Create an inspection
|
||||
response = self.client.post('/new', data={
|
||||
'inspector_name': 'Test Inspector',
|
||||
'location': 'Test Location',
|
||||
'inspection_date': '2026-02-23',
|
||||
'installation_name': 'Test Installation'
|
||||
}, follow_redirects=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Generate PDF
|
||||
response = self.client.get('/generate_pdf/1')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content_type, 'application/pdf')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Loading…
Reference in a new issue