Skip to content

Commit

Permalink
frontend, backend: add info fields
Browse files Browse the repository at this point in the history
  • Loading branch information
div72 committed May 29, 2023
1 parent a4beb44 commit 27ad547
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 24 deletions.
39 changes: 34 additions & 5 deletions formie/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
ResponseReturnValue = "ResponseReturnValue"

from formie import auth
from formie.models import db, Field, ChoiceField, Form, Model, TextField, RangeField
from formie.models import db, Field, ChoiceField, Form, InfoField, Model, TextField, RangeField

bp = Blueprint("forms", __name__, url_prefix="/forms")

Expand Down Expand Up @@ -58,6 +58,24 @@ def validate_schema(data: JSONData) -> str:
if TYPE_CHECKING:
field = cast(dict[str, JSONData], field)

if "type" not in field:
return f"Field #{i} needs a type."

if field["type"] == "info":
if "text" not in field:
return f"Field #{i} needs information text."

if len(field) != 2:
return f"Field #{i} cannot have more than 2 attributes."

if not isinstance(field["text"], str):
return f"Field #{i} has invalid text type."

if len(field["text"]) > 512:
return f"Field #{i}'s information text cannot have more than 512 characters."

continue

if (
"name" not in field
or not isinstance(field["name"], str)
Expand All @@ -68,9 +86,6 @@ def validate_schema(data: JSONData) -> str:
if len(field["name"]) > 256:
return f"Field #{i}'s question cannot be longer than 256 characters."

if "type" not in field:
return f"Field #{i} needs a type."

if field["type"] == "text":
if "default" not in field:
return f"Field #{i} needs a default attribute."
Expand Down Expand Up @@ -145,6 +160,9 @@ def validate_answer(schema: list[Field], form: dict[str, str]) -> str:
"""Validates an answer against the given schema. Returns an error string on failure."""

for i, field in enumerate(schema):
if isinstance(field, InfoField):
continue

if not isinstance(field, ChoiceField) or field.single:
if f"col{i}" not in form:
return f"Question #{i + 1} is missing an answer."
Expand Down Expand Up @@ -193,7 +211,7 @@ def validate_answer(schema: list[Field], form: dict[str, str]) -> str:
def decode_fields(data: list[dict[str, JSONData]]) -> list[Field]:
fields: list[Field] = []
for elem in data:
assert isinstance(elem["name"], str)
assert isinstance(elem["type"], str)

if elem["type"] == "text":
del elem["type"]
Expand Down Expand Up @@ -228,6 +246,11 @@ def decode_fields(data: list[dict[str, JSONData]]) -> list[Field]:
)
)
elem["type"] = "range"
elif elem["type"] == "info":
assert isinstance(elem["text"], str)

fields.append(InfoField(text=elem["text"]))

return fields


Expand All @@ -240,6 +263,9 @@ def create_model(name: str, fields: list[Field]) -> Type[Model]:

cols = {"id": db.Column(db.Integer, primary_key=True)}
for i, field in enumerate(fields):
if isinstance(field, InfoField):
continue

if isinstance(field, TextField):
col = db.Column(db.Text, default=field.default)
elif isinstance(field, ChoiceField):
Expand Down Expand Up @@ -390,6 +416,9 @@ def view_results(form_id: int) -> ResponseReturnValue:
for res in model.query.all():
cols = [res.id]
for i, field in enumerate(fields):
if isinstance(field, InfoField):
continue

if not isinstance(field, ChoiceField):
cols.append(getattr(res, f"col{i}"))
else:
Expand Down
5 changes: 5 additions & 0 deletions formie/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class Field:
name: str # max 256 bytes


@dataclass
class InfoField:
text: str # max 512 characters


@dataclass
class TextField(Field):
default: str # max 1023 bytes
Expand Down
54 changes: 35 additions & 19 deletions formie/static/new_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ function change_field(field, typ) {
}
}

function new_info() {
fields_div.insertAdjacentHTML('beforeend', `
<div id="field${index}">
<textarea id="text"></textarea>
<button onclick="this.parentElement.remove()">Remove field</button>
</div>`);
}

function add_choice(element, is_radio) {
var typ = 'checkbox';
if (is_radio) typ = 'radio';
Expand All @@ -48,25 +56,33 @@ function make_schema() {
let schema = [];
for (field of fields_div.children) {
let schema_field = {};
schema_field.name = field.children[0].value;
if (field.children[1].selectedIndex === 3) {
schema_field.type = "range";
schema_field.min = parseInt(field.children[2].children[1].value);
schema_field.max = parseInt(field.children[2].children[3].value);
schema_field.default = parseInt(field.children[2].children[5].value);
} else if (field.children[1].selectedIndex === 0) {
schema_field.type = "text";
schema_field.default = field.children[2].lastChild.value;
} else {
schema_field.choices = [];
schema_field.single = false;
schema_field.type = "choice";
schema_field.default = 0;
if (field.children[1].selectedIndex === 1) schema_field.single = true;
for (choice of field.children[2].children) {
if (choice.tagName == 'INPUT' && choice.type == 'text') schema_field.choices.push(choice.value);
}
}
if (field.children[0].tagName === 'TEXTAREA') {
// Information field
schema_field.type = "info";
schema_field.text = field.children[0].value;
} else {
// Question field
schema_field.name = field.children[0].value;

if (field.children[1].selectedIndex === 3) {
schema_field.type = "range";
schema_field.min = parseInt(field.children[2].children[1].value);
schema_field.max = parseInt(field.children[2].children[3].value);
schema_field.default = parseInt(field.children[2].children[5].value);
} else if (field.children[1].selectedIndex === 0) {
schema_field.type = "text";
schema_field.default = field.children[2].lastChild.value;
} else {
schema_field.choices = [];
schema_field.single = false;
schema_field.type = "choice";
schema_field.default = 0;
if (field.children[1].selectedIndex === 1) schema_field.single = true;
for (choice of field.children[2].children) {
if (choice.tagName == 'INPUT' && choice.type == 'text') schema_field.choices.push(choice.value);
}
}
}
schema.push(schema_field);
}
return schema;
Expand Down
2 changes: 2 additions & 0 deletions formie/templates/forms/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
{% endif %}
{% elif field['type'] == 'range' %}
<input type="number" name="col{{ index }}" min="{{ field['min'] }}" max="{{ field['max'] }}" value="{{ field['default'] }}"></input>
{% elif field['type'] == 'info' %}
<p>{{ field['text'] }}</p>
{% endif %}
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions formie/templates/forms/new.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

{% block content %}
<button onclick="new_field()">New field</button>
<button onclick="new_info()">New information</button>
<div id="fields"></div>
<br><input id="hide_results" type="checkbox">Make results private.</input>
<br><input id="disallow_anon_answer" type="checkbox">Block non-logged in users from answering.</input>
Expand Down
2 changes: 2 additions & 0 deletions formie/templates/forms/results.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
<tr>
<th>ID</th>
{% for question in schema %}
{% if question["name"] %}
<th>{{ question["name"] }}</th>
{% endif %}
{% endfor %}
</tr>
{% for result in results %}
Expand Down

0 comments on commit 27ad547

Please sign in to comment.