diff --git a/README.md b/README.md index 8fa7219..617a39d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ -# flask_crud_app +# Flask CRUD application +Example app for inventory management, built using python flask. Forked from [this project](https://github.com/MovieTone/crud-flask-export-csv). -A CRUD application using python flask \ No newline at end of file +**Preview of original app:** +![image](https://github.com/MovieTone/crud-flask-export-csv/assets/15722914/442353d3-4064-4fc7-b796-5c40ac532338) diff --git a/main.py b/main.py new file mode 100644 index 0000000..68ab553 --- /dev/null +++ b/main.py @@ -0,0 +1,123 @@ +from flask import Flask, request, render_template, redirect, abort, send_file +from sqlalchemy import exc +from models import * +import csv + +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///inventory.db' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +db.init_app(app) + + +@app.before_first_request +def create_table(): + db.create_all() + + +@app.route('/') +def index(): + return redirect('/view') + + +@app.route('/create/', methods=['GET', 'POST']) +def create(): + if request.method == 'GET': + return render_template('create.html') + + if request.method == 'POST': + sku = request.form['sku'] + name = request.form['name'] + description = request.form['description'] + price = request.form['price'] + try: + qty = int(request.form['qty']) + except ValueError: + return render_template('create.html', exc='qty') + + item = Item(sku=sku, name=name, description=description, price=price, qty=qty) + + 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='price') + + return redirect('/view') + + +@app.route('/view/', methods=['GET', 'POST']) +def view_list(): + if request.method == 'POST': + outfile = open('inventory_export.csv', 'w', newline='') + outcsv = csv.writer(outfile) + records = db.session.query(Item).all() + outcsv.writerow([column.name for column in Item.__mapper__.columns]) + [outcsv.writerow([getattr(curr, column.name) for column in Item.__mapper__.columns]) for curr in records] + outfile.close() + return send_file('inventory_export.csv', as_attachment=True) + + items = Item.query.all() + return render_template('viewlist.html', items=items) + + +@app.route('/view//') +def view_item(sku): + item = Item.query.filter_by(sku=sku).first() + if item: + return render_template('viewitem.html', item=item) + return f"Item {sku} is not found" + + +@app.route('/update//', methods=['GET', 'POST']) +def update(sku): + item = Item.query.filter_by(sku=sku).first() + if request.method == 'POST': + if item: + sku = request.form['sku'] + name = request.form['name'] + description = request.form['description'] + try: + price = float(request.form['price']) + except ValueError: + return render_template('update.html', item=item, exc='price') + try: + qty = int(request.form['qty']) + except ValueError: + return render_template('update.html', item=item, exc='qty') + + try: + setattr(item, 'sku', sku) + setattr(item, 'name', name) + setattr(item, 'description', description) + setattr(item, 'price', price) + setattr(item, 'qty', qty) + + 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='price') + + return redirect(f'/view/') + return f"Item {sku} is not found" + + return render_template('update.html', item=item) + + +@app.route('/delete//', methods=['GET', 'POST']) +def delete(sku): + item = Item.query.filter_by(sku=sku).first() + if request.method == 'POST': + if item: + db.session.delete(item) + db.session.commit() + return redirect('/view') + abort(404) + + return render_template('delete.html') + + +if __name__ == '__main__': + app.run(host='localhost', port=5000) diff --git a/models.py b/models.py new file mode 100644 index 0000000..b6f7699 --- /dev/null +++ b/models.py @@ -0,0 +1,24 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + + +class Item(db.Model): + __tablename__ = "Item" + + id = db.Column(db.Integer(), primary_key=True) + sku = db.Column(db.String(), unique=True) + name = db.Column(db.String()) + description = db.Column(db.String()) + price = db.Column(db.DECIMAL(9, 2)) + qty = db.Column(db.Integer()) + + def __init__(self, sku, name, description, price, qty): + self.sku = sku + self.name = name + self.description = description + self.price = price + self.qty = qty + + def __repr__(self): + return f"{self.name}:{self.sku}" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dfa4c55 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +# This file is used by pip to install required python packages +# Usage: pip install -r requirements.txt + +# Flask Framework +click==7.1.2 +Flask==2.0.2 +itsdangerous==2.0.1 +Jinja2==3.0.0 +MarkupSafe==2.0.0rc2 +Werkzeug==2.0.0 + +# Flask Packages +Flask-Login==0.4.0 +Flask-Migrate==2.0.2 +Flask-Script==2.0.5 +Flask-SQLAlchemy==2.4.0 +Flask-WTF==0.14.2 +Flask-User==1.0.1.5 +SQLAlchemy==1.3.24 + +# Automated tests +pytest==3.0.5 +pytest-cov==2.4.0 \ No newline at end of file diff --git a/templates/create.html b/templates/create.html new file mode 100644 index 0000000..b27e1fa --- /dev/null +++ b/templates/create.html @@ -0,0 +1,58 @@ + + + + + Add an Item + + + +
+ Add | + View +
+ +

Add new Item

+ +
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+
+ +

+ {% if exc == 'integrity' %} + Item with such SKU already exists + {% endif %} + {% if exc == 'price' %} + Data input error. Price must have a numeric value + {% endif %} + {% if exc == 'qty' %} + Data input error. Quantity must be an integer + {% endif %} +

+ + + diff --git a/templates/delete.html b/templates/delete.html new file mode 100644 index 0000000..ecedf45 --- /dev/null +++ b/templates/delete.html @@ -0,0 +1,20 @@ + + + + + Delete Item + + +
+ Add | + View +
+ +
+ Do you want to delete the item? + + Cancel +
+ + + \ No newline at end of file diff --git a/templates/update.html b/templates/update.html new file mode 100644 index 0000000..7c71235 --- /dev/null +++ b/templates/update.html @@ -0,0 +1,59 @@ + + + + + Update Item + + + +
+ Add | + View +
+ +

Update Item

+ +
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ +

+
+ +

+ {% if exc == 'integrity' %} + Item with such SKU already exists + {% endif %} + {% if exc == 'price' %} + Data input error. Price must have a numeric value + {% endif %} + {% if exc == 'qty' %} + Data input error. Quantity must be an integer + {% endif %} +

+ + \ No newline at end of file diff --git a/templates/viewList.html b/templates/viewList.html new file mode 100644 index 0000000..da29226 --- /dev/null +++ b/templates/viewList.html @@ -0,0 +1,63 @@ + + + + + View Inventory + + +
+ Add | + View +
+ +

Item Inventory

+ + + + + + + + + + {% for item in items %} + + + + + + + + + + + {% endfor %} +
SKUNameDescriptionPriceQuantity
+ {{item.sku}} + + {{item.name}} + + {{item.description}} + + ${{item.price}} + + {{item.qty}} + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + \ No newline at end of file