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 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 #4
0
        def clean(self, value):
            if value.startswith("[_NEW_]"):
                value = value[7:]

                # we must get groups for this org only
                group = ContactGroup.get_user_group(self.user.get_org(), value)
                if not group:
                    group = ContactGroup.create_static(self.user.get_org(), self.user, name=value)
                return group

            return super(RegisterTriggerForm.AddNewGroupChoiceField, self).clean(value)
Beispiel #5
0
        def clean(self, value):
            if value.startswith("[_NEW_]"):  # pragma: needs cover
                value = value[7:]

                # we must get groups for this org only
                group = ContactGroup.get_user_group(self.user.get_org(), value)
                if not group:
                    group = ContactGroup.create_static(self.user.get_org(), self.user, name=value)
                return group

            return super().clean(value)
Beispiel #6
0
    def create_group(self, name, contacts=(), query=None):
        if contacts and query:
            raise ValueError("Can't provide contact list for a dynamic group")

        if query:
            return ContactGroup.create_dynamic(self.org, self.user, name, query=query)
        else:
            group = ContactGroup.create_static(self.org, self.user, name)
            if contacts:
                group.contacts.add(*contacts)
            return group
Beispiel #7
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 #8
0
    def get_context_data(self, **kwargs):
        org = self.request.user.get_org()
        counts = ContactGroup.get_system_group_counts(org)

        # if there isn't a search filtering the queryset, we can replace the count function with a quick cache lookup to
        # speed up paging
        if hasattr(self, 'system_group') and 'search' not in self.request.REQUEST:
            self.object_list.count = lambda: counts[self.system_group]

        context = super(ContactListView, self).get_context_data(**kwargs)

        folders = [dict(count=counts[ContactGroup.TYPE_ALL], label=_("All Contacts"), url=reverse('contacts.contact_list')),
                   dict(count=counts[ContactGroup.TYPE_FAILED], label=_("Failed"), url=reverse('contacts.contact_failed')),
                   dict(count=counts[ContactGroup.TYPE_BLOCKED], label=_("Blocked"), url=reverse('contacts.contact_blocked'))]

        groups_qs = ContactGroup.user_groups.filter(org=org, is_active=True).select_related('org')
        groups_qs = groups_qs.extra(select={'lower_group_name': 'lower(contacts_contactgroup.name)'}).order_by('lower_group_name')
        groups = [dict(pk=g.pk, label=g.name, count=g.get_member_count(), is_dynamic=g.is_dynamic) for g in groups_qs]

        # resolve the paginated object list so we can initialize a cache of URNs and fields
        contacts = list(context['object_list'])
        Contact.bulk_cache_initialize(org, contacts, for_show_only=True)

        context['contacts'] = contacts
        context['groups'] = groups
        context['folders'] = folders
        context['has_contacts'] = contacts or org.has_contacts()
        context['send_form'] = SendMessageForm(self.request.user)
        return context
Beispiel #9
0
    def __init__(self, *args, **kwargs):
        self.user = kwargs["user"]
        del kwargs["user"]

        super().__init__(*args, **kwargs)
        self.fields["group"].initial = self.instance.group
        self.fields["group"].queryset = ContactGroup.get_user_groups(self.user.get_org(), ready_only=False)
Beispiel #10
0
    def import_triggers(cls, exported_json, org, user, same_site=False):
        """
        Import triggers from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(_("Unknown version (%s)" % exported_json.get('version', 0)))

        # first things first, let's create our groups if necesary and map their ids accordingly
        if 'triggers' in exported_json:
            for trigger_spec in exported_json['triggers']:

                # resolve our groups
                groups = []
                for group_spec in trigger_spec['groups']:

                    group = None

                    if same_site:
                        group = ContactGroup.user_groups.filter(org=org, pk=group_spec['id']).first()

                    if not group:
                        group = ContactGroup.user_groups.filter(org=org, name=group_spec['name']).first()

                    if not group:
                        group = ContactGroup.create(org, user, group_spec['name'])

                    if not group.is_active:
                        group.is_active = True
                        group.save()

                    groups.append(group)

                flow = Flow.objects.get(org=org, pk=trigger_spec['flow']['id'])

                # see if that trigger already exists
                trigger = Trigger.objects.filter(org=org, trigger_type=trigger_spec['trigger_type'])

                if trigger_spec['keyword']:
                    trigger = trigger.filter(keyword__iexact=trigger_spec['keyword'])

                if groups:
                    trigger = trigger.filter(groups__in=groups)

                trigger = trigger.first()

                channel = trigger_spec.get('channel', None)  # older exports won't have a channel

                if trigger:
                    trigger.is_archived = False
                    trigger.flow = flow
                    trigger.save()
                else:
                    trigger = Trigger.objects.create(org=org, trigger_type=trigger_spec['trigger_type'],
                                                     keyword=trigger_spec['keyword'], flow=flow,
                                                     created_by=user, modified_by=user,
                                                     channel=channel)

                    for group in groups:
                        trigger.groups.add(group)
Beispiel #11
0
    def clean_name(self):
        data = self.cleaned_data['name'].strip()

        if not ContactGroup.is_valid_name(data):
            raise forms.ValidationError("Group name must not be blank or begin with + or -")

        return data
Beispiel #12
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 #13
0
    def create_groups(self, orgs):
        """
        Creates the contact groups for each org
        """
        self._log("Creating %d groups... " % (len(orgs) * len(GROUPS)))

        for org in orgs:
            user = org.cache["users"][0]
            for g in GROUPS:
                if g["query"]:
                    group = ContactGroup.create_dynamic(org, user, g["name"], g["query"], evaluate=False)
                else:
                    group = ContactGroup.create_static(org, user, g["name"])
                group.member = g["member"]
                group.count = 0
                org.cache["groups"].append(group)

        self._log(self.style.SUCCESS("OK") + "\n")
Beispiel #14
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 #15
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 #16
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 #17
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 #18
0
    def _resolve_import_groups(cls, org, user, same_site: bool, specs):
        groups = []
        for spec in specs:
            group = None

            if same_site:  # pragma: needs cover
                group = ContactGroup.user_groups.filter(org=org, uuid=spec["uuid"]).first()

            if not group:
                group = ContactGroup.get_user_group_by_name(org, spec["name"])

            if not group:
                group = ContactGroup.create_static(org, user, spec["name"])  # pragma: needs cover

            if not group.is_active:  # pragma: needs cover
                group.is_active = True
                group.save()

            groups.append(group)

        return groups
Beispiel #19
0
def omnibox_mixed_search(org, query, types):
    """
    Performs a mixed group, contact and URN search, returning the first N matches of each type.
    """
    query_terms = query.split(" ") if query else None
    search_types = types or (SEARCH_ALL_GROUPS, SEARCH_CONTACTS, SEARCH_URNS)
    per_type_limit = 25
    results = []

    if SEARCH_ALL_GROUPS in search_types or SEARCH_STATIC_GROUPS in search_types:
        groups = ContactGroup.get_user_groups(org, ready_only=True)

        # exclude dynamic groups if not searching all groups
        if SEARCH_ALL_GROUPS not in search_types:
            groups = groups.filter(query=None)

        if query:
            groups = term_search(groups, ("name__icontains", ), query_terms)

        results += list(groups.order_by(Upper("name"))[:per_type_limit])

    if SEARCH_CONTACTS in search_types:
        try:
            search_results = search_contacts(
                org.id, org.cached_all_contacts_group.uuid, query, "name")
            contacts = IDSliceQuerySet(Contact, search_results.contact_ids, 0,
                                       len(search_results.contact_ids))
            results += list(contacts[:per_type_limit])
            Contact.bulk_cache_initialize(org, contacts=results)

        except SearchException:
            pass

    if SEARCH_URNS in search_types:
        if not org.is_anon and query and len(query) >= 3:
            try:
                # build an ORed query of all sendable schemes
                sendable_schemes = org.get_schemes(Channel.ROLE_SEND)
                scheme_query = " OR ".join(f"{s} ~ {json.dumps(query)}"
                                           for s in sendable_schemes)
                search_results = search_contacts(
                    org.id, org.cached_all_contacts_group.uuid, scheme_query,
                    "name")
                urns = ContactURN.objects.filter(
                    contact_id__in=search_results.contact_ids,
                    scheme__in=sendable_schemes)
                results += list(
                    urns.prefetch_related("contact").order_by(
                        Upper("path"))[:per_type_limit])
            except SearchException:
                pass

    return results
Beispiel #20
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 #21
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 #22
0
 def _create_groups(self, count, base_names, contacts):
     """
     Creates the given number of groups and fills them with contacts
     """
     groups = []
     num_bases = len(base_names)
     for g in range(0, count):
         name = '%s %d' % (base_names[g % num_bases], g + 1)
         group = ContactGroup.create_static(self.org, self.user, name)
         group.contacts.add(*contacts[(g % num_bases)::num_bases])
         groups.append(ContactGroup.user_groups.get(pk=group.pk))
     return groups
Beispiel #23
0
 def _create_groups(self, count, base_names, contacts):
     """
     Creates the given number of groups and fills them with contacts
     """
     groups = []
     num_bases = len(base_names)
     for g in range(0, count):
         name = '%s %d' % (base_names[g % num_bases], g + 1)
         group = ContactGroup.create_static(self.org, self.user, name)
         group.contacts.add(*contacts[(g % num_bases)::num_bases])
         groups.append(ContactGroup.user_groups.get(pk=group.pk))
     return groups
def cleanse_group_names(action):
    from temba.contacts.models import ContactGroup
    if action['type'] == 'add_group' or action['type'] == 'del_group':
        if 'group' in action and 'groups' not in action:
            action['groups'] = [action['group']]
        for group in action['groups']:
            if isinstance(group, dict):
                if 'name' not in group:
                    group['name'] = 'Unknown'
                if not ContactGroup.is_valid_name(group['name']):
                    group['name'] = '%s %s' % ('Contacts', group['name'])
    return action
Beispiel #25
0
        def clean(self, value):
            if value.startswith("[_NEW_]"):
                value = value[7:]

                # we must get groups for this org only
                groups = ContactGroup.user_groups.filter(name=value, org=self.user.get_org())
                if groups:
                    group = groups[0]
                else:
                    group = ContactGroup.create(self.user.get_org(), self.user, name=value)
                return group

            return super(RegisterTriggerForm.AddNewGroupChoiceField, self).clean(value)
Beispiel #26
0
        def clean(self, value):
            if value.startswith("[_NEW_]"):
                value = value[7:]

                # we must get groups for this org only
                groups = ContactGroup.user_groups.filter(name=value, org=self.user.get_org())
                if groups:
                    group = groups[0]
                else:
                    group = ContactGroup.create(self.user.get_org(), self.user, name=value)
                return group

            return super(RegisterTriggerForm.AddNewGroupChoiceField, self).clean(value)
Beispiel #27
0
    def create_groups(self, orgs):
        """
        Creates the contact groups for each org
        """
        self._log("Creating %d groups... " % (len(orgs) * len(GROUPS)))

        for org in orgs:
            user = org.cache["users"][0]
            for g in GROUPS:
                if g["query"]:
                    group = ContactGroup.create_dynamic(org,
                                                        user,
                                                        g["name"],
                                                        g["query"],
                                                        evaluate=False)
                else:
                    group = ContactGroup.create_static(org, user, g["name"])
                group.member = g["member"]
                group.count = 0
                org.cache["groups"].append(group)

        self._log(self.style.SUCCESS("OK") + "\n")
Beispiel #28
0
def cleanse_group_names(action):
    from temba.contacts.models import ContactGroup

    if action["type"] == "add_group" or action["type"] == "del_group":
        if "group" in action and "groups" not in action:
            action["groups"] = [action["group"]]
        for group in action["groups"]:
            if isinstance(group, dict):
                if "name" not in group:
                    group["name"] = "Unknown"
                if not ContactGroup.is_valid_name(group["name"]):
                    group["name"] = "%s %s" % ("Contacts", group["name"])
    return action
Beispiel #29
0
    def create_org(self, spec, superuser, country, locations):
        self._log(f"\nCreating org {spec['name']}...\n")

        org = Org.objects.create(
            uuid=spec["uuid"],
            name=spec["name"],
            timezone=pytz.timezone("America/Los_Angeles"),
            brand="rapidpro.io",
            country=country,
            created_on=timezone.now(),
            created_by=superuser,
            modified_by=superuser,
        )
        ContactGroup.create_system_groups(org)
        ContactField.create_system_fields(org)
        org.init_topups(100_000)

        # set our sequences to make ids stable across orgs
        with connection.cursor() as cursor:
            for seq_name in RESET_SEQUENCES:
                cursor.execute(
                    f"ALTER SEQUENCE {seq_name} RESTART WITH {spec['sequence_start']}"
                )

        self.create_users(spec, org)
        self.create_channels(spec, org, superuser)
        self.create_fields(spec, org, superuser)
        self.create_globals(spec, org, superuser)
        self.create_labels(spec, org, superuser)
        self.create_groups(spec, org, superuser)
        self.create_flows(spec, org, superuser)
        self.create_contacts(spec, org, superuser)
        self.create_group_contacts(spec, org, superuser)
        self.create_campaigns(spec, org, superuser)
        self.create_templates(spec, org, superuser)
        self.create_classifiers(spec, org, superuser)
        self.create_ticketers(spec, org, superuser)

        return org
Beispiel #30
0
    def include_groups(self):
        from temba.contacts.models import ContactGroup

        self.request['assets'].append({
            'type':
            "group_set",
            'url':
            '%s/group/' % self.base_assets_url,
            'content': [
                serialize_group(g)
                for g in ContactGroup.get_user_groups(self.org)
            ]
        })
        return self
Beispiel #31
0
    def create_groups(self, orgs):
        self._log("Creating %d groups... " % (len(orgs) * len(GROUPS)))

        for org in orgs:
            user = org.cache['users'][0]
            for g in GROUPS:
                if g['query']:
                    group = ContactGroup.create_dynamic(org, user, g['name'], g['query'])
                else:
                    group = ContactGroup.user_groups.create(org=org, name=g['name'], created_by=user, modified_by=user)
                group.member = g['member']
                org.cache['groups'].append(group)

        self._log(self.style.SUCCESS("OK") + '\n')
Beispiel #32
0
    def include_groups(self):
        from temba.contacts.models import ContactGroup

        self.request["assets"].append({
            "type":
            "group_set",
            "url":
            "%s/group/" % self.base_assets_url,
            "content": [
                serialize_group(g)
                for g in ContactGroup.get_user_groups(self.org)
            ],
        })
        return self
    def include_groups(self, org):
        from temba.contacts.models import ContactGroup

        self.request['assets'].append({
            'type':
            "group",
            'url':
            get_assets_url(org, self.asset_timestamp, 'group'),
            'content':
            [serialize_group(g) for g in ContactGroup.get_user_groups(org)],
            'is_set':
            True
        })
        return self
Beispiel #34
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 #35
0
def omnibox_mixed_search(org, search, types):
    """
    Performs a mixed group, contact and URN search, returning the first N matches of each type.
    """
    search_terms = search.split(' ') if search else None
    search_types = types or (SEARCH_ALL_GROUPS, SEARCH_CONTACTS, SEARCH_URNS)
    per_type_limit = 25
    results = []

    if SEARCH_ALL_GROUPS in search_types or SEARCH_STATIC_GROUPS in search_types:
        groups = ContactGroup.get_user_groups(org)

        # exclude dynamic groups if not searching all groups
        if SEARCH_ALL_GROUPS not in search_types:
            groups = groups.filter(query=None)

        if search:
            groups = term_search(groups, ('name__icontains',), search_terms)

        results += list(groups.order_by(Upper('name'))[:per_type_limit])

    if SEARCH_CONTACTS in search_types:
        contacts = Contact.objects.filter(org=org, is_active=True, is_blocked=False, is_stopped=False, is_test=False)

        try:
            search_id = int(search)
        except (ValueError, TypeError):
            search_id = None

        if org.is_anon and search_id is not None:
            contacts = contacts.filter(id=search_id)
        elif search:
            contacts = term_search(contacts, ('name__icontains',), search_terms)

        results += list(contacts.order_by(Upper('name'))[:per_type_limit])

    if SEARCH_URNS in search_types:
        # only include URNs that are send-able
        from temba.channels.models import Channel
        allowed_schemes = org.get_schemes(Channel.ROLE_SEND) if not org.is_anon else []

        urns = ContactURN.objects.filter(org=org, scheme__in=allowed_schemes).exclude(contact=None)

        if search:
            urns = term_search(urns, ('path__icontains',), search_terms)

        results += list(urns.prefetch_related('contact').order_by(Upper('path'))[:per_type_limit])

    return results  # sorted(results, key=lambda o: o.name if hasattr(o, 'name') else o.path)
Beispiel #36
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 #37
0
    def __init__(self, user, trigger_type, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.user = user
        self.org = user.get_org()
        self.trigger_type = Trigger.get_type(trigger_type)

        flow_types = self.trigger_type.allowed_flow_types
        flows = self.org.flows.filter(flow_type__in=flow_types, is_active=True, is_archived=False, is_system=False)

        self.fields["flow"].queryset = flows.order_by("name")

        groups = ContactGroup.get_user_groups(self.org, ready_only=False)

        self.fields["groups"].queryset = groups
        self.fields["exclude_groups"].queryset = groups
Beispiel #38
0
    def remap_group(group):
        if type(group) is dict:

            # we haven't been mapped yet (also, non-uuid groups can't be mapped)
            if "uuid" not in group or group["uuid"] not in uuid_map:
                group_instance = ContactGroup.get_user_group(
                    flow.org, group["name"])
                if group_instance:
                    # map group references that started with a uuid
                    if "uuid" in group:
                        uuid_map[group["uuid"]] = group_instance.uuid
                    group["uuid"] = group_instance.uuid

            # we were already mapped
            elif group["uuid"] in uuid_map:
                group["uuid"] = uuid_map[group["uuid"]]
Beispiel #39
0
def omnibox_mixed_search(org, search, types):
    """
    Performs a mixed group, contact and URN search, returning the first N matches of each type.
    """
    search_terms = search.split(" ") if search else None
    search_types = types or (SEARCH_ALL_GROUPS, SEARCH_CONTACTS, SEARCH_URNS)
    per_type_limit = 25
    results = []

    if SEARCH_ALL_GROUPS in search_types or SEARCH_STATIC_GROUPS in search_types:
        groups = ContactGroup.get_user_groups(org)

        # exclude dynamic groups if not searching all groups
        if SEARCH_ALL_GROUPS not in search_types:
            groups = groups.filter(query=None)

        if search:
            groups = term_search(groups, ("name__icontains",), search_terms)

        results += list(groups.order_by(Upper("name"))[:per_type_limit])

    if SEARCH_CONTACTS in search_types:
        sort_struct = {"field_type": "attribute", "sort_direction": "asc", "field_name": "name.keyword"}

        try:
            search_id = int(search)
        except (ValueError, TypeError):
            search_id = None

        if org.is_anon and search_id is not None:
            search_text = f"id = {search_id}"
        elif search:
            # we use trigrams on Elasticsearch, minimum required length for a term is 3
            filtered_search_terms = (
                search_term for search_term in search_terms if search_term != "" and len(search_term) >= 3
            )

            search_text = " AND ".join(f"name ~ {search_term}" for search_term in filtered_search_terms)

        else:
            search_text = None

        from temba.utils.es import ES

        try:
            search_object, _ = contact_es_search(org, search_text, sort_struct=sort_struct)

            es_search = search_object.source(fields=("id",)).using(ES)[:per_type_limit].execute()
            contact_ids = list(mapEStoDB(Contact, es_search, only_ids=True))
            es_results = Contact.objects.filter(id__in=contact_ids).order_by(Upper("name"))

            results += list(es_results[:per_type_limit])

            Contact.bulk_cache_initialize(org, contacts=results)

        except SearchException:
            # ignore SearchException
            pass

    if SEARCH_URNS in search_types:
        # only include URNs that are send-able
        from temba.channels.models import Channel

        allowed_schemes = org.get_schemes(Channel.ROLE_SEND)

        from temba.utils.es import ES, ModelESSearch

        if search:
            # we use trigrams on Elasticsearch, minimum required length for a term is 3
            filtered_search_terms = (
                search_term for search_term in search_terms if search_term != "" and len(search_term) >= 3
            )

            must_condition = [{"match_phrase": {"urns.path": search_term}} for search_term in filtered_search_terms]
        else:
            must_condition = []

        es_query = {
            "query": {
                "bool": {
                    "filter": [
                        {"term": {"org_id": org.id}},
                        {"term": {"groups": str(org.cached_all_contacts_group.uuid)}},
                    ],
                    "must": [
                        {
                            "nested": {
                                "path": "urns",
                                "query": {
                                    "bool": {
                                        "must": must_condition,
                                        "should": [{"term": {"urns.scheme": scheme}} for scheme in allowed_schemes],
                                    }
                                },
                            }
                        }
                    ],
                }
            },
            "sort": [{"name.keyword": {"order": "asc"}}],
        }

        if not org.is_anon:
            search_object = ModelESSearch(model=Contact, index="contacts").from_dict(es_query).params(routing=org.id)

            es_search = search_object.source(fields=("id",)).using(ES)[:per_type_limit].execute()
            es_results = mapEStoDB(Contact, es_search, only_ids=True)

            # get ContactURNs for filtered Contacts
            urns = ContactURN.objects.filter(contact_id__in=list(es_results))

            # we got max `per_type_limit` contacts, but each contact can have multiple URNs and we need to limit the
            # results to the per type limit
            results += list(urns.prefetch_related("contact").order_by(Upper("path"))[:per_type_limit])

    return results  # sorted(results, key=lambda o: o.name if hasattr(o, 'name') else o.path)
Beispiel #40
0
def omnibox_mixed_search(org, search, types):
    """
    Performs a mixed group, contact and URN search, returning the first N matches of each type.
    """
    search_terms = search.split(" ") if search else None
    search_types = types or (SEARCH_ALL_GROUPS, SEARCH_CONTACTS, SEARCH_URNS)
    per_type_limit = 25
    results = []

    if SEARCH_ALL_GROUPS in search_types or SEARCH_STATIC_GROUPS in search_types:
        groups = ContactGroup.get_user_groups(org, ready_only=True)

        # exclude dynamic groups if not searching all groups
        if SEARCH_ALL_GROUPS not in search_types:
            groups = groups.filter(query=None)

        if search:
            groups = term_search(groups, ("name__icontains", ), search_terms)

        results += list(groups.order_by(Upper("name"))[:per_type_limit])

    if SEARCH_CONTACTS in search_types:
        sort_struct = {
            "field_type": "attribute",
            "sort_direction": "asc",
            "field_name": "name.keyword"
        }

        try:
            search_id = int(search)
        except (ValueError, TypeError):
            search_id = None

        if org.is_anon and search_id is not None:
            search_text = f"id = {search_id}"
        elif search:
            search_text = " AND ".join(f"name ~ {search_term}"
                                       for search_term in search_terms)

        else:
            search_text = None

        from temba.utils.es import ES

        try:
            search_object, _ = contact_es_search(org,
                                                 search_text,
                                                 sort_struct=sort_struct)

            es_search = search_object.source(
                fields=("id", )).using(ES)[:per_type_limit].execute()
            contact_ids = list(mapEStoDB(Contact, es_search, only_ids=True))
            es_results = Contact.objects.filter(id__in=contact_ids).order_by(
                Upper("name"))

            results += list(es_results[:per_type_limit])

            Contact.bulk_cache_initialize(org, contacts=results)

        except SearchException:
            # ignore SearchException
            pass

    if SEARCH_URNS in search_types:
        # only include URNs that are send-able
        from temba.channels.models import Channel

        allowed_schemes = org.get_schemes(Channel.ROLE_SEND)

        from temba.utils.es import ES, ModelESSearch

        if search:
            # we use trigrams on Elasticsearch, minimum required length for a term is 3
            filtered_search_terms = (
                search_term for search_term in search_terms
                if search_term != ""  # and len(search_term) >= 3
            )

            must_condition = [{
                "match_phrase": {
                    "urns.path": search_term
                }
            } for search_term in filtered_search_terms]
        else:
            must_condition = []

        es_query = {
            "query": {
                "bool": {
                    "filter": [
                        {
                            "term": {
                                "org_id": org.id
                            }
                        },
                        {
                            "term": {
                                "groups":
                                str(org.cached_all_contacts_group.uuid)
                            }
                        },
                    ],
                    "must": [{
                        "nested": {
                            "path": "urns",
                            "query": {
                                "bool": {
                                    "must":
                                    must_condition,
                                    "should": [{
                                        "term": {
                                            "urns.scheme": scheme
                                        }
                                    } for scheme in allowed_schemes],
                                }
                            },
                        }
                    }],
                }
            },
            "sort": [{
                "name.keyword": {
                    "order": "asc"
                }
            }],
        }

        if not org.is_anon:
            search_object = ModelESSearch(
                model=Contact,
                index="contacts").from_dict(es_query).params(routing=org.id)

            es_search = search_object.source(
                fields=("id", )).using(ES)[:per_type_limit].execute()
            es_results = mapEStoDB(Contact, es_search, only_ids=True)

            # get ContactURNs for filtered Contacts
            urns = ContactURN.objects.filter(contact_id__in=list(es_results))

            # we got max `per_type_limit` contacts, but each contact can have multiple URNs and we need to limit the
            # results to the per type limit
            results += list(
                urns.prefetch_related("contact").order_by(
                    Upper("path"))[:per_type_limit])

    return results  # sorted(results, key=lambda o: o.name if hasattr(o, 'name') else o.path)
Beispiel #41
0
 def validate_name(self, value):
     if not ContactGroup.is_valid_name(value):
         raise serializers.ValidationError("Name contains illegal characters.")
     return value
Beispiel #42
0
 def create_group(self, name, contacts):
     group = ContactGroup.create(self.org, self.user, name)
     group.contacts.add(*contacts)
     return group
Beispiel #43
0
    def import_campaigns(cls, exported_json, org, user, same_site=False):
        """
        Import campaigns from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION

        if Flow.is_before_version(exported_json.get("version", "0"), EARLIEST_IMPORT_VERSION):  # pragma: needs cover
            raise ValueError(_("Unknown version (%s)" % exported_json.get("version", 0)))

        if "campaigns" in exported_json:
            for campaign_spec in exported_json["campaigns"]:
                name = campaign_spec["name"]
                campaign = None
                group = None

                # first check if we have the objects by id
                if same_site:
                    group = ContactGroup.user_groups.filter(uuid=campaign_spec["group"]["uuid"], org=org).first()
                    if group:  # pragma: needs cover
                        group.name = campaign_spec["group"]["name"]
                        group.save()

                    campaign = Campaign.objects.filter(org=org, uuid=campaign_spec["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_user_group(org, campaign_spec["group"]["name"])

                if not campaign:
                    campaign = Campaign.objects.filter(org=org, name=name).first()

                # all else fails, create the objects from scratch
                if not group:
                    group = ContactGroup.create_static(org, user, campaign_spec["group"]["name"])

                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()

                # fill our campaign with events
                for event_spec in campaign_spec["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,
                            )

                # update our scheduled events for this campaign
                EventFire.update_campaign_events(campaign)
Beispiel #44
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 #45
0
    def import_campaigns(cls, exported_json, org, user, same_site=False):
        """
        Import campaigns from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION

        if exported_json.get("version", 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(_("Unknown version (%s)" % exported_json.get("version", 0)))

        if "campaigns" in exported_json:
            for campaign_spec in exported_json["campaigns"]:
                name = campaign_spec["name"]
                campaign = None
                group = None

                # first check if we have the objects by id
                if same_site:
                    group = ContactGroup.user_groups.filter(
                        id=campaign_spec["group"]["id"], org=org, is_active=True
                    ).first()
                    if group:
                        group.name = campaign_spec["group"]["name"]
                        group.save()

                    campaign = Campaign.objects.filter(org=org, id=campaign_spec["id"]).first()
                    if campaign:
                        campaign.name = Campaign.get_unique_name(org, name, ignore=campaign)
                        campaign.save()

                # fall back to lookups by name
                if not group:
                    group = ContactGroup.user_groups.filter(name=campaign_spec["group"]["name"], org=org).first()

                if not campaign:
                    campaign = Campaign.objects.filter(org=org, name=name).first()

                # all else fails, create the objects from scratch
                if not group:
                    group = ContactGroup.create(org, user, campaign_spec["group"]["name"])

                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()

                # we want to nuke old single message flows
                for event in campaign.events.all():
                    if event.flow.flow_type == Flow.MESSAGE:
                        event.flow.delete()

                # and all of the events, we'll recreate these
                campaign.events.all().delete()

                # fill our campaign with events
                for event_spec in campaign_spec["events"]:
                    relative_to = ContactField.get_or_create(
                        org, key=event_spec["relative_to"]["key"], label=event_spec["relative_to"]["label"]
                    )

                    # create our message flow for message events
                    if event_spec["event_type"] == MESSAGE_EVENT:
                        event = CampaignEvent.create_message_event(
                            org,
                            user,
                            campaign,
                            relative_to,
                            event_spec["offset"],
                            event_spec["unit"],
                            event_spec["message"],
                            event_spec["delivery_hour"],
                        )
                        event.update_flow_name()
                    else:
                        flow = Flow.objects.filter(org=org, id=event_spec["flow"]["id"]).first()
                        if flow:
                            CampaignEvent.create_flow_event(
                                org,
                                user,
                                campaign,
                                relative_to,
                                event_spec["offset"],
                                event_spec["unit"],
                                flow,
                                event_spec["delivery_hour"],
                            )

                # update our scheduled events for this campaign
                EventFire.update_campaign_events(campaign)
Beispiel #46
0
    def import_campaigns(cls, exported_json, org, user, same_site=False):
        """
        Import campaigns from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(_("Unknown version (%s)" % exported_json.get('version', 0)))

        if 'campaigns' in exported_json:
            for campaign_spec in exported_json['campaigns']:
                name = campaign_spec['name']
                campaign = None
                group = None

                # first check if we have the objects by id
                if same_site:
                    group = ContactGroup.user_groups.filter(uuid=campaign_spec['group']['uuid'], org=org).first()
                    if group:
                        group.name = campaign_spec['group']['name']
                        group.save()

                    campaign = Campaign.objects.filter(org=org, uuid=campaign_spec['uuid']).first()
                    if campaign:
                        campaign.name = Campaign.get_unique_name(org, name, ignore=campaign)
                        campaign.save()

                # fall back to lookups by name
                if not group:
                    group = ContactGroup.get_user_group(org, campaign_spec['group']['name'])

                if not campaign:
                    campaign = Campaign.objects.filter(org=org, name=name).first()

                # all else fails, create the objects from scratch
                if not group:
                    group = ContactGroup.create_static(org, user, campaign_spec['group']['name'])

                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()

                # we want to nuke old single message flows
                for event in campaign.events.all():
                    if event.flow.flow_type == Flow.MESSAGE:
                        event.flow.release()

                # and all of the events, we'll recreate these
                campaign.events.all().delete()

                # fill our campaign with events
                for event_spec in campaign_spec['events']:
                    relative_to = ContactField.get_or_create(org, user,
                                                             key=event_spec['relative_to']['key'],
                                                             label=event_spec['relative_to']['label'])

                    # create our message flow for message events
                    if event_spec['event_type'] == CampaignEvent.TYPE_MESSAGE:
                        event = CampaignEvent.create_message_event(org, user, campaign, relative_to,
                                                                   event_spec['offset'],
                                                                   event_spec['unit'],
                                                                   event_spec['message'],
                                                                   event_spec['delivery_hour'])
                        event.update_flow_name()
                    else:
                        flow = Flow.objects.filter(org=org, is_active=True, 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'])

                # update our scheduled events for this campaign
                EventFire.update_campaign_events(campaign)
Beispiel #47
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 #48
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 #49
0
            def __init__(self, user, *args, **kwargs):
                super().__init__(*args, **kwargs)

                self.fields["group"].queryset = ContactGroup.get_user_groups(user.get_org()).order_by("name")
Beispiel #50
0
 def pre_process(self, request, *args, **kwargs):
     if hasattr(self, 'system_group'):
         org = request.user.get_org()
         self.queryset = ContactGroup.get_system_group_queryset(org, self.system_group)
Beispiel #51
0
 def create_group(self, name, contacts):
     group = ContactGroup.create(self.org, self.user, name)
     group.contacts.add(*contacts)
     return group
Beispiel #52
0
    def import_campaigns(cls, exported_json, org, user, site=None):
        """
        Import campaigns from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(
                _("Unknown version (%s)" % exported_json.get('version', 0)))

        if 'campaigns' in exported_json:
            for campaign_spec in exported_json['campaigns']:
                name = campaign_spec['name']
                campaign = None
                group = None

                # first check if we have the objects by id
                if site and site == exported_json.get('site', None):
                    group = ContactGroup.user_groups.filter(
                        id=campaign_spec['group']['id'],
                        org=org,
                        is_active=True).first()
                    if group:
                        group.name = campaign_spec['group']['name']
                        group.save()

                    campaign = Campaign.objects.filter(
                        org=org, id=campaign_spec['id']).first()
                    if campaign:
                        campaign.name = Campaign.get_unique_name(
                            name, org, ignore=campaign)
                        campaign.save()

                # fall back to lookups by name
                if not group:
                    group = ContactGroup.user_groups.filter(
                        name=campaign_spec['group']['name'], org=org).first()

                if not campaign:
                    campaign = Campaign.objects.filter(org=org,
                                                       name=name).first()

                # all else fails, create the objects from scratch
                if not group:
                    group = ContactGroup.create(org, user,
                                                campaign_spec['group']['name'])

                if not campaign:
                    campaign = Campaign.objects.create(
                        name=Campaign.get_unique_name(name, org),
                        org=org,
                        group=group,
                        created_by=user,
                        modified_by=user)
                else:
                    campaign.group = group
                    campaign.save()

                # we want to nuke old single message flows
                for event in campaign.events.all():
                    if event.flow.flow_type == Flow.MESSAGE:
                        event.flow.delete()

                # and all of the events, we'll recreate these
                campaign.events.all().delete()

                # fill our campaign with events
                for event_spec in campaign_spec['events']:
                    relative_to = ContactField.get_or_create(
                        org,
                        key=event_spec['relative_to']['key'],
                        label=event_spec['relative_to']['label'])

                    # create our message flow for message events
                    if event_spec['event_type'] == MESSAGE_EVENT:
                        flow = Flow.create_single_message(
                            org, user, event_spec['message'])
                    else:
                        flow = Flow.objects.filter(
                            org=org, id=event_spec['flow']['id']).first()

                    if flow:
                        event = campaign.events.create(
                            created_by=user,
                            modified_by=user,
                            offset=event_spec['offset'],
                            unit=event_spec['unit'],
                            event_type=event_spec['event_type'],
                            delivery_hour=event_spec['delivery_hour'],
                            message=event_spec['message'],
                            flow=flow,
                            relative_to=relative_to)
                        event.update_flow_name()

                # update our scheduled events for this campaign
                EventFire.update_campaign_events(campaign)
Beispiel #53
0
    def import_triggers(cls, exported_json, org, user, same_site=False):
        """
        Import triggers from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION

        if Flow.is_before_version(exported_json.get("version", 0), EARLIEST_IMPORT_VERSION):  # pragma: needs cover
            raise ValueError(_("Unknown version (%s)" % exported_json.get("version", 0)))

        # first things first, let's create our groups if necesary and map their ids accordingly
        if "triggers" in exported_json:
            for trigger_spec in exported_json["triggers"]:

                # resolve our groups
                groups = []
                for group_spec in trigger_spec["groups"]:

                    group = None

                    if same_site:  # pragma: needs cover
                        group = ContactGroup.user_groups.filter(org=org, uuid=group_spec["uuid"]).first()

                    if not group:
                        group = ContactGroup.get_user_group(org, group_spec["name"])

                    if not group:
                        group = ContactGroup.create_static(org, user, group_spec["name"])

                    if not group.is_active:  # pragma: needs cover
                        group.is_active = True
                        group.save()

                    groups.append(group)

                flow = Flow.objects.get(org=org, uuid=trigger_spec["flow"]["uuid"], is_active=True)

                # see if that trigger already exists
                trigger = Trigger.objects.filter(org=org, trigger_type=trigger_spec["trigger_type"])

                if trigger_spec["keyword"]:
                    trigger = trigger.filter(keyword__iexact=trigger_spec["keyword"])

                if groups:
                    trigger = trigger.filter(groups__in=groups)

                trigger = trigger.first()
                if trigger:
                    trigger.is_archived = False
                    trigger.flow = flow
                    trigger.save()
                else:

                    # if we have a channel resolve it
                    channel = trigger_spec.get("channel", None)  # older exports won't have a channel
                    if channel:
                        channel = Channel.objects.filter(uuid=channel, org=org).first()

                    trigger = Trigger.objects.create(
                        org=org,
                        trigger_type=trigger_spec["trigger_type"],
                        keyword=trigger_spec["keyword"],
                        flow=flow,
                        created_by=user,
                        modified_by=user,
                        channel=channel,
                    )

                    for group in groups:
                        trigger.groups.add(group)
Beispiel #54
0
 def validate_name(self, value):
     if not ContactGroup.is_valid_name(value):
         raise serializers.ValidationError(
             "Name contains illegal characters.")
     return value
Beispiel #55
0
    def import_triggers(cls, exported_json, org, user, same_site=False):
        """
        Import triggers from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(
                _("Unknown version (%s)" % exported_json.get('version', 0)))

        # first things first, let's create our groups if necesary and map their ids accordingly
        if 'triggers' in exported_json:
            for trigger_spec in exported_json['triggers']:

                # resolve our groups
                groups = []
                for group_spec in trigger_spec['groups']:

                    group = None

                    if same_site:
                        group = ContactGroup.user_groups.filter(
                            org=org, uuid=group_spec['uuid']).first()

                    if not group:
                        group = ContactGroup.get_user_group(
                            org, group_spec['name'])

                    if not group:
                        group = ContactGroup.create_static(
                            org, user, group_spec['name'])

                    if not group.is_active:
                        group.is_active = True
                        group.save()

                    groups.append(group)

                flow = Flow.objects.get(org=org,
                                        uuid=trigger_spec['flow']['uuid'],
                                        is_active=True)

                # see if that trigger already exists
                trigger = Trigger.objects.filter(
                    org=org, trigger_type=trigger_spec['trigger_type'])

                if trigger_spec['keyword']:
                    trigger = trigger.filter(
                        keyword__iexact=trigger_spec['keyword'])

                if groups:
                    trigger = trigger.filter(groups__in=groups)

                trigger = trigger.first()
                if trigger:
                    trigger.is_archived = False
                    trigger.flow = flow
                    trigger.save()
                else:

                    # if we have a channel resolve it
                    channel = trigger_spec.get(
                        'channel', None)  # older exports won't have a channel
                    if channel:
                        channel = Channel.objects.filter(uuid=channel,
                                                         org=org).first()

                    trigger = Trigger.objects.create(
                        org=org,
                        trigger_type=trigger_spec['trigger_type'],
                        keyword=trigger_spec['keyword'],
                        flow=flow,
                        created_by=user,
                        modified_by=user,
                        channel=channel)

                    for group in groups:
                        trigger.groups.add(group)
Beispiel #56
0
    def import_campaigns(cls, exported_json, org, user, same_site=False):
        """
        Import campaigns from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if Flow.is_before_version(
                exported_json.get('version', "0"),
                EARLIEST_IMPORT_VERSION):  # pragma: needs cover
            raise ValueError(
                _("Unknown version (%s)" % exported_json.get('version', 0)))

        if 'campaigns' in exported_json:
            for campaign_spec in exported_json['campaigns']:
                name = campaign_spec['name']
                campaign = None
                group = None

                # first check if we have the objects by id
                if same_site:
                    group = ContactGroup.user_groups.filter(
                        uuid=campaign_spec['group']['uuid'], org=org).first()
                    if group:  # pragma: needs cover
                        group.name = campaign_spec['group']['name']
                        group.save()

                    campaign = Campaign.objects.filter(
                        org=org, uuid=campaign_spec['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_user_group(
                        org, campaign_spec['group']['name'])

                if not campaign:
                    campaign = Campaign.objects.filter(org=org,
                                                       name=name).first()

                # all else fails, create the objects from scratch
                if not group:
                    group = ContactGroup.create_static(
                        org, user, campaign_spec['group']['name'])

                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()

                # we want to nuke old single message flows
                for event in campaign.events.all():
                    if event.flow.flow_type == Flow.MESSAGE:
                        event.flow.release()

                # and all of the events, we'll recreate these
                campaign.events.all().delete()

                # fill our campaign with events
                for event_spec in campaign_spec['events']:
                    relative_to = ContactField.get_or_create(
                        org,
                        user,
                        key=event_spec['relative_to']['key'],
                        label=event_spec['relative_to']['label'])

                    # 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)
                        event.update_flow_name()
                    else:
                        flow = Flow.objects.filter(
                            org=org,
                            is_active=True,
                            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'])

                # update our scheduled events for this campaign
                EventFire.update_campaign_events(campaign)
Beispiel #57
0
            def __init__(self, user, *args, **kwargs):
                super(CampaignCRUDL.Create.CampaignForm,
                      self).__init__(*args, **kwargs)

                self.fields['group'].queryset = ContactGroup.get_user_groups(
                    user.get_org()).order_by('name')
Beispiel #58
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
Beispiel #59
0
            def __init__(self, user, *args, **kwargs):
                super().__init__(*args, **kwargs)

                self.fields["group"].queryset = ContactGroup.get_user_groups(user.get_org()).order_by("name")