{"action":"created","comment":{"url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/29106775","id":29106775,"diff_hunk":"@@ -415,3 +416,377 @@ var getOrgUnitCalendar = module.exports.getOrgUnitCalendar = function(orgUnit, s\n         return callback(null, events);\n     });\n };\n+\n+/**\n+ * Export the organisational units, event series and events under an organisational unit\n+ *\n+ * @param  {OrgUnit}        orgUnit             The organisational unit to export\n+ * @param  {Number}         format              The format in which the organisational units, event series and events should be exported\n+ * @param  {Function}       callback            Standard callback function\n+ * @param  {Object}         callback.err        An error object, if any\n+ * @param  {Object|String}  callback.data       The exported organisational units, event series and events\n+ */\n+var exportOrgUnit = module.exports.exportOrgUnit = function(orgUnit, format, callback) {\n+    // Get all the organisational units under the given organisational unit\n+    _getChildOrgUnits([orgUnit.id], function(err, orgUnitIds) {\n+        if (err) {\n+            return callback(err);\n+        }\n+\n+        // Get the event series and events under the organisational units\n+        var options = {\n+            'where': {\n+                'id': orgUnitIds\n+            },\n+            'include': [\n+                // Series that are associated to the organisational unit\n+                {'model': DB.Serie, 'include': [\n+                    {'model': DB.Event, 'include': [\n+                        {'model': DB.User, 'as': 'Organisers'}\n+                    ]}\n+                ]}\n+\n+                // TODO: Events that are directly associated to the organisational unit\n+            ]\n+        };\n+        DB.OrgUnit.findAll(options).complete(function(err, orgUnits) {\n+            if (err) {\n+                log().error({\n+                    'err': err,\n+                    'id': orgUnit.id\n+                }, 'Unable to export an organisational unit');\n+                return callback({'code': 500, 'msg': 'Unable to export an organisational unit'});\n+            }\n+\n+            if (format === 'csv') {\n+                return _exportOrgUnitCSV(orgUnit.id, orgUnits, callback);\n+            } else {\n+                return _exportOrgUnitJSON(orgUnit.id, orgUnits, callback);\n+            }\n+        });\n+    });\n+};\n+\n+/**\n+ * Find all the organisational units under a set of organisational units. This function will\n+ * perform a breadth-first search through the organisational unit structure generating in an SQL\n+ * query per level. This means that if the maximum depth of one of the trees is 4, 4 SQL queries will be executed\n+ *\n+ * @param  {Number[]}   orgUnitIds                  The ids of the organisational units to expand\n+ * @param  {Function}   callback                    Standard callback function\n+ * @param  {Object}     callback.err                An error object, if any\n+ * @param  {Number[]}   callback.allOrgUnitIds      The ids of the organisational units that are part of the trees (including the original root ids)\n+ * @api private\n+ */\n+var _getChildOrgUnits = function(orgUnitIds, callback, _allOrgUnitIds) {\n+    _allOrgUnitIds = _allOrgUnitIds || orgUnitIds;\n+\n+    // Get the children of the specified organisational units\n+    _getDirectChildOrgUnits(orgUnitIds, function(err, childOrgUnitIds) {\n+        if (err) {\n+            return callback(err);\n+        }\n+\n+        // If no further children could be produced, we return\n+        if (_.isEmpty(childOrgUnitIds)) {\n+            return callback(null, _allOrgUnitIds);\n+        }\n+\n+        // Add the children to the set of known organisational unit ids and get the children's children\n+        _allOrgUnitIds = _.union(_allOrgUnitIds, childOrgUnitIds);","path":"node_modules/gh-orgunit/lib/internal/dao.js","position":89,"original_position":89,"commit_id":"86999d5bad507fef60f797244d3564a31a0fed90","original_commit_id":"86999d5bad507fef60f797244d3564a31a0fed90","user":{"login":"nicolaasmatthijs","id":109850,"avatar_url":"https://avatars.githubusercontent.com/u/109850?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolaasmatthijs","html_url":"https://github.com/nicolaasmatthijs","followers_url":"https://api.github.com/users/nicolaasmatthijs/followers","following_url":"https://api.github.com/users/nicolaasmatthijs/following{/other_user}","gists_url":"https://api.github.com/users/nicolaasmatthijs/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolaasmatthijs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolaasmatthijs/subscriptions","organizations_url":"https://api.github.com/users/nicolaasmatthijs/orgs","repos_url":"https://api.github.com/users/nicolaasmatthijs/repos","events_url":"https://api.github.com/users/nicolaasmatthijs/events{/privacy}","received_events_url":"https://api.github.com/users/nicolaasmatthijs/received_events","type":"User","site_admin":false},"body":"Is there a scenario in which an orgunit can be in multiple locations in the hierarchy? If so, we'd need to remove duplicates from `_allOrgUnitIds` and would need to remove orgunits we've already seen from `childOrgUnitIds`","created_at":"2015-04-26T05:01:40Z","updated_at":"2015-04-26T05:01:40Z","html_url":"https://github.com/fronteerio/grasshopper/pull/96#discussion_r29106775","pull_request_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96","_links":{"self":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/29106775"},"html":{"href":"https://github.com/fronteerio/grasshopper/pull/96#discussion_r29106775"},"pull_request":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96"}}},"pull_request":{"url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96","id":33467278,"html_url":"https://github.com/fronteerio/grasshopper/pull/96","diff_url":"https://github.com/fronteerio/grasshopper/pull/96.diff","patch_url":"https://github.com/fronteerio/grasshopper/pull/96.patch","issue_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/96","number":96,"state":"open","locked":false,"title":"Export data under an organisational unit","user":{"login":"simong","id":92180,"avatar_url":"https://avatars.githubusercontent.com/u/92180?v=3","gravatar_id":"","url":"https://api.github.com/users/simong","html_url":"https://github.com/simong","followers_url":"https://api.github.com/users/simong/followers","following_url":"https://api.github.com/users/simong/following{/other_user}","gists_url":"https://api.github.com/users/simong/gists{/gist_id}","starred_url":"https://api.github.com/users/simong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simong/subscriptions","organizations_url":"https://api.github.com/users/simong/orgs","repos_url":"https://api.github.com/users/simong/repos","events_url":"https://api.github.com/users/simong/events{/privacy}","received_events_url":"https://api.github.com/users/simong/received_events","type":"User","site_admin":false},"body":"This is the first stab at exporting an organisational unit. It also defines the structure of how an orgunit could be imported and adds the `externalId` properties on the necessary models","created_at":"2015-04-16T16:14:11Z","updated_at":"2015-04-26T05:01:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8bdb0500bea5fb240ef526bb70cab42f8d2c8d50","assignee":{"login":"nicolaasmatthijs","id":109850,"avatar_url":"https://avatars.githubusercontent.com/u/109850?v=3","gravatar_id":"","url":"https://api.github.com/users/nicolaasmatthijs","html_url":"https://github.com/nicolaasmatthijs","followers_url":"https://api.github.com/users/nicolaasmatthijs/followers","following_url":"https://api.github.com/users/nicolaasmatthijs/following{/other_user}","gists_url":"https://api.github.com/users/nicolaasmatthijs/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolaasmatthijs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolaasmatthijs/subscriptions","organizations_url":"https://api.github.com/users/nicolaasmatthijs/orgs","repos_url":"https://api.github.com/users/nicolaasmatthijs/repos","events_url":"https://api.github.com/users/nicolaasmatthijs/events{/privacy}","received_events_url":"https://api.github.com/users/nicolaasmatthijs/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96/commits","review_comments_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96/comments","review_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments{/number}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/96/comments","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/86999d5bad507fef60f797244d3564a31a0fed90","head":{"label":"fronteerio:export","ref":"export","sha":"86999d5bad507fef60f797244d3564a31a0fed90","user":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=3","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"repo":{"id":24999124,"name":"grasshopper","full_name":"fronteerio/grasshopper","owner":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=3","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/fronteerio/grasshopper","description":"Grasshopper Event Engine","fork":false,"url":"https://api.github.com/repos/fronteerio/grasshopper","forks_url":"https://api.github.com/repos/fronteerio/grasshopper/forks","keys_url":"https://api.github.com/repos/fronteerio/grasshopper/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fronteerio/grasshopper/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fronteerio/grasshopper/teams","hooks_url":"https://api.github.com/repos/fronteerio/grasshopper/hooks","issue_events_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/events{/number}","events_url":"https://api.github.com/repos/fronteerio/grasshopper/events","assignees_url":"https://api.github.com/repos/fronteerio/grasshopper/assignees{/user}","branches_url":"https://api.github.com/repos/fronteerio/grasshopper/branches{/branch}","tags_url":"https://api.github.com/repos/fronteerio/grasshopper/tags","blobs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fronteerio/grasshopper/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/refs{/sha}","trees_url":"https://api.github.com/repos/fronteerio/grasshopper/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/{sha}","languages_url":"https://api.github.com/repos/fronteerio/grasshopper/languages","stargazers_url":"https://api.github.com/repos/fronteerio/grasshopper/stargazers","contributors_url":"https://api.github.com/repos/fronteerio/grasshopper/contributors","subscribers_url":"https://api.github.com/repos/fronteerio/grasshopper/subscribers","subscription_url":"https://api.github.com/repos/fronteerio/grasshopper/subscription","commits_url":"https://api.github.com/repos/fronteerio/grasshopper/commits{/sha}","git_commits_url":"https://api.github.com/repos/fronteerio/grasshopper/git/commits{/sha}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/comments{/number}","issue_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/comments{/number}","contents_url":"https://api.github.com/repos/fronteerio/grasshopper/contents/{+path}","compare_url":"https://api.github.com/repos/fronteerio/grasshopper/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fronteerio/grasshopper/merges","archive_url":"https://api.github.com/repos/fronteerio/grasshopper/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fronteerio/grasshopper/downloads","issues_url":"https://api.github.com/repos/fronteerio/grasshopper/issues{/number}","pulls_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls{/number}","milestones_url":"https://api.github.com/repos/fronteerio/grasshopper/milestones{/number}","notifications_url":"https://api.github.com/repos/fronteerio/grasshopper/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fronteerio/grasshopper/labels{/name}","releases_url":"https://api.github.com/repos/fronteerio/grasshopper/releases{/id}","created_at":"2014-10-09T16:51:39Z","updated_at":"2015-04-20T18:50:27Z","pushed_at":"2015-04-24T22:41:50Z","git_url":"git://github.com/fronteerio/grasshopper.git","ssh_url":"git@github.com:fronteerio/grasshopper.git","clone_url":"https://github.com/fronteerio/grasshopper.git","svn_url":"https://github.com/fronteerio/grasshopper","homepage":null,"size":4513,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":16,"forks":4,"open_issues":16,"watchers":0,"default_branch":"master"}},"base":{"label":"fronteerio:master","ref":"master","sha":"b78aace80c150199ee56043cceb4670295960700","user":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=3","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"repo":{"id":24999124,"name":"grasshopper","full_name":"fronteerio/grasshopper","owner":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=3","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/fronteerio/grasshopper","description":"Grasshopper Event Engine","fork":false,"url":"https://api.github.com/repos/fronteerio/grasshopper","forks_url":"https://api.github.com/repos/fronteerio/grasshopper/forks","keys_url":"https://api.github.com/repos/fronteerio/grasshopper/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fronteerio/grasshopper/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fronteerio/grasshopper/teams","hooks_url":"https://api.github.com/repos/fronteerio/grasshopper/hooks","issue_events_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/events{/number}","events_url":"https://api.github.com/repos/fronteerio/grasshopper/events","assignees_url":"https://api.github.com/repos/fronteerio/grasshopper/assignees{/user}","branches_url":"https://api.github.com/repos/fronteerio/grasshopper/branches{/branch}","tags_url":"https://api.github.com/repos/fronteerio/grasshopper/tags","blobs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fronteerio/grasshopper/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/refs{/sha}","trees_url":"https://api.github.com/repos/fronteerio/grasshopper/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/{sha}","languages_url":"https://api.github.com/repos/fronteerio/grasshopper/languages","stargazers_url":"https://api.github.com/repos/fronteerio/grasshopper/stargazers","contributors_url":"https://api.github.com/repos/fronteerio/grasshopper/contributors","subscribers_url":"https://api.github.com/repos/fronteerio/grasshopper/subscribers","subscription_url":"https://api.github.com/repos/fronteerio/grasshopper/subscription","commits_url":"https://api.github.com/repos/fronteerio/grasshopper/commits{/sha}","git_commits_url":"https://api.github.com/repos/fronteerio/grasshopper/git/commits{/sha}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/comments{/number}","issue_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/comments{/number}","contents_url":"https://api.github.com/repos/fronteerio/grasshopper/contents/{+path}","compare_url":"https://api.github.com/repos/fronteerio/grasshopper/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fronteerio/grasshopper/merges","archive_url":"https://api.github.com/repos/fronteerio/grasshopper/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fronteerio/grasshopper/downloads","issues_url":"https://api.github.com/repos/fronteerio/grasshopper/issues{/number}","pulls_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls{/number}","milestones_url":"https://api.github.com/repos/fronteerio/grasshopper/milestones{/number}","notifications_url":"https://api.github.com/repos/fronteerio/grasshopper/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fronteerio/grasshopper/labels{/name}","releases_url":"https://api.github.com/repos/fronteerio/grasshopper/releases{/id}","created_at":"2014-10-09T16:51:39Z","updated_at":"2015-04-20T18:50:27Z","pushed_at":"2015-04-24T22:41:50Z","git_url":"git://github.com/fronteerio/grasshopper.git","ssh_url":"git@github.com:fronteerio/grasshopper.git","clone_url":"https://github.com/fronteerio/grasshopper.git","svn_url":"https://github.com/fronteerio/grasshopper","homepage":null,"size":4513,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":16,"forks":4,"open_issues":16,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96"},"html":{"href":"https://github.com/fronteerio/grasshopper/pull/96"},"issue":{"href":"https://api.github.com/repos/fronteerio/grasshopper/issues/96"},"comments":{"href":"https://api.github.com/repos/fronteerio/grasshopper/issues/96/comments"},"review_comments":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96/comments"},"review_comment":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/96/commits"},"statuses":{"href":"https://api.github.com/repos/fronteerio/grasshopper/statuses/86999d5bad507fef60f797244d3564a31a0fed90"}}}}