11import json
2+ import os
23import requests
34
5+ import requests
6+ import boto3
7+
8+ from config import AWS_ACCESS_KEY_ID , AWS_SECRET_ACCESS_KEY
49from flask import Blueprint
510from flask import request
611from flask import jsonify
712from flask import redirect
813
9- from sqlalchemy import desc
14+ from sqlalchemy import func , desc
1015
1116import structlog
17+ from werkzeug .utils import secure_filename
1218
1319from conditional .util .context_processors import get_member_name
1420
15- from conditional .models .models import MajorProject
21+ from conditional .models .models import MajorProject , MajorProjectSkill
1622
1723from conditional .util .ldap import ldap_is_eval_director
1824from conditional .util .ldap import ldap_get_member
@@ -32,19 +38,39 @@ def display_major_project(user_dict=None):
3238 log = logger .new (request = request , auth_dict = user_dict )
3339 log .info ("Display Major Project Page" )
3440
41+ # There is probably a better way to do this, but it does work
42+
43+ proj_list = db .session .query (
44+ MajorProject .id ,
45+ MajorProject .date ,
46+ MajorProject .uid ,
47+ MajorProject .name ,
48+ MajorProject .tldr ,
49+ MajorProject .timeSpent ,
50+ MajorProject .description ,
51+ MajorProject .status ,
52+ func .array_agg (MajorProjectSkill .skill ).label ("skills" )
53+ ).outerjoin (MajorProjectSkill ,
54+ MajorProject .id == MajorProjectSkill .project_id
55+ ).group_by (MajorProject .id
56+ ).where (MajorProject .date >= start_of_year ()
57+ ).order_by (desc (MajorProject .date ))
58+
3559 major_projects = [
3660 {
61+ "id" : p .id ,
62+ "date" : p .date ,
3763 "username" : p .uid ,
3864 "name" : ldap_get_member (p .uid ).cn ,
3965 "proj_name" : p .name ,
66+ "tldr" : p .tldr ,
67+ "time_spent" : p .timeSpent ,
68+ "skills" : p .skills ,
69+ "desc" : p .description ,
4070 "status" : p .status ,
41- "description" : p .description ,
42- "id" : p .id ,
43- "is_owner" : bool (user_dict ["username" ] == p .uid ),
71+ "is_owner" : bool (user_dict ["username" ] == p .uid )
4472 }
45- for p in MajorProject .query .filter (
46- MajorProject .date > start_of_year ()
47- ).order_by (desc (MajorProject .id ))
73+ for p in proj_list
4874 ]
4975
5076 major_projects_len = len (major_projects )
@@ -53,8 +79,32 @@ def display_major_project(user_dict=None):
5379 "major_project_submission.html" ,
5480 major_projects = major_projects ,
5581 major_projects_len = major_projects_len ,
56- username = user_dict ["username" ],
57- )
82+ username = user_dict ["username" ])
83+
84+ @major_project_bp .route ("/major_project/upload" , methods = ["POST" ])
85+ @auth .oidc_auth ("default" )
86+ @get_user
87+ def upload_major_project_files (user_dict = None ):
88+ log = logger .new (request = request , auth_dict = user_dict )
89+ log .info ('Uploading Major Project File(s)' )
90+
91+ log .info (f"user_dict: { user_dict } " )
92+
93+ if len (list (request .files .keys ())) < 1 :
94+ return "No file" , 400
95+
96+ # Temporarily save files to a place, to be uploaded on submit
97+
98+ for _ , file in request .files .lists ():
99+ file = file [0 ]
100+ safe_name = secure_filename (file .filename )
101+ filename = f"/tmp/{ user_dict ['username' ]} /{ safe_name } "
102+
103+ os .makedirs (os .path .dirname (filename ), exist_ok = True )
104+ file .save (filename )
105+
106+ return jsonify ({"success" : True }), 200
107+
58108
59109
60110@major_project_bp .route ("/major_project/submit" , methods = ["POST" ])
@@ -65,27 +115,88 @@ def submit_major_project(user_dict=None):
65115 log .info ("Submit Major Project" )
66116
67117 post_data = request .get_json ()
118+
119+ print (f"Post Data: { post_data } " ) # TODO: Remove this later
120+
68121 name = post_data ["projectName" ]
122+ tldr = post_data ['projectTldr' ]
123+ time_spent = post_data ['projectTimeSpent' ]
124+ skills = post_data ['projectSkills' ]
69125 description = post_data ["projectDescription" ]
70126
71- if name == "" or description == "" :
127+ user_id = user_dict ['username' ]
128+
129+ log .info (user_id )
130+
131+ # All fields are required in order to be able to submit the form
132+ # TODO: Do we want any of the fields to have enforced min or max lengths?
133+ if name == "" or tldr == "" or time_spent == "" or skills == "" or description == "" :
72134 return jsonify ({"success" : False }), 400
73- project = MajorProject (user_dict ["username" ], name , description )
135+
136+ # TODO: Ensure all the information is being passed to the object
137+ project = MajorProject (user_id , name , tldr , time_spent , description )
74138
75- # Don't you dare try pinging @channel
139+ # Save the info to the database
140+ db .session .add (project )
141+ db .session .commit ()
142+
143+
144+ project = MajorProject .query .filter (
145+ MajorProject .name == name and MajorProject .uid == user_id
146+ ).first ()
147+
148+ skills_list = filter (lambda x : x != 'None' , skills )
149+ print (f"Skills: { list (skills_list )} " )
150+
151+ for skill in skills_list :
152+ skill = skill .strip ()
153+
154+ if skill != "" and skill != 'None' :
155+ mp_skill = MajorProjectSkill (project .id , skill )
156+ db .session .add (mp_skill )
157+
158+ db .session .commit ()
159+
160+ # Fail if attempting to retreive non-existent project
161+ if project is None :
162+ return jsonify ({"success" : False }), 500
163+
164+ # Sanitize input so that the Slackbot cannot ping @channel
76165 name = name .replace ("<!" , "<! " )
77166
78- username = user_dict ["username" ]
79- send_slack_ping (
80- {
81- "text" : f"<!subteam^S5XENJJAH> *{ get_member_name (username )} * ({ username } )"
82- f" submitted their major project, *{ name } *! Please be sure to reach out"
83- f" to E-Board members to answer any questions they may have regarding"
84- f" your project!"
85- }
167+ # Connect to S3 bucket
168+ s3 = boto3 .resource (
169+ "s3" ,
170+ endpoint_url = "https://s3.csh.rit.edu" ,
171+ aws_access_key_id = AWS_ACCESS_KEY_ID ,
172+ aws_secret_access_key = AWS_SECRET_ACCESS_KEY
86173 )
87- db .session .add (project )
88- db .session .commit ()
174+
175+ bucket = s3 .create_bucket (Bucket = "major-project-media" )
176+
177+ # Collect all the locally cached files and put them in the bucket
178+ for file in os .listdir (f"/tmp/{ user_id } " ):
179+ filepath = f"/tmp/{ user_id } /{ file } "
180+
181+ # TODO: Remove this later
182+ print (f"Filepath in S3: { filepath } " )
183+
184+ bucket .upload_file (filepath , f"{ project .id } --{ file } " )
185+ os .remove (filepath )
186+
187+ # Delete the temp directory once all the files have been stored in S3
188+ os .rmdir (f"/tmp/{ user_id } " )
189+
190+
191+ # Send the slack ping only after we know that the data was properly saved to the DB
192+ # TODO: Maybe add more info to the slack ping?
193+ # send_slack_ping(
194+ # {
195+ # "text": f"<!subteam^S5XENJJAH> *{get_member_name(user_id)}* ({user_id})"
196+ # f" submitted their major project, *{name}*!"
197+ # }
198+ # )
199+
89200 return jsonify ({"success" : True }), 200
90201
91202
@@ -106,8 +217,10 @@ def major_project_review(user_dict=None):
106217
107218 print (post_data )
108219 MajorProject .query .filter (MajorProject .id == pid ).update ({"status" : status })
220+
109221 db .session .flush ()
110222 db .session .commit ()
223+
111224 return jsonify ({"success" : True }), 200
112225
113226
@@ -123,12 +236,14 @@ def major_project_delete(pid, user_dict=None):
123236
124237 if creator == user_dict ["username" ] or ldap_is_eval_director (user_dict ["account" ]):
125238 MajorProject .query .filter (MajorProject .id == pid ).delete ()
239+
126240 db .session .flush ()
127241 db .session .commit ()
242+
128243 return jsonify ({"success" : True }), 200
129244
130245 return "Must be project owner to delete!" , 401
131246
132247
133248def send_slack_ping (payload ):
134- requests .post (app .config ["WEBHOOK_URL" ], json .dumps (payload ), timeout = 120 )
249+ requests .post (app .config ["WEBHOOK_URL" ], json .dumps (payload ), timeout = 120 )
0 commit comments