diff --git a/app.py b/app.py index ce3f4e4..359dd29 100644 --- a/app.py +++ b/app.py @@ -2,120 +2,36 @@ from flask import Flask, request, render_template, redirect, abort, send_file from sqlalchemy import exc from models import * import csv +from process_csv import process_csv # Import the CSV processing function + +# Import the Blueprints +from routes.viewall import viewall_bp +from routes.viewasset import viewasset_bp +from routes.create import addasset_bp +from routes.update import update_bp +from routes.delete import delete_bp +from routes.upload import upload_bp app = Flask(__name__) +app.secret_key = 'your_secret_key' # Required for flashing messages and session app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://assetadmin:1234@localhost/asset_test_db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) +# Register the Blueprints +app.register_blueprint(viewall_bp) +app.register_blueprint(viewasset_bp) +app.register_blueprint(addasset_bp) +app.register_blueprint(update_bp) +app.register_blueprint(delete_bp) +app.register_blueprint(upload_bp) with app.app_context(): db.create_all() - @app.route('/') def index(): return redirect('/viewall') - -@app.route('/create/', methods=['GET', 'POST']) -def create(): - if request.method == 'GET': - return render_template('create.html') - - if request.method == 'POST': - assettag = request.form['assettag'] - hostname = request.form['hostname'] - warrantyfrom = request.form['warrantyfrom'] - status = request.form['status'] - try: - staffnum = int(request.form['staffnum']) - except ValueError: - return render_template('create.html', exc='staffnum') - - item = Asset(assettag=assettag, hostname=hostname, warrantyfrom=warrantyfrom, status=status, staffnum=staffnum) - - try: - db.session.add(item) - db.session.commit() - except exc.IntegrityError: - return render_template('create.html', exc='integrity') - except exc.StatementError: - return render_template('create.html', exc='status') - - return redirect('/viewall') - - -@app.route('/viewall/', methods=['GET', 'POST']) -def view_list(): - if request.method == 'POST': - outfile = open('inventory_export.csv', 'w', newline='') - outcsv = csv.writer(outfile, delimiter='|') - records = db.session.query(Asset).all() - outcsv.writerow([column.name for column in Asset.__mapper__.columns]) - [outcsv.writerow([getattr(curr, column.name) for column in Asset.__mapper__.columns]) for curr in records] - outfile.close() - return send_file('inventory_export.csv', as_attachment=True) - - items = Asset.query.all() - return render_template('viewList.html', items=items) - - -@app.route('/view//') -def view_item(assettag): - item = Asset.query.filter_by(assettag=assettag).first() - if item: - return render_template('viewitem.html', item=item) - return f"Asset {assettag} is not found" - - -@app.route('/update//', methods=['GET', 'POST']) -def update(assettag): - item = Asset.query.filter_by(assettag=assettag).first() - if request.method == 'POST': - if item: - assettag = request.form['assettag'] - hostname = request.form['hostname'] - warrantyfrom = request.form['warrantyfrom'] - status = request.form['status'] - if status not in ['Active', 'Inactive']: - return render_template('update.html', item=item, exc='status') # Ensure status is valid - try: - staffnum = int(request.form['staffnum']) - except ValueError: - return render_template('update.html', item=item, exc='staffnum') - - try: - setattr(item, 'assettag', assettag) - setattr(item, 'hostname', hostname) - setattr(item, 'warrantyfrom', warrantyfrom) - setattr(item, 'status', status) - setattr(item, 'staffnum', staffnum) - - db.session.commit() - except exc.IntegrityError: - return render_template('update.html', item=item, exc='integrity') - except (exc.StatementError, exc.InvalidRequestError) as e: - return render_template('update.html', item=item, exc='status') - - return redirect(f'/viewall/') - return f"Asset {assettag} is not found" - - return render_template('update.html', item=item) - - -@app.route('/delete//', methods=['GET', 'POST']) -def delete(assettag): - item = Asset.query.filter_by(assettag=assettag).first() - if request.method == 'POST': - if item: - db.session.delete(item) - db.session.commit() - return redirect('/viewall') - abort(404) - - return render_template('delete.html') - - if __name__ == '__main__': app.run(host='localhost', port=5000) diff --git a/process_csv.py b/process_csv.py new file mode 100644 index 0000000..27b524e --- /dev/null +++ b/process_csv.py @@ -0,0 +1,34 @@ +import csv +from io import TextIOWrapper +from models import db, Asset # Import your database and model + +def process_csv(file): + """ + Processes the uploaded CSV file and returns a list of Asset objects. + Raises an exception if the file is invalid or data is incorrect. + """ + csv_file = TextIOWrapper(file, encoding='utf-8') + reader = csv.DictReader(csv_file) + + # Validate CSV headers + required_headers = ['assettag', 'hostname', 'warrantyfrom', 'status', 'staffnum'] + if not all(header in reader.fieldnames for header in required_headers): + raise ValueError("CSV file must include headers: assettag, hostname, warrantyfrom, status, staffnum.") + + # Process each row + assets = [] + for row in reader: + # Validate required fields + if not row['assettag']: + raise ValueError("Missing 'assettag' in one or more rows.") + + # Create Asset object + assets.append(Asset( + assettag=row['assettag'], + hostname=row['hostname'], + warrantyfrom=row['warrantyfrom'], + status=row['status'], + staffnum=row['staffnum'] + )) + + return assets diff --git a/routes/create.py b/routes/create.py new file mode 100644 index 0000000..d8a004b --- /dev/null +++ b/routes/create.py @@ -0,0 +1,31 @@ +from flask import Blueprint, request, render_template, redirect +from models import Asset, db + +addasset_bp = Blueprint('addasset', __name__) + +@addasset_bp.route('/create/', methods=['GET', 'POST']) +def create(): + if request.method == 'GET': + return render_template('create.html') + + if request.method == 'POST': + assettag = request.form['assettag'] + hostname = request.form['hostname'] + warrantyfrom = request.form['warrantyfrom'] + status = request.form['status'] + try: + staffnum = int(request.form['staffnum']) + except ValueError: + return render_template('create.html', exc='staffnum') + + item = Asset(assettag=assettag, hostname=hostname, warrantyfrom=warrantyfrom, status=status, staffnum=staffnum) + + try: + db.session.add(item) + db.session.commit() + except exc.IntegrityError: + return render_template('create.html', exc='integrity') + except exc.StatementError: + return render_template('create.html', exc='status') + + return redirect('/viewall') \ No newline at end of file diff --git a/routes/csvpreview.py b/routes/csvpreview.py new file mode 100644 index 0000000..94e3804 --- /dev/null +++ b/routes/csvpreview.py @@ -0,0 +1,25 @@ +from flask import Blueprint, request, render_template +from models import Asset, db + +viewcsv_bp = Blueprint('viewcsv', __name__) + +@viewcsv_bp.route('/csv_preview', methods=['GET', 'POST']) +def csv_preview(): + # Retrieve the CSV data from the session + csv_data = session.get('csv_data', []) + + if request.method == 'POST': + # Save the data to the database + assets = [AssetTest(**row) for row in csv_data] + db.session.add_all(assets) + db.session.commit() + + # Clear the session data + session.pop('csv_data', None) + + # Redirect to view list page with success message + flash("Data saved to the database successfully!", 'success') + return redirect(url_for('view_list')) + + # Render the preview page for GET requests + return render_template('csv_preview.html', csv_data=csv_data) \ No newline at end of file diff --git a/routes/delete.py b/routes/delete.py new file mode 100644 index 0000000..427d940 --- /dev/null +++ b/routes/delete.py @@ -0,0 +1,16 @@ +from flask import Blueprint, request, render_template, redirect +from models import Asset, db + +delete_bp = Blueprint('deleteasset', __name__) + +@delete_bp.route('/delete//', methods=['GET', 'POST']) +def delete(assettag): + item = Asset.query.filter_by(assettag=assettag).first() + if request.method == 'POST': + if item: + db.session.delete(item) + db.session.commit() + return redirect('/viewall') + abort(404) + + return render_template('delete.html') \ No newline at end of file diff --git a/routes/update.py b/routes/update.py new file mode 100644 index 0000000..99b47dd --- /dev/null +++ b/routes/update.py @@ -0,0 +1,38 @@ +from flask import Blueprint, request, render_template, redirect +from models import Asset, db + +update_bp = Blueprint('editasset', __name__) + +@update_bp.route('/update//', methods=['GET', 'POST']) +def update(assettag): + item = Asset.query.filter_by(assettag=assettag).first() + if request.method == 'POST': + if item: + assettag = request.form['assettag'] + hostname = request.form['hostname'] + warrantyfrom = request.form['warrantyfrom'] + status = request.form['status'] + if status not in ['Active', 'Inactive']: + return render_template('update.html', item=item, exc='status') # Ensure status is valid + try: + staffnum = int(request.form['staffnum']) + except ValueError: + return render_template('update.html', item=item, exc='staffnum') + + try: + setattr(item, 'assettag', assettag) + setattr(item, 'hostname', hostname) + setattr(item, 'warrantyfrom', warrantyfrom) + setattr(item, 'status', status) + setattr(item, 'staffnum', staffnum) + + db.session.commit() + except exc.IntegrityError: + return render_template('update.html', item=item, exc='integrity') + except (exc.StatementError, exc.InvalidRequestError) as e: + return render_template('update.html', item=item, exc='status') + + return redirect(f'/viewall/') + return f"Asset {assettag} is not found" + + return render_template('update.html', item=item) \ No newline at end of file diff --git a/routes/upload.py b/routes/upload.py new file mode 100644 index 0000000..e8d3df3 --- /dev/null +++ b/routes/upload.py @@ -0,0 +1,37 @@ +from flask import Blueprint, request, render_template, redirect +from models import Asset, db + +upload_bp = Blueprint('uploadcsv', __name__) + +@upload_bp.route('/uploadcsv', methods=['GET', 'POST']) +def upload_file(): + if request.method == 'POST': + # Check if a file was uploaded + if 'file' not in request.files: + flash("No file uploaded.", 'error') + return redirect(url_for('upload_file')) + + 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(url_for('upload_file')) + + try: + # Process the CSV file + assets = process_csv(file) + + # Store the processed data in the session for preview + session['csv_data'] = [asset.__dict__ for asset in assets] + + # Redirect to preview page + return redirect(url_for('csv_preview')) + + except Exception as e: + # Handle errors during file processing + flash(f"Error processing CSV file: {str(e)}", 'error') + return redirect(url_for('upload_file')) + + # Render the upload page for GET requests + return render_template('upload.html') \ No newline at end of file diff --git a/routes/viewall.py b/routes/viewall.py new file mode 100644 index 0000000..f94e0fd --- /dev/null +++ b/routes/viewall.py @@ -0,0 +1,19 @@ +from flask import Blueprint, request, render_template, send_file +from models import Asset, db +import csv + +viewall_bp = Blueprint('viewall', __name__) + +@viewall_bp.route('/viewall/', methods=['GET', 'POST']) +def view_list(): + if request.method == 'POST': + outfile = open('inventory_export.csv', 'w', newline='') + outcsv = csv.writer(outfile, delimiter='|') + records = db.session.query(Asset).all() + outcsv.writerow([column.name for column in Asset.__mapper__.columns]) + [outcsv.writerow([getattr(curr, column.name) for column in Asset.__mapper__.columns]) for curr in records] + outfile.close() + return send_file('inventory_export.csv', as_attachment=True) + + items = Asset.query.all() + return render_template('viewList.html', items=items) \ No newline at end of file diff --git a/routes/viewasset.py b/routes/viewasset.py new file mode 100644 index 0000000..46b2afb --- /dev/null +++ b/routes/viewasset.py @@ -0,0 +1,11 @@ +from flask import Blueprint, request, render_template +from models import Asset, db + +viewasset_bp = Blueprint('viewasset', __name__) + +@viewasset_bp.route('/view//') +def view_item(assettag): + item = Asset.query.filter_by(assettag=assettag).first() + if item: + return render_template('viewitem.html', item=item) + return f"Asset {assettag} is not found" \ No newline at end of file diff --git a/templates/csv_preview.html b/templates/csv_preview.html new file mode 100644 index 0000000..ce3a0d6 --- /dev/null +++ b/templates/csv_preview.html @@ -0,0 +1,49 @@ + + + + + + Preview CSV Data + + +

Preview CSV Data

+ + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +

{{ message }}

+ {% endfor %} + {% endif %} + {% endwith %} + + + + + + + + + + + + + + {% for row in csv_data %} + + + + + + + + {% endfor %} + +
Asset TagHostnameWarranty FromStatusStaff Number
{{ row.assettag }}{{ row.hostname }}{{ row.warrantyfrom }}{{ row.status }}{{ row.staffnum }}
+ + +
+ +
+ + \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..66887ca --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,21 @@ + + + + + + Upload CSV File + + +

Upload CSV File

+
+ + +

+ +
+ + {% if error %} +

{{ error }}

+ {% endif %} + + \ No newline at end of file diff --git a/templates/viewList.html b/templates/viewList.html index 6f11dc2..a45b266 100644 --- a/templates/viewList.html +++ b/templates/viewList.html @@ -56,6 +56,9 @@
+
+ +