def create_contact(self, name=None, number=None, twitter=None, twitterid=None, urn=None, **kwargs): """ Create a contact in the master test org """ urns = [] if number: urns.append(URN.from_tel(number)) if twitter: urns.append(URN.from_twitter(twitter)) if twitterid: urns.append(URN.from_twitterid(twitterid)) if urn: urns.append(urn) if not name and not urns: # pragma: no cover raise ValueError("Need a name or URN to create a contact") kwargs["name"] = name kwargs["urns"] = urns if "org" not in kwargs: kwargs["org"] = self.org if "user" not in kwargs: kwargs["user"] = self.user return Contact.get_or_create_by_urns(**kwargs)
def create_contact(self, name=None, number=None, twitter=None, twitterid=None, urn=None, is_test=False, **kwargs): """ Create a contact in the master test org """ urns = [] if number: urns.append(URN.from_tel(number)) if twitter: urns.append(URN.from_twitter(twitter)) if twitterid: urns.append(URN.from_twitterid(twitterid)) if urn: urns.append(urn) if not name and not urns: # pragma: no cover raise ValueError("Need a name or URN to create a contact") kwargs['name'] = name kwargs['urns'] = urns kwargs['is_test'] = is_test if 'org' not in kwargs: kwargs['org'] = self.org if 'user' not in kwargs: kwargs['user'] = self.user return Contact.get_or_create_by_urns(**kwargs)
def create_contact(self, name=None, number=None, twitter=None, twitterid=None, urn=None, is_test=False, **kwargs): """ Create a contact in the master test org """ urns = [] if number: urns.append(URN.from_tel(number)) if twitter: urns.append(URN.from_twitter(twitter)) if twitterid: urns.append(URN.from_twitterid(twitterid)) if urn: urns.append(urn) if not name and not urns: # pragma: no cover raise ValueError("Need a name or URN to create a contact") kwargs["name"] = name kwargs["urns"] = urns kwargs["is_test"] = is_test if "org" not in kwargs: kwargs["org"] = self.org if "user" not in kwargs: kwargs["user"] = self.user return Contact.get_or_create_by_urns(**kwargs)
def test_resolve(self, mock_lookup_user): self.joe = self.create_contact("joe", twitter="therealjoe") urn = self.joe.get_urns()[0] # test no return value, should cause joe to be stopped mock_lookup_user.return_value = [] resolve_twitter_ids() self.joe.refresh_from_db() urn.refresh_from_db() self.assertTrue(self.joe.is_stopped) self.assertIsNone(urn.display) self.assertEqual("twitter:therealjoe", urn.identity) self.assertEqual("therealjoe", urn.path) self.joe.unstop(self.admin) # test a real return value mock_lookup_user.return_value = [dict(screen_name="TheRealJoe", id="123456")] resolve_twitter_ids() urn.refresh_from_db() self.assertIsNone(urn.contact) new_urn = self.joe.get_urns()[0] self.assertEqual("twitterid:123456", new_urn.identity) self.assertEqual("123456", new_urn.path) self.assertEqual("therealjoe", new_urn.display) self.assertEqual("twitterid:123456#therealjoe", new_urn.urn) old_fred = self.create_contact("old fred", urn=URN.from_twitter("fred")) new_fred = self.create_contact("new fred", urn=URN.from_twitterid("12345", screen_name="fred")) mock_lookup_user.return_value = [dict(screen_name="fred", id="12345")] resolve_twitter_ids() # new fred shouldn't have any URNs anymore as he really is old_fred self.assertEqual(0, len(new_fred.urns.all())) # old fred should be unchanged self.assertEqual("twitterid:12345", old_fred.urns.all()[0].identity) self.jane = self.create_contact("jane", twitter="jane10") mock_lookup_user.side_effect = Exception( "Twitter API returned a 404 (Not Found), No user matches for specified terms." ) resolve_twitter_ids() self.jane.refresh_from_db() self.assertTrue(self.jane.is_stopped) self.sarah = self.create_contact("sarah", twitter="sarah20") mock_lookup_user.side_effect = Exception("Unable to reach API") resolve_twitter_ids() self.sarah.refresh_from_db() self.assertFalse(self.sarah.is_stopped)
def resolve_twitter_ids(): r = get_redis_connection() # TODO: we can't use our non-overlapping task decorator as it creates a loop in the celery resolver when registering if r.get("resolve_twitter_ids_task"): # pragma: no cover return with r.lock("resolve_twitter_ids_task", 1800): # look up all 'twitter' URNs, limiting to 30k since that's the most our API would allow anyways twitter_urns = ContactURN.objects.filter( scheme=TWITTER_SCHEME, contact__is_stopped=False, contact__is_blocked=False ).exclude(contact=None) twitter_urns = twitter_urns[:30000].only("id", "org", "contact", "path") api_key = settings.TWITTER_API_KEY api_secret = settings.TWITTER_API_SECRET client = Twython(api_key, api_secret) updated = 0 print("found %d twitter urns to resolve" % len(twitter_urns)) # contacts we will stop stop_contacts = [] # we try to look these up 100 at a time for urn_batch in chunk_list(twitter_urns, 100): screen_names = [u.path for u in urn_batch] screen_map = {u.path: u for u in urn_batch} # try to fetch our users by screen name try: resp = client.lookup_user(screen_name=",".join(screen_names)) for twitter_user in resp: screen_name = twitter_user["screen_name"].lower() twitter_id = twitter_user["id"] if screen_name in screen_map and twitter_user["id"]: twitterid_urn = URN.normalize(URN.from_twitterid(twitter_id, screen_name)) old_urn = screen_map[screen_name] # create our new contact URN new_urn = ContactURN.get_or_create(old_urn.org, old_urn.contact, twitterid_urn) # if our new URN already existed for another contact and it is newer # than our old contact, reassign it to the old contact if ( new_urn.contact != old_urn.contact and new_urn.contact.created_on > old_urn.contact.created_on ): new_urn.contact = old_urn.contact new_urn.save(update_fields=["contact"]) # get rid of our old URN ContactURN.objects.filter(id=old_urn.id).update(contact=None) del screen_map[screen_name] updated += 1 except Exception as e: # if this wasn't an exception caused by not finding any of the users, then break if str(e).find("No user matches") < 0: # exit, we'll try again later print("exiting resolve_twitter_ids due to exception: %s" % e) break # add all remaining contacts to the contacts we will stop for contact in screen_map.values(): stop_contacts.append(contact) # stop all the contacts we couldn't resolve that have only a twitter URN stopped = 0 for contact_urn in stop_contacts: contact = contact_urn.contact if len(contact.urns.all()) == 1: contact.stop(contact.created_by) stopped += 1 if len(twitter_urns) > 0: print("updated %d twitter urns, %d stopped" % (updated, len(stop_contacts)))