{"action":"created","comment":{"url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments/60927238","id":60927238,"diff_hunk":"@@ -1359,68 +1353,59 @@ class ContactURN(models.Model):\n     org = models.ForeignKey(Org,\n                             help_text=\"The organization for this URN, can be null\")\n \n-    priority = models.IntegerField(default=STANDARD_PRIORITY,\n+    priority = models.IntegerField(default=PRIORITY_STANDARD,\n                                    help_text=\"The priority of this URN for the contact it is associated with\")\n \n     channel = models.ForeignKey(Channel, null=True, blank=True,\n                                 help_text=\"The preferred channel for this URN\")\n \n     @classmethod\n-    def create(cls, org, contact, scheme, path, channel=None, priority=None):\n-        urn = cls.format_urn(scheme, path)\n+    def create(cls, org, contact, urn_as_string, channel=None, priority=None):\n+        scheme, path = cls.parse(urn_as_string)\n \n         if not priority:\n-            priority = URN_SCHEME_PRIORITIES[scheme] if scheme in URN_SCHEME_PRIORITIES else STANDARD_PRIORITY\n+            priority = cls.PRIORITY_DEFAULTS.get(scheme, cls.PRIORITY_STANDARD)\n \n         return cls.objects.create(org=org, contact=contact, priority=priority, channel=channel,\n-                                  scheme=scheme, path=path, urn=urn)\n-\n-    @classmethod\n-    def get_existing_urn(cls, org, scheme, path):\n-        urn = cls.format_urn(scheme, path)\n-        return ContactURN.objects.filter(org=org, urn=urn).first()\n+                                  scheme=scheme, path=path, urn=urn_as_string)\n \n     @classmethod\n-    def get_or_create(cls, org, scheme, path, channel=None):\n-        existing = ContactURN.get_existing_urn(org, scheme, path)\n+    def lookup(cls, org, urn_as_string, country_code=None, normalize=True):\n+        \"\"\"\n+        Looks up an existing URN by a formatted URN string, e.g. \"tel:+250234562222\"\n+        \"\"\"\n+        if normalize:\n+            urn_as_string = cls.normalize(urn_as_string, country_code)\n \n-        with org.lock_on(OrgLock.contacts):\n-            if existing:\n-                return existing\n-            else:\n-                return cls.create(org, None, scheme, path, channel)\n+        return cls.objects.filter(org=org, urn=urn_as_string).select_related('contact').first()\n \n     @classmethod\n-    def parse_urn(cls, urn):\n-        # for the tel case, we parse ourselves due to a Python bug for those that don't start with +\n-        # see: http://bugs.python.org/issue14072\n-        parsed = urlparse(urn)\n-        if urn.startswith('tel:'):\n-            path = parsed.path\n-            if path.startswith('tel:'):\n-                path = parsed.path.split(':')[1]\n-\n-            parsed = ParseResult('tel', parsed.netloc, path, parsed.params, parsed.query, parsed.fragment)\n+    def parse(cls, urn_as_string):\n+        \"\"\"\n+        Splits a formatted URN string into scheme and path\n+        \"\"\"\n+        result = tuple(urn_as_string.split(':'))\n \n-        # URN isn't valid without a scheme and path\n-        if not parsed.scheme or not parsed.path:\n-            raise ValueError(\"URNs must define a scheme (%s) and path (%s), none found in: %s\" % (parsed.scheme, parsed.path, urn))\n+        if len(result) != 2 or not result[0] or not result[1]:\n+            raise ValueError(\"URN strings must contain a scheme and a path\")\n \n-        return parsed\n+        return result\n \n     @classmethod\n-    def format_urn(cls, scheme, namespace_specific_string):\n+    def format(cls, scheme, path):","path":"temba/contacts/models.py","position":427,"original_position":427,"commit_id":"f300902dc27907206033b4c4ed64db6228d030b2","original_commit_id":"f300902dc27907206033b4c4ed64db6228d030b2","user":{"login":"rowanseymour","id":675558,"avatar_url":"https://avatars.githubusercontent.com/u/675558?v=3","gravatar_id":"","url":"https://api.github.com/users/rowanseymour","html_url":"https://github.com/rowanseymour","followers_url":"https://api.github.com/users/rowanseymour/followers","following_url":"https://api.github.com/users/rowanseymour/following{/other_user}","gists_url":"https://api.github.com/users/rowanseymour/gists{/gist_id}","starred_url":"https://api.github.com/users/rowanseymour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rowanseymour/subscriptions","organizations_url":"https://api.github.com/users/rowanseymour/orgs","repos_url":"https://api.github.com/users/rowanseymour/repos","events_url":"https://api.github.com/users/rowanseymour/events{/privacy}","received_events_url":"https://api.github.com/users/rowanseymour/received_events","type":"User","site_admin":false},"body":"Yeah sorry posted that before your last comment appeared. I really like it apart from the variable name collision... but guess I could live with that. \r\n\r\nAlso still not in love with `to_parts` as a replacement for `parse`","created_at":"2016-04-25T14:57:16Z","updated_at":"2016-04-25T14:57:16Z","html_url":"https://github.com/nyaruka/rapidpro/pull/645#discussion_r60927238","pull_request_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645","_links":{"self":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments/60927238"},"html":{"href":"https://github.com/nyaruka/rapidpro/pull/645#discussion_r60927238"},"pull_request":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645"}}},"pull_request":{"url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645","id":67692485,"html_url":"https://github.com/nyaruka/rapidpro/pull/645","diff_url":"https://github.com/nyaruka/rapidpro/pull/645.diff","patch_url":"https://github.com/nyaruka/rapidpro/pull/645.patch","issue_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/645","number":645,"state":"open","locked":false,"title":"URN strings instead of URN tuples","user":{"login":"rowanseymour","id":675558,"avatar_url":"https://avatars.githubusercontent.com/u/675558?v=3","gravatar_id":"","url":"https://api.github.com/users/rowanseymour","html_url":"https://github.com/rowanseymour","followers_url":"https://api.github.com/users/rowanseymour/followers","following_url":"https://api.github.com/users/rowanseymour/following{/other_user}","gists_url":"https://api.github.com/users/rowanseymour/gists{/gist_id}","starred_url":"https://api.github.com/users/rowanseymour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rowanseymour/subscriptions","organizations_url":"https://api.github.com/users/rowanseymour/orgs","repos_url":"https://api.github.com/users/rowanseymour/repos","events_url":"https://api.github.com/users/rowanseymour/events{/privacy}","received_events_url":"https://api.github.com/users/rowanseymour/received_events","type":"User","site_admin":false},"body":"Also switches methods where scheme and path are passed as separate params","created_at":"2016-04-25T09:52:45Z","updated_at":"2016-04-25T14:57:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1b19c876f4e2a4b8a2b892747642238cd88a28e6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645/commits","review_comments_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645/comments","review_comment_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/645/comments","statuses_url":"https://api.github.com/repos/nyaruka/rapidpro/statuses/f300902dc27907206033b4c4ed64db6228d030b2","head":{"label":"nyaruka:no_more_urn_tuples","ref":"no_more_urn_tuples","sha":"f300902dc27907206033b4c4ed64db6228d030b2","user":{"login":"nyaruka","id":299256,"avatar_url":"https://avatars.githubusercontent.com/u/299256?v=3","gravatar_id":"","url":"https://api.github.com/users/nyaruka","html_url":"https://github.com/nyaruka","followers_url":"https://api.github.com/users/nyaruka/followers","following_url":"https://api.github.com/users/nyaruka/following{/other_user}","gists_url":"https://api.github.com/users/nyaruka/gists{/gist_id}","starred_url":"https://api.github.com/users/nyaruka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nyaruka/subscriptions","organizations_url":"https://api.github.com/users/nyaruka/orgs","repos_url":"https://api.github.com/users/nyaruka/repos","events_url":"https://api.github.com/users/nyaruka/events{/privacy}","received_events_url":"https://api.github.com/users/nyaruka/received_events","type":"Organization","site_admin":false},"repo":{"id":27415685,"name":"rapidpro","full_name":"nyaruka/rapidpro","owner":{"login":"nyaruka","id":299256,"avatar_url":"https://avatars.githubusercontent.com/u/299256?v=3","gravatar_id":"","url":"https://api.github.com/users/nyaruka","html_url":"https://github.com/nyaruka","followers_url":"https://api.github.com/users/nyaruka/followers","following_url":"https://api.github.com/users/nyaruka/following{/other_user}","gists_url":"https://api.github.com/users/nyaruka/gists{/gist_id}","starred_url":"https://api.github.com/users/nyaruka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nyaruka/subscriptions","organizations_url":"https://api.github.com/users/nyaruka/orgs","repos_url":"https://api.github.com/users/nyaruka/repos","events_url":"https://api.github.com/users/nyaruka/events{/privacy}","received_events_url":"https://api.github.com/users/nyaruka/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nyaruka/rapidpro","description":"RapidPro is a hosted service allowing organizations to visually build scalable interactive messaging applications.","fork":true,"url":"https://api.github.com/repos/nyaruka/rapidpro","forks_url":"https://api.github.com/repos/nyaruka/rapidpro/forks","keys_url":"https://api.github.com/repos/nyaruka/rapidpro/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nyaruka/rapidpro/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nyaruka/rapidpro/teams","hooks_url":"https://api.github.com/repos/nyaruka/rapidpro/hooks","issue_events_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/events{/number}","events_url":"https://api.github.com/repos/nyaruka/rapidpro/events","assignees_url":"https://api.github.com/repos/nyaruka/rapidpro/assignees{/user}","branches_url":"https://api.github.com/repos/nyaruka/rapidpro/branches{/branch}","tags_url":"https://api.github.com/repos/nyaruka/rapidpro/tags","blobs_url":"https://api.github.com/repos/nyaruka/rapidpro/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nyaruka/rapidpro/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nyaruka/rapidpro/git/refs{/sha}","trees_url":"https://api.github.com/repos/nyaruka/rapidpro/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nyaruka/rapidpro/statuses/{sha}","languages_url":"https://api.github.com/repos/nyaruka/rapidpro/languages","stargazers_url":"https://api.github.com/repos/nyaruka/rapidpro/stargazers","contributors_url":"https://api.github.com/repos/nyaruka/rapidpro/contributors","subscribers_url":"https://api.github.com/repos/nyaruka/rapidpro/subscribers","subscription_url":"https://api.github.com/repos/nyaruka/rapidpro/subscription","commits_url":"https://api.github.com/repos/nyaruka/rapidpro/commits{/sha}","git_commits_url":"https://api.github.com/repos/nyaruka/rapidpro/git/commits{/sha}","comments_url":"https://api.github.com/repos/nyaruka/rapidpro/comments{/number}","issue_comment_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/comments{/number}","contents_url":"https://api.github.com/repos/nyaruka/rapidpro/contents/{+path}","compare_url":"https://api.github.com/repos/nyaruka/rapidpro/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nyaruka/rapidpro/merges","archive_url":"https://api.github.com/repos/nyaruka/rapidpro/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nyaruka/rapidpro/downloads","issues_url":"https://api.github.com/repos/nyaruka/rapidpro/issues{/number}","pulls_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls{/number}","milestones_url":"https://api.github.com/repos/nyaruka/rapidpro/milestones{/number}","notifications_url":"https://api.github.com/repos/nyaruka/rapidpro/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nyaruka/rapidpro/labels{/name}","releases_url":"https://api.github.com/repos/nyaruka/rapidpro/releases{/id}","deployments_url":"https://api.github.com/repos/nyaruka/rapidpro/deployments","created_at":"2014-12-02T04:53:16Z","updated_at":"2016-04-05T09:24:14Z","pushed_at":"2016-04-25T10:22:07Z","git_url":"git://github.com/nyaruka/rapidpro.git","ssh_url":"git@github.com:nyaruka/rapidpro.git","clone_url":"https://github.com/nyaruka/rapidpro.git","svn_url":"https://github.com/nyaruka/rapidpro","homepage":null,"size":46010,"stargazers_count":7,"watchers_count":7,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":7,"default_branch":"master"}},"base":{"label":"nyaruka:master","ref":"master","sha":"561f9bbaa4634b2abf570827240a542e1774a804","user":{"login":"nyaruka","id":299256,"avatar_url":"https://avatars.githubusercontent.com/u/299256?v=3","gravatar_id":"","url":"https://api.github.com/users/nyaruka","html_url":"https://github.com/nyaruka","followers_url":"https://api.github.com/users/nyaruka/followers","following_url":"https://api.github.com/users/nyaruka/following{/other_user}","gists_url":"https://api.github.com/users/nyaruka/gists{/gist_id}","starred_url":"https://api.github.com/users/nyaruka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nyaruka/subscriptions","organizations_url":"https://api.github.com/users/nyaruka/orgs","repos_url":"https://api.github.com/users/nyaruka/repos","events_url":"https://api.github.com/users/nyaruka/events{/privacy}","received_events_url":"https://api.github.com/users/nyaruka/received_events","type":"Organization","site_admin":false},"repo":{"id":27415685,"name":"rapidpro","full_name":"nyaruka/rapidpro","owner":{"login":"nyaruka","id":299256,"avatar_url":"https://avatars.githubusercontent.com/u/299256?v=3","gravatar_id":"","url":"https://api.github.com/users/nyaruka","html_url":"https://github.com/nyaruka","followers_url":"https://api.github.com/users/nyaruka/followers","following_url":"https://api.github.com/users/nyaruka/following{/other_user}","gists_url":"https://api.github.com/users/nyaruka/gists{/gist_id}","starred_url":"https://api.github.com/users/nyaruka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nyaruka/subscriptions","organizations_url":"https://api.github.com/users/nyaruka/orgs","repos_url":"https://api.github.com/users/nyaruka/repos","events_url":"https://api.github.com/users/nyaruka/events{/privacy}","received_events_url":"https://api.github.com/users/nyaruka/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nyaruka/rapidpro","description":"RapidPro is a hosted service allowing organizations to visually build scalable interactive messaging applications.","fork":true,"url":"https://api.github.com/repos/nyaruka/rapidpro","forks_url":"https://api.github.com/repos/nyaruka/rapidpro/forks","keys_url":"https://api.github.com/repos/nyaruka/rapidpro/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nyaruka/rapidpro/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nyaruka/rapidpro/teams","hooks_url":"https://api.github.com/repos/nyaruka/rapidpro/hooks","issue_events_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/events{/number}","events_url":"https://api.github.com/repos/nyaruka/rapidpro/events","assignees_url":"https://api.github.com/repos/nyaruka/rapidpro/assignees{/user}","branches_url":"https://api.github.com/repos/nyaruka/rapidpro/branches{/branch}","tags_url":"https://api.github.com/repos/nyaruka/rapidpro/tags","blobs_url":"https://api.github.com/repos/nyaruka/rapidpro/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nyaruka/rapidpro/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nyaruka/rapidpro/git/refs{/sha}","trees_url":"https://api.github.com/repos/nyaruka/rapidpro/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nyaruka/rapidpro/statuses/{sha}","languages_url":"https://api.github.com/repos/nyaruka/rapidpro/languages","stargazers_url":"https://api.github.com/repos/nyaruka/rapidpro/stargazers","contributors_url":"https://api.github.com/repos/nyaruka/rapidpro/contributors","subscribers_url":"https://api.github.com/repos/nyaruka/rapidpro/subscribers","subscription_url":"https://api.github.com/repos/nyaruka/rapidpro/subscription","commits_url":"https://api.github.com/repos/nyaruka/rapidpro/commits{/sha}","git_commits_url":"https://api.github.com/repos/nyaruka/rapidpro/git/commits{/sha}","comments_url":"https://api.github.com/repos/nyaruka/rapidpro/comments{/number}","issue_comment_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/comments{/number}","contents_url":"https://api.github.com/repos/nyaruka/rapidpro/contents/{+path}","compare_url":"https://api.github.com/repos/nyaruka/rapidpro/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nyaruka/rapidpro/merges","archive_url":"https://api.github.com/repos/nyaruka/rapidpro/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nyaruka/rapidpro/downloads","issues_url":"https://api.github.com/repos/nyaruka/rapidpro/issues{/number}","pulls_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls{/number}","milestones_url":"https://api.github.com/repos/nyaruka/rapidpro/milestones{/number}","notifications_url":"https://api.github.com/repos/nyaruka/rapidpro/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nyaruka/rapidpro/labels{/name}","releases_url":"https://api.github.com/repos/nyaruka/rapidpro/releases{/id}","deployments_url":"https://api.github.com/repos/nyaruka/rapidpro/deployments","created_at":"2014-12-02T04:53:16Z","updated_at":"2016-04-05T09:24:14Z","pushed_at":"2016-04-25T10:22:07Z","git_url":"git://github.com/nyaruka/rapidpro.git","ssh_url":"git@github.com:nyaruka/rapidpro.git","clone_url":"https://github.com/nyaruka/rapidpro.git","svn_url":"https://github.com/nyaruka/rapidpro","homepage":null,"size":46010,"stargazers_count":7,"watchers_count":7,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645"},"html":{"href":"https://github.com/nyaruka/rapidpro/pull/645"},"issue":{"href":"https://api.github.com/repos/nyaruka/rapidpro/issues/645"},"comments":{"href":"https://api.github.com/repos/nyaruka/rapidpro/issues/645/comments"},"review_comments":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645/comments"},"review_comment":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/645/commits"},"statuses":{"href":"https://api.github.com/repos/nyaruka/rapidpro/statuses/f300902dc27907206033b4c4ed64db6228d030b2"}}}}