diff --git a/bootstrap/config.py b/bootstrap/config.py index a343a9b..c2f6fb7 100644 --- a/bootstrap/config.py +++ b/bootstrap/config.py @@ -13,6 +13,7 @@ DATABASE_FILE = os.path.join(DATA_DIR, "db.sqlite") SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(DATABASE_FILE) TEMPLATES_FOLDER = os.path.join(basepath, "webapp", "templates") +STATIC_FOLDER = os.path.join(basepath, "webapp", "static") MAKEDIRS = [ DATA_DIR, TASKS_DIR, TASKS_IN_DIR, TASKS_PROC_DIR, TASKS_BUF_DIR, diff --git a/bootstrap/run.py b/bootstrap/run.py index 3a1b730..44799f3 100644 --- a/bootstrap/run.py +++ b/bootstrap/run.py @@ -1,4 +1,4 @@ -from config import TEMPLATES_FOLDER +from config import TEMPLATES_FOLDER, STATIC_FOLDER from oshipka import init_db from populate import populate_db @@ -8,6 +8,7 @@ if init_db(app): populate_db(app) app.template_folder = TEMPLATES_FOLDER +app.static_folder = STATIC_FOLDER if __name__ == "__main__": app.run(debug=True) diff --git a/oshipka.sh b/oshipka.sh index d998a95..65e195c 100755 --- a/oshipka.sh +++ b/oshipka.sh @@ -16,13 +16,24 @@ Usage $0 [ bootstrap | worker | web ] " worker () { + source venv/bin/activate python worker.py } web () { + source venv/bin/activate python run.py } +init_venv() { + virtualenv -p python3 venv +} + +install_reqs() { + source venv/bin/activate + pip install -r requirements.txt +} + bootstrap() { shift PROJECT_PATH=$1 @@ -52,6 +63,8 @@ bootstrap() { echo "INFO: Bootstrapping project $PROJECT_NAME..." mkdir -p ${PROJECT_PATH} cp -r ${OSHIPKA_PATH}/bootstrap/* ${PROJECT_PATH}/ + cp ${OSHIPKA_PATH}/bootstrap/.gitignore ${PROJECT_PATH}/.gitignore + init_venv } command_main() { diff --git a/oshipka/webapp/templates/delete_instance.html b/oshipka/webapp/templates/delete_instance.html new file mode 100644 index 0000000..0a16e3c --- /dev/null +++ b/oshipka/webapp/templates/delete_instance.html @@ -0,0 +1,10 @@ +{% extends "layout.html" %} + +{% block content %} +

Delete {{ model_name }}:{{ instance.uuid }} ?

+
+ + +
+ Back +{% endblock %} \ No newline at end of file diff --git a/oshipka/webapp/templates/list_instances.html b/oshipka/webapp/templates/list_instances.html new file mode 100644 index 0000000..011fd7b --- /dev/null +++ b/oshipka/webapp/templates/list_instances.html @@ -0,0 +1,16 @@ +{% extends "layout.html" %} + +{% block content %} +

{{ model_name_pl }}:

+ +{% endblock %} \ No newline at end of file diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py new file mode 100644 index 0000000..013b7fc --- /dev/null +++ b/oshipka/webapp/views.py @@ -0,0 +1,106 @@ +import inflect +from flask import flash, render_template, redirect, request, url_for + +from oshipka.persistance import db + + +def list_view(model_view, template): + def inner(): + instances = model_view.model.query.all() + return render_template(template, instances=instances) + + return inner + + +def get_view(model_view, template): + def inner(uuid): + model = model_view.model + instance = model.query.filter_by(uuid=uuid).first() + if not instance: + flash("No {}:{}".format(model_view.model_name, uuid)) + return redirect(request.referrer or url_for('home')) + return render_template(template, instance=instance) + + return inner + + +def serialize_form(): + return dict(request.form) + + +def update_view(model_view, template): + def inner(uuid): + model = model_view.model + instance = model.query.filter_by(uuid=uuid).first() + if not instance: + flash("No {}:{}".format(model_view.model_name, uuid)) + return redirect(request.referrer or url_for('home')) + if request.method == "GET": + return render_template(template, instance=instance) + serialized_form = serialize_form() + + _next = serialized_form.pop('_next') if '_next' in serialized_form else None + for k, v in serialized_form.items(): + setattr(instance, k, v) + db.session.add(instance) + db.session.commit() + flash("Updated {}:{}".format(model_view.model_name, uuid)) + return redirect(_next or request.referrer or url_for('home')) + + return inner + + +def delete_view(model_view): + def inner(uuid): + model = model_view.model + instance = model.query.filter_by(uuid=uuid).first() + if not instance: + flash("No {}:{}".format(model_view.model_name, uuid)) + return redirect(request.referrer or url_for('home')) + if request.method == "GET": + return render_template("delete_instance.html", instance=instance, + model_name=model_view.model_name) + + serialized_form = serialize_form() + _next = serialized_form.pop('_next') if '_next' in serialized_form else None + db.session.delete(instance) + db.session.commit() + flash("Deleted {}:{}".format(model_view.model_name, uuid)) + return redirect(_next or request.referrer or url_for('home')) + + return inner + + +class ModelView(object): + def __init__(self, app, model): + self.app = app + self.model = model + + p = inflect.engine() + + self.model_name = model.__name__.lower() + self.model_name_pl = p.plural(self.model_name) + + def register_list(self, list_template): + url = '/{}'.format(self.model_name_pl) + self.app.add_url_rule(url, + 'list_{}'.format(self.model_name), + list_view(self, list_template)) + + def register_get(self, retrieve_template): + url = '/{}/'.format(self.model_name_pl) + self.app.add_url_rule(url, + 'get_{}'.format(self.model_name), + get_view(self, retrieve_template)) + + def register_update(self, update_template): + url = '/{}//edit'.format(self.model_name_pl) + self.app.add_url_rule(url, methods=["GET", "POST"], + endpoint='update_{}'.format(self.model_name), + view_func=update_view(self, update_template)) + + def register_delete(self): + url = '/{}//delete'.format(self.model_name_pl) + self.app.add_url_rule(url, methods=["GET", "POST"], + endpoint='delete_{}'.format(self.model_name), + view_func=delete_view(self)) diff --git a/requirements.txt b/requirements.txt index f2ef029..fb12e80 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ filelock==3.0.12 Flask==1.1.1 Flask-SQLAlchemy==2.4.1 Flask-Table==0.5.0 +inflect==4.1.0 itsdangerous==1.1.0 Jinja2==2.11.1 MarkupSafe==1.1.1