def view_errand(request, errand_id, access_id): errand = Errand.objects.filter(access_id=access_id).first() days_to_add = 1 if errand.urgency == 1 else 3 errand_expiration = errand.requested_time + timedelta(days=days_to_add) errand_expiration_hours = math.floor( (errand_expiration - timezone.now()).total_seconds() / 3600) contact_preference = 'Texting' if errand.requestor.contact_preference == 1 else 'Phone call' address = helper.gmaps_reverse_geocode( (errand.requestor.lat, errand.requestor.lon)) requestor_number = helper.format_mobile_number( errand.requestor.mobile_number) if errand is not None: return render( request, 'errand_matcher/errand-view.html', { 'errand_number': int(errand.id), 'requestor': errand.requestor, 'time_left': errand_expiration_hours, 'additional_info': errand.additional_info, 'contact_preference': contact_preference, 'address': address, 'requestor_number': requestor_number, 'base_url': helper.get_base_url() }) else: return render(request, 'errand_matcher/404.html', {'base_url': helper.get_base_url()})
def partner_setup(request): if request.method == 'POST': name = request.POST['setup-name'] org = request.POST['setup-organization'] email = request.POST['setup-email'] phone = request.POST['setup-phone-number'] content = "We received a request to setup a partner account:\n"\ "Name: {}\n"\ "Organization: {}\n"\ "Email: {}\n"\ "Phone: {}\n".format(name, org, email, phone) message = Mail(from_email='*****@*****.**', to_emails='*****@*****.**', subject='LivelyHood Partner Organization Interest', html_content=content) try: sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY')) response = sg.send(message) print(response.status_code) print(response.body) print(response.headers) except Exception as e: print(e.message) return render(request, 'errand_matcher/partner.html', { 'setup_done': 'true', 'base_url': helper.get_base_url() }) else: return HttpResponseNotAllowed()
def remind_volunteer_to_complete(errand, volunteer): url = "{}/errand/{}/status/{}".format(helper.get_base_url(), errand.id, errand.access_id) message = "LivelyHood here! Just checking in on the progress of this delivery."\ " Click here to see the details again or let us know if you’ve completed it! {} Thank you!".format(url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message)
def confirm_successful_claim(errand, volunteer): url = "{}/errand/{}/status/{}".format(helper.get_base_url(), errand.id, errand.access_id) message = "Thanks for accepting this request! See details at {}".format( url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message)
def welcome_new_volunteer(volunteer): tiny_faq_url = helper.make_tiny_url("{}#above-faq".format( helper.get_base_url())) message = "Welcome to #TeamLivelyHood! We’ll text you when somebody nearby needs your help. If you ever need assistance, you can reach us here."\ "\n\nMessage and data rates may apply. Reply STOP to opt-out of messages".format(tiny_faq_url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message)
def view_errand(request, errand_id, access_id): errand = Errand.objects.filter(access_id=access_id).first() errand_expiration_hours = math.floor( (errand.due_by - timezone.now()).total_seconds() / 3600) requestor_number = helper.format_mobile_number( errand.requestor.mobile_number) if errand is not None: return render( request, 'errand_matcher/errand-view.html', { 'access_id': access_id, 'errand': errand, 'errand_number': int(errand.id), 'time_left': errand_expiration_hours, 'requestor_number': requestor_number, 'base_url': helper.get_base_url() }) else: return render(request, 'errand_matcher/404.html', {'base_url': helper.get_base_url()})
def partner_password_reset(request): if request.method == 'POST': recover_email = request.POST['recover-email'] user = User.objects.filter(username=recover_email).first() if user is not None: # Generate a password reset token uidb64 = urlsafe_base64_encode(force_bytes(user.pk)) password_reset_token = PasswordResetTokenGenerator().make_token( user) password_reset_url = helper.get_base_url( ) + "/partner/reset/{}/{}".format(uidb64, password_reset_token) content = "<p>Hi there, we received a password reset request for your account. Please click here to reset your password:</p>"\ "<p><a href='{}'>{}</a></p>"\ "<p>If you did not request a password change, please ignore this message. Thanks,</p>"\ "<p>Team LivelyHood</p>"\ "<p>{}</p>".format(password_reset_url, password_reset_url, helper.get_base_url()) print(content) message = Mail(from_email='*****@*****.**', to_emails=recover_email, subject='LivelyHood Password Reset', html_content=content) try: sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY')) response = sg.send(message) print(response.status_code) print(response.body) print(response.headers) except Exception as e: print(e.message) return render(request, 'errand_matcher/partner-password-reset-done.html', {'base_url': helper.get_base_url()}) else: return render(request, 'errand_matcher/partner-password-reset.html', {'base_url': helper.get_base_url()})
def alert_volunteer_to_claim_errand(errand, volunteer): url = "{}/errand/{}/accept/{}".format( helper.get_base_url(), errand.id, helper.strip_mobile_number(volunteer.mobile_number)) deadline_str = helper.convert_errand_deadline_to_str(errand) message = "LivelyHood here! {} needs help getting groceries! Can you make a delivery by {}? "\ "Click here for more information and to let us know if you can help. {}".format( errand.requestor.user.first_name, deadline_str, url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message)
def partner_password_reset_confirm(request, uidb64, token): if request.method == 'POST': uid = urlsafe_base64_decode(uidb64).decode() user = User.objects.filter(pk=uid).first() password = request.POST['new-password'] try: validate_password(password) except: return render( request, 'errƒand_matcher/partner-password-reset-confirm.html', { 'base_url': helper.get_base_url(), 'warning': 'Your password is not valid.', 'user': user }) user.set_password(password) user.save() authenticated_user = authenticate(request, username=user.username, password=password) login(request, authenticated_user) return redirect('/partner/dashboard/') else: uid = urlsafe_base64_decode(uidb64).decode() user = User.objects.filter(pk=uid).first() token_generator = PasswordResetTokenGenerator() if user is not None and token_generator.check_token(user, token): return render( request, 'errand_matcher/partner-password-reset-confirm.html', { 'base_url': helper.get_base_url(), 'user': user }) else: return redirect('/error/')
def match_errands(): logger.info('Matching errands') errands = Errand.objects.filter(status=1) for errand in errands: # convert to failed status if exceeded 1 day since creation # for urgent or 3 days for non-urgent days_to_add = 1 if errand.urgency == 1 else 3 errand_expiration = errand.requested_time + timedelta(days=days_to_add) if timezone.now() > errand_expiration: errand.status = 4 errand.save() # alert on-call staffer site_configuration = SiteConfiguration.objects.first() message = 'ERRAND FAILURE! {} {}: {} requested at {}'.format( errand.requestor.user.first_name, errand.requestor.user.last_name, helper.strip_mobile_number(errand.requestor.mobile_number), errand.requested_time) helper.send_sms( helper.format_mobile_number(get_support_mobile_number()), message) else: if errand.request_round < 5: # TO DO: what if there are no volunteers? volunteers = helper.match_errand_to_volunteers(errand) deadline_str = '{} at 6 p.m.'.format(weekday_lookup[( timezone.now() + timedelta(days=days_to_add)).date().isoweekday()]) for v in volunteers: url = "{}/errand/{}/accept/{}".format( helper.get_base_url(), errand.id, helper.strip_mobile_number(v.mobile_number)) message = "LivelyHood here! {} needs help getting groceries! Can you make a delivery by {}?"\ " Click here for more information and to let us know if you can help. {}".format( errand.requestor.user.first_name, deadline_str, url) helper.send_sms( helper.format_mobile_number(v.mobile_number), message) errand.contacted_volunteers.add(v) errand.request_round += 1 errand.save()
def partner_password_reset_done(request): return render(request, 'errand_matcher/partner-password-reset-done.html', {'base_url': helper.get_base_url()})
def partner_request(request): if request.method == 'POST': edit_errand_id = request.POST.get('edit-errand-id') if edit_errand_id is None: # new request requestor_number = request.POST['add-requestor-phone'] requestor_number_digits = ''.join(i for i in requestor_number if (i.isdigit() or i == '+')) requestor_first_name = request.POST['add-requestor-first-name'] requestor_last_name = request.POST['add-requestor-last-name'] requestor_address = request.POST['add-requestor-address'] requestor_apartment = request.POST['add-requestor-apartment'] requestor_internal_note = request.POST['internal-notes'] errand_due_by = request.POST['errand-due-by'] + " 23:59:59" errand_instructions = request.POST['volunteer-instructions'] coord_location = helper.gmaps_geocode(requestor_address) requestor = helper.get_user_from_mobile_number_str( requestor_number_digits, user_type='requestor') if len(requestor_number_digits) == 10: requestor_number_digits = '+1' + requestor_number_digits if requestor is None: user = User(username=requestor_number_digits, first_name=requestor_first_name, last_name=requestor_last_name, email='', password=User.objects.make_random_password(), user_type=2) user.save() requestor = Requestor(user=user, mobile_number=requestor_number_digits, lon=coord_location['lng'], lat=coord_location['lat'], address_str=requestor_address, apt_no=requestor_apartment, internal_note=requestor_internal_note) requestor.save() else: user = requestor.user user.first_name = requestor_first_name user.last_name = requestor_last_name user.save() requestor.address_str = requestor_address requestor.apt_no = requestor_apartment requestor.lon = coord_location['lng'] requestor.lat = coord_location['lat'] requestor.internal_note = requestor_internal_note requestor.save() errand = Errand(requested_time=timezone.now(), status=0, due_by=errand_due_by, requestor=requestor, additional_info=errand_instructions, affiliated_partner=request.user.partner) errand.save() return redirect('/partner/dashboard/') else: #edit existing request errand_id = request.POST['edit-errand-id'] requestor_id = request.POST['edit-requestor-id'] requestor_number = request.POST['edit-requestor-phone'] requestor_number_digits = ''.join(i for i in requestor_number if (i.isdigit() or i == '+')) if len(requestor_number_digits) == 10: requestor_number_digits = '+1' + requestor_number_digits requestor_first_name = request.POST['edit-requestor-first-name'] requestor_last_name = request.POST['edit-requestor-last-name'] requestor_address = request.POST['edit-requestor-address'] requestor_apartment = request.POST['edit-requestor-apartment'] requestor_internal_note = request.POST['edit-internal-notes'] errand_due_by = request.POST['edit-errand-due-by'] + " 23:59:59" errand_instructions = request.POST['edit-volunteer-instructions'] user = User.objects.get(id=requestor_id) user.username = requestor_number_digits user.first_name = requestor_first_name user.last_name = requestor_last_name user.save() requestor = user.requestor requestor.mobile_number = requestor_number_digits requestor.address_str = requestor_address requestor.apt_no = requestor_apartment requestor.internal_note = requestor_internal_note requestor.save() errand = Errand.objects.get(id=errand_id) errand.due_by = errand_due_by errand.additional_info = errand_instructions errand.save() return redirect('/partner/dashboard/') else: return render(request, 'errand_matcher/404.html', {'base_url': helper.get_base_url()})
def error(request): return render(request, 'errand_matcher/404.html', {'base_url': helper.get_base_url()})
def volunteer_signup_done(request): return render(request, 'errand_matcher/volunteer-signup-done.html', {'base_url': helper.get_base_url()})
def accept_errand(request, errand_id, volunteer_number): if request.method == 'POST': # To DO: what if errand isn't open? errand = Errand.objects.get(id=errand_id) volunteer = helper.get_volunteer_from_mobile_number_str( volunteer_number) # update errand errand.status = 2 errand.claimed_time = timezone.now() errand.claimed_volunteer = volunteer errand.access_id = uuid.uuid4() errand.save() # send text to volunteer with unique link url = "{}/errand/{}/status/{}".format(helper.get_base_url(), errand.id, errand.access_id) message = "Thanks for accepting this request! See details at {}".format( url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message) return HttpResponse(status=204) else: # TO DO: verify that volunteer is associated with errand errand = Errand.objects.get(id=errand_id) volunteer = helper.get_volunteer_from_mobile_number_str( volunteer_number) # TO DO: failure case if no modes modes = [] if volunteer.walks: modes.append('walking') if volunteer.has_bike: modes.append('bicycling') if volunteer.has_car: modes.append('driving') distances = helper.gmaps_distance( (volunteer.lat, volunteer.lon), (errand.requestor.lat, errand.requestor.lon), modes) if len(distances) == 1: distance_str = distances[0][1] + ' ' + distances[0][0] else: last_item = distances.pop() distance_str = '' for distance_mode, distance_duration in distances: distance_str = distance_str + distance_duration + ' ' + distance_mode + ', ' distance_str = distance_str + 'or ' + last_item[ 1] + ' ' + last_item[0] urgency_str = [ k for k, v in errand_urgency_lookup.items() if v == errand.urgency ][0] address = helper.gmaps_reverse_geocode( (errand.requestor.lat, errand.requestor.lon)) requestor_number = helper.format_mobile_number( errand.requestor.mobile_number) contact_preference = 'Texting' if errand.requestor.contact_preference == 1 else 'Phone call' # on-staff number site_configuration = SiteConfiguration.objects.first() staff_number = phonenumbers.format_number( site_configuration.mobile_number_on_call, phonenumbers.PhoneNumberFormat.NATIONAL) return render( request, 'errand_matcher/errand-accept.html', { 'requestor': errand.requestor, 'errand_urgency': urgency_str, 'distance': distance_str, 'errand_status': errand.status, 'additional_info': errand.additional_info, 'contact_preference': contact_preference, 'address': address, 'requestor_number': requestor_number, 'staff_number': staff_number, 'base_url': helper.get_base_url() })
def volunteer_signup(request): if request.method == 'POST': first_name = request.POST['first_name'] last_name = request.POST['last_name'] email = request.POST['email'] mobile_number = request.POST['mobile_number'] frequency = request.POST['frequency'] language = request.POST.get('language', '') transportation = request.POST['transportation'] lat = request.POST['lat'] lon = request.POST['lon'] user = User(username=email, first_name=first_name, last_name=last_name, email=email, password=make_password(mobile_number), user_type=1) user.save() freq_no = frequency_choice_lookup[frequency] walks = False has_bike = False has_car = False transportation_arr = transportation.split(', ') if "My own two feet" in transportation_arr: walks = True if "Bike" in transportation_arr: has_bike = True if "Car" in transportation_arr: has_car = True speaks_spanish = False speaks_russian = False speaks_chinese = False if len(language) > 0: language_arr = language.split(', ') if "Spanish" in language_arr: speaks_spanish = True if "Russian" in language_arr: speaks_russian = True if "Chinese" in language_arr: speaks_chinese = True volunteer = Volunteer( user=user, # PhoneNumberField requires country code mobile_number='+1' + mobile_number, lon=lon, lat=lat, frequency=freq_no, walks=walks, has_bike=has_bike, has_car=has_car, speaks_spanish=speaks_spanish, speaks_russian=speaks_russian, speaks_chinese=speaks_chinese, consented=True) volunteer.save() tiny_faq_url = helper.make_tiny_url("{}#above-faq".format( helper.get_base_url())) message = "Thanks for signing up to help make deliveries for at-risk members of your community!"\ " We'll text you when someone nearby needs your help. In the meantime, you can get ready by reading our FAQs:{}"\ " . And if you ever need help, you can always text us here.\n"\ "Reply STOP to stop receiving notifications of new requests.".format(tiny_faq_url) helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message) return HttpResponse(status=204) else: return render(request, 'errand_matcher/volunteer-signup.html', {'GMAPS_API_KEY': os.environ.get('GMAPS_API_KEY')})
def volunteer_signup(request): if request.method == 'POST': first_name = request.POST['add-volunteer-first-name'] last_name = request.POST['add-volunteer-last-name'] email = request.POST['add-volunteer-email'] mobile_number = request.POST['add-volunteer-phone'] frequency = request.POST['frequencyRadio'] language = request.POST.getlist('language') transportation = request.POST.getlist('transport') lat = request.POST['address-latitude'] lon = request.POST['address-longitude'] # Check to see if any users already exist with this email as a username matches = User.objects.filter(username=email).count() if matches > 0: return render( request, 'errand_matcher/volunteer-signup-v2.html', { 'GMAPS_API_KEY': os.environ.get('GMAPS_API_KEY'), 'base_url': helper.get_base_url(), 'exists': email }) # Did not find a user, this is fine user = User(username=email, first_name=first_name, last_name=last_name, email=email, password=BaseUserManager().make_random_password(), user_type=1) user.save() freq_no = frequency_choice_lookup[frequency] walks = False has_bike = False has_car = False if "My own two feet" in transportation: walks = True if "Bike" in transportation: has_bike = True if "Car" in transportation: has_car = True speaks_spanish = False speaks_russian = False speaks_chinese = False if "Spanish" in language: speaks_spanish = True if "Russian" in language: speaks_russian = True if "Chinese" in language: speaks_chinese = True volunteer = Volunteer( user=user, # PhoneNumberField requires country code mobile_number='+1' + mobile_number, lon=lon, lat=lat, frequency=freq_no, walks=walks, has_bike=has_bike, has_car=has_car, speaks_spanish=speaks_spanish, speaks_russian=speaks_russian, speaks_chinese=speaks_chinese, consented=True) volunteer.save() messages.welcome_new_volunteer(volunteer) return render(request, 'errand_matcher/volunteer-signup-done.html', { 'base_url': helper.get_base_url(), 'name': first_name }) else: return render( request, 'errand_matcher/volunteer-signup-v2.html', { 'GMAPS_API_KEY': os.environ.get('GMAPS_API_KEY'), 'base_url': helper.get_base_url(), 'exists': None })
def accept_errand(request, errand_id, volunteer_number): if request.method == 'POST': # To DO: what if errand isn't open? errand = Errand.objects.get(id=errand_id) volunteer = helper.get_user_from_mobile_number_str( str(volunteer_number)) # update errand errand.status = 2 errand.claimed_time = timezone.now() errand.claimed_volunteer = volunteer errand.access_id = uuid.uuid4() errand.save() messages.confirm_successful_claim(errand, volunteer) return HttpResponse(status=204) else: errand = Errand.objects.filter(id=errand_id).first() # errand no longer exists if errand is None: return render(request, 'errand_matcher/errand-DNE.html', {'base_url': helper.get_base_url()}) # TO DO: verify that volunteer is associated with errand volunteer = helper.get_user_from_mobile_number_str( str(volunteer_number)) # TO DO: failure case if no modes modes = [] if volunteer.walks: modes.append('walking') if volunteer.has_bike: modes.append('bicycling') if volunteer.has_car: modes.append('driving') distances = helper.gmaps_distance( (volunteer.lat, volunteer.lon), (errand.requestor.lat, errand.requestor.lon), modes) if len(distances) == 1: distance_str = distances[0][1] + ' ' + distances[0][0] else: last_item = distances.pop() distance_str = '' for distance_mode, distance_duration in distances: distance_str = distance_str + distance_duration + ' ' + distance_mode + ', ' distance_str = distance_str + 'or ' + last_item[ 1] + ' ' + last_item[0] deadline_str = helper.convert_errand_deadline_to_str(errand) requestor_number = helper.format_mobile_number( errand.requestor.mobile_number) # on-staff number site_configuration = SiteConfiguration.objects.first() staff_number = phonenumbers.format_number( site_configuration.mobile_number_on_call, phonenumbers.PhoneNumberFormat.NATIONAL) return render( request, 'errand_matcher/errand-accept.html', { 'requestor': errand.requestor, 'errand_deadline': deadline_str, 'distance': distance_str, 'errand_status': errand.status, 'additional_info': errand.additional_info, 'requestor_number': requestor_number, 'staff_number': staff_number, 'base_url': helper.get_base_url() })