Fixed import issues
This commit is contained in:
@@ -6547,32 +6547,61 @@ def import_time_attendance():
|
|||||||
"""Enhanced import with duplicate review"""
|
"""Enhanced import with duplicate review"""
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
# Check if file is uploaded
|
# Check if this is coming from invalid review (file is already in session)
|
||||||
if 'file' not in request.files:
|
coming_from_invalid_review = request.form.get('from_invalid_review', 'false').lower() == 'true'
|
||||||
flash('No file selected.', 'error')
|
|
||||||
return redirect(request.url)
|
|
||||||
|
|
||||||
file = request.files['file']
|
print(f"\n🔍 IMPORT FLOW DEBUG:")
|
||||||
if file.filename == '':
|
print(f" Coming from invalid review: {coming_from_invalid_review}")
|
||||||
flash('No file selected.', 'error')
|
|
||||||
return redirect(request.url)
|
|
||||||
|
|
||||||
# Validate file extension
|
if coming_from_invalid_review:
|
||||||
if not file.filename.lower().endswith(('.xlsx', '.xls')):
|
# Retrieve file from session
|
||||||
flash('Please upload an Excel file (.xlsx or .xls).', 'error')
|
if 'pending_import_file' not in session or 'pending_import_filename' not in session:
|
||||||
return redirect(request.url)
|
flash('Session expired. Please upload the file again.', 'error')
|
||||||
|
return redirect(url_for('import_time_attendance'))
|
||||||
|
|
||||||
# Save uploaded file temporarily
|
temp_path = session['pending_import_file']
|
||||||
filename = secure_filename(file.filename)
|
filename = session['pending_import_filename']
|
||||||
temp_path = os.path.join(app.config.get('UPLOAD_FOLDER', '/tmp'),
|
|
||||||
f"temp_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}")
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(temp_path), exist_ok=True)
|
# Verify file still exists
|
||||||
file.save(temp_path)
|
if not os.path.exists(temp_path):
|
||||||
|
flash('Temporary file not found. Please upload the file again.', 'error')
|
||||||
|
session.pop('pending_import_file', None)
|
||||||
|
session.pop('pending_import_filename', None)
|
||||||
|
return redirect(url_for('import_time_attendance'))
|
||||||
|
|
||||||
# Store file path in session for duplicate review
|
print(f"✅ Retrieved file from session: {filename}")
|
||||||
session['pending_import_file'] = temp_path
|
print(f"✅ Temp path exists: {os.path.exists(temp_path)}")
|
||||||
session['pending_import_filename'] = filename
|
|
||||||
|
else:
|
||||||
|
# Normal file upload flow
|
||||||
|
if 'file' not in request.files:
|
||||||
|
flash('No file uploaded.', 'error')
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
if file.filename == '':
|
||||||
|
flash('No file selected.', 'error')
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
# Validate file extension
|
||||||
|
if not file.filename.lower().endswith(('.xlsx', '.xls')):
|
||||||
|
flash('Please upload an Excel file (.xlsx or .xls).', 'error')
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
|
# Save uploaded file temporarily
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
temp_path = os.path.join(app.config.get('UPLOAD_FOLDER', '/tmp'),
|
||||||
|
f"temp_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}")
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(temp_path), exist_ok=True)
|
||||||
|
file.save(temp_path)
|
||||||
|
|
||||||
|
# Store file path in session for duplicate/invalid review
|
||||||
|
session['pending_import_file'] = temp_path
|
||||||
|
session['pending_import_filename'] = filename
|
||||||
|
|
||||||
|
print(f"✅ Uploaded new file: {filename}")
|
||||||
|
print(f"✅ Saved to: {temp_path}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import_service = TimeAttendanceImportService(db, logger_handler)
|
import_service = TimeAttendanceImportService(db, logger_handler)
|
||||||
@@ -6581,54 +6610,74 @@ def import_time_attendance():
|
|||||||
skip_duplicates = request.form.get('skip_duplicates', 'true').lower() == 'true'
|
skip_duplicates = request.form.get('skip_duplicates', 'true').lower() == 'true'
|
||||||
validate_only = request.form.get('validate_only', 'false').lower() == 'true'
|
validate_only = request.form.get('validate_only', 'false').lower() == 'true'
|
||||||
analyze_duplicates = request.form.get('analyze_duplicates', 'false').lower() == 'true'
|
analyze_duplicates = request.form.get('analyze_duplicates', 'false').lower() == 'true'
|
||||||
|
analyze_invalid = request.form.get('analyze_invalid', 'false').lower() == 'true'
|
||||||
|
|
||||||
|
print(f"📋 Import Options:")
|
||||||
|
print(f" Skip duplicates: {skip_duplicates}")
|
||||||
|
print(f" Validate only: {validate_only}")
|
||||||
|
print(f" Analyze duplicates: {analyze_duplicates}")
|
||||||
|
print(f" Analyze invalid: {analyze_invalid}")
|
||||||
|
print(f" Coming from invalid review: {coming_from_invalid_review}")
|
||||||
|
|
||||||
# Check if this is coming from duplicate review
|
# Check if this is coming from duplicate review
|
||||||
force_import_hashes = request.form.getlist('force_import_hashes[]')
|
force_import_hashes = request.form.getlist('force_import_hashes[]')
|
||||||
|
|
||||||
# If analyzing for duplicates, show review page
|
# If analyzing for duplicates, show review page (but not if coming from invalid review)
|
||||||
if analyze_duplicates and not force_import_hashes:
|
if analyze_duplicates and not force_import_hashes and not coming_from_invalid_review:
|
||||||
|
print("🔍 Analyzing for duplicates...")
|
||||||
duplicate_analysis = import_service.analyze_for_duplicates(temp_path)
|
duplicate_analysis = import_service.analyze_for_duplicates(temp_path)
|
||||||
|
|
||||||
if duplicate_analysis['duplicate_records'] > 0:
|
if duplicate_analysis['duplicate_records'] > 0:
|
||||||
|
print(f"⚠️ Found {duplicate_analysis['duplicate_records']} duplicates")
|
||||||
# Show duplicate review page
|
# Show duplicate review page
|
||||||
return render_template('time_attendance_duplicate_review.html',
|
return render_template('time_attendance_duplicate_review.html',
|
||||||
analysis=duplicate_analysis,
|
analysis=duplicate_analysis,
|
||||||
filename=filename)
|
filename=filename)
|
||||||
else:
|
else:
|
||||||
|
print("✅ No duplicates found")
|
||||||
flash('No duplicates found. Proceeding with import.', 'info')
|
flash('No duplicates found. Proceeding with import.', 'info')
|
||||||
|
|
||||||
# Check for invalid rows and show review if any
|
# Check for invalid rows and show review if any (but not if coming from invalid review)
|
||||||
analyze_invalid = request.form.get('analyze_invalid', 'false').lower() == 'true'
|
if analyze_invalid and not coming_from_invalid_review:
|
||||||
|
print("🔍 Analyzing for invalid rows...")
|
||||||
if analyze_invalid:
|
|
||||||
invalid_analysis = import_service.analyze_for_invalid_rows(temp_path)
|
invalid_analysis = import_service.analyze_for_invalid_rows(temp_path)
|
||||||
|
|
||||||
if invalid_analysis['invalid_rows'] > 0:
|
if invalid_analysis['invalid_rows'] > 0:
|
||||||
|
print(f"⚠️ Found {invalid_analysis['invalid_rows']} invalid rows")
|
||||||
# Show invalid row review page
|
# Show invalid row review page
|
||||||
return render_template('time_attendance_invalid_review.html',
|
return render_template('time_attendance_invalid_review.html',
|
||||||
analysis=invalid_analysis,
|
analysis=invalid_analysis,
|
||||||
filename=filename)
|
filename=filename)
|
||||||
else:
|
else:
|
||||||
|
print("✅ All rows are valid")
|
||||||
flash('All rows are valid. Proceeding with import.', 'info')
|
flash('All rows are valid. Proceeding with import.', 'info')
|
||||||
|
|
||||||
# Validate file
|
# If coming from invalid review, skip validation (already done)
|
||||||
validation_result = import_service.validate_excel_file(temp_path)
|
if not coming_from_invalid_review:
|
||||||
|
print("🔍 Validating file...")
|
||||||
|
# Validate file
|
||||||
|
validation_result = import_service.validate_excel_file(temp_path)
|
||||||
|
|
||||||
if not validation_result['valid']:
|
if not validation_result['valid']:
|
||||||
flash(f"File validation failed: {'; '.join(validation_result['errors'])}", 'error')
|
print(f"❌ Validation failed: {validation_result['errors']}")
|
||||||
return render_template('time_attendance_import.html',
|
flash(f"File validation failed: {'; '.join(validation_result['errors'])}", 'error')
|
||||||
validation_result=validation_result)
|
return render_template('time_attendance_import.html',
|
||||||
|
validation_result=validation_result)
|
||||||
|
|
||||||
if validation_result['warnings']:
|
if validation_result['warnings']:
|
||||||
for warning in validation_result['warnings']:
|
for warning in validation_result['warnings']:
|
||||||
flash(warning, 'warning')
|
flash(warning, 'warning')
|
||||||
|
|
||||||
if validate_only:
|
if validate_only:
|
||||||
flash(f"File validation successful! Found {validation_result['valid_rows']} valid records.", 'success')
|
print(f"✅ Validation successful: {validation_result['valid_rows']} valid records")
|
||||||
return render_template('time_attendance_import.html',
|
flash(f"File validation successful! Found {validation_result['valid_rows']} valid records.", 'success')
|
||||||
validation_result=validation_result)
|
return render_template('time_attendance_import.html',
|
||||||
|
validation_result=validation_result)
|
||||||
|
else:
|
||||||
|
print("⏭️ Skipping validation (already validated)")
|
||||||
|
|
||||||
# Proceed with import
|
# Proceed with import
|
||||||
|
print("🚀 Starting import process...")
|
||||||
import_source = request.form.get('import_source', f"Manual Import - {filename}")
|
import_source = request.form.get('import_source', f"Manual Import - {filename}")
|
||||||
import_result = import_service.import_from_excel(
|
import_result = import_service.import_from_excel(
|
||||||
temp_path,
|
temp_path,
|
||||||
@@ -6639,6 +6688,12 @@ def import_time_attendance():
|
|||||||
)
|
)
|
||||||
|
|
||||||
if import_result['success']:
|
if import_result['success']:
|
||||||
|
print(f"✅ Import successful!")
|
||||||
|
print(f" Batch ID: {import_result['batch_id']}")
|
||||||
|
print(f" Imported: {import_result['imported_records']}/{import_result['total_records']}")
|
||||||
|
print(f" Duplicates: {import_result['duplicate_records']}")
|
||||||
|
print(f" Failed: {import_result['failed_records']}")
|
||||||
|
|
||||||
logger_handler.logger.info(
|
logger_handler.logger.info(
|
||||||
f"User {session['username']} successfully imported time attendance data - "
|
f"User {session['username']} successfully imported time attendance data - "
|
||||||
f"Batch: {import_result['batch_id']}, "
|
f"Batch: {import_result['batch_id']}, "
|
||||||
@@ -6661,33 +6716,41 @@ def import_time_attendance():
|
|||||||
flash(f"Note: {import_result['failed_records']} records failed to import. "
|
flash(f"Note: {import_result['failed_records']} records failed to import. "
|
||||||
f"Check the error details below.", 'warning')
|
f"Check the error details below.", 'warning')
|
||||||
|
|
||||||
|
# Clean up temp file after successful import
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
try:
|
||||||
|
os.remove(temp_path)
|
||||||
|
session.pop('pending_import_file', None)
|
||||||
|
session.pop('pending_import_filename', None)
|
||||||
|
print("🗑️ Cleaned up temp file")
|
||||||
|
except Exception as cleanup_error:
|
||||||
|
print(f"⚠️ Failed to cleanup temp file: {cleanup_error}")
|
||||||
|
|
||||||
return render_template('time_attendance_import_result.html',
|
return render_template('time_attendance_import_result.html',
|
||||||
import_result=import_result)
|
import_result=import_result)
|
||||||
else:
|
else:
|
||||||
|
print(f"❌ Import failed: {import_result['errors']}")
|
||||||
flash(f"Import failed: {'; '.join(import_result['errors'][:3])}", 'error')
|
flash(f"Import failed: {'; '.join(import_result['errors'][:3])}", 'error')
|
||||||
if len(import_result['errors']) > 3:
|
if len(import_result['errors']) > 3:
|
||||||
flash(f"...and {len(import_result['errors']) - 3} more errors", 'warning')
|
flash(f"...and {len(import_result['errors']) - 3} more errors", 'warning')
|
||||||
return render_template('time_attendance_import.html',
|
return render_template('time_attendance_import.html',
|
||||||
import_result=import_result)
|
import_result=import_result)
|
||||||
|
|
||||||
finally:
|
except Exception as import_error:
|
||||||
# Clean up if import completed or failed (not if showing duplicate review)
|
print(f"❌ Import exception: {import_error}")
|
||||||
if not analyze_duplicates or force_import_hashes:
|
import traceback
|
||||||
if os.path.exists(temp_path):
|
print(f"❌ Traceback: {traceback.format_exc()}")
|
||||||
try:
|
raise
|
||||||
os.remove(temp_path)
|
|
||||||
if 'pending_import_file' in session:
|
|
||||||
session.pop('pending_import_file')
|
|
||||||
if 'pending_import_filename' in session:
|
|
||||||
session.pop('pending_import_filename')
|
|
||||||
except Exception as cleanup_error:
|
|
||||||
logger_handler.logger.warning(f"Failed to cleanup temp file: {cleanup_error}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger_handler.log_database_error('time_attendance_import', e)
|
logger_handler.log_database_error('time_attendance_import', e)
|
||||||
|
print(f"❌ Top-level exception: {e}")
|
||||||
|
import traceback
|
||||||
|
print(f"❌ Traceback: {traceback.format_exc()}")
|
||||||
flash('Import failed due to an unexpected error.', 'error')
|
flash('Import failed due to an unexpected error.', 'error')
|
||||||
return render_template('time_attendance_import.html')
|
return render_template('time_attendance_import.html')
|
||||||
|
|
||||||
|
# GET request
|
||||||
return render_template('time_attendance_import.html')
|
return render_template('time_attendance_import.html')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -321,7 +321,10 @@
|
|||||||
<!-- Invalid Rows List -->
|
<!-- Invalid Rows List -->
|
||||||
{% if analysis.invalid_rows > 0 %}
|
{% if analysis.invalid_rows > 0 %}
|
||||||
<form id="invalidReviewForm" method="POST" action="{{ url_for('import_time_attendance') }}">
|
<form id="invalidReviewForm" method="POST" action="{{ url_for('import_time_attendance') }}">
|
||||||
|
<input type="hidden" name="from_invalid_review" value="true">
|
||||||
<input type="hidden" name="analyze_invalid" value="false">
|
<input type="hidden" name="analyze_invalid" value="false">
|
||||||
|
<input type="hidden" name="analyze_duplicates" value="false">
|
||||||
|
<input type="hidden" name="validate_only" value="false">
|
||||||
<input type="hidden" name="skip_duplicates" value="true">
|
<input type="hidden" name="skip_duplicates" value="true">
|
||||||
<input type="hidden" name="import_source" value="Import (Skipped Invalid Rows) - {{ filename }}">
|
<input type="hidden" name="import_source" value="Import (Skipped Invalid Rows) - {{ filename }}">
|
||||||
|
|
||||||
@@ -457,29 +460,57 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const form = document.getElementById('invalidReviewForm');
|
const form = document.getElementById('invalidReviewForm');
|
||||||
const proceedBtn = document.getElementById('proceedBtn');
|
const proceedBtn = document.getElementById('proceedBtn');
|
||||||
|
|
||||||
if (form && proceedBtn) {
|
console.log('🔍 Invalid Review Page - Debug Info:');
|
||||||
proceedBtn.addEventListener('click', function(e) {
|
console.log(' Form element:', form);
|
||||||
const validCount = {{ analysis.valid_rows }};
|
console.log(' Button element:', proceedBtn);
|
||||||
const invalidCount = {{ analysis.invalid_rows }};
|
console.log(' Form action:', form ? form.action : 'N/A');
|
||||||
|
console.log(' Form method:', form ? form.method : 'N/A');
|
||||||
|
|
||||||
const message = `You are about to import ${validCount} valid record(s).\n\n` +
|
if (form && proceedBtn) {
|
||||||
`${invalidCount} invalid row(s) will be skipped.\n\n` +
|
form.addEventListener('submit', function(e) {
|
||||||
`Continue with import?`;
|
e.preventDefault();
|
||||||
|
|
||||||
if (!confirm(message)) {
|
const validCount = {{ analysis.valid_rows }};
|
||||||
e.preventDefault();
|
const invalidCount = {{ analysis.invalid_rows }};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show loading state
|
console.log(`📊 Attempting to import ${validCount} valid records`);
|
||||||
proceedBtn.disabled = true;
|
console.log(`📊 Skipping ${invalidCount} invalid records`);
|
||||||
proceedBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing Import...';
|
|
||||||
});
|
const message = `You are about to import ${validCount} valid record(s).\n\n` +
|
||||||
}
|
`${invalidCount} invalid row(s) will be skipped.\n\n` +
|
||||||
});
|
`Continue with import?`;
|
||||||
|
|
||||||
|
if (confirm(message)) {
|
||||||
|
console.log('✅ User confirmed import');
|
||||||
|
|
||||||
|
// Log all form data for debugging
|
||||||
|
const formData = new FormData(form);
|
||||||
|
console.log('📋 Form Data Being Submitted:');
|
||||||
|
for (let [key, value] of formData.entries()) {
|
||||||
|
console.log(` ${key}: ${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
proceedBtn.disabled = true;
|
||||||
|
proceedBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing Import...';
|
||||||
|
|
||||||
|
// Use HTMLFormElement.submit() which bypasses event listeners
|
||||||
|
console.log('🚀 Submitting form to:', form.action);
|
||||||
|
HTMLFormElement.prototype.submit.call(form);
|
||||||
|
} else {
|
||||||
|
console.log('❌ User cancelled import');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('❌ Form or button not found!');
|
||||||
|
if (!form) console.error(' Missing form element with id="invalidReviewForm"');
|
||||||
|
if (!proceedBtn) console.error(' Missing button element with id="proceedBtn"');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user