Beispiel #1
0
    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}])
Beispiel #2
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])
Beispiel #3
0
    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])
Beispiel #4
0
    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, [])
Beispiel #5
0
    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)
Beispiel #6
0
    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
Beispiel #7
0
    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)
Beispiel #8
0
    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
Beispiel #10
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
    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
Beispiel #13
0
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))
Beispiel #14
0
    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
        }])
Beispiel #15
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
Beispiel #16
0
    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])
Beispiel #17
0
 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))
Beispiel #18
0
 def save(self, obj):
     obj.org = self.request.user.get_org()
     self.object = ContactGroup.get_or_create(obj.org, self.request.user, obj.name)
Beispiel #19
0
    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
Beispiel #20
0
 def save(self, obj):
     obj.org = self.request.user.get_org()
     self.object = ContactGroup.get_or_create(obj.org, self.request.user, obj.name)
Beispiel #21
0
    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
Beispiel #22
0
    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