From 43cdcff115f485e1736900742eb82925a708e155 Mon Sep 17 00:00:00 2001 From: Norm Rasmussen Date: Thu, 9 Mar 2023 15:15:42 -0500 Subject: [PATCH] CSV & Manual Upload is working! Fixed both features. --- app/__pycache__/routes.cpython-311.pyc | Bin 15563 -> 17069 bytes app/routes.py | 137 +++++++++++++------------ app/static/css/styles.css | 2 +- app/templates/bulk_add.html | 23 +++-- app/templates/csv.html | 2 +- app/templates/index.html | 8 +- 6 files changed, 89 insertions(+), 83 deletions(-) diff --git a/app/__pycache__/routes.cpython-311.pyc b/app/__pycache__/routes.cpython-311.pyc index ae269c9b369b0f5537c6ab98ac7847b7671b2ede..e06da3ab6d3b671b405439c41ceb2376009181b5 100644 GIT binary patch delta 6310 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 QvwJGms+C*eU1Q|G0fb%B(f|Me delta 5262 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 DQ{}rI diff --git a/app/routes.py b/app/routes.py index 5510247..d5c8132 100644 --- a/app/routes.py +++ b/app/routes.py @@ -37,7 +37,7 @@ def download_csv(): def key_response(response): if "402" in str(response): error = response.text - return render_template("index.html", title="Error Home", errors=error) + return render_template("index.html", title="Error Home", error=error) if "401" in str(response): error = [ "Unauthorized access error.", @@ -45,26 +45,20 @@ def key_response(response): "such as the key being changed.", "Remember, they are paired to each educator!", ] - return render_template("index.html", title="Error Home", errors=error) + 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") + return render_template("bulk_add.html", title="Active Session") def allowed_file(filename): return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS -@app.route("/dev", methods=["GET", "POST"]) -def dev_test(): - return render_template("options.html", title="Dev Test") - - # DONE: Remove header for main page. # DONE: Leave boxes but change outcome depending if file has been uploaded. @@ -75,35 +69,45 @@ def ask_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 session.get("key"): - return render_template("options.html", title="Options Home") + specials = '"!@#$%^&*()-+?_=,<>/"' + #if session.get("key"): + # return render_template("bulk_add.html", title="Options Home") 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", errors=error) + if (any(char in specials for char in session["key"]) or + re.search(r"[\s]", session["key"])): + error = "Invalid Key." + session.clear() + return render_template("index.html", title="Home", error=error) if session["key"] is not None and len(session["key"]) > 10: endpoint = "/v2/properties/school" headers = {"accept": "application/json", "X-Api-Key": session["key"]} response = requests.get(url + endpoint, headers=headers) return key_response(response) error = "Hm. That doesn't seem right" + session.clear() return render_template("index.html", title="Home", error=error) - + session.clear() return render_template("index.html", title="Home") @app.route("/", methods=["GET", "POST"]) def render_home(): if session.get("key"): - return render_template("options.html", title="Home") + return render_template("bulk_add.html", title="Home") return render_template("index.html", title="Enter Key") -@app.route("/options", methods=["GET", "POST"]) -@app.route("/", methods=["GET", "POST"]) +#@app.route("/options", methods=["GET", "POST"]) +#@app.route("/bulk_add", methods=["GET", "POST"]) +@app.route("/clear_session", methods=["GET", "POST"]) def clear_session(): - session.clear() + if session.get("key"): + print("Session Formula") + # [session.pop(key) for key in list(session.keys())] + 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") @@ -111,19 +115,9 @@ def clear_session(): def table(): return render_template("table.html", tables=[session["dfhtml"]], titles=["Table"]) -""" -uploaded_file = request.files['file'] -if uploaded_file.filename != '': -print("File has name") -uploaded_file.save(uploaded_file.filename) -return render_template("options.html", title="Home, Now with CSV!") -""" - @app.route("/upload_file", methods=["GET", "POST"]) -@app.route("/bulk_add", methods=["GET", "POST"]) def upload_file(): print("Uploading CSV") - csvData = pd.DataFrame() if request.method == "POST": if "file" not in request.files: flash("No file found or uploaded") @@ -136,51 +130,51 @@ def upload_file(): # return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) - session["file"] = filename file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename) + session["file"] = filename session["filepath"] = file_path file.save(file_path) - # csvData = pd.read_csv(file_path) file = list(csv.reader(open(file_path, "r"))) - emails = [] - groups = [] - for col in file: - emails.append(col[0]) - #groups.append(col(range(1,20))) - print(emails) - #print(groups) + return divide_values(file) + return render_template("bulk_add.html", title="Bulk Add") - #print(emails) - # for item in data: - # print(item[0]) - # lines = reader(csvData) - # csvData = list(lines) - # print(csvData) - selection = request.form.get('learner-groups') - if selection == "all-groups": - if request.form['preview']: - return api_csv_all_groups(csvData) - elif request.form['submit']: - return "Submitted Selection" - elif selection == "some-groups": - return api_csv_some_groups(csvData) - return render_template( - "bulk_add.html", table=html_data, title="Uploaded File" - ) - return render_template("options.html", title="Home, now with a CSV Table!") +def divide_values(file): + emails = [] + groups = [] + selection = request.form.get('learner-groups') + if request.form['submit']: + if selection == "all-groups": + for item in file[1:]: + emails.append(item[0]) + groups.append(item[1:]) + # FEAT: These two extract the groups and emails into two lists + groups = [item for group in groups for item in group] + groups = list(set(groups)) + print(emails) + print(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.form['preview']: + error="Preview Button Still Under Construction. Try again later." + return render_template("bulk_add.html", error=error, title="Preview Not Yet") -def api_csv_all_groups(csvData): - # htmlcsv = csvData.to_html() - # for items in csvData: - - - # emails = csvData['Email'].values.tolist() - # emails = [nan for nan in emails if str(nan) != 'nan'] - - # groups = csvData['Groups'].values.tolist() - # groups = [nan for nan in groups if str(nan) != 'nan'] + return render_template( + "bulk_add.html", title="Uploaded File" + ) +def api_csv_parse(emails, groups): if emails and groups: return api_add_ppl_groups(emails, groups) elif emails: @@ -189,7 +183,16 @@ def api_csv_all_groups(csvData): return api_add_groups(groups) return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission") -def api_csv_some_groups(csvData): +def api_csv_all_groups(emails, groups): + if emails and groups: + return api_add_ppl_groups(emails, groups) + elif emails: + return api_add_ppl(emails) + elif groups: + return api_add_groups(groups) + return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission") + +def api_csv_some_groups(emails, groups): htmlcsv = csvData.to_html() emails = csvData['Email'].values.tolist() diff --git a/app/static/css/styles.css b/app/static/css/styles.css index 8955a2c..3d214e2 100644 --- a/app/static/css/styles.css +++ b/app/static/css/styles.css @@ -134,7 +134,7 @@ img { .instructions-list{ display:flex; - justify-content: center; + justify-content: space-around; width: 100%; } diff --git a/app/templates/bulk_add.html b/app/templates/bulk_add.html index b2d19cf..5aabfe6 100644 --- a/app/templates/bulk_add.html +++ b/app/templates/bulk_add.html @@ -3,15 +3,16 @@ {% include 'header.html' %} {% include 'logo.html' %} {% block content %} -

Please find your options below. Some things to note:

+

You're currently accessing {{ session.school }}.

+

Instructions

    -
  • Left side - Manual entry: +
  • Left side - Manual entry:

      -
    • Both fields don't need to be filled out!
    • +
    • Both fields don't need to be filled out!
    • You can add just people or just groups.
    • -
    • Adding both Emails and Groups will add all people to all groups.
    • +
    • Using both fields will add all people to all groups.
@@ -19,19 +20,19 @@
    -
  • Right side - CSV Upload: +
  • Right side - CSV Upload:

      -
    • The CSV will only look for one or both columns with the exact wording as the header row!
    • The Header rows must be Email and/or Groups
    • -
    • You can easily add people to multiple groups.
    • -
    • To add every person to every group, simply upload the CSV and select option 1 below.
    • -
    • For adding people to specific groups, format the csv as | Name | Group 1 | Group 2 | and select option 2 below.
    • -
    • There are no limits the number of people or groups that can be added.
    • +
    • Please format the csv like this:
    • +
    • Email,Groups
    • +
    • email@email.com, group1, group2, group3
    • +
    • email2@email.com, group5, group1
+

 

{% if error %}

{{ error }}

{% endif %} @@ -43,7 +44,7 @@

Emails

-

Please paste in the Group Names which these learners should be added to.

+

Group Names

diff --git a/app/templates/csv.html b/app/templates/csv.html index 1f4708e..25cf8db 100644 --- a/app/templates/csv.html +++ b/app/templates/csv.html @@ -14,7 +14,7 @@
-

+ diff --git a/app/templates/index.html b/app/templates/index.html index 8939499..53eb833 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -3,9 +3,11 @@ {% include 'logo.html' %} {% block content %}

Hello! Please click below to enter your key.

- {% for error in errors %} -

{{ error }}

- {% endfor %} +

+ {% if error %} + {{ error }} + {% endif %} +