{"action":"created","comment":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/49282116","id":49282116,"diff_hunk":"@@ -0,0 +1,154 @@\n+from django.core.exceptions import ValidationError\n+from django.core.validators import validate_email\n+from django.db import transaction\n+\n+from corehq.apps.locations.models import SQLLocation\n+from corehq.apps.sms.mixin import apply_leniency, VerifiedNumber\n+from corehq.apps.users.models import WebUser, CommCareUser\n+from custom.ilsgateway.models import ILSMigrationStats, ILSMigrationProblem\n+from custom.logistics.mixin import UserMigrationMixin\n+from custom.logistics.utils import iterate_over_api_objects\n+\n+\n+class BalanceMigration(UserMigrationMixin):\n+\n+    def __init__(self, domain, endpoint):\n+        self.domain = domain\n+        self.endpoint = endpoint\n+\n+    def _get_total_counts(self, func, limit=1, **kwargs):\n+        meta, _ = func(limit=limit, **kwargs)\n+        return meta['total_count'] if meta else 0\n+\n+    @transaction.atomic\n+    def validate_sms_users(self, date=None):\n+        for sms_user in iterate_over_api_objects(\n+                self.endpoint.get_smsusers, filters=dict(date_updated__gte=date)\n+        ):\n+            description = \"\"\n+            user = CommCareUser.get_by_username(self.get_username(sms_user)[0])\n+            if not user:\n+                description = \"Not exists\"\n+                ILSMigrationProblem.objects.create(\n+                    domain=self.domain,\n+                    external_id=sms_user.id,\n+                    object_type='smsuser',\n+                    description=description\n+                )\n+                continue\n+\n+            phone_numbers = {\n+                apply_leniency(connection.phone_number) for connection in sms_user.phone_numbers\n+            }\n+\n+            if phone_numbers - set(user.phone_numbers):\n+                description += \"Invalid phone numbers, \"\n+\n+            phone_to_backend = {\n+                connection.phone_number: connection.backend\n+                for connection in sms_user.phone_numbers\n+            }\n+\n+            default_phone_number = [\n+                connection.phone_number for connection in sms_user.phone_numbers if connection.default\n+            ]\n+\n+            default_phone_number = default_phone_number[0] if default_phone_number else None\n+\n+            if default_phone_number and (apply_leniency(default_phone_number) != user.default_phone_number):\n+                description += \"Invalid default phone number, \"\n+\n+            for phone_number in user.phone_numbers:\n+                vn = VerifiedNumber.by_phone(phone_number)\n+                if not vn or vn.owner_id != user.get_id:\n+                    description += \"Phone number not verified, \"\n+                else:\n+                    backend = phone_to_backend.get(phone_number)\n+                    if backend != 'push_backend' and vn.backend_id != 'MOBILE_BACKEND_TEST' \\\n+                            or (backend == 'push_backend' and vn.backend_id):\n+                        description += \"Invalid backend, \"\n+\n+            if description:\n+                migration_problem, _ = ILSMigrationProblem.objects.get_or_create(\n+                    domain=self.domain,\n+                    object_id=user.get_id,\n+                    object_type='smsuser'\n+                )\n+                migration_problem.external_id = sms_user.id\n+                migration_problem.description = description.rstrip(' ,')\n+                migration_problem.save()\n+            else:\n+                ILSMigrationProblem.objects.filter(\n+                    domain=self.domain,\n+                    external_id=sms_user.id,\n+                    object_type='smsuser'\n+                ).delete()\n+\n+    @transaction.atomic\n+    def validate_web_users(self, date=None):\n+        unique_usernames = set()\n+        for web_user in iterate_over_api_objects(\n+            self.endpoint.get_webusers, filters=dict(date_updated__gte=date)\n+        ):\n+            if web_user.email:\n+                username = web_user.email.lower()\n+            else:\n+                username = web_user.username.lower()\n+                try:\n+                    validate_email(username)\n+                except ValidationError:\n+                    # We are not migrating users without valid email in v1\n+                    continue\n+\n+            unique_usernames.add(username)\n+            couch_web_user = WebUser.get_by_username(username)\n+            if not couch_web_user or self.domain not in couch_web_user.get_domains():\n+                description = \"Not exists\"\n+                ILSMigrationProblem.objects.get_or_create(\n+                    domain=self.domain,\n+                    object_type='webuser',\n+                    description=description,\n+                    external_id=web_user.email or web_user.username\n+                )\n+                continue\n+\n+            if not web_user.location:\n+                continue\n+\n+            try:\n+                sql_location = SQLLocation.objects.get(external_id=web_user.location, domain=self.domain)\n+                if couch_web_user.get_domain_membership(self.domain).location_id != sql_location.location_id:\n+                    ILSMigrationProblem.objects.get_or_create(\n+                        domain=self.domain,\n+                        object_type='webuser',","path":"custom/ilsgateway/balance.py","position":123,"original_position":123,"commit_id":"d28d7d5c49349c09b0d887c3f34d5ca246cc7322","original_commit_id":"d28d7d5c49349c09b0d887c3f34d5ca246cc7322","user":{"login":"gcapalbo","id":1028814,"avatar_url":"https://avatars.githubusercontent.com/u/1028814?v=3","gravatar_id":"","url":"https://api.github.com/users/gcapalbo","html_url":"https://github.com/gcapalbo","followers_url":"https://api.github.com/users/gcapalbo/followers","following_url":"https://api.github.com/users/gcapalbo/following{/other_user}","gists_url":"https://api.github.com/users/gcapalbo/gists{/gist_id}","starred_url":"https://api.github.com/users/gcapalbo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gcapalbo/subscriptions","organizations_url":"https://api.github.com/users/gcapalbo/orgs","repos_url":"https://api.github.com/users/gcapalbo/repos","events_url":"https://api.github.com/users/gcapalbo/events{/privacy}","received_events_url":"https://api.github.com/users/gcapalbo/received_events","type":"User","site_admin":false},"body":"should also set the object_id here","created_at":"2016-01-10T21:58:04Z","updated_at":"2016-01-10T21:58:04Z","html_url":"https://github.com/dimagi/commcare-hq/pull/9803#discussion_r49282116","pull_request_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803","_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/49282116"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/9803#discussion_r49282116"},"pull_request":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803"}}},"pull_request":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803","id":55336716,"html_url":"https://github.com/dimagi/commcare-hq/pull/9803","diff_url":"https://github.com/dimagi/commcare-hq/pull/9803.diff","patch_url":"https://github.com/dimagi/commcare-hq/pull/9803.patch","issue_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/9803","number":9803,"state":"open","locked":false,"title":"ILS: Added balance migration task","user":{"login":"kkrampa","id":5538370,"avatar_url":"https://avatars.githubusercontent.com/u/5538370?v=3","gravatar_id":"","url":"https://api.github.com/users/kkrampa","html_url":"https://github.com/kkrampa","followers_url":"https://api.github.com/users/kkrampa/followers","following_url":"https://api.github.com/users/kkrampa/following{/other_user}","gists_url":"https://api.github.com/users/kkrampa/gists{/gist_id}","starred_url":"https://api.github.com/users/kkrampa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kkrampa/subscriptions","organizations_url":"https://api.github.com/users/kkrampa/orgs","repos_url":"https://api.github.com/users/kkrampa/repos","events_url":"https://api.github.com/users/kkrampa/events{/privacy}","received_events_url":"https://api.github.com/users/kkrampa/received_events","type":"User","site_admin":false},"body":"@gcapalbo ","created_at":"2016-01-07T13:19:24Z","updated_at":"2016-01-10T21:58:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3c2735ceb1d58c52d314410749b30c68615d62cd","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803/commits","review_comments_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803/comments","review_comment_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/9803/comments","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/d28d7d5c49349c09b0d887c3f34d5ca246cc7322","head":{"label":"lwyszomi:ils-balance-migration","ref":"ils-balance-migration","sha":"d28d7d5c49349c09b0d887c3f34d5ca246cc7322","user":{"login":"lwyszomi","id":429541,"avatar_url":"https://avatars.githubusercontent.com/u/429541?v=3","gravatar_id":"","url":"https://api.github.com/users/lwyszomi","html_url":"https://github.com/lwyszomi","followers_url":"https://api.github.com/users/lwyszomi/followers","following_url":"https://api.github.com/users/lwyszomi/following{/other_user}","gists_url":"https://api.github.com/users/lwyszomi/gists{/gist_id}","starred_url":"https://api.github.com/users/lwyszomi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lwyszomi/subscriptions","organizations_url":"https://api.github.com/users/lwyszomi/orgs","repos_url":"https://api.github.com/users/lwyszomi/repos","events_url":"https://api.github.com/users/lwyszomi/events{/privacy}","received_events_url":"https://api.github.com/users/lwyszomi/received_events","type":"User","site_admin":false},"repo":{"id":12557346,"name":"commcare-hq","full_name":"lwyszomi/commcare-hq","owner":{"login":"lwyszomi","id":429541,"avatar_url":"https://avatars.githubusercontent.com/u/429541?v=3","gravatar_id":"","url":"https://api.github.com/users/lwyszomi","html_url":"https://github.com/lwyszomi","followers_url":"https://api.github.com/users/lwyszomi/followers","following_url":"https://api.github.com/users/lwyszomi/following{/other_user}","gists_url":"https://api.github.com/users/lwyszomi/gists{/gist_id}","starred_url":"https://api.github.com/users/lwyszomi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lwyszomi/subscriptions","organizations_url":"https://api.github.com/users/lwyszomi/orgs","repos_url":"https://api.github.com/users/lwyszomi/repos","events_url":"https://api.github.com/users/lwyszomi/events{/privacy}","received_events_url":"https://api.github.com/users/lwyszomi/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/lwyszomi/commcare-hq","description":"A server-side tool to help manage community health workers, written in Django","fork":true,"url":"https://api.github.com/repos/lwyszomi/commcare-hq","forks_url":"https://api.github.com/repos/lwyszomi/commcare-hq/forks","keys_url":"https://api.github.com/repos/lwyszomi/commcare-hq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lwyszomi/commcare-hq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lwyszomi/commcare-hq/teams","hooks_url":"https://api.github.com/repos/lwyszomi/commcare-hq/hooks","issue_events_url":"https://api.github.com/repos/lwyszomi/commcare-hq/issues/events{/number}","events_url":"https://api.github.com/repos/lwyszomi/commcare-hq/events","assignees_url":"https://api.github.com/repos/lwyszomi/commcare-hq/assignees{/user}","branches_url":"https://api.github.com/repos/lwyszomi/commcare-hq/branches{/branch}","tags_url":"https://api.github.com/repos/lwyszomi/commcare-hq/tags","blobs_url":"https://api.github.com/repos/lwyszomi/commcare-hq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lwyszomi/commcare-hq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lwyszomi/commcare-hq/git/refs{/sha}","trees_url":"https://api.github.com/repos/lwyszomi/commcare-hq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lwyszomi/commcare-hq/statuses/{sha}","languages_url":"https://api.github.com/repos/lwyszomi/commcare-hq/languages","stargazers_url":"https://api.github.com/repos/lwyszomi/commcare-hq/stargazers","contributors_url":"https://api.github.com/repos/lwyszomi/commcare-hq/contributors","subscribers_url":"https://api.github.com/repos/lwyszomi/commcare-hq/subscribers","subscription_url":"https://api.github.com/repos/lwyszomi/commcare-hq/subscription","commits_url":"https://api.github.com/repos/lwyszomi/commcare-hq/commits{/sha}","git_commits_url":"https://api.github.com/repos/lwyszomi/commcare-hq/git/commits{/sha}","comments_url":"https://api.github.com/repos/lwyszomi/commcare-hq/comments{/number}","issue_comment_url":"https://api.github.com/repos/lwyszomi/commcare-hq/issues/comments{/number}","contents_url":"https://api.github.com/repos/lwyszomi/commcare-hq/contents/{+path}","compare_url":"https://api.github.com/repos/lwyszomi/commcare-hq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lwyszomi/commcare-hq/merges","archive_url":"https://api.github.com/repos/lwyszomi/commcare-hq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lwyszomi/commcare-hq/downloads","issues_url":"https://api.github.com/repos/lwyszomi/commcare-hq/issues{/number}","pulls_url":"https://api.github.com/repos/lwyszomi/commcare-hq/pulls{/number}","milestones_url":"https://api.github.com/repos/lwyszomi/commcare-hq/milestones{/number}","notifications_url":"https://api.github.com/repos/lwyszomi/commcare-hq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lwyszomi/commcare-hq/labels{/name}","releases_url":"https://api.github.com/repos/lwyszomi/commcare-hq/releases{/id}","created_at":"2013-09-03T07:05:35Z","updated_at":"2015-03-13T12:55:20Z","pushed_at":"2016-01-08T14:17:17Z","git_url":"git://github.com/lwyszomi/commcare-hq.git","ssh_url":"git@github.com:lwyszomi/commcare-hq.git","clone_url":"https://github.com/lwyszomi/commcare-hq.git","svn_url":"https://github.com/lwyszomi/commcare-hq","homepage":"http://www.commcarehq.org","size":209088,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dimagi:master","ref":"master","sha":"6fc35383581417a9b551ee0f8619c1cdcb629b3f","user":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"repo":{"id":247278,"name":"commcare-hq","full_name":"dimagi/commcare-hq","owner":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dimagi/commcare-hq","description":"A server-side tool to help manage community health workers, written in Django","fork":false,"url":"https://api.github.com/repos/dimagi/commcare-hq","forks_url":"https://api.github.com/repos/dimagi/commcare-hq/forks","keys_url":"https://api.github.com/repos/dimagi/commcare-hq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dimagi/commcare-hq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dimagi/commcare-hq/teams","hooks_url":"https://api.github.com/repos/dimagi/commcare-hq/hooks","issue_events_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/events{/number}","events_url":"https://api.github.com/repos/dimagi/commcare-hq/events","assignees_url":"https://api.github.com/repos/dimagi/commcare-hq/assignees{/user}","branches_url":"https://api.github.com/repos/dimagi/commcare-hq/branches{/branch}","tags_url":"https://api.github.com/repos/dimagi/commcare-hq/tags","blobs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dimagi/commcare-hq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/refs{/sha}","trees_url":"https://api.github.com/repos/dimagi/commcare-hq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/{sha}","languages_url":"https://api.github.com/repos/dimagi/commcare-hq/languages","stargazers_url":"https://api.github.com/repos/dimagi/commcare-hq/stargazers","contributors_url":"https://api.github.com/repos/dimagi/commcare-hq/contributors","subscribers_url":"https://api.github.com/repos/dimagi/commcare-hq/subscribers","subscription_url":"https://api.github.com/repos/dimagi/commcare-hq/subscription","commits_url":"https://api.github.com/repos/dimagi/commcare-hq/commits{/sha}","git_commits_url":"https://api.github.com/repos/dimagi/commcare-hq/git/commits{/sha}","comments_url":"https://api.github.com/repos/dimagi/commcare-hq/comments{/number}","issue_comment_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/comments{/number}","contents_url":"https://api.github.com/repos/dimagi/commcare-hq/contents/{+path}","compare_url":"https://api.github.com/repos/dimagi/commcare-hq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dimagi/commcare-hq/merges","archive_url":"https://api.github.com/repos/dimagi/commcare-hq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dimagi/commcare-hq/downloads","issues_url":"https://api.github.com/repos/dimagi/commcare-hq/issues{/number}","pulls_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls{/number}","milestones_url":"https://api.github.com/repos/dimagi/commcare-hq/milestones{/number}","notifications_url":"https://api.github.com/repos/dimagi/commcare-hq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dimagi/commcare-hq/labels{/name}","releases_url":"https://api.github.com/repos/dimagi/commcare-hq/releases{/id}","created_at":"2009-07-09T17:00:07Z","updated_at":"2015-12-15T18:32:41Z","pushed_at":"2016-01-10T21:33:45Z","git_url":"git://github.com/dimagi/commcare-hq.git","ssh_url":"git@github.com:dimagi/commcare-hq.git","clone_url":"https://github.com/dimagi/commcare-hq.git","svn_url":"https://github.com/dimagi/commcare-hq","homepage":"http://www.commcarehq.org","size":243977,"stargazers_count":57,"watchers_count":57,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":64,"mirror_url":null,"open_issues_count":51,"forks":64,"open_issues":51,"watchers":57,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/9803"},"issue":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/9803"},"comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/9803/comments"},"review_comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803/comments"},"review_comment":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9803/commits"},"statuses":{"href":"https://api.github.com/repos/dimagi/commcare-hq/statuses/d28d7d5c49349c09b0d887c3f34d5ca246cc7322"}}}}