def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = ContactGroup.get_or_create(self.org, self.admin, "Customers") developers = ContactGroup.get_or_create(self.org, self.admin, "Developers") ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") developers.update_contacts(self.admin, [self.frank], add=True) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 3): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [ {'uuid': developers.uuid, 'name': "Developers", 'count': 1}, {'uuid': customers.uuid, 'name': "Customers", 'count': 0} ]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertEqual(response.json['results'], [{'uuid': customers.uuid, 'name': "Customers", 'count': 0}])
def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = self.create_group("Customers", [self.frank]) developers = self.create_group("Developers", query="isdeveloper = YES") # group belong to other org ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 3): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [ {'uuid': developers.uuid, 'name': "Developers", 'query': "isdeveloper = YES", 'count': 0}, {'uuid': customers.uuid, 'name': "Customers", 'query': None, 'count': 1} ]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertResultsByUUID(response, [customers])
def test_campaigns(self): url = reverse('api.v2.campaigns') self.assertEndpointAccess(url) reporters = self.create_group("Reporters", [self.joe, self.frank]) campaign1 = Campaign.create(self.org, self.admin, "Reminders #1", reporters) campaign2 = Campaign.create(self.org, self.admin, "Reminders #2", reporters) # create campaign for other org spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") Campaign.create(self.org2, self.admin2, "Cool stuff", spammers) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 2): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [campaign2, campaign1]) self.assertEqual(response.json['results'][0], { 'uuid': campaign2.uuid, 'name': "Reminders #2", 'group': {'uuid': reporters.uuid, 'name': "Reporters"}, 'created_on': format_datetime(campaign2.created_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % campaign1.uuid) self.assertResultsByUUID(response, [campaign1])
def test_campaign_events(self): url = reverse('api.v2.campaign_events') self.assertEndpointAccess(url) flow = self.create_flow() reporters = self.create_group("Reporters", [self.joe, self.frank]) registration = ContactField.get_or_create(self.org, self.admin, 'registration', "Registration") campaign1 = Campaign.create(self.org, self.admin, "Reminders", reporters) event1 = CampaignEvent.create_message_event(self.org, self.admin, campaign1, registration, 1, CampaignEvent.UNIT_DAYS, "Don't forget to brush your teeth") campaign2 = Campaign.create(self.org, self.admin, "Notifications", reporters) event2 = CampaignEvent.create_flow_event(self.org, self.admin, campaign2, registration, 6, CampaignEvent.UNIT_HOURS, flow, delivery_hour=12) # create event for another org joined = ContactField.get_or_create(self.org2, self.admin2, 'joined', "Joined On") spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") spam = Campaign.create(self.org2, self.admin2, "Cool stuff", spammers) CampaignEvent.create_flow_event(self.org2, self.admin2, spam, joined, 6, CampaignEvent.UNIT_HOURS, flow, delivery_hour=12) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 4): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [event2, event1]) self.assertEqual(response.json['results'][0], { 'uuid': event2.uuid, 'campaign': {'uuid': campaign2.uuid, 'name': "Notifications"}, 'relative_to': {'key': "registration", 'label': "Registration"}, 'offset': 6, 'unit': 'hours', 'delivery_hour': 12, 'flow': {'uuid': flow.uuid, 'name': "Color Flow"}, 'message': None, 'created_on': format_datetime(event2.created_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % event1.uuid) self.assertResultsByUUID(response, [event1]) # filter by campaign name response = self.fetchJSON(url, 'campaign=Reminders') self.assertResultsByUUID(response, [event1]) # filter by campaign UUID response = self.fetchJSON(url, 'campaign=%s' % campaign1.uuid) self.assertResultsByUUID(response, [event1]) # filter by invalid campaign response = self.fetchJSON(url, 'campaign=invalid') self.assertResultsByUUID(response, [])
def save(self): name = self.validated_data.get('name') if self.instance: self.instance.name = name self.instance.save(update_fields=('name',)) return self.instance else: return ContactGroup.get_or_create(self.context['org'], self.context['user'], name)
def validate_groups(self, value): if value is not None: self.group_objs = [] for name in value: if not ContactGroup.is_valid_name(name): raise serializers.ValidationError(_("Invalid group name: '%s'") % name) self.group_objs.append(ContactGroup.get_or_create(self.org, self.user, name)) return value
def save(self): name = self.validated_data.get("name") if self.instance: self.instance.name = name self.instance.save(update_fields=("name",)) return self.instance else: return ContactGroup.get_or_create(self.context["org"], self.context["user"], name)
def save(self): name = self.validated_data.get("name") if self.instance: self.instance.name = name self.instance.save(update_fields=("name", )) return self.instance else: return ContactGroup.get_or_create(self.context["org"], self.context["user"], name)
def restore_object(self, attrs, instance=None): """ Create or update our campaign """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if 'campaign' in attrs: campaign = Campaign.objects.get(pk=attrs['campaign'], is_active=True, is_archived=False, org=org) campaign.name = attrs['name'] campaign.group = ContactGroup.get_or_create(org, self.user, attrs['group']) campaign.save() else: group = ContactGroup.get_or_create(org, self.user, attrs['group']) campaign = Campaign.objects.create(name=attrs['name'], group=group, org=org, created_by=self.user, modified_by=self.user) return campaign
def restore_object(self, attrs, instance=None): """ Create or update our campaign """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if 'campaign' in attrs: campaign = Campaign.objects.get(pk=attrs['campaign'], is_active=True, is_archived=False, org=org) campaign.name = attrs['name'] campaign.group = ContactGroup.get_or_create(attrs['group'], self.user) campaign.save() else: group = ContactGroup.get_or_create(attrs['group'], self.user) campaign = Campaign.objects.create(name=attrs['name'], group=group, org=org, created_by=self.user, modified_by=self.user) return campaign
def get_whatsappable_group(org): user = org.administrators.first() whatsapp_groups = ContactGroup.user_groups.filter(org=org, name=WHATSAPPABLE_GROUP) if whatsapp_groups.exists(): return ContactGroup.get_or_create(org, user=user, name=WHATSAPPABLE_GROUP) return ContactGroup.create_dynamic(org, user=user, name=WHATSAPPABLE_GROUP, query='%s="%s"' % (HAS_WHATSAPP_KEY, YES))
def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = ContactGroup.get_or_create(self.org, self.admin, "Customers") developers = ContactGroup.get_or_create(self.org, self.admin, "Developers") spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") developers.update_contacts(self.admin, [self.frank], add=True) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 1): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [{ 'uuid': developers.uuid, 'name': "Developers", 'count': 1 }, { 'uuid': customers.uuid, 'name': "Customers", 'count': 0 }]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertEqual(response.json['results'], [{ 'uuid': customers.uuid, 'name': "Customers", 'count': 0 }])
def parse_groups(cls, org, json_obj): # we actually instantiate our contacts here groups = [] for group_data in json_obj.get(VariableContactAction.GROUPS): group_uuid = group_data.get(VariableContactAction.UUID, None) group_name = group_data.get(VariableContactAction.NAME) # flows from when true deletion was allowed need this if not group_name: group_name = "Missing" group = ContactGroup.get_or_create(org, org.get_user(), group_name, uuid=group_uuid) groups.append(group) return groups
def test_contacts(self): url = reverse('api.v2.contacts') self.assertEndpointAccess(url) # create some more contacts (in addition to Joe and Frank) contact1 = self.create_contact("Ann", "0788000001", language='fre') contact2 = self.create_contact("Bob", "0788000002") contact3 = self.create_contact("Cat", "0788000003") contact4 = self.create_contact("Don", "0788000004") contact1.set_field(self.user, 'nickname', "Annie", label="Nick name") contact1.fail() contact2.block(self.user) contact3.release(self.user) # put some contacts in a group group = ContactGroup.get_or_create(self.org, self.admin, "Customers") group.update_contacts(self.user, [self.joe], add=True) # add contacts separately for predictable modified_on group.update_contacts(self.user, [contact1], add=True) # ordering contact1.refresh_from_db() self.joe.refresh_from_db() # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 7): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [contact1, self.joe, contact2, contact4, self.frank]) self.assertEqual(response.json['results'][0], { 'uuid': contact1.uuid, 'name': "Ann", 'language': "fre", 'urns': ["tel:+250788000001"], 'groups': [{'uuid': group.uuid, 'name': group.name}], 'fields': {'nickname': "Annie"}, 'blocked': False, 'failed': True, 'created_on': format_datetime(contact1.created_on), 'modified_on': format_datetime(contact1.modified_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % contact2.uuid) self.assertResultsByUUID(response, [contact2]) # filter by URN response = self.fetchJSON(url, 'urn=tel%3A%2B250788000004') self.assertResultsByUUID(response, [contact4]) # filter by group name response = self.fetchJSON(url, 'group=Customers') self.assertResultsByUUID(response, [contact1, self.joe]) # filter by group UUID response = self.fetchJSON(url, 'group=%s' % group.uuid) self.assertResultsByUUID(response, [contact1, self.joe]) # filter by invalid group response = self.fetchJSON(url, 'group=invalid') self.assertResultsByUUID(response, []) # filter by before response = self.fetchJSON(url, 'before=%s' % format_datetime(contact4.modified_on)) self.assertResultsByUUID(response, [contact4, self.frank]) # filter by after response = self.fetchJSON(url, 'after=%s' % format_datetime(self.joe.modified_on)) self.assertResultsByUUID(response, [contact1, self.joe])
def from_json(cls, org, json): group = json.get(InGroupTest.TEST) name = group.get(InGroupTest.NAME) uuid = group.get(InGroupTest.UUID) return InGroupTest( ContactGroup.get_or_create(org, org.created_by, name, uuid=uuid))
def save(self, obj): obj.org = self.request.user.get_org() self.object = ContactGroup.get_or_create(obj.org, self.request.user, obj.name)
def restore_object(self, attrs, instance=None): """ Update our contact """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if org.is_anon: raise ValidationError("Cannot update contacts on anonymous organizations") uuid = attrs.get('uuid', None) if uuid: contact = Contact.objects.get(uuid=uuid, org=org, is_active=True) urns = attrs.get('urns', None) phone = attrs.get('phone', None) # user didn't specify either urns or phone, stick to what already exists if urns is None and phone is None: urns = [(u.scheme, u.path) for u in contact.urns.all()] # user only specified phone, build our urns from it if phone: urns = [(TEL_SCHEME, attrs['phone'])] if uuid: contact.update_urns(urns) else: contact = Contact.get_or_create(self.user, org, urns=urns, uuid=uuid) changed = [] # update our name and language if attrs.get('name', None): contact.name = attrs['name'] changed.append('name') if 'language' in attrs: contact.language = attrs['language'] changed.append('language') # save our contact if it changed if changed: contact.save(update_fields=changed) # update our fields fields = attrs.get('fields', None) if not fields is None: for key, value in fields.items(): existing_by_key = ContactField.objects.filter(org=self.user.get_org(), key__iexact=key, is_active=True).first() if existing_by_key: contact.set_field(existing_by_key.key, value) continue # TODO as above, need to get users to stop updating via label existing_by_label = ContactField.objects.filter(org=self.user.get_org(), label__iexact=key, is_active=True).first() if existing_by_label: contact.set_field(existing_by_label.key, value) # update our groups by UUID or name (deprecated) group_uuids = attrs.get('group_uuids', None) group_names = attrs.get('groups', None) if not group_uuids is None: contact.update_groups(group_uuids) elif not group_names is None: # by name creates groups if necessary groups = [ContactGroup.get_or_create(self.user.get_org(), self.user, name) for name in group_names] contact.update_groups(groups) return contact
def import_campaigns(cls, org, user, campaign_defs, same_site=False) -> list: """ Import campaigns from a list of exported campaigns """ imported = [] for campaign_def in campaign_defs: name = campaign_def["name"] group_ref = campaign_def["group"] campaign = None group = None # if export is from this site, lookup objects by UUID if same_site: group = ContactGroup.get_or_create(org, user, group_ref["name"], uuid=group_ref["uuid"]) campaign = Campaign.objects.filter(org=org, uuid=campaign_def["uuid"]).first() if campaign: # pragma: needs cover campaign.name = Campaign.get_unique_name(org, name, ignore=campaign) campaign.save() # fall back to lookups by name if not group: group = ContactGroup.get_or_create(org, user, group_ref["name"]) if not campaign: campaign = Campaign.objects.filter(org=org, name=name).first() if not campaign: campaign_name = Campaign.get_unique_name(org, name) campaign = Campaign.create(org, user, campaign_name, group) else: campaign.group = group campaign.save() # deactivate all of our events, we'll recreate these for event in campaign.events.all(): event.release(user) # fill our campaign with events for event_spec in campaign_def["events"]: field_key = event_spec["relative_to"]["key"] if field_key == "created_on": relative_to = ContactField.system_fields.filter(org=org, key=field_key).first() else: relative_to = ContactField.get_or_create( org, user, key=field_key, label=event_spec["relative_to"]["label"], value_type="D" ) start_mode = event_spec.get("start_mode", CampaignEvent.MODE_INTERRUPT) # create our message flow for message events if event_spec["event_type"] == CampaignEvent.TYPE_MESSAGE: message = event_spec["message"] base_language = event_spec.get("base_language") if not isinstance(message, dict): try: message = json.loads(message) except ValueError: # if it's not a language dict, turn it into one message = dict(base=message) base_language = "base" event = CampaignEvent.create_message_event( org, user, campaign, relative_to, event_spec["offset"], event_spec["unit"], message, event_spec["delivery_hour"], base_language=base_language, start_mode=start_mode, ) event.update_flow_name() else: flow = Flow.objects.filter( org=org, is_active=True, is_system=False, uuid=event_spec["flow"]["uuid"] ).first() if flow: CampaignEvent.create_flow_event( org, user, campaign, relative_to, event_spec["offset"], event_spec["unit"], flow, event_spec["delivery_hour"], start_mode=start_mode, ) imported.append(campaign) return imported
def restore_object(self, attrs, instance=None): """ Update our contact """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if org.is_anon: raise ValidationError("Cannot update contacts on anonymous organizations") uuid = attrs.get('uuid', None) if uuid: contact = Contact.objects.get(uuid=uuid, org=org, is_active=True) urns = attrs.get('urns', None) phone = attrs.get('phone', None) # user didn't specify either urns or phone, stick to what already exists if urns is None and phone is None: urns = [(u.scheme, u.path) for u in contact.urns.all()] # user only specified phone, build our urns from it if phone: urns = [(TEL_SCHEME, attrs['phone'])] if uuid: contact.update_urns(urns) else: contact = Contact.get_or_create(self.user, org, urns=urns, uuid=uuid) changed = [] # update our name and language if attrs.get('name', None): contact.name = attrs['name'] changed.append('name') if 'language' in attrs: contact.language = attrs['language'] changed.append('language') # save our contact if it changed if changed: contact.save(update_fields=changed) # update our fields fields = attrs.get('fields', None) if not fields is None: for key, value in fields.items(): existing_by_key = ContactField.objects.filter(org=self.user.get_org(), key__iexact=key, is_active=True).first() if existing_by_key: contact.set_field(existing_by_key.key, value) continue # TODO as above, need to get users to stop updating via label existing_by_label = ContactField.objects.filter(org=self.user.get_org(), label__iexact=key, is_active=True).first() if existing_by_label: contact.set_field(existing_by_label.key, value) # handle our groups groups = attrs.get('groups', None) if not groups is None: contact.groups.clear() # add the groups they specified for label in groups: contact.groups.add(ContactGroup.get_or_create(label, self.user)) return contact