Added filter by project

This commit is contained in:
2025-08-14 20:34:10 -04:00
parent c2f402f45a
commit a313943ee1
3 changed files with 138 additions and 82 deletions
+32 -1
View File
@@ -3001,6 +3001,7 @@ def attendance_report():
date_to = request.args.get('date_to', '') date_to = request.args.get('date_to', '')
location_filter = request.args.get('location', '') location_filter = request.args.get('location', '')
employee_filter = request.args.get('employee', '') employee_filter = request.args.get('employee', '')
project_filter = request.args.get('project', '')
# Build base query - conditional based on column existence # Build base query - conditional based on column existence
if has_location_accuracy: if has_location_accuracy:
@@ -3068,6 +3069,11 @@ def attendance_report():
conditions.append("ad.employee_id ILIKE :employee") conditions.append("ad.employee_id ILIKE :employee")
params['employee'] = f"%{employee_filter}%" params['employee'] = f"%{employee_filter}%"
if project_filter:
# Join with QRCode and filter by project_id
query = query.join(QRCode, AttendanceData.qr_code_id == QRCode.id).filter(QRCode.project_id == int(project_filter))
print(f"📊 Applied project filter: {project_filter}")
# Add conditions to query # Add conditions to query
if conditions: if conditions:
base_query += " AND " + " AND ".join(conditions) base_query += " AND " + " AND ".join(conditions)
@@ -3171,6 +3177,23 @@ def attendance_report():
print(f"⚠️ Error loading locations: {e}") print(f"⚠️ Error loading locations: {e}")
locations = [] locations = []
# Update the locations query to get only active projects for the dropdown
try:
projects = db.session.execute(text("""
SELECT p.id, p.name, COUNT(DISTINCT ad.id) as attendance_count
FROM projects p
LEFT JOIN qr_codes qc ON qc.project_id = p.id
LEFT JOIN attendance_data ad ON ad.qr_code_id = qc.id
WHERE p.active_status = true
GROUP BY p.id, p.name
HAVING COUNT(DISTINCT ad.id) > 0
ORDER BY p.name
""")).fetchall()
print(f"✅ Loaded {len(projects)} projects with attendance data")
except Exception as e:
print(f"⚠️ Error loading projects: {e}")
projects = []
# Get attendance statistics # Get attendance statistics
try: try:
if has_location_accuracy: if has_location_accuracy:
@@ -3222,11 +3245,13 @@ def attendance_report():
return render_template('attendance_report.html', return render_template('attendance_report.html',
attendance_records=processed_records, attendance_records=processed_records,
locations=locations, locations=locations,
projects=projects,
stats=stats, stats=stats,
date_from=date_from, date_from=date_from,
date_to=date_to, date_to=date_to,
location_filter=location_filter, location_filter=location_filter,
employee_filter=employee_filter, employee_filter=employee_filter,
project_filter=project_filter,
today_date=today_date, today_date=today_date,
current_date_formatted=current_date_formatted, current_date_formatted=current_date_formatted,
has_location_accuracy_feature=has_location_accuracy) has_location_accuracy_feature=has_location_accuracy)
@@ -3452,7 +3477,8 @@ def export_configuration():
'date_from': request.args.get('date_from', ''), 'date_from': request.args.get('date_from', ''),
'date_to': request.args.get('date_to', ''), 'date_to': request.args.get('date_to', ''),
'location_filter': request.args.get('location', ''), 'location_filter': request.args.get('location', ''),
'employee_filter': request.args.get('employee', '') 'employee_filter': request.args.get('employee', ''),
'project_filter': request.args.get('project', '')
} }
print(f"📊 Filters: {filters}") print(f"📊 Filters: {filters}")
@@ -3670,6 +3696,11 @@ def create_excel_export(selected_columns, column_names, filters):
query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%")) query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%"))
print(f"📊 Applied employee filter: {filters['employee_filter']}") print(f"📊 Applied employee filter: {filters['employee_filter']}")
# Apply project filter
if filters.get('project_filter'):
query = query.filter(QRCode.project_id == int(filters['project_filter']))
print(f"📊 Applied project filter to export: {filters['project_filter']}")
# Execute query and get results # Execute query and get results
results = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all() results = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all()
print(f"📊 Found {len(results)} records for export") print(f"📊 Found {len(results)} records for export")
+78 -67
View File
@@ -947,66 +947,75 @@ function exportAttendanceWithAccuracy() {
} }
function exportAttendance() { function exportAttendance() {
// Log export action // Log export action
console.log('Export button clicked - redirecting to configuration page'); console.log("Export button clicked - redirecting to configuration page");
// Get current filters // Get current filters
const currentFilters = getCurrentFilters(); const currentFilters = getCurrentFilters();
// Build URL with current filters // Build URL with current filters
const params = new URLSearchParams(); const params = new URLSearchParams();
if (currentFilters.date_from) params.append('date_from', currentFilters.date_from); if (currentFilters.date_from)
if (currentFilters.date_to) params.append('date_to', currentFilters.date_to); params.append("date_from", currentFilters.date_from);
if (currentFilters.location) params.append('location', currentFilters.location); if (currentFilters.date_to) params.append("date_to", currentFilters.date_to);
if (currentFilters.employee) params.append('employee', currentFilters.employee); if (currentFilters.location)
params.append("location", currentFilters.location);
if (currentFilters.employee)
params.append("employee", currentFilters.employee);
// Navigate to export configuration page // Navigate to export configuration page
const configUrl = '/export-configuration' + (params.toString() ? '?' + params.toString() : ''); const configUrl =
window.location.href = configUrl; "/export-configuration" +
(params.toString() ? "?" + params.toString() : "");
window.location.href = configUrl;
} }
function getCurrentFilters() { function getCurrentFilters() {
// Extract current filter values from the page // Extract current filter values from the page
return { return {
date_from: document.getElementById('date_from')?.value || '', date_from: document.getElementById("date_from")?.value || "",
date_to: document.getElementById('date_to')?.value || '', date_to: document.getElementById("date_to")?.value || "",
location: document.getElementById('location')?.value || '', location: document.getElementById("location")?.value || "",
employee: document.getElementById('employee')?.value || '' employee: document.getElementById("employee")?.value || "",
}; project: document.getElementById("project")?.value || "",
};
} }
// Add a quick CSV export function as backup (keep existing functionality) // Add a quick CSV export function as backup (keep existing functionality)
function exportAttendanceCSV() { function exportAttendanceCSV() {
// Build export URL with current filters for CSV // Build export URL with current filters for CSV
const params = new URLSearchParams(); const params = new URLSearchParams();
const filters = getCurrentFilters(); const filters = getCurrentFilters();
if (filters.date_from) params.append('date_from', filters.date_from); if (filters.date_from) params.append("date_from", filters.date_from);
if (filters.date_to) params.append('date_to', filters.date_to); if (filters.date_to) params.append("date_to", filters.date_to);
if (filters.location) params.append('location', filters.location); if (filters.location) params.append("location", filters.location);
if (filters.employee) params.append('employee', filters.employee); if (filters.employee) params.append("employee", filters.employee);
params.append('export', 'csv'); if (filters.project) params.append("project", filters.project);
params.append("export", "csv");
// Create a temporary link and click it to download // Create a temporary link and click it to download
const downloadUrl = window.location.pathname + '?' + params.toString(); const downloadUrl = window.location.pathname + "?" + params.toString();
window.open(downloadUrl, '_blank'); window.open(downloadUrl, "_blank");
} }
// Enhanced export menu (if you want to add dropdown with multiple export options) // Enhanced export menu (if you want to add dropdown with multiple export options)
function showExportMenu() { function showExportMenu() {
// Create export options menu // Create export options menu
const existingMenu = document.getElementById('exportMenu'); const existingMenu = document.getElementById("exportMenu");
if (existingMenu) { if (existingMenu) {
existingMenu.remove(); existingMenu.remove();
return; return;
} }
const exportBtn = document.querySelector('button[onclick="exportAttendance()"]'); const exportBtn = document.querySelector(
if (!exportBtn) return; 'button[onclick="exportAttendance()"]'
);
if (!exportBtn) return;
const menu = document.createElement('div'); const menu = document.createElement("div");
menu.id = 'exportMenu'; menu.id = "exportMenu";
menu.style.cssText = ` menu.style.cssText = `
position: absolute; position: absolute;
top: 100%; top: 100%;
right: 0; right: 0;
@@ -1019,7 +1028,7 @@ function showExportMenu() {
margin-top: 5px; margin-top: 5px;
`; `;
menu.innerHTML = ` menu.innerHTML = `
<div style="padding: 0.5rem;"> <div style="padding: 0.5rem;">
<button onclick="exportAttendance(); closeExportMenu();" <button onclick="exportAttendance(); closeExportMenu();"
style="width: 100%; padding: 0.75rem; border: none; background: none; text-align: left; cursor: pointer; border-radius: 4px;" style="width: 100%; padding: 0.75rem; border: none; background: none; text-align: left; cursor: pointer; border-radius: 4px;"
@@ -1038,36 +1047,38 @@ function showExportMenu() {
</div> </div>
`; `;
exportBtn.parentElement.style.position = 'relative'; exportBtn.parentElement.style.position = "relative";
exportBtn.parentElement.appendChild(menu); exportBtn.parentElement.appendChild(menu);
// Close menu when clicking outside // Close menu when clicking outside
setTimeout(() => { setTimeout(() => {
document.addEventListener('click', function closeOnClickOutside(e) { document.addEventListener("click", function closeOnClickOutside(e) {
if (!menu.contains(e.target) && e.target !== exportBtn) { if (!menu.contains(e.target) && e.target !== exportBtn) {
closeExportMenu(); closeExportMenu();
document.removeEventListener('click', closeOnClickOutside); document.removeEventListener("click", closeOnClickOutside);
} }
}); });
}, 100); }, 100);
} }
function closeExportMenu() { function closeExportMenu() {
const menu = document.getElementById('exportMenu'); const menu = document.getElementById("exportMenu");
if (menu) { if (menu) {
menu.remove(); menu.remove();
} }
} }
// Initialize export functionality when page loads // Initialize export functionality when page loads
document.addEventListener('DOMContentLoaded', function() { document.addEventListener("DOMContentLoaded", function () {
// Update export button to use enhanced functionality // Update export button to use enhanced functionality
const exportBtn = document.querySelector('button[onclick="exportAttendance()"]'); const exportBtn = document.querySelector(
if (exportBtn) { 'button[onclick="exportAttendance()"]'
// You can modify the button to show a dropdown instead );
// exportBtn.onclick = showExportMenu; if (exportBtn) {
// exportBtn.innerHTML = '<i class="fas fa-download"></i> Export Data <i class="fas fa-chevron-down" style="margin-left: 0.5rem;"></i>'; // You can modify the button to show a dropdown instead
} // exportBtn.onclick = showExportMenu;
// exportBtn.innerHTML = '<i class="fas fa-download"></i> Export Data <i class="fas fa-chevron-down" style="margin-left: 0.5rem;"></i>';
}
console.log('Enhanced export functionality initialized'); console.log("Enhanced export functionality initialized");
}); });
+16 -2
View File
@@ -99,6 +99,20 @@
value="{{ employee_filter }}" value="{{ employee_filter }}"
placeholder="Enter Employee ID"> placeholder="Enter Employee ID">
</div> </div>
<div class="filter-group">
<label for="project">
<i class="fas fa-project-diagram"></i>
Project
</label>
<select id="project" name="project">
<option value="">All Projects</option>
{% for project in projects %}
<option value="{{ project.id }}" {{ 'selected' if project.id|string == project_filter else '' }}>
{{ project.name }} ({{ project.attendance_count }} records)
</option>
{% endfor %}
</select>
</div>
<div class="filter-actions"> <div class="filter-actions">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
@@ -122,7 +136,7 @@
<h3> <h3>
<i class="fas fa-list"></i> <i class="fas fa-list"></i>
Attendance Records Attendance Records
{% if date_from or date_to or location_filter or employee_filter %} {% if date_from or date_to or location_filter or employee_filter or project_filter %}
<span class="filter-indicator">(Filtered)</span> <span class="filter-indicator">(Filtered)</span>
{% endif %} {% endif %}
</h3> </h3>
@@ -299,7 +313,7 @@
{% else %} {% else %}
No staff have checked in yet. QR codes need to be scanned to generate attendance data. No staff have checked in yet. QR codes need to be scanned to generate attendance data.
{% endif %}</p> {% endif %}</p>
{% if date_from or date_to or location_filter or employee_filter %} {% if date_from or date_to or location_filter or employee_filter or project_filter %}
<button onclick="clearFilters()" class="btn btn-primary"> <button onclick="clearFilters()" class="btn btn-primary">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
Clear All Filters Clear All Filters