Compare commits

13 Commits

Author SHA1 Message Date
7630cb80aa Opted for timeout/inactivity to clear the session rather than closing the tab/window. That should do for the time being. Need to add the client path sessions to when the ession is created. Currently, it will only delete the old files if the user clicks templates first. If they don't and files exist... they may not get deleted. 2023-03-17 17:22:18 -04:00
b0addb6475 Figured out brackets regex. Need to make sure there are no other special characters that put this function at risk. But it now deletes upon clearing session. 2023-03-16 21:36:01 -04:00
44259a8c51 Clear session now deletes all the templates. Added socketio disconnect function too. Need to remove special characters from filepath since glob can't recognize. 2023-03-16 17:36:10 -04:00
dae2eb440e Code-Input was updated to 1.2.2 and currently works! I have the upload functionality working and have begun working on an undo button. It currently saves all the files to a dir everytime you access /templates but I haven't implemented a full undo yet. 2023-03-15 17:07:34 -04:00
8afdec139b So close! I'm able to use syntax highlighting AND grab the content with js (which then adds to a textarea), but the Flask for loop is only noticed on the first iteration. I can't grab the subsequent values. I need to find a way to dynamically find IDs. 2023-03-13 17:23:56 -04:00
a435a66b81 Figured out Templatesflask --debug run People can now change code and upload it without needing a code editor. 2023-03-10 17:59:16 -05:00
43cdcff115 CSV & Manual Upload is working! Fixed both features. 2023-03-09 15:15:42 -05:00
030c5541cf Finished the manual upload and now working on CSV 2023-03-08 16:11:32 -05:00
9e3ac00285 Good progress. Bulk adds are working for emails and groups seperately, but not together. I think I need to for loop through each item to make it work. 2023-03-07 21:35:44 -05:00
1cfc4fd9d1 Added instructions for the bulk add page. Starting to consolidate. Offloaded some get functions to a backup file that I don't need right now. 2023-03-07 17:52:36 -05:00
49de321387 Figured out that I can use a single endpoint for a tons of additionalPOST requests, which has opened up a lot of doors. Started to create a robust add option. Also added a template button as an idea, but need to see what's possible 2023-03-07 14:56:01 -05:00
92f4ca2fbc Starting to move the header down for better functionality. need to separate CSV submission. 2023-03-07 09:59:27 -05:00
8d0815b676 Lots of changed. merging csv upload with main options page. 2023-03-06 09:33:42 -05:00
34 changed files with 976 additions and 560 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@ -5,7 +5,9 @@ app = Flask(__name__)
app.config.from_object(Config)
# Upload folder
UPLOAD_FOLDER = 'static/files'
app.config['UPLOAD_FOLDER'] =UPLOAD_FOLDER
#UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files"
# UPLOAD_FOLDER = 'static/files'
#app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
#ALLOWED_EXTENSIONS = {"csv"}
from app import routes

Binary file not shown.

View File

@ -1,8 +1,55 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
from wtforms.fields import (SubmitField,
PasswordField,
StringField,
TextAreaField,
IntegerField,
BooleanField,
RadioField)
from wtforms.validators import InputRequired, Length
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
from flask_codemirror.fields import CodeMirrorField
class RequestForm(FlaskForm):
apikey = StringField("Academy API Key", validators=[DataRequired()])
submit = SubmitField("Submit")
class ApiKey(FlaskForm):
api_key = PasswordField('Api Key',
validators=[InputRequired(),
Length(min=20, max=25)]
)
class TemplateForm(FlaskForm):
name = StringField("Template File Name",
validators=[InputRequired()])
body = TextAreaField("Template Code",
validators=[InputRequired()])
submit = SubmitField('Upload Templates')
# template_code = CodeMirrorField( language='htmlembedded',
# config={'lineNumbers': 'true'})
class CsvForm(FlaskForm):
file = FileField(validators=[FileRequired()])
all_or_some = RadioField("All or Some",
choices=['All learners in all groups',
'Learners only in adjacent groups'],
validators=[InputRequired()])
class CourseForm(FlaskForm):
title = StringField('Title',
validators=[InputRequired(),
Length(min=10, max=100)])
description = TextAreaField('Course Description',
validators=[InputRequired(),
Length(max=200)])
price = IntegerField('Price', validators=[InputRequired()])
level = RadioField('Level',
choices=['Beginner', 'Intermediate', 'Advanced'],
validators=[InputRequired()])
available = BooleanField('Available', default='checked')

View File

@ -1,101 +1,49 @@
import requests
import shutil
import itertools
import pandas as pd
import re
import os
import csv
import glob
from app import app
from flask import redirect, flash, request, render_template, session, make_response
# import eventlet
from datetime import datetime, timezone, timedelta
# from flask_socketio import SocketIO
# from flask_session import Session
from functools import wraps
import flask
from flask import (
redirect,
flash,
request,
render_template,
session,
make_response,
url_for,
g,
)
from werkzeug.utils import secure_filename
from app import app
# Upload folder
UPLOAD_FOLDER = '/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files'
# UPLOAD_FOLDER = 'static/files'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
ALLOWED_EXTENSIONS = {'csv'}
UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/csv"
TEMPLATES_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/templates/"
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
app.config["TEMPLATES_FOLDER"] = TEMPLATES_FOLDER
ALLOWED_EXTENSIONS = {"csv"}
#SESSION_PERMANENT = False
#PERMANENT_SESSION_LIFETIME = 1800
app.config.update(SECRET_KEY=os.urandom(24))
app.permanent_session_lifetime = timedelta(minutes=5)
# flask.session.modified = True
# Global Variables
#eventlet.monkey_patch()
#socketio = SocketIO(app)
specials = '"!@#$%^&*()-+?_=,<>/"'
url = "https://api.northpass.com/"
def key_response(response):
if "402" in str(response):
error = response.text
return render_template("index.html", title="Error Home", error=error)
if "401" in str(response):
error = [
"Unauthorized access error.",
"This can mean a lot of things,",
"such as the key being changed.",
"Remember, they are paired to each educator!",
]
return render_template("index.html", title="Error Home", error=error)
return correct_key(response)
def correct_key(response):
data = response.json()
session["school"] = data["data"]["attributes"]["properties"]["name"]
print(session["school"])
return render_template("options.html", title="Options")
@app.route("/", methods=["GET", "POST"])
def ask_key():
"""This is the main function that asks for the API Key.
Without this key, no other functions will work.
It also assigns the api key to the session and clears the session upon each reload.
"""
session.clear()
if request.method == "POST":
session["key"] = request.form.get("apikey")
#if re.search(r"\s", session["key"]):
# error = "Hm. That doesn't seem right"
# return render_template("index.html", title="Home", error=error)
if session["key"] is not None and len(session["key"]) > 10:
url = "https://api.northpass.com/v2/properties/school"
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
response = requests.get(url, headers=headers)
return key_response(response)
error = "Hm. That doesn't seem right"
return render_template("index.html", title="Home", error=error)
return render_template("index.html", title="Home")
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/csv", methods=["GET", "POST"])
def parse_csv():
csvData = pd.DataFrame()
if request.method == 'POST':
if 'file' not in request.files:
flash('No file found or uploaded')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No file found or uploaded')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(file_path)
csvData = pd.read_csv(file_path, usecols = ['Email'], index_col=False)
html_data = csvData.to_html()
return render_template("csv.html", table=html_data, title="Uploaded File")
return render_template("csv.html", title="Upload")
@app.route("/", methods=["GET", "POST"])
def render_home():
return render_template("index.html", title="Home")
@app.route("/table")
def table():
return render_template("table.html", tables=[session["dfhtml"]], titles=["Table"])
@app.route("/downloadcsv", methods=["GET", "POST"])
def download_csv():
if request.method == "GET":
download = make_response(session["dfcsv"])
@ -104,278 +52,424 @@ def download_csv():
return download
@app.route("/get_courses", methods=["GET", "POST"])
def get_courses():
array = []
course_dict = {}
pd.set_option("display.max_colwidth", 100)
count = 0
dataframe = pd.DataFrame()
def key_response(response):
if "402" in str(response):
error = response.text
return render_template("index.html", title="Error Home", error=error)
if "401" in str(response):
error = "Unauthorized access error.(401)"
return render_template("index.html", title="Error Home", error=error)
return correct_key(response)
def correct_key(response):
data = response.json()
session["raw_school"] = data["data"]["attributes"]["properties"]["name"]
session["sani_school"] = session["raw_school"].replace('[','').replace(']', '')
return render_template("home.html", title="Active Session")
def allowed_file(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
def key_required(check):
@wraps(check)
def decorated_function(*args, **kwargs):
if session.get("key") is None:
return redirect("/", code=302)
return check(*args, **kwargs)
return decorated_function
@app.route("/", methods=["GET", "POST"])
def ask_key():
"""This is the main function that asks for the API Key.
Without this key, no other functions will work.
It also assigns the api key to the session and clears the session upon each reload.
"""
if request.method == "POST":
while True:
count += 1
url = f"https://api.northpass.com/v2/courses?page={count}"
session["key"] = request.form.get("apikey")
if any(char in specials for char in session["key"]) or re.search(
r"[\s]", session["key"]
):
error = "Invalid Key."
session.clear()
return render_template("index.html", title="Home", error=error)
if session["key"] is not None and len(session["key"]) > 10:
endpoint = "/v2/properties/school"
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
response = requests.get(url, headers=headers)
data = response.json()
nextlink = data["links"]
for response in data["data"]:
uuid = response["id"]
course_dict = {"id": uuid}
for keys, values in response["attributes"].items():
course_dict[keys] = values
array.append(course_dict)
dataframe = pd.DataFrame(array).drop(
["list_image_url", "permalink"], axis=1
)
dataframe["full_description"] = dataframe[
"full_description"
].str.replace(r"<[^<>]*>", "", regex=True)
print(dataframe)
if "next" not in nextlink:
break
dfcourse = dataframe.to_html()
session["dfcsv"] = dataframe.to_csv()
return render_template("get.html", table=dfcourse, title="List of Courses")
else:
return "This isn't working. Let's go our own way."
response = requests.get(url + endpoint, headers=headers)
return key_response(response)
error = "Hm. That doesn't seem right"
session.clear()
return render_template("index.html", title="Home", error=error)
session.clear()
return render_template("index.html", title="Home")
@app.route("/get_people", methods=["GET", "POST"])
def get_people():
array = []
ppl_dict = {}
count = 0
dataframe = pd.DataFrame()
@app.route("/render_home", methods=["GET", "POST"])
@key_required
def render_home():
if session.get("key"):
return render_template("home.html", title="Home")
return render_template("home.html", title="Enter Key")
@app.route("/clear_session", methods=["GET", "POST"])
def clear_session():
if session.get("key"):
if session.get("client_path"):
client = session["client_path"]
print(client)
wildcard = glob.glob(client + '_*')
print(wildcard)
for directory in wildcard:
print(directory)
try:
shutil.rmtree(directory)
print(directory)
except OSError:
print(OSError)
print("Error?")
session.clear()
error = "Session Cleared!"
return render_template("index.html", error=error, title="Home, New session")
return render_template("index.html", title="Home, New session")
@app.route("/table")
def table():
return render_template("table.html", tables=[session["table"]], titles=["Table"])
@app.route("/upload_file", methods=["GET", "POST"])
@key_required
def upload_file():
if request.method == "POST":
while True:
count += 1
url = f"https://api.northpass.com/v2/people?page={count}"
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
response = requests.get(url, headers=headers)
data = response.json()
nextlink = data["links"]
for response in data["data"]:
uuid = response["id"]
ppl_dict = {"id": uuid}
for keys, values in response["attributes"].items():
ppl_dict[keys] = values
array.append(ppl_dict)
dataframe = pd.DataFrame(array).drop("custom_avatar_url", axis=1)
print(dataframe)
if "next" not in nextlink:
break
dfppl = dataframe.to_html()
session["dfcsv"] = dataframe.to_csv()
return render_template("get.html", table=dfppl, title="List of People")
else:
return "what what"
if "file" not in request.files:
flash("No file found or uploaded")
return redirect(url_for("bulk_add_opts"))
file = request.files["file"]
if file.filename == "":
print("no file exists")
flash("No file found or uploaded")
return redirect(url_for("home"))
# return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
session["file"] = filename
session["filepath"] = file_path
file.save(file_path)
file = list(csv.reader(open(file_path, "r")))
return divide_values(file)
return render_template("home.html", title="Bulk Add")
@app.route("/add_ppl_opts", methods=["POST"])
def add_ppl_opts():
array = []
dict_response = {}
dataframe = pd.DataFrame()
count = 0
def divide_values(file):
emails = []
groups = []
selection = request.form.get("learner-groups")
if request.form["submit"]:
if selection == "all-groups":
for item in file[1:]:
emails.append(item[0])
groups.append(item[1:])
# FEAT: These two extract the groups and emails into two lists
groups = [item for group in groups for item in group]
groups = list(set(groups))
return api_csv_parse(emails, groups)
# We're good here. This can now be sent to the api
# functions with emails and groups.
elif selection == "some-groups":
submissions = []
for item in file[1:]:
# FEAT: This extracts each row as a list. Perfect for Learners in Specific Groups.
submissions.append(item)
for item in submissions:
emails.append(item[0])
print(type(emails))
groups = item[1:]
return api_csv_parse(emails, groups)
return emails
if request.method == "POST":
while True:
count += 1
url = f"https://api.northpass.com/v2/groups?page={count}"
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
response = requests.get(url, headers=headers)
data = response.json()
nextlink = data["links"]
if request.form["preview"]:
error = "Preview Button Still Under Construction. Try again later."
return render_template("bulk_add.html", error=error, title="Preview Not Yet")
for response in data["data"]:
uuid = response["id"]
dict_response = {"id": uuid}
for keys, values in response["attributes"].items():
dict_response[keys] = values
array.append(dict_response)
dataframe = pd.DataFrame(array).drop("group_enrollment_link", axis=1)
print(dataframe)
if "next" not in nextlink:
break
dfgroups = dataframe.to_html()
session["dfcsv"] = dataframe.to_csv()
return render_template(
"bulk_add_ppl.html", table=dfgroups, titles="Bulk Add Learners"
)
else:
return "This isn't working. Let's go our own way."
return render_template("bulk_add.html", title="Uploaded File")
@app.route("/bulk_add_ppl", methods=["GET", "POST"])
def bulk_add_ppl():
def api_csv_parse(emails, groups):
if emails and groups:
return api_add_ppl_groups(emails, groups)
elif emails:
return api_add_ppl(emails)
elif groups:
return api_add_groups(groups)
return render_template("bulk_add.html", title="CSV Submission")
@app.route("/bulk_add_opts", methods=["GET", "POST"])
@key_required
def bulk_add_opts():
return render_template("bulk_add.html", titles="Bulk Add Options")
@app.route("/bulk_add", methods=["GET", "POST"])
@key_required
def bulk_add():
if request.method == "POST":
emails = request.form.get("emails")
groups = request.form.get("groups")
emails.split(",")
groups.split(",")
if emails:
if "\n" in emails:
emails = emails.split("\n")
emails = [email.strip() for email in emails]
emails = [re.sub(r"[,]", "", email) for email in emails]
elif "," in emails:
emails = emails.split(",")
emails = [email.strip() for email in emails]
else:
emails = emails.split()
url = "https://api.northpass.com/v2/bulk/people"
combinations = list(itertools.product(emails, groups))
print(combinations)
payload = {
"data": {"attributes": {"people": [{"email": emails, "groups": groups}]}}
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
response = requests.post(url, json=payload, headers=headers)
response = str(response)
if "202" in response:
error = "Success! People have been added successfully."
return render_template(
"bulk_add_ppl.html",
table=session["dfgroups"],
title="People Added",
error=error,
)
elif "403" in response:
error = "Uh oh. Looks like you don't have appropriate privileges."
elif "422" in response:
error = "Hm. Looks like something was wrong with the names."
return render_template(
"bulk_add_people.html",
table=session["dfgroups"],
title="People Added",
error=error,
)
else:
error = "Shrug"
return render_template("bulk_add_ppl.html", title="Shrug", error=error)
if groups:
if "\n" in groups:
groups = groups.split("\n")
groups = [group.strip() for group in groups]
groups = [re.sub(r"[,]", "", group) for group in groups]
elif "," in groups:
groups = groups.split(",")
groups = [group.strip() for group in groups]
else:
groups = groups.split()
if emails and groups:
return api_add_ppl_groups(emails, groups)
elif emails:
return api_add_ppl(emails)
elif groups:
return api_add_groups(groups)
return render_template("bulk_add.html")
@app.route("/add_groups_opts", methods=["POST"])
def add_groups_opts():
array = []
dict_response = {}
count = 0
dataframe = pd.DataFrame()
if request.method == "POST":
while True:
count += 1
url = f"https://api.northpass.com/v2/groups?page={count}"
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
response = requests.get(url, headers=headers)
data = response.json()
nextlink = data["links"]
for response in data["data"]:
uuid = response["id"]
dict_response = {"id": uuid}
for keys, values in response["attributes"].items():
dict_response[keys] = values
array.append(dict_response)
dataframe = pd.DataFrame(array).drop("group_enrollment_link", axis=1)
print(dataframe)
if "next" not in nextlink:
break
session["dfgroups"] = dataframe.to_html()
session["dfcsv"] = dataframe.to_csv()
return render_template(
"bulk_add_groups.html", table=session["dfgroups"], titles="Bulk Add Groups"
)
else:
return "This isn't working. Let's go our own way."
@app.route("/bulk_add_groups", methods=["GET", "POST"])
def bulk_add_groups():
grouparr = []
count = 0
if request.method == "POST":
groups = request.form.get("groups")
if '\n' in groups:
groups.split('\n')
groups = [group.strip() for group in groups]
elif ',' in groups:
groups.split(",")
groups = [group.strip() for group in groups]
for group in groups:
groupdict = {}
groupdict["name"] = group
grouparr.append(groupdict)
url = "https://api.northpass.com/v2/bulk/groups"
payload = {"data": {"attributes": {"groups": grouparr}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
response = requests.post(url, json=payload, headers=headers)
print(type(response))
response = str(response)
if "202" in response:
error = "Success! Groups have been added successfully."
return render_template(
"bulk_add_groups.html",
table=session["dfgroups"],
title="Groups Added",
error=error,
)
elif "403" in response:
error = [ "Uh oh. Looks like you're not the",
"admin or don't have appropriate privileges.",
"Please talk to your academy admin." ]
elif "422" in response:
error = ["Hm. Looks like something was wrong with the group names.",
"Reach out to the manager of this app."]
return render_template(
"bulk_add_groups.html",
table=session["dfgroups"],
title="Groups Added",
error=error,
)
else:
error = "Shrug"
return render_template("bulk_add_groups.html", title="Shrug", error=error)
@app.route("/ppl_to_groups_opts", methods=["GET", "POST"])
def ppl_to_groups_opts():
pass
@app.route("/ppl_to_groups", methods=["GET", "POST"])
def ppl_to_groups():
person_ids = []
group_ids = []
url = "https://api.northpass.com/v2/bulk/people/membership"
payload = {
"payload": {
"person_ids": person_ids,
"group_ids": group_ids,
}
}
def api_add_ppl(emails):
payload2 = []
endpoint = "v2/bulk/people"
for email in emails:
payload2.append({"email": email})
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
return payload
# response = requests.post(url + endpoint, json=payload, headers=headers)
# return check_response(response)
app.secret_key = "@&I\x1a?\xce\x94\xbb0w\x17\xbf&Y\xa2\xc2(A\xf5\xf2\x97\xba\xeb\xfa"
def api_add_groups(groups):
payload2 = []
endpoint = "v2/bulk/people"
for group in groups:
payload2.append({"groups": group})
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
return payload
# response = requests.post(url + endpoint, json=payload, headers=headers)
# return check_response(response)
if __name__ == "__main__":
ask_key()
def api_add_ppl_groups(emails, groups):
payload2 = []
endpoint = "v2/bulk/people"
combinations = list(itertools.product(emails, groups))
for combo in combinations:
payload2.append({"email": combo[0], "groups": combo[1]})
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
return payload
# response = requests.post(url + endpoint, json=payload, headers=headers)
# return check_response(response)
def check_response(response):
response = str(response)
if "202" in response:
error = "Success! People have been added successfully."
return render_template("bulk_add.html", title="People Added", error=error)
elif "403" in response:
error = "Uh oh. Looks like you don't have appropriate privileges."
return render_template("bulk_add.html", error=error)
elif "422" in response:
error = "Hm. Looks like something was wrong with the data you added."
return render_template("bulk_add.html", error=error)
else:
error = "Something went wrong, but I'm not sure what."
return render_template("bulk_add.html", title="Shrug", error=error)
@app.route("/load_templates", methods=["GET", "POST"])
@key_required
def load_templates():
count = 0
templates = []
while True:
count += 1
endpoint = "v2/custom_templates"
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
response = requests.get(url + endpoint, headers=headers)
data = response.json()
nextlink = data["links"]
for response in data["data"]:
last_updated = response["attributes"]["updated_at"].split("T")
full_updated = response["attributes"]["updated_at"]
g.full_updated = datetime.fromisoformat(full_updated)
last_updated = last_updated[0]
name, body, last_updated = (
response["attributes"]["name"],
response["attributes"]["body"],
last_updated,
)
templates.append((name, body, last_updated))
if "next" not in nextlink:
break
save_templates_backup(templates)
return render_template(
"templates.html",
title="Templates",
templates=templates,
)
return render_template("options.html")
@app.route("/templates", methods=["GET", "POST"])
@key_required
def templates():
if request.method == "POST":
if request.form["submit-template"]:
name = request.form.get("template_name")
body = request.form.get("body")
if body == "":
error = (
"Ooph. Looks like you didn't load the changes before submitting."
)
return render_template("templates.html", error=error)
else:
endpoint = "v2/custom_templates"
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-Api-Key": session["key"],
}
payload = {"custom_template": {"name": name, "body": body}}
response = requests.post(url + endpoint, json=payload, headers=headers)
return check_templates(response, name)
return load_templates()
def check_templates(response, name):
response = str(response)
if "201" in response:
error = (
f"Success! The {name} template was successfully uploaded for "
+ session["raw_school"]
+ "."
)
button = "Undo"
return render_template(
"templates.html", title="Templates Added", error=error, button=button
)
elif "403" in response:
error = "Uh oh. Looks like you don't have appropriate privileges."
return render_template("templates.html", error=error)
elif "404" in response:
error = "Hm. Looks like something was wrong in the templates."
return render_template("templates.html", error=error)
else:
error = "Something went wrong, but I'm not sure what."
return render_template("templates.html", title="Shrug", error=error)
def save_templates_backup(templates):
session["client_path"] = os.path.join(TEMPLATES_FOLDER, session["sani_school"])
print(session["client_path"])
today = datetime.now(timezone.utc)
if os.path.exists(session["client_path"]):
pass
else:
path = session["client_path"] + "_" + str(today)
os.mkdir(path)
for tupe in templates:
file_name = tupe[0] + ".liquid"
file_body = tupe[1]
complete_path = os.path.join(path, file_name)
with open(complete_path, "w+") as temp:
temp.write(file_body)
temp.close
@app.route("/undo_template", methods=["POST"])
@key_required
def undo_template():
if request.method == "POST":
if request.form["undo_templates"]:
pass
@app.route("/stop", methods=["POST"])
def stop():
print("stopping")
'''
@socketio.on('disconnect')
def client_disconnect():
clear_session()
print("Disconnected!")
@socketio.on('message')
def handle_message(data):
print('received message: ' + data)
@socketio.on('connect')
def test_connect():
print("connection established")
@app.before_request
def before_request_func():
print("before_request executing!")
@app.after_request
def after_request_func(response):
print("after_request executing!")
return response
'''
# flask.session.permanent = False
# app.permanent_session_lifetime = datetime.timedelta(minutes=20)
# flask.session.modified = True
# flask.secret_key = "@&I\x1a?\xce\x94\xbb0w\x17\xbf&Y\xa2\xc2(A\xf5\xf2\x97\xba\xeb\xfa"
# if __name__ == "__main__":
# socketio.run(app, debug=True)
# ask_key()

BIN
app/static/.DS_Store vendored Normal file

Binary file not shown.

4
app/static/css/prism.css Normal file
View File

@ -0,0 +1,4 @@
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+clike+javascript&plugins=line-numbers */
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}

7
app/static/css/prism.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/* Custom Variables | Color Scheme */
/* 1.0 - Foundational Styling */
:root {
--primary: #66C92D;
@ -49,6 +49,8 @@ html {
justify-content: center;
}
/* 1.1 - Dataframe and Table Styling */
input,
select {
width: 200px;
@ -79,6 +81,8 @@ select {
background: #CCD4D8;
}
/* Titles and Header Copy/Test */
h1 {
font-size: 4rem;
font-weight: bold;
@ -97,13 +101,15 @@ img {
height: 80px;
}
.main {
.logo-div {
margin: 4px;
flex-wrap: wrap;
padding: 25px;
justify-content: center;
}
/* 1.? - Header and Navigation Bar Styling */
.header {
margin: 0;
padding: 20px;
@ -118,20 +124,39 @@ img {
display: flex;
height: 48px;
padding: 8px 16px;
text-decoration: none;
}
.nav-link {
text-decoration: none;
}
li {
.instructions-list{
display:flex;
justify-content: space-around;
width: 100%;
}
.instructions-left
.instructions-right {
display: flex;
align-items: center;
}
li {
display: block;
}
.button-background {
margin-right: 8px;
}
.radio-options {
display: flex;
justify-content: inherit;
}
.navbutton {
border-radius: 4px;
margin-right: 4px;
@ -140,10 +165,32 @@ li {
cursor: pointer;
font-size: 14px;
line-height: 16px;
text-decoration: none;
padding: 8px 16px 8px 12px;
}
.man-csv-opts {
display: flex;
justify-content: space-evenly;
}
.csv-upload {
align-items: right;
}
.navoption {
color: var(--background);
text-decoration: none;
}
.window-body {
display: flex;
justify-content: space-around;
}
#templates {
width: 75%;
}
#currentDate {
color: var(--primary);
}
@ -153,15 +200,16 @@ li {
font-weight: 600;
}
.header {
display: flex;
justify-content: space-evenly;
}
@media screen and (max-width: 1250px) {
h1 {
display: flex;
}
.header {
flex-direction: column-reverse;
}
#currentTime {
font-size: 3rem;
margin-bottom: 1rem;
@ -172,9 +220,12 @@ li {
}
}
ul {
display: flex;
display: block;
text-align: center;
}
/* 1.? - Card Layout in options.html only */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@ -190,28 +241,6 @@ ul {
border: 1px solid #66C92D;
color: #FFFFFF;
}
/*
.card:link,
.card:visited {
color: white;
text-decoration: none;
margin: 1.2rem;
padding: 4rem 8rem;
background-color: var(--background-light);
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
cursor: pointer;
position: relative;
outline: none;
transition: 0.1s;
}*/
.card:hover,
.card:focus {
@ -222,7 +251,7 @@ ul {
bottom: 0;
}
.card:hover > .card__name {
.card:checked > .card__name {
bottom: 0;
}
@ -239,3 +268,70 @@ ul {
left: 50%;
transition: 0.1s;
}
/* Styling for the options2.html file only */
.fields {
display: grid;
width: 60px; height: 40px; margin: 0;
appearance: none; -webkit-appearance: none;
cursor: pointer;
background: var(--background-dark);
border-radius: 20px;
}
input:not(:nth-of-type(4n+1))::before,
input:nth-of-type(n+5)::after {
content: '';
border-radius: 20px;
pointer-events: none;
grid-area: 1/1;
}
input:not(:nth-of-type(4n+1))::before { transform: translatex(-85px); }
input:nth-of-type(n+5)::after { transform: translatey(-60px); }
input:checked { background: limegreen; }
/* a checked box's right borders */
input:not(:nth-of-type(4n)):checked + input:checked::before {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background: limegreen;
}
/* a checked box's bottom borders */
input:nth-last-of-type(n+5):checked + * + * + * + input:checked::after {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
background: limegreen;
}
/* a checked box's adjacent (rightside) checked box's left borders */
input:not(:nth-of-type(4n)):checked + input:checked + input::before {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
background: limegreen;
}
/* a checked box's adjacent (below) checked box's top borders */
input:not(:nth-of-type(4n)):checked + * + * + * + input:checked + input::before {
border-top-left-radius: 0;
border-top-right-radius: 0;
background: limegreen;
}
/* a checked box's (in last column) left borders */
input:nth-of-type(4n-1):checked + input:checked {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* a checked box's (in last column) adjacent (below) checked box's top borders */
input:nth-of-type(4n):checked + * + * + * + input:checked {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.selections {
display: grid;
grid: repeat(5, 60px) / repeat(4, 85px);
align-items: center; justify-items: center;
margin: 0;
}

View File

@ -0,0 +1,4 @@
Email,Groups
norm+72@northpass.com,All Apologies,Come as you are
norm+90@northpass.com,Come as you are
norm+98@northpass.com,The Pines,The Sea,An Ocean
1 Email,Groups
2 norm+72@northpass.com,All Apologies,Come as you are
3 norm+90@northpass.com,Come as you are
4 norm+98@northpass.com,The Pines,The Sea,An Ocean

View File

@ -0,0 +1,4 @@
Email,Groups,
norm+72@northpass.com,All Apologies,Come As You Are
norm+90@northpass.com,Come As You Are,
norm+98@northpass.com,Unplugged,
1 Email Groups
2 norm+72@northpass.com All Apologies Come As You Are
3 norm+90@northpass.com Come As You Are
4 norm+98@northpass.com Unplugged

View File

@ -1,40 +0,0 @@
document.addEventListener("DOMContentLoaded", function() {
getAllGroups();
});
const apiKey = 'session["key"]';
const groups= [];
const getAllGroups = async (num) => {
if(num === 1){
}
let page = num;
await axios({
method: 'get',
url: `https://api.northpass.com/v2/groups?page=${page}`,
headers: {
'accept': '*/*',
'x-api-key': apiKey,
'content-type': 'application/json'
}
})
.then(async (res) => {
if (res.data.links.next != null) {
page++;
for (let i = 0; i < res.data.data.length; i++) {
let groupName = res.data.data[i].attributes.name;
selectInput = '<option value='+ groupName +'>'+ groupName+'</option>';
$('#groups').append(selectInput);
groups.push(res.data.data[i].attributes.name);
}
await getAllGroups(page);
} else {
for (let i = 0; i < res.data.data.length; i++) {
groups.push(res.data.data[i].attributes.name);
}
}
})
.catch(err => {
console.log(err);
})
}

View File

@ -0,0 +1,62 @@
{% block content %}
<p></p>
</div>
<div class="card-grid">
<form class="card"
id="get_people"
action="{{ url_for('get_people')}}"
method="post">
<a class="a-card"
onclick="document.forms['get_people'].submit()"
style="cursor:pointer;">
<i class="ri-car-line card__icon"></i>
<p class="card__name">Get People</p>
</a></form>
<form class="card"
id="get_courses"
action="{{ url_for('get_courses')}}"
method="post">
<a class="a-card"
onclick="document.forms['get_courses'].submit()"
style="cursor:pointer;">
<i class="ri-plane-line card__icon"></i>
<p class="card__name">Get Courses</p>
</a></form>
<form class="card"
id="bulk_add_ppl_opts"
action="{{ url_for('bulk_add_ppl_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['bulk_add_ppl_opts'].submit()"
style="cursor:pointer;">
<i class="ri-send-plane-line card__icon"></i>
<p class="card__name">Bulk Add People</p>
</a></form>
<form class="card"
id="add_groups_opts"
action="{{ url_for('bulk_add_groups_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['bulk_add_groups_opts'].submit()"
style="cursor:pointer;">
<i class="ri-shape-2-line card__icon"></i>
<p class="card__name">Bulk Add Groups</p>
</a></form>
<form class="card"
id="ppl_to_groups_opts"
action="{{ url_for('ppl_to_groups_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['ppl_to_groups_opts'].submit()"
style="cursor:pointer;">
<i class="ri-group-line card__icon"></i>
<p class="card__name">Add Active People to Groups</p>
</div>
{% endblock %}

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
{% block content %}
<h2>You're currently accessing {{ session.raw_school }}.</h2>
<h2>&nbsp;</h2>
<div class="instructions-list">
<div class="instructions-left">
<ul>
<li> <h3>Left side - Manual entry:</h3>
<ul>
<li> Both fields don't need to be filled out!</li>
<li> You can add just people or just groups.</li>
<li> Using both fields will add all people to all groups.</li>
</ul>
</li>
</ul>
</div>
<div class="instructions-right">
<ul>
<li> <h3>Right side - CSV Upload:</h3>
<ul>
<li> The Header rows must be <strong>Email</strong> and/or <strong>Groups</strong></li>
<li> Please format the csv like this:</li>
<li> Email,Groups</li>
<li> email@email.com, group1, group2, group3 </li>
<li> email2@email.com, group5, group1 </li>
</ul>
</li>
</ul>
</div>
</div>
<p>&nbsp;</p>
{% if error %}
<p class=error><strong> </strong>{{ error }}</p>
{% endif %}
<p>&nbsp;</p>
<div class="man-csv-opts">
<div class="manual-opts">
<p><label for="Bulk Add"> Each item must be comma separated or placed on a
new line.</label></p>
<form action="{{ url_for("bulk_add")}}" method="post">
<p>Emails</p>
<textarea id="emails" name="emails" rows="4" cols="50"></textarea>
<p>Group Names</p>
<textarea id="groups" name="groups" rows="4" cols="50"></textarea>
<p></p>
<input type="submit" value="Submit"></input>
</form>
</div>
{% include 'csv.html' %}
</div>
</div>
{% if table %}
{% include 'table.html' %}
{% endif %}
{% endblock %}

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
{% block content %}
<h4>Hello! Please enter the emails below.</h4>
{% if error %}
<p class=error><strong> </strong>{{ error }}</p>
{% endif %}
<p><label for="Bulk Add People"> Please select the appropriate options below.</label></p>
<form action="{{ url_for("bulk_add_ppl")}}" method="post">
<p>Please Copy and Paste Emails of learners you'd like to add</p>
<textarea id="emails" name="emails" rows="4" cols="50"></textarea>
<p>Please paste in the Group UUIDs which these learners should be added to.</p>
<textarea id="groups" name="groups" rows="4" cols="50"></textarea>
<p></p>
<input type="submit" value="Submit"></input>
</form>
{% include 'table.html' %}
{% endblock %}

13
app/templates/cmtest.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
<p>&nbsp;</p>
{% if error %}
<h3>{{ error }}</h4>
{% endif %}
<form method="POST" action="/">
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>

View File

@ -1,21 +1,21 @@
<!DOCTYPE html>
{% extends 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
{% block content %}
<h3> If you'd like to upload a CSV. Please do so here:<h3>
<div class="csv-upload">
<h3> If you'd like to upload a CSV. Please do so here:</h3>
<p></p>
<form method="post" action="" enctype="multipart/form-data">
<form method="POST"
action="{{ url_for('upload_file') }}"
enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="Submit CSV"></p>
<div class="radio-options">
<input type="radio" id="all-groups" name="learner-groups" value="all-groups" checked>
<label for="learner-groups">All Learners in All Groups</label>
</div>
<div class="radio-options">
<input type="radio" id="some-groups" name="learner-groups" value="some-groups">
<label for="learner-groups">Learners Only in Adjacent Groups</label>
</div>
<input type="submit" name="preview" value="Preview"></input>
<input type="submit" name="submit" value="Submit to Academy"></input>
</form>
{% if table %}
{% include 'table.html' %}
{% else %}
<p></p>
{% endif %}
{% endblock %}
</div>

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
{% block content %}
{% include 'table.html' %}
{% endblock %}

View File

@ -15,10 +15,23 @@
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename="css/styles.css") }}" />
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2.2/code-input.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2.2/code-input.min.css">
<link href="{{ url_for('static', filename='css/prism.css')}}" rel="stylesheet" />
</head>
<body>
{% block content %} {% endblock %}
<script src="{{ url_for('static', filename='css/prism.js' )}}"></script>
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.7.0/build/highlight.min.js"></script>
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
</body>
</html>

View File

@ -1,48 +1,49 @@
<!DOCTYPE html>
<header class="header">
<ul class="header_opts">
<form class="navbutton" action="{{ url_for('render_home')}}">
<a href="{{ url_for('render_home')}}">
<div class="icon-background">
<i class="ri-home-line"></i>
</div>
Home
<form class="navbutton"
id="render_home"
action="{{ url_for('render_home')}}"
method="post">
<a class="navoption"
onclick="document.forms['render_home'].submit()"
style="cursor:pointer;">
<i class="ri-home-line card__icon"></i>
<p class="navselection">Home</p>
</a>
</form>
<form class="navbutton"
action="{{ url_for('get_people')}}"
<form class="navbutton"
id="bulk_add_opts"
action="{{ url_for('bulk_add_opts')}}"
method="post">
<a class="navoption"
onclick="document.forms['get_people'].submit()"
onclick="document.forms['bulk_add_opts'].submit()"
style="cursor:pointer;">
<i class="ri-car-line card__icon"></i>
<p class="navselection">Get People</p>
<i class="ri-send-plane-line card__icon"></i>
<p class="navselection">Bulk Add</p>
</a>
</form>
<form class="navbutton"
id="get_people"
action="{{ url_for('get_people')}}"
method="post">
<a class="navoption" onclick="document.forms['get_people'].submit()" class="nav-link">
<div class="icon-background">
<i class="ri-home-line"></i>
</div>
Get People
id="load_templates"
action="{{ url_for('load_templates')}}"
method="post">
<a class="navoption"
onclick="document.forms['load_templates'].submit()"
style="cursor:pointer;">
<i class="ri-car-line card__icon"></i>
<p class="navselection">Templates</p>
</a>
</form>
<form
class="navbutton"
id="get_courses"
action="{{ url_for('get_courses')}}"
method="post">
<a onclick="document.forms['get_courses'].submit()" class="nav-link">
<div class="icon-background">
<i class="ri-briefcase-line"></i>
</div>
Get Courses
<form class="navbutton">
<a class = "navoption"
href="{{ url_for('clear_session')}}"
style="cursor:pointer;">
<i class="ri-scissors-line card__icon"></i>
<p class="navselection">New Customer</p>
</a>
</form>
</header>

8
app/templates/home.html Normal file
View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
{% block content %}
<h2>Academy: {{ session.raw_school }}.</h2>
{% endblock %}

View File

@ -3,14 +3,17 @@
{% include 'logo.html' %}
{% block content %}
<h4>Hello! Please click below to enter your key.</h4>
{% for error in error %}
<h2 class=error><strong> {{ error }} </strong></h2>
{% endfor %}
<h2 class=error><strong>
{% if error %}
{{ error }}
{% endif %}
</strong></h2>
<p>
</p>
<form action="{{ url_for("ask_key")}}" method="post">
<input type="text" name="apikey">
<input type="submit" value="Submit">
<input id="pw" type="password" name="apikey">
<input id="sbm" type="submit" value="Submit">
</form>
</div>
</div>
</body>
{% endblock %}

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<div class="main">
<a href="{{ url_for('render_home')}}">
<img
src="{{ url_for('static', filename="NP-Logo-Primary-FC.png") }}"
alt="Northpass Full Color Logo"
>
</img></a>
{% for message in get_flashed_messages() %}
<h2> {{ message }} </h2>
{% endfor %}
<div class="logo-div">
<a href="{{ url_for('render_home')}}">
<img
src="{{ url_for('static', filename="NP-Logo-Primary-FC.png") }}"
alt="Northpass Full Color Logo"
>
</img></a>
{% for message in get_flashed_messages() %}
<h2> {{ message }} </h2>
{% endfor %}

View File

@ -1,65 +1,9 @@
<!DOCTYPE html>
{% extends 'head.html' %}
{% include 'logo.html' %}
{% include 'header.html' %}
{% block content %}
<h4>Hello! Please find the options for {{ session.school }}.</h4>
<p></p>
</div>
<div class="card-grid">
<form class="card"
action="{{ url_for('get_people')}}"
method="post">
<a class="a-card"
onclick="document.forms['get_people'].submit()"
style="cursor:pointer;">
<i class="ri-car-line card__icon"></i>
<p class="card__name">Get People</p>
</a></form>
<form class="card"
id="get_courses"
action="{{ url_for('get_courses')}}"
method="post">
<a class="a-card"
onclick="document.forms['get_courses'].submit()"
style="cursor:pointer;">
<i class="ri-plane-line card__icon"></i>
<p class="card__name">Get Courses</p>
</a></form>
<form class="card"
id="add_ppl_opts"
action="{{ url_for('add_ppl_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['add_ppl_opts'].submit()"
style="cursor:pointer;">
<i class="ri-send-plane-line card__icon"></i>
<p class="card__name">Bulk Add People</p>
</a></form>
<form class="card"
id="add_groups_opts"
action="{{ url_for('add_groups_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['add_groups_opts'].submit()"
style="cursor:pointer;">
<i class="ri-shape-2-line card__icon"></i>
<p class="card__name">Bulk Add Groups</p>
</a></form>
<form class="card"
id="ppl_to_groups_opts"
action="{{ url_for('ppl_to_groups_opts')}}"
method="post">
<a class="a-card"
onclick="document.forms['ppl_to_groups_opts'].submit()"
style="cursor:pointer;">
<i class="ri-group-line card__icon"></i>
<p class="card__name">Add Active People to Groups</p>
</div>
{% endblock %}
<h2>Hello! You're currently accessing {{ session.raw_school }}.</h2>
{% endblock %}

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<h4> Select one of the options below. </h4>
<main id="selections">
<input type=checkbox id="get_people">
<input type=checkbox id="get_courses">
<input type=checkbox id="get_groups">
<input type=checkbox id="get_enrollments">
</main>

View File

@ -1,9 +1,11 @@
<!DOCTYPE html>
<div class="table">
<h4>Hello! Here are your results.</h4>
<a id="download" href="/downloadcsv">Click here to download as CSV.</a>
</div>
<div class="panda-table">
{{ table | safe }}
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,99 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
<p>&nbsp;</p>
{% if error %}
<h3>{{ error }}</h4>
{% endif %}
{% if button %}
<br>
<form
id="undo_template"
method="post"
action="{{ url_for('undo_template')}}">
<input type="submit" name="undo_template" value="Undo Last Upload"></input>
</form>
{% endif %}
{% if templates %}
<h2> Here are the liquid templates for </h2>
<h2 style="color:#F05323"><strong> {{ session.raw_school }} </strong></h2>
{% endif %}
<div class="templates_display" >
{% for templates in templates %}
<p>&nbsp;</p>
<div class="window-body">
<form
id="templates"
action="{{ url_for('templates')}}"
method="post">
<h2> {{ templates[0] }}</h2>
<h3> Last Updated: {{ templates [2] }}</h3>
<code-input
lang="HTML"
value="{{ templates[1] }}"
id="editor"
name="body"
template="code-input">
{{ templates[1] }}
</code-input>
<label for="template_name">
Create New Template Name (optional):
</label>
<input
type="text"
name="template_name"
value="{{ templates[0] }}">
</input>
<p>&nbsp;</p>
<input
type="submit"
name="submit-template"
value="Submit Template">
</input>
</form>
</div>
</div>
<p>&nbsp;</p>
{% endfor %}
{% if templates %}
<h3> Advanced users only: create new template </h3>
<p>&nbsp;</p>
<div class="html_code">
<form
id="templates"
action="{{ url_for('templates')}}"
method="post">
<h2>
Enter the code below
</h2>
<code-input
lang="HTML"
id="editor"
name="body"
template="code-input">
</code-input>
<p>&nbsp;</p>
<label for="template_name">
Template Name:
</label>
<input
type="text"
name="template_name">
</input>
<p>&nbsp;</p>
<input type="submit" name="submit_template" value="Submit Template"</input>
</form>
</div>
{% endif %}
<script>
codeInput.registerTemplate("code-input", codeInput.templates.hljs(hljs,
[
]));
</script>
<script>
codeInput.registerTemplate("code-input", codeInput.templates.prism(Prism, []));
</script>

6
app/wsgi.py Normal file
View File

@ -0,0 +1,6 @@
import eventlet
from eventlet import wsgi
from app import apicalls
app = apicalls()
wsgi.server(eventlet.list(("127.0.0.1", 5000), app))

View File

@ -1,5 +1,4 @@
import os
class Config(object):
SECRET_KEY = os.environ.get("NORTHPASS") or "north-pass"