272 lines
13 KiB
Python
272 lines
13 KiB
Python
import json
|
|
from surveyapp import mongo
|
|
from flask import Flask, render_template, url_for, request, Blueprint, flash, redirect, abort
|
|
from flask_login import login_required, current_user
|
|
from surveyapp.graphs.forms import BarPieForm, ScatterchartForm, HistogramForm, MapForm, BoxForm
|
|
from bson.objectid import ObjectId
|
|
|
|
from surveyapp.graphs.utils import save_image, delete_image
|
|
from surveyapp.surveys.utils import parse_data, read_file
|
|
|
|
|
|
graphs = Blueprint("graphs", __name__)
|
|
|
|
|
|
class JSONEncoder(json.JSONEncoder):
|
|
def default(self, o):
|
|
if isinstance(o, ObjectId):
|
|
return str(o)
|
|
return json.JSONEncoder.default(self, o)
|
|
|
|
|
|
# Page where user chooses the type of graph they would like to create for their survey
|
|
@graphs.route('/choosegraph/<survey_id>', methods=['GET'])
|
|
@login_required
|
|
def choose_graph(survey_id):
|
|
file_obj = mongo.db.surveys.find_one_or_404({"_id":ObjectId(survey_id)})
|
|
if file_obj["user"] != current_user._id:
|
|
flash("You do not have access to that page", "danger")
|
|
return redirect(url_for("main.index"))
|
|
return render_template("graphs/choosegraph.html", title="Select Graph", survey_id=survey_id)
|
|
|
|
|
|
|
|
@graphs.route('/graph/<survey_id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
def graph(survey_id):
|
|
# Get the file object so that we can load the data
|
|
file_obj = mongo.db.surveys.find_one_or_404({"_id":ObjectId(survey_id)})
|
|
if file_obj["user"] != current_user._id:
|
|
flash("You do not have access to that page", "danger")
|
|
return redirect(url_for("main.index"))
|
|
# Get the id of the graph (if it exists yet)
|
|
graph_id = request.args.get("graph_id")
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
# i.e. if user is choosing to edit an existing graph then it already has a type
|
|
if graph_obj:
|
|
chart_type = graph_obj["type"]
|
|
# Else user is creating a new graph of a chosen type
|
|
else:
|
|
chart_type = request.args.get("chart_type")
|
|
# Read the csv file in
|
|
df = read_file(file_obj["fileName"])
|
|
# parse the columns to get information regarding type of data
|
|
column_info = parse_data(df)
|
|
# Convert the dataframe to a dict of records to be handled by D3.js on the client side.
|
|
chart_data = df.to_dict(orient='records')
|
|
# ----------SAME ROUTE USED FOR BAR AND PIE CHART----------
|
|
if chart_type == "Bar chart" or chart_type == "Pie chart":
|
|
return pie_bar_chart(survey_id, column_info, chart_data, graph_id, file_obj["title"], chart_type)
|
|
# ----------SCATTER CHART----------
|
|
elif chart_type == "Scatter chart":
|
|
return scatter_chart(survey_id, column_info, chart_data, graph_id, file_obj["title"])
|
|
# ----------HISTOGRAM----------
|
|
elif chart_type == "Histogram":
|
|
return histogram(survey_id, column_info, chart_data, graph_id, file_obj["title"])
|
|
# ----------MAP CHART----------
|
|
elif chart_type == "Map":
|
|
return map_chart(survey_id, column_info, chart_data, graph_id, file_obj["title"])
|
|
# ----------Box and whisker CHART----------
|
|
elif chart_type == "Box and whisker":
|
|
return box_chart(survey_id, column_info, chart_data, graph_id, file_obj["title"])
|
|
else:
|
|
flash("something went wrong", "danger")
|
|
abort(404)
|
|
|
|
|
|
|
|
|
|
# Function that renders the box-chart page
|
|
def box_chart(survey_id, column_info, chart_data, graph_id, title):
|
|
form = BoxForm()
|
|
# Populate the form options. A box chart can take any data type for x-axis but y-axis must be numerical
|
|
for column in column_info:
|
|
form.x_axis.choices.append((column["title"], column["title"]))
|
|
if column["data_type"] == "numerical":
|
|
# We insert a tuple, The first is the 'value' of the select, the second is the text displayed
|
|
form.y_axis.choices.append((column["title"], column["title"]))
|
|
# Now we have specified the 'select' options for the form, we can check 'form.validate_on_submit'
|
|
if form.validate_on_submit():
|
|
image_data = request.form["image"]
|
|
file_name = save_image(image_data, graph_id)
|
|
# setting upsert=true in the update will create the entry if it doesn't yet exist, else it updates
|
|
mongo.db.graphs.update_one({"_id": ObjectId(graph_id)},
|
|
{"$set": {"title" : form.title.data,
|
|
"surveyId": survey_id,
|
|
"user" : current_user._id,
|
|
"type" : "Box and whisker",
|
|
"xAxis" : form.x_axis.data,
|
|
"yAxis": form.y_axis.data,
|
|
"image": file_name}}, upsert=True)
|
|
# If we are editing the graph instead of creating new, we want to prepopulate the fields
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
if graph_obj:
|
|
form.x_axis.data = graph_obj["xAxis"]
|
|
form.y_axis.data = graph_obj["yAxis"]
|
|
form.title.data = graph_obj["title"]
|
|
data = {"chart_data": chart_data, "title": title, "column_info" : column_info}
|
|
return render_template("graphs/boxchart.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Box and whisker")
|
|
|
|
|
|
|
|
# Function that renders the map page
|
|
def map_chart(survey_id, column_info, chart_data, graph_id, title):
|
|
form = MapForm()
|
|
# Populate the form options.
|
|
for column in column_info:
|
|
form.variable.choices.append((column["title"], column["title"]))
|
|
# Now we have specified the 'select' options for the form, we can check 'form.validate_on_submit'
|
|
if form.validate_on_submit():
|
|
image_data = request.form["image"]
|
|
file_name = save_image(image_data, graph_id)
|
|
# setting upsert=true in the update will create the entry if it doesn't yet exist, else it updates
|
|
mongo.db.graphs.update_one({"_id": ObjectId(graph_id)},
|
|
{"$set": {"title" : form.title.data,
|
|
"surveyId": survey_id,
|
|
"user" : current_user._id,
|
|
"type" : "Map",
|
|
"variable" : form.variable.data,
|
|
"scope" : form.scope.data,
|
|
"image": file_name}}, upsert=True)
|
|
|
|
# If we are editing the graph instead of creating new, we want to prepopulate the fields
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
if graph_obj:
|
|
form.variable.data = graph_obj["variable"]
|
|
form.scope.data = graph_obj["scope"]
|
|
form.title.data = graph_obj["title"]
|
|
data = {"chart_data": chart_data, "title": title, "column_info" : column_info}
|
|
return render_template("graphs/map.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Map")
|
|
|
|
|
|
|
|
# Function that renders the bar-chart and pie chart pages
|
|
def pie_bar_chart(survey_id, column_info, chart_data, graph_id, title, chart_type):
|
|
form = BarPieForm()
|
|
# Populate the form options. A bar/pie chart can take any data type for x-axis but y-axis must be numerical
|
|
for column in column_info:
|
|
form.x_axis.choices.append((column["title"], column["title"]))
|
|
if column["data_type"] == "numerical":
|
|
# We insert a tuple, The first is the 'value' of the select, the second is the text displayed
|
|
form.y_axis.choices.append((column["title"], column["title"]))
|
|
# Now we have specified the 'select' options for the form, we can check 'form.validate_on_submit'
|
|
if form.validate_on_submit():
|
|
image_data = request.form["image"]
|
|
file_name = save_image(image_data, graph_id)
|
|
# setting upsert=true in the update will create the entry if it doesn't yet exist, else it updates
|
|
mongo.db.graphs.update_one({"_id": ObjectId(graph_id)},
|
|
{"$set": {"title" : form.title.data,
|
|
"surveyId": survey_id,
|
|
"user" : current_user._id,
|
|
"type" : chart_type,
|
|
"xAxis" : form.x_axis.data,
|
|
"yAxis": form.y_axis.data,
|
|
"yAggregation": form.y_axis_agg.data,
|
|
"image": file_name}}, upsert=True)
|
|
|
|
# If we are editing the graph instead of creating new, we want to prepopulate the fields
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
if graph_obj:
|
|
form.x_axis.data = graph_obj["xAxis"]
|
|
form.y_axis.data = graph_obj["yAxis"]
|
|
form.y_axis_agg.data = graph_obj["yAggregation"]
|
|
form.title.data = graph_obj["title"]
|
|
data = {"chart_data": chart_data, "title": title, "column_info" : column_info}
|
|
if chart_type == "Bar chart":
|
|
return render_template("graphs/barchart.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Bar chart")
|
|
else:
|
|
return render_template("graphs/piechart.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Pie chart")
|
|
|
|
|
|
|
|
def scatter_chart(survey_id, column_info, chart_data, graph_id, title):
|
|
form = ScatterchartForm()
|
|
for column in column_info:
|
|
# Scatter charts require both x and y axis to have some numerical value (i.e. ordinal/interval but not categorical)
|
|
if column["data_type"] == "numerical":
|
|
# We insert a tuple, The first is the 'value' of the select, the second is the text displayed
|
|
form.x_axis.choices.append((column["title"], column["title"]))
|
|
form.y_axis.choices.append((column["title"], column["title"]))
|
|
# Now we have specified the 'select' options for the form, we can prevalidate for 'form.validate_on_submit'
|
|
if form.validate_on_submit():
|
|
image_data = request.form["image"]
|
|
file_name = save_image(image_data, graph_id)
|
|
# setting upsert=true in the update will create the entry if it doesn't yet exist, else it updates
|
|
mongo.db.graphs.update_one({"_id": ObjectId(graph_id)},
|
|
{"$set": {"title" : form.title.data,
|
|
"surveyId": survey_id,
|
|
"user" : current_user._id,
|
|
"type" : "Scatter chart",
|
|
"xAxis" : form.x_axis.data,
|
|
"xAxisFrom" : form.x_axis_from.data,
|
|
"xAxisTo" : form.x_axis_to.data,
|
|
"yAxis": form.y_axis.data,
|
|
"yAxisFrom": form.y_axis_from.data,
|
|
"yAxisTo": form.y_axis_to.data,
|
|
"line": form.line.data,
|
|
"image": file_name}}, upsert=True)
|
|
|
|
# If we are editing the graph instead of creating new, we want to prepopulate the fields
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
if graph_obj:
|
|
form.x_axis.data = graph_obj["xAxis"]
|
|
form.x_axis_from.data = graph_obj["xAxisFrom"]
|
|
form.x_axis_to.data = graph_obj["xAxisTo"]
|
|
form.y_axis.data = graph_obj["yAxis"]
|
|
form.y_axis_from.data = graph_obj["yAxisFrom"]
|
|
form.y_axis_to.data = graph_obj["yAxisTo"]
|
|
form.line.data = graph_obj["line"]
|
|
form.title.data = graph_obj["title"]
|
|
data = {"chart_data": chart_data, "title": title, "column_info" : column_info}
|
|
return render_template("graphs/scatterchart.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Scatter chart")
|
|
|
|
|
|
|
|
|
|
def histogram(survey_id, column_info, chart_data, graph_id, title):
|
|
form = HistogramForm()
|
|
for column in column_info:
|
|
# Scatter charts require both x and y axis to have some numerical value (i.e. ordinal/interval but not categorical)
|
|
if column["data_type"] == "numerical":
|
|
# We insert a tuple, The first is the 'value' of the select, the second is the text displayed
|
|
form.x_axis.choices.append((column["title"], column["title"]))
|
|
# Now we have specified the 'select' options for the form, we can prevalidate for 'form.validate_on_submit'
|
|
if form.validate_on_submit():
|
|
image_data = request.form["image"]
|
|
file_name = save_image(image_data, graph_id)
|
|
# setting upsert=true in the update will create the entry if it doesn't yet exist, else it updates
|
|
mongo.db.graphs.update_one({"_id": ObjectId(graph_id)},
|
|
{"$set": {"title" : form.title.data,
|
|
"surveyId": survey_id,
|
|
"user" : current_user._id,
|
|
"type" : "Histogram",
|
|
"xAxis" : form.x_axis.data,
|
|
"xAxisFrom" : form.x_axis_from.data,
|
|
"xAxisTo" : form.x_axis_to.data,
|
|
"groupSize" : form.group_count.data,
|
|
"image": file_name}}, upsert=True)
|
|
|
|
# If we are editing the graph instead of creating new, we want to prepopulate the fields
|
|
graph_obj = mongo.db.graphs.find_one({"_id":ObjectId(graph_id)})
|
|
if graph_obj:
|
|
form.x_axis.data = graph_obj["xAxis"]
|
|
form.x_axis_from.data = graph_obj["xAxisFrom"]
|
|
form.x_axis_to.data = graph_obj["xAxisTo"]
|
|
form.group_count.data = graph_obj["groupSize"]
|
|
form.title.data = graph_obj["title"]
|
|
data = {"chart_data": chart_data, "title": title, "column_info" : column_info}
|
|
return render_template("graphs/histogram.html", data=data, form=form, survey_id=survey_id, graph_id=graph_id, chart_type="Histogram")
|
|
|
|
|
|
# DELETE A Graph
|
|
@graphs.route("/home/<survey_id>/<graph_id>/delete", methods=['POST'])
|
|
@login_required
|
|
def delete_graph(graph_id, survey_id):
|
|
graph_obj = mongo.db.graphs.find_one_or_404({"_id":ObjectId(graph_id)})
|
|
if graph_obj["user"] != current_user._id:
|
|
flash("You do not have access to that page", "danger")
|
|
abort(403)
|
|
delete_image(graph_obj["image"])
|
|
mongo.db.graphs.delete_one(graph_obj)
|
|
return redirect(url_for('surveys.dashboard', survey_id=survey_id))
|