Compare commits
13 Commits
137b1e632a
...
templates
| Author | SHA1 | Date | |
|---|---|---|---|
| 7630cb80aa | |||
| b0addb6475 | |||
| 44259a8c51 | |||
| dae2eb440e | |||
| 8afdec139b | |||
| a435a66b81 | |||
| 43cdcff115 | |||
| 030c5541cf | |||
| 9e3ac00285 | |||
| 1cfc4fd9d1 | |||
| 49de321387 | |||
| 92f4ca2fbc | |||
| 8d0815b676 |
Binary file not shown.
@ -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.
BIN
app/__pycache__/forms.cpython-311.pyc
Normal file
BIN
app/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
57
app/forms.py
57
app/forms.py
@ -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')
|
||||
|
||||
|
||||
766
app/routes.py
766
app/routes.py
@ -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
BIN
app/static/.DS_Store
vendored
Normal file
Binary file not shown.
4
app/static/css/prism.css
Normal file
4
app/static/css/prism.css
Normal 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
7
app/static/css/prism.js
Normal file
File diff suppressed because one or more lines are too long
@ -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;
|
||||
}
|
||||
|
||||
4
app/static/files/csv/FLASK-TEST_-_Sheet1.csv
Normal file
4
app/static/files/csv/FLASK-TEST_-_Sheet1.csv
Normal 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
|
||||
|
4
app/static/files/csv/FLASK-TEST_-_Sheet1_1.csv
Normal file
4
app/static/files/csv/FLASK-TEST_-_Sheet1_1.csv
Normal 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,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);
|
||||
})
|
||||
}
|
||||
62
app/templates/_backup_options.html
Normal file
62
app/templates/_backup_options.html
Normal 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 %}
|
||||
59
app/templates/bulk_add.html
Normal file
59
app/templates/bulk_add.html
Normal 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> </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> </p>
|
||||
{% if error %}
|
||||
<p class=error><strong> </strong>{{ error }}</p>
|
||||
{% endif %}
|
||||
<p> </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 %}
|
||||
@ -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
13
app/templates/cmtest.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
{% include 'head.html' %}
|
||||
{% include 'header.html' %}
|
||||
{% include 'logo.html' %}
|
||||
<p> </p>
|
||||
{% if error %}
|
||||
<h3>{{ error }}</h4>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="/">
|
||||
{{ form.name.label }} {{ form.name(size=20) }}
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
{% include 'head.html' %}
|
||||
{% include 'header.html' %}
|
||||
{% include 'logo.html' %}
|
||||
{% block content %}
|
||||
{% include 'table.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
8
app/templates/home.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
9
app/templates/options2.html
Normal file
9
app/templates/options2.html
Normal 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>
|
||||
@ -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>
|
||||
|
||||
99
app/templates/templates.html
Normal file
99
app/templates/templates.html
Normal file
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
{% include 'head.html' %}
|
||||
{% include 'header.html' %}
|
||||
{% include 'logo.html' %}
|
||||
<p> </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> </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> </p>
|
||||
<input
|
||||
type="submit"
|
||||
name="submit-template"
|
||||
value="Submit Template">
|
||||
</input>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p> </p>
|
||||
{% endfor %}
|
||||
{% if templates %}
|
||||
<h3> Advanced users only: create new template </h3>
|
||||
<p> </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> </p>
|
||||
<label for="template_name">
|
||||
Template Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="template_name">
|
||||
</input>
|
||||
<p> </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
6
app/wsgi.py
Normal 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))
|
||||
Reference in New Issue
Block a user