{"action":"created","comment":{"url":"https://api.github.com/repos/learningequality/kolibri/pulls/comments/50489782","id":50489782,"diff_hunk":"@@ -0,0 +1,26 @@\n+Planned features\n+================\n+\n+Skeleton TODO\n+-------------\n+\n+ - Plugin class definitions\n+ - Hooks?\n+ - Logging in general\n+ - Logging in tests\n+ - sketch out kolibri command\n+ - implement settings pattern\n+ - python setup.py sdist bdist_wininst upload\n+ - versioning\n+ - changelog\n+ - Circle repo\n+\n+Decision TODO\n+-------------\n+\n+#. Django 1.8 or 1.9 (LTS or latest?)\n+#. CircleCI, right?\n+#. Should JS / BDD tests be separated out? How to run optionally?","path":"TODO.rst","position":23,"original_position":23,"commit_id":"fa8e634546c64ca9488c979ed013bd1e87f8ff48","original_commit_id":"fa8e634546c64ca9488c979ed013bd1e87f8ff48","user":{"login":"rtibbles","id":1680573,"avatar_url":"https://avatars.githubusercontent.com/u/1680573?v=3","gravatar_id":"","url":"https://api.github.com/users/rtibbles","html_url":"https://github.com/rtibbles","followers_url":"https://api.github.com/users/rtibbles/followers","following_url":"https://api.github.com/users/rtibbles/following{/other_user}","gists_url":"https://api.github.com/users/rtibbles/gists{/gist_id}","starred_url":"https://api.github.com/users/rtibbles/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtibbles/subscriptions","organizations_url":"https://api.github.com/users/rtibbles/orgs","repos_url":"https://api.github.com/users/rtibbles/repos","events_url":"https://api.github.com/users/rtibbles/events{/privacy}","received_events_url":"https://api.github.com/users/rtibbles/received_events","type":"User","site_admin":false},"body":"BDD - i.e. integration tests - should be run optionally.\r\n\r\nJavascript tests should not, as they will be unit tests also.","created_at":"2016-01-22T00:58:02Z","updated_at":"2016-01-22T00:58:02Z","html_url":"https://github.com/learningequality/kolibri/pull/4#discussion_r50489782","pull_request_url":"https://api.github.com/repos/learningequality/kolibri/pulls/4","_links":{"self":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/comments/50489782"},"html":{"href":"https://github.com/learningequality/kolibri/pull/4#discussion_r50489782"},"pull_request":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/4"}}},"pull_request":{"url":"https://api.github.com/repos/learningequality/kolibri/pulls/4","id":56698141,"html_url":"https://github.com/learningequality/kolibri/pull/4","diff_url":"https://github.com/learningequality/kolibri/pull/4.diff","patch_url":"https://github.com/learningequality/kolibri/pull/4.patch","issue_url":"https://api.github.com/repos/learningequality/kolibri/issues/4","number":4,"state":"open","locked":false,"title":"moving skeleton from kolibri_skeleton repo","user":{"login":"benjaoming","id":374612,"avatar_url":"https://avatars.githubusercontent.com/u/374612?v=3","gravatar_id":"","url":"https://api.github.com/users/benjaoming","html_url":"https://github.com/benjaoming","followers_url":"https://api.github.com/users/benjaoming/followers","following_url":"https://api.github.com/users/benjaoming/following{/other_user}","gists_url":"https://api.github.com/users/benjaoming/gists{/gist_id}","starred_url":"https://api.github.com/users/benjaoming/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benjaoming/subscriptions","organizations_url":"https://api.github.com/users/benjaoming/orgs","repos_url":"https://api.github.com/users/benjaoming/repos","events_url":"https://api.github.com/users/benjaoming/events{/privacy}","received_events_url":"https://api.github.com/users/benjaoming/received_events","type":"User","site_admin":false},"body":"NEWS\r\n====\r\n\r\nAnything you read here is a proposal, even though the sentence doesn't start with *\"I propose that ...\"* :)\r\n\r\nThe skeleton doesn't concern node.js or the likes. I simply don't have the skills, and I hope someone will jump in and make the most awesome skeleton for JS packages and Restful APIs shortly after the Python stuff is laid out.\r\n\r\nTODO\r\n=====\r\n\r\nA more universal TODO can be maintained [here](http://kolibri.readthedocs.org/en/latest/todo.html), as for now, this is the PR TODO which we may defer.\r\n\r\nWhether to solve these now or after merge, is undecided ( @MCGallaspy ? )\r\n\r\n* [ ] Cleanup current codebase: Write docstrings for everything and have 100% test coverage.\r\n* [ ] Switch from JSON config to ini config with @aronasorman 's link to configparser backport.\r\n* [ ] Identify module/package structure. For instance, find the right place for our UAP and content core.\r\n* [ ] Implement a couple more hooks for the plugin API\r\n* [ ] Add skeleton BDD testing with `tox -e bdd`\r\n* [ ] Write start of **Developer Guide** chapter for the docs\r\n* [ ] Do the `kolibri-static` dist packaging mechanism\r\n* [ ] Add example of how package data is handled\r\n* [ ] Discuss and implement logging\r\n* [ ] Add versioning based on KA Lite semantic model and auto-appending date + git commit hashes for dev releases (like Django)\r\n\r\nWe have coverage with codecov.io\r\n--------------------------------------------\r\n\r\nThis will show detailed code coverage reports in all PRs. Before we start, we need to have it at 100%! :)\r\n\r\nSphinx integrates with our codebase\r\n----------------------------------------------\r\n\r\nWhat happens is basically that we can start writing documentation directly in our source code. Certain chapters will need more careful explanation and can be written as separate files. Others can be separate but still import source code comments. Case in question is how we use the `docstring` from `kolibri.utils.cli`:\r\n\r\nhttp://kolibri.readthedocs.org/en/latest/usage.html\r\n\r\n![cli](https://cloud.githubusercontent.com/assets/374612/12469668/1009a7ae-bfee-11e5-9a1c-e5065f17e490.png)\r\n\r\n\r\nWe no longer have a script that launches a different process that forks a daemon\r\n----------------------------------------------------------------\r\n\r\nWe spent a lot of time with these issues. The problem was that `kalitectl.py` was called from a script `bin/kalite` and then went on to invoke Django. Instead, now we have a convention `kolibri.__main__` module which will already have the correct path when called as `python -m kolibri [command line arguments]`. For installations, using `setup.py`'s `entry_point` option, we can make the `kolibri` alias without placing any executables anywhere. As for Windows, things got slightly easier because even though the PATH for point at executables vary between different Windows environments, we can create a kolibri.bat that only needs to do `python.exe -m kolibri [args]`.\r\n\r\nThe default django `runserver` command is also back, including the nifty auto-reloading feature:\r\n\r\n![screenshot from 2016-01-20 19 16 50](https://cloud.githubusercontent.com/assets/374612/12469678/2a0388aa-bfee-11e5-80ca-2bd18ac8b218.png)\r\n\r\n\r\nThe docopt clutter is gone\r\n--------------------------\r\n\r\nVanilla docopt is invoked, no more of the problems with CLI argument order. Explicitness FTW.\r\n\r\n    kolibri manage runserver -- --django-arg=123  # <- notice the -- separation\r\n\r\nPEP8 checks will fail your PR\r\n------------\r\n\r\nAs for pep8, the current proposal is to be strict. Before the CI will even consider running the tests, it'll check that everything is pep8. If not, it breaks.\r\n\r\nStatic code analysis will fail your PR\r\n---------------------------------\r\n\r\nMore importantly perhaps, static code checks are enforced. As with pep8, these are common to have in an IDE and should prompt warnings. Typical examples from KA Lite counted:\r\n\r\n* Unused imports\r\n* Circular imports\r\n* Unused assignments\r\n* Assignments to builtins\r\n\r\nPy2+3\r\n--------\r\n\r\nAll code is considered to be Python 2 + 3. We will probably need the `six` library a lot.\r\n\r\nMulti-env testing\r\n--------\r\n\r\nTox is used so the CI will run tests both in Python 3.4 and 2.7. Read instructions here:\r\n\r\nhttp://kolibri.readthedocs.org/en/latest/#testing\r\n\r\n\r\nMake is a nice to have (tox is where it happens)\r\n-----------------------------\r\n\r\nThere is a Makefil. But it's not mandatory for anything -- tox is on the other hand. But we should try to avoid Make for the sake of our Windows devs.\r\n\r\n\r\nKolibri settings layers\r\n---------------------------\r\n\r\n***Kolibri is technically speaking a collection of Django applications, with preconfigured Django projects that are invoked through the `kolibri` CLI.***\r\n\r\nIn order to make the Kolibri fly, we have two layers of configuration:\r\n\r\n1. The main Kolibri configuration file, located in `~/.kolibri/kolibri_settings.json` (NB! Whether or not it's JSON is totally up for debate, however I found ConfigParser totally incompatible betwee Py2 and Py3 and PyYaml to be a third-party with possible bindings).\r\n1. Django's settings module: We could stick with old `~/.kolibri/settings.py`, but let's see how far we can get with injecting values from the above file because it's simply easier to deal with a real settings API and not a crazy Python file :)\r\n\r\n\r\nPlugin API\r\n-------------\r\n\r\nThe thing I've been trying to work out and will still add more methods and \"hooks\" for, is the plugin API. It's become quite versatile and flexible.\r\n\r\nThere were to paradigms that I've ended up mixing so they should have the best of both worlds.\r\n\r\nBut firstly, behold that plugins are enabled and disabled directly from command line:\r\n\r\n![loading_a_plugin](https://cloud.githubusercontent.com/assets/374612/12469773/ff880514-bfee-11e5-9551-0bbbcc19f46a.png)\r\n\r\nOnce the plugin is loaded, it will write itself into the kolibri settings file. If you run Django migrations, you now see that it's part of `INSTALLED_APPS`:\r\n\r\n![migration](https://cloud.githubusercontent.com/assets/374612/12469788/19183d14-bfef-11e5-92fc-d073e27cfe0a.png)\r\n\r\nHooks\r\n__________________________________\r\n\r\nThere is an example plugin that has the following code:\r\n\r\n```python\r\n# -*- coding: utf-8 -*-\r\n\"\"\"\r\nKolibri example plugin\r\n======================\r\n\r\nCopy and modify this code for your own plugin.\r\n\r\n\"\"\"\r\nfrom __future__ import absolute_import\r\nfrom __future__ import print_function\r\nfrom __future__ import unicode_literals\r\n\r\n\r\nfrom django.utils.translation import ugettext as _\r\n\r\nfrom kolibri.plugins.base import KolibriPluginBase\r\nfrom kolibri.plugins.hooks import NAVIGATION_POPULATE\r\n\r\n\r\nclass ExamplePlugin(KolibriPluginBase):\r\n\r\n    @classmethod\r\n    def enable(cls, config):\r\n        print(\"Activating example plugin\")\r\n        # Make this automatic and use __name__ ?\r\n        config[\"INSTALLED_APPS\"].append(\"kolibri.plugins.example_plugin\")\r\n\r\n    @classmethod\r\n    def disable(cls, config):\r\n        print(\"Deactivating example plugin\")\r\n        # Make this automatic and use __name__ ?\r\n        config[\"INSTALLED_APPS\"].remove(\"kolibri.plugins.example_plugin\")\r\n\r\n    def main_navigation(self):\r\n        print(\"CALLED\")\r\n        return [{\r\n            'menu_name': _(\"Google\"),\r\n            'menu_url': 'http://google.com',\r\n        }]\r\n\r\n    def hooks(self):\r\n        return {\r\n            NAVIGATION_POPULATE: self.main_navigation\r\n        }\r\n```\r\n\r\nIt currently yields this:\r\n\r\n![screenshot from 2016-01-21 03 34 28](https://cloud.githubusercontent.com/assets/374612/12469880/e5768fd2-bfef-11e5-93ae-89a5a279b17d.png)\r\n\r\n\r\nSo where are the hooks called / rendered?\r\n__________________________________\r\n\r\nAnywhere! That's the beauty of it. Any core component or plugin can access `kolibri.plugins.hooks` and get a list of callables and use them in the context they desire. The important part is that we write down the intention of the hook and do not break this contract.\r\n\r\nWhy a plugin class?\r\n__________________________________\r\n\r\nFor extendibility! Even though plugins are django apps and that makes them a bit tricky to extend, we still want to be able to overwrite a plugin easily. Exactly how this will go down is yet to come, but a singular plugin class inheritance is the beginning.","created_at":"2016-01-21T02:38:36Z","updated_at":"2016-01-22T00:58:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b4573d3a948d56d366311f361c6329c2971d51c2","assignee":{"login":"MCGallaspy","id":8888020,"avatar_url":"https://avatars.githubusercontent.com/u/8888020?v=3","gravatar_id":"","url":"https://api.github.com/users/MCGallaspy","html_url":"https://github.com/MCGallaspy","followers_url":"https://api.github.com/users/MCGallaspy/followers","following_url":"https://api.github.com/users/MCGallaspy/following{/other_user}","gists_url":"https://api.github.com/users/MCGallaspy/gists{/gist_id}","starred_url":"https://api.github.com/users/MCGallaspy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MCGallaspy/subscriptions","organizations_url":"https://api.github.com/users/MCGallaspy/orgs","repos_url":"https://api.github.com/users/MCGallaspy/repos","events_url":"https://api.github.com/users/MCGallaspy/events{/privacy}","received_events_url":"https://api.github.com/users/MCGallaspy/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/learningequality/kolibri/pulls/4/commits","review_comments_url":"https://api.github.com/repos/learningequality/kolibri/pulls/4/comments","review_comment_url":"https://api.github.com/repos/learningequality/kolibri/pulls/comments{/number}","comments_url":"https://api.github.com/repos/learningequality/kolibri/issues/4/comments","statuses_url":"https://api.github.com/repos/learningequality/kolibri/statuses/fa8e634546c64ca9488c979ed013bd1e87f8ff48","head":{"label":"benjaoming:skeleton","ref":"skeleton","sha":"fa8e634546c64ca9488c979ed013bd1e87f8ff48","user":{"login":"benjaoming","id":374612,"avatar_url":"https://avatars.githubusercontent.com/u/374612?v=3","gravatar_id":"","url":"https://api.github.com/users/benjaoming","html_url":"https://github.com/benjaoming","followers_url":"https://api.github.com/users/benjaoming/followers","following_url":"https://api.github.com/users/benjaoming/following{/other_user}","gists_url":"https://api.github.com/users/benjaoming/gists{/gist_id}","starred_url":"https://api.github.com/users/benjaoming/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benjaoming/subscriptions","organizations_url":"https://api.github.com/users/benjaoming/orgs","repos_url":"https://api.github.com/users/benjaoming/repos","events_url":"https://api.github.com/users/benjaoming/events{/privacy}","received_events_url":"https://api.github.com/users/benjaoming/received_events","type":"User","site_admin":false},"repo":{"id":50074781,"name":"kolibri","full_name":"benjaoming/kolibri","owner":{"login":"benjaoming","id":374612,"avatar_url":"https://avatars.githubusercontent.com/u/374612?v=3","gravatar_id":"","url":"https://api.github.com/users/benjaoming","html_url":"https://github.com/benjaoming","followers_url":"https://api.github.com/users/benjaoming/followers","following_url":"https://api.github.com/users/benjaoming/following{/other_user}","gists_url":"https://api.github.com/users/benjaoming/gists{/gist_id}","starred_url":"https://api.github.com/users/benjaoming/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benjaoming/subscriptions","organizations_url":"https://api.github.com/users/benjaoming/orgs","repos_url":"https://api.github.com/users/benjaoming/repos","events_url":"https://api.github.com/users/benjaoming/events{/privacy}","received_events_url":"https://api.github.com/users/benjaoming/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/benjaoming/kolibri","description":"This is where Kolibri shall be born, watch and participate!","fork":true,"url":"https://api.github.com/repos/benjaoming/kolibri","forks_url":"https://api.github.com/repos/benjaoming/kolibri/forks","keys_url":"https://api.github.com/repos/benjaoming/kolibri/keys{/key_id}","collaborators_url":"https://api.github.com/repos/benjaoming/kolibri/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/benjaoming/kolibri/teams","hooks_url":"https://api.github.com/repos/benjaoming/kolibri/hooks","issue_events_url":"https://api.github.com/repos/benjaoming/kolibri/issues/events{/number}","events_url":"https://api.github.com/repos/benjaoming/kolibri/events","assignees_url":"https://api.github.com/repos/benjaoming/kolibri/assignees{/user}","branches_url":"https://api.github.com/repos/benjaoming/kolibri/branches{/branch}","tags_url":"https://api.github.com/repos/benjaoming/kolibri/tags","blobs_url":"https://api.github.com/repos/benjaoming/kolibri/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/benjaoming/kolibri/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/benjaoming/kolibri/git/refs{/sha}","trees_url":"https://api.github.com/repos/benjaoming/kolibri/git/trees{/sha}","statuses_url":"https://api.github.com/repos/benjaoming/kolibri/statuses/{sha}","languages_url":"https://api.github.com/repos/benjaoming/kolibri/languages","stargazers_url":"https://api.github.com/repos/benjaoming/kolibri/stargazers","contributors_url":"https://api.github.com/repos/benjaoming/kolibri/contributors","subscribers_url":"https://api.github.com/repos/benjaoming/kolibri/subscribers","subscription_url":"https://api.github.com/repos/benjaoming/kolibri/subscription","commits_url":"https://api.github.com/repos/benjaoming/kolibri/commits{/sha}","git_commits_url":"https://api.github.com/repos/benjaoming/kolibri/git/commits{/sha}","comments_url":"https://api.github.com/repos/benjaoming/kolibri/comments{/number}","issue_comment_url":"https://api.github.com/repos/benjaoming/kolibri/issues/comments{/number}","contents_url":"https://api.github.com/repos/benjaoming/kolibri/contents/{+path}","compare_url":"https://api.github.com/repos/benjaoming/kolibri/compare/{base}...{head}","merges_url":"https://api.github.com/repos/benjaoming/kolibri/merges","archive_url":"https://api.github.com/repos/benjaoming/kolibri/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/benjaoming/kolibri/downloads","issues_url":"https://api.github.com/repos/benjaoming/kolibri/issues{/number}","pulls_url":"https://api.github.com/repos/benjaoming/kolibri/pulls{/number}","milestones_url":"https://api.github.com/repos/benjaoming/kolibri/milestones{/number}","notifications_url":"https://api.github.com/repos/benjaoming/kolibri/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/benjaoming/kolibri/labels{/name}","releases_url":"https://api.github.com/repos/benjaoming/kolibri/releases{/id}","deployments_url":"https://api.github.com/repos/benjaoming/kolibri/deployments","created_at":"2016-01-21T02:03:13Z","updated_at":"2016-01-21T14:31:46Z","pushed_at":"2016-01-22T00:57:52Z","git_url":"git://github.com/benjaoming/kolibri.git","ssh_url":"git@github.com:benjaoming/kolibri.git","clone_url":"https://github.com/benjaoming/kolibri.git","svn_url":"https://github.com/benjaoming/kolibri","homepage":"http://learningequality.org/kolibri/","size":51,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"learningequality:master","ref":"master","sha":"4f660bf8c0102653b6afc125f05fdc00767759fc","user":{"login":"learningequality","id":3926525,"avatar_url":"https://avatars.githubusercontent.com/u/3926525?v=3","gravatar_id":"","url":"https://api.github.com/users/learningequality","html_url":"https://github.com/learningequality","followers_url":"https://api.github.com/users/learningequality/followers","following_url":"https://api.github.com/users/learningequality/following{/other_user}","gists_url":"https://api.github.com/users/learningequality/gists{/gist_id}","starred_url":"https://api.github.com/users/learningequality/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/learningequality/subscriptions","organizations_url":"https://api.github.com/users/learningequality/orgs","repos_url":"https://api.github.com/users/learningequality/repos","events_url":"https://api.github.com/users/learningequality/events{/privacy}","received_events_url":"https://api.github.com/users/learningequality/received_events","type":"Organization","site_admin":false},"repo":{"id":49976939,"name":"kolibri","full_name":"learningequality/kolibri","owner":{"login":"learningequality","id":3926525,"avatar_url":"https://avatars.githubusercontent.com/u/3926525?v=3","gravatar_id":"","url":"https://api.github.com/users/learningequality","html_url":"https://github.com/learningequality","followers_url":"https://api.github.com/users/learningequality/followers","following_url":"https://api.github.com/users/learningequality/following{/other_user}","gists_url":"https://api.github.com/users/learningequality/gists{/gist_id}","starred_url":"https://api.github.com/users/learningequality/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/learningequality/subscriptions","organizations_url":"https://api.github.com/users/learningequality/orgs","repos_url":"https://api.github.com/users/learningequality/repos","events_url":"https://api.github.com/users/learningequality/events{/privacy}","received_events_url":"https://api.github.com/users/learningequality/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/learningequality/kolibri","description":"This is where Kolibri shall be born, watch and participate!","fork":false,"url":"https://api.github.com/repos/learningequality/kolibri","forks_url":"https://api.github.com/repos/learningequality/kolibri/forks","keys_url":"https://api.github.com/repos/learningequality/kolibri/keys{/key_id}","collaborators_url":"https://api.github.com/repos/learningequality/kolibri/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/learningequality/kolibri/teams","hooks_url":"https://api.github.com/repos/learningequality/kolibri/hooks","issue_events_url":"https://api.github.com/repos/learningequality/kolibri/issues/events{/number}","events_url":"https://api.github.com/repos/learningequality/kolibri/events","assignees_url":"https://api.github.com/repos/learningequality/kolibri/assignees{/user}","branches_url":"https://api.github.com/repos/learningequality/kolibri/branches{/branch}","tags_url":"https://api.github.com/repos/learningequality/kolibri/tags","blobs_url":"https://api.github.com/repos/learningequality/kolibri/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/learningequality/kolibri/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/learningequality/kolibri/git/refs{/sha}","trees_url":"https://api.github.com/repos/learningequality/kolibri/git/trees{/sha}","statuses_url":"https://api.github.com/repos/learningequality/kolibri/statuses/{sha}","languages_url":"https://api.github.com/repos/learningequality/kolibri/languages","stargazers_url":"https://api.github.com/repos/learningequality/kolibri/stargazers","contributors_url":"https://api.github.com/repos/learningequality/kolibri/contributors","subscribers_url":"https://api.github.com/repos/learningequality/kolibri/subscribers","subscription_url":"https://api.github.com/repos/learningequality/kolibri/subscription","commits_url":"https://api.github.com/repos/learningequality/kolibri/commits{/sha}","git_commits_url":"https://api.github.com/repos/learningequality/kolibri/git/commits{/sha}","comments_url":"https://api.github.com/repos/learningequality/kolibri/comments{/number}","issue_comment_url":"https://api.github.com/repos/learningequality/kolibri/issues/comments{/number}","contents_url":"https://api.github.com/repos/learningequality/kolibri/contents/{+path}","compare_url":"https://api.github.com/repos/learningequality/kolibri/compare/{base}...{head}","merges_url":"https://api.github.com/repos/learningequality/kolibri/merges","archive_url":"https://api.github.com/repos/learningequality/kolibri/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/learningequality/kolibri/downloads","issues_url":"https://api.github.com/repos/learningequality/kolibri/issues{/number}","pulls_url":"https://api.github.com/repos/learningequality/kolibri/pulls{/number}","milestones_url":"https://api.github.com/repos/learningequality/kolibri/milestones{/number}","notifications_url":"https://api.github.com/repos/learningequality/kolibri/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/learningequality/kolibri/labels{/name}","releases_url":"https://api.github.com/repos/learningequality/kolibri/releases{/id}","deployments_url":"https://api.github.com/repos/learningequality/kolibri/deployments","created_at":"2016-01-19T19:22:07Z","updated_at":"2016-01-19T19:48:58Z","pushed_at":"2016-01-21T15:09:21Z","git_url":"git://github.com/learningequality/kolibri.git","ssh_url":"git@github.com:learningequality/kolibri.git","clone_url":"https://github.com/learningequality/kolibri.git","svn_url":"https://github.com/learningequality/kolibri","homepage":"http://learningequality.org/kolibri/","size":6,"stargazers_count":2,"watchers_count":2,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":2,"forks":3,"open_issues":2,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/4"},"html":{"href":"https://github.com/learningequality/kolibri/pull/4"},"issue":{"href":"https://api.github.com/repos/learningequality/kolibri/issues/4"},"comments":{"href":"https://api.github.com/repos/learningequality/kolibri/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/learningequality/kolibri/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/learningequality/kolibri/statuses/fa8e634546c64ca9488c979ed013bd1e87f8ff48"}}}}