Skip to content

Commit

Permalink
Implement isGradingPublished (plus related features) (#2856)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Apr 13, 2024
1 parent 4556d3f commit d2b322a
Show file tree
Hide file tree
Showing 37 changed files with 769 additions and 287 deletions.
4 changes: 2 additions & 2 deletions src/commons/XMLParser/XMLParserHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
AssessmentType,
BaseQuestion,
emptyLibrary,
GradingStatuses,
IMCQQuestion,
IProgrammingQuestion,
Library,
Expand Down Expand Up @@ -72,6 +71,7 @@ const makeAssessmentOverview = (result: any, maxXpVal: number): AssessmentOvervi
return {
type: capitalizeFirstLetter(rawOverview.kind) as AssessmentType,
isManuallyGraded: true, // TODO: This is temporarily hardcoded to true. To be redone when overhauling MissionControl
isPublished: false,
closeAt: rawOverview.duedate,
coverImage: rawOverview.coverimage,
id: EDITING_ID,
Expand All @@ -84,8 +84,8 @@ const makeAssessmentOverview = (result: any, maxXpVal: number): AssessmentOvervi
shortSummary: task.WEBSUMMARY ? task.WEBSUMMARY[0] : '',
status: AssessmentStatuses.attempting,
story: rawOverview.story,
isGradingPublished: false,
xp: 0,
gradingStatus: 'none' as GradingStatuses,
maxTeamSize: 1,
hasVotingFeatures: false
};
Expand Down
24 changes: 13 additions & 11 deletions src/commons/achievement/utils/InsertFakeAchievements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import {
coverImageUrl
} from '../../../features/achievement/AchievementConstants';
import { GoalType } from '../../../features/achievement/AchievementTypes';
import { AssessmentConfiguration, AssessmentOverview } from '../../assessment/AssessmentTypes';
import {
AssessmentConfiguration,
AssessmentOverview,
AssessmentStatuses
} from '../../assessment/AssessmentTypes';
import AchievementInferencer from './AchievementInferencer';
import { isExpired, isReleased } from './DateHelper';

function assessmentCompleted(assessmentOverview: AssessmentOverview): boolean {
return (
assessmentOverview.gradingStatus === 'graded' ||
(!assessmentOverview.isManuallyGraded && assessmentOverview.status === 'submitted')
);
function assessmentPublished(assessmentOverview: AssessmentOverview): boolean {
return assessmentOverview.isGradingPublished;
}

function insertFakeAchievements(
Expand All @@ -34,7 +35,8 @@ function insertFakeAchievements(
// Reduce clutter for achievements that cannot be earned at that point
if (
!isReleased(new Date(assessmentOverview.openAt)) ||
(isExpired(new Date(assessmentOverview.closeAt)) && assessmentOverview.status !== 'submitted')
(isExpired(new Date(assessmentOverview.closeAt)) &&
assessmentOverview.status !== AssessmentStatuses.submitted)
) {
return;
}
Expand All @@ -53,7 +55,7 @@ function insertFakeAchievements(
requiredCompletionFrac: 0
}
},
assessmentOverview.status === 'submitted'
assessmentOverview.status === AssessmentStatuses.submitted
);

// goal for assessment grading
Expand All @@ -69,14 +71,14 @@ function insertFakeAchievements(
requiredCompletionFrac: 0
}
},
assessmentOverview.gradingStatus === 'graded'
assessmentOverview.isGradingPublished
);
}

inferencer.insertFakeAchievement({
uuid: idString,
title: assessmentOverview.title,
xp: assessmentCompleted(assessmentOverview)
xp: assessmentPublished(assessmentOverview)
? assessmentOverview.xp
: assessmentOverview.maxXp,
isVariableXp: false,
Expand All @@ -98,7 +100,7 @@ function insertFakeAchievements(
});

// if completed, add the uuid into the appropriate array
if (assessmentCompleted(assessmentOverview)) {
if (assessmentPublished(assessmentOverview)) {
assessmentTypes.forEach((type, idx) => {
if (type === assessmentOverview.type) {
categorisedUuids[idx].push(idString);
Expand Down
22 changes: 18 additions & 4 deletions src/commons/application/actions/SessionActions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createAction } from '@reduxjs/toolkit';
import {
paginationToBackendParams,
ungradedToBackendParams
unpublishedToBackendParams
} from 'src/features/grading/GradingUtils';
import { OptionType } from 'src/pages/academy/teamFormation/subcomponents/TeamFormationForm';

Expand Down Expand Up @@ -54,6 +54,7 @@ import {
LOGOUT_GOOGLE,
NotificationConfiguration,
NotificationPreference,
PUBLISH_GRADING,
REAUTOGRADE_ANSWER,
REAUTOGRADE_SUBMISSION,
REMOVE_GITHUB_OCTOKIT_OBJECT_AND_ACCESS_TOKEN,
Expand All @@ -74,6 +75,7 @@ import {
SUBMIT_GRADING_AND_CONTINUE,
TimeOption,
Tokens,
UNPUBLISH_GRADING,
UNSUBMIT_SUBMISSION,
UPDATE_ASSESSMENT,
UPDATE_ASSESSMENT_CONFIGS,
Expand Down Expand Up @@ -134,7 +136,7 @@ export const fetchGrading = createAction(FETCH_GRADING, (submissionId: number) =
/**
* @param filterToGroup - param that when set to true, only shows submissions under the group
* of the grader
* @param gradedFilter - backend params to filter to ungraded
* @param publishedFilter - backend params to filter to unpublished
* @param pageParams - param that contains offset and pageSize, informing backend about how
* many entries, starting from what offset, to get
* @param filterParams - param that contains columnFilters converted into JSON for
Expand All @@ -144,10 +146,10 @@ export const fetchGradingOverviews = createAction(
FETCH_GRADING_OVERVIEWS,
(
filterToGroup = true,
gradedFilter = ungradedToBackendParams(false),
publishedFilter = unpublishedToBackendParams(false),
pageParams = paginationToBackendParams(0, 10),
filterParams = {}
) => ({ payload: { filterToGroup, gradedFilter, pageParams, filterParams } })
) => ({ payload: { filterToGroup, publishedFilter, pageParams, filterParams } })
);

export const fetchTeamFormationOverviews = createAction(
Expand Down Expand Up @@ -326,6 +328,18 @@ export const unsubmitSubmission = createAction(UNSUBMIT_SUBMISSION, (submissionI
payload: { submissionId }
}));

/**
* Publishing / unpublishing actions
*/

export const publishGrading = createAction(PUBLISH_GRADING, (submissionId: number) => ({
payload: { submissionId }
}));

export const unpublishGrading = createAction(UNPUBLISH_GRADING, (submissionId: number) => ({
payload: { submissionId }
}));

/**
* Notification actions
*/
Expand Down
52 changes: 40 additions & 12 deletions src/commons/application/actions/__tests__/SessionActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import { Chapter, Variant } from 'js-slang/dist/types';
import { mockStudents } from 'src/commons/mocks/UserMocks';
import {
paginationToBackendParams,
ungradedToBackendParams
unpublishedToBackendParams
} from 'src/features/grading/GradingUtils';

import { GradingOverviews, GradingQuery } from '../../../../features/grading/GradingTypes';
import { TeamFormationOverview } from '../../../../features/teamFormation/TeamFormationTypes';
import { Assessment, AssessmentOverview } from '../../../assessment/AssessmentTypes';
import {
Assessment,
AssessmentConfiguration,
AssessmentOverview,
AssessmentStatuses,
ProgressStatuses
} from '../../../assessment/AssessmentTypes';
import { Notification } from '../../../notificationBadge/NotificationBadgeTypes';
import { GameState, Role, Story } from '../../ApplicationTypes';
import {
Expand Down Expand Up @@ -172,7 +178,7 @@ test('fetchGradingOverviews generates correct default action object', () => {
type: FETCH_GRADING_OVERVIEWS,
payload: {
filterToGroup: true,
gradedFilter: ungradedToBackendParams(false),
publishedFilter: unpublishedToBackendParams(false),
pageParams: paginationToBackendParams(0, 10),
filterParams: {}
}
Expand All @@ -181,15 +187,15 @@ test('fetchGradingOverviews generates correct default action object', () => {

test('fetchGradingOverviews generates correct action object', () => {
const filterToGroup = false;
const gradedFilter = ungradedToBackendParams(true);
const publishedFilter = unpublishedToBackendParams(true);
const pageParams = { offset: 123, pageSize: 456 };
const filterParams = { abc: 'xxx', def: 'yyy' };
const action = fetchGradingOverviews(filterToGroup, gradedFilter, pageParams, filterParams);
const action = fetchGradingOverviews(filterToGroup, publishedFilter, pageParams, filterParams);
expect(action).toEqual({
type: FETCH_GRADING_OVERVIEWS,
payload: {
filterToGroup: filterToGroup,
gradedFilter: gradedFilter,
publishedFilter: publishedFilter,
pageParams: pageParams,
filterParams: filterParams
}
Expand Down Expand Up @@ -328,11 +334,12 @@ test('setCourseRegistration generates correct action object', () => {
});

test('setAssessmentConfigurations generates correct action object', () => {
const assesmentConfigurations = [
const assesmentConfigurations: AssessmentConfiguration[] = [
{
assessmentConfigId: 1,
type: 'Mission1',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -343,6 +350,7 @@ test('setAssessmentConfigurations generates correct action object', () => {
assessmentConfigId: 2,
type: 'Mission2',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -353,6 +361,7 @@ test('setAssessmentConfigurations generates correct action object', () => {
assessmentConfigId: 3,
type: 'Mission3',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand Down Expand Up @@ -533,6 +542,7 @@ test('updateAssessmentOverviews generates correct action object', () => {
{
type: 'Missions',
isManuallyGraded: true,
isPublished: false,
closeAt: 'test_string',
coverImage: 'test_string',
id: 0,
Expand All @@ -541,10 +551,10 @@ test('updateAssessmentOverviews generates correct action object', () => {
openAt: 'test_string',
title: 'test_string',
shortSummary: 'test_string',
status: 'not_attempted',
status: AssessmentStatuses.not_attempted,
story: null,
xp: 0,
gradingStatus: 'none',
isGradingPublished: false,
maxTeamSize: 1,
hasVotingFeatures: false
}
Expand Down Expand Up @@ -595,9 +605,10 @@ test('updateGradingOverviews generates correct action object', () => {
studentUsername: 'E0123456',
studentUsernames: [],
submissionId: 1,
submissionStatus: 'attempting',
isGradingPublished: false,
progress: ProgressStatuses.attempting,
groupName: 'group',
gradingStatus: 'excluded',
submissionStatus: AssessmentStatuses.attempting,
questionCount: 6,
gradedCount: 0
}
Expand Down Expand Up @@ -772,6 +783,7 @@ test('updateAssessmentTypes generates correct action object', () => {
assessmentConfigId: 1,
type: 'Missions',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -782,6 +794,7 @@ test('updateAssessmentTypes generates correct action object', () => {
assessmentConfigId: 2,
type: 'Quests',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -791,7 +804,8 @@ test('updateAssessmentTypes generates correct action object', () => {
{
assessmentConfigId: 3,
type: 'Paths',
isManuallyGraded: true,
isManuallyGraded: false,
isGradingAutoPublished: true,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -802,6 +816,7 @@ test('updateAssessmentTypes generates correct action object', () => {
assessmentConfigId: 4,
type: 'Contests',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -810,8 +825,20 @@ test('updateAssessmentTypes generates correct action object', () => {
},
{
assessmentConfigId: 5,
type: 'PEs',
isManuallyGraded: false,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
hoursBeforeEarlyXpDecay: 0,
earlySubmissionXp: 0
},
{
assessmentConfigId: 6,
type: 'Others',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand All @@ -831,6 +858,7 @@ test('deleteAssessmentConfig generates correct action object', () => {
assessmentConfigId: 1,
type: 'Mission1',
isManuallyGraded: true,
isGradingAutoPublished: false,
displayInDashboard: true,
hasTokenCounter: false,
hasVotingFeatures: false,
Expand Down
Loading

0 comments on commit d2b322a

Please sign in to comment.