-
Notifications
You must be signed in to change notification settings - Fork 0
Added .sql to create user_activity view. #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| -- user_activity view — canvas database (primary Canvas instance) | ||
| -- | ||
| -- In CD2, everything is identified by user_id (as in, Canvas ID), so there will be | ||
| -- unique activity per user_id. We can't assume that a single user_id will map to | ||
| -- a single integration_id (PUID) or sis_user_id (student number), however. | ||
| -- We have not 'squished' any of these into single lines, so, e.g., if one user_id | ||
| -- maps to two integration_ids, there will be two rows with the same activity. | ||
| -- In other words, there will be one row per unique user_id x integration_id | ||
| -- x sis_user_id (x workflow_state) combination. | ||
| -- | ||
| -- Almost every user_id has two pseudonyms entries, one with an integration_id etc., | ||
| -- and one that is quite empty. We have filtered out any pseudonyms with no integration_id, | ||
| -- as these lines will be useless when only searching on integration_id. | ||
| -- But *all* non-null integration_ids have been kept in, even those with atypical | ||
| -- formats, such as 12345ABCD_INC12345. | ||
| -- | ||
| -- Note there's also no filtering here based on the workflow_state of the entry in | ||
| -- pseudonyms or users, though both can be, e.g., 'deleted'. | ||
| -- | ||
| -- From Claude: | ||
| -- Run against the "canvas" CD2 database, connected as the CD2 database owner | ||
| -- (the db user created for this database in setup/prepare_aurora_db.py), so | ||
| -- pg-deps-management's view-dependency hooks pick up the view as owned by a | ||
| -- role sync_table already knows how to drop/recreate on schema change. | ||
|
|
||
| CREATE OR REPLACE VIEW canvas.user_activity AS | ||
| -- I don't actually expect much, if any, aggregation to happen here, | ||
| -- as I haven't seen multiple pseudonyms with the same user_id, integration_id, | ||
| -- sis_user_id, AND workflow_state. The point of this subquery is to | ||
| -- pair up user_ids (which will have all the associated CD2 activity) | ||
| -- with any/all integration_ids linked (the same is happening with sis_user_ids | ||
| -- and workflow_states, but those are not the main goal). | ||
| WITH pseudonym_agg AS ( | ||
| SELECT | ||
| user_id, | ||
| integration_id, | ||
| sis_user_id, | ||
| workflow_state, | ||
| MAX(last_login_at) AS last_login_at, | ||
| MAX(last_request_at) AS last_request_at, | ||
|
Comment on lines
+39
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe use a
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Totally fair, but I think we could just use
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good |
||
| SUM(login_count) AS login_count | ||
| FROM canvas.pseudonyms | ||
| WHERE integration_id IS NOT NULL | ||
| GROUP BY user_id, integration_id, sis_user_id, workflow_state | ||
| ), | ||
| enrollment_agg AS ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about including SUM(total_activity_time) for enrollments? I'm not sure how accurate it is, but it adds another dimension that they can use when reviewing what accounts are being used? |
||
| SELECT | ||
| user_id, | ||
| COUNT(*) AS enrolment_count_total, | ||
| -- active enrolments != classes currently being taken | ||
| -- I believe it's all enrolments, past and present, that they student didn't drop | ||
| -- or otherwise get removed from. I believe completed courses are active. | ||
| COUNT(*) FILTER (WHERE workflow_state = 'active') AS enrolment_count_active | ||
| FROM canvas.enrollments | ||
| GROUP BY user_id | ||
| ), | ||
| submission_agg AS ( | ||
| SELECT | ||
| user_id, | ||
| COUNT(*) AS submission_count, | ||
| MAX(submitted_at) AS last_submission_at | ||
| FROM canvas.submissions | ||
| WHERE submitted_at IS NOT NULL | ||
| AND workflow_state IN ('submitted', 'graded') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. include |
||
| GROUP BY user_id | ||
| ), | ||
|
|
||
| -- Note that quiz_submissions is basically a subset of submissions: | ||
| -- all quiz_submission entries should have a corresponding entry in submissions. | ||
| -- I say 'basically' because I've seen different created_at dates for | ||
| -- the 'same' entry -- probably they are created, empty, before the student | ||
| -- actually does anything, and those times are not necessarily the same, | ||
| -- meaning one *can* exist without the other. | ||
|
Comment on lines
+67
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got an error when trying to run the query with this comment here. Move the comment inside the table expression. |
||
| quiz_submission_agg AS ( | ||
| SELECT | ||
| user_id, | ||
| COUNT(*) AS quiz_submission_count, | ||
| MAX(finished_at) AS last_quiz_submission_at | ||
| FROM canvas.quiz_submissions | ||
| WHERE workflow_state = 'complete' | ||
| GROUP BY user_id | ||
| ), | ||
| discussion_entry_agg AS ( | ||
| SELECT | ||
| user_id, | ||
| COUNT(*) AS discussion_entry_count, | ||
| MAX(created_at) AS last_discussion_entry_at | ||
| FROM canvas.discussion_entries | ||
| WHERE workflow_state = 'active' | ||
| GROUP BY user_id | ||
| ), | ||
| conversation_message_agg AS ( | ||
| SELECT | ||
| author_id AS user_id, | ||
| COUNT(*) AS conversation_message_count, | ||
| MAX(created_at) AS last_conversation_message_at | ||
| FROM canvas.conversation_messages | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe take a look at what messages look like when they're system generated? eg |
||
| -- messages don't have workflow_states | ||
| GROUP BY author_id | ||
| ) | ||
| SELECT | ||
| pa.integration_id AS sis_integration_id, | ||
| pa.sis_user_id, | ||
| pa.user_id, | ||
| pa.workflow_state AS pseudonym_workflow_state, | ||
| u.merged_into_user_id, | ||
| pa.last_login_at, | ||
| pa.last_request_at, | ||
| COALESCE(pa.login_count, 0) AS login_count, | ||
| COALESCE(ea.enrolment_count_active, 0) AS enrolment_count_active, | ||
| COALESCE(ea.enrolment_count_total, 0) AS enrolment_count_total, | ||
| COALESCE(sa.submission_count, 0) AS submission_count, | ||
| sa.last_submission_at, | ||
| COALESCE(qa.quiz_submission_count, 0) AS quiz_submission_count, | ||
| qa.last_quiz_submission_at, | ||
| COALESCE(da.discussion_entry_count, 0) AS discussion_entry_count, | ||
| da.last_discussion_entry_at, | ||
| COALESCE(ca.conversation_message_count, 0) AS conversation_message_count, | ||
| ca.last_conversation_message_at | ||
| FROM pseudonym_agg pa | ||
| LEFT JOIN canvas.users u ON u.id = pa.user_id | ||
| LEFT JOIN enrollment_agg ea ON ea.user_id = pa.user_id | ||
| LEFT JOIN submission_agg sa ON sa.user_id = pa.user_id | ||
| LEFT JOIN quiz_submission_agg qa ON qa.user_id = pa.user_id | ||
| LEFT JOIN discussion_entry_agg da ON da.user_id = pa.user_id | ||
| LEFT JOIN conversation_message_agg ca ON ca.user_id = pa.user_id; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ORDER BY sis_integration_id | ||
|
|
||
| -- If running against catalog instead, change athena to athena_catalog | ||
| GRANT SELECT ON canvas.user_activity TO athena; | ||
| GRANT SELECT ON canvas.user_activity TO ubc_it_easd_cache | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| Hi EASD folks, | ||
|
|
||
| The view has been created. Here are a couple notes on what the data covers: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what we should provide to them is a copy of the view DDL plus a detailed description. I asked Claude for a draft. I'd interleave your observations/experience into something like this. Data dictionary —
|
||
| - This view has one row per unique sis_integration_id (PUID) x sis_user_id (student number) x user_id (Canvas user ID) x pseudonyms_workflow_state combination. The latter is the status of the entry in our CD2 pseudonyms table. | ||
| - As we anticipate your searching to be by PUID, we have filtered to only rows with non-null PUIDs. All other fields may be null. | ||
| - The view name is user_activity, and the role name is ubc_it_easd_cache. | ||
| - The stats presented per user cover: | ||
| - logins and requests | ||
| - enrolments | ||
| - Note that 'active' enrolments are not just the student's enrolments for the current semester, but generally represent all enrolments, past and present, where the student did not drop the course or was otherwise removed. | ||
| - submissions | ||
| - This covers assignments, quizzes, etc. | ||
| - quiz_submissions | ||
| - This should be a subset of the above. This may not cover everything that we might think of as quizzes, as Canvas has 'Classic' and 'New' quizzes and typically only counts the former as quizzes. But all of these will be included in submissions above. | ||
| - discussion_entries | ||
| - conversation_messages | ||
| - This covers direct messages sent via Canvas's messaging system, which are often announcements sent from the instructor to all students. | ||
|
|
||
| Don't hesitate to reach out if there's anything else I can clarify about the data! Best, | ||
| Anna | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A common convention is to name views with something that indicates that it's a view since the consumer may not know they're working against a view.
Craig has used
_vwsuffix on his views. Let's name thiscanvas.user_activity_vw