diff --git a/Survey Form App with Flask/Procfile b/Survey Form App with Flask/Procfile new file mode 100644 index 0000000..8001d1a --- /dev/null +++ b/Survey Form App with Flask/Procfile @@ -0,0 +1 @@ +web: gunicorn app:app \ No newline at end of file diff --git a/Survey Form App with Flask/README.md b/Survey Form App with Flask/README.md new file mode 100644 index 0000000..2547087 --- /dev/null +++ b/Survey Form App with Flask/README.md @@ -0,0 +1,19 @@ +# Survey Form App with Flask + +[](https://survey-app-flask.herokuapp.com) + +This web application presents an user interface created using Bootstrap 4 to the user and expects user inputs. It also has a route that uses DataTables wherein the user can view all form submissions. + + + + + +If the user has not filled out all of the required fields, client-side JavaScript handles form validation. + + + +Even in the case that an user disables JavaScript using Developer Tools, the server-side validation logic takes care of that. + + + + diff --git a/Survey Form App with Flask/app.py b/Survey Form App with Flask/app.py new file mode 100644 index 0000000..ecd8c0e --- /dev/null +++ b/Survey Form App with Flask/app.py @@ -0,0 +1,83 @@ +""" +Controller Logic for the application +""" + +import csv + +from flask import Flask, jsonify, redirect, render_template, request + +# Configure application +app = Flask(__name__) + +# Reload templates when they are changed +app.config["TEMPLATES_AUTO_RELOAD"] = True + + +@app.after_request +def after_request(response): + """ + disable caching + """ + response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" + response.headers["Expires"] = 0 + response.headers["Pragma"] = "no-cache" + return response + + +@app.route("/", methods=["GET"]) +def get_index(): + """ + redirect users to survey form + """ + return redirect("/form") + + +@app.route("/form", methods=["GET"]) +def get_form(): + """ + respond with the survey form + """ + return render_template("form.html") + + +@app.route("/form", methods=["POST"]) +def post_form(): + """ + receive details from form + """ + failure = [] + if not request.form.get("name"): + failure.append("Please enter your name.") + if not request.form.get("email"): + failure.append("Please enter your email.") + if not request.form.get("phone"): + failure.append("Please enter your phone number.") + if not request.form.get("gender"): + failure.append("Please enter your gender.") + if not request.form.get("choice"): + failure.append("Please enter your choice.") + else: + with open("survey.csv", "a") as file: + """ + write files to the CSV file + """ + f = csv.writer(file) + f.writerow( + (request.form.get("name"), + request.form.get("email"), + request.form.get("phone"), + request.form.get("gender"), + request.form.get("choice")) + ) + return redirect("/sheet") + return render_template("error.html", failure=failure) + + +@app.route("/sheet", methods=["GET"]) +def get_sheet(): + with open("survey.csv", "r") as file: + """ + read data from the CSV file + """ + f = csv.reader(file) + return render_template("sheet.html", data=list(f)) diff --git a/Survey Form App with Flask/requirements.txt b/Survey Form App with Flask/requirements.txt new file mode 100644 index 0000000..f163f4d --- /dev/null +++ b/Survey Form App with Flask/requirements.txt @@ -0,0 +1,2 @@ +flask +gunicorn \ No newline at end of file diff --git a/Survey Form App with Flask/screenshots/clientValidation.png b/Survey Form App with Flask/screenshots/clientValidation.png new file mode 100644 index 0000000..2e2b101 Binary files /dev/null and b/Survey Form App with Flask/screenshots/clientValidation.png differ diff --git a/Survey Form App with Flask/screenshots/dataView.png b/Survey Form App with Flask/screenshots/dataView.png new file mode 100644 index 0000000..84a15dc Binary files /dev/null and b/Survey Form App with Flask/screenshots/dataView.png differ diff --git a/Survey Form App with Flask/screenshots/disablingJS.png b/Survey Form App with Flask/screenshots/disablingJS.png new file mode 100644 index 0000000..8aa612c Binary files /dev/null and b/Survey Form App with Flask/screenshots/disablingJS.png differ diff --git a/Survey Form App with Flask/screenshots/form.png b/Survey Form App with Flask/screenshots/form.png new file mode 100644 index 0000000..89da14f Binary files /dev/null and b/Survey Form App with Flask/screenshots/form.png differ diff --git a/Survey Form App with Flask/screenshots/serverValidation.png b/Survey Form App with Flask/screenshots/serverValidation.png new file mode 100644 index 0000000..8c81fce Binary files /dev/null and b/Survey Form App with Flask/screenshots/serverValidation.png differ diff --git a/Survey Form App with Flask/static/form.js b/Survey Form App with Flask/static/form.js new file mode 100644 index 0000000..ae5ef3a --- /dev/null +++ b/Survey Form App with Flask/static/form.js @@ -0,0 +1,24 @@ +/** + * Front-end Code for /form route + */ + +// wait for DOM to finish loading +$(document).ready(() => { + // find all elements to be validated + var forms = document.getElementsByClassName("needs-validation"); + // add validation logic to those elements + var validation = Array.prototype.filter.call(forms, form => { + form.addEventListener( + "submit", + event => { + if (form.checkValidity() === false) { + // prevent form submission + event.preventDefault(); + event.stopPropagation(); + } + form.classList.add("was-validated"); + }, + false + ); + }); +}); diff --git a/Survey Form App with Flask/static/styles.css b/Survey Form App with Flask/static/styles.css new file mode 100644 index 0000000..9afc556 --- /dev/null +++ b/Survey Form App with Flask/static/styles.css @@ -0,0 +1,58 @@ +/** + * Stylesheet for the application + */ + +table { + text-align: center; +} + +.form { + width: 40vw; +} + +.custom-switch { + padding-left: 2.25rem; +} + +.custom-switch .custom-control-label::before { + margin-top: 1px; + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; +} + +.custom-switch .custom-control-label::after { + margin-top: 1px; + top: calc(0.15625rem + 3px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #8f9ba6; + border-radius: 0.5rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, + border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, + border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, + -webkit-transform 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + -webkit-transform: translateX(0.75rem); + transform: translateX(0.75rem); +} + +.custom-switch + .custom-control-input:disabled:checked + ~ .custom-control-label::before { + background-color: rgba(32, 168, 216, 0.5); +} diff --git a/Survey Form App with Flask/static/table.js b/Survey Form App with Flask/static/table.js new file mode 100644 index 0000000..fb7d092 --- /dev/null +++ b/Survey Form App with Flask/static/table.js @@ -0,0 +1,13 @@ +/** + * Front-end Code for /sheet route + */ + +// wait for DOM to finish loading +$(document).ready(() => { + // initialise DataTable + $("#table").DataTable({ + searching: false, + paging: false, + info: false + }); +}); diff --git a/Survey Form App with Flask/survey.csv b/Survey Form App with Flask/survey.csv new file mode 100644 index 0000000..97a1f57 --- /dev/null +++ b/Survey Form App with Flask/survey.csv @@ -0,0 +1,6 @@ +Sudipto Ghosh,sudipto@ghosh.pro,8826358310,Male,Non-Veg + +John Doe,jdoe@gmail.com,1234567890,Male,Non-Veg +David Malan,malan@harvard.edu,7894561230,Male,Non-Veg +Brenda Anderson,banderson@harvard.edu,4567891230,Female,Veg +David Foo,david@gmail.com,1234567890,Male,Veg diff --git a/Survey Form App with Flask/templates/error.html b/Survey Form App with Flask/templates/error.html new file mode 100644 index 0000000..bed2191 --- /dev/null +++ b/Survey Form App with Flask/templates/error.html @@ -0,0 +1,15 @@ +{% extends "layout.html" %} {% block main %} + + + +
{{ f }}
+ {% endfor %} +| Name | +Phone | +Gender | +Choice | + + + {% for d in data %} {% if d %} +|
|---|---|---|---|---|
| + {{ d[0] }} + | ++ {{ d[1] }} + | ++ {{ d[2] }} + | ++ {{ d[3] }} + | ++ {{ d[4] }} + | +