From be5c33c581d5b4913f44f595659eac9e425afa95 Mon Sep 17 00:00:00 2001 From: Francesco Siddi Date: Mon, 2 Feb 2015 20:42:48 +0100 Subject: [PATCH] Procedural node add and edit forms --- attract/application/__init__.py | 2 + attract/application/modules/nodes/__init__.py | 62 ++++++++++- attract/application/modules/nodes/forms.py | 103 +++++++++++++++++- attract/application/templates/layout.html | 6 +- .../application/templates/node_types/add.html | 31 ++++++ .../{nodes => node_types}/index.html | 2 +- attract/application/templates/nodes/add.html | 37 +++---- attract/application/templates/nodes/edit.html | 28 +++++ .../application/templates/shots/index.html | 4 +- 9 files changed, 242 insertions(+), 33 deletions(-) create mode 100644 attract/application/templates/node_types/add.html rename attract/application/templates/{nodes => node_types}/index.html (94%) create mode 100644 attract/application/templates/nodes/edit.html diff --git a/attract/application/__init__.py b/attract/application/__init__.py index a635f2b2..99ba8d78 100644 --- a/attract/application/__init__.py +++ b/attract/application/__init__.py @@ -23,6 +23,7 @@ thumb = Thumbnail(app) assets = Environment(app) # Import controllers +from application.modules.nodes import node_types from application.modules.nodes import nodes from application.modules.main import homepage from application.modules.shots import shots @@ -32,4 +33,5 @@ from application.modules.projects import projects app.register_blueprint(filemanager) app.register_blueprint(shots, url_prefix='/shots') app.register_blueprint(projects, url_prefix='/projects') +app.register_blueprint(node_types, url_prefix='/node-types') app.register_blueprint(nodes, url_prefix='/nodes') diff --git a/attract/application/modules/nodes/__init__.py b/attract/application/modules/nodes/__init__.py index 9130b669..8e9f2cc1 100644 --- a/attract/application/modules/nodes/__init__.py +++ b/attract/application/modules/nodes/__init__.py @@ -11,19 +11,22 @@ from application import db from application.modules.nodes.models import Node, NodeType from application.modules.nodes.forms import NodeTypeForm +from application.modules.nodes.forms import get_node_form +from application.modules.nodes.forms import process_node_form # Name of the Blueprint +node_types = Blueprint('node_types', __name__) nodes = Blueprint('nodes', __name__) -@nodes.route("/") +@node_types.route("/") def index(): """Display the node types """ node_types = [t for t in NodeType.query.all()] - return render_template('nodes/index.html', - title='nodes', + return render_template('node_types/index.html', + title='node_types', node_types=node_types) shots = [] @@ -45,7 +48,7 @@ def index(): shots=shots) -@nodes.route("/add", methods=('GET', 'POST')) +@node_types.route("/add", methods=['GET', 'POST']) def add(): form = NodeTypeForm() @@ -58,5 +61,52 @@ def add(): db.session.add(node_type) db.session.commit() - return redirect(url_for('nodes.index')) - return render_template('nodes/add.html', form=form) + return redirect(url_for('node_types.index')) + return render_template('node_types/add.html', form=form) + + +@nodes.route("//add", methods=['GET', 'POST']) +def add(node_type): + """Generic function to add a node of any type + """ + form = get_node_form(node_type) + if form.validate_on_submit(): + if process_node_form(form): + return redirect('/') + else: + print form.errors + return render_template('nodes/add.html', + node_type=node_type, + form=form) + + +@nodes.route("//edit", methods=['GET', 'POST']) +def edit(node_id): + """Generic node editing form + """ + node = Node.query.get_or_404(node_id) + form = get_node_form(node.node_type.url) + + if form.validate_on_submit(): + if process_node_form(form, node_id): + return redirect(url_for('node.edit', node_id=node_id)) + + form.name.data = node.name + form.description.data = node.description + + # We populate the form, basing ourselves on the default node properties + for node_property in node.properties: + for field in form: + if field.name == node_property.custom_field.name_url: + value = node_property.value + # We cast values into the right type + if node_property.custom_field.field_type == 'integer': + value = int(value) + if node_property.custom_field.field_type == 'select': + value = int(value) + field.data = value + + + return render_template('nodes/edit.html', + node=node, + form=form) \ No newline at end of file diff --git a/attract/application/modules/nodes/forms.py b/attract/application/modules/nodes/forms.py index 10783a64..25191960 100644 --- a/attract/application/modules/nodes/forms.py +++ b/attract/application/modules/nodes/forms.py @@ -4,13 +4,112 @@ from wtforms import BooleanField from wtforms import SelectField from wtforms import TextAreaField from wtforms import IntegerField +from wtforms import HiddenField +from application.modules.nodes.models import CustomFields from wtforms.validators import DataRequired -from application.modules.nodes.models import Node, NodeType +from application import db + +from application.modules.nodes.models import Node, NodeType, NodeProperties + class NodeTypeForm(Form): - name = TextField('Node Name', validators=[DataRequired()]) + name = TextField('Name', validators=[DataRequired()]) url = TextField('Url', validators=[DataRequired()]) description = TextAreaField('Description', validators=[DataRequired()]) is_extended = BooleanField('Is extended') + + +def get_node_form(node_type): + node_type = NodeType.query.filter_by(url=node_type).first() + class ProceduralForm(Form): + pass + + setattr(ProceduralForm, + 'name', + TextField('Name', validators=[DataRequired()])) + setattr(ProceduralForm, + 'description', + TextAreaField('Description', validators=[DataRequired()])) + setattr(ProceduralForm, + 'node_type_id', + HiddenField(default=node_type.id)) + + for custom_field in CustomFields.query\ + .join(NodeType)\ + .filter(NodeType.url == node_type.url): + + if custom_field.field_type == 'text': + field_properties = TextAreaField(custom_field.name, + validators=[DataRequired()]) + elif custom_field.field_type == 'string': + field_properties = TextField(custom_field.name, + validators=[DataRequired()]) + elif custom_field.field_type == 'integer': + field_properties = IntegerField(custom_field.name, + validators=[DataRequired()]) + elif custom_field.field_type == 'select': + options = Node.query\ + .join(NodeType)\ + .filter(NodeType.url==custom_field.name_url)\ + .all() + field_properties = SelectField(custom_field.name, + coerce=int, + choices=[(option.id, option.name) for option in options] ) + + setattr(ProceduralForm, custom_field.name_url, field_properties) + + return ProceduralForm() + + +def process_node_form(form, node_id=None): + """Generic function used to process new nodes, as well as edits + """ + if form.validate_on_submit(): + node_type = NodeType.query.get(form.node_type_id.data) + if node_id: + node = Node.query.get(node_id) + node.name = form.name.data + node.description = form.description.data + else: + node = Node( + name=form.name.data, + description=form.description.data, + node_type_id=form.node_type_id.data) + db.session.add(node) + db.session.commit() + + for custom_field in CustomFields.query\ + .join(NodeType)\ + .filter(NodeType.url == node_type.url): + + for field in form: + if field.name == custom_field.name_url: + if node_id: + # Query for the indivitual property + # TODO: collect all properties and loop through them + node_property = NodeProperties.query\ + .filter_by(node_id=node_id)\ + .filter_by(custom_field_id=custom_field.id)\ + .first() + if node_property: + # Update the value of the property + node_property.value = field.data + else: + # If the property is missing we add it + node_property = NodeProperties( + node_id=node.id, + custom_field_id=custom_field.id, + value=field.data) + db.session.add(node_property) + else: + node_property = NodeProperties( + node_id=node.id, + custom_field_id=custom_field.id, + value=field.data) + db.session.add(node_property) + db.session.commit() + return True + else: + return False diff --git a/attract/application/templates/layout.html b/attract/application/templates/layout.html index 6de0aa8d..fc1017f4 100644 --- a/attract/application/templates/layout.html +++ b/attract/application/templates/layout.html @@ -47,10 +47,12 @@
  • Tasks
  • Profile
  • +
  • Task Types
  • +
  • Log out
  • - - + + diff --git a/attract/application/templates/node_types/add.html b/attract/application/templates/node_types/add.html new file mode 100644 index 00000000..11922c68 --- /dev/null +++ b/attract/application/templates/node_types/add.html @@ -0,0 +1,31 @@ +{% extends 'layout.html' %} + +{% block body %} +
    +

    Add Node type

    +
    +
    +
    + {{ form.hidden_tag() }} +
    + {{ form.name.label }} + {{ form.name(size=20, class='form-control') }} +
    +
    + {{ form.description.label }} + {{ form.description(size=20, class='form-control') }} +
    +
    + {{ form.url.label }} + {{ form.url(size=20, class='form-control') }} +
    +
    + {{ form.is_extended.label }} + {{ form.is_extended(class='form-control') }} +
    + +
    +
    +
    +
    +{% endblock %} diff --git a/attract/application/templates/nodes/index.html b/attract/application/templates/node_types/index.html similarity index 94% rename from attract/application/templates/nodes/index.html rename to attract/application/templates/node_types/index.html index f9c8f603..839ef571 100644 --- a/attract/application/templates/nodes/index.html +++ b/attract/application/templates/node_types/index.html @@ -44,7 +44,7 @@
    - Add + Add
    diff --git a/attract/application/templates/nodes/add.html b/attract/application/templates/nodes/add.html index 4efb942b..2245cf18 100644 --- a/attract/application/templates/nodes/add.html +++ b/attract/application/templates/nodes/add.html @@ -2,28 +2,25 @@ {% block body %}
    -

    Add Node type

    +

    Add {{ node_type }}

    -
    - {{ form.hidden_tag() }} -
    - {{ form.name.label }} - {{ form.name(size=20, class='form-control') }} -
    -
    - {{ form.description.label }} - {{ form.description(size=20, class='form-control') }} -
    -
    - {{ form.url.label }} - {{ form.url(size=20, class='form-control') }} -
    -
    - {{ form.is_extended.label }} - {{ form.is_extended(class='form-control') }} -
    - + + {% for field in form %} + {% if field.name == 'csrf_token' %} + {{ field }} + {% else %} + {% if field.type == "HiddenField" %} + {{ field }} + {% else %} +
    + {{ field.label }} + {{ field(class='form-control') }} +
    + {% endif %} + {% endif %} + {% endfor %} +
    diff --git a/attract/application/templates/nodes/edit.html b/attract/application/templates/nodes/edit.html new file mode 100644 index 00000000..59821e16 --- /dev/null +++ b/attract/application/templates/nodes/edit.html @@ -0,0 +1,28 @@ +{% extends 'layout.html' %} + +{% block body %} +
    +

    Edit {{ node.node_type.name }}

    +
    +
    +
    + {% for field in form %} + {% if field.name == 'csrf_token' %} + {{ field }} + {% else %} + {% if field.type == "HiddenField" %} + {{ field }} + {% else %} +
    + {{ field.label }} + {{ field(class='form-control') }} +
    + {% endif %} + {% endif %} + {% endfor %} + +
    +
    +
    +
    +{% endblock %} diff --git a/attract/application/templates/shots/index.html b/attract/application/templates/shots/index.html index bd4830f7..61d92db5 100644 --- a/attract/application/templates/shots/index.html +++ b/attract/application/templates/shots/index.html @@ -34,7 +34,7 @@ {% endif %} - Edit + Edit {% endfor %} @@ -56,7 +56,7 @@
    - Add + Add