Sep 16 - Optimize code, part 2
This commit is contained in:
+98
-38
@@ -24,6 +24,7 @@ from sqlalchemy import text, or_, and_
|
||||
from logger_handler import log_user_activity, log_database_operations
|
||||
from utils.helpers import (
|
||||
admin_required,
|
||||
load_project_manager_scope,
|
||||
expand_employee_id_filter,
|
||||
get_base_employee_id,
|
||||
get_client_ip,
|
||||
@@ -837,27 +838,38 @@ def time_attendance_locations_api():
|
||||
Used by the time attendance records page to dynamically scope the location dropdown."""
|
||||
try:
|
||||
project_id = request.args.get('project_id', '').strip()
|
||||
conditions = ["location_name IS NOT NULL"]
|
||||
params = {}
|
||||
|
||||
if project_id:
|
||||
try:
|
||||
project_id_int = int(project_id)
|
||||
params['project_id'] = int(project_id)
|
||||
except (ValueError, TypeError):
|
||||
return jsonify({'success': False, 'error': 'Invalid project_id'}), 400
|
||||
conditions.append("project_id = :project_id")
|
||||
|
||||
result = db.session.execute(text("""
|
||||
SELECT DISTINCT location_name
|
||||
FROM time_attendance
|
||||
WHERE project_id = :project_id
|
||||
AND location_name IS NOT NULL
|
||||
ORDER BY location_name
|
||||
"""), {'project_id': project_id_int})
|
||||
else:
|
||||
result = db.session.execute(text("""
|
||||
SELECT DISTINCT location_name
|
||||
FROM time_attendance
|
||||
WHERE location_name IS NOT NULL
|
||||
ORDER BY location_name
|
||||
"""))
|
||||
# Project Managers see only their assigned projects / locations (§4)
|
||||
is_pm, allowed_project_ids, allowed_location_names = load_project_manager_scope()
|
||||
if is_pm:
|
||||
if not (allowed_project_ids or allowed_location_names):
|
||||
return jsonify({'success': True, 'locations': []})
|
||||
scope = []
|
||||
if allowed_project_ids:
|
||||
placeholders = ', '.join(f':pm_project_{i}' for i in range(len(allowed_project_ids)))
|
||||
scope.append(f"project_id IN ({placeholders})")
|
||||
params.update({f'pm_project_{i}': pid for i, pid in enumerate(allowed_project_ids)})
|
||||
if allowed_location_names:
|
||||
placeholders = ', '.join(f':pm_location_{i}' for i in range(len(allowed_location_names)))
|
||||
scope.append(f"location_name IN ({placeholders})")
|
||||
params.update({f'pm_location_{i}': loc for i, loc in enumerate(allowed_location_names)})
|
||||
conditions.append('(' + ' OR '.join(scope) + ')')
|
||||
|
||||
result = db.session.execute(text(f"""
|
||||
SELECT DISTINCT location_name
|
||||
FROM time_attendance
|
||||
WHERE {' AND '.join(conditions)}
|
||||
ORDER BY location_name
|
||||
"""), params)
|
||||
|
||||
locations = [row[0] for row in result.fetchall()]
|
||||
logger_handler.logger.info(
|
||||
@@ -878,28 +890,39 @@ def attendance_locations_api():
|
||||
Used by the attendance report page to dynamically scope the location dropdown when a project is selected."""
|
||||
try:
|
||||
project_id = request.args.get('project_id', '').strip()
|
||||
conditions = ["ad.location_name IS NOT NULL"]
|
||||
params = {}
|
||||
|
||||
if project_id:
|
||||
try:
|
||||
project_id_int = int(project_id)
|
||||
params['project_id'] = int(project_id)
|
||||
except (ValueError, TypeError):
|
||||
return jsonify({'success': False, 'error': 'Invalid project_id'}), 400
|
||||
conditions.append("qc.project_id = :project_id")
|
||||
|
||||
result = db.session.execute(text("""
|
||||
SELECT DISTINCT ad.location_name
|
||||
FROM attendance_data ad
|
||||
INNER JOIN qr_codes qc ON ad.qr_code_id = qc.id
|
||||
WHERE qc.project_id = :project_id
|
||||
AND ad.location_name IS NOT NULL
|
||||
ORDER BY ad.location_name
|
||||
"""), {'project_id': project_id_int})
|
||||
else:
|
||||
result = db.session.execute(text("""
|
||||
SELECT DISTINCT location_name
|
||||
FROM attendance_data
|
||||
WHERE location_name IS NOT NULL
|
||||
ORDER BY location_name
|
||||
"""))
|
||||
# Project Managers see only their assigned projects / locations (§4)
|
||||
is_pm, allowed_project_ids, allowed_location_names = load_project_manager_scope()
|
||||
if is_pm:
|
||||
if not (allowed_project_ids or allowed_location_names):
|
||||
return jsonify({'success': True, 'locations': []})
|
||||
scope = []
|
||||
if allowed_project_ids:
|
||||
placeholders = ', '.join(f':pm_project_{i}' for i in range(len(allowed_project_ids)))
|
||||
scope.append(f"qc.project_id IN ({placeholders})")
|
||||
params.update({f'pm_project_{i}': pid for i, pid in enumerate(allowed_project_ids)})
|
||||
if allowed_location_names:
|
||||
placeholders = ', '.join(f':pm_location_{i}' for i in range(len(allowed_location_names)))
|
||||
scope.append(f"ad.location_name IN ({placeholders})")
|
||||
params.update({f'pm_location_{i}': loc for i, loc in enumerate(allowed_location_names)})
|
||||
conditions.append('(' + ' OR '.join(scope) + ')')
|
||||
|
||||
result = db.session.execute(text(f"""
|
||||
SELECT DISTINCT ad.location_name
|
||||
FROM attendance_data ad
|
||||
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
|
||||
WHERE {' AND '.join(conditions)}
|
||||
ORDER BY ad.location_name
|
||||
"""), params)
|
||||
|
||||
locations = [row[0] for row in result.fetchall()]
|
||||
logger_handler.logger.info(
|
||||
@@ -930,15 +953,35 @@ def search_employees_api():
|
||||
|
||||
search_pattern = f"%{search_query}%"
|
||||
|
||||
# Project Managers may only search within their own projects (§4). A PM
|
||||
# scoped by locations is resolved to the projects those locations belong
|
||||
# to, so their report filter still works without exposing other projects.
|
||||
is_pm, allowed_project_ids, allowed_location_names = load_project_manager_scope()
|
||||
pm_project_ids = []
|
||||
if is_pm:
|
||||
pm_project_ids = list(allowed_project_ids)
|
||||
if allowed_location_names:
|
||||
location_projects = db.session.query(QRCode.project_id).filter(
|
||||
QRCode.location.in_(allowed_location_names),
|
||||
QRCode.project_id.isnot(None)
|
||||
).distinct().all()
|
||||
pm_project_ids.extend(row[0] for row in location_projects)
|
||||
pm_project_ids = sorted(set(pm_project_ids))
|
||||
if not pm_project_ids:
|
||||
return jsonify({'employees': []})
|
||||
|
||||
# 1. Registered employees — search by ID or name
|
||||
employees = Employee.query.filter(
|
||||
employee_query = Employee.query.filter(
|
||||
db.or_(
|
||||
Employee.id.like(search_pattern),
|
||||
Employee.firstName.like(search_pattern),
|
||||
Employee.lastName.like(search_pattern),
|
||||
db.func.concat(Employee.firstName, ' ', Employee.lastName).like(search_pattern)
|
||||
)
|
||||
).limit(10).all()
|
||||
)
|
||||
if is_pm:
|
||||
employee_query = employee_query.filter(Employee.contractId.in_(pm_project_ids))
|
||||
employees = employee_query.limit(10).all()
|
||||
|
||||
employee_list = [{
|
||||
'id': emp.id,
|
||||
@@ -955,17 +998,25 @@ def search_employees_api():
|
||||
if len(employee_list) < 10:
|
||||
remaining_slots = 10 - len(employee_list)
|
||||
try:
|
||||
unregistered_conditions = ["e.id IS NULL", "ad.employee_id LIKE :pattern"]
|
||||
unregistered_params = {'pattern': search_pattern, 'lim': remaining_slots}
|
||||
if is_pm:
|
||||
placeholders = ', '.join(f':pm_project_{i}' for i in range(len(pm_project_ids)))
|
||||
unregistered_conditions.append(f"qc.project_id IN ({placeholders})")
|
||||
unregistered_params.update(
|
||||
{f'pm_project_{i}': pid for i, pid in enumerate(pm_project_ids)})
|
||||
|
||||
unregistered_rows = db.session.execute(
|
||||
text("""
|
||||
text(f"""
|
||||
SELECT DISTINCT ad.employee_id
|
||||
FROM attendance_data ad
|
||||
LEFT JOIN employee e ON CAST(ad.employee_id AS UNSIGNED) = e.id
|
||||
WHERE e.id IS NULL
|
||||
AND ad.employee_id LIKE :pattern
|
||||
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
|
||||
WHERE {' AND '.join(unregistered_conditions)}
|
||||
ORDER BY ad.employee_id
|
||||
LIMIT :lim
|
||||
"""),
|
||||
{'pattern': search_pattern, 'lim': remaining_slots}
|
||||
unregistered_params
|
||||
).fetchall()
|
||||
|
||||
for row in unregistered_rows:
|
||||
@@ -999,7 +1050,16 @@ def get_project_locations_api():
|
||||
|
||||
if not project_id:
|
||||
return jsonify({'success': False, 'locations': [], 'error': 'Project ID required'})
|
||||
|
||||
|
||||
# Project Managers may only ask about their own projects (§4)
|
||||
is_pm, allowed_project_ids, _ = load_project_manager_scope()
|
||||
if is_pm and int(project_id) not in allowed_project_ids:
|
||||
logger_handler.logger.warning(
|
||||
f"Project Manager {session.get('username')} requested locations for "
|
||||
f"project {project_id}, which is not assigned to them"
|
||||
)
|
||||
return jsonify({'success': True, 'locations': []})
|
||||
|
||||
# Get active QR codes for this project
|
||||
qr_codes = QRCode.query.filter_by(
|
||||
project_id=int(project_id),
|
||||
|
||||
Reference in New Issue
Block a user