Skip to content

Commit d2b322a

Browse files
josh1248GabrielCWTRichDom2185
authored
Implement isGradingPublished (plus related features) (#2856)
* Dummy publish grading function * skeleton redux loop for publishGrading, update GradingOverview * New column filter for isPublished (WIP) * Implement anticipated backend format and route implementations for publish and unpublish buttons * Remove misleading devnote and simplify message contents * fix: Change gradingOverviews to use backend response * Readjust publish button from HTML icon button to Blueprintjs text button * Publish and unpublish buttons set up * set up frontend-only type submissionProgress derived from backend status and isPublished * Implement derived submissionProgress frontend field from status and isGradingPublished from backend * Improve type safety in Grading page * Implement business logic to disable unsubmission if published or not submitted * Use notPublished param in backend * minor type safety change * Update new actions post-merge conflicts * Update tests * Update jsdocs and field names from ungraded to unpublished * adjust field names in test from unpublishedFilter to publishedFilter * Updated showGrading to be based on published status * Update getAssessmentOverviews to compute submission progress * Fix tests * Scaffold function for comprehensive progress status (WIP) * Update RequestsSaga.ts * fix crash * Introduce soon-to-be comprehensive field into table * Shift conversion functions into utils * Update business logic using comprehensive state * Introduce progress status to assessment overviews * clean up display of progressStatus * remove derived submissionProgress field, add in status field from backend * remove direct gradingStatus use in grading page * remove gradingStatus from assessments * Remove more references to gradingStatus * Replace references to gradingStatus with progress * there is a lot of reference to gradingStatus. * Replace gradingStatus with progress in testing * Fully remove gradingStatus and replace with progress * Fix accidental import * Update to use new backend parameter names in backend * Remove isPublished field entirely from grading overviews to avoid confusion with publishing within assessment overviews * Update tests * Dummy publish all button in ground control * redux loop for publishall and unpublishall (part 1) * Remove re-autograde for published or non-submitted assessments * Add publishAll button * Replace deprecated ag-grid functions with current versions * Implement unpublish all button * Add unique key, avoid specialkey flag from react * Fix button layout * Fix button format * increase width for accessibility * autoPublish settings inserted into admin panel configs * cleanup of isAutoPublished field within assessment configs * update tests for assessmentConfiguration * remove un-needed import * remove debug-only submissionStatus in grading submissions table * Revert accidental removal of XP field * bp5-minimal tag to clean up bulk publishing buttons - thanks gabriel :) * Fix errors post-merge * Fix lint error * feat: Implement published and unpublished notifications and remove deprecated ones * chore: Remove commented code * Fix compile error * Remove unnecessary typecast * Remove unnecessary typecast * Remove unused import * update notification types to reflect backend * change notification types to reflect backend changes * remove git stash artifacts * remove git stash artefacts * add tooltips, update assessment fields * remove development artifacts * remove TODO comment as issue has been raised * Update GradingUtils.ts jsdocs * linting * readjust progress statuses and conversion functions * update colours and logos * expose backend isGradingPublished field and update tests * update assessment cards to show purely based on isGradingPublished status without further check for graded * remove hardcoded assessment status * linting * revert yarn lock change * Update yarn.lock * update mocks and tests * remove un-needed import * linting * Use enum values instead of strings for color keys * Use prop over BP CSS API * remove un-needed field calculation, fix typos * update student username to show nusnet id instead of duplicating student name (thanks gabriel :D) --------- Co-authored-by: GabrielCWT <[email protected]> Co-authored-by: Richard Dominick <[email protected]>
1 parent 4556d3f commit d2b322a

37 files changed

+769
-287
lines changed

src/commons/XMLParser/XMLParserHelper.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
AssessmentType,
1010
BaseQuestion,
1111
emptyLibrary,
12-
GradingStatuses,
1312
IMCQQuestion,
1413
IProgrammingQuestion,
1514
Library,
@@ -72,6 +71,7 @@ const makeAssessmentOverview = (result: any, maxXpVal: number): AssessmentOvervi
7271
return {
7372
type: capitalizeFirstLetter(rawOverview.kind) as AssessmentType,
7473
isManuallyGraded: true, // TODO: This is temporarily hardcoded to true. To be redone when overhauling MissionControl
74+
isPublished: false,
7575
closeAt: rawOverview.duedate,
7676
coverImage: rawOverview.coverimage,
7777
id: EDITING_ID,
@@ -84,8 +84,8 @@ const makeAssessmentOverview = (result: any, maxXpVal: number): AssessmentOvervi
8484
shortSummary: task.WEBSUMMARY ? task.WEBSUMMARY[0] : '',
8585
status: AssessmentStatuses.attempting,
8686
story: rawOverview.story,
87+
isGradingPublished: false,
8788
xp: 0,
88-
gradingStatus: 'none' as GradingStatuses,
8989
maxTeamSize: 1,
9090
hasVotingFeatures: false
9191
};

src/commons/achievement/utils/InsertFakeAchievements.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import {
55
coverImageUrl
66
} from '../../../features/achievement/AchievementConstants';
77
import { GoalType } from '../../../features/achievement/AchievementTypes';
8-
import { AssessmentConfiguration, AssessmentOverview } from '../../assessment/AssessmentTypes';
8+
import {
9+
AssessmentConfiguration,
10+
AssessmentOverview,
11+
AssessmentStatuses
12+
} from '../../assessment/AssessmentTypes';
913
import AchievementInferencer from './AchievementInferencer';
1014
import { isExpired, isReleased } from './DateHelper';
1115

12-
function assessmentCompleted(assessmentOverview: AssessmentOverview): boolean {
13-
return (
14-
assessmentOverview.gradingStatus === 'graded' ||
15-
(!assessmentOverview.isManuallyGraded && assessmentOverview.status === 'submitted')
16-
);
16+
function assessmentPublished(assessmentOverview: AssessmentOverview): boolean {
17+
return assessmentOverview.isGradingPublished;
1718
}
1819

1920
function insertFakeAchievements(
@@ -34,7 +35,8 @@ function insertFakeAchievements(
3435
// Reduce clutter for achievements that cannot be earned at that point
3536
if (
3637
!isReleased(new Date(assessmentOverview.openAt)) ||
37-
(isExpired(new Date(assessmentOverview.closeAt)) && assessmentOverview.status !== 'submitted')
38+
(isExpired(new Date(assessmentOverview.closeAt)) &&
39+
assessmentOverview.status !== AssessmentStatuses.submitted)
3840
) {
3941
return;
4042
}
@@ -53,7 +55,7 @@ function insertFakeAchievements(
5355
requiredCompletionFrac: 0
5456
}
5557
},
56-
assessmentOverview.status === 'submitted'
58+
assessmentOverview.status === AssessmentStatuses.submitted
5759
);
5860

5961
// goal for assessment grading
@@ -69,14 +71,14 @@ function insertFakeAchievements(
6971
requiredCompletionFrac: 0
7072
}
7173
},
72-
assessmentOverview.gradingStatus === 'graded'
74+
assessmentOverview.isGradingPublished
7375
);
7476
}
7577

7678
inferencer.insertFakeAchievement({
7779
uuid: idString,
7880
title: assessmentOverview.title,
79-
xp: assessmentCompleted(assessmentOverview)
81+
xp: assessmentPublished(assessmentOverview)
8082
? assessmentOverview.xp
8183
: assessmentOverview.maxXp,
8284
isVariableXp: false,
@@ -98,7 +100,7 @@ function insertFakeAchievements(
98100
});
99101

100102
// if completed, add the uuid into the appropriate array
101-
if (assessmentCompleted(assessmentOverview)) {
103+
if (assessmentPublished(assessmentOverview)) {
102104
assessmentTypes.forEach((type, idx) => {
103105
if (type === assessmentOverview.type) {
104106
categorisedUuids[idx].push(idString);

src/commons/application/actions/SessionActions.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createAction } from '@reduxjs/toolkit';
22
import {
33
paginationToBackendParams,
4-
ungradedToBackendParams
4+
unpublishedToBackendParams
55
} from 'src/features/grading/GradingUtils';
66
import { OptionType } from 'src/pages/academy/teamFormation/subcomponents/TeamFormationForm';
77

@@ -54,6 +54,7 @@ import {
5454
LOGOUT_GOOGLE,
5555
NotificationConfiguration,
5656
NotificationPreference,
57+
PUBLISH_GRADING,
5758
REAUTOGRADE_ANSWER,
5859
REAUTOGRADE_SUBMISSION,
5960
REMOVE_GITHUB_OCTOKIT_OBJECT_AND_ACCESS_TOKEN,
@@ -74,6 +75,7 @@ import {
7475
SUBMIT_GRADING_AND_CONTINUE,
7576
TimeOption,
7677
Tokens,
78+
UNPUBLISH_GRADING,
7779
UNSUBMIT_SUBMISSION,
7880
UPDATE_ASSESSMENT,
7981
UPDATE_ASSESSMENT_CONFIGS,
@@ -134,7 +136,7 @@ export const fetchGrading = createAction(FETCH_GRADING, (submissionId: number) =
134136
/**
135137
* @param filterToGroup - param that when set to true, only shows submissions under the group
136138
* of the grader
137-
* @param gradedFilter - backend params to filter to ungraded
139+
* @param publishedFilter - backend params to filter to unpublished
138140
* @param pageParams - param that contains offset and pageSize, informing backend about how
139141
* many entries, starting from what offset, to get
140142
* @param filterParams - param that contains columnFilters converted into JSON for
@@ -144,10 +146,10 @@ export const fetchGradingOverviews = createAction(
144146
FETCH_GRADING_OVERVIEWS,
145147
(
146148
filterToGroup = true,
147-
gradedFilter = ungradedToBackendParams(false),
149+
publishedFilter = unpublishedToBackendParams(false),
148150
pageParams = paginationToBackendParams(0, 10),
149151
filterParams = {}
150-
) => ({ payload: { filterToGroup, gradedFilter, pageParams, filterParams } })
152+
) => ({ payload: { filterToGroup, publishedFilter, pageParams, filterParams } })
151153
);
152154

153155
export const fetchTeamFormationOverviews = createAction(
@@ -326,6 +328,18 @@ export const unsubmitSubmission = createAction(UNSUBMIT_SUBMISSION, (submissionI
326328
payload: { submissionId }
327329
}));
328330

331+
/**
332+
* Publishing / unpublishing actions
333+
*/
334+
335+
export const publishGrading = createAction(PUBLISH_GRADING, (submissionId: number) => ({
336+
payload: { submissionId }
337+
}));
338+
339+
export const unpublishGrading = createAction(UNPUBLISH_GRADING, (submissionId: number) => ({
340+
payload: { submissionId }
341+
}));
342+
329343
/**
330344
* Notification actions
331345
*/

src/commons/application/actions/__tests__/SessionActions.ts

+40-12
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@ import { Chapter, Variant } from 'js-slang/dist/types';
22
import { mockStudents } from 'src/commons/mocks/UserMocks';
33
import {
44
paginationToBackendParams,
5-
ungradedToBackendParams
5+
unpublishedToBackendParams
66
} from 'src/features/grading/GradingUtils';
77

88
import { GradingOverviews, GradingQuery } from '../../../../features/grading/GradingTypes';
99
import { TeamFormationOverview } from '../../../../features/teamFormation/TeamFormationTypes';
10-
import { Assessment, AssessmentOverview } from '../../../assessment/AssessmentTypes';
10+
import {
11+
Assessment,
12+
AssessmentConfiguration,
13+
AssessmentOverview,
14+
AssessmentStatuses,
15+
ProgressStatuses
16+
} from '../../../assessment/AssessmentTypes';
1117
import { Notification } from '../../../notificationBadge/NotificationBadgeTypes';
1218
import { GameState, Role, Story } from '../../ApplicationTypes';
1319
import {
@@ -172,7 +178,7 @@ test('fetchGradingOverviews generates correct default action object', () => {
172178
type: FETCH_GRADING_OVERVIEWS,
173179
payload: {
174180
filterToGroup: true,
175-
gradedFilter: ungradedToBackendParams(false),
181+
publishedFilter: unpublishedToBackendParams(false),
176182
pageParams: paginationToBackendParams(0, 10),
177183
filterParams: {}
178184
}
@@ -181,15 +187,15 @@ test('fetchGradingOverviews generates correct default action object', () => {
181187

182188
test('fetchGradingOverviews generates correct action object', () => {
183189
const filterToGroup = false;
184-
const gradedFilter = ungradedToBackendParams(true);
190+
const publishedFilter = unpublishedToBackendParams(true);
185191
const pageParams = { offset: 123, pageSize: 456 };
186192
const filterParams = { abc: 'xxx', def: 'yyy' };
187-
const action = fetchGradingOverviews(filterToGroup, gradedFilter, pageParams, filterParams);
193+
const action = fetchGradingOverviews(filterToGroup, publishedFilter, pageParams, filterParams);
188194
expect(action).toEqual({
189195
type: FETCH_GRADING_OVERVIEWS,
190196
payload: {
191197
filterToGroup: filterToGroup,
192-
gradedFilter: gradedFilter,
198+
publishedFilter: publishedFilter,
193199
pageParams: pageParams,
194200
filterParams: filterParams
195201
}
@@ -328,11 +334,12 @@ test('setCourseRegistration generates correct action object', () => {
328334
});
329335

330336
test('setAssessmentConfigurations generates correct action object', () => {
331-
const assesmentConfigurations = [
337+
const assesmentConfigurations: AssessmentConfiguration[] = [
332338
{
333339
assessmentConfigId: 1,
334340
type: 'Mission1',
335341
isManuallyGraded: true,
342+
isGradingAutoPublished: false,
336343
displayInDashboard: true,
337344
hasTokenCounter: false,
338345
hasVotingFeatures: false,
@@ -343,6 +350,7 @@ test('setAssessmentConfigurations generates correct action object', () => {
343350
assessmentConfigId: 2,
344351
type: 'Mission2',
345352
isManuallyGraded: true,
353+
isGradingAutoPublished: false,
346354
displayInDashboard: true,
347355
hasTokenCounter: false,
348356
hasVotingFeatures: false,
@@ -353,6 +361,7 @@ test('setAssessmentConfigurations generates correct action object', () => {
353361
assessmentConfigId: 3,
354362
type: 'Mission3',
355363
isManuallyGraded: true,
364+
isGradingAutoPublished: false,
356365
displayInDashboard: true,
357366
hasTokenCounter: false,
358367
hasVotingFeatures: false,
@@ -533,6 +542,7 @@ test('updateAssessmentOverviews generates correct action object', () => {
533542
{
534543
type: 'Missions',
535544
isManuallyGraded: true,
545+
isPublished: false,
536546
closeAt: 'test_string',
537547
coverImage: 'test_string',
538548
id: 0,
@@ -541,10 +551,10 @@ test('updateAssessmentOverviews generates correct action object', () => {
541551
openAt: 'test_string',
542552
title: 'test_string',
543553
shortSummary: 'test_string',
544-
status: 'not_attempted',
554+
status: AssessmentStatuses.not_attempted,
545555
story: null,
546556
xp: 0,
547-
gradingStatus: 'none',
557+
isGradingPublished: false,
548558
maxTeamSize: 1,
549559
hasVotingFeatures: false
550560
}
@@ -595,9 +605,10 @@ test('updateGradingOverviews generates correct action object', () => {
595605
studentUsername: 'E0123456',
596606
studentUsernames: [],
597607
submissionId: 1,
598-
submissionStatus: 'attempting',
608+
isGradingPublished: false,
609+
progress: ProgressStatuses.attempting,
599610
groupName: 'group',
600-
gradingStatus: 'excluded',
611+
submissionStatus: AssessmentStatuses.attempting,
601612
questionCount: 6,
602613
gradedCount: 0
603614
}
@@ -772,6 +783,7 @@ test('updateAssessmentTypes generates correct action object', () => {
772783
assessmentConfigId: 1,
773784
type: 'Missions',
774785
isManuallyGraded: true,
786+
isGradingAutoPublished: false,
775787
displayInDashboard: true,
776788
hasTokenCounter: false,
777789
hasVotingFeatures: false,
@@ -782,6 +794,7 @@ test('updateAssessmentTypes generates correct action object', () => {
782794
assessmentConfigId: 2,
783795
type: 'Quests',
784796
isManuallyGraded: true,
797+
isGradingAutoPublished: false,
785798
displayInDashboard: true,
786799
hasTokenCounter: false,
787800
hasVotingFeatures: false,
@@ -791,7 +804,8 @@ test('updateAssessmentTypes generates correct action object', () => {
791804
{
792805
assessmentConfigId: 3,
793806
type: 'Paths',
794-
isManuallyGraded: true,
807+
isManuallyGraded: false,
808+
isGradingAutoPublished: true,
795809
displayInDashboard: true,
796810
hasTokenCounter: false,
797811
hasVotingFeatures: false,
@@ -802,6 +816,7 @@ test('updateAssessmentTypes generates correct action object', () => {
802816
assessmentConfigId: 4,
803817
type: 'Contests',
804818
isManuallyGraded: true,
819+
isGradingAutoPublished: false,
805820
displayInDashboard: true,
806821
hasTokenCounter: false,
807822
hasVotingFeatures: false,
@@ -810,8 +825,20 @@ test('updateAssessmentTypes generates correct action object', () => {
810825
},
811826
{
812827
assessmentConfigId: 5,
828+
type: 'PEs',
829+
isManuallyGraded: false,
830+
isGradingAutoPublished: false,
831+
displayInDashboard: true,
832+
hasTokenCounter: false,
833+
hasVotingFeatures: false,
834+
hoursBeforeEarlyXpDecay: 0,
835+
earlySubmissionXp: 0
836+
},
837+
{
838+
assessmentConfigId: 6,
813839
type: 'Others',
814840
isManuallyGraded: true,
841+
isGradingAutoPublished: false,
815842
displayInDashboard: true,
816843
hasTokenCounter: false,
817844
hasVotingFeatures: false,
@@ -831,6 +858,7 @@ test('deleteAssessmentConfig generates correct action object', () => {
831858
assessmentConfigId: 1,
832859
type: 'Mission1',
833860
isManuallyGraded: true,
861+
isGradingAutoPublished: false,
834862
displayInDashboard: true,
835863
hasTokenCounter: false,
836864
hasVotingFeatures: false,

0 commit comments

Comments
 (0)