144 lines
4.4 KiB
Python
144 lines
4.4 KiB
Python
"""WTForms for the application."""
|
|
|
|
from flask_wtf import FlaskForm
|
|
from wtforms import (
|
|
StringField,
|
|
PasswordField,
|
|
SubmitField,
|
|
IntegerField,
|
|
DateField,
|
|
TextAreaField,
|
|
SelectField,
|
|
FileField,
|
|
BooleanField,
|
|
RadioField,
|
|
)
|
|
import os
|
|
from werkzeug.utils import secure_filename
|
|
|
|
from wtforms.validators import (
|
|
DataRequired,
|
|
Length,
|
|
Email,
|
|
ValidationError,
|
|
)
|
|
|
|
from flask_login import current_user
|
|
|
|
|
|
def validate_unique_username(username):
|
|
from app.models import User
|
|
|
|
if User.query.filter_by(username=username).first():
|
|
raise ValidationError("Username already taken.")
|
|
|
|
|
|
def validate_unique_email(email):
|
|
from app.models import User
|
|
|
|
if User.query.filter_by(email=email).first():
|
|
raise ValidationError("Email already registered.")
|
|
|
|
|
|
class LoginForm(FlaskForm):
|
|
"""Login form."""
|
|
|
|
username = StringField(
|
|
"Username", validators=[DataRequired(), Length(min=1, max=64)]
|
|
)
|
|
password = PasswordField("Password", validators=[DataRequired()])
|
|
submit = SubmitField("Login")
|
|
|
|
|
|
class UserForm(FlaskForm):
|
|
"""User creation and edit form."""
|
|
|
|
username = StringField(
|
|
"Username", validators=[DataRequired(), Length(min=1, max=64)]
|
|
)
|
|
full_name = StringField(
|
|
"Full Name", validators=[DataRequired(), Length(min=1, max=120)]
|
|
)
|
|
email = StringField("Email", validators=[DataRequired(), Email(), Length(max=120)])
|
|
password = PasswordField("Password")
|
|
is_admin = BooleanField("Admin")
|
|
submit = SubmitField("Submit")
|
|
|
|
def validate_username(self, field):
|
|
if current_user and current_user.id != field.data:
|
|
validate_unique_username(field.data)
|
|
|
|
def validate_email(self, field):
|
|
if current_user and current_user.id != field.data:
|
|
validate_unique_email(field.data)
|
|
|
|
|
|
class InspectionForm(FlaskForm):
|
|
"""Inspection report form."""
|
|
|
|
installation_name = StringField(
|
|
"Installation Name", validators=[DataRequired(), Length(max=120)]
|
|
)
|
|
location = StringField("Location", validators=[DataRequired(), Length(max=120)])
|
|
inspection_date = DateField("Date of Inspection", validators=[DataRequired()])
|
|
reference_number = IntegerField("Reference Number", validators=[DataRequired()])
|
|
observations = TextAreaField("Observations", validators=[DataRequired()])
|
|
conclusion_text = TextAreaField("Conclusion Comments", validators=[DataRequired()])
|
|
conclusion_status = RadioField(
|
|
"Conclusion Status",
|
|
choices=[
|
|
("ok", "OK for operation in current state"),
|
|
(
|
|
"minor",
|
|
"Minor comments — Remedial actions required for continued operation",
|
|
),
|
|
(
|
|
"major",
|
|
"Major comments — Operation suspended until resolution and satisfactory follow-up inspection",
|
|
),
|
|
],
|
|
default="ok",
|
|
)
|
|
# Inspectors (multiple) as a field of selectable users and free-text names
|
|
inspectors = SelectField("Inspectors", coerce=int, validators=[DataRequired()])
|
|
# Photo upload field
|
|
photos = FileField(
|
|
"Photos", validators=[DataRequired(), validate_photo_upload], multiple=True
|
|
)
|
|
submit = SubmitField("Complete Report")
|
|
|
|
|
|
class PhotoForm(FlaskForm):
|
|
"""Photo upload form."""
|
|
|
|
caption = StringField("Caption", validators=[Length(max=255)])
|
|
action_required = RadioField(
|
|
"Action Required",
|
|
choices=[
|
|
("none", "No action required"),
|
|
("urgent", "Urgent action required"),
|
|
("before_next", "Action required before next inspection"),
|
|
],
|
|
default="none",
|
|
)
|
|
submit = SubmitField("Upload")
|
|
|
|
|
|
def validate_photo_upload(form, field):
|
|
from wtforms.validators import ValidationError
|
|
|
|
allowed_extensions = {"jpg", "jpeg", "png", "gif", "webp"}
|
|
max_size_mb = 10
|
|
for file in field.data:
|
|
if file:
|
|
ext = file.filename.rsplit(".", 1)[-1].lower()
|
|
if ext not in allowed_extensions:
|
|
raise ValidationError(
|
|
"Invalid file type. Allowed types: jpg, jpeg, png, gif, webp."
|
|
)
|
|
# Check file size
|
|
file.seek(0, 2) # seek to end
|
|
size = file.tell()
|
|
file.seek(0) # reset to start
|
|
if size > max_size_mb * 1024 * 1024: # 10MB in bytes
|
|
raise ValidationError("File size must be <= 10MB.")
|