From 1d762a2641c5e3ef524d4f29d8b973fe89a968dc Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Wed, 13 Aug 2025 21:03:38 -0400 Subject: [PATCH] UPdate qr code edit page --- app.py | 102 +++++++++++++++--- templates/edit_qr_code.html | 201 ++++++++++++++++++------------------ 2 files changed, 185 insertions(+), 118 deletions(-) diff --git a/app.py b/app.py index d79105a..e8b84df 100644 --- a/app.py +++ b/app.py @@ -2422,7 +2422,7 @@ def create_qr_code(): @login_required @log_database_operations('qr_code_edit') def edit_qr_code(qr_id): - """Enhanced edit QR code with project association and URL regeneration""" + """Enhanced edit QR code with project association and URL regeneration - COORDINATE FIX""" try: qr_code = QRCode.query.get_or_404(qr_id) @@ -2434,7 +2434,10 @@ def edit_qr_code(qr_id): 'location_address': qr_code.location_address, 'location_event': qr_code.location_event, 'project_id': qr_code.project_id, - 'qr_url': qr_code.qr_url + 'qr_url': qr_code.qr_url, + 'address_latitude': qr_code.address_latitude, + 'address_longitude': qr_code.address_longitude, + 'coordinate_accuracy': qr_code.coordinate_accuracy } # Update QR code fields @@ -2444,11 +2447,12 @@ def edit_qr_code(qr_id): qr_code.location_address = request.form['location_address'] qr_code.location_event = request.form.get('location_event', '') - # Handle coordinates update - latitude = request.form.get('latitude') - longitude = request.form.get('longitude') + # SIMPLIFIED COORDINATE HANDLING - Remove validation requirement + latitude = request.form.get('address_latitude') # Changed from 'latitude' to match form + longitude = request.form.get('address_longitude') # Changed from 'longitude' to match form coordinate_accuracy = request.form.get('coordinate_accuracy', 'geocoded') + # Update coordinates if provided, or keep existing ones if latitude and longitude: try: qr_code.address_latitude = float(latitude) @@ -2456,19 +2460,33 @@ def edit_qr_code(qr_id): qr_code.coordinate_accuracy = coordinate_accuracy qr_code.coordinates_updated_date = datetime.utcnow() except (ValueError, TypeError): - pass # Keep existing coordinates if invalid + # If invalid coordinates, keep existing ones + pass + elif latitude == '' and longitude == '': + # Explicitly clear coordinates if empty strings are sent + qr_code.address_latitude = None + qr_code.address_longitude = None + qr_code.coordinate_accuracy = None + qr_code.coordinates_updated_date = None + # If no coordinate data is provided, keep existing coordinates unchanged - # Handle project association + # Handle project association - FIXED LOGIC new_project_id = request.form.get('project_id') - if new_project_id: - new_project_id = int(new_project_id) - project = Project.query.get(new_project_id) - if project and project.active_status: - qr_code.project_id = new_project_id - else: - flash('Selected project is not valid or inactive.', 'error') + if new_project_id and new_project_id.strip(): # Check for non-empty string + try: + new_project_id = int(new_project_id) + project = Project.query.get(new_project_id) + if project and project.active_status: + qr_code.project_id = new_project_id + else: + flash('Selected project is not valid or inactive.', 'error') + return render_template('edit_qr_code.html', qr_code=qr_code, projects=Project.query.filter_by(active_status=True).all()) + except (ValueError, TypeError): + # Invalid project_id format + flash('Invalid project selection.', 'error') return render_template('edit_qr_code.html', qr_code=qr_code, projects=Project.query.filter_by(active_status=True).all()) else: + # Empty or None project_id means unassign from project qr_code.project_id = None # Regenerate URL if name changed @@ -2483,16 +2501,24 @@ def edit_qr_code(qr_id): qr_code.qr_url = new_qr_url qr_code.qr_code_image = new_qr_image + # ENHANCED CHANGE DETECTION + project_changed = old_data['project_id'] != qr_code.project_id + coordinates_changed = (old_data['address_latitude'] != qr_code.address_latitude or + old_data['address_longitude'] != qr_code.address_longitude) + db.session.commit() - # Log changes + # Log changes with improved tracking new_data = { 'name': qr_code.name, 'location': qr_code.location, 'location_address': qr_code.location_address, 'location_event': qr_code.location_event, 'project_id': qr_code.project_id, - 'qr_url': qr_code.qr_url + 'qr_url': qr_code.qr_url, + 'address_latitude': qr_code.address_latitude, + 'address_longitude': qr_code.address_longitude, + 'coordinate_accuracy': qr_code.coordinate_accuracy } changes = {} @@ -2500,11 +2526,53 @@ def edit_qr_code(qr_id): if old_data[key] != new_data[key]: changes[key] = {'old': old_data[key], 'new': new_data[key]} + # Enhanced logging for all changes if changes: logger_handler.logger.info(f"User {session['username']} updated QR code {qr_id}: {json.dumps(changes)}") + + # Log specific change actions + if project_changed: + old_project_name = None + new_project_name = None + + if old_data['project_id']: + old_project = Project.query.get(old_data['project_id']) + old_project_name = old_project.name if old_project else f"Project ID {old_data['project_id']}" + + if qr_code.project_id: + new_project = Project.query.get(qr_code.project_id) + new_project_name = new_project.name if new_project else f"Project ID {qr_code.project_id}" + + project_change_msg = f"QR code '{qr_code.name}' project changed from '{old_project_name or 'No Project'}' to '{new_project_name or 'No Project'}'" + logger_handler.logger.info(f"User {session['username']} - {project_change_msg}") + + if coordinates_changed: + coord_change_msg = f"QR code '{qr_code.name}' coordinates updated" + if qr_code.has_coordinates: + coord_change_msg += f" to {qr_code.coordinates_display}" + else: + coord_change_msg += " (coordinates cleared)" + logger_handler.logger.info(f"User {session['username']} - {coord_change_msg}") + # Success message with detailed information url_message = f" URL updated to: {qr_code.qr_url}" if name_changed else "" - flash(f'QR Code "{qr_code.name}" updated successfully!{url_message}', 'success') + project_message = "" + coord_message = "" + + if project_changed: + if qr_code.project_id: + current_project = Project.query.get(qr_code.project_id) + project_message = f" Project updated to: {current_project.name if current_project else 'Unknown'}" + else: + project_message = " Removed from project" + + if coordinates_changed: + if qr_code.has_coordinates: + coord_message = f" Coordinates updated: {qr_code.coordinates_display}" + else: + coord_message = " Coordinates cleared" + + flash(f'QR Code "{qr_code.name}" updated successfully!{url_message}{project_message}{coord_message}', 'success') return redirect(url_for('dashboard')) # Get active projects for dropdown diff --git a/templates/edit_qr_code.html b/templates/edit_qr_code.html index 5d64530..9bed09b 100644 --- a/templates/edit_qr_code.html +++ b/templates/edit_qr_code.html @@ -777,6 +777,7 @@ }; let originalData = {}; + let originalProjectId = {% if qr_code.project_id %}{{ qr_code.project_id }}{% else %}null{% endif %}; // Initialize the form when page loads document.addEventListener('DOMContentLoaded', function() { @@ -797,25 +798,15 @@ } function initializeForm() { - // Form validation on submit + // Form validation on submit - REMOVED COORDINATE UPDATE REQUIREMENT document.getElementById('editQRForm').addEventListener('submit', function(e) { if (!validateForm()) { e.preventDefault(); return false; } - // Check if changes were made - if (!hasChanges()) { - e.preventDefault(); - showStatus('warning', 'No changes detected. Please modify the information to update the QR code.'); - return false; - } - - // Confirm changes before submission - if (!confirmSaveChanges()) { - e.preventDefault(); - return false; - } + // REMOVED: Check if changes were made - Allow form submission regardless of changes + // This enables updates even when only project changes or coordinate updates occur // Show loading state const submitBtn = document.getElementById('submitBtn'); @@ -851,87 +842,56 @@ }); } - function initializeCoordinatesFeature() { - const geocodeBtn = document.getElementById('geocodeBtn'); - const clearBtn = document.getElementById('clearCoordinatesBtn'); - - // Geocode button click - geocodeBtn.addEventListener('click', function() { - const address = document.getElementById('location_address').value.trim(); - if (address.length > 10) { - geocodeAddress(address); - } else { - showStatus('error', 'Please enter a complete address first'); - } - }); - - // Clear coordinates button - clearBtn.addEventListener('click', clearCoordinates); - } - - function initializeChangeTracking() { - const inputs = document.querySelectorAll('#name, #location, #location_address, #location_event'); - - inputs.forEach(input => { - input.addEventListener('input', updateCurrentInfo); - }); - } - - function updateCurrentInfo() { - document.getElementById('currentName').textContent = document.getElementById('name').value || '-'; - document.getElementById('currentLocation').textContent = document.getElementById('location').value || '-'; - document.getElementById('currentEvent').textContent = document.getElementById('location_event').value || '-'; - document.getElementById('currentAddress').textContent = document.getElementById('location_address').value || '-'; - } - - async function geocodeAddress(address) { + function geocodeAddress(address) { const geocodeBtn = document.getElementById('geocodeBtn'); const statusDiv = document.getElementById('coordinateStatus'); // Show loading state - geocodeBtn.innerHTML = ' Getting coordinates...'; + geocodeBtn.innerHTML = ' Getting Coordinates...'; geocodeBtn.disabled = true; - try { - const response = await fetch('/api/geocode', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ address: address }) + showStatus('info', 'Getting coordinates from address...'); + + const geocodeUrl = `https://api.opencagedata.com/geocode/v1/json?q=${encodeURIComponent(address)}&key=b0e9dd83c98e4cfebc6a9ef1d83f5a6e&limit=1&countrycode=my&language=en`; + + fetch(geocodeUrl) + .then(response => response.json()) + .then(data => { + console.log('Geocoding response:', data); + + if (data.results && data.results.length > 0) { + const result = data.results[0]; + const lat = result.geometry.lat; + const lng = result.geometry.lng; + const confidence = result.confidence || 0; + + // Update coordinates data + coordinatesData = { + latitude: lat, + longitude: lng, + accuracy: confidence >= 8 ? 'high' : confidence >= 5 ? 'medium' : 'low' + }; + + updateCoordinatesDisplay(); + updateHiddenFields(); + + const confidenceText = confidence >= 8 ? 'high' : confidence >= 5 ? 'medium' : 'low'; + showStatus('success', `Coordinates updated successfully! Accuracy: ${confidenceText}`); + + console.log('✓ Coordinates updated:', coordinatesData); + } else { + showStatus('error', 'Could not find coordinates for this address. Please check the address and try again.'); + } + }) + .catch(error => { + console.error('Geocoding error:', error); + showStatus('error', 'Failed to get coordinates. Please try again.'); + }) + .finally(() => { + // Reset button state + geocodeBtn.innerHTML = ' Update Coordinates'; + geocodeBtn.disabled = false; }); - - const result = await response.json(); - - if (result.success) { - // Update coordinates - coordinatesData = { - latitude: result.data.latitude, - longitude: result.data.longitude, - accuracy: result.data.accuracy - }; - - // Update display - updateCoordinatesDisplay(); - - // Update hidden form fields - updateHiddenFields(); - - // Show success message - showStatus('success', result.message); - - } else { - showStatus('error', result.message); - } - - } catch (error) { - console.error('Geocoding error:', error); - showStatus('error', 'Network error occurred. Please try again.'); - } - - // Reset button state - geocodeBtn.innerHTML = ' Update Coordinates'; - geocodeBtn.disabled = false; } function updateCoordinatesDisplay() { @@ -972,6 +932,7 @@ showStatus('warning', 'Coordinates cleared. Click "Update QR Code" to save changes.'); } + // ENHANCED hasChanges function - Now includes project changes and allows coordinate-only updates function hasChanges() { const currentData = { name: document.getElementById('name').value.trim(), @@ -980,30 +941,35 @@ location_event: document.getElementById('location_event').value.trim() }; - return Object.keys(originalData).some(key => { + // Check basic field changes + const fieldChanges = Object.keys(originalData).some(key => { return currentData[key] !== originalData[key]; }); + + // Check project changes + const currentProjectId = document.getElementById('project_id').value; + const projectIdValue = currentProjectId ? parseInt(currentProjectId) : null; + const projectChanged = originalProjectId !== projectIdValue; + + console.log('Change detection:', { + fieldChanges, + projectChanged, + originalProjectId, + currentProjectId: projectIdValue + }); + + return fieldChanges || projectChanged; } - function confirmSaveChanges() { - const changeCount = Object.keys(originalData).filter(key => { - const currentValue = document.getElementById(key).value.trim(); - return currentValue !== originalData[key]; - }).length; - - return confirm( - `You have made ${changeCount} change${changeCount !== 1 ? 's' : ''} to this QR code.\n\n` + - 'Saving will update the QR code with the new information.\n\n' + - 'Are you sure you want to continue?' - ); - } + // REMOVED: confirmSaveChanges function - No longer needed since we're allowing all updates function showStatus(type, message) { const statusDiv = document.getElementById('coordinateStatus'); const iconMap = { 'success': 'fas fa-check-circle', 'error': 'fas fa-exclamation-circle', - 'warning': 'fas fa-exclamation-triangle' + 'warning': 'fas fa-exclamation-triangle', + 'info': 'fas fa-info-circle' }; statusDiv.innerHTML = ` @@ -1080,6 +1046,39 @@ errorElement.remove(); } } + + function initializeCoordinatesFeature() { + const geocodeBtn = document.getElementById('geocodeBtn'); + const clearBtn = document.getElementById('clearCoordinatesBtn'); + + // Geocode button click + geocodeBtn.addEventListener('click', function() { + const address = document.getElementById('location_address').value.trim(); + if (address.length > 10) { + geocodeAddress(address); + } else { + showStatus('error', 'Please enter a complete address first'); + } + }); + + // Clear coordinates button + clearBtn.addEventListener('click', clearCoordinates); + } + + function initializeChangeTracking() { + const inputs = document.querySelectorAll('#name, #location, #location_address, #location_event'); + + inputs.forEach(input => { + input.addEventListener('input', updateCurrentInfo); + }); + } + + function updateCurrentInfo() { + document.getElementById('currentName').textContent = document.getElementById('name').value || '-'; + document.getElementById('currentLocation').textContent = document.getElementById('location').value || '-'; + document.getElementById('currentEvent').textContent = document.getElementById('location_event').value || '-'; + document.getElementById('currentAddress').textContent = document.getElementById('location_address').value || '-'; + }