Compare commits

3 Commits

16 changed files with 188 additions and 283 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
/app/static/files/

View File

@ -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

View File

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

View File

@ -1,76 +0,0 @@
@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()
if request.method == "POST":
while True:
count += 1
endpoint = f"v2/courses?page={count}"
headers = {"accept": "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"]:
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."
@app.route("/get_people", methods=["GET", "POST"])
def get_people():
array = []
ppl_dict = {}
count = 0
dataframe = pd.DataFrame()
if request.method == "POST":
print("get People POST")
while True:
count += 1
endpoint = f"v2/people?page={count}"
headers = {"accept": "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"]:
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 render_template("get.html", error="Something went wrong")

View File

@ -5,7 +5,10 @@ import re
import os
import csv
import glob
# 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 (
@ -17,24 +20,25 @@ from flask import (
make_response,
url_for,
g,
send_file,
)
from werkzeug.utils import secure_filename
from app import app
UPLOAD_FOLDER = (
"/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/csv/"
)
TEMPLATES_FOLDER = (
"/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/templates/"
)
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=30)
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/"
@ -61,8 +65,7 @@ def key_response(response):
def correct_key(response):
data = response.json()
session["raw_school"] = data["data"]["attributes"]["properties"]["name"]
session["sani_school"] = session["raw_school"].replace("[", "").replace("]", "")
session["client_path"] = os.path.join(TEMPLATES_FOLDER, session["sani_school"])
session["sani_school"] = session["raw_school"].replace('[','').replace(']', '')
return render_template("home.html", title="Active Session")
@ -117,16 +120,16 @@ def render_home():
@app.route("/clear_session", methods=["GET", "POST"])
def clear_session():
if session.get("key"):
print("key exists")
print(session["key"])
if session.get("client_path"):
client = session["client_path"]
print(client)
wildcard = glob.glob(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?")
@ -174,7 +177,7 @@ def divide_values(file):
for item in file[1:]:
emails.append(item[0])
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 = list(set(groups))
return api_csv_parse(emails, groups)
@ -183,7 +186,7 @@ def divide_values(file):
elif selection == "some-groups":
submissions = []
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)
for item in submissions:
emails.append(item[0])
@ -410,48 +413,23 @@ def check_templates(response, name):
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)
today = today.strftime("%m-%d-%Y %H:%M:%S")
session["client_path"] = session["client_path"] + "_" + str(today)
if os.path.exists(session["client_path"]):
pass
else:
os.mkdir(session["client_path"])
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(session["client_path"], file_name)
complete_path = os.path.join(path, file_name)
with open(complete_path, "w+") as temp:
temp.write(file_body)
temp.close
@app.route("/download_templates", methods=["GET", "POST"])
@key_required
def download_templates():
zipped_file = f"{session['sani_school']}"
zipped_file = zipped_file.replace(" ", "")
zipped_file = zipped_file.replace("'", "")
os.chdir = session["client_path"]
file_path = session["client_path"]
zipped_path = os.path.join(TEMPLATES_FOLDER, zipped_file)
download = shutil.make_archive(zipped_path, 'zip', file_path)
return send_file(download, as_attachment=True)
@app.after_request
def delete_zip(response):
zipped_path = TEMPLATES_FOLDER
wildcard = glob.glob(zipped_path + ".zip")
for zipfile in wildcard:
try:
shutil.rmtree(zipfile)
except OSError:
print(OSError)
return response
@app.route("/undo_template", methods=["POST"])
@key_required
def undo_template():
@ -462,3 +440,36 @@ def undo_template():
@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()

View File

@ -205,10 +205,6 @@ justify-content: space-evenly;
justify-content: space-evenly;
}
#editor{
height:550px;
}
@media screen and (max-width: 1250px) {
h1 {
display: flex;
@ -228,132 +224,114 @@ ul {
text-align: center;
}
/*
@-webkit-keyframes come-in {
0% {
-webkit-transform: translatey(100px);
transform: translatey(100px);
opacity: 0;
}
30% {
-webkit-transform: translateX(-50px) scale(0.4);
transform: translateX(-50px) scale(0.4);
}
70% {
-webkit-transform: translateX(0px) scale(1.2);
transform: translateX(0px) scale(1.2);
}
100% {
-webkit-transform: translatey(0px) scale(1);
transform: translatey(0px) scale(1);
opacity: 1;
}
}
@keyframes come-in {
0% {
-webkit-transform: translatey(100px);
transform: translatey(100px);
opacity: 0;
}
30% {
-webkit-transform: translateX(-50px) scale(0.4);
transform: translateX(-50px) scale(0.4);
}
70% {
-webkit-transform: translateX(0px) scale(1.2);
transform: translateX(0px) scale(1.2);
}
100% {
-webkit-transform: translatey(0px) scale(1);
transform: translatey(0px) scale(1);
opacity: 1;
}
}*/
/* 1.? - Card Layout in options.html only */
.floating-container {
position: fixed;
width: 100px;
height: 100px;
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 100px;
grid-gap: 2rem;
padding: 20px;
}
.card {
background-color: #089FB7;
padding: 5px;
border-radius: 10px;
border: 1px solid #66C92D;
color: #FFFFFF;
}
.card:hover,
.card:focus {
transform: scale(1.02);
}
.card:focus > .card__name {
bottom: 0;
right: 0;
margin: 35px 25px;
}
.floating-container:hover {
height: 300px;
}
.floating-container:hover .floating-button {
box-shadow: 0 10px 25px rgba(44, 179, 240, 0.6);
-webkit-transform: translatey(5px);
transform: translatey(5px);
-webkit-transition: all 0.3s;
transition: all 0.3s;
}
.floating-container:hover .element-container .float-element:nth-child(1) {
-webkit-animation: come-in 0.4s forwards 0.2s;
animation: come-in 0.4s forwards 0.2s;
}
.floating-container:hover .element-container .float-element:nth-child(2) {
-webkit-animation: come-in 0.4s forwards 0.4s;
animation: come-in 0.4s forwards 0.4s;
}
.floating-container:hover .element-container .float-element:nth-child(3) {
-webkit-animation: come-in 0.4s forwards 0.6s;
animation: come-in 0.4s forwards 0.6s;
}
.floating-container .floating-button {
position: absolute;
width: 65px;
height: 65px;
background: #66C92E;
.card:checked > .card__name {
bottom: 0;
border-radius: 50%;
left: 0;
right: 0;
margin: auto;
color: white;
line-height: 70px;
text-align: center;
font-size: 23px;
z-index: 100;
box-shadow: 0 10px 25px -5px rgba(44, 179, 240, 0.6);
cursor: pointer;
-webkit-transition: all 0.3s;
transition: all 0.3s;
}
.material-icons{
line-height: 0;
color: white;
.card__icon {
font-size: 2rem;
padding: 1rem;
display: grid;
}
/*
.floating-container .float-element {
.card__name {
font-weight: 400;
transform: translate(-50%, -50%);
position: relative;
display: block;
border-radius: 50%;
width: 50px;
height: 50px;
margin: 15px auto;
color: white;
font-weight: 500;
text-align: center;
line-height: 50px;
z-index: 0;
opacity: 0;
-webkit-transform: translateY(100px);
transform: translateY(100px);
left: 50%;
transition: 0.1s;
}
.floating-container .float-element .material-icons {
vertical-align: middle;
font-size: 16px;
/* 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;
}
.floating-container .float-element:nth-child(1) {
background: #42A5F5;
box-shadow: 0 20px 20px -10px rgba(66, 165, 245, 0.5);
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;
}
.floating-container .float-element:nth-child(2) {
background: #4CAF50;
box-shadow: 0 20px 20px -10px rgba(76, 175, 80, 0.5);
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;
}
.floating-container .float-element:nth-child(3) {
background: #FF9800;
box-shadow: 0 20px 20px -10px rgba(255, 152, 0, 0.5);
}*/

View File

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

View File

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

View File

@ -4,7 +4,6 @@
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
rel="stylesheet"
@ -22,8 +21,10 @@
<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>
@ -31,5 +32,6 @@
window.history.replaceState( null, null, window.location.href );
}
</script>
</body>
</html>

View File

@ -11,10 +11,9 @@
<p>
</p>
<form action="{{ url_for("ask_key")}}" method="post">
<input id="fields" type="password" name="apikey">
<input id="fields" type="submit" value="Submit">
<input id="pw" type="password" name="apikey">
<input id="sbm" type="submit" value="Submit">
</form>
</div>
</div>
</body>
{% endblock %}

View File

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

View File

@ -7,7 +7,6 @@
<h3>{{ error }}</h4>
{% endif %}
{% if button %}
<br>
<form
@ -74,8 +73,7 @@
lang="HTML"
id="editor"
name="body"
template="code-input"
style="height:550px;">
template="code-input">
</code-input>
<p>&nbsp;</p>
<label for="template_name">
@ -90,15 +88,6 @@
</form>
</div>
{% 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>
codeInput.registerTemplate("code-input", codeInput.templates.hljs(hljs,

6
app/wsgi.py Normal file
View File

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