Restructurign code

This commit is contained in:
Nguyen Ngo
2025-08-19 13:15:07 -04:00
parent 8c1a3f8a38
commit 4248917d17
7 changed files with 279 additions and 227 deletions
+4 -227
View File
@@ -32,236 +32,13 @@ VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager']
# Roles that have staff-level permissions (non-admin roles)
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager']
# Import and initialize models
from models import set_db
User, QRCode, QRCodeStyle, Project, AttendanceData = set_db(db)
# Initialize the logging system
logger_handler = AppLogger(app, db)
# User Model
class User(db.Model):
"""
User model to manage system users with role-based access control
"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
full_name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
role = db.Column(db.String(20), nullable=False, default='staff') # admin or staff
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
created_date = db.Column(db.DateTime, default=datetime.utcnow)
active_status = db.Column(db.Boolean, default=True)
last_login_date = db.Column(db.DateTime, nullable=True)
# Relationships
created_users = db.relationship('User', backref=db.backref('creator', remote_side=[id]))
created_qr_codes = db.relationship('QRCode', backref='creator', lazy='dynamic')
def set_password(self, password):
"""Hash and set user password"""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Verify user password"""
return check_password_hash(self.password_hash, password)
def is_admin(self):
"""Check if user has admin privileges"""
return self.role == 'admin'
def has_staff_permissions(self):
"""Check if user has staff-level permissions (includes new roles)"""
return self.role in STAFF_LEVEL_ROLES
def has_export_permissions(user_role):
"""Check if user role has export permissions"""
return user_role in ['admin', 'payroll']
def get_role_display_name(self):
"""Get user-friendly role name"""
role_names = {
'admin': 'Administrator',
'staff': 'Staff User',
'payroll': 'Payroll Specialist',
'project_manager': 'Project Manager'
}
return role_names.get(self.role, self.role.title())
# QR Code Model
class QRCode(db.Model):
"""
Enhanced QR Code model to manage QR code records and metadata with address coordinates
"""
__tablename__ = 'qr_codes'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
location = db.Column(db.String(100), nullable=False)
location_address = db.Column(db.Text, nullable=False)
location_event = db.Column(db.String(200), nullable=False)
qr_code_image = db.Column(db.Text, nullable=False) # Base64 encoded image
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
created_date = db.Column(db.DateTime, default=datetime.utcnow)
active_status = db.Column(db.Boolean, default=True)
qr_url = db.Column(db.String(255), unique=True, nullable=True)
# Address Coordinates Fields
address_latitude = db.Column(db.Float, nullable=True)
address_longitude = db.Column(db.Float, nullable=True)
coordinate_accuracy = db.Column(db.String(50), nullable=True, default='geocoded')
coordinates_updated_date = db.Column(db.DateTime, nullable=True)
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True)
# The project_id field:
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True)
# QR Code Customization fields
fill_color = db.Column(db.String(7), default="#000000") # Hex color
back_color = db.Column(db.String(7), default="#FFFFFF") # Background color
box_size = db.Column(db.Integer, default=10)
border = db.Column(db.Integer, default=4)
error_correction = db.Column(db.String(1), default='L')
style_id = db.Column(db.Integer, db.ForeignKey('qr_code_styles.id'), nullable=True)
# Relationship to style
style = db.relationship('QRCodeStyle', backref='qr_codes')
@property
def has_coordinates(self):
"""Check if this QR code has address coordinates"""
return self.address_latitude is not None and self.address_longitude is not None
@property
def coordinates_display(self):
"""Get formatted coordinates for display"""
if self.has_coordinates:
return f"{self.address_latitude:.10f}, {self.address_longitude:.10f}"
return "Coordinates not available"
def update_coordinates(self, latitude, longitude, accuracy='geocoded'):
"""Update the address coordinates for this QR code"""
self.address_latitude = latitude
self.address_longitude = longitude
self.coordinate_accuracy = accuracy
self.coordinates_updated_date = datetime.utcnow()
class QRCodeStyle(db.Model):
"""QR Code customization styles"""
__tablename__ = 'qr_code_styles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False) # Style name
fill_color = db.Column(db.String(7), default="#000000") # Hex color for QR modules
back_color = db.Column(db.String(7), default="#FFFFFF") # Hex color for background
box_size = db.Column(db.Integer, default=10) # Size of each QR module
border = db.Column(db.Integer, default=4) # Border size
error_correction = db.Column(db.String(1), default='L') # L, M, Q, H
is_default = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'))
def __repr__(self):
return f'<QRCodeStyle {self.name}>'
class Project(db.Model):
"""
Project model to organize QR codes by projects
"""
__tablename__ = 'projects'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text, nullable=True)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
created_date = db.Column(db.DateTime, default=datetime.utcnow)
active_status = db.Column(db.Boolean, default=True)
# Relationships
qr_codes = db.relationship('QRCode', backref='project', lazy='dynamic')
creator = db.relationship('User', backref='created_projects')
def __repr__(self):
return f'<Project {self.name}>'
@property
def qr_count(self):
"""Get count of QR codes in this project"""
return self.qr_codes.filter_by(active_status=True).count()
@property
def total_qr_count(self):
"""Get total count of QR codes (including inactive) in this project"""
return self.qr_codes.count()
# Attendance Data Model
class AttendanceData(db.Model):
"""Enhanced attendance tracking model with location support"""
__tablename__ = 'attendance_data'
# Existing fields
id = db.Column(db.Integer, primary_key=True)
qr_code_id = db.Column(db.Integer, db.ForeignKey('qr_codes.id', ondelete='CASCADE'), nullable=False)
employee_id = db.Column(db.String(50), nullable=False)
check_in_date = db.Column(db.Date, nullable=False, default=datetime.today)
check_in_time = db.Column(db.Time, nullable=False, default=datetime.now().time)
device_info = db.Column(db.String(200))
user_agent = db.Column(db.Text)
ip_address = db.Column(db.String(45))
location_name = db.Column(db.String(100), nullable=False)
status = db.Column(db.String(20), default='present')
created_timestamp = db.Column(db.DateTime, default=datetime.utcnow)
updated_timestamp = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
latitude = db.Column(db.Float, nullable=True)
longitude = db.Column(db.Float, nullable=True)
accuracy = db.Column(db.Float, nullable=True)
location_accuracy = db.Column(db.Float, nullable=True)
altitude = db.Column(db.Float, nullable=True)
location_source = db.Column(db.String(50), default='manual')
address = db.Column(db.String(500), nullable=True)
# Relationships
qr_code = db.relationship('QRCode', backref=db.backref('attendance_records', lazy='dynamic'))
def __repr__(self):
return f'<AttendanceData {self.employee_id} at {self.location_name} on {self.check_in_date}>'
@property
def has_location_data(self):
"""Check if this record has GPS coordinates"""
return self.latitude is not None and self.longitude is not None
@property
def location_accuracy_level(self):
"""Get human-readable accuracy level"""
if not self.accuracy:
return 'unknown'
elif self.accuracy <= 50:
return 'high'
elif self.accuracy <= 100:
return 'medium'
else:
return 'low'
@property
def coordinates_display(self):
"""Get formatted coordinates for display"""
if self.has_location_data:
return f"{self.latitude:.10f}, {self.longitude:.10f}"
return "No GPS data"
def to_dict(self):
"""Convert to dictionary for JSON responses"""
return {
'id': self.id,
'employee_id': self.employee_id,
'check_in_date': self.check_in_date.isoformat(),
'check_in_time': self.check_in_time.isoformat(),
'location_name': self.location_name,
'status': self.status,
'has_location': self.has_location_data,
'coordinates': self.coordinates_display,
'accuracy': self.accuracy,
'address': self.address,
'location_source': self.location_source
}
# Utility functions
def is_valid_role(role):
"""Check if role is valid"""