From 9770cf8345d47cb086bd386c0d5c0fca165a6d11 Mon Sep 17 00:00:00 2001 From: Marius Peter Date: Fri, 22 Apr 2022 13:24:41 +0200 Subject: First commit! :fire: --- .gitignore | 4 + app/.gitignore | 4 + app/database.py | 0 app/forms.py | 24 +++ app/model.py | 39 ++++ app/placeholders.py | 7 + app/routes.py | 215 +++++++++++++++++++++++ app/static/fonts/PublicSans-Black.otf | Bin 0 -> 49640 bytes app/static/fonts/PublicSans-BlackItalic.otf | Bin 0 -> 51720 bytes app/static/fonts/PublicSans-Bold.otf | Bin 0 -> 56032 bytes app/static/fonts/PublicSans-BoldItalic.otf | Bin 0 -> 60100 bytes app/static/fonts/PublicSans-ExtraBold.otf | Bin 0 -> 56468 bytes app/static/fonts/PublicSans-ExtraBoldItalic.otf | Bin 0 -> 60340 bytes app/static/fonts/PublicSans-ExtraLight.otf | Bin 0 -> 56584 bytes app/static/fonts/PublicSans-ExtraLightItalic.otf | Bin 0 -> 60244 bytes app/static/fonts/PublicSans-Italic.otf | Bin 0 -> 60316 bytes app/static/fonts/PublicSans-Light.otf | Bin 0 -> 55120 bytes app/static/fonts/PublicSans-LightItalic.otf | Bin 0 -> 58588 bytes app/static/fonts/PublicSans-Medium.otf | Bin 0 -> 56180 bytes app/static/fonts/PublicSans-MediumItalic.otf | Bin 0 -> 59748 bytes app/static/fonts/PublicSans-Regular.otf | Bin 0 -> 56792 bytes app/static/fonts/PublicSans-SemiBold.otf | Bin 0 -> 56720 bytes app/static/fonts/PublicSans-SemiBoldItalic.otf | Bin 0 -> 60168 bytes app/static/fonts/PublicSans-Thin.otf | Bin 0 -> 50824 bytes app/static/fonts/PublicSans-ThinItalic.otf | Bin 0 -> 52780 bytes app/static/styles/style.css | 79 +++++++++ app/templates/add_product.html | 44 +++++ app/templates/base.html | 30 ++++ app/templates/errors/module-not-found.html | 14 ++ app/templates/home.html | 22 +++ app/templates/modules/calculator.html | 8 + app/templates/modules/catalog.html | 35 ++++ app/templates/modules/creator.html | 8 + app/templates/modules/logger.html | 8 + app/templates/modules/stock.html | 8 + 35 files changed, 549 insertions(+) create mode 100644 .gitignore create mode 100644 app/.gitignore create mode 100644 app/database.py create mode 100644 app/forms.py create mode 100644 app/model.py create mode 100644 app/placeholders.py create mode 100644 app/routes.py create mode 100644 app/static/fonts/PublicSans-Black.otf create mode 100644 app/static/fonts/PublicSans-BlackItalic.otf create mode 100644 app/static/fonts/PublicSans-Bold.otf create mode 100644 app/static/fonts/PublicSans-BoldItalic.otf create mode 100644 app/static/fonts/PublicSans-ExtraBold.otf create mode 100644 app/static/fonts/PublicSans-ExtraBoldItalic.otf create mode 100644 app/static/fonts/PublicSans-ExtraLight.otf create mode 100644 app/static/fonts/PublicSans-ExtraLightItalic.otf create mode 100644 app/static/fonts/PublicSans-Italic.otf create mode 100644 app/static/fonts/PublicSans-Light.otf create mode 100644 app/static/fonts/PublicSans-LightItalic.otf create mode 100644 app/static/fonts/PublicSans-Medium.otf create mode 100644 app/static/fonts/PublicSans-MediumItalic.otf create mode 100644 app/static/fonts/PublicSans-Regular.otf create mode 100644 app/static/fonts/PublicSans-SemiBold.otf create mode 100644 app/static/fonts/PublicSans-SemiBoldItalic.otf create mode 100644 app/static/fonts/PublicSans-Thin.otf create mode 100644 app/static/fonts/PublicSans-ThinItalic.otf create mode 100644 app/static/styles/style.css create mode 100644 app/templates/add_product.html create mode 100644 app/templates/base.html create mode 100644 app/templates/errors/module-not-found.html create mode 100644 app/templates/home.html create mode 100644 app/templates/modules/calculator.html create mode 100644 app/templates/modules/catalog.html create mode 100644 app/templates/modules/creator.html create mode 100644 app/templates/modules/logger.html create mode 100644 app/templates/modules/stock.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed08861 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# .gitignore + +.venv/ +*~undo-tree~ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..1ac0ed9 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,4 @@ +# .gitignore for app + +*__pycache__ +*.db \ No newline at end of file diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..e69de29 diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000..daa394b --- /dev/null +++ b/app/forms.py @@ -0,0 +1,24 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SubmitField, BooleanField +from wtforms.validators import DataRequired, Length, EqualTo + + +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")] + ) + submit = SubmitField("Create Alias") + + +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 NewMessage(FlaskForm): + recipient = StringField("Recipient", validators=[DataRequired()]) + message = StringField("message", validators=[DataRequired()]) diff --git a/app/model.py b/app/model.py new file mode 100644 index 0000000..fa4e00a --- /dev/null +++ b/app/model.py @@ -0,0 +1,39 @@ +import SQAlchemy + +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) + + 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 __repr__(self): + return f"" + + +class Doobie: + def __init__(self, name, prices, quantity): + self.name = name + self.prices = prices + self.quantity = quantity + + def __repr__(self): + return self.name diff --git a/app/placeholders.py b/app/placeholders.py new file mode 100644 index 0000000..a2360b4 --- /dev/null +++ b/app/placeholders.py @@ -0,0 +1,7 @@ +modules = [ + "catalog", + "creator", + "logger", + "calculator", + "stock", +] diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..d2bd3f0 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,215 @@ +""" +routes.py module +---------------- + +This Python module contains the logic supporting: +1. Navigating between website pages +2. Interpreting user requests to the server +3. Dispatching requested content back to the user + +Python dependencies: +- flask: provides web application features +- forms: provides secure user form submission +- sqlalchemy: provides communication with database on server. + +Personal imports: +These are used to avoid cluttering this file with +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 placeholders as p + +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.route("/") +@app.route("/fapg/home") +def project(): + """This is our project welcome page.""" + michel = Users( + name_first="Michel", + name_last="Peter", + email="le-boss@fapg.com", + phone_mobile="00000000", + phone_alternative="0000000000", + updated="2022-04-21", + ) + modules = Modules.query.all() + print(module.name for module in modules) + return render_template("home.html", user=michel, project=fapg, modules=modules) + + +@app.route("/module/") +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: + return render_template("errors/module-not-found.html", module=module) + + +# 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) diff --git a/app/static/fonts/PublicSans-Black.otf b/app/static/fonts/PublicSans-Black.otf new file mode 100644 index 0000000..bbbaa26 Binary files /dev/null and b/app/static/fonts/PublicSans-Black.otf differ diff --git a/app/static/fonts/PublicSans-BlackItalic.otf b/app/static/fonts/PublicSans-BlackItalic.otf new file mode 100644 index 0000000..46e3f71 Binary files /dev/null and b/app/static/fonts/PublicSans-BlackItalic.otf differ diff --git a/app/static/fonts/PublicSans-Bold.otf b/app/static/fonts/PublicSans-Bold.otf new file mode 100644 index 0000000..7a2b62b Binary files /dev/null and b/app/static/fonts/PublicSans-Bold.otf differ diff --git a/app/static/fonts/PublicSans-BoldItalic.otf b/app/static/fonts/PublicSans-BoldItalic.otf new file mode 100644 index 0000000..718357f Binary files /dev/null and b/app/static/fonts/PublicSans-BoldItalic.otf differ diff --git a/app/static/fonts/PublicSans-ExtraBold.otf b/app/static/fonts/PublicSans-ExtraBold.otf new file mode 100644 index 0000000..09b52dd Binary files /dev/null and b/app/static/fonts/PublicSans-ExtraBold.otf differ diff --git a/app/static/fonts/PublicSans-ExtraBoldItalic.otf b/app/static/fonts/PublicSans-ExtraBoldItalic.otf new file mode 100644 index 0000000..5b33222 Binary files /dev/null and b/app/static/fonts/PublicSans-ExtraBoldItalic.otf differ diff --git a/app/static/fonts/PublicSans-ExtraLight.otf b/app/static/fonts/PublicSans-ExtraLight.otf new file mode 100644 index 0000000..49b407d Binary files /dev/null and b/app/static/fonts/PublicSans-ExtraLight.otf differ diff --git a/app/static/fonts/PublicSans-ExtraLightItalic.otf b/app/static/fonts/PublicSans-ExtraLightItalic.otf new file mode 100644 index 0000000..c76e855 Binary files /dev/null and b/app/static/fonts/PublicSans-ExtraLightItalic.otf differ diff --git a/app/static/fonts/PublicSans-Italic.otf b/app/static/fonts/PublicSans-Italic.otf new file mode 100644 index 0000000..38996ad Binary files /dev/null and b/app/static/fonts/PublicSans-Italic.otf differ diff --git a/app/static/fonts/PublicSans-Light.otf b/app/static/fonts/PublicSans-Light.otf new file mode 100644 index 0000000..126544e Binary files /dev/null and b/app/static/fonts/PublicSans-Light.otf differ diff --git a/app/static/fonts/PublicSans-LightItalic.otf b/app/static/fonts/PublicSans-LightItalic.otf new file mode 100644 index 0000000..1e6aa6f Binary files /dev/null and b/app/static/fonts/PublicSans-LightItalic.otf differ diff --git a/app/static/fonts/PublicSans-Medium.otf b/app/static/fonts/PublicSans-Medium.otf new file mode 100644 index 0000000..93507a5 Binary files /dev/null and b/app/static/fonts/PublicSans-Medium.otf differ diff --git a/app/static/fonts/PublicSans-MediumItalic.otf b/app/static/fonts/PublicSans-MediumItalic.otf new file mode 100644 index 0000000..f5ddb90 Binary files /dev/null and b/app/static/fonts/PublicSans-MediumItalic.otf differ diff --git a/app/static/fonts/PublicSans-Regular.otf b/app/static/fonts/PublicSans-Regular.otf new file mode 100644 index 0000000..d2b3f16 Binary files /dev/null and b/app/static/fonts/PublicSans-Regular.otf differ diff --git a/app/static/fonts/PublicSans-SemiBold.otf b/app/static/fonts/PublicSans-SemiBold.otf new file mode 100644 index 0000000..4ab6b89 Binary files /dev/null and b/app/static/fonts/PublicSans-SemiBold.otf differ diff --git a/app/static/fonts/PublicSans-SemiBoldItalic.otf b/app/static/fonts/PublicSans-SemiBoldItalic.otf new file mode 100644 index 0000000..a28f6c0 Binary files /dev/null and b/app/static/fonts/PublicSans-SemiBoldItalic.otf differ diff --git a/app/static/fonts/PublicSans-Thin.otf b/app/static/fonts/PublicSans-Thin.otf new file mode 100644 index 0000000..dee0ae2 Binary files /dev/null and b/app/static/fonts/PublicSans-Thin.otf differ diff --git a/app/static/fonts/PublicSans-ThinItalic.otf b/app/static/fonts/PublicSans-ThinItalic.otf new file mode 100644 index 0000000..c6b481a Binary files /dev/null and b/app/static/fonts/PublicSans-ThinItalic.otf differ diff --git a/app/static/styles/style.css b/app/static/styles/style.css new file mode 100644 index 0000000..ca60a7b --- /dev/null +++ b/app/static/styles/style.css @@ -0,0 +1,79 @@ +:root { + --primary-color: #003B5C; + --secondary-color: #C3D7EE; + --home: #c8c8c8; + --yes: #80FF80; + --no: #FF8080; + font-size: 18; + --fast-speed: 0.2s; + --med-speed: 0.4s; + --slow-speed: 1s; +} + +body { + font-family: 'Public Sans', sans-serif; + line-height: 1.2; + margin: 0; + padding: 0; +} + +/* h1 { */ +/* margin-left: 2rem; */ +/* margin-right: 1rem; */ +/* } */ + +/* h2 { */ +/* margin-left: 1rem; */ +/* margin-right: 1rem; */ +/* } */ + +/* p { */ +/* margin-left: 1rem; */ +/* margin-right: 1rem; */ +/* } */ + + +@font-face { + font-family: "Public Sans"; + src: url("/static/fonts/PublicSans-Regular.otf"); +} + +nav { + display: flex; + background: darkgrey; + color: white; + margin: 0.5em; + padding: 0.5em; +} + +h1 { + margin: 0; +} + +nav ul { + display: flex; + flex-wrap: wrap; + margin: 0; + justify-content: right; + list-style: none; +} + +nav ul li { + margin: 0 0 0.5em 0; + padding: 0.5em 0; +} + +.button { + height: 1em; + padding: 0.5em; + margin: 0 0.5em; + background: dimgray; + color: white; + border-radius: 8px; + text-decoration: none; +} + +.button:hover { + background: white; + color: black; +} diff --git a/app/templates/add_product.html b/app/templates/add_product.html new file mode 100644 index 0000000..ad7ea0a --- /dev/null +++ b/app/templates/add_product.html @@ -0,0 +1,44 @@ +{# -*- mode: jinja2; -*- #} + +{% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} + +{% block content %} + + {% block title %}Add a New Product{% endblock %} + + {% if message %} + + {# the form was submitted and message exists #} +

{{ message }}

+ {# links #} +

Submit another product.

+

Return to the index.

+ +{% else %} + + {# the form is displayed when template opens via GET not POST #} +

Add a new sock to our inventory.

+

Return to the index.

+{# show flash - based on WTForms validators +see https://pythonprogramming.net/flash-flask-tutorial/ +get_flashed_messages() exists here because of flash() +in the route function +#} +{% with errors = get_flashed_messages() %} + {% if errors %} + {% for err in errors %} + + {% endfor %} + {% endif %} +{% endwith %} +{# end of flash #} + +{# the form, thanks to WTForms #} +{{ wtf.quick_form(form) }} + +{% endif %} +{% endblock %} diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..0b53b5c --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,30 @@ +{# -*- mode: jinja2; -*- #} + + + + + + + + Farm Manager + + + + + + + +
+ {% block content %}{% endblock %} +
+ + + diff --git a/app/templates/errors/module-not-found.html b/app/templates/errors/module-not-found.html new file mode 100644 index 0000000..6496503 --- /dev/null +++ b/app/templates/errors/module-not-found.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %} +Module not found +{% endblock %} + +{% block content %} +

+ No module found with name {{ module }}. +

+

+ If you'd like to suggest a module, please send us an e-mail +

+{% endblock %} diff --git a/app/templates/home.html b/app/templates/home.html new file mode 100644 index 0000000..a9bac51 --- /dev/null +++ b/app/templates/home.html @@ -0,0 +1,22 @@ + + +{% extends "base.html" %} + +{% block title %} + Welcome, {{ user.name_first }}! +{% endblock %} + + +{% block content %} + +

You are logged in as {{ user }} on + project {{ project }}.

+ +

Available modules

+
+ {% for module in modules %} +
{{ module.name }}
{{ module.description }}
+ {% endfor %} +
+ +{% endblock %} diff --git a/app/templates/modules/calculator.html b/app/templates/modules/calculator.html new file mode 100644 index 0000000..f951a17 --- /dev/null +++ b/app/templates/modules/calculator.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block title %} +Calculator +{% endblock %} + +{% block content %} +insert module content here. +{% endblock %} diff --git a/app/templates/modules/catalog.html b/app/templates/modules/catalog.html new file mode 100644 index 0000000..857c654 --- /dev/null +++ b/app/templates/modules/catalog.html @@ -0,0 +1,35 @@ + + +{% extends "base.html" %} +{% block title %} + Catalog +{% endblock %} + +{% block content %} + insert module content here. + +

+ Add product to your catalog +

+ + + + + + + + + + + + {% for product in catalog %} + + + + + + + {% endfor %} + +
NamePriceSupplierUpdated
{{ product.name }}{{ product.price }}{{ product.supplier }}{{ product.updated }}
+{% endblock %} diff --git a/app/templates/modules/creator.html b/app/templates/modules/creator.html new file mode 100644 index 0000000..588f428 --- /dev/null +++ b/app/templates/modules/creator.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block title %} +Creator +{% endblock %} + +{% block content %} +insert module content here. +{% endblock %} diff --git a/app/templates/modules/logger.html b/app/templates/modules/logger.html new file mode 100644 index 0000000..7ba93b3 --- /dev/null +++ b/app/templates/modules/logger.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block title %} +Logger +{% endblock %} + +{% block content %} +insert module content here. +{% endblock %} diff --git a/app/templates/modules/stock.html b/app/templates/modules/stock.html new file mode 100644 index 0000000..5d20d5c --- /dev/null +++ b/app/templates/modules/stock.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block title %} +Stock +{% endblock %} + +{% block content %} +insert module content here. +{% endblock %} -- cgit v1.2.3