/
voluntr.py
370 lines (275 loc) · 14.1 KB
/
voluntr.py
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
from flask import Flask, request, redirect, render_template, flash, url_for, json, make_response
from app import app, db
from models.org import Organization, Opportunity
from csvdata.orgcsv import add_orgs
from csvdata.oppscsv import add_opportunities
from filters import Filters
import datetime, os
from helpers.datetime_helpers import *
from helpers.opp_validation import *
from helpers.oauth_helpers import *
from helpers.category_helpers import *
from helpers.state_names import *
from helpers.query_helpers import *
from helpers.opp_search_helpers import *
is_production = 'IS_PRODUCTION' in os.environ
# Voluntr landing page - accessed at localhost:5000 for now
@app.route("/", methods=['GET'])
def index():
''' displays a landing page that invites the user to interact
with the app as either a individual volunteer or an organization/non-profit
representative '''
return render_template('index.html', title="Voluntr", is_production=is_production)
@app.route("/filters", methods=['GET', 'POST'])
def set_filters():
'''displays a form for volunteers to select their interests and availability'''
if request.method == 'POST':
cat = process_category(request.form)
avail = process_availability(request.form)
zipcode = process_zipcode(request.form)
distance = process_distance(request.form)
resp = make_response(redirect("/opportunities")) # tells the cookie to redirect to /opp after setting cookie
resp.set_cookie('filters', str("0/" + cat + "/" + avail + "/" + zipcode + "/" + distance )) # prepares cookie to be set with index of zero
return resp # sets cookie and redirects
categories = get_categories()
return render_template('volunteer/filters.html', title="Voluntr | Filters", categories = categories, is_production=is_production)
@app.route("/opportunities", methods=['GET'])
def opportunities():
'''display search results to volunteer'''
if 'filters' in request.cookies:
cookie = (request.cookies.get('filters')) #grabs cookie
filters = cookie.split("/") # splits cookie into list
index = int(filters[0]) # grabs index from list
cat = filters[1] # grabs categories from list
categories = cat.split("-")
avail = filters[2] # grabs available days
availability = avail.split("-") # splits into list
zipcode = filters[3] #grabs zipcode from list
distance = filters[4] #grabs distance from list
search = Filters(categories=categories, availability=availability, zipcode=zipcode, distance=distance) # creates filter with given category and availability
opps = search.search() #grabs list of opportunities
error = check_opps(opps)
if error:
flash(error)
return redirect('/filters')
opp = opps[index] # picks out the opp at index
index = increment(index, len(opps)) # increments index
event_date = readable_date(opp.startDateTime)
event_time = readable_times(opp.startDateTime, opp.duration)
resp = make_response(render_template('volunteer/opportunities.html',
opp=opp, event_date = event_date, event_time=event_time,
json=json, title="Voluntr | Browse Opportunities",is_production=is_production)
) # tells the cookie what to load while it sets itself
resp.set_cookie('filters', str(index) + "/" + cat + "/" + avail + "/" + zipcode + "/" + distance ) #preps cookie for setting
return resp # sets cookie and displays page
return redirect("/filters") # redirects to filters if no cookie exists
@app.route("/matches", methods=['GET'])
def display_matches():
'''lists all opportunities that a volunteer user saved'''
return render_template('volunteer/matches.html', title="Voluntr | Saved Opportunities", is_production=is_production)
@app.route("/match", methods=['POST'])
def display_match():
"""displays a single oppotunity that the user saved"""
oppId = request.form['oppId']
opp = get_opp_by_id(oppId)
event_date = readable_date(opp.startDateTime)
event_time = readable_times(opp.startDateTime, opp.duration)
return render_template('volunteer/single_opp.html', title="Voluntr | Saved Opportunity",
opp=opp, event_date = event_date, event_time=event_time, is_production=is_production)
@app.route("/org/login", methods=['GET'])
def org_login():
'''displays a form for organizations to signup or login to Voluntr'''
return render_template('organization/login.html', title="Voluntr | Log In", is_production=is_production)
@app.route("/org/login", methods=['POST'])
def login():
'''process a login attempt via OAuth token, return JSON'''
token = request.get_json()["authToken"]
# Start building a response
response_content = {"token": token}
# Check the validity of the OAuth token:
userid = process_oauth_token(token)
if (userid):
response_content["valid_token"] = True
# If the token is valid, see if the ID corresponds to an existing Voluntr account
org_account = Organization.query.filter_by(userid=userid).first()
if (org_account):
response_content["account_exists"] = True
else:
response_content["account_exists"] = False
else:
response_content["valid_token"] = False
return json.jsonify(response_content)
@app.route("/org/signup", methods=['POST'])
def signup():
'''process a sign-up attempt with an Oauth token and some form data'''
token = request.form['token']
orgName = request.form['orgName']
email = request.form['email']
url = request.form['url']
contactName = request.form['contactName']
# call process_oauth_token to convert token to google id
userid = process_oauth_token(token)
# retrieve the user data from the database
user = Organization.query.filter_by(id=userid).first()
# if userid not in database, create new user
if not (user):
new_user = Organization(userid=userid, email=email, orgName=orgName, contactName=contactName, url=url)
db.session.add(new_user)
db.session.commit()
# redirect user to logged-in view, and set cookie with OAuth token:
resp = make_response(redirect("/org/opportunities"))
resp.set_cookie('token', token)
return resp
@app.route("/org/opportunities", methods=['GET'])
def manage_opportunities():
'''displays all volunteer opportunities associated with an organization, with options to create
new opportunities, or view an individual opportunity'''
if 'token' in request.cookies:
org = process_org_token(request.cookies.get('token'))
# define variables to pass into the template
org_name = org.orgName
# check for deleted opportunities:
opp_id = request.args.get("del")
if opp_id:
hide_opp = get_opp_by_id(opp_id)
hide_opp.display = False
if validate_opp_data() == True:
db.session.commit()
# display all active opportunities on the org homepage
opps = db.session.query(Opportunity).filter_by(owner_id = org.userid, display = 1).all()
# format datetime into more readable strings
for opp in opps:
opp.startDateTime = readable_date(opp.startDateTime)
return render_template('organization/opportunities.html', title='Voluntr | Opportunities', headerName = org_name, opportunities = opps, is_production=is_production)
# just in case cookie isn't there for some reason
# this should probably lead to an error page, or at least somewhere else.. so i just did this instead
return redirect("/")
@app.route("/org/add", methods=['GET', 'POST'])
def new_opportunity():
'''displays a form for organizations to add a new volunteer opportunity'''
if request.method == 'POST':
# get all form data
title = validate_title(request.form["title"])
address = request.form["address"]
city = request.form["city"]
state = request.form["state"]
zip_code = request.form["zipcode"]
category_class = request.form["category"]
category = get_category(category_class)
description = validate_description(request.form["description"])
next_steps = validate_next_steps(request.form["nextSteps"])
# Either get the form's date/times, or add the date/times we've chosen to signify "flexible schedule"
if request.form.get("flexible"):
date = "2100-01-01"
start_time = "23:30"
end_time = "23:30"
else:
date = request.form["date"]
start_time = request.form["startTime"]
end_time = request.form["endTime"]
# format dateStartTime
start_date_time = create_datetime(date, start_time)
# get duration as an int
duration = get_duration(start_time, end_time)
# user is obtained by taking an ouath token from a cookie and using a function that grabs the id and queries th DB
org = process_org_token(request.cookies.get('token'))
# TODO: server-side validation will be added here soon.
if validate_opp_data():
# save new, opportunity to db.
new_opp = Opportunity(title, address, city,
state, zip_code, description,
start_date_time, duration,
category_class, category,
next_steps, org.userid)
db.session.add(new_opp)
db.session.commit()
return redirect('/org/opportunities')
# response for GET requests:
categories = get_categories()
states = get_states()
return render_template('organization/add.html', title='Voluntr | Add Opportunity', categories = categories, states = states, is_production=is_production)
@app.route("/org/profile", methods=['GET', 'POST'])
def view_profile():
''' displays a form pre-populated with data about the organization account'''
if request.method == 'POST':
org = process_org_token(request.cookies.get('token'))
# update org data with form data
org.contactName = request.form["contactName"]
org.email = request.form["email"]
org.orgName = request.form["orgName"]
org.url = request.form["url"]
# TODO: server-side validation will be added here soon.
if validate_org_data() == True:
# save updated opportunity to db.
db.session.commit()
return redirect('/org/opportunities')
org = process_org_token(request.cookies.get('token'))
return render_template('organization/profile.html', org=org, title='Voluntr | Account Profile', is_production=is_production)
@app.route("/org/edit", methods=['GET', 'POST'])
def edit_opportunity():
''' displays a form pre-populated with data for a single opportunity, so the user can
either edit individual fields and repost the opportunity, or remove the opportunity
from the app '''
if request.method == "POST":
id = request.args.get("id", type=int)
opp = get_opp_by_id(id)
# use form data to update opportunity
opp.title = validate_title(request.form["title"])
opp.address = request.form["address"]
opp.city = request.form["city"]
opp.state = request.form["state"]
opp.zipcode = request.form["zipcode"]
opp.description = validate_description(request.form["description"])
# Either get the form's date/times, or add the date/times we've chosen to signify "flexible schedule"
if request.form.get("flexible"):
date = "2100-01-01"
start_time = "23:30"
end_time = "23:30"
else:
date = request.form["date"]
start_time = request.form["startTime"]
end_time = request.form["endTime"]
# format dateStartTime and duration
opp.startDateTime = create_datetime(date, start_time)
opp.duration = get_duration(start_time, end_time)
opp.category_class = request.form["category"]
opp.category = get_category(request.form["category"])
opp.nextSteps = validate_next_steps(request.form["nextSteps"])
# TODO: add real validation steps here
if validate_opp_data() == True:
# update db with new form information
db.session.commit()
return redirect('/org/opportunities')
# for GET requests:
id = request.args.get("id", type=int)
opp = get_opp_by_id(id)
event_date = opp.startDateTime.strftime('%Y-%m-%d')
time_start = get_start_time(opp.startDateTime)
time_end = get_end_time(opp.startDateTime, opp.duration)
categories = get_categories()
states = get_states()
return render_template('organization/edit.html', title='Voluntr | Edit Opportunity', opp=opp,
event_date = event_date, time_start = time_start, time_end = time_end,
categories=categories, states=states, is_production=is_production)
@app.route("/org/opportunity", methods=['GET'])
def show_opportunity():
'''displays details about a specific volunteer opportunity'''
id = request.args.get("id", type=int)
opp = get_opp_by_id(id)
event_date = readable_date(opp.startDateTime)
event_time = readable_times(opp.startDateTime, opp.duration)
return render_template('organization/preview.html', title="Voluntr | Preview Post", opp=opp,
event_date = event_date, event_time = event_time, is_production=is_production)
# Run this route upon app startup to load sample data
@app.route("/drop_create", methods=['GET'])
def dropCreate():
# disable this route for production environment
if not is_production:
db.drop_all()
db.create_all()
add_orgs()
add_opportunities()
return redirect('/')
# runs the app, always the last line
if __name__ == '__main__':
app.run(threaded = True)