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 volunteer_login(request): if request.method == 'POST': mobile_number_str = request.POST.get('phone-input') # Is mobile number associated with Volunteer? volunteer = helper.get_volunteer_from_mobile_number_str( mobile_number_str) # If Volunteer found, create OTP if volunteer is not None: user_otp = UserOTP.objects.create( mobile_number=volunteer.mobile_number) # deliver OTP message = "Livelyhood here! {} is your one-time password for online login. Please do not share.".format( user_otp.token) helper.send_sms( helper.format_mobile_number(user_otp.mobile_number), message) return render(request, 'errand_matcher/volunteer-login-otp.html') # If no Volunteer found, show warning and redirect back to signup else: return render(request, 'errand_matcher/volunteer-login.html', {'warning': 'Not found'}) else: return render(request, 'errand_matcher/volunteer-login.html')
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 thank_and_survey(volunteer): message = "Thank you for helping a neighbor today! You made someone’s day with your gesture."\ " Please take a minute to complete this survey so we continue to help more people."\ " Click here for survey: https://forms.gle/ZKd5z3obSFvyejA49."\ "\n\nWe hope you will volunteer with LivelyHood again!" helper.send_sms(helper.format_mobile_number(volunteer.mobile_number), message)
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 alert_staff_of_unclaimed_errand(errand): message = 'Errand unclaimed! {} {}: {} requested at {}'.format( errand.requestor.user.first_name, errand.requestor.user.last_name, helper.strip_mobile_number(errand.requestor.mobile_number), errand.requested_time) staff_number = helper.format_mobile_number( helper.get_support_mobile_number()) helper.send_sms(staff_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 sms_inbound(request): from_number = request.POST['From'] body = request.POST['body'] # forward text to on-call staffer message = '{}: {}'.format(from_number, body) helper.send_sms( helper.format_mobile_number(helper.get_support_mobile_number()), message) return
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 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 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() })
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 send_otp(user_otp): message = "LivelyHood here! {} is your one-time password for online login. Please do not share.".format( user_otp.token) helper.send_sms(helper.format_mobile_number(user_otp.mobile_number), message)