CSV page and modularity
This commit is contained in:
parent
0da3885866
commit
ae6673f938
118
app.py
118
app.py
@ -2,120 +2,36 @@ from flask import Flask, request, render_template, redirect, abort, send_file
|
|||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from models import *
|
from models import *
|
||||||
import csv
|
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 = 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_DATABASE_URI'] = 'mysql://assetadmin:1234@localhost/asset_test_db'
|
||||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
db.init_app(app)
|
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():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return redirect('/viewall')
|
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/<string:assettag>/')
|
|
||||||
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/<string:assettag>/', 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/<string:assettag>/', 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__':
|
if __name__ == '__main__':
|
||||||
app.run(host='localhost', port=5000)
|
app.run(host='localhost', port=5000)
|
||||||
|
34
process_csv.py
Normal file
34
process_csv.py
Normal file
@ -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
|
31
routes/create.py
Normal file
31
routes/create.py
Normal file
@ -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')
|
25
routes/csvpreview.py
Normal file
25
routes/csvpreview.py
Normal file
@ -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)
|
16
routes/delete.py
Normal file
16
routes/delete.py
Normal file
@ -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/<string:assettag>/', 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')
|
38
routes/update.py
Normal file
38
routes/update.py
Normal file
@ -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/<string:assettag>/', 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)
|
37
routes/upload.py
Normal file
37
routes/upload.py
Normal file
@ -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')
|
19
routes/viewall.py
Normal file
19
routes/viewall.py
Normal file
@ -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)
|
11
routes/viewasset.py
Normal file
11
routes/viewasset.py
Normal file
@ -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/<string:assettag>/')
|
||||||
|
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"
|
49
templates/csv_preview.html
Normal file
49
templates/csv_preview.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Preview CSV Data</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Preview CSV Data</h1>
|
||||||
|
|
||||||
|
<!-- Display flash messages -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<p style="color: {% if category == 'error' %}red{% else %}green{% endif %};">{{ message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<!-- Display CSV data in a table -->
|
||||||
|
<table border="1">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Asset Tag</th>
|
||||||
|
<th>Hostname</th>
|
||||||
|
<th>Warranty From</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Staff Number</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in csv_data %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.assettag }}</td>
|
||||||
|
<td>{{ row.hostname }}</td>
|
||||||
|
<td>{{ row.warrantyfrom }}</td>
|
||||||
|
<td>{{ row.status }}</td>
|
||||||
|
<td>{{ row.staffnum }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Confirmation form -->
|
||||||
|
<form method="POST">
|
||||||
|
<button type="submit">Confirm and Save to Database</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
21
templates/upload.html
Normal file
21
templates/upload.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Upload CSV File</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Upload CSV File</h1>
|
||||||
|
<form action="/uploadcsv" method="POST" enctype="multipart/form-data">
|
||||||
|
<label for="file">Choose a CSV file:</label>
|
||||||
|
<input type="file" id="file" name="file" accept=".csv" required>
|
||||||
|
<br><br>
|
||||||
|
<button type="submit">Upload</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
<p style="color: red;">{{ error }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -56,6 +56,9 @@
|
|||||||
<form action="/create" method="get">
|
<form action="/create" method="get">
|
||||||
<button type="submit">Add new Item</button>
|
<button type="submit">Add new Item</button>
|
||||||
</form>
|
</form>
|
||||||
|
<form action="/uploadcsv" method="get">
|
||||||
|
<button type="submit">Import from CSV</button>
|
||||||
|
</form>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<button type="submit">Export Data</button>
|
<button type="submit">Export Data</button>
|
||||||
</form>
|
</form>
|
||||||
|
Loading…
Reference in New Issue
Block a user