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)
|
app.config.from_object(Config)
|
||||||
|
|
||||||
# Upload folder
|
# Upload folder
|
||||||
UPLOAD_FOLDER = 'static/files'
|
#UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files"
|
||||||
app.config['UPLOAD_FOLDER'] =UPLOAD_FOLDER
|
# UPLOAD_FOLDER = 'static/files'
|
||||||
|
#app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
|
||||||
|
#ALLOWED_EXTENSIONS = {"csv"}
|
||||||
|
|
||||||
from app import routes
|
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 flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, PasswordField, BooleanField, SubmitField
|
from wtforms.fields import (SubmitField,
|
||||||
from wtforms.validators import DataRequired
|
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):
|
class ApiKey(FlaskForm):
|
||||||
apikey = StringField("Academy API Key", validators=[DataRequired()])
|
api_key = PasswordField('Api Key',
|
||||||
submit = SubmitField("Submit")
|
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 requests
|
||||||
|
import shutil
|
||||||
import itertools
|
import itertools
|
||||||
import pandas as pd
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import csv
|
||||||
import glob
|
import glob
|
||||||
from app import app
|
# import eventlet
|
||||||
from flask import redirect, flash, request, render_template, session, make_response
|
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 werkzeug.utils import secure_filename
|
||||||
|
from app import app
|
||||||
|
|
||||||
# Upload folder
|
UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/csv"
|
||||||
UPLOAD_FOLDER = '/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files'
|
TEMPLATES_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/templates/"
|
||||||
# UPLOAD_FOLDER = 'static/files'
|
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
|
||||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
app.config["TEMPLATES_FOLDER"] = TEMPLATES_FOLDER
|
||||||
ALLOWED_EXTENSIONS = {'csv'}
|
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():
|
def download_csv():
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
download = make_response(session["dfcsv"])
|
download = make_response(session["dfcsv"])
|
||||||
@ -104,278 +52,424 @@ def download_csv():
|
|||||||
return download
|
return download
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_courses", methods=["GET", "POST"])
|
def key_response(response):
|
||||||
def get_courses():
|
if "402" in str(response):
|
||||||
array = []
|
error = response.text
|
||||||
course_dict = {}
|
return render_template("index.html", title="Error Home", error=error)
|
||||||
pd.set_option("display.max_colwidth", 100)
|
if "401" in str(response):
|
||||||
count = 0
|
error = "Unauthorized access error.(401)"
|
||||||
dataframe = pd.DataFrame()
|
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":
|
if request.method == "POST":
|
||||||
while True:
|
session["key"] = request.form.get("apikey")
|
||||||
count += 1
|
if any(char in specials for char in session["key"]) or re.search(
|
||||||
url = f"https://api.northpass.com/v2/courses?page={count}"
|
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"]}
|
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url + endpoint, headers=headers)
|
||||||
data = response.json()
|
return key_response(response)
|
||||||
nextlink = data["links"]
|
error = "Hm. That doesn't seem right"
|
||||||
|
session.clear()
|
||||||
for response in data["data"]:
|
return render_template("index.html", title="Home", error=error)
|
||||||
uuid = response["id"]
|
session.clear()
|
||||||
course_dict = {"id": uuid}
|
return render_template("index.html", title="Home")
|
||||||
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."
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_people", methods=["GET", "POST"])
|
@app.route("/render_home", methods=["GET", "POST"])
|
||||||
def get_people():
|
@key_required
|
||||||
array = []
|
def render_home():
|
||||||
ppl_dict = {}
|
if session.get("key"):
|
||||||
count = 0
|
return render_template("home.html", title="Home")
|
||||||
dataframe = pd.DataFrame()
|
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":
|
if request.method == "POST":
|
||||||
while True:
|
if "file" not in request.files:
|
||||||
count += 1
|
flash("No file found or uploaded")
|
||||||
url = f"https://api.northpass.com/v2/people?page={count}"
|
return redirect(url_for("bulk_add_opts"))
|
||||||
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
|
file = request.files["file"]
|
||||||
response = requests.get(url, headers=headers)
|
if file.filename == "":
|
||||||
data = response.json()
|
print("no file exists")
|
||||||
nextlink = data["links"]
|
flash("No file found or uploaded")
|
||||||
|
return redirect(url_for("home"))
|
||||||
for response in data["data"]:
|
# return redirect(request.url)
|
||||||
uuid = response["id"]
|
if file and allowed_file(file.filename):
|
||||||
ppl_dict = {"id": uuid}
|
filename = secure_filename(file.filename)
|
||||||
for keys, values in response["attributes"].items():
|
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
|
||||||
ppl_dict[keys] = values
|
session["file"] = filename
|
||||||
array.append(ppl_dict)
|
session["filepath"] = file_path
|
||||||
dataframe = pd.DataFrame(array).drop("custom_avatar_url", axis=1)
|
file.save(file_path)
|
||||||
print(dataframe)
|
file = list(csv.reader(open(file_path, "r")))
|
||||||
|
return divide_values(file)
|
||||||
if "next" not in nextlink:
|
return render_template("home.html", title="Bulk Add")
|
||||||
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"
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/add_ppl_opts", methods=["POST"])
|
def divide_values(file):
|
||||||
def add_ppl_opts():
|
emails = []
|
||||||
array = []
|
groups = []
|
||||||
dict_response = {}
|
selection = request.form.get("learner-groups")
|
||||||
dataframe = pd.DataFrame()
|
if request.form["submit"]:
|
||||||
count = 0
|
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":
|
if request.form["preview"]:
|
||||||
while True:
|
error = "Preview Button Still Under Construction. Try again later."
|
||||||
count += 1
|
return render_template("bulk_add.html", error=error, title="Preview Not Yet")
|
||||||
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"]:
|
return render_template("bulk_add.html", title="Uploaded File")
|
||||||
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."
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/bulk_add_ppl", methods=["GET", "POST"])
|
def api_csv_parse(emails, groups):
|
||||||
def bulk_add_ppl():
|
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":
|
if request.method == "POST":
|
||||||
emails = request.form.get("emails")
|
emails = request.form.get("emails")
|
||||||
groups = request.form.get("groups")
|
groups = request.form.get("groups")
|
||||||
emails.split(",")
|
if emails:
|
||||||
groups.split(",")
|
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"
|
if groups:
|
||||||
combinations = list(itertools.product(emails, groups))
|
if "\n" in groups:
|
||||||
print(combinations)
|
groups = groups.split("\n")
|
||||||
payload = {
|
groups = [group.strip() for group in groups]
|
||||||
"data": {"attributes": {"people": [{"email": emails, "groups": groups}]}}
|
groups = [re.sub(r"[,]", "", group) for group in groups]
|
||||||
}
|
elif "," in groups:
|
||||||
headers = {
|
groups = groups.split(",")
|
||||||
"accept": "application/json",
|
groups = [group.strip() for group in groups]
|
||||||
"content-type": "application/json",
|
else:
|
||||||
"X-Api-Key": session["key"],
|
groups = groups.split()
|
||||||
}
|
|
||||||
response = requests.post(url, json=payload, headers=headers)
|
if emails and groups:
|
||||||
response = str(response)
|
return api_add_ppl_groups(emails, groups)
|
||||||
if "202" in response:
|
elif emails:
|
||||||
error = "Success! People have been added successfully."
|
return api_add_ppl(emails)
|
||||||
return render_template(
|
elif groups:
|
||||||
"bulk_add_ppl.html",
|
return api_add_groups(groups)
|
||||||
table=session["dfgroups"],
|
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."
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/add_groups_opts", methods=["POST"])
|
def api_add_ppl(emails):
|
||||||
def add_groups_opts():
|
payload2 = []
|
||||||
array = []
|
endpoint = "v2/bulk/people"
|
||||||
dict_response = {}
|
for email in emails:
|
||||||
count = 0
|
payload2.append({"email": email})
|
||||||
dataframe = pd.DataFrame()
|
payload = {"data": {"attributes": {"people": payload2}}}
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headers = {
|
headers = {
|
||||||
"accept": "application/json",
|
"accept": "application/json",
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"X-Api-Key": session["key"],
|
"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__":
|
def api_add_ppl_groups(emails, groups):
|
||||||
ask_key()
|
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 {
|
:root {
|
||||||
--primary: #66C92D;
|
--primary: #66C92D;
|
||||||
@ -49,6 +49,8 @@ html {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 1.1 - Dataframe and Table Styling */
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select {
|
select {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
@ -79,6 +81,8 @@ select {
|
|||||||
background: #CCD4D8;
|
background: #CCD4D8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Titles and Header Copy/Test */
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -97,13 +101,15 @@ img {
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.logo-div {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 1.? - Header and Navigation Bar Styling */
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@ -118,20 +124,39 @@ img {
|
|||||||
display: flex;
|
display: flex;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
.instructions-list{
|
||||||
|
display:flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions-left
|
||||||
|
.instructions-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-background {
|
.button-background {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio-options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.navbutton {
|
.navbutton {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
@ -140,10 +165,32 @@ li {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
text-decoration: none;
|
|
||||||
padding: 8px 16px 8px 12px;
|
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 {
|
#currentDate {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
@ -153,15 +200,16 @@ li {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1250px) {
|
@media screen and (max-width: 1250px) {
|
||||||
h1 {
|
h1 {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
#currentTime {
|
#currentTime {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@ -172,9 +220,12 @@ li {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: block;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 1.? - Card Layout in options.html only */
|
||||||
|
|
||||||
.card-grid {
|
.card-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@ -190,28 +241,6 @@ ul {
|
|||||||
border: 1px solid #66C92D;
|
border: 1px solid #66C92D;
|
||||||
color: #FFFFFF;
|
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:hover,
|
||||||
.card:focus {
|
.card:focus {
|
||||||
@ -222,7 +251,7 @@ ul {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover > .card__name {
|
.card:checked > .card__name {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,3 +268,70 @@ ul {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transition: 0.1s;
|
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>
|
<!DOCTYPE html>
|
||||||
{% extends 'head.html' %}
|
<div class="csv-upload">
|
||||||
{% include 'header.html' %}
|
<h3> If you'd like to upload a CSV. Please do so here:</h3>
|
||||||
{% include 'logo.html' %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h3> If you'd like to upload a CSV. Please do so here:<h3>
|
|
||||||
<p></p>
|
<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="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>
|
</form>
|
||||||
|
</div>
|
||||||
{% if table %}
|
|
||||||
{% include 'table.html' %}
|
|
||||||
{% else %}
|
|
||||||
<p></p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
|
{% include 'logo.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'table.html' %}
|
{% include 'table.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -15,10 +15,23 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename="css/styles.css") }}" />
|
<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://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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block content %} {% endblock %}
|
{% 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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,48 +1,49 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<header class="header">
|
<header class="header">
|
||||||
|
|
||||||
<ul class="header_opts">
|
<form class="navbutton"
|
||||||
<form class="navbutton" action="{{ url_for('render_home')}}">
|
id="render_home"
|
||||||
<a href="{{ url_for('render_home')}}">
|
action="{{ url_for('render_home')}}"
|
||||||
<div class="icon-background">
|
method="post">
|
||||||
<i class="ri-home-line"></i>
|
<a class="navoption"
|
||||||
</div>
|
onclick="document.forms['render_home'].submit()"
|
||||||
Home
|
style="cursor:pointer;">
|
||||||
|
<i class="ri-home-line card__icon"></i>
|
||||||
|
<p class="navselection">Home</p>
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="navbutton"
|
<form class="navbutton"
|
||||||
action="{{ url_for('get_people')}}"
|
id="bulk_add_opts"
|
||||||
|
action="{{ url_for('bulk_add_opts')}}"
|
||||||
method="post">
|
method="post">
|
||||||
<a class="navoption"
|
<a class="navoption"
|
||||||
onclick="document.forms['get_people'].submit()"
|
onclick="document.forms['bulk_add_opts'].submit()"
|
||||||
style="cursor:pointer;">
|
style="cursor:pointer;">
|
||||||
<i class="ri-car-line card__icon"></i>
|
<i class="ri-send-plane-line card__icon"></i>
|
||||||
<p class="navselection">Get People</p>
|
<p class="navselection">Bulk Add</p>
|
||||||
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="navbutton"
|
<form class="navbutton"
|
||||||
id="get_people"
|
id="load_templates"
|
||||||
action="{{ url_for('get_people')}}"
|
action="{{ url_for('load_templates')}}"
|
||||||
method="post">
|
method="post">
|
||||||
<a class="navoption" onclick="document.forms['get_people'].submit()" class="nav-link">
|
<a class="navoption"
|
||||||
<div class="icon-background">
|
onclick="document.forms['load_templates'].submit()"
|
||||||
<i class="ri-home-line"></i>
|
style="cursor:pointer;">
|
||||||
</div>
|
<i class="ri-car-line card__icon"></i>
|
||||||
Get People
|
<p class="navselection">Templates</p>
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form
|
<form class="navbutton">
|
||||||
class="navbutton"
|
<a class = "navoption"
|
||||||
id="get_courses"
|
href="{{ url_for('clear_session')}}"
|
||||||
action="{{ url_for('get_courses')}}"
|
style="cursor:pointer;">
|
||||||
method="post">
|
<i class="ri-scissors-line card__icon"></i>
|
||||||
<a onclick="document.forms['get_courses'].submit()" class="nav-link">
|
<p class="navselection">New Customer</p>
|
||||||
<div class="icon-background">
|
|
||||||
<i class="ri-briefcase-line"></i>
|
|
||||||
</div>
|
|
||||||
Get Courses
|
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</header>
|
</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' %}
|
{% include 'logo.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h4>Hello! Please click below to enter your key.</h4>
|
<h4>Hello! Please click below to enter your key.</h4>
|
||||||
{% for error in error %}
|
<h2 class=error><strong>
|
||||||
<h2 class=error><strong> {{ error }} </strong></h2>
|
{% if error %}
|
||||||
{% endfor %}
|
{{ error }}
|
||||||
|
{% endif %}
|
||||||
|
</strong></h2>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
<form action="{{ url_for("ask_key")}}" method="post">
|
<form action="{{ url_for("ask_key")}}" method="post">
|
||||||
<input type="text" name="apikey">
|
<input id="pw" type="password" name="apikey">
|
||||||
<input type="submit" value="Submit">
|
<input id="sbm" type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div class="main">
|
<div class="logo-div">
|
||||||
<a href="{{ url_for('render_home')}}">
|
<a href="{{ url_for('render_home')}}">
|
||||||
<img
|
<img
|
||||||
src="{{ url_for('static', filename="NP-Logo-Primary-FC.png") }}"
|
src="{{ url_for('static', filename="NP-Logo-Primary-FC.png") }}"
|
||||||
alt="Northpass Full Color Logo"
|
alt="Northpass Full Color Logo"
|
||||||
>
|
>
|
||||||
</img></a>
|
</img></a>
|
||||||
{% for message in get_flashed_messages() %}
|
{% for message in get_flashed_messages() %}
|
||||||
<h2> {{ message }} </h2>
|
<h2> {{ message }} </h2>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -1,65 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% extends 'head.html' %}
|
{% extends 'head.html' %}
|
||||||
{% include 'logo.html' %}
|
{% include 'logo.html' %}
|
||||||
|
{% include 'header.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h4>Hello! Please find the options for {{ session.school }}.</h4>
|
<h2>Hello! You're currently accessing {{ session.raw_school }}.</h2>
|
||||||
|
|
||||||
<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 %}
|
|
||||||
|
|
||||||
|
{% 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>
|
<!DOCTYPE html>
|
||||||
|
<div class="table">
|
||||||
<h4>Hello! Here are your results.</h4>
|
<h4>Hello! Here are your results.</h4>
|
||||||
<a id="download" href="/downloadcsv">Click here to download as CSV.</a>
|
<a id="download" href="/downloadcsv">Click here to download as CSV.</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="panda-table">
|
<div class="panda-table">
|
||||||
{{ table | safe }}
|
{{ table | safe }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</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