diff --git a/routes/confirm_save.py b/routes/confirm_save.py index f72cb2d..564cd67 100644 --- a/routes/confirm_save.py +++ b/routes/confirm_save.py @@ -11,14 +11,14 @@ def confirm_save(): # Check if assets data is present in the request if 'assets' not in request.form: flash("No assets data found in the request.", "error") - return redirect(url_for('uploadcsv.upload_file')) + return redirect(url_for('uploadcsv.import_from_csv')) try: # Parse the JSON data from the form edited_assets = json.loads(request.form['assets']) except json.JSONDecodeError: flash("Invalid JSON data in the request.", "error") - return redirect(url_for('uploadcsv.upload_file')) + return redirect(url_for('uploadcsv.import_from_csv')) # Validate each asset errors = [] @@ -31,23 +31,36 @@ def confirm_save(): # If there are validation errors, flash them and redirect back to the preview page for error in errors: flash(error, "error") - return redirect(url_for('uploadcsv.upload_file')) + return redirect(url_for('uploadcsv.import_from_csv')) # Save validated assets to the database for asset_data in edited_assets: - asset = Asset(**{ - attrib.attrib_name: asset_data[attrib.attrib_name] - for attrib in item_attributes - }) - db.session.add(asset) + primary_attrib = next((attrib for attrib in item_attributes if attrib.primary), None) + if not primary_attrib: + flash("Primary attribute not defined in configuration.", "error") + return redirect(url_for('uploadcsv.import_from_csv')) + + primary_value = asset_data[primary_attrib.attrib_name] + asset = Asset.query.filter_by(**{primary_attrib.attrib_name: primary_value}).first() + + if asset: + # Update existing asset + for attrib in item_attributes: + setattr(asset, attrib.attrib_name, asset_data[attrib.attrib_name]) + else: + # Add new asset + asset = Asset(**asset_data) + db.session.add(asset) try: db.session.commit() except Exception as e: db.session.rollback() flash(f"Error saving data to the database: {str(e)}", "error") - return redirect(url_for('uploadcsv.upload_file')) + return redirect(url_for('uploadcsv.import_from_csv')) # Clear session data after successful insertion - session.pop('assets', None) + session.pop('new_entries', None) + session.pop('existing_entries', None) + session.pop('invalid_entries', None) return redirect('/viewall') \ No newline at end of file diff --git a/routes/upload.py b/routes/upload.py index 222eede..e919a82 100644 --- a/routes/upload.py +++ b/routes/upload.py @@ -6,14 +6,56 @@ from config import item_attributes upload_bp = Blueprint('uploadcsv', __name__) -@upload_bp.route('/uploadcsv', methods=['GET', 'POST']) -def upload_file(): - # Check if primary attribute is defined +def _process_csv_file(file, mode): + """ + Helper function to process CSV file and separate entries based on mode. + mode: 'import' or 'edit' + """ + # Extract CSV data + csvdata = get_csv_data(file) + + # Validate each row of data + errors = [] + for i, row in enumerate(csvdata, start=1): + error = validate_values(row) + if error: + errors.append(f"Row {i}: {error}") + + if errors: + return None, errors + + # Separate entries based on mode primary_attrib = next((attrib for attrib in item_attributes if attrib.primary), None) if not primary_attrib: - flash("Primary attribute not defined in configuration.", "error") - return redirect(request.url) + return None, ["Primary attribute not defined in configuration."] + new_entries = [] + existing_entries = [] + invalid_entries = [] + + for row in csvdata: + primary_value = row[primary_attrib.attrib_name] + asset_exists = Asset.query.filter_by(**{primary_attrib.attrib_name: primary_value}).first() + + if mode == 'import': + if asset_exists: + invalid_entries.append(row) # Existing entries are invalid for import + else: + new_entries.append(row) + elif mode == 'edit': + if asset_exists: + existing_entries.append(row) # Existing entries are valid for edit + else: + invalid_entries.append(row) # Non-existing entries are invalid for edit + + return { + 'new_entries': new_entries, + 'existing_entries': existing_entries, + 'invalid_entries': invalid_entries + }, None + +@upload_bp.route('/import_from_csv', methods=['GET', 'POST']) +def import_from_csv(): if request.method == 'POST': # Check if a file was uploaded if 'file' not in request.files: @@ -27,48 +69,61 @@ def upload_file(): flash("Please upload a valid CSV file.", "error") return redirect(request.url) - try: - # Extract CSV data - csvdata = get_csv_data(file) - except Exception as e: - flash(f"Error reading CSV file: {str(e)}", "error") - return redirect(request.url) - - # Validate each row of data - errors = [] - for i, row in enumerate(csvdata, start=1): - error = validate_values(row) - if error: - errors.append(f"Row {i}: {error}") - + # Process the CSV file + result, errors = _process_csv_file(file, mode='import') if errors: - # Flash all errors and redirect back to the upload page for error in errors: flash(error, "error") return redirect(request.url) - # Separate new and existing assets - new_assets = [] - existing_assets = [] - for row in csvdata: - primary_value = row[primary_attrib.attrib_name] - asset_exists = Asset.query.filter_by(**{primary_attrib.attrib_name: primary_value}).first() - if asset_exists: - existing_assets.append(row) - else: - new_assets.append(row) + # Store entries in session for further processing + session['new_entries'] = result['new_entries'] + session['invalid_entries'] = result['invalid_entries'] - # Store new assets in session for further processing - session['new_assets'] = new_assets - session['existing_assets'] = existing_assets - - # Redirect to preview page with the CSV data + # Redirect to preview page return render_template( 'csv_preview.html', - new_assets=new_assets, - existing_assets=existing_assets, + new_entries=result['new_entries'], + invalid_entries=result['invalid_entries'], item_attributes=item_attributes ) # Render the upload page for GET requests - return render_template('upload.html') \ No newline at end of file + return render_template('upload.html', mode="addnew") + +@upload_bp.route('/edit_using_csv', methods=['GET', 'POST']) +def edit_using_csv(): + if request.method == 'POST': + # Check if a file was uploaded + if 'file' not in request.files: + flash("No file uploaded.", "error") + return redirect(request.url) + + file = request.files['file'] + + # Check if the file is a CSV + if file.filename == '' or not file.filename.endswith('.csv'): + flash("Please upload a valid CSV file.", "error") + return redirect(request.url) + + # Process the CSV file + result, errors = _process_csv_file(file, mode='edit') + if errors: + for error in errors: + flash(error, "error") + return redirect(request.url) + + # Store entries in session for further processing + session['existing_entries'] = result['existing_entries'] + session['invalid_entries'] = result['invalid_entries'] + + # Redirect to preview page + return render_template( + 'csv_preview.html', + existing_entries=result['existing_entries'], + invalid_entries=result['invalid_entries'], + item_attributes=item_attributes + ) + + # Render the upload page for GET requests + return render_template('upload.html', mode="update") \ No newline at end of file diff --git a/static/edited_csv.js b/static/edited_csv.js index cf26c0a..a0327b8 100644 --- a/static/edited_csv.js +++ b/static/edited_csv.js @@ -3,8 +3,8 @@ function collectEditedData(event) { event.preventDefault(); // Extract headers (attribute names) from the table - const headers = [...document.querySelectorAll('.table-new-assets thead th')].map(th => th.dataset.attrib); - const rows = document.querySelectorAll('.table-new-assets tbody tr'); + const headers = [...document.querySelectorAll('.table-valid-entries thead th')].map(th => th.dataset.attrib); + const rows = document.querySelectorAll('.table-valid-entries tbody tr'); const assets = []; // Iterate through rows and collect data diff --git a/templates/csv_preview.html b/templates/csv_preview.html index e6abc22..a5e64f6 100644 --- a/templates/csv_preview.html +++ b/templates/csv_preview.html @@ -23,27 +23,28 @@ {% endif %} {% endwith %} - - {% for table_name, assets, editable, table_class in [ - ('New Assets', new_assets, true, 'table-new-assets'), - ('Existing Assets', existing_assets, false, 'table-existing-assets') + + {% for table_name, entries, editable, tableclass in [ + ('New Entries', new_entries, true, 'table-valid-entries'), + ('Existing Entries', existing_entries, true, 'table-valid-entries'), + ('Invalid Entries', invalid_entries, false, 'table-invalid-entries') ] %} - {% if assets %} + {% if entries %}
{{ attrib.display_name }} | +{{ attrib.display_name }} | {% endfor %}
---|---|
- {{ asset[attrib.attrib_name] }} + {{ entry[attrib.attrib_name] }} | {% endfor %}