From ef9d97c234b2d609936c01834a62e399559b3b7a Mon Sep 17 00:00:00 2001 From: Steven Lageveen Date: Wed, 23 Oct 2024 08:54:51 -0400 Subject: [PATCH] Added url to lms for assignments and discussions --- .../SelectAssignments.tsx | 1 + .../edit-tile-discussions.tsx | 3 +- .../atoms/edit-tile-goals/edit-tile-goals.tsx | 3 +- .../atoms/entry-details/entry-details.tsx | 51 +++++++------------ .../crystals/entry-view/entry-view.tsx | 22 +++++++- IguideME.Web/Frontend/src/types/tile.ts | 3 ++ IguideME.Web/IguideME.Web.csproj | 2 +- IguideME.Web/Models/App/AppAssignment.cs | 16 ++++-- IguideME.Web/Models/App/AppDiscussion.cs | 7 +++ IguideME.Web/Models/Tile.cs | 6 ++- .../Services/Constants/DatabaseQueries.cs | 28 +++++++--- IguideME.Web/Services/DatabaseManager.cs | 41 +++++++++------ .../LMSHandlers/BrightspaceHandler.cs | 1 + .../Services/LMSHandlers/CanvasHandler.cs | 17 ++++--- .../Services/Workers/AssignmentWorker.cs | 1 + 15 files changed, 131 insertions(+), 71 deletions(-) diff --git a/IguideME.Web/Frontend/src/components/atoms/edit-tile-assignments/SelectAssignments.tsx b/IguideME.Web/Frontend/src/components/atoms/edit-tile-assignments/SelectAssignments.tsx index 50149c41..1b2153cf 100644 --- a/IguideME.Web/Frontend/src/components/atoms/edit-tile-assignments/SelectAssignments.tsx +++ b/IguideME.Web/Frontend/src/components/atoms/edit-tile-assignments/SelectAssignments.tsx @@ -64,6 +64,7 @@ const SelectAssignments: FC = ({ value: entries, onChang const ass = assignments.get(id); return { title: ass ? ass.title : 'No title found', + html_url: ass ? ass.html_url : '', tile_id: -1, // Set the correct id on the backend weight: 0, content_id: id, diff --git a/IguideME.Web/Frontend/src/components/atoms/edit-tile-discussions/edit-tile-discussions.tsx b/IguideME.Web/Frontend/src/components/atoms/edit-tile-discussions/edit-tile-discussions.tsx index 25f1e9b9..a2784802 100644 --- a/IguideME.Web/Frontend/src/components/atoms/edit-tile-discussions/edit-tile-discussions.tsx +++ b/IguideME.Web/Frontend/src/components/atoms/edit-tile-discussions/edit-tile-discussions.tsx @@ -72,6 +72,7 @@ const DiscussionSelect: FC = ({ const top = topics.find((disc) => disc.id === id); return { title: top ? top.title : 'No title found', + html_url: top ? top.html_url : '', tile_id: -1, // Set the correct id on the backend weight: 0, content_id: id, @@ -142,7 +143,7 @@ const DiscussionSelect: FC = ({ /> = ({ entry }): ReactElement => { switch (viewType) { case 'graph': return ( -
-

{entry.title}

-
- -
+
+
); case 'grid': return ( -
-

- {entry.title} -

-
-
- -
-
- - -
+
+
+ +
+
+ +
); @@ -98,25 +90,19 @@ export const DiscussionDetail: FC = ({ entry }): ReactElement => { switch (viewType) { case 'graph': return ( -
-

{entry.title}

-
- -
+
+
); case 'grid': return ( -
-

{entry.title}

-
-
- -
-
- - -
+
+
+ +
+
+ +
); @@ -158,7 +144,6 @@ export const LearningGoalDetail: FC = ({ entry }): ReactElement => { return (
-

{entry.title}

{learningGoal.results?.every((b) => b) ? Passed diff --git a/IguideME.Web/Frontend/src/components/crystals/entry-view/entry-view.tsx b/IguideME.Web/Frontend/src/components/crystals/entry-view/entry-view.tsx index 52fac8af..dd982228 100644 --- a/IguideME.Web/Frontend/src/components/crystals/entry-view/entry-view.tsx +++ b/IguideME.Web/Frontend/src/components/crystals/entry-view/entry-view.tsx @@ -1,5 +1,6 @@ import { AssignmentDetail, DiscussionDetail, LearningGoalDetail } from '@/components/atoms/entry-details/entry-details'; import { TileType, type TileEntry } from '@/types/tile'; +import { ExportOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { type FC, type ReactElement } from 'react'; interface Props { @@ -8,8 +9,25 @@ interface Props { } const EntryView: FC = ({ entry, type }): ReactElement => { return ( -
- {renderViewType()} +
+
+
+
{false && }
+
+

+ {entry.title} +

+
+
+ {entry.html_url ? + + + + : ''} +
+
+ {renderViewType()} +
); diff --git a/IguideME.Web/Frontend/src/types/tile.ts b/IguideME.Web/Frontend/src/types/tile.ts index 23e8c08c..49edbdfa 100644 --- a/IguideME.Web/Frontend/src/types/tile.ts +++ b/IguideME.Web/Frontend/src/types/tile.ts @@ -39,6 +39,7 @@ export enum TileType { export interface TileEntry { tile_id: number; title: string; + html_url: string; weight: number; content_id: number; } @@ -53,6 +54,7 @@ export interface Assignment { id: number; course_id: number; title: string; + html_url: string; published: PublilshedStatus; muted: boolean; due_date: number; @@ -66,6 +68,7 @@ export interface DiscussionTopic { course_id: number; title: string; author: string; + html_url: string; date: number; message: string; grades?: Grades; diff --git a/IguideME.Web/IguideME.Web.csproj b/IguideME.Web/IguideME.Web.csproj index 9e839d4d..9bf383c6 100644 --- a/IguideME.Web/IguideME.Web.csproj +++ b/IguideME.Web/IguideME.Web.csproj @@ -9,7 +9,7 @@ - + diff --git a/IguideME.Web/Models/App/AppAssignment.cs b/IguideME.Web/Models/App/AppAssignment.cs index af117493..626b5e6e 100644 --- a/IguideME.Web/Models/App/AppAssignment.cs +++ b/IguideME.Web/Models/App/AppAssignment.cs @@ -3,7 +3,8 @@ namespace IguideME.Web.Models.App { - public enum PublishStatus { + public enum PublishStatus + { NotPublished, LMSPublished, ExternalData @@ -23,6 +24,9 @@ public class AppAssignment [JsonProperty("external_id")] public int? ExternalID { get; set; } + [JsonProperty("html_url")] + public string HtmlUrl { get; set; } + [JsonProperty("published")] public PublishStatus Published { get; set; } @@ -43,6 +47,7 @@ public AppAssignment( int id, int courseID, string title, + string url, int? externalID, PublishStatus published, bool muted, @@ -59,12 +64,14 @@ public AppAssignment( this.DueDate = dueDate; this.MaxGrade = maxGrade; this.GradingType = gradingType; + this.HtmlUrl = url; } public AppAssignment( int id, int courseID, string title, + string url, int? externalID, int published, bool muted, @@ -75,8 +82,9 @@ public AppAssignment( this.ID = id; this.CourseID = courseID; this.Title = title; + this.HtmlUrl = url; this.ExternalID = externalID; - this.Published = (PublishStatus) published; + this.Published = (PublishStatus)published; this.Muted = muted; this.DueDate = dueDate; this.MaxGrade = maxGrade; @@ -86,6 +94,7 @@ public AppAssignment( public AppAssignment( int courseID, string title, + string url, int? externalID, int published, bool muted, @@ -96,8 +105,9 @@ public AppAssignment( this.ID = -1; this.CourseID = courseID; this.Title = title; + this.HtmlUrl = url; this.ExternalID = externalID; - this.Published = (PublishStatus) published; + this.Published = (PublishStatus)published; this.Muted = muted; this.DueDate = dueDate; this.MaxGrade = maxGrade; diff --git a/IguideME.Web/Models/App/AppDiscussion.cs b/IguideME.Web/Models/App/AppDiscussion.cs index c07ed1d6..8a9cbec9 100644 --- a/IguideME.Web/Models/App/AppDiscussion.cs +++ b/IguideME.Web/Models/App/AppDiscussion.cs @@ -23,6 +23,9 @@ public class AppDiscussionTopic [JsonProperty("author")] public string Author { get; set; } + [JsonProperty("html_url")] + public string HtmlUrl { get; set; } + [JsonProperty("date")] public long Date { get; set; } @@ -40,6 +43,7 @@ public AppDiscussionTopic( int courseID, string title, string author, + string url, long date, string message, IEnumerable entries) @@ -48,6 +52,7 @@ public AppDiscussionTopic( this.CourseID = courseID; this.Title = title; this.Author = author; + this.HtmlUrl = url; this.Date = date; this.Message = message; this.Entries = entries; @@ -58,6 +63,7 @@ public AppDiscussionTopic( int courseID, string title, string author, + string url, long date, string message, AppGrades grades) @@ -66,6 +72,7 @@ public AppDiscussionTopic( this.CourseID = courseID; this.Title = title; this.Author = author; + this.HtmlUrl = url; this.Date = date; this.Message = message; this.Grades = grades; diff --git a/IguideME.Web/Models/Tile.cs b/IguideME.Web/Models/Tile.cs index 404cedcc..4c624082 100644 --- a/IguideME.Web/Models/Tile.cs +++ b/IguideME.Web/Models/Tile.cs @@ -116,17 +116,21 @@ public class TileEntry [JsonProperty(PropertyName = "title")] public string Title { get; set; } + [JsonProperty(PropertyName = "html_url")] + public string HtmlUrl { get; set; } + [JsonProperty(PropertyName = "content_id")] public int ContentID { get; set; } [JsonProperty(PropertyName = "weight")] public double Weight { get; set; } - public TileEntry(int tileID, int ContentID, string title, double weight) + public TileEntry(int tileID, int ContentID, string title, string url, double weight) { this.TileID = tileID; this.ContentID = ContentID; this.Title = title; + this.HtmlUrl = url; this.Weight = weight; } } diff --git a/IguideME.Web/Services/Constants/DatabaseQueries.cs b/IguideME.Web/Services/Constants/DatabaseQueries.cs index 4dce0bec..72d6df3b 100644 --- a/IguideME.Web/Services/Constants/DatabaseQueries.cs +++ b/IguideME.Web/Services/Constants/DatabaseQueries.cs @@ -9,12 +9,13 @@ public static class DatabaseQueries public static readonly Dictionary MIGRATIONS = new() { - // { - // "001_drop_notifications", - // @" - // DROP TABLE notifications - // ;" - // }, + { + "004_add_url_columns", + @" + ALTER TABLE assignments ADD html_url STRING; + ALTER TABLE discussions ADD html_url STRING; + ;" + }, }; // //================================ Tables ================================// @@ -351,6 +352,7 @@ FOREIGN KEY(`sync_id`) REFERENCES `sync_history`(`sync_id`) `assignment_id` INTEGER PRIMARY KEY AUTOINCREMENT, `course_id` INTEGER, `title` STRING, + `html_url` STRING, `external_id` STRING DEFAULT null UNIQUE, `published` INTEGER DEFAULT 1, `muted` BOOLEAN DEFAULT false, @@ -365,6 +367,7 @@ FOREIGN KEY(`course_id`) REFERENCES `course_settings`(`course_id`) `discussion_id` INTEGER PRIMARY KEY, `course_id` INTEGER, `title` STRING, + `html_url` STRING, `author` STRING, `date` INTEGER, `message` TEXT DEFAULT NULL, @@ -586,6 +589,7 @@ ORDER BY `sync_id` ( `external_id`, `course_id`, `title`, + `html_url`, `published`, `muted`, `due_date`, @@ -595,6 +599,7 @@ ORDER BY `sync_id` @externalID, @courseID, @title, + @htmlUrl, @published, @muted, @dueDate, @@ -669,6 +674,7 @@ ORDER BY `sync_id` ( `discussion_id`, `course_id`, `title`, + `html_url`, `author`, `date`, `message`) @@ -676,6 +682,7 @@ ORDER BY `sync_id` @discussionID, @courseID, @title, + @htmlUrl, @authorName, @date, @message @@ -1143,6 +1150,11 @@ WHEN 0 THEN `assignments`.`title` WHEN 1 THEN `discussions`.`title` WHEN 2 THEN `learning_goals`.`title` END title, + CASE `tiles`.`type` + WHEN 0 THEN `assignments`.`html_url` + WHEN 1 THEN `discussions`.`html_url` + WHEN 2 THEN '' + END title, `tile_entries`.`weight` FROM `tile_entries` INNER JOIN `tiles` @@ -1208,6 +1220,7 @@ INNER JOIN `tiles` @"SELECT `assignment_id`, `course_id`, `title`, + `html_url`, `external_id`, `published`, `muted`, @@ -1222,6 +1235,7 @@ INNER JOIN `tiles` @"SELECT `assignment_id`, `course_id`, `title`, + `html_url`, `external_id`, `published`, `muted`, @@ -1237,6 +1251,7 @@ INNER JOIN `tiles` @"SELECT `discussions`.`discussion_id`, `discussions`.`title`, `discussions`.`author`, + `discussions`.`html_url`, `discussions`.`date`, `discussions`.`message` FROM `discussions` @@ -1247,6 +1262,7 @@ INNER JOIN `tiles` @"SELECT `discussions`.`discussion_id`, `discussions`.`title`, `discussions`.`author`, + `discussions`.`html_url`, `discussions`.`date`, `discussions`.`message`, (SELECT COUNT(*) FROM `discussion_entries` WHERE `discussion_entries`.`discussion_id`=@) diff --git a/IguideME.Web/Services/DatabaseManager.cs b/IguideME.Web/Services/DatabaseManager.cs index ee516927..6bbcf59e 100644 --- a/IguideME.Web/Services/DatabaseManager.cs +++ b/IguideME.Web/Services/DatabaseManager.cs @@ -31,7 +31,7 @@ private DatabaseManager(bool isDev = false) _connection_string = "Data Source=/data/IguideME2.db;Version=3;New=False;Compress=True;"; } - // DatabaseManager.s_instance.RunMigrations(); + DatabaseManager.s_instance.RunMigrations(); DatabaseManager.s_instance.CreateTables(); } @@ -569,6 +569,7 @@ public List GetExternalAssignments(int courseID) r.GetInt32(0), r.GetInt32(1), r.GetValue(2).ToString(), + "", r.GetInt32(3), r.GetInt32(4), r.GetBoolean(5), @@ -595,6 +596,7 @@ public int RegisterAssignment(AppAssignment assignment) new SQLiteParameter("externalID", assignment.ExternalID), new SQLiteParameter("courseID", assignment.CourseID), new SQLiteParameter("title", assignment.Title), + new SQLiteParameter("htmlUrl", assignment.HtmlUrl), new SQLiteParameter("published", assignment.Published), new SQLiteParameter("muted", assignment.Muted), new SQLiteParameter("dueDate", assignment.DueDate), @@ -673,6 +675,7 @@ public void RegisterDiscussion(AppDiscussionTopic discussion, long syncID) new SQLiteParameter("discussionID", discussion.ID), new SQLiteParameter("courseID", discussion.CourseID), new SQLiteParameter("title", discussion.Title), + new SQLiteParameter("htmlUrl", discussion.HtmlUrl), new SQLiteParameter("authorName", discussion.Author), new SQLiteParameter("date", discussion.Date), new SQLiteParameter("message", discussion.Message) @@ -2463,6 +2466,7 @@ public List GetAllTileEntries(int courseID) r.GetInt32(0), r.GetInt32(1), "This is another title", + "", r.GetDouble(2) ) ); @@ -3238,12 +3242,13 @@ public Dictionary GetAssignmentsMap(int courseID) r.GetInt32(0), r.GetInt32(1), r.GetValue(2).ToString(), - r.GetInt32(3), + r.GetValue(3).ToString(), r.GetInt32(4), - r.GetBoolean(5), - r.GetInt64(6), - r.GetDouble(7), - (AppGradingType)r.GetInt32(8) + r.GetInt32(5), + r.GetBoolean(6), + r.GetInt64(7), + r.GetDouble(8), + (AppGradingType)r.GetInt32(9) ); assignments.Add(r.GetInt32(0), row); } @@ -3275,12 +3280,13 @@ public AppAssignment GetAssignment(int courseID, int internalID) r.GetInt32(0), r.GetInt32(1), r.GetValue(2).ToString(), - r.GetInt32(3), + r.GetValue(3).ToString(), r.GetInt32(4), - r.GetBoolean(5), - r.GetInt64(6), - r.GetDouble(7), - (AppGradingType)r.GetInt32(8) + r.GetInt32(5), + r.GetBoolean(6), + r.GetInt64(7), + r.GetDouble(8), + (AppGradingType)r.GetInt32(9) ); } catch (Exception e) @@ -3312,8 +3318,9 @@ public List GetDiscussions(int courseID) courseID, r.GetValue(1).ToString(), r.GetValue(2).ToString(), - r.GetInt32(3), - r.GetValue(4).ToString(), + r.GetValue(3).ToString(), + r.GetInt32(4), + r.GetValue(5).ToString(), new List() { } ); discussions.Add(row); @@ -3376,8 +3383,9 @@ public AppDiscussionTopic GetTopicGradesForUser(int courseID, int contentID, str courseID, r.GetValue(1).ToString(), r.GetValue(2).ToString(), - r.GetInt32(3), - r.GetValue(4).ToString(), + r.GetValue(3).ToString(), + r.GetInt32(4), + r.GetValue(5).ToString(), new AppGrades( 100 * grade / max, 100 * comparison.Minimum / max, @@ -3554,7 +3562,8 @@ public List GetTileEntries(int tileID) r.GetInt32(0), r.GetInt32(1), r.GetValue(2).ToString(), - r.GetDouble(3) + r.GetValue(3).ToString(), + r.GetDouble(4) ) ); } diff --git a/IguideME.Web/Services/LMSHandlers/BrightspaceHandler.cs b/IguideME.Web/Services/LMSHandlers/BrightspaceHandler.cs index 3574c58e..0d8ac7d7 100644 --- a/IguideME.Web/Services/LMSHandlers/BrightspaceHandler.cs +++ b/IguideME.Web/Services/LMSHandlers/BrightspaceHandler.cs @@ -416,6 +416,7 @@ FROM grade_objects new AppAssignment( r.GetInt32(1), r.GetValue(2).ToString(), + "", r.GetInt32(0), 1, //bool published, false, //bool muted, diff --git a/IguideME.Web/Services/LMSHandlers/CanvasHandler.cs b/IguideME.Web/Services/LMSHandlers/CanvasHandler.cs index dd969395..26062186 100644 --- a/IguideME.Web/Services/LMSHandlers/CanvasHandler.cs +++ b/IguideME.Web/Services/LMSHandlers/CanvasHandler.cs @@ -56,12 +56,12 @@ public void SendMessage(string userID, string subject, string body) try { - Conversation conv = new(this.Connector) - { - Subject = subject, - Body = body, - Recipients = new string[] { recipient } - }; + Conversation conv = new(this.Connector) + { + Subject = subject, + Body = body, + Recipients = new string[] { recipient } + }; _logger.LogInformation( "Created conversation with subject: {subject}, body: {body}, recipients: {recipients}", @@ -146,6 +146,7 @@ public IEnumerable GetAssignments(int courseID) .Select(ass => new AppAssignment( courseID, ass.Name, + ass.Url, ass.ID ?? -1, ass.IsPublished ? 1 : 0, ass.IsMuted, @@ -208,13 +209,14 @@ int courseID // Graded quizzes (assignment quizzes) are also treated as assignments by canvas and will be handled accordingly. return Connector .FindCourseById(courseID) - .Quizzes.Where(quiz => quiz.Type != QuizType.Assignment) + .Quizzes.Where(quiz => quiz.Type != QuizType.Assignment) // TODO: Filter out graded survey from assignments .Select(quiz => ( new AppAssignment( courseID, quiz.Name, + quiz.Url, quiz.ID ?? -1, quiz.IsPublished ? 1 : 0, false, @@ -247,6 +249,7 @@ public IEnumerable GetDiscussions(int courseID) courseID, topic.Title, _databaseManager.GetUserIDFromName(courseID, topic.UserName), + topic.Url, topic.PostedAt.HasValue ? ((DateTimeOffset)topic.PostedAt).ToUnixTimeMilliseconds() : 0, topic.Message, topic.Entries.SelectMany(entry => diff --git a/IguideME.Web/Services/Workers/AssignmentWorker.cs b/IguideME.Web/Services/Workers/AssignmentWorker.cs index 4b4b9e1f..920a9d7e 100644 --- a/IguideME.Web/Services/Workers/AssignmentWorker.cs +++ b/IguideME.Web/Services/Workers/AssignmentWorker.cs @@ -98,6 +98,7 @@ public void Start() foreach (AppAssignment assignment in assignments) { _logger.LogInformation("Processing assignment: {Name}", assignment.Title); + // The internal id is obtained after the assignment is registered in the database. assignment.ID = _databaseManager.RegisterAssignment(assignment);