-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathmajor_project_submission.py
More file actions
224 lines (164 loc) · 6.79 KB
/
major_project_submission.py
File metadata and controls
224 lines (164 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import collections
import json
import os
from flask import Blueprint
from flask import request
from flask import jsonify
from flask import redirect
import requests
import boto3
import structlog
from werkzeug.utils import secure_filename
from conditional import db, get_user, auth, app
from conditional.models.models import MajorProject
from conditional.models.models import MajorProjectSkill
from conditional.util.context_processors import get_member_name
from conditional.util.ldap import ldap_get_member
from conditional.util.flask import render_template
from conditional.util.s3 import list_files_in_folder
from conditional.util.user_dict import user_dict_is_eval_director
from conditional.util.major_project import get_project_list
collections.Callable = collections.abc.Callable
logger = structlog.get_logger()
major_project_bp = Blueprint("major_project_bp", __name__)
@major_project_bp.route("/major_project/")
@auth.oidc_auth("default")
@get_user
def display_major_project(user_dict=None):
log = logger.new(request=request, auth_dict=user_dict)
log.info("Display Major Project Page")
# There is probably a better way to do this, but it does work
proj_list: list = get_project_list()
bucket: str = app.config['S3_BUCKET_ID']
major_projects: list[dict] = [
{
"id": p.id,
"date": p.date,
"username": p.uid,
"name": ldap_get_member(p.uid).cn,
"proj_name": p.name,
"tldr": p.tldr,
"time_spent": p.time_spent,
"skills": p.skills,
"desc": p.description,
"links": list(filter(None, p.links.split("\n"))),
"status": p.status,
"is_owner": bool(user_dict["username"] == p.uid),
"files": list_files_in_folder(bucket, f"{p.id}/")
}
for p in proj_list
]
# return names in 'first last (username)' format
return render_template(
"major_project_submission.html",
major_projects=major_projects,
major_projects_len=len(major_projects),
username=user_dict["username"])
@major_project_bp.route("/major_project/upload", methods=["POST"])
@auth.oidc_auth("default")
@get_user
def upload_major_project_files(user_dict=None):
log = logger.new(request=request, auth_dict=user_dict)
log.info('Uploading Major Project File(s)')
if len(list(request.files.keys())) <1:
return "No file", 400
# Temporarily save files to a place, to be uploaded on submit
for _, file in request.files.lists():
file = file[0]
safe_name: str = secure_filename(file.filename)
filename = f"/tmp/{user_dict['username']}/{safe_name}"
os.makedirs(os.path.dirname(filename), exist_ok=True)
file.save(filename)
return jsonify({"success": True}), 200
@major_project_bp.route("/major_project/submit", methods=["POST"])
@auth.oidc_auth("default")
@get_user
def submit_major_project(user_dict=None):
log = logger.new(request=request, auth_dict=user_dict)
log.info("Submit Major Project")
post_data = request.get_json()
name = post_data["projectName"]
tldr = post_data['projectTldr']
time_spent = post_data['projectTimeSpent']
skills = post_data['projectSkills']
description = post_data["projectDescription"]
links = post_data['projectLinks']
user_id = user_dict['username']
log.info(user_id)
# All fields are required in order to be able to submit the form
if not name or not tldr or not time_spent or not description:
return jsonify({"success": False}), 400
project: MajorProject = MajorProject(user_id, name, tldr, time_spent, description, links)
# Save the info to the database
db.session.add(project)
db.session.commit()
project = MajorProject.query.filter(
MajorProject.name == name,
MajorProject.uid == user_id
).first()
skills_list: list = list(filter(lambda x: x != 'None', skills))
for skill in skills_list:
skill = skill.strip()
if skill not in ("", 'None'):
mp_skill = MajorProjectSkill(project.id, skill)
db.session.add(mp_skill)
db.session.commit()
# Fail if attempting to retreive non-existent project
if project is None:
return jsonify({"success": False}), 500
# Sanitize input so that the Slackbot cannot ping @channel
name = name.replace("<!", "<! ")
# Connect to S3 bucket
s3 = boto3.client("s3",
aws_access_key_id=app.config['AWS_ACCESS_KEY_ID'],
aws_secret_access_key=app.config['AWS_SECRET_ACCESS_KEY'],
endpoint_url=app.config['S3_URI'])
# Collect all the locally cached files and put them in the bucket
temp_dir: str = f"/tmp/{user_id}"
if os.path.exists(temp_dir):
for file in os.listdir(temp_dir):
filepath = f"{temp_dir}/{file}"
s3.upload_file(filepath, 'major-project-media', f"{project.id}/{file}")
os.remove(filepath)
# Delete the temp directory once all the files have been stored in S3
os.rmdir(temp_dir)
# Send the slack ping only after we know that the data was properly saved to the DB
send_slack_ping(
{
"text": f"<!subteam^S5XENJJAH> *{get_member_name(user_id)}* ({user_id})"
f" submitted their major project, *{name}*!"
}
)
return jsonify({"success": True}), 200
@major_project_bp.route("/major_project/review", methods=["POST"])
@auth.oidc_auth("default")
@get_user
def major_project_review(user_dict=None):
log = logger.new(request=request, auth_dict=user_dict)
if not user_dict_is_eval_director(user_dict["account"]):
return redirect("/dashboard", code=302)
post_data = request.get_json()
pid = post_data["id"]
status = post_data["status"]
log.info(f"{status} Major Project ID: {pid}")
print(post_data)
MajorProject.query.filter(MajorProject.id == pid).update({"status": status})
db.session.flush()
db.session.commit()
return jsonify({"success": True}), 200
@major_project_bp.route("/major_project/delete/<pid>", methods=["DELETE"])
@auth.oidc_auth("default")
@get_user
def major_project_delete(pid, user_dict=None):
log = logger.new(request=request, auth_dict=user_dict)
log.info(f"Delete Major Project ID: {pid}")
major_project = MajorProject.query.filter(MajorProject.id == pid).first()
creator = major_project.uid
if creator == user_dict["username"] or user_dict_is_eval_director(user_dict["account"]):
MajorProject.query.filter(MajorProject.id == pid).delete()
db.session.flush()
db.session.commit()
return jsonify({"success": True}), 200
return "Must be project owner to delete!", 401
def send_slack_ping(payload):
requests.post(app.config["WEBHOOK_URL"], json.dumps(payload), timeout=120)