Skip to content

Commit 606467b

Browse files
committed
"Mark as spam" feature for contact.PhoneNumber
1 parent 6411297 commit 606467b

File tree

14 files changed

+94
-30
lines changed

14 files changed

+94
-30
lines changed

deployment/setup_db.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from what_apps.slashroot import config as slashroot_config
1212
from what_apps.people import config as people_config
1313
from what_apps.contact import config as contact_config
14+
from what_apps.comm import config as comm_config
1415

1516
utility = ManagementUtility(['', 'syncdb', '--noinput'])
1617
utility.execute()
@@ -29,8 +30,9 @@
2930
admin.save()
3031

3132
rusty, rusty_profile = people_config.setup()
32-
rusty_contact = contact_config.setup()
33+
rusty_contact, rusty_home_number, rusty_work_number = contact_config.setup()
3334

3435
rusty_profile.contact_info = rusty_contact
3536
rusty_profile.save()
3637

38+
comm_config.setup(rusty_home_number)

templates/comm/call_alert.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="displayCall" id="displayCall_{{call.id}}" call="{{call.id}}" status="{{call.status}}">
22
<div class="basicCallInfo">
33
<h5><a href="{{call.resolve_task.get_absolute_url}}">PhoneCall #{{call.id}}</a> {{call.created}}</h5>
4-
From {%if call.from_user %}{{call.from_user.get_full_name}}{% else %}Unknown Caller{% endif %}</strong>
4+
From <a href="{{call.from_number.get_absolute_url}}">{{call.from_number}}</a></strong>
55
{% if call.from_user.userprofile %}
66
<strong>(<a href="{{call.from_user.get_absolute_url}}">{{call.from_user.userprofile.user.get_full_name}}</a>)</strong>
77
{% else %}

templates/contact/phone_number_profile.html

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,25 @@
33
{% block title %}{{phone_number}}{% endblock %}
44

55
{% block more_content %}
6+
7+
68

79
{% if not phone_number.user %}
810
<h2><a href='/contact/new_contact/?phone_number={{phone_number.number}}'>Add to Contacts</a></h2>
911
{% endif %}
1012

11-
<h2><a href="/comm/outgoing_call_menu/?phone_number={{phone_number.id}}">Place Call</a></h2>
13+
<h2><a href="/comm/outgoing_call_menu/?phone_number={{phone_number.id}}">Place Call to this number</a></h2>
14+
15+
<form action="" method="POST">
16+
{% csrf_token %}
17+
{% if phone_number.spam %}
18+
<input type="hidden" value="0" name="spam">
19+
<button>This number is marked as spam. Click here to unmark.</button>
20+
{% else %}
21+
<input type="hidden" value="1" name="spam">
22+
<button>Mark as spam.</button>
23+
{% endif %}
24+
</form>
1225

1326

1427

urls.py

-5
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010

1111
admin.autodiscover()
1212

13-
14-
15-
16-
17-
1813
urlpatterns = patterns('',
1914
#MAIN
2015
(r'^$', 'what_apps.main.views.main_landing'),

what_apps/comm/config.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from what_apps.comm.factories import PhonecallFactory
2+
3+
4+
def setup(from_number):
5+
PhonecallFactory.create(from_number=from_number)
6+

what_apps/comm/factories.py

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
import factory
22
import datetime
33
from models import PhoneCall
4-
from contact.factories import PhoneNumberFactory, PhoneServiceFactory
4+
from what_apps.contact.factories import PhoneNumberFactory, PhoneServiceFactory
5+
from what_apps.people.models import UserProfile
6+
from twilio.rest.resources.phone_numbers import PhoneNumber
7+
from what_apps.contact.models import ContactInfo
58

69

710
class PhonecallFactory(factory.Factory):
811
FACTORY_FOR = PhoneCall
9-
service = factory.Subfactory(PhoneServiceFactory)
12+
service = factory.SubFactory(PhoneServiceFactory)
1013
call_id = factory.Sequence(lambda n: n)
1114
account_id = factory.Sequence(lambda n: n)
1215
dial = True
13-
from_number = factory.Subfactory(PhoneNumberFactory)
14-
to_number = factory.Subfactory(PhoneNumberFactory)
16+
from_number = factory.SubFactory(PhoneNumberFactory)
17+
to_number = factory.SubFactory(PhoneNumberFactory)
1518
ended = datetime.datetime.now()
1619

17-
@class_method
20+
@classmethod
1821
def _prepare(cls,
1922
create,
2023
from_number=None,
2124
to_number=None,
2225
from_user=None,
2326
to_user=None,
24-
is_twilio=False
27+
is_twilio=False,
28+
dial=False,
2529
**kwargs):
2630

2731
if from_user and from_number:
@@ -33,12 +37,15 @@ def _prepare(cls,
3337
if from_user:
3438
UserProfile.objects.get_or_create(user=from_user, defaults={'contact_info':ContactInfo.objects.create()})
3539
from_number = PhoneNumber.objects.get_or_create(owner=from_user.userprofile.contact_info, defaults=dict(number="+15551231234"))[0]
36-
else:
37-
3840
if to_number:
3941
pass #We'll just use this.
4042
elif to_user:
4143
UserProfile.objects.get_or_create(user=to_user, defaults={'contact_info':ContactInfo.objects.create()})
4244
to_number = PhoneNumber.objects.create(owner=to_user.userprofile.contact_info, number="+15551231234")
4345
else:
44-
to_number = PhoneNumber.objects.get_or_create(type='mobile', number='+18455551234')[0]
46+
to_number = PhoneNumber.objects.get_or_create(type='mobile', number='+18455551234')[0]
47+
48+
return super(PhonecallFactory, cls)._prepare(create,
49+
from_number=from_number,
50+
to_number=to_number,
51+
**kwargs)

what_apps/comm/models.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515
from what_apps.utility.models import FixedObject
1616

1717

18-
19-
20-
2118
class Communication(models.Model):
2219
'''
2320
A single instance of communication - a phone call, email, etc.
2421
'''
2522
created = models.DateTimeField(auto_now_add=True)
2623
description = models.TextField(blank=True, null=True)
2724

25+
2826
class CommunicationInvolvement(models.Model):
2927
'''
3028
People are involved in communication in different ways depending on the medium, the circumstances, and sometimes the actual people.
@@ -70,6 +68,7 @@ def summary(self):
7068
if self.direction == "from":
7169
return "%s called " % (self.person.userprofile.user.first_name)
7270

71+
7372
class PhoneCallQuerySet(QuerySet):
7473
def involving(self, user_list=None, include_from=True, include_to=True, subtractive=False):
7574
'''
@@ -112,6 +111,7 @@ def has_recording(self, voicemail=True):
112111
else:
113112
return calls_with_recordings.filter(tasks__tags__name="voicemail")
114113

114+
115115
class PhoneCall(Communication):
116116
'''
117117
This model is loosely based on a twilio phone call HTTP request, which is explained here:
@@ -277,6 +277,7 @@ def set_resolve_status(self, status, user):
277277
resolve_task = self.resolve_task()
278278
return status_is_changed
279279

280+
280281
class PhoneCallRecording(models.Model):
281282
'''
282283
Tracks the audio recording of a telephone call.
@@ -319,6 +320,7 @@ def message_count(self):
319320
count = self.number
320321
return count
321322

323+
322324
class PhoneCallEvent(object):
323325
'''
324326
Not a model.
@@ -348,13 +350,11 @@ def summary(self):
348350
summary = "caller hung up"
349351
return summary
350352

351-
352-
353-
354353
#TODO: Move this stuff elsewhere. Maybe the .save() method.
355354

356355
RESOLVE_PHONE_CALL = "TaskPrototype__resolve_phone_call"
357356

357+
358358
def notifyCallSave(instance, **kwargs):
359359
'''
360360
When phone calls are saved, push to watch_calls

what_apps/comm/provider_views.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ def answer(request, this_is_only_a_test=False):
3939
#Now we need a call object with the appropriate details, regardless of the provider.
4040
call_info = standardize_call_info(request, provider=provider)
4141
call = call_object_from_call_info(call_info) #Identify the call, saving it as a new object if necessary.
42-
comm_logger.info('%s %s call from %s' % (call_info['status'], provider, call.from_number))
42+
if call.from_number.spam:
43+
r.reject()
44+
comm_logger.info('%s %s call from %s (rejected because spam)' % (call_info['status'], provider, call.from_number))
45+
else:
46+
comm_logger.info('%s %s call from %s' % (call_info['status'], provider, call.from_number))
4347

4448
if not call.ended:
4549
r.say(SLASHROOT_EXPRESSIONS['public_greeting'], voice=random_tropo_voice()) #Greet them.

what_apps/comm/response.py

+8
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ def prompt_and_record(self, recording_object=None, prompt=None, transcribe=False
150150
recording_kwargs['transcription'] = {'id':kwargs['call_id'], "url":"%s/comm/transcription_handler/%s/%s/" % ((resources.COMM_DOMAIN,) + recording_url_args)}
151151
self.response_object.record(**recording_kwargs)
152152

153+
def reject(self):
154+
if self.provider.name == "Twilio":
155+
self.response_object.reject()
156+
else:
157+
raise NotImplementedError("Twilio only for the moment.")
158+
159+
160+
153161
def hangup(self, *args, **kwargs):
154162
self.response_object.hangup(*args, **kwargs)
155163

what_apps/comm/tests.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import json
6767
import what_apps.do.config as do_config
6868
import what_apps.mellon.config as mellon_config
69+
from what_apps.contact.factories import PhoneNumberFactory
6970

7071

7172

@@ -268,13 +269,24 @@ def test_turn_tropo_request_into_phone_call_object(self):
268269
class CallerInitialExperience(TestCase):
269270
def setUp(self):
270271
prepare_testcase_for_answer_tests(self)
271-
272+
272273
def test_twilio_greeting(self):
273274
'''
274275
Tests that the greeting is equal to the say command
275276
'''
276277
self.assertEqual(self.twilio_response_object.verbs[0].name, 'Say')
277278
self.assertEqual(self.twilio_response_object.verbs[0].body, SLASHROOT_EXPRESSIONS['public_greeting'])
279+
280+
def test_spam_number_is_hung_up_upon(self):
281+
spam_caller = PhoneNumberFactory.create(number="001-001-0123", spam=True)
282+
request_dict = TYPICAL_TWILIO_REQUEST.copy()
283+
request_dict['From'] = request_dict['Caller'] = "+10010010123"
284+
request_dict['CallSid'] = "DefinitelyNotTheSameCallBLAHBLAHblahblah"
285+
request = FakeRequest(data=request_dict)
286+
287+
response = answer(request)
288+
289+
self.assertEqual(response._container[0].verbs[0].name, "Reject")
278290

279291
def test_twilio_answer_second_command_dial(self):
280292
'''

what_apps/contact/config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ def setup():
1717
owner=rusty_contact,
1818
type="work")
1919

20-
return rusty_contact
20+
return rusty_contact, home_number, work_number
2121

2222

what_apps/contact/factories.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import factory
22
import datetime
3+
from what_apps.contact.models import PhoneNumber, PhoneProvider
4+
35

46
class PhoneNumberFactory(factory.Factory):
7+
FACTORY_FOR = PhoneNumber
58
type = 'mobile'
6-
number = factory.Sequence(lambda n: '888-555-%04.f' % n)
9+
number = factory.Sequence(lambda n: '888-555-%04.f' % float(n)) # Typecast required because factory_boy passes str
10+
11+
12+
class PhoneServiceFactory(factory.Factory):
13+
FACTORY_FOR = PhoneProvider

what_apps/contact/models.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,16 @@ class PhoneNumber(models.Model):
7474

7575
def __unicode__(self):
7676
if self.owner:
77-
return "%s (%s)" % (self.owner, self.type)
77+
name = "%s (%s)" % (self.owner, self.type)
7878
elif self.number == "+10000000000":
79-
return "Blank Number"
79+
name = "Blank Number"
8080
else:
81-
return "Unknown Caller #%s" % self.id
81+
name = "Unknown Caller #%s" % self.id
82+
83+
if self.spam:
84+
name += " (SPAM!)"
85+
86+
return name
8287

8388

8489
def save(self, owner=None, *args, **kwargs):

what_apps/contact/views.py

+5
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,9 @@ def toggle_dial_list(request, dial_list_id):
219219

220220
def phone_number_profile(request, phone_number_id):
221221
phone_number = PhoneNumber.objects.get(id=phone_number_id)
222+
223+
if request.POST:
224+
phone_number.spam = int(request.POST['spam'])
225+
phone_number.save()
226+
222227
return render(request, 'contact/phone_number_profile.html', locals())

0 commit comments

Comments
 (0)