Restarted adding the get information. Currently, it pulls courses. Added action button to the bottom with two options. First, if a table exists with the course info, you can download it. Second, it brings you NP admin panel for the academy you're currently accessing.
This commit is contained in:
20
Flask.yaml
20
Flask.yaml
@ -1,20 +0,0 @@
|
|||||||
session_name: '0'
|
|
||||||
windows:
|
|
||||||
- focus: 'true'
|
|
||||||
layout: e90b,204x63,0,0[204x46,0,0,0,204x16,0,47{36x16,0,47,2,84x16,37,47,3,82x16,122,47,4}]
|
|
||||||
options: {}
|
|
||||||
panes:
|
|
||||||
- shell_command:
|
|
||||||
- cd /Users/normrasmussen/Documents/Projects/CSM_webapp
|
|
||||||
- nvim
|
|
||||||
- shell_command:
|
|
||||||
- cd /Users/normrasmussen
|
|
||||||
- cmatrix
|
|
||||||
- focus: 'true'
|
|
||||||
shell_command:
|
|
||||||
- cd /Users/normrasmussen/Documents/Projects/CSM_webapp
|
|
||||||
- Python
|
|
||||||
- shell_command:
|
|
||||||
- cd /Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files
|
|
||||||
- zsh
|
|
||||||
window_name: flask main
|
|
||||||
Binary file not shown.
124
app/routes.py
124
app/routes.py
@ -5,7 +5,10 @@ import re
|
|||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
import glob
|
import glob
|
||||||
|
import time
|
||||||
|
import pandas as pd
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
from pathlib import Path
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import flask
|
import flask
|
||||||
from flask import (
|
from flask import (
|
||||||
@ -39,7 +42,7 @@ app.permanent_session_lifetime = timedelta(minutes=30)
|
|||||||
specials = '"!@#$%^&*()-+?_=,<>/"'
|
specials = '"!@#$%^&*()-+?_=,<>/"'
|
||||||
url = "https://api.northpass.com/"
|
url = "https://api.northpass.com/"
|
||||||
|
|
||||||
|
@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"])
|
||||||
@ -47,6 +50,12 @@ def download_csv():
|
|||||||
download.headers["Content-Type"] = "text/csv"
|
download.headers["Content-Type"] = "text/csv"
|
||||||
return download
|
return download
|
||||||
|
|
||||||
|
@app.route("/send_to_admin")
|
||||||
|
def send_to_admin():
|
||||||
|
if request.method == "GET":
|
||||||
|
url = f"https://app.northpass.com/app/admin/{session['admin_id']}"
|
||||||
|
return redirect(url)
|
||||||
|
|
||||||
|
|
||||||
def key_response(response):
|
def key_response(response):
|
||||||
if "402" in str(response):
|
if "402" in str(response):
|
||||||
@ -62,6 +71,7 @@ def correct_key(response):
|
|||||||
data = response.json()
|
data = response.json()
|
||||||
session["raw_school"] = data["data"]["attributes"]["properties"]["name"]
|
session["raw_school"] = data["data"]["attributes"]["properties"]["name"]
|
||||||
session["sani_school"] = session["raw_school"].replace("[", "").replace("]", "")
|
session["sani_school"] = session["raw_school"].replace("[", "").replace("]", "")
|
||||||
|
session["admin_id"] = data["data"]["id"]
|
||||||
session["client_path"] = os.path.join(TEMPLATES_FOLDER, session["sani_school"])
|
session["client_path"] = os.path.join(TEMPLATES_FOLDER, session["sani_school"])
|
||||||
return render_template("home.html", title="Active Session")
|
return render_template("home.html", title="Active Session")
|
||||||
|
|
||||||
@ -174,7 +184,7 @@ def divide_values(file):
|
|||||||
for item in file[1:]:
|
for item in file[1:]:
|
||||||
emails.append(item[0])
|
emails.append(item[0])
|
||||||
groups.append(item[1:])
|
groups.append(item[1:])
|
||||||
# FEAT: These two extract the groups and emails into two lists
|
# FEAT: These two extract the groups and emails into two lists
|
||||||
groups = [item for group in groups for item in group]
|
groups = [item for group in groups for item in group]
|
||||||
groups = list(set(groups))
|
groups = list(set(groups))
|
||||||
return api_csv_parse(emails, groups)
|
return api_csv_parse(emails, groups)
|
||||||
@ -183,7 +193,7 @@ def divide_values(file):
|
|||||||
elif selection == "some-groups":
|
elif selection == "some-groups":
|
||||||
submissions = []
|
submissions = []
|
||||||
for item in file[1:]:
|
for item in file[1:]:
|
||||||
# FEAT: This extracts each row as a list. Perfect for Learners in Specific Groups.
|
# FEAT: This extracts each row as a list. Perfect for Learners in Specific Groups.
|
||||||
submissions.append(item)
|
submissions.append(item)
|
||||||
for item in submissions:
|
for item in submissions:
|
||||||
emails.append(item[0])
|
emails.append(item[0])
|
||||||
@ -437,20 +447,111 @@ def download_templates():
|
|||||||
os.chdir = session["client_path"]
|
os.chdir = session["client_path"]
|
||||||
file_path = session["client_path"]
|
file_path = session["client_path"]
|
||||||
zipped_path = os.path.join(TEMPLATES_FOLDER, zipped_file)
|
zipped_path = os.path.join(TEMPLATES_FOLDER, zipped_file)
|
||||||
download = shutil.make_archive(zipped_path, 'zip', file_path)
|
download = shutil.make_archive(zipped_path, "zip", file_path)
|
||||||
|
delete_zip()
|
||||||
return send_file(download, as_attachment=True)
|
return send_file(download, as_attachment=True)
|
||||||
|
|
||||||
@app.after_request
|
|
||||||
def delete_zip(response):
|
def delete_zip():
|
||||||
|
print("Sleeping...")
|
||||||
|
time.sleep(5)
|
||||||
zipped_path = TEMPLATES_FOLDER
|
zipped_path = TEMPLATES_FOLDER
|
||||||
wildcard = glob.glob(zipped_path + ".zip")
|
wildcard = glob.glob(zipped_path + "*.zip")
|
||||||
for zipfile in wildcard:
|
wildcard = str(wildcard)
|
||||||
|
wildzip = wildcard[-5:-2]
|
||||||
|
if wildzip == "zip":
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(zipfile)
|
path = Path(wildcard)
|
||||||
|
print(path)
|
||||||
|
shutil.rmtree(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
print(OSError)
|
print(OSError)
|
||||||
return response
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/get_info", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
|
def get_info():
|
||||||
|
return render_template("get_info.html", title="Get Customer Information")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/get_info/<variable>", methods=["GET", "POST"])
|
||||||
|
@key_required
|
||||||
|
def get_courses(variable):
|
||||||
|
count = 0
|
||||||
|
courses = []
|
||||||
|
cats = []
|
||||||
|
course_dict = {}
|
||||||
|
if request.method == "POST":
|
||||||
|
while True:
|
||||||
|
count += 1
|
||||||
|
url = f"https://api.northpass.com/v2/courses?page={count}"
|
||||||
|
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
for response in data["data"]:
|
||||||
|
status = response["attributes"]["status"]
|
||||||
|
uuid = response["id"]
|
||||||
|
name = response["attributes"]["name"]
|
||||||
|
ecount = response["attributes"]["enrollments_count"]
|
||||||
|
created = response["attributes"]["created_at"]
|
||||||
|
update = response["attributes"]["updated_at"]
|
||||||
|
unpub = response["attributes"]["unpublished_changes"]
|
||||||
|
course_dict = {
|
||||||
|
"Id": uuid,
|
||||||
|
"Name": name,
|
||||||
|
"Status": status,
|
||||||
|
"Enrollments": ecount,
|
||||||
|
"Created At": created,
|
||||||
|
"Last Updated": update,
|
||||||
|
"Unpublished Changes?": unpub,
|
||||||
|
}
|
||||||
|
cat_id = response["relationships"]["categories"]["data"]
|
||||||
|
headers = {"accept": "application/json", "X-Api-Key": session["key"]}
|
||||||
|
cats = []
|
||||||
|
if len(cat_id) == 0:
|
||||||
|
pass
|
||||||
|
elif len(cat_id) == 1:
|
||||||
|
categoryid = cat_id[0]["id"]
|
||||||
|
url = f"https://api.northpass.com/v2/categories/{categoryid}"
|
||||||
|
cat_resp = requests.get(url, headers=headers)
|
||||||
|
cat_data = cat_resp.json()
|
||||||
|
cat_name = cat_data["data"]["attributes"]["name"]
|
||||||
|
cats.append(cat_name)
|
||||||
|
course_dict.update({"Categories": cats})
|
||||||
|
else:
|
||||||
|
for item in cat_id:
|
||||||
|
categoryid = item["id"]
|
||||||
|
url = f"https://api.northpass.com/v2/categories/{categoryid}"
|
||||||
|
cat_resp = requests.get(url, headers=headers)
|
||||||
|
cat_data = cat_resp.json()
|
||||||
|
cat_name = cat_data["data"]["attributes"]["name"]
|
||||||
|
cats.append(cat_name)
|
||||||
|
course_dict.update({"Categories": cats})
|
||||||
|
|
||||||
|
try:
|
||||||
|
courses.append(course_dict)
|
||||||
|
except TypeError as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
finally:
|
||||||
|
pd.set_option("display.max_colwidth", 30)
|
||||||
|
df = pd.DataFrame.from_records(courses)
|
||||||
|
df.iloc[-1] = df.iloc[-1].astype(str).str.replace("[\]\[]",'')
|
||||||
|
df.fillna('', inplace=True)
|
||||||
|
table = df.to_html()
|
||||||
|
session["dfcsv"] = df.to_csv()
|
||||||
|
|
||||||
|
if data["data"] == []:
|
||||||
|
break
|
||||||
|
|
||||||
|
return render_template("get_info.html",
|
||||||
|
title="Course Information",
|
||||||
|
table=table)
|
||||||
|
|
||||||
|
return "You didn't post up"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/undo_template", methods=["POST"])
|
@app.route("/undo_template", methods=["POST"])
|
||||||
@key_required
|
@key_required
|
||||||
@ -459,6 +560,7 @@ def undo_template():
|
|||||||
if request.form["undo_templates"]:
|
if request.form["undo_templates"]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.route("/stop", methods=["POST"])
|
@app.route("/stop", methods=["POST"])
|
||||||
def stop():
|
def stop():
|
||||||
print("stopping")
|
print("stopping")
|
||||||
|
|||||||
@ -187,6 +187,11 @@ justify-content: space-evenly;
|
|||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.get_opts{
|
||||||
|
display:flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
#templates {
|
#templates {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
}
|
}
|
||||||
@ -228,7 +233,7 @@ ul {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@-webkit-keyframes come-in {
|
@-webkit-keyframes come-in {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: translatey(100px);
|
-webkit-transform: translatey(100px);
|
||||||
@ -268,7 +273,16 @@ ul {
|
|||||||
transform: translatey(0px) scale(1);
|
transform: translatey(0px) scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
background: #eaedf2;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
.floating-container {
|
.floating-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -279,7 +293,7 @@ ul {
|
|||||||
margin: 35px 25px;
|
margin: 35px 25px;
|
||||||
}
|
}
|
||||||
.floating-container:hover {
|
.floating-container:hover {
|
||||||
height: 300px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
.floating-container:hover .floating-button {
|
.floating-container:hover .floating-button {
|
||||||
box-shadow: 0 10px 25px rgba(44, 179, 240, 0.6);
|
box-shadow: 0 10px 25px rgba(44, 179, 240, 0.6);
|
||||||
@ -311,7 +325,7 @@ ul {
|
|||||||
right: 0;
|
right: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
color: white;
|
color: white;
|
||||||
line-height: 70px;
|
line-height: 65px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 23px;
|
font-size: 23px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
@ -320,11 +334,6 @@ ul {
|
|||||||
-webkit-transition: all 0.3s;
|
-webkit-transition: all 0.3s;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
.material-icons{
|
|
||||||
line-height: 0;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
.floating-container .float-element {
|
.floating-container .float-element {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
@ -356,4 +365,4 @@ ul {
|
|||||||
.floating-container .float-element:nth-child(3) {
|
.floating-container .float-element:nth-child(3) {
|
||||||
background: #FF9800;
|
background: #FF9800;
|
||||||
box-shadow: 0 20px 20px -10px rgba(255, 152, 0, 0.5);
|
box-shadow: 0 20px 20px -10px rgba(255, 152, 0, 0.5);
|
||||||
}*/
|
}
|
||||||
|
|||||||
17
app/templates/footer_button.html
Normal file
17
app/templates/footer_button.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<div class="floating-container">
|
||||||
|
<div class="floating-button">+</div>
|
||||||
|
<div class="element-container">
|
||||||
|
<a href="https://app.northpass.com/admin/schools/{{ session.admin_id }}">
|
||||||
|
<span class="float-element">
|
||||||
|
<i class="material-icons">admin_panel_settings</i>
|
||||||
|
</span>
|
||||||
|
{% if table %}
|
||||||
|
<a id="download" href="/downloadcsv">
|
||||||
|
<span class="float-element tooltip-left">
|
||||||
|
<i class="material-icons">download_for_offline
|
||||||
|
</i></a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
8
app/templates/get_courses.html
Normal file
8
app/templates/get_courses.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
{% include 'head.html' %}
|
||||||
|
{% include 'header.html' %}
|
||||||
|
{% include 'logo.html' %}
|
||||||
|
{% include 'subheader.html' %}
|
||||||
|
{% block content %}
|
||||||
|
{% include 'table.html' %}
|
||||||
|
{% endblock %}
|
||||||
10
app/templates/get_info.html
Normal file
10
app/templates/get_info.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
{% include 'head.html' %}
|
||||||
|
{% include 'header.html' %}
|
||||||
|
{% include 'logo.html' %}
|
||||||
|
{% include 'subheader.html' %}
|
||||||
|
{% block content %}
|
||||||
|
{% if table %}
|
||||||
|
{% include 'table.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@ -24,6 +24,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block content %} {% endblock %}
|
{% block content %} {% endblock %}
|
||||||
|
{% include 'footer_button.html'%}
|
||||||
<script src="{{ url_for('static', filename='css/prism.js' )}}"></script>
|
<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 src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.7.0/build/highlight.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -13,6 +13,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form class="navbutton"
|
||||||
|
id="get_info"
|
||||||
|
action="{{ url_for('get_info')}}"
|
||||||
|
method="post">
|
||||||
|
<a class="navoption"
|
||||||
|
onclick="document.forms['get_info'].submit()"
|
||||||
|
style="cursor:pointer;">
|
||||||
|
<i class="ri-cloud-line card__icon"></i>
|
||||||
|
<p class="navselection">Get Info</p>
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
<form class="navbutton"
|
<form class="navbutton"
|
||||||
id="bulk_add_opts"
|
id="bulk_add_opts"
|
||||||
action="{{ url_for('bulk_add_opts')}}"
|
action="{{ url_for('bulk_add_opts')}}"
|
||||||
|
|||||||
15
app/templates/subheader.html
Normal file
15
app/templates/subheader.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<div class="get_opts">
|
||||||
|
|
||||||
|
<form class="navbutton"
|
||||||
|
id="get_courses"
|
||||||
|
action="{{ url_for('get_courses', variable='courses')}}"
|
||||||
|
method="post">
|
||||||
|
<a class="navoption"
|
||||||
|
onclick="document.forms['get_courses'].submit()"
|
||||||
|
style="cursor:pointer;">
|
||||||
|
<p class="navselection">Get Courses</p>
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -1,7 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<h4>Hello! Here are your results.</h4>
|
<h4>Here are your results.</h4>
|
||||||
<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 }}
|
||||||
|
|||||||
@ -90,15 +90,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="floating-container">
|
|
||||||
<div class="floating-button">
|
|
||||||
<a href="{{ url_for('download_templates') }}">
|
|
||||||
<span class="float-element tooltip-left">
|
|
||||||
<i class="material-icons">download_for_offline
|
|
||||||
</i></a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
codeInput.registerTemplate("code-input", codeInput.templates.hljs(hljs,
|
codeInput.registerTemplate("code-input", codeInput.templates.hljs(hljs,
|
||||||
@ -108,3 +99,4 @@
|
|||||||
<script>
|
<script>
|
||||||
codeInput.registerTemplate("code-input", codeInput.templates.prism(Prism, []));
|
codeInput.registerTemplate("code-input", codeInput.templates.prism(Prism, []));
|
||||||
</script>
|
</script>
|
||||||
|
{% include 'footer_button.html'%}
|
||||||
|
|||||||
Reference in New Issue
Block a user