Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Survey Form App with Flask/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn app:app
19 changes: 19 additions & 0 deletions Survey Form App with Flask/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Survey Form App with Flask

[![Heroku](https://heroku-badge.herokuapp.com/?app=survey-app-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.

![Form](./screenshots/form.png)

![Data](./screenshots/dataView.png)

If the user has not filled out all of the required fields, client-side JavaScript handles form validation.

![Client-side Validation](./screenshots/clientValidation.png)

Even in the case that an user disables JavaScript using Developer Tools, the server-side validation logic takes care of that.

![Disabling JavaScript](./screenshots/disablingJS.png)

![Server-side Validation](./screenshots/serverValidation.png)
83 changes: 83 additions & 0 deletions Survey Form App with Flask/app.py
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 2 additions & 0 deletions Survey Form App with Flask/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask
gunicorn
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Survey Form App with Flask/screenshots/form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions Survey Form App with Flask/static/form.js
Original file line number Diff line number Diff line change
@@ -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
);
});
});
58 changes: 58 additions & 0 deletions Survey Form App with Flask/static/styles.css
Original file line number Diff line number Diff line change
@@ -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);
}
13 changes: 13 additions & 0 deletions Survey Form App with Flask/static/table.js
Original file line number Diff line number Diff line change
@@ -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
});
});
6 changes: 6 additions & 0 deletions Survey Form App with Flask/survey.csv
Original file line number Diff line number Diff line change
@@ -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
Expand Down
15 changes: 15 additions & 0 deletions Survey Form App with Flask/templates/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends "layout.html" %} {% block main %}

<!--
View for errors
-->

<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Error</h4>
<hr />
{% for f in failure %}
<p>{{ f }}</p>
{% endfor %}
</div>

{% endblock %}
83 changes: 83 additions & 0 deletions Survey Form App with Flask/templates/form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{% extends "layout.html" %} {% block main %}

<!--
View for /form route
-->

<h1 class="mb-4 text-center display-4">Survey Form</h1>

<form action="/form" method="post" class="container needs-validation form" novalidate>
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="name" placeholder="Name" type="text" required />
<div class="invalid-feedback">
Please enter your name
</div>
</div>

<div class="form-group">
<input autocomplete="off" class="form-control" name="email" placeholder="Email" type="email" required />
<div class="invalid-feedback">
Please enter your email
</div>
</div>

<div class="form-group">
<input autocomplete="off" class="form-control" name="phone" placeholder="Phone Number" type="text" required />
<div class="invalid-feedback">
Please enter your phone number
</div>
</div>

<div class="form-group">
<select class="form-control" name="choice" id="choice" required>
<option disabled selected value="">Food Preference</option>
<option value="Veg">Vegetarian</option>
<option value="Non-Veg">Non-Vegetarian</option>
</select>
<div class="invalid-feedback">
Please enter a preference for food
</div>
</div>

<div class="text-center">
<div class="custom-control-inline">
<label>
Gender
</label>
</div>

<div class="custom-control custom-radio custom-control-inline mb-3">
<input class="custom-control-input" id="genderM" name="gender" type="radio" value="Male" required />
<label class="custom-control-label" for="genderM">
Male
</label>
</div>

<div class="custom-control custom-radio custom-control-inline mb-3">
<input class="custom-control-input" id="genderF" name="gender" type="radio" value="Female" required />
<label class="custom-control-label" for="genderF">
Female
</label>
</div>

<div class="custom-control custom-radio custom-control-inline mb-3">
<input class="custom-control-input" id="genderN" name="gender" type="radio" value="Neutral" required />
<label class="custom-control-label" for="genderN">
Neutral
</label>
</div>
</div>

<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input slider" id="switch" required />
<label class="custom-control-label" for="switch">
I agree that the information given above is correct.
</label>
</div>

<button class="btn btn-primary btn-block mt-3" type="submit">Submit</button>
</form>

<script src="/static/form.js"></script>

{% endblock %}
46 changes: 46 additions & 0 deletions Survey Form App with Flask/templates/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
Base View Template
-->

<!DOCTYPE html>

<html>

<head>
<meta name="viewport" content="initial-scale=1, width=device-width" />

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

<link href="/static/styles.css" rel="stylesheet" />

<title>Survey</title>
</head>

<body>
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand mb-1 h1" href="/">Survey</a>
<button aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler"
type="button" data-target="#navbarNav" data-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/form">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/sheet">Sheet</a>
</li>
</ul>
</div>
</nav>

<main class="container-fluid p-5">
{% block main %}{% endblock %}
</main>
</body>

</html>
Loading