{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/201491651","pull_request_review_id":136005628,"id":201491651,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMTQ5MTY1MQ==","diff_hunk":"@@ -0,0 +1,276 @@\n+\"\"\"\n+APIs providing support for Journals functionality.\n+\"\"\"\n+import logging\n+import hashlib\n+from urlparse import urlsplit, urlunsplit\n+import six\n+\n+from django.conf import settings\n+from django.contrib.auth import get_user_model\n+from django.core.cache import cache\n+from django.core.exceptions import ObjectDoesNotExist\n+from edx_rest_api_client.client import EdxRestApiClient\n+from openedx.core.djangoapps.catalog.models import CatalogIntegration\n+from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers\n+from openedx.core.lib.token_utils import JwtBuilder\n+from slumber.exceptions import HttpClientError, HttpServerError\n+\n+\n+LOGGER = logging.getLogger(\"edx.journals\")\n+JOURNALS_CACHE_TIMEOUT = 3600  # Value is in seconds\n+JOURNALS_API_PATH = '/journal/api/v1/'\n+JOURNAL_WORKER_USERNAME = 'journals_worker'\n+User = get_user_model()\n+\n+\n+class DiscoveryApiClient(object):\n+    \"\"\"\n+    Class for interacting with the discovery service journals endpoint\n+    \"\"\"\n+    def __init__(self):\n+        \"\"\"\n+        Initialize an authenticated Discovery service API client by using the\n+        provided user.\n+        \"\"\"\n+        catalog_integration = CatalogIntegration.current()\n+\n+        # Client can't be used if there is no catalog integration\n+        if not (catalog_integration and catalog_integration.enabled):\n+            LOGGER.info(\"Unable to create DiscoveryApiClient because catalog integration not set up or enabled\")\n+            return None\n+\n+        try:\n+            user = catalog_integration.get_service_user()\n+        except ObjectDoesNotExist:\n+            LOGGER.info(\"Unable to retrieve catalog integration service user\")\n+            return None\n+\n+        jwt = JwtBuilder(user).build_token([])\n+        url = configuration_helpers.get_value('COURSE_CATALOG_API_URL', settings.COURSE_CATALOG_API_URL)\n+        self.client = EdxRestApiClient(self.create_journals_url(url), jwt=jwt)\n+\n+    def create_journals_url(self, url):\n+        \"\"\"\n+        rewrite the discovery url to point to journals endpoint\n+        \"\"\"\n+        split_url = urlsplit(url)\n+        override_url = urlunsplit((\n+            split_url.scheme,\n+            split_url.netloc,\n+            JOURNALS_API_PATH,\n+            split_url.query,\n+            split_url.fragment,\n+        ))\n+        return override_url\n+\n+    def get_journals(self, orgs):\n+        \"\"\"\n+        get_journals from discovery, filter on orgs is supplied\n+        \"\"\"\n+        try:\n+            if orgs:\n+                response = self.client.journals.get(orgs=','.join(orgs), status='active')\n+            else:\n+                response = self.client.journals.get(status='active')\n+            LOGGER.debug('response is type=%s', type(response))\n+            return response.get('results')\n+        except (HttpClientError, HttpServerError) as err:\n+            LOGGER.exception(\n+                'Failed to get journals from discovery-service [%s]',\n+                err.content\n+            )\n+            return []\n+\n+    def get_journal_bundles(self, uuid=''):","path":"openedx/features/journals/api.py","position":74,"original_position":85,"commit_id":"10f909c3344146f3d4ce5ea1736f0a68c7cb0db2","original_commit_id":"af86b9a9ac3c1a5522ff4efe63b6d9a9983c6de6","user":{"login":"bill-filler","id":27503539,"node_id":"MDQ6VXNlcjI3NTAzNTM5","avatar_url":"https://avatars3.githubusercontent.com/u/27503539?v=4","gravatar_id":"","url":"https://api.github.com/users/bill-filler","html_url":"https://github.com/bill-filler","followers_url":"https://api.github.com/users/bill-filler/followers","following_url":"https://api.github.com/users/bill-filler/following{/other_user}","gists_url":"https://api.github.com/users/bill-filler/gists{/gist_id}","starred_url":"https://api.github.com/users/bill-filler/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bill-filler/subscriptions","organizations_url":"https://api.github.com/users/bill-filler/orgs","repos_url":"https://api.github.com/users/bill-filler/repos","events_url":"https://api.github.com/users/bill-filler/events{/privacy}","received_events_url":"https://api.github.com/users/bill-filler/received_events","type":"User","site_admin":false},"body":"fixed, always returns a list now","created_at":"2018-07-10T20:55:09Z","updated_at":"2018-07-10T20:55:09Z","html_url":"https://github.com/edx/edx-platform/pull/18395#discussion_r201491651","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/18395","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/201491651"},"html":{"href":"https://github.com/edx/edx-platform/pull/18395#discussion_r201491651"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/18395"}},"in_reply_to_id":200473816},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/18395","id":195642886,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk1NjQyODg2","html_url":"https://github.com/edx/edx-platform/pull/18395","diff_url":"https://github.com/edx/edx-platform/pull/18395.diff","patch_url":"https://github.com/edx/edx-platform/pull/18395.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/18395","number":18395,"state":"open","locked":false,"title":"Add journals support in LMS","user":{"login":"bill-filler","id":27503539,"node_id":"MDQ6VXNlcjI3NTAzNTM5","avatar_url":"https://avatars3.githubusercontent.com/u/27503539?v=4","gravatar_id":"","url":"https://api.github.com/users/bill-filler","html_url":"https://github.com/bill-filler","followers_url":"https://api.github.com/users/bill-filler/followers","following_url":"https://api.github.com/users/bill-filler/following{/other_user}","gists_url":"https://api.github.com/users/bill-filler/gists{/gist_id}","starred_url":"https://api.github.com/users/bill-filler/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bill-filler/subscriptions","organizations_url":"https://api.github.com/users/bill-filler/orgs","repos_url":"https://api.github.com/users/bill-filler/repos","events_url":"https://api.github.com/users/bill-filler/events{/privacy}","received_events_url":"https://api.github.com/users/bill-filler/received_events","type":"User","site_admin":false},"body":"**Journals**\r\nThis PR contains a new Django application (journals) that allows learners to interact with a content Journal through LMS dashboard and course listing page. The majority of the capabilities are provided through the [Journals IDA](https://github.com/edx/journals) and through Journal modules added to [Discovery](https://github.com/edx/course-discovery/pull/1428) and [Ecommerce](https://github.com/edx/ecommerce/pull/1790) services. Code that was added outside of the Journals app was necessary to integrate Journals into the dashboard and main content listing page only.\r\n\r\n**Journal**:\r\n\r\nThe Journal is a new product type that will be offered for purchase in the LMS. It is indepedent from a course, and contains a collection of resources/content-types (documents, videos, rich text, etc) that can be updated easily through the Journal service. A Journal is linked to an organization and you can purchase/receive access to it. One notable difference is that a Journal will have an access_length, which determines the amount of time the learner will have access to it post-purchase. This is our first stage towards a subscription model. The Discovery Service is the source-of-truth for all Journal related marketing information that is displayed in the LMS.\r\n\r\n**JournalBundle**:\r\n\r\nThe Journal Bundle is a collection of Journals and Courses. It works similar to a program in the bundling aspect, the difference lies in the fact that it doesn't necessarily constitute a progression of courses. The first (and possibly most common) use case that will use this is bundling a single course with a single journal. Journal Bundles are defined in Discovery Service.\r\n\r\n**Things to note:**\r\n\r\nThe Journals app was intentionally decoupled as much as possible from the rest of the LMS, both for future developer sanity, and to minimally affect the rest of the platform should the scope of the Journals product change.\r\n\r\nThe Journals product has a seperate IDA (Journals) which works as the publishing platform, the consumption platform, and the marketing platform (for standalone Journals). This is different from the structure of studio/lms and so some information may be handled differently in this application.\r\n\r\n**Functionality Added to LMS to support Journals:**\r\n\r\n\"Cards\" for Journals and Journal Bundles on main LMS index page such that learners can discover and purchase Journals. These are the equivalent to course cards and Program cards in the LMS.\r\n\r\nJournals dashboard, that lists Journals that have been purchased by the learner. Similar to Programs dashboard in LMS.\r\n\r\n**API**:\r\n\r\napi.py - This class provides an abstraction to Journal specific information that is needed by the LMS. Specifically, it provides an api to fetch Journals and Journal Bundles from the Discovery Service, both of which are needed to display Journals information on main LMS index page as well as dashboard. Additionally it provides an api to fetch JournalAccess records from the Journal Service. This is used to determine which Journals a user has access to and displays this information on the \"Journals\" Dashboard.\r\n\r\n**Views**:\r\n\r\nlearner_dashboard.py: This view adds a \"Journals\" tab to the dashboard and displays a list of Journals that the learner has access to. Clicking on a Journal from the dashboard will direct learner to the Journal Service to view the Journal.\r\n\r\nmarketing.py: This view provides a marketing page for Journal Bundles. It can be thought of as the equivalent to a Program About page. Specifically, it will show information about Journal(s) and Course(s) which have been bundled together for marketing and discouting purposes. The bundle definition and meta-data lives in the Discovery service.","created_at":"2018-06-18T20:43:57Z","updated_at":"2018-07-10T20:55:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dc1708fdd5b303839e0b0bf8446cb33948d824d0","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/18395/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/18395/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/18395/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/10f909c3344146f3d4ce5ea1736f0a68c7cb0db2","head":{"label":"edx:whitelabel/journal","ref":"whitelabel/journal","sha":"10f909c3344146f3d4ce5ea1736f0a68c7cb0db2","user":{"login":"edx","id":3179841,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNzk4NDE=","avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"node_id":"MDEwOlJlcG9zaXRvcnkxMDM5MTA3Mw==","name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNzk4NDE=","avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2018-07-10T19:18:51Z","pushed_at":"2018-07-10T20:48:18Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":944790,"stargazers_count":3762,"watchers_count":3762,"language":"Python","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2121,"mirror_url":null,"archived":false,"open_issues_count":201,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":2121,"open_issues":201,"watchers":3762,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"5f3e9cb1b81ccdc2a42e4d3d2083fa8a9a8ab3fe","user":{"login":"edx","id":3179841,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNzk4NDE=","avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"node_id":"MDEwOlJlcG9zaXRvcnkxMDM5MTA3Mw==","name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNzk4NDE=","avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2018-07-10T19:18:51Z","pushed_at":"2018-07-10T20:48:18Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":944790,"stargazers_count":3762,"watchers_count":3762,"language":"Python","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2121,"mirror_url":null,"archived":false,"open_issues_count":201,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":2121,"open_issues":201,"watchers":3762,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/18395"},"html":{"href":"https://github.com/edx/edx-platform/pull/18395"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/18395"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/18395/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/18395/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/18395/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/10f909c3344146f3d4ce5ea1736f0a68c7cb0db2"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"bill-filler"}}