diff --git a/api/tests.py b/api/tests.py index c845e683..919e6748 100644 --- a/api/tests.py +++ b/api/tests.py @@ -3,8 +3,9 @@ from django.test import RequestFactory, TestCase -from .views import reminders, eligible_jurisdiction +from .views import reminders, eligible_jurisdiction, unsubscribe from alerts.models import Alert +from decouple import config class testReminders(TestCase): @@ -17,6 +18,9 @@ def _post(self, url, params): def _get(self, url, params): return self.factory.get(url, params) + # def _delete(self, url): + # return self.factory.delete(url) + def testReminderWithArraignmentIn8Days(self): arraignment_datetime = (datetime.today() + timedelta(days=8)).strftime('%Y-%m-%dT%H:%M:%S') request = self._post('/api/reminders', { @@ -94,3 +98,31 @@ def testEligibleJurisdictions(self): 'seminole', 'sequoyah', 'stephens', 'texas', 'tillman', 'tulsa', 'wagoner', 'washington', 'washita', 'woods', 'woodward']}) + + def testUnsubscribe(self): + alert = Alert( + when='2020-07-27', + to="+1-000-001-0002", + what="test reminder" + ) + alert.save() + self.assertEqual(Alert.objects.filter(to='+1-000-001-0002').count(), 1) + + request = self._post('api/unsubscribe/000-001-0002', { + 'key': config('SECRET_KEY') + }) + response = unsubscribe(request, '000-001-0002') + resp_json = json.loads(response.content) + message = resp_json.get('message', None) + self.assertEqual(message, 'Reminders for 000-001-0002 deleted.') + self.assertEqual(Alert.objects.filter(to='+1-000-001-0002').count(), 0) + + def testUnsubsribeNotExists(self): + request = self._post('api/unsubscribe/000-001-0003', { + 'key': config('SECRET_KEY') + }) + response = unsubscribe(request, '000-001-0003') + resp_json = json.loads(response.content) + message = resp_json.get('message', None) + self.assertEqual(message, 'There are no reminders set for 000-001-0003.') + self.assertEqual(Alert.objects.filter(to='+1-000-001-0003').count(), 0) diff --git a/api/urls.py b/api/urls.py index ced310fd..31dc5d52 100644 --- a/api/urls.py +++ b/api/urls.py @@ -8,4 +8,5 @@ path('case', views.case, name='case'), path('reminders', views.reminders, name='reminders'), path('eligible-jurisdiction', views.eligible_jurisdiction, name='eligible_jurisdiction'), + path('unsubscribe/', views.unsubscribe, name='unsubscribe'), ] diff --git a/api/views.py b/api/views.py index 9052c0e9..ef14d5e9 100644 --- a/api/views.py +++ b/api/views.py @@ -4,6 +4,7 @@ from django.http import JsonResponse, HttpResponse from django.shortcuts import render from django.views.decorators.csrf import csrf_exempt +from decouple import config import oscn @@ -104,6 +105,21 @@ def eligible_jurisdiction(request): return HttpResponse(status=405) +@csrf_exempt +def unsubscribe(request, phone): + key = request.POST.get('key', None) + message ='' + if key == config('SECRET_KEY'): + formatted_phone = "+1-" + phone + alerts = Alert.objects.filter(to=formatted_phone) + if not alerts: + message = f"There are no reminders set for {phone}." + else: + alerts.delete() + message = f"Reminders for {phone} deleted." + else: + message = "Unauthorized." + return JsonResponse({'message': message}) def find_arraignment_or_return_False(events): for event in events: diff --git a/website/templates/base_generic.html b/website/templates/base_generic.html index f5a6d854..413a6dae 100644 --- a/website/templates/base_generic.html +++ b/website/templates/base_generic.html @@ -14,6 +14,7 @@ {% load static %} + diff --git a/website/templates/index.html b/website/templates/index.html index 9d4a5261..39aa9895 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -66,8 +66,10 @@

FEWER FINES

{% if messages %} - {% for message in messages %} - {{ message }}

+ {% for message in messages %} + {% if not message.extra_tags %} + {{ message }}

+ {% endif %} {% endfor %} {% endif %}

Fill Out Reminder Form:

@@ -130,13 +132,54 @@

5. When will I be reminded of my court case?

6. How often will I be reminded?

Currently, you will only be reminded once.

-
-

7. How do I unsubscribe?

-

+
+

7. How do I contact the people behind CourtBot?

+

You can contact us at courtbotmuskogee@gmail.com.

-

8. How do I contact the people behind CourtBot?

-

You can contact us at courtbotmuskogee@gmail.com.

+

8. How do I unsubscribe?

+

Click unsubscribe to remove reminders.

+ + + + + + {% if messages %} + {% for message in messages %} + {% if message.extra_tags and message.extra_tags == 'unsubscribe' %} +

{{ message }}

+ {% endif %} + {% endfor %} + {% endif %} + + + {% if messages %} + {% for message in messages %} + {% if message.extra_tags and message.extra_tags == 'verify' %} +

{{ message }}

+
+
+ +
+
+ + + {% endif %} + {% endfor %} + {% endif %} +
diff --git a/website/urls.py b/website/urls.py index 83cb4ac7..5c91fd64 100644 --- a/website/urls.py +++ b/website/urls.py @@ -3,5 +3,7 @@ urlpatterns = [ path('', views.index, name='index'), - path('schedule_reminders', views.schedule_reminders, name='schedule_reminders') + path('schedule_reminders', views.schedule_reminders, name='schedule_reminders'), + path('unsubscribe_reminders', views.unsubscribe_reminders, name='unsubscribe_reminders'), + path('send_verification_code', views.send_verification_code, name='send_verification_code') ] \ No newline at end of file diff --git a/website/views.py b/website/views.py index 2e08e048..30929977 100644 --- a/website/views.py +++ b/website/views.py @@ -1,6 +1,7 @@ from django.shortcuts import render, redirect from datetime import datetime, timedelta import re +from random import randint from django.http import JsonResponse, HttpResponse from django.shortcuts import render @@ -9,15 +10,21 @@ import oscn, requests, json - from alerts.models import Alert +from twilio.rest import Client +from decouple import config + +TWILIO_ACCOUNT_SID = config('TWILIO_ACCOUNT_SID') +TWILIO_AUTH_TOKEN = config('TWILIO_AUTH_TOKEN') +TWILIO_FROM_NUMBER = config('TWILIO_FROM_NUMBER') def index(request): # """View function for home page of site.""" # Render the HTML template index.html with the data in the context variable return render(request, 'index.html') + def check_valid_case(request): # Process form data and requests arraignment data form api/case @@ -42,6 +49,7 @@ def set_case_reminder(arraignment_datetime, case_num, phone_num): "phone_num": f"+1-{phone_num}" }) resp = json.loads(reminder_request.content) + if resp.get('error', None): return False, resp['error'] message = f'Text reminder for case {case_num} occuring on {arraignment_datetime} was scheduled under {phone_num}.' @@ -72,5 +80,41 @@ def schedule_reminders(request): if add_num: _, another_reminder_message = set_case_reminder(arraignment_datetime, case_num, add_num) messages.info(request, another_reminder_message) + return redirect('/#form') +@csrf_exempt +def unsubscribe_reminders(request): + code = request.POST['verification_code'] + if int(code) == request.session.get('verification_code', None): + phone = request.session.get('phone_number', None) + # request to api for reminder deletion, includes secret key to prevent unauthorized request + remove_request = requests.post(f"http://courtbot-python.herokuapp.com/api/unsubscribe/{phone}", { + "key": config('SECRET_KEY') + }) + resp = json.loads(remove_request.content) + messages.info(request, resp.get('message', None), extra_tags='unsubscribe') + del request.session['verification_code'] + del request.session['phone_number'] + return redirect('/#contact') + else: + messages.error(request, 'Invalid code', extra_tags='verify') + return redirect('/#contact') + +@csrf_exempt +def send_verification_code(request): + # Sends random 4 digit code to verify owner of phone number requesting cancellation + phone = request.POST['remove_phone_num'] + formatted_phone = '+1-' + phone + code = randint(1000,9999) + # save code and phone number in session for other view function + request.session['verification_code'] = code + request.session['phone_number'] = phone + client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) + message = client.messages.create( + to=formatted_phone, + from_=TWILIO_FROM_NUMBER, + body=f"Courtbot unsubscribe verification code: {code}" + ) + messages.info(request, 'Enter verification code', extra_tags='verify') + return redirect("/#contact")