{"action":"created","comment":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/73734543","id":73734543,"diff_hunk":"@@ -0,0 +1,652 @@\n+\"\"\"\n+Bulk rearrange locations.\n+\n+This includes support for changing location types, changing locations' parents,\n+deleting things, and so on.  See the spec doc for specifics:\n+https://docs.google.com/document/d/1gZFPP8yXjPazaJDP9EmFORi88R-jSytH6TTgMxTGQSk/\n+\"\"\"\n+import copy\n+import itertools\n+from collections import Counter, defaultdict\n+\n+from django.db import transaction\n+\n+from dimagi.utils.decorators.memoized import memoized\n+\n+from corehq.apps.domain.models import Domain\n+from corehq.apps.locations.models import Location, SQLLocation, LocationType\n+from .tree_utils import BadParentError, CycleError, assert_no_cycles, expansion_validators\n+from .const import LOCATION_SHEET_HEADERS, LOCATION_TYPE_SHEET_HEADERS, ROOT_LOCATION_TYPE\n+\n+\n+class LocationExcelSheetError(Exception):\n+    pass\n+\n+\n+class LocationUploadResult(object):\n+    def __init__(self):\n+        self.messages = []\n+        self.errors = []\n+        self.warnings = []\n+\n+    @property\n+    def success(self):\n+        return not self.errors\n+\n+\n+class LocationTypeStub(object):\n+    \"\"\"\n+    A representation of location type excel row\n+    \"\"\"\n+    titles = LOCATION_TYPE_SHEET_HEADERS\n+\n+    def __init__(self, name, code, parent_code, do_delete, shares_cases,\n+                 view_descendants, expand_from, sync_to, index):\n+        self.name = name\n+        self.code = code\n+        # if parent_code is '', it must be top location type\n+        self.parent_code = parent_code or ROOT_LOCATION_TYPE\n+        self.do_delete = do_delete\n+        self.shares_cases = shares_cases\n+        self.view_descendants = view_descendants\n+        self.expand_from = expand_from\n+        self.sync_to = sync_to\n+        self.index = index\n+\n+    @classmethod\n+    def from_excel_row(cls, row, index):\n+        name = row.get(cls.titles['name'])\n+        code = row.get(cls.titles['code'])\n+        parent_code = row.get(cls.titles['parent_code'])\n+        do_delete = row.get(cls.titles['do_delete'], 'N').lower() in ['y', 'yes']\n+        shares_cases = row.get(cls.titles['shares_cases'], 'N').lower() in ['y', 'yes']\n+        view_descendants = row.get(cls.titles['view_descendants'], 'N').lower() in ['y', 'yes']\n+        expand_from = row.get(cls.titles['expand_from'])\n+        sync_to = row.get(cls.titles['sync_to'])\n+        index = index\n+        return cls(name, code, parent_code, do_delete, shares_cases,\n+                   view_descendants, expand_from, sync_to, index)\n+\n+    def is_new(self, old_collection):\n+        return self.code not in old_collection.types_by_code\n+\n+    def _needs_save(self, old_collection):\n+        # returns True if this should be saved\n+        if self.is_new(old_collection) or self.do_delete:\n+            return True\n+\n+        old_version = old_collection.types_by_code[self.code]\n+        if (old_version.parent_type and old_version.parent_type.code != self.parent_code):\n+            return True\n+\n+        for attr in ['name', 'code', 'shares_cases'\n+                     'view_descendants', 'expand_from', 'sync_to']:\n+            if getattr(old_version, attr, None) != getattr(self, attr, None):\n+                return True\n+        return False\n+\n+    def _as_db_object(self, old_collection, parent_type, domain):\n+        # returns a SQLLocation version of the stub","path":"corehq/apps/locations/bulk_management.py","position":89,"original_position":89,"commit_id":"ea8514ecba91d8875853478a2fcdd6ab59667eb6","original_commit_id":"ea8514ecba91d8875853478a2fcdd6ab59667eb6","user":{"login":"esoergel","id":2367539,"avatar_url":"https://avatars.githubusercontent.com/u/2367539?v=3","gravatar_id":"","url":"https://api.github.com/users/esoergel","html_url":"https://github.com/esoergel","followers_url":"https://api.github.com/users/esoergel/followers","following_url":"https://api.github.com/users/esoergel/following{/other_user}","gists_url":"https://api.github.com/users/esoergel/gists{/gist_id}","starred_url":"https://api.github.com/users/esoergel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esoergel/subscriptions","organizations_url":"https://api.github.com/users/esoergel/orgs","repos_url":"https://api.github.com/users/esoergel/repos","events_url":"https://api.github.com/users/esoergel/events{/privacy}","received_events_url":"https://api.github.com/users/esoergel/received_events","type":"User","site_admin":false},"body":"[tiny] wrong model (guessing copy/paste error)","created_at":"2016-08-05T18:01:46Z","updated_at":"2016-08-05T18:01:46Z","html_url":"https://github.com/dimagi/commcare-hq/pull/12199#discussion_r73734543","pull_request_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199","_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/73734543"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/12199#discussion_r73734543"},"pull_request":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199"}}},"pull_request":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199","id":74826529,"html_url":"https://github.com/dimagi/commcare-hq/pull/12199","diff_url":"https://github.com/dimagi/commcare-hq/pull/12199.diff","patch_url":"https://github.com/dimagi/commcare-hq/pull/12199.patch","issue_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/12199","number":12199,"state":"open","locked":false,"title":"Add bulk organization management test skeleton","user":{"login":"esoergel","id":2367539,"avatar_url":"https://avatars.githubusercontent.com/u/2367539?v=3","gravatar_id":"","url":"https://api.github.com/users/esoergel","html_url":"https://github.com/esoergel","followers_url":"https://api.github.com/users/esoergel/followers","following_url":"https://api.github.com/users/esoergel/following{/other_user}","gists_url":"https://api.github.com/users/esoergel/gists{/gist_id}","starred_url":"https://api.github.com/users/esoergel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esoergel/subscriptions","organizations_url":"https://api.github.com/users/esoergel/orgs","repos_url":"https://api.github.com/users/esoergel/repos","events_url":"https://api.github.com/users/esoergel/events{/privacy}","received_events_url":"https://api.github.com/users/esoergel/received_events","type":"User","site_admin":false},"body":"@sravfeyn @sheelio\nThis is a mock-up of the tests we spoke about, Sravan.  Feel free to use and modify or discard as you see fit.","created_at":"2016-06-22T17:42:48Z","updated_at":"2016-08-05T18:01:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1aa5f3205f4abbe763b30e90a7e255b5e3736b14","assignee":{"login":"sravfeyn","id":829142,"avatar_url":"https://avatars.githubusercontent.com/u/829142?v=3","gravatar_id":"","url":"https://api.github.com/users/sravfeyn","html_url":"https://github.com/sravfeyn","followers_url":"https://api.github.com/users/sravfeyn/followers","following_url":"https://api.github.com/users/sravfeyn/following{/other_user}","gists_url":"https://api.github.com/users/sravfeyn/gists{/gist_id}","starred_url":"https://api.github.com/users/sravfeyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sravfeyn/subscriptions","organizations_url":"https://api.github.com/users/sravfeyn/orgs","repos_url":"https://api.github.com/users/sravfeyn/repos","events_url":"https://api.github.com/users/sravfeyn/events{/privacy}","received_events_url":"https://api.github.com/users/sravfeyn/received_events","type":"User","site_admin":false},"assignees":[{"login":"sravfeyn","id":829142,"avatar_url":"https://avatars.githubusercontent.com/u/829142?v=3","gravatar_id":"","url":"https://api.github.com/users/sravfeyn","html_url":"https://github.com/sravfeyn","followers_url":"https://api.github.com/users/sravfeyn/followers","following_url":"https://api.github.com/users/sravfeyn/following{/other_user}","gists_url":"https://api.github.com/users/sravfeyn/gists{/gist_id}","starred_url":"https://api.github.com/users/sravfeyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sravfeyn/subscriptions","organizations_url":"https://api.github.com/users/sravfeyn/orgs","repos_url":"https://api.github.com/users/sravfeyn/repos","events_url":"https://api.github.com/users/sravfeyn/events{/privacy}","received_events_url":"https://api.github.com/users/sravfeyn/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199/commits","review_comments_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199/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/12199/comments","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/ea8514ecba91d8875853478a2fcdd6ab59667eb6","head":{"label":"dimagi:loc-tests","ref":"loc-tests","sha":"ea8514ecba91d8875853478a2fcdd6ab59667eb6","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}","deployments_url":"https://api.github.com/repos/dimagi/commcare-hq/deployments","created_at":"2009-07-09T17:00:07Z","updated_at":"2016-05-16T15:23:03Z","pushed_at":"2016-08-05T17:50:27Z","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":316140,"stargazers_count":72,"watchers_count":72,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":70,"mirror_url":null,"open_issues_count":45,"forks":70,"open_issues":45,"watchers":72,"default_branch":"master"}},"base":{"label":"dimagi:master","ref":"master","sha":"fb57400f242b2227d46751e73397fff294b4674b","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}","deployments_url":"https://api.github.com/repos/dimagi/commcare-hq/deployments","created_at":"2009-07-09T17:00:07Z","updated_at":"2016-05-16T15:23:03Z","pushed_at":"2016-08-05T17:50:27Z","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":316140,"stargazers_count":72,"watchers_count":72,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":70,"mirror_url":null,"open_issues_count":45,"forks":70,"open_issues":45,"watchers":72,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/12199"},"issue":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/12199"},"comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/12199/comments"},"review_comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/12199/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/12199/commits"},"statuses":{"href":"https://api.github.com/repos/dimagi/commcare-hq/statuses/ea8514ecba91d8875853478a2fcdd6ab59667eb6"}}}}
{"actor":{"display_login":"esoergel"}}