{"action":"created","issue":{"url":"https://api.github.com/repos/hypothesis/product-backlog/issues/341","repository_url":"https://api.github.com/repos/hypothesis/product-backlog","labels_url":"https://api.github.com/repos/hypothesis/product-backlog/issues/341/labels{/name}","comments_url":"https://api.github.com/repos/hypothesis/product-backlog/issues/341/comments","events_url":"https://api.github.com/repos/hypothesis/product-backlog/issues/341/events","html_url":"https://github.com/hypothesis/product-backlog/issues/341","id":243333387,"number":341,"title":"Canvas users have to manually create and join Hypothesis groups","user":{"login":"seanh","id":22498,"avatar_url":"https://avatars3.githubusercontent.com/u/22498?v=4","gravatar_id":"","url":"https://api.github.com/users/seanh","html_url":"https://github.com/seanh","followers_url":"https://api.github.com/users/seanh/followers","following_url":"https://api.github.com/users/seanh/following{/other_user}","gists_url":"https://api.github.com/users/seanh/gists{/gist_id}","starred_url":"https://api.github.com/users/seanh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanh/subscriptions","organizations_url":"https://api.github.com/users/seanh/orgs","repos_url":"https://api.github.com/users/seanh/repos","events_url":"https://api.github.com/users/seanh/events{/privacy}","received_events_url":"https://api.github.com/users/seanh/received_events","type":"User","site_admin":false},"labels":[{"id":620337428,"url":"https://api.github.com/repos/hypothesis/product-backlog/labels/canvas","name":"canvas","color":"5319e7","default":false}],"state":"open","locked":false,"assignee":{"login":"seanh","id":22498,"avatar_url":"https://avatars3.githubusercontent.com/u/22498?v=4","gravatar_id":"","url":"https://api.github.com/users/seanh","html_url":"https://github.com/seanh","followers_url":"https://api.github.com/users/seanh/followers","following_url":"https://api.github.com/users/seanh/following{/other_user}","gists_url":"https://api.github.com/users/seanh/gists{/gist_id}","starred_url":"https://api.github.com/users/seanh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanh/subscriptions","organizations_url":"https://api.github.com/users/seanh/orgs","repos_url":"https://api.github.com/users/seanh/repos","events_url":"https://api.github.com/users/seanh/events{/privacy}","received_events_url":"https://api.github.com/users/seanh/received_events","type":"User","site_admin":false},"assignees":[{"login":"seanh","id":22498,"avatar_url":"https://avatars3.githubusercontent.com/u/22498?v=4","gravatar_id":"","url":"https://api.github.com/users/seanh","html_url":"https://github.com/seanh","followers_url":"https://api.github.com/users/seanh/followers","following_url":"https://api.github.com/users/seanh/following{/other_user}","gists_url":"https://api.github.com/users/seanh/gists{/gist_id}","starred_url":"https://api.github.com/users/seanh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanh/subscriptions","organizations_url":"https://api.github.com/users/seanh/orgs","repos_url":"https://api.github.com/users/seanh/repos","events_url":"https://api.github.com/users/seanh/events{/privacy}","received_events_url":"https://api.github.com/users/seanh/received_events","type":"User","site_admin":false},{"login":"sheetaluk","id":502186,"avatar_url":"https://avatars3.githubusercontent.com/u/502186?v=4","gravatar_id":"","url":"https://api.github.com/users/sheetaluk","html_url":"https://github.com/sheetaluk","followers_url":"https://api.github.com/users/sheetaluk/followers","following_url":"https://api.github.com/users/sheetaluk/following{/other_user}","gists_url":"https://api.github.com/users/sheetaluk/gists{/gist_id}","starred_url":"https://api.github.com/users/sheetaluk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sheetaluk/subscriptions","organizations_url":"https://api.github.com/users/sheetaluk/orgs","repos_url":"https://api.github.com/users/sheetaluk/repos","events_url":"https://api.github.com/users/sheetaluk/events{/privacy}","received_events_url":"https://api.github.com/users/sheetaluk/received_events","type":"User","site_admin":false},{"login":"dawariley","id":5212631,"avatar_url":"https://avatars2.githubusercontent.com/u/5212631?v=4","gravatar_id":"","url":"https://api.github.com/users/dawariley","html_url":"https://github.com/dawariley","followers_url":"https://api.github.com/users/dawariley/followers","following_url":"https://api.github.com/users/dawariley/following{/other_user}","gists_url":"https://api.github.com/users/dawariley/gists{/gist_id}","starred_url":"https://api.github.com/users/dawariley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dawariley/subscriptions","organizations_url":"https://api.github.com/users/dawariley/orgs","repos_url":"https://api.github.com/users/dawariley/repos","events_url":"https://api.github.com/users/dawariley/events{/privacy}","received_events_url":"https://api.github.com/users/dawariley/received_events","type":"User","site_admin":false},{"login":"ajpeddakotla","id":7463523,"avatar_url":"https://avatars3.githubusercontent.com/u/7463523?v=4","gravatar_id":"","url":"https://api.github.com/users/ajpeddakotla","html_url":"https://github.com/ajpeddakotla","followers_url":"https://api.github.com/users/ajpeddakotla/followers","following_url":"https://api.github.com/users/ajpeddakotla/following{/other_user}","gists_url":"https://api.github.com/users/ajpeddakotla/gists{/gist_id}","starred_url":"https://api.github.com/users/ajpeddakotla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ajpeddakotla/subscriptions","organizations_url":"https://api.github.com/users/ajpeddakotla/orgs","repos_url":"https://api.github.com/users/ajpeddakotla/repos","events_url":"https://api.github.com/users/ajpeddakotla/events{/privacy}","received_events_url":"https://api.github.com/users/ajpeddakotla/received_events","type":"User","site_admin":false}],"milestone":null,"comments":2,"created_at":"2017-07-17T09:05:15Z","updated_at":"2018-03-08T14:23:54Z","closed_at":null,"author_association":"NONE","body":"## See also\r\n\r\n* [Canvas][canvas] is an LMS (Learning Management System) used by schools to deliver courses. (Similar to Blackboard, Moodle, Sakai...)\r\n* [Canvas source code on GitHub][canvas_source]\r\n* The Hypothesis Canvas app:\r\n  * [Production deployment][canvas_app]\r\n  * [Source code][canvas_app_github]\r\n  * Canvas app Google Docs from @judell:\r\n    * [Canvas Architecture Notes][canvas_architecture_notes]\r\n    * [Installing the App][installing_the_app]\r\n    * [Using the App][using_the_app]\r\n    * [Student Guide][student_guide]\r\n  * Screencasts from @jeremydean:\r\n    * [Student walkthrough screencast][student_walkthrough_screencast]\r\n    * [Teacher walkthrough screencast][teacher_walkthrough_screencast]\r\n* [Feb 2017 Canvas spike by Nick][canvas_spike]\r\n* [May 2017 Canvas Feature Spec Sheet][canvas fss] (google doc)\r\n* [Canvas GitHub Issues label in the product-backlog repo][canvas_github_label], including:\r\n  * Canvas app isn't production code quality <https://github.com/hypothesis/product-backlog/issues/340>\r\n  * Canvas app isn't on production infrastructure <https://github.com/hypothesis/product-backlog/issues/339>\r\n  * Canvas users have to manually create and activate Hypothesis accounts <https://github.com/hypothesis/product-backlog/issues/342>\r\n* [#feat-canvas Slack channel][canvas_slack_channel]\r\n* [Canvas folder on Google Drive][canvas_google_drive_folder] (we should move all Canvas-related Google Docs into this folder)\r\n* [Our Instructure-hosted Canvas instance][instructure_canvas_instance], we can use this to try out Canvas\r\n\r\n[canvas]: https://www.canvaslms.com/\r\n[canvas_app]: https://lti.hypothesislabs.com/ \"The current Canvas app deployment at lti.hypothesislabs.com\"\r\n[canvas_app_github]: https://github.com/judell/lti \"The GitHub repo for the Canvas app\"\r\n[canvas_source]: https://github.com/instructure/canvas-lms \"The Canvas source code on GitHub\"\r\n[canvas_spike]: https://github.com/hypothesis/product-backlog/issues/114#issuecomment-277220242 \"Feb 2017 Canvas spike by Nick\"\r\n[canvas fss]: https://docs.google.com/document/d/19dM-f4pScrhqmpHk6QHg36P3pVu6QTZ6wULmc_1ZTfM/edit# \"May 2017 Canvas Feature Spec Sheet\"\r\n[canvas_github_label]: https://github.com/hypothesis/product-backlog/labels/canvas \"Canvas GitHub Issues label in the product-backlog repo\"\r\n[canvas_architecture_notes]: https://docs.google.com/document/d/1iobDNeP9shOrnKX-oR9Yj9rLeozrHUlaB4BgdM0Ikyg/edit# \"Canvas Architecture Notes Google Doc\"\r\n[installing_the_app]: https://docs.google.com/document/d/13FFtk2qRogtU3qxR_oa3kq2ak-S_p7HHVnNM12eZGy8/edit# \"Installing the App Google Doc\"\r\n[using_the_app]: https://docs.google.com/document/d/1EvxGoX81H8AWDcskDph8dmu4Ov4gMSkGGXr5_5ggx3I/edit# \"Using the App Google Doc\"\r\n[student_guide]: https://docs.google.com/document/d/1iVOO-BPci8xZMZa2wl_N6RXKKAvONUXHTZ0MRKvQCN8/edit \"Student Guide Google Doc\"\r\n[canvas_slack_channel]: https://hypothes-is.slack.com/messages/C643EDCP2/ \"#feat-canvas Slack channel\"\r\n[student_walkthrough_screencast]: https://www.screencast.com/t/gLpMVztT35uL \"Student walkthrough screencast\"\r\n[teacher_walkthrough_screencast]: https://www.screencast.com/t/u7g1gv5sm \"Teacher walkthrough screencast\"\r\n[canvas_google_drive_folder]: https://drive.google.com/drive/u/1/folders/0Bww2MCTPhg08eUJsbVVIOTd1am8 \"Canvas folder on Google Drive\"\r\n[instructure_canvas_instance]: https://hypothesis.instructure.com/ \"Our Instructure-hosted Canvas instance\"\r\n\r\n## Problem\r\n\r\nTeachers using the Canvas app commonly create a private Hypothesis group for each Canvas course (or for large courses, maybe one Hypothesis group for each section or group within the course - this isn't 100% clear to me yet). Currently the teacher needs to:\r\n\r\n1. Create a Hypothesis private group, including creating a unique name for the group (Hypothesis group names don't strictly need to be unique but being a member of multiple groups with the same name would be confusing)\r\n2. Invite all of the students to join the group, by sharing the group link with them\r\n3. Make sure that all the students have successfully joined the group\r\n\r\nStudents can't begin a group-based Hypothesis annotation assignment in Canvas until they've joined the relevant Hypothesis group.\r\n\r\nAfter successfully joining the Hypothesis group, on launching Hypothesis within the Canvas exercise, each student must select the correct group from the groups dropdown menu in Hypothesis before they begin annotating. Since you can't move annotations between groups in Hypothesis, if students create (possibly many) annotations in the wrong group they need to delete them and manually recreate them one by one. The _Public_ group (or other most recently used group) will be the default selected group, not the one created for the course.\r\n\r\n## Dependencies\r\n\r\n* This issue is blocked by <https://github.com/hypothesis/product-backlog/issues/340> and <https://github.com/hypothesis/product-backlog/issues/339> - we can't implement improvements to the Canvas app until we've at least made significant progress on bringing the Canvas app up to production quality, and we can't deploy improvements to the Canvas app until we've migrated it onto our production infrastructure.\r\n\r\n* This issue may be blocked by https://github.com/hypothesis/product-backlog/issues/342. We probably need to be able to create third-party accounts for Canvas users and log them in, before we can create third-party Canvas groups. It's probably possible to make at least some progress on this groups issue without having sorted the accounts issue first, though.\r\n\r\n## Rejected solution: publisher group\r\n\r\nOne possible solution would be to use publisher groups, as we do for publisher sites such as eLife: there is one big publisher group for each publisher, and whenever Hypothesis is launched on a publisher's site it's locked to this publisher group.\r\n\r\nPublisher groups don't seem appropriate for the Canvas use case because rather than one small private group for each course, you would get one big public group for all of Canvas.\r\n\r\nFor example if one class had annotated a document and then later another class (perhaps taking the same course in a later semester, or perhaps a class from a completely different school) tried to annotate the same document for their own assignment they would find the first class's annotations already on the document and viewable to them. Two classes simultaneously annotating the same document in different course or schools would see each other's annotations appearing in real time. All Canvas users would be in one big group.\r\n\r\nIf we implement a publisher group for Canvas and then later implement a better solution, e.g. per-course groups, then what would we do with the existing Canvas publisher group? It would contain students annotations, which they may still want access to.\r\n\r\nThere are variations on the publisher group approach that might be possible with more work, but these don't seem a great fit either. For example separate publisher groups for each Canvas instance, or each installation of our Canvas app.\r\n\r\n## Suggested solution\r\n\r\nWhen Hypothesis is launched within Canvas:\r\n\r\n1. The Canvas app should create a private group, within the Canvas authority, for the Canvas course that the Canvas app is currently being used in, if such a group doesn't already exist.\r\n\r\n2. The Canvas user (student or teacher) who has launched the Canvas app should automatically be a member of the group.\r\n\r\n3. The Hypothesis client should be locked to this \"course group\" in the groups dropdown menu.\r\n\r\n## Questions\r\n\r\n* When the Canvas app is launched within an assignment within a course within Canvas, what information can we get from Canvas about that course?\r\n\r\n* How can we generate a name for the course group?\r\n\r\n* How can we uniquely and robustly link a Hypothesis group to a Canvas course? (Probably requires a new optional field on Hypothesis groups - the Canvas course ID. Or these course <-> group links could be tracked by the Canvas app in its own database.)\r\n\r\n* How can we find the existing Hypothesis group for a Canvas course, if one already exists?\r\n\r\n* How can we make all members of the Canvas course automatically be members of the Hypothesis group?\r\n\r\n  For normal private Hypothesis groups we keep a list in the Hypothesis database of all the users who're members of the group. You have to manually join the group to get your name added to this list.\r\n\r\n  For the Public group we don't keep any such list - everyone can read the Public group and anyone with a first-party account can write it.\r\n\r\n  For publisher groups we don't keep a list of members in the db either - anyone can read the publisher group, and any user account with the same authority as the publisher group can write it. So only eLife third-party accounts can write the eLife publisher group.\r\n\r\n  For Canvas we probably don't want to keep lists of members of groups in our database either - we'd have to get the list of all members of the course from Canvas, and keep it in sync whenever users join or leave the Canvas course. But the publisher groups solution - all Canvas users would be able to read and write all Canvas groups - doesn't seem right either. We probably want to piggy back on the fact that we know the Canvas user has access to the Canvas course because they've launched the Canvas app within the course, and maybe have the Canvas app pass something to the Hypothesis client (which passes it to the web service) that identifies the user's HTTP requests as having access to the group (but this has problems with direct links, see below).\r\n\r\n* How can we make share / direct links work?\r\n\r\n  With normal private groups, when a user clicks on a direct link, they either see a \"You're not authorized to see that annotation\" message in the client, or they see the annotation in context, depending on whether they're a member of the group. As mentioned above we may not want to keep a list of the members of a Canvas group in the DB, we may want to piggy back on the fact that we know the Canvas user has access to the Canvas course because they've launched the Canvas app within the course, but if we do that then how will we get direct links, which launch the client outside of Canvas, to work?\r\n\r\n  Simply hiding the share buttons in the client when in Canvas is one solution, at least for v1."},"comment":{"url":"https://api.github.com/repos/hypothesis/product-backlog/issues/comments/371500990","html_url":"https://github.com/hypothesis/product-backlog/issues/341#issuecomment-371500990","issue_url":"https://api.github.com/repos/hypothesis/product-backlog/issues/341","id":371500990,"user":{"login":"jeremydean","id":3261902,"avatar_url":"https://avatars0.githubusercontent.com/u/3261902?v=4","gravatar_id":"","url":"https://api.github.com/users/jeremydean","html_url":"https://github.com/jeremydean","followers_url":"https://api.github.com/users/jeremydean/followers","following_url":"https://api.github.com/users/jeremydean/following{/other_user}","gists_url":"https://api.github.com/users/jeremydean/gists{/gist_id}","starred_url":"https://api.github.com/users/jeremydean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeremydean/subscriptions","organizations_url":"https://api.github.com/users/jeremydean/orgs","repos_url":"https://api.github.com/users/jeremydean/repos","events_url":"https://api.github.com/users/jeremydean/events{/privacy}","received_events_url":"https://api.github.com/users/jeremydean/received_events","type":"User","site_admin":false},"created_at":"2018-03-08T14:23:54Z","updated_at":"2018-03-08T14:23:54Z","author_association":"NONE","body":"@seanh looking back on this after what i've learned in the interim, i think this is still close to the right solution. wondering if this architecture is possible, though:\r\n\r\n- each school's LMS is given its own _instance_ (hypothesis.instructure.com, for example) is given its own namespace\r\n- that instance/namespace/authority is associated with an overarching _private_ group, unlike elife which is public\r\n- private groups are generated for all courses within that broader private namespace\r\n\r\nso, essentially, we have a private group with private groups within it."}}
{"actor":{"display_login":"jeremydean"}}