def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = ContactGroup.get_or_create(self.org, self.admin, "Customers") developers = ContactGroup.get_or_create(self.org, self.admin, "Developers") ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") developers.update_contacts(self.admin, [self.frank], add=True) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 3): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [ {'uuid': developers.uuid, 'name': "Developers", 'count': 1}, {'uuid': customers.uuid, 'name': "Customers", 'count': 0} ]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertEqual(response.json['results'], [{'uuid': customers.uuid, 'name': "Customers", 'count': 0}])
def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = self.create_group("Customers", [self.frank]) developers = self.create_group("Developers", query="isdeveloper = YES") # group belong to other org ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 3): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [ {'uuid': developers.uuid, 'name': "Developers", 'query': "isdeveloper = YES", 'count': 0}, {'uuid': customers.uuid, 'name': "Customers", 'query': None, 'count': 1} ]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertResultsByUUID(response, [customers])
def validate_groups(self, value): if value is not None: self.group_objs = [] for name in value: if not ContactGroup.is_valid_name(name): raise serializers.ValidationError(_("Invalid group name: '%s'") % name) self.group_objs.append(ContactGroup.get_or_create(self.org, self.user, name)) return value
def 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)
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)
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
def test_campaigns(self): url = reverse('api.v2.campaigns') self.assertEndpointAccess(url) reporters = self.create_group("Reporters", [self.joe, self.frank]) campaign1 = Campaign.create(self.org, self.admin, "Reminders #1", reporters) campaign2 = Campaign.create(self.org, self.admin, "Reminders #2", reporters) # create campaign for other org spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") Campaign.create(self.org2, self.admin2, "Cool stuff", spammers) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 2): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [campaign2, campaign1]) self.assertEqual(response.json['results'][0], { 'uuid': campaign2.uuid, 'name': "Reminders #2", 'group': {'uuid': reporters.uuid, 'name': "Reporters"}, 'created_on': format_datetime(campaign2.created_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % campaign1.uuid) self.assertResultsByUUID(response, [campaign1])
def 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
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)
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)
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
def test_campaign_events(self): url = reverse('api.v2.campaign_events') self.assertEndpointAccess(url) flow = self.create_flow() reporters = self.create_group("Reporters", [self.joe, self.frank]) registration = ContactField.get_or_create(self.org, self.admin, 'registration', "Registration") campaign1 = Campaign.create(self.org, self.admin, "Reminders", reporters) event1 = CampaignEvent.create_message_event(self.org, self.admin, campaign1, registration, 1, CampaignEvent.UNIT_DAYS, "Don't forget to brush your teeth") campaign2 = Campaign.create(self.org, self.admin, "Notifications", reporters) event2 = CampaignEvent.create_flow_event(self.org, self.admin, campaign2, registration, 6, CampaignEvent.UNIT_HOURS, flow, delivery_hour=12) # create event for another org joined = ContactField.get_or_create(self.org2, self.admin2, 'joined', "Joined On") spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") spam = Campaign.create(self.org2, self.admin2, "Cool stuff", spammers) CampaignEvent.create_flow_event(self.org2, self.admin2, spam, joined, 6, CampaignEvent.UNIT_HOURS, flow, delivery_hour=12) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 4): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [event2, event1]) self.assertEqual(response.json['results'][0], { 'uuid': event2.uuid, 'campaign': {'uuid': campaign2.uuid, 'name': "Notifications"}, 'relative_to': {'key': "registration", 'label': "Registration"}, 'offset': 6, 'unit': 'hours', 'delivery_hour': 12, 'flow': {'uuid': flow.uuid, 'name': "Color Flow"}, 'message': None, 'created_on': format_datetime(event2.created_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % event1.uuid) self.assertResultsByUUID(response, [event1]) # filter by campaign name response = self.fetchJSON(url, 'campaign=Reminders') self.assertResultsByUUID(response, [event1]) # filter by campaign UUID response = self.fetchJSON(url, 'campaign=%s' % campaign1.uuid) self.assertResultsByUUID(response, [event1]) # filter by invalid campaign response = self.fetchJSON(url, 'campaign=invalid') self.assertResultsByUUID(response, [])
def 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")
def save(self): name = self.validated_data.get('name') if self.instance: self.instance.name = name self.instance.save(update_fields=('name',)) return self.instance else: return ContactGroup.get_or_create(self.context['org'], self.context['user'], name)
def save(self): name = self.validated_data.get("name") if self.instance: self.instance.name = name self.instance.save(update_fields=("name",)) return self.instance else: return ContactGroup.get_or_create(self.context["org"], self.context["user"], name)
def save(self): name = self.validated_data.get('name') if self.instance: self.instance.name = name self.instance.save(update_fields=('name', )) return self.instance else: return ContactGroup.get_or_create(self.context['org'], self.context['user'], name)
def save(self): name = self.validated_data.get("name") if self.instance: self.instance.name = name self.instance.save(update_fields=("name", )) return self.instance else: return ContactGroup.get_or_create(self.context["org"], self.context["user"], name)
def _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
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
def restore_object(self, attrs, instance=None): """ Create or update our campaign """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if 'campaign' in attrs: campaign = Campaign.objects.get(pk=attrs['campaign'], is_active=True, is_archived=False, org=org) campaign.name = attrs['name'] campaign.group = ContactGroup.get_or_create(org, self.user, attrs['group']) campaign.save() else: group = ContactGroup.get_or_create(org, self.user, attrs['group']) campaign = Campaign.objects.create(name=attrs['name'], group=group, org=org, created_by=self.user, modified_by=self.user) return campaign
def restore_object(self, attrs, instance=None): """ Create or update our campaign """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if 'campaign' in attrs: campaign = Campaign.objects.get(pk=attrs['campaign'], is_active=True, is_archived=False, org=org) campaign.name = attrs['name'] campaign.group = ContactGroup.get_or_create(attrs['group'], self.user) campaign.save() else: group = ContactGroup.get_or_create(attrs['group'], self.user) campaign = Campaign.objects.create(name=attrs['name'], group=group, org=org, created_by=self.user, modified_by=self.user) return campaign
def _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
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)
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
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
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 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')
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
def test_groups(self): url = reverse('api.v2.groups') self.assertEndpointAccess(url) customers = ContactGroup.get_or_create(self.org, self.admin, "Customers") developers = ContactGroup.get_or_create(self.org, self.admin, "Developers") spammers = ContactGroup.get_or_create(self.org2, self.admin2, "Spammers") developers.update_contacts(self.admin, [self.frank], add=True) # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 1): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertEqual(response.json['results'], [{ 'uuid': developers.uuid, 'name': "Developers", 'count': 1 }, { 'uuid': customers.uuid, 'name': "Customers", 'count': 0 }]) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % customers.uuid) self.assertEqual(response.json['results'], [{ 'uuid': customers.uuid, 'name': "Customers", 'count': 0 }])
def 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)
def parse_groups(cls, org, json_obj): # we actually instantiate our contacts here groups = [] for group_data in json_obj.get(VariableContactAction.GROUPS): group_uuid = group_data.get(VariableContactAction.UUID, None) group_name = group_data.get(VariableContactAction.NAME) # flows from when true deletion was allowed need this if not group_name: group_name = "Missing" group = ContactGroup.get_or_create(org, org.get_user(), group_name, uuid=group_uuid) groups.append(group) return groups
def __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
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"]]
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)
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)
def validate_name(self, value): if not ContactGroup.is_valid_name(value): raise serializers.ValidationError("Name contains illegal characters.") return value
def create_group(self, name, contacts): group = ContactGroup.create(self.org, self.user, name) group.contacts.add(*contacts) return group
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)
def test_contacts(self): url = reverse('api.v2.contacts') self.assertEndpointAccess(url) # create some more contacts (in addition to Joe and Frank) contact1 = self.create_contact("Ann", "0788000001", language='fre') contact2 = self.create_contact("Bob", "0788000002") contact3 = self.create_contact("Cat", "0788000003") contact4 = self.create_contact("Don", "0788000004") contact1.set_field(self.user, 'nickname', "Annie", label="Nick name") contact1.fail() contact2.block(self.user) contact3.release(self.user) # put some contacts in a group group = ContactGroup.get_or_create(self.org, self.admin, "Customers") group.update_contacts(self.user, [self.joe], add=True) # add contacts separately for predictable modified_on group.update_contacts(self.user, [contact1], add=True) # ordering contact1.refresh_from_db() self.joe.refresh_from_db() # no filtering with self.assertNumQueries(NUM_BASE_REQUEST_QUERIES + 7): response = self.fetchJSON(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.json['next'], None) self.assertResultsByUUID(response, [contact1, self.joe, contact2, contact4, self.frank]) self.assertEqual(response.json['results'][0], { 'uuid': contact1.uuid, 'name': "Ann", 'language': "fre", 'urns': ["tel:+250788000001"], 'groups': [{'uuid': group.uuid, 'name': group.name}], 'fields': {'nickname': "Annie"}, 'blocked': False, 'failed': True, 'created_on': format_datetime(contact1.created_on), 'modified_on': format_datetime(contact1.modified_on) }) # filter by UUID response = self.fetchJSON(url, 'uuid=%s' % contact2.uuid) self.assertResultsByUUID(response, [contact2]) # filter by URN response = self.fetchJSON(url, 'urn=tel%3A%2B250788000004') self.assertResultsByUUID(response, [contact4]) # filter by group name response = self.fetchJSON(url, 'group=Customers') self.assertResultsByUUID(response, [contact1, self.joe]) # filter by group UUID response = self.fetchJSON(url, 'group=%s' % group.uuid) self.assertResultsByUUID(response, [contact1, self.joe]) # filter by invalid group response = self.fetchJSON(url, 'group=invalid') self.assertResultsByUUID(response, []) # filter by before response = self.fetchJSON(url, 'before=%s' % format_datetime(contact4.modified_on)) self.assertResultsByUUID(response, [contact4, self.frank]) # filter by after response = self.fetchJSON(url, 'after=%s' % format_datetime(self.joe.modified_on)) self.assertResultsByUUID(response, [contact1, self.joe])
def 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)
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)
def restore_object(self, attrs, instance=None): """ Update our contact """ if instance: # pragma: no cover raise ValidationError("Invalid operation") org = self.user.get_org() if org.is_anon: raise ValidationError("Cannot update contacts on anonymous organizations") uuid = attrs.get('uuid', None) if uuid: contact = Contact.objects.get(uuid=uuid, org=org, is_active=True) urns = attrs.get('urns', None) phone = attrs.get('phone', None) # user didn't specify either urns or phone, stick to what already exists if urns is None and phone is None: urns = [(u.scheme, u.path) for u in contact.urns.all()] # user only specified phone, build our urns from it if phone: urns = [(TEL_SCHEME, attrs['phone'])] if uuid: contact.update_urns(urns) else: contact = Contact.get_or_create(self.user, org, urns=urns, uuid=uuid) changed = [] # update our name and language if attrs.get('name', None): contact.name = attrs['name'] changed.append('name') if 'language' in attrs: contact.language = attrs['language'] changed.append('language') # save our contact if it changed if changed: contact.save(update_fields=changed) # update our fields fields = attrs.get('fields', None) if not fields is None: for key, value in fields.items(): existing_by_key = ContactField.objects.filter(org=self.user.get_org(), key__iexact=key, is_active=True).first() if existing_by_key: contact.set_field(existing_by_key.key, value) continue # TODO as above, need to get users to stop updating via label existing_by_label = ContactField.objects.filter(org=self.user.get_org(), label__iexact=key, is_active=True).first() if existing_by_label: contact.set_field(existing_by_label.key, value) # update our groups by UUID or name (deprecated) group_uuids = attrs.get('group_uuids', None) group_names = attrs.get('groups', None) if not group_uuids is None: contact.update_groups(group_uuids) elif not group_names is None: # by name creates groups if necessary groups = [ContactGroup.get_or_create(self.user.get_org(), self.user, name) for name in group_names] contact.update_groups(groups) return contact
def save(self, obj): obj.org = self.request.user.get_org() self.object = ContactGroup.get_or_create(obj.org, self.request.user, obj.name)
def __init__(self, user, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["group"].queryset = ContactGroup.get_user_groups(user.get_org()).order_by("name")
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)
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)
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)
def validate_name(self, value): if not ContactGroup.is_valid_name(value): raise serializers.ValidationError( "Name contains illegal characters.") return value
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)
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)
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')
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