From ce73f10a4aaa717de025fd2dc591294fea0db8de Mon Sep 17 00:00:00 2001 From: Marius Peter Date: Sun, 1 May 2022 21:38:13 +0200 Subject: Basic app. --- app/database.py | 0 app/forms.py | 101 +++++++++++++++++---- app/model.py | 168 +++++++++++++++++++++++++++++----- app/placeholders.py | 7 -- app/routes.py | 254 ++++++++++++++++++---------------------------------- 5 files changed, 317 insertions(+), 213 deletions(-) delete mode 100644 app/database.py delete mode 100644 app/placeholders.py (limited to 'app') diff --git a/app/database.py b/app/database.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/forms.py b/app/forms.py index daa394b..302db58 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,24 +1,93 @@ +""" +Form classes must be named 'Add{Item}', Item referring to the +model concerned. +""" + from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, SubmitField, BooleanField -from wtforms.validators import DataRequired, Length, EqualTo +from wtforms import ( + SubmitField, + SelectField, + RadioField, + HiddenField, + StringField, + PasswordField, + IntegerField, + FloatField, + DateTimeField, +) +from wtforms.validators import InputRequired, Length, NumberRange + + +class AddCustomer(FlaskForm): + name = StringField("Customer name", validators=[InputRequired()]) + name_alternative = StringField("Alternative name") + # date_time_created = DateTimeField("Creation date") + code_customer = StringField("Customer code") + code_accounting = StringField("Accounting code") + address = StringField("Address") + postal_code = StringField("Postal Code") + city = StringField("City") + country = StringField("Country") + phone = StringField("Phone") + website = StringField("Website") + email = StringField("E-mail") + professional_id_1 = StringField("Professional ID 1") + professional_id_2 = StringField("Professional ID 2") + tax_id = StringField("Tax ID") + payment_terms = StringField("Payment Terms", default="Tu vas payer sale chien!") + submit = SubmitField("Add/Update Product") + + +class AddProduct(FlaskForm): + name = StringField("Product name", validators=[InputRequired()]) + code_accounting = StringField("Accounting code", default=0) + unit_weight = FloatField("Unit weight", default=0) + price_net = FloatField("Price (net)", default=0) + price_gross = FloatField("Price (gross)", default=0) + tax_rate = FloatField("Tax rate", default=0) + submit = SubmitField("Add/Update Product") -class RegistrationForm(FlaskForm): - alias = StringField("Alias", validators=[DataRequired(), Length(min=2, max=20)]) - password = PasswordField("Password", validators=[DataRequired()]) - password_confirm = PasswordField( - "Confirm Password", validators=[DataRequired(), EqualTo("password")] +class AddLog(FlaskForm): + target = SelectField( + "Type", + choices=[("False", "Log"), ("True", "Target")], + validators=[InputRequired()], ) - submit = SubmitField("Create Alias") + nno3 = FloatField("NNO3", default=0) + p = FloatField("P", default=0) + k = FloatField("K", default=0) + ca = FloatField("Ca", default=0) + mg = FloatField("Mg", default=0) + s = FloatField("S", default=0) + na = FloatField("Na", default=0) + cl = FloatField("Cl", default=0) + fe = FloatField("Fe", default=0) + zn = FloatField("Zn", default=0) + b = FloatField("B", default=0) + mn = FloatField("Mn", default=0) + cu = FloatField("Cu", default=0) + mo = FloatField("Mo", default=0) + si = FloatField("Si", default=0) + nnh4 = FloatField("NNH4", default=0) + submit = SubmitField("Add/Update Log") -class LoginForm(FlaskForm): - alias = StringField("Alias", validators=[DataRequired(), Length(min=2, max=20)]) - password = PasswordField("Password", validators=[DataRequired()]) - remember = BooleanField("Remember Alias") - submit = SubmitField("Login Alias") +class AddOrder(FlaskForm): # TODO + name = StringField("Product name", validators=[InputRequired()]) + code_accounting = StringField("Accounting code", default=0) + unit_weight = FloatField("Unit weight", default=0) + price_net = FloatField("Price (net)", default=0) + price_gross = FloatField("Price (gross)", default=0) + tax_rate = FloatField("Tax rate", default=0) + submit = SubmitField("Add/Update Product") -class NewMessage(FlaskForm): - recipient = StringField("Recipient", validators=[DataRequired()]) - message = StringField("message", validators=[DataRequired()]) +class AddInvoice(FlaskForm): + name = StringField("Product name", validators=[InputRequired()]) + code_accounting = StringField("Accounting code", default=0) + unit_weight = FloatField("Unit weight", default=0) + price_net = FloatField("Price (net)", default=0) + price_gross = FloatField("Price (gross)", default=0) + tax_rate = FloatField("Tax rate", default=0) + submit = SubmitField("Add/Update Product") diff --git a/app/model.py b/app/model.py index fa4e00a..1cf59d8 100644 --- a/app/model.py +++ b/app/model.py @@ -1,39 +1,161 @@ -import SQAlchemy +# import SQLAlchemy +from flask_sqlalchemy import SQLAlchemy +from datetime import datetime + +db = SQLAlchemy() -class Users(db.Model): - id = db.Column(db.Integer, primary_key=True) - name_first = db.Column(db.String(20), nullable=False) - name_last = db.Column(db.String(20), nullable=False) + +class Module(db.Model): + name = db.Column( + "Name", db.String(20), unique=True, nullable=False, primary_key=True + ) + description = db.Column("Description", db.String(50), nullable=False) def __repr__(self): - return f"" + return f"" -class Projects(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(20), nullable=False) - name_full = db.Column(db.String(20), nullable=False) - nickname = db.Column(db.String(20), nullable=False) - city = db.Column(db.String(20), nullable=False) +class Customer(db.Model): + primary_key = db.Column("CustomerId", db.Integer, nullable=False, primary_key=True) + name = db.Column("Name", db.String(20), nullable=False) + name_alternative = db.Column("NameAlternative", db.String(20)) + date_time_created = db.Column("DateTimeCreated", db.String(20), nullable=False) + date_time_updated = db.Column("DateTimeUpdated", db.String(20), nullable=False) + code_customer = db.Column("CodeCustomer", db.String(20), unique=True) + code_accounting = db.Column("CodeAccounting", db.String(20), unique=True) + address = db.Column("Address", db.String(20)) + postal_code = db.Column("PostalCode", db.Integer) + city = db.Column("City", db.String(20)) + country = db.Column("Country", db.String(20)) + phone = db.Column("Phone", db.String(20)) + website = db.Column("Website", db.String(20)) + email = db.Column("Email", db.String(20)) + professional_id_1 = db.Column("ProfessionalId1", db.String(20), unique=True) + professional_id_2 = db.Column("ProfessionalId2", db.String(20), unique=True) + tax_id = db.Column("TaxId", db.String(20), unique=True) + payment_terms = db.Column("PaymentTerms", db.String(20), unique=True) + + def __repr__(self): + return f"" + + +class Product(db.Model): + primary_key = db.Column("ProductId", db.Integer, primary_key=True) + name = db.Column("Name", db.String(20), nullable=False, unique=True) + code_accounting = db.Column("CodeAccounting", db.String(20)) + unit_weight = db.Column("UnitWeight", db.Float) + price_net = db.Column("PriceNet", db.Float) + price_gross = db.Column("PriceGross", db.Float) + tax_rate = db.Column("TaxRate", db.Float) + date_time_created = db.Column( + "DateTimeCreated", + db.String, + default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) + date_time_updated = db.Column( + "DateTimeUpdated", + db.String, + default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) + + def __init__( + self, + name, + code_accounting, + unit_weight, + price_net, + price_gross, + tax_rate, + ): + self.name = name + self.code_accounting = code_accounting + self.unit_weight = unit_weight + self.price_net = price_net + self.price_gross = price_gross + self.tax_rate = tax_rate def __repr__(self): - return f"" + return f"" -class Modules(db.Model): +class Log(db.Model): + primary_key = db.Column("LogId", db.Integer, primary_key=True) + target = db.Column("Target", db.String, default="False") + nno3 = db.Column("NNO3", db.Float, default=0) + p = db.Column("P", db.Float, default=0) + k = db.Column("K", db.Float, default=0) + ca = db.Column("Ca", db.Float, default=0) + mg = db.Column("Mg", db.Float, default=0) + s = db.Column("S", db.Float, default=0) + na = db.Column("Na", db.Float, default=0) + cl = db.Column("Cl", db.Float, default=0) + fe = db.Column("Fe", db.Float, default=0) + zn = db.Column("Zn", db.Float, default=0) + b = db.Column("B", db.Float, default=0) + mn = db.Column("Mn", db.Float, default=0) + cu = db.Column("Cu", db.Float, default=0) + mo = db.Column("Mo", db.Float, default=0) + si = db.Column("Si", db.Float, default=0) + nnh4 = db.Column("NNH4", db.Float, default=0) + date_time_created = db.Column( + "DateTimeCreated", + db.String, + default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) + date_time_updated = db.Column( + "DateTimeUpdated", + db.String, + default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) + + def __init__( + self, target, nno3, p, k, ca, mg, s, na, cl, fe, zn, b, mn, cu, mo, si, nnh4 + ): + self.target = target + self.nno3 = nno3 + self.p = p + self.k = k + self.ca = ca + self.mg = mg + self.s = s + self.na = na + self.cl = cl + self.fe = fe + self.zn = zn + self.b = b + self.mn = mn + self.cu = cu + self.mo = mo + self.si = si + self.nnh4 = nnh4 + self.date_time_created = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + def __repr__(self): + return f"" + + +# Everything after here is garbage + + +class User(db.Model): id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(20), unique=True, nullable=False) - description = db.Column(db.String(50), nullable=False) + name_first = db.Column(db.String(20), nullable=False) + name_last = db.Column(db.String(20), nullable=False) + email = db.Column(db.String(20), nullable=False) + phone_mobile = db.Column(db.Integer, nullable=False) + phone_alternative = db.Column(db.Integer) + updated = db.Column(db.String) def __repr__(self): - return f"" + return f"" -class Doobie: - def __init__(self, name, prices, quantity): - self.name = name - self.prices = prices - self.quantity = quantity +class Order(db.Model): # TODO + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.String(20), nullable=False) + customer = db.Column(db.String(20), nullable=False) + content = db.Column(db.String(10), nullable=False) + updated = db.Column(db.String) def __repr__(self): - return self.name + return f"" diff --git a/app/placeholders.py b/app/placeholders.py deleted file mode 100644 index a2360b4..0000000 --- a/app/placeholders.py +++ /dev/null @@ -1,7 +0,0 @@ -modules = [ - "catalog", - "creator", - "logger", - "calculator", - "stock", -] diff --git a/app/routes.py b/app/routes.py index d2bd3f0..11cd3c2 100644 --- a/app/routes.py +++ b/app/routes.py @@ -19,161 +19,26 @@ placeholder data for posts' content. from flask import Flask, render_template, request, redirect, flash, url_for, jsonify -from flask_sqlalchemy import SQLAlchemy from flask_bootstrap import Bootstrap -from flask_wtf import FlaskForm -from wtforms import ( - SubmitField, - SelectField, - RadioField, - HiddenField, - StringField, - IntegerField, - FloatField, -) -from wtforms.validators import InputRequired, Length, Regexp, NumberRange from datetime import datetime +import inspect -import placeholders as p +from model import * +from forms import * app = Flask(__name__) -# Flask-Bootstrap requires this line -Bootstrap(app) - - -# Flask-WTF encryption key -app.config["SECRET_KEY"] = "Scooby_Lu,_where_are_you?" - -# Our database name -db_name = "fapg.db" -app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + db_name -app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True -db = SQLAlchemy(app) - - -class Users(db.Model): - id = db.Column(db.Integer, primary_key=True) - name_first = db.Column(db.String(20), nullable=False) - name_last = db.Column(db.String(20), nullable=False) - email = db.Column(db.String(20), nullable=False) - phone_mobile = db.Column(db.Integer, nullable=False) - phone_alternative = db.Column(db.Integer) - updated = db.Column(db.String) - - def __init__( - self, name_first, name_last, email, phone_mobile, phone_alternative, updated - ): - self.name_first = name_first - self.name_last = name_last - self.email = email - self.phone_mobile = phone_mobile - self.phone_alternative = phone_alternative - self.updated = updated - - def __repr__(self): - return f"" - - -class Products(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(20), nullable=False) - supplier = db.Column(db.String(20), nullable=False) - price = db.Column(db.Float(10), nullable=False) - updated = db.Column(db.String) - - def __init__(self, name, supplier, price, updated): - self.name = name - self.supplier = supplier - self.price = price - self.updated = updated - - def __repr__(self): - return f"" - - -class Projects(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(20), nullable=False) - name_full = db.Column(db.String(20), nullable=False) - nickname = db.Column(db.String(20), nullable=False) - city = db.Column(db.String(20), nullable=False) - - def __repr__(self): - return f"" - - -class Modules(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(20), unique=True, nullable=False) - description = db.Column(db.String(50), nullable=False) - - def __init__(self, name, description, updated): - self.name = name - self.description = description - self.updated = updated - - def __repr__(self): - return f"" - - -class AddProduct(FlaskForm): - # id used only by update/edit - id = HiddenField() - name = StringField("Product name", validators=[InputRequired()]) - supplier = SelectField( - "Choose a supplier", - choices=[ - ("", ""), - ("Mister Brown", "Mister Brown"), - ("Madame Cerise", "Madame Cerise"), - ("Biton la Malice", "G. Biton la Malice"), - ("Leroy Merlin", "Leroy Merlin"), - ("other", "Other"), - ], - ) - price = FloatField("Retail price per unit") - # updated - date - handled in the route function - updated = HiddenField() - submit = SubmitField("Add/Update Product") - - -# add a new product to the database -@app.route("/add_product", methods=["GET", "POST"]) -def add_product(): - form = AddProduct() - if form.validate_on_submit(): - name = request.form["name"] - supplier = request.form["supplier"] - price = request.form["price"] - # get today's date from function, above all the routes - updated = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # the data to be inserted into FAPG model - the table, products - record = Products(name, supplier, price, updated) - # Flask-SQLAlchemy magic adds record to database - db.session.add(record) - db.session.commit() - # create a message to send to the template - message = f"The data for product {name} has been submitted." - return render_template("add_product.html", message=message) - else: - # show validaton errors - # see https://pythonprogramming.net/flash-flask-tutorial/ - for field, errors in form.errors.items(): - for error in errors: - flash( - "Error in {}: {}".format(getattr(form, field).label.text, error), - "error", - ) - return render_template("add_product.html", form=form) + +app.config.from_pyfile("../config.py") +db.init_app(app) @app.route("/") @app.route("/fapg/home") def project(): """This is our project welcome page.""" - michel = Users( + michel = User( name_first="Michel", name_last="Peter", email="le-boss@fapg.com", @@ -181,35 +46,90 @@ def project(): phone_alternative="0000000000", updated="2022-04-21", ) - modules = Modules.query.all() + modules = Module.query.all() print(module.name for module in modules) - return render_template("home.html", user=michel, project=fapg, modules=modules) + return render_template("home.html", user=michel, project="fapg", modules=modules) -@app.route("/module/") +@app.route("/modules") +def all_modules(): + return redirect("/") + + +@app.route("/modules/") def render_module(module): - modules = Modules.query.all() - catalog = Products.query.all() - user_modules = [module.name for module in modules] - print(user_modules) - # If a module was purchased by a user and added to their database, - # they have access to the corresponding module route. - if module in user_modules: - return render_template( - f"modules/{module}.html", - modules=modules, - catalog=catalog, - ) - else: + modules = Module.query.all() + if module not in [mod.name for mod in modules]: return render_template("errors/module-not-found.html", module=module) + customers = Customer.query.order_by(Customer.primary_key.desc()) + products = Product.query.order_by(Product.primary_key.desc()) + logs = Log.query.order_by(Log.primary_key.desc()) + latest_target = ( + Log.query.filter_by(target="True").order_by(Log.primary_key.desc()).first() + ) + flash(f"Successfully accessed module {module}.", "info") + flash(f"Still fighting against styling in {module}.", "error") + return render_template( + f"modules/{module}.html", + module=module, + modules=modules, + customers=customers, + products=products, + logs=logs, + target=latest_target, + ) + + +@app.route("/delete--from-", methods=["POST"]) +def delete_item(pk, table): + """Delete item with Primary Key = pk from corresponding database + table. + """ + model = globals()[table] + record = model.query.filter_by(primary_key=pk).first() + db.session.delete(record) + db.session.commit() + flash(f"Successfully removed item #{pk} from {table} table.", "info") + return redirect("/modules") + + +@app.route("/add-", methods=["GET", "POST"]) +def add_item(item): + """Add a new item to a corresponding database table. + + The item must match a database model class name (table). Then, we + match the model class attributes with request.form values. + + """ + if item not in db.metadata.tables.keys(): + return render_template("errors/item-not-found.html", item=item) + if request.method == "GET": + form = globals()[f"Add{item.capitalize()}"]() + return render_template("add-item.html", item=item, form=form) + if request.method == "POST": + table = globals()[item.capitalize()] + table_fields = inspect.signature(table).parameters + form_values = [request.form[key] for key in table_fields] + debug = f"Ready to insert {form_values}" + record = table(*form_values) + db.session.add(record) + db.session.commit() + item_pk = table.query.order_by(table.primary_key.desc()).first().primary_key + flash( + f"Successfully added item #{item_pk} to {table.__table__.name.capitalize()} table.", + "info", + ) + return redirect(f"/modules") + + +@app.route("/add-invoice", methods=["GET", "POST"]) +def add_invoice(): + form = AddInvoice() + if request.method == "GET": + return render_template("add-invoice.html") -# If this file is executed as a script (i.e. double-clicked), -# the Python interpreter will run the Flask process and begin serving -# the web pages on the standard localhost address (127.0.0.1). -# But if this file is called as a module by another Python script, it will not -# serve content to the web pages, but the function definitions contained in -# this file will be available to the calling script. -# E.g. calling script will know what the yes() function is. -if __name__ == "__main__": - app.run(debug=True) +@app.route("/preview-invoice", methods=["GET", "POST"]) +def preview_invoice(): + if request.method == "GET": + return render_template("preview-invoice.html") -- cgit v1.2.3