{"action":"created","comment":{"url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments/33065223","id":33065223,"diff_hunk":"@@ -0,0 +1,209 @@\n+# -*- coding: utf-8 -*-\n+from __future__ import unicode_literals\n+\n+from django.db import models, migrations\n+\n+# language=SQL\n+TRIGGER_SQL = \"\"\"\n+----------------------------------------------------------------------\n+-- Trigger procedure to update group count\n+----------------------------------------------------------------------\n+CREATE OR REPLACE FUNCTION update_group_count() RETURNS TRIGGER AS $$\n+DECLARE\n+  is_test BOOLEAN;\n+BEGIN\n+  -- contact being added to group\n+  IF TG_OP = 'INSERT' THEN\n+    -- is this a test contact\n+    SELECT contacts_contact.is_test INTO STRICT is_test FROM contacts_contact WHERE id = NEW.contact_id;\n+\n+    IF NOT is_test THEN\n+      -- increment our group count\n+      UPDATE contacts_contactgroup SET count = count + 1 WHERE id = NEW.contactgroup_id;\n+    END IF;\n+\n+  -- contact being removed from a group\n+  ELSIF TG_OP = 'DELETE' THEN\n+    -- is this a test contact\n+    SELECT contacts_contact.is_test INTO STRICT is_test FROM contacts_contact WHERE id = OLD.contact_id;\n+\n+    IF NOT is_test THEN\n+      -- decrement our group count\n+      UPDATE contacts_contactgroup SET count = count - 1 WHERE id = OLD.contactgroup_id;\n+    END IF;\n+\n+  -- table being cleared, reset all counts\n+  ELSIF TG_OP = 'TRUNCATE' THEN\n+    UPDATE contacts_contactgroup SET count = 0;\n+  END IF;\n+\n+  RETURN NULL;\n+END;\n+$$ LANGUAGE plpgsql;\n+\n+-- install for INSERT and DELETE on contacts_contactgroup_contacts\n+DROP TRIGGER IF EXISTS when_contact_groups_changed_then_update_count_trg on contacts_contactgroup_contacts;\n+CREATE TRIGGER when_contact_groups_changed_then_update_count_trg\n+   AFTER INSERT OR DELETE ON contacts_contactgroup_contacts\n+   FOR EACH ROW EXECUTE PROCEDURE update_group_count();\n+\n+-- install for TRUNCATE on contacts_contactgroup_contacts\n+DROP TRIGGER IF EXISTS when_contact_groups_truncate_then_update_count_trg on contacts_contactgroup_contacts;\n+CREATE TRIGGER when_contact_groups_truncate_then_update_count_trg\n+  AFTER TRUNCATE ON contacts_contactgroup_contacts\n+  EXECUTE PROCEDURE update_group_count();\n+\n+----------------------------------------------------------------------\n+-- Toggle a contact's membership of a system group in their org\n+----------------------------------------------------------------------\n+CREATE OR REPLACE FUNCTION\n+  contact_toggle_system_group(_contact_id INT, _org_id INT, _group_type CHAR(1), _add BOOLEAN)\n+RETURNS VOID AS $$\n+DECLARE\n+  _group_id INT;\n+BEGIN\n+  -- lookup the group id\n+  SELECT id INTO STRICT _group_id FROM contacts_contactgroup\n+  WHERE org_id = _org_id AND group_type = _group_type;\n+\n+  -- don't do anything if group doesn't exist for some inexplicable reason\n+  IF _group_id IS NULL THEN\n+    RETURN;\n+  END IF;\n+\n+  IF _add THEN\n+    BEGIN\n+      INSERT INTO contacts_contactgroup_contacts (contactgroup_id, contact_id) VALUES (_group_id, _contact_id);\n+    EXCEPTION WHEN unique_violation THEN\n+      -- do nothing\n+    END;\n+  ELSE\n+    DELETE FROM contacts_contactgroup_contacts WHERE contactgroup_id = _group_id AND contact_id = _contact_id;\n+  END IF;\n+END;\n+$$ LANGUAGE plpgsql;\n+\n+----------------------------------------------------------------------\n+-- Convenience method to call contact_toggle_system_group with a row\n+----------------------------------------------------------------------\n+CREATE OR REPLACE FUNCTION\n+  contact_toggle_system_group(_contact contacts_contact, _group_type CHAR(1), _add BOOLEAN)\n+RETURNS VOID AS $$\n+DECLARE\n+  _group_id INT;\n+BEGIN\n+  PERFORM contact_toggle_system_group(_contact.id, _contact.org_id, _group_type, _add);\n+END;\n+$$ LANGUAGE plpgsql;\n+\n+----------------------------------------------------------------------\n+-- Trigger procedure to update contact system groups on column changes\n+----------------------------------------------------------------------\n+CREATE OR REPLACE FUNCTION update_contact_system_groups() RETURNS TRIGGER AS $$\n+BEGIN\n+  -- new contact added\n+  IF TG_OP = 'INSERT' AND NEW.is_active AND NOT NEW.is_test THEN\n+    IF NEW.is_blocked THEN\n+      PERFORM contact_toggle_system_group(NEW, 'B', true);\n+    ELSE\n+      PERFORM contact_toggle_system_group(NEW, 'A', true);\n+      IF NEW.is_failed THEN\n+          PERFORM contact_toggle_system_group(NEW, 'F', true);\n+      END IF;\n+    END IF;\n+  END IF;\n+\n+  -- existing contact updated\n+  IF TG_OP = 'UPDATE' AND NOT NEW.is_test THEN\n+    -- do nothing for inactive contacts\n+    IF NOT OLD.is_active AND NOT NEW.is_active THEN\n+      RETURN NULL;\n+    END IF;\n+\n+    -- is being blocked\n+    IF NOT OLD.is_blocked AND NEW.is_blocked THEN\n+      PERFORM contact_toggle_system_group(NEW, 'A', false);\n+      PERFORM contact_toggle_system_group(NEW, 'B', true);\n+      PERFORM contact_toggle_system_group(NEW, 'F', false);\n+    END IF;\n+\n+    -- is being unblocked\n+    IF OLD.is_blocked AND NOT NEW.is_blocked THEN\n+      PERFORM contact_toggle_system_group(NEW, 'A', true);\n+      PERFORM contact_toggle_system_group(NEW, 'B', false);\n+      IF NEW.is_failed THEN\n+        PERFORM contact_toggle_system_group(NEW, 'F', true);\n+      END IF;\n+    END IF;\n+\n+    -- is being failed\n+    IF NOT OLD.is_failed AND NEW.is_failed THEN\n+      PERFORM contact_toggle_system_group(NEW, 'F', true);\n+    END IF;\n+\n+    -- is being unfailed\n+    IF OLD.is_failed AND NOT NEW.is_failed THEN\n+      PERFORM contact_toggle_system_group(NEW, 'F', false);\n+    END IF;\n+\n+    -- is being released\n+    IF OLD.is_active AND NOT NEW.is_active THEN\n+      PERFORM contact_toggle_system_group(NEW, 'A', false);\n+      PERFORM contact_toggle_system_group(NEW, 'B', false);\n+      PERFORM contact_toggle_system_group(NEW, 'F', false);\n+    END IF;\n+\n+    -- is being unreleased\n+    IF NOT OLD.is_active AND NEW.is_active THEN\n+      IF NOT NEW.is_blocked THEN\n+        PERFORM contact_toggle_system_group(NEW, 'A', true);\n+      ELSE\n+        PERFORM contact_toggle_system_group(NEW, 'B', true);\n+      END IF;\n+      IF NEW.is_failed THEN\n+        PERFORM contact_toggle_system_group(NEW, 'F', true);\n+      END IF;\n+    END IF;\n+\n+  END IF;\n+\n+  RETURN NULL;\n+END;\n+$$ LANGUAGE plpgsql;\n+\n+-- install for INSERT and UPDATE on contacts_contact\n+DROP TRIGGER IF EXISTS when_contacts_changed_then_update_groups_trg ON contacts_contact;\n+CREATE TRIGGER when_contacts_changed_then_update_groups_trg\n+   AFTER INSERT OR UPDATE ON contacts_contact\n+   FOR EACH ROW EXECUTE PROCEDURE update_contact_system_groups();\n+\n+----------------------------------------------------------------------\n+-- Trigger procedure to prevent illegal state changes to contacts\n+----------------------------------------------------------------------\n+CREATE OR REPLACE FUNCTION contact_check_update() RETURNS TRIGGER AS $$\n+BEGIN\n+  IF OLD.is_test != NEW.is_test THEN\n+    RAISE EXCEPTION 'is_test cannot be changed';\n+  END IF;\n+\n+  RETURN NEW;\n+END;\n+$$ LANGUAGE plpgsql;\n+\n+-- install for UPDATE on contacts_contact\n+DROP TRIGGER IF EXISTS contact_check_update_trg ON contacts_contact;\n+CREATE TRIGGER contact_check_update_trg\n+   BEFORE UPDATE ON contacts_contact\n+   FOR EACH ROW EXECUTE PROCEDURE contact_check_update();\n+\"\"\"","path":"temba/contacts/migrations/0018_update_group_triggers.py","position":198,"original_position":198,"commit_id":"fad042a43ce3051cdf1ab9ee0446e8128a87c70c","original_commit_id":"fad042a43ce3051cdf1ab9ee0446e8128a87c70c","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 tho looking at this now am thinking I should update `contact_check_update` to disallow failing, blocking and de-activating of test contacts... in which case there would be 4 columns","created_at":"2015-06-23T17:07:03Z","updated_at":"2015-06-23T17:07:03Z","html_url":"https://github.com/nyaruka/rapidpro/pull/232#discussion_r33065223","pull_request_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232","_links":{"self":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments/33065223"},"html":{"href":"https://github.com/nyaruka/rapidpro/pull/232#discussion_r33065223"},"pull_request":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232"}}},"pull_request":{"url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232","id":38234423,"html_url":"https://github.com/nyaruka/rapidpro/pull/232","diff_url":"https://github.com/nyaruka/rapidpro/pull/232.diff","patch_url":"https://github.com/nyaruka/rapidpro/pull/232.patch","issue_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/232","number":232,"state":"open","locked":false,"title":"Contact system groups by database trigger","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":"`Contact.is_test` becomes un-changeable to simplify possible state changes, hence changes to some unit tests to use `Contact.get_test_contact` rather than switching existing contacts into test mode.\r\n","created_at":"2015-06-22T14:01:15Z","updated_at":"2015-06-23T17:07:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"45f756e00dbd8eeb1554d9e19ff27e7dbd867855","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232/commits","review_comments_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232/comments","review_comment_url":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nyaruka/rapidpro/issues/232/comments","statuses_url":"https://api.github.com/repos/nyaruka/rapidpro/statuses/fad042a43ce3051cdf1ab9ee0446e8128a87c70c","head":{"label":"nyaruka:system_groups_by_trigger","ref":"system_groups_by_trigger","sha":"fad042a43ce3051cdf1ab9ee0446e8128a87c70c","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}","created_at":"2014-12-02T04:53:16Z","updated_at":"2015-05-08T12:28:17Z","pushed_at":"2015-06-23T16:33:18Z","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":22494,"stargazers_count":4,"watchers_count":4,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":4,"default_branch":"master"}},"base":{"label":"nyaruka:master","ref":"master","sha":"df9afcfc5610fc17b7ffabe68fe40aa79cf8c182","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}","created_at":"2014-12-02T04:53:16Z","updated_at":"2015-05-08T12:28:17Z","pushed_at":"2015-06-23T16:33:18Z","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":22494,"stargazers_count":4,"watchers_count":4,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232"},"html":{"href":"https://github.com/nyaruka/rapidpro/pull/232"},"issue":{"href":"https://api.github.com/repos/nyaruka/rapidpro/issues/232"},"comments":{"href":"https://api.github.com/repos/nyaruka/rapidpro/issues/232/comments"},"review_comments":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232/comments"},"review_comment":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nyaruka/rapidpro/pulls/232/commits"},"statuses":{"href":"https://api.github.com/repos/nyaruka/rapidpro/statuses/fad042a43ce3051cdf1ab9ee0446e8128a87c70c"}}}}