Skip to content

Commit

Permalink
Merge pull request rapidpro#148 from praekelt/feature/upstream-merge
Browse files Browse the repository at this point in the history
Update staging with upstream changes
  • Loading branch information
KaitCrawford authored Nov 3, 2016
2 parents c325ef2 + 68c30b6 commit 83e3d81
Show file tree
Hide file tree
Showing 22 changed files with 141 additions and 68 deletions.
16 changes: 8 additions & 8 deletions casepro/cases/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ def test_fetch(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json, {
'id': self.case.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'assignee': {'id': self.moh.pk, 'name': "MOH"},
'labels': [{'id': self.aids.pk, 'name': "AIDS"}],
'summary': "Summary",
Expand Down Expand Up @@ -870,12 +870,12 @@ def test_timeline(self, mock_fetch_contact_messages):
self.assertEqual(len(response.json['results']), 3)
self.assertEqual(response.json['results'][0]['type'], 'I')
self.assertEqual(response.json['results'][0]['item']['text'], "What is AIDS?")
self.assertEqual(response.json['results'][0]['item']['contact'], {'id': self.ann.pk, 'name': "Ann"})
self.assertEqual(response.json['results'][0]['item']['contact'], {'id': self.ann.pk, 'display': "Ann"})
self.assertEqual(
response.json['results'][0]['item']['case']['user_assignee'], {'id': self.user1.pk, 'name': "Evan"})
self.assertEqual(response.json['results'][1]['type'], 'O')
self.assertEqual(response.json['results'][1]['item']['text'], "Non casepro message...")
self.assertEqual(response.json['results'][1]['item']['contact'], {'id': self.ann.pk, 'name': "Ann"})
self.assertEqual(response.json['results'][1]['item']['contact'], {'id': self.ann.pk, 'display': "Ann"})
self.assertEqual(response.json['results'][2]['type'], 'A')
self.assertEqual(response.json['results'][2]['item']['action'], 'O')

Expand Down Expand Up @@ -975,10 +975,10 @@ def test_timeline(self, mock_fetch_contact_messages):
self.assertEqual(len(items), 7)
self.assertEqual(items[0]['type'], 'I')
self.assertEqual(items[0]['item']['text'], "What is AIDS?")
self.assertEqual(items[0]['item']['contact'], {'id': self.ann.pk, 'name': "Ann"})
self.assertEqual(items[0]['item']['contact'], {'id': self.ann.pk, 'display': "Ann"})
self.assertEqual(items[1]['type'], 'O')
self.assertEqual(items[1]['item']['text'], "Non casepro message...")
self.assertEqual(items[1]['item']['contact'], {'id': self.ann.pk, 'name': "Ann"})
self.assertEqual(items[1]['item']['contact'], {'id': self.ann.pk, 'display': "Ann"})
self.assertEqual(items[1]['item']['sender'], None)
self.assertEqual(items[2]['type'], 'A')
self.assertEqual(items[2]['item']['action'], 'O')
Expand Down Expand Up @@ -1030,7 +1030,7 @@ def test_search(self):
'id': case2.pk,
'assignee': {'id': self.who.pk, 'name': "WHO"},
'user_assignee': None,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'labels': [],
'summary': "",
'opened_on': format_iso8601(case2.opened_on),
Expand All @@ -1040,7 +1040,7 @@ def test_search(self):
'id': self.case.pk,
'assignee': {'id': self.moh.pk, 'name': "MOH"},
'user_assignee': {'id': self.user1.pk, 'name': "Evan"},
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'labels': [{'id': self.aids.pk, 'name': "AIDS"}],
'summary': "Summary",
'opened_on': format_iso8601(self.case.opened_on),
Expand All @@ -1057,7 +1057,7 @@ def test_search(self):
'id': self.case.pk,
'assignee': {'id': self.moh.pk, 'name': "MOH"},
'user_assignee': {'id': self.user1.pk, 'name': "Evan"},
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'labels': [{'id': self.aids.pk, 'name': "AIDS"}],
'summary': "Summary",
'opened_on': format_iso8601(self.case.opened_on),
Expand Down
1 change: 0 additions & 1 deletion casepro/cases/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def get_context_data(self, **kwargs):
'all_labels': [l.as_json() for l in labels]
})

context['anon_contacts'] = getattr(settings, 'SITE_ANON_CONTACTS', False)
context['max_msg_chars'] = MAX_MESSAGE_CHARS
context['can_update'] = can_update
context['alert'] = self.request.GET.get('alert', None)
Expand Down
33 changes: 23 additions & 10 deletions casepro/contacts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ class Contact(models.Model):
"""
A contact in RapidPro
"""
DISPLAY_NAME = 'name'
DISPLAY_URNS = 'urns'
DISPLAY_ANON = 'uuid'

SAVE_GROUPS_ATTR = '__data__groups'

org = models.ForeignKey(Org, verbose_name=_("Organization"), related_name="contacts")
Expand Down Expand Up @@ -330,18 +334,24 @@ def get_or_create_from_urn(cls, org, urn, name=None):
def lock(cls, org, uuid):
return get_redis_connection().lock(CONTACT_LOCK_KEY % (org.pk, uuid), timeout=60)

def get_display_name(self):
def get_display(self):
"""
Gets the display name of this contact. If name is empty or site uses anonymous contacts, this is generated from
the backend UUID. If no UUID is set for the contact, an empty string is returned.
Gets the display of this contact. If the site uses anonymous contacts this is generated from the backend UUID.
If the display setting is recognised and set then that field is returned, otherwise the name is returned.
If no name is set an empty string is returned.
"""
if not self.name or getattr(settings, 'SITE_ANON_CONTACTS', False):
if self.uuid:
return self.uuid[:6].upper()
return ""
else:
display_format = getattr(settings, 'SITE_CONTACT_DISPLAY', self.DISPLAY_NAME)

if display_format == self.DISPLAY_ANON and self.uuid:
return self.uuid[:6].upper()
elif display_format == self.DISPLAY_URNS and self.urns:
_scheme, path = URN.to_parts(self.urns[0])
return path
elif display_format == self.DISPLAY_NAME and self.name:
return self.name

return "---"

def get_fields(self, visible=None):
fields = self.fields if self.fields else {}

Expand Down Expand Up @@ -421,9 +431,12 @@ def as_json(self, full=True):
"""
Prepares a contact for JSON serialization
"""
result = {'id': self.pk, 'name': self.get_display_name()}
result = {'id': self.pk, 'display': self.get_display()}

if full:
hidden_fields = getattr(settings, 'SITE_HIDE_CONTACT_FIELDS', [])
result['urns'] = self.urns if "urns" not in hidden_fields else []
result['name'] = self.name if "name" not in hidden_fields else None
result['groups'] = [g.as_json(full=False) for g in self.groups.all()]
result['fields'] = self.get_fields(visible=True)
result['language'] = self.get_language()
Expand All @@ -433,4 +446,4 @@ def as_json(self, full=True):
return result

def __str__(self):
return self.get_display_name()
return self.get_display()
51 changes: 37 additions & 14 deletions casepro/contacts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,21 @@ def test_save(self):

self.assertEqual(set(contact.groups.all()), {spammers, boffins})

def test_get_display_name(self):
self.assertEqual(self.ann.get_display_name(), "Ann")

# if site uses anon contacts then obscure this
with override_settings(SITE_ANON_CONTACTS=True):
self.assertEqual(self.ann.get_display_name(), "7B7DD8")

# likewise if name if empty
self.ann.name = ""
self.assertEqual(self.ann.get_display_name(), "7B7DD8")
def test_get_display(self):
# if the site uses 'uuid' for the display
with override_settings(SITE_CONTACT_DISPLAY="uuid"):
self.assertEqual(self.ann.get_display(), "7B7DD8")

# if the site uses 'urns' for the display
self.ann.urns = ['tel:+2345']
with override_settings(SITE_CONTACT_DISPLAY="urns"):
self.assertEqual(self.ann.get_display(), "+2345")
self.ann.refresh_from_db()

# if the site uses 'name' or something unrecognised for the display
self.assertEqual(self.ann.get_display(), "Ann")
self.ann.name = None
self.assertEqual(self.ann.get_display(), "---")

def test_get_fields(self):
self.assertEqual(self.ann.get_fields(), {'age': "32", 'state': "WA"}) # what is stored on the contact
Expand All @@ -144,12 +149,14 @@ def test_release(self):
self.assertEqual(self.ann.incoming_messages.filter(is_active=False, is_handled=True).count(), 2)

def test_as_json(self):
self.assertEqual(self.ann.as_json(full=False), {'id': self.ann.pk, 'name': "Ann"})
self.assertEqual(self.ann.as_json(full=False), {'id': self.ann.pk, 'display': "Ann"})

# full=True means include visible contact fields and laanguage etc
self.assertEqual(self.ann.as_json(full=True), {
'id': self.ann.pk,
'display': "Ann",
'name': "Ann",
'urns': [],
'language': {'code': 'eng', 'name': "English"},
'groups': [{'id': self.reporters.pk, 'name': "Reporters"}],
'fields': {'nickname': None, 'age': "32"},
Expand All @@ -158,21 +165,35 @@ def test_as_json(self):
})

self.ann.language = None
self.ann.urns = ["tel:+2345678", "mailto:[email protected]"]
self.ann.save()

self.assertEqual(self.ann.as_json(full=True), {
'id': self.ann.pk,
'display': "Ann",
'name': "Ann",
'urns': ["tel:+2345678", "mailto:[email protected]"],
'language': None,
'groups': [{'id': self.reporters.pk, 'name': "Reporters"}],
'fields': {'nickname': None, 'age': "32"},
'blocked': False,
'stopped': False
})

# if site uses anon contacts then name is obscured
with override_settings(SITE_ANON_CONTACTS=True):
self.assertEqual(self.ann.as_json(full=False), {'id': self.ann.pk, 'name': "7B7DD8"})
# If the urns and name fields are hidden they should not be returned
# SITE_CONTACT_DISPLAY overrules this for the 'display' attr
with override_settings(SITE_HIDE_CONTACT_FIELDS=["urns", "name"], SITE_CONTACT_DISPLAY="uuid"):
self.assertEqual(self.ann.as_json(full=True), {
'id': self.ann.pk,
'display': "7B7DD8",
'urns': [],
'name': None,
'language': None,
'groups': [{'id': self.reporters.pk, 'name': "Reporters"}],
'fields': {'nickname': None, 'age': "32"},
'blocked': False,
'stopped': False
})

@patch('casepro.test.TestBackend.push_contact')
def test_get_or_create_from_urn(self, mock_push_contact):
Expand Down Expand Up @@ -260,7 +281,9 @@ def test_fetch(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json, {
'id': self.ann.pk,
'display': "Ann",
'name': "Ann",
'urns': [],
'language': {'code': 'eng', 'name': "English"},
'fields': {'age': '32', 'nickname': None},
'groups': [{'id': self.reporters.pk, 'name': "Reporters"}],
Expand Down
18 changes: 9 additions & 9 deletions casepro/msgs/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ def test_as_json(self):

self.assertEqual(msg.as_json(), {
'id': msg.backend_id,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'text': "Hello",
'time': msg.created_on,
'labels': [{'id': self.aids.pk, 'name': "AIDS"}],
Expand Down Expand Up @@ -1135,7 +1135,7 @@ def test_search(self):
self.assertEqual(len(response.json['results']), 3)
self.assertEqual(response.json['results'][0]['id'], 103)
self.assertEqual(response.json['results'][1]['id'], 102)
self.assertEqual(response.json['results'][1]['contact'], {'id': self.bob.pk, 'name': "Bob"})
self.assertEqual(response.json['results'][1]['contact'], {'id': self.bob.pk, 'display': "Bob"})
self.assertEqual(response.json['results'][1]['text'], "I ♡ RapidPro")
self.assertEqual(response.json['results'][1]['labels'], [{'id': self.pregnancy.pk, 'name': "Pregnancy"}])
self.assertEqual(response.json['results'][2]['id'], 101)
Expand All @@ -1148,7 +1148,7 @@ def test_search(self):

self.assertEqual(len(response.json['results']), 2)
self.assertEqual(response.json['results'][0]['id'], 105)
self.assertEqual(response.json['results'][0]['contact'], {'id': cat.pk, 'name': "Cat"})
self.assertEqual(response.json['results'][0]['contact'], {'id': cat.pk, 'display': "Cat"})
self.assertEqual(response.json['results'][0]['text'], "AIDS??")
self.assertEqual(response.json['results'][0]['labels'], [{'id': self.aids.pk, 'name': "AIDS"}])
self.assertEqual(response.json['results'][0]['case'], {
Expand Down Expand Up @@ -1503,7 +1503,7 @@ def test_as_json(self):

self.assertEqual(outgoing.as_json(), {
'id': outgoing.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'urn': None,
'text': "That's great",
'time': outgoing.created_on,
Expand Down Expand Up @@ -1538,7 +1538,7 @@ def test_search(self):
self.assertEqual(response.json['results'], [
{
'id': out2.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'urn': None,
'text': "Hello 2",
'case': None,
Expand All @@ -1547,7 +1547,7 @@ def test_search(self):
},
{
'id': out1.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'urn': None,
'text': "Hello 1",
'case': None,
Expand All @@ -1563,7 +1563,7 @@ def test_search(self):
self.assertEqual(response.json['results'], [
{
'id': out2.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'urn': None,
'text': "Hello 2",
'case': None,
Expand Down Expand Up @@ -1598,7 +1598,7 @@ def test_search_replies(self):
self.assertEqual(response.json['results'], [
{
'id': out2.pk,
'contact': {'id': self.bob.pk, 'name': "Bob"},
'contact': {'id': self.bob.pk, 'display': "Bob"},
'urn': None,
'text': "Hello 2",
'case': None,
Expand All @@ -1613,7 +1613,7 @@ def test_search_replies(self):
},
{
'id': out1.pk,
'contact': {'id': self.ann.pk, 'name': "Ann"},
'contact': {'id': self.ann.pk, 'display': "Ann"},
'urn': None,
'text': "Hello 1",
'case': {'id': case.pk, 'assignee': {'id': self.moh.pk, 'name': "MOH"}, 'user_assignee': None},
Expand Down
6 changes: 4 additions & 2 deletions casepro/orgs_ext/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import absolute_import, unicode_literals

import pytz

from dash.orgs.models import TaskState
from django.core.urlresolvers import reverse

Expand Down Expand Up @@ -58,7 +60,7 @@ def test_edit(self):
form = response.context['form']

self.assertEqual(form.initial['name'], "UNICEF")
self.assertEqual(form.initial['timezone'], "Africa/Kampala")
self.assertEqual(form.initial['timezone'], pytz.timezone("Africa/Kampala"))

self.assertEqual(form.fields['banner_text'].initial, "Howdy (U)Partner!")
self.assertEqual(form.fields['contact_fields'].choices, [(self.age.pk, "Age (age)"),
Expand All @@ -82,7 +84,7 @@ def test_edit(self):
self.unicef._config = None

self.assertEqual(self.unicef.name, "UNIZEFF")
self.assertEqual(self.unicef.timezone, "Africa/Kigali")
self.assertEqual(self.unicef.timezone, pytz.timezone("Africa/Kigali"))
self.assertEqual(self.unicef.get_banner_text(), "Chill")

self.assertEqual(set(Group.get_suspend_from(self.unicef)), {self.males})
Expand Down
3 changes: 2 additions & 1 deletion casepro/settings_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@
SITE_ORGS_STORAGE_ROOT = 'orgs'
SITE_EXTERNAL_CONTACT_URL = 'http://localhost:8001/contact/read/%s/'
SITE_BACKEND = 'casepro.backend.NoopBackend'
SITE_ANON_CONTACTS = False
SITE_CASE_RESPONSE_REQUIRED_TIME = None # specified in minutes, None to disable
SITE_HIDE_CONTACT_FIELDS = [] # Listed fields should not be displayed
SITE_CONTACT_DISPLAY = "name" # Overrules SITE_HIDE_CONTACT_FIELDS Options: 'name', 'uuid' or 'urns'
SITE_ALLOW_CASE_WITHOUT_MESSAGE = True

# junebug configuration
Expand Down
3 changes: 1 addition & 2 deletions casepro/statistics/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import unicode_literals

import pytz
import six

from dash.orgs.models import Org
Expand All @@ -21,7 +20,7 @@ def datetime_to_date(dt, org):
"""
Convert a datetime to a date using the given org's timezone
"""
return dt.astimezone(pytz.timezone(org.timezone)).date()
return dt.astimezone(org.timezone).date()


class BaseCount(models.Model):
Expand Down
5 changes: 3 additions & 2 deletions casepro/test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import unicode_literals

import json
import pytz

from dash.test import DashTest
from datetime import datetime, date, time
Expand Down Expand Up @@ -37,8 +38,8 @@ def setUp(self):
settings.SITE_ORGS_STORAGE_ROOT = 'test_orgs'

# some orgs
self.unicef = self.create_org("UNICEF", timezone="Africa/Kampala", subdomain="unicef")
self.nyaruka = self.create_org("Nyaruka", timezone="Africa/Kigali", subdomain="nyaruka")
self.unicef = self.create_org("UNICEF", timezone=pytz.timezone("Africa/Kampala"), subdomain="unicef")
self.nyaruka = self.create_org("Nyaruka", timezone=pytz.timezone("Africa/Kigali"), subdomain="nyaruka")

# some admins for those orgs
self.admin = self.create_admin(self.unicef, "Kidus", "[email protected]")
Expand Down
2 changes: 1 addition & 1 deletion karma/test-controllers.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('controllers:', () ->
ureporters: {id: 703, name: "U-Reporters"},

# contacts
ann: {id: 401, name: "Ann", fields: {'age': 35}, groups: [{id: 701, name: "Females"}, {id: 703, name: "U-Reporters"}]},
ann: {id: 401, name: "Ann", fields: {'age': 35}, groups: [{id: 701, name: "Females"}, {id: 703, name: "U-Reporters"}], urns: []},
bob: {id: 402, name: "Bob", fields: {}, groups: []}
}
)
Expand Down
Loading

0 comments on commit 83e3d81

Please sign in to comment.