Skip to content
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

feat: selectable students waiting step #2025

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openassessment/conf/locale/en/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: edx-ora2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-23 14:54+0000\n"
"POT-Creation-Date: 2024-01-31 07:58-0500\n"
"PO-Revision-Date: 2014-06-04 15:41-0400\n"
"Last-Translator: Muhammad Ayub khan <ayubkhan@edx.org>\n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
Expand Down
174 changes: 91 additions & 83 deletions openassessment/conf/locale/en/LC_MESSAGES/djangojs.po

Large diffs are not rendered by default.

Binary file modified openassessment/conf/locale/eo/LC_MESSAGES/django.mo
Binary file not shown.
2 changes: 1 addition & 1 deletion openassessment/conf/locale/eo/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: edx-ora2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-23 14:54+0000\n"
"POT-Creation-Date: 2024-01-31 07:58-0500\n"
"PO-Revision-Date: 2014-06-04 15:41-0400\n"
"Last-Translator: Muhammad Ayub khan <ayubkhan@edx.org>\n"
"Language-Team: openedx-translation <openedx-translation@googlegroups.com>\n"
Expand Down
Binary file modified openassessment/conf/locale/eo/LC_MESSAGES/djangojs.mo
Binary file not shown.
174 changes: 91 additions & 83 deletions openassessment/conf/locale/eo/LC_MESSAGES/djangojs.po

Large diffs are not rendered by default.

Binary file modified openassessment/conf/locale/es_419/LC_MESSAGES/djangojs.mo
Binary file not shown.
10 changes: 10 additions & 0 deletions openassessment/conf/locale/es_419/LC_MESSAGES/djangojs.po
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,13 @@ msgstr "Este ejercio de ORA ya ha sido liberado. Los cambios solo afectarán a l
#: xblock/static/js/src/studio/oa_edit.js:319
msgid "error count: "
msgstr "recuento de errores:"

#: openassessment/xblock/static/dist/openassessment-studio.979e8b88dd0d9cee68f7.js:25
#: openassessment/xblock/static/js/src/lms/components/WaitingStepList.jsx:38
msgid "Action"
msgstr "Acción"

#: openassessment/xblock/static/dist/openassessment-studio.979e8b88dd0d9cee68f7.js:25
#: openassessment/xblock/static/js/src/lms/components/WaitingStepList.jsx:47
msgid "Review"
msgstr "Revisar"
Binary file modified openassessment/conf/locale/es_ES/LC_MESSAGES/djangojs.mo
Binary file not shown.
10 changes: 10 additions & 0 deletions openassessment/conf/locale/es_ES/LC_MESSAGES/djangojs.po
Original file line number Diff line number Diff line change
Expand Up @@ -589,3 +589,13 @@ msgstr "Esta tarea de respuesta abierta ya ha sido publicada. Los cambios solo a
#: xblock/static/js/src/studio/oa_edit.js:319
msgid "error count: "
msgstr "recuento de errores:"

#: openassessment/xblock/static/dist/openassessment-studio.979e8b88dd0d9cee68f7.js:25
#: openassessment/xblock/static/js/src/lms/components/WaitingStepList.jsx:38
msgid "Action"
msgstr "Acción"

#: openassessment/xblock/static/dist/openassessment-studio.979e8b88dd0d9cee68f7.js:25
#: openassessment/xblock/static/js/src/lms/components/WaitingStepList.jsx:47
msgid "Review"
msgstr "Revisar"
6 changes: 6 additions & 0 deletions openassessment/xblock/openassessmentblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from bleach.sanitizer import Cleaner
from lazy import lazy
from webob import Response
from edx_toggles.toggles import SettingDictToggle
from xblock.core import XBlock
from xblock.exceptions import NoSuchServiceError
from xblock.fields import Boolean, Integer, List, Scope, String
Expand Down Expand Up @@ -69,6 +70,10 @@

logger = logging.getLogger(__name__) # pylint: disable=invalid-name

ENABLE_SELECTABLE_LEARNER_WAITING_REVIEW = SettingDictToggle(
"FEATURES", "ENABLE_SELECTABLE_LEARNER_WAITING_REVIEW", default=False, module_name=__name__
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about this feature flag definition living here? It makes sense from a code encapsulation perspective, but we also need to make sure that it makes it into the documented list, otherwise no one will know it exists.

@feanil do you know if the docs will pick this toggle up if it's defined here?

Either way, it should be documented, so @johnvente could you add a docstring for this flag like what's done in edx-platform?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree @pomegranited I will add the respective doc here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for documenting this flag @johnvente ! Just going to wait a bit to see what @feanil has to say.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. haven't heard anything back, but I think the code tells us the answer.

@johnvente have you seen how the other feature flags are used by ORA2?

cf

FEATURES = {

FEATURE_TOGGLES_BY_FLAG_NAME = {

I think we should follow that convention and move this flag + docs into an ENABLE_ORA_SELECTABLE_LEARNER_WAITING_REVIEW flag in lms/cms common ([e.g. like ENABLE_ORA_ALL_FILE_URLS in lms.common.env and cms.common.env), and use the ORA xblock.config_mixin to check the flag itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pomegranited I will go back here ASAP to review that meanwhile @feanil can take a look as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @johnvente , sorry for my delays here.

I've nudged Feanil on Slack, but if he doesn't get back to us, then I'll finish my review and merge this when I start my week on Tuesday.

CC @itsjeyd

Copy link
Contributor Author

@johnvente johnvente Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HI @pomegranited! sorry for the delay, I've changed the feature flag as you mentioned now is called ENABLE_ORA_SELECTABLE_LEARNER_WAITING_REVIEW I updated the PR's description as well


def load(path):
"""Handy helper for getting resources from our kit."""
Expand Down Expand Up @@ -756,6 +761,7 @@ def waiting_step_details_view(self, context=None): # pylint: disable=unused-arg
context_dict = {
"title": self.title,
"peer_assessment_required": peer_assessment_required,
"selectable_learners_enabled": ENABLE_SELECTABLE_LEARNER_WAITING_REVIEW.is_enabled(),
}

if peer_assessment_required:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions openassessment/xblock/static/dist/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"openassessment-editor-textarea.js.map": "/openassessment-editor-textarea.2cee26d88c3441ada635.js.map",
"openassessment-editor-tinymce.js": "/openassessment-editor-tinymce.0b97b77ad7f1b7150f67.js",
"openassessment-editor-tinymce.js.map": "/openassessment-editor-tinymce.0b97b77ad7f1b7150f67.js.map",
"openassessment-lms.css": "/openassessment-lms.370a944c6f75b9557efa.css",
"openassessment-lms.js": "/openassessment-lms.370a944c6f75b9557efa.js",
"openassessment-lms.css.map": "/openassessment-lms.370a944c6f75b9557efa.css.map",
"openassessment-lms.js.map": "/openassessment-lms.370a944c6f75b9557efa.js.map",
"openassessment-lms.css": "/openassessment-lms.f0246312ee3c55c0b0a1.css",
"openassessment-lms.js": "/openassessment-lms.f0246312ee3c55c0b0a1.js",
"openassessment-lms.css.map": "/openassessment-lms.f0246312ee3c55c0b0a1.css.map",
"openassessment-lms.js.map": "/openassessment-lms.f0246312ee3c55c0b0a1.js.map",
"openassessment-ltr.css": "/openassessment-ltr.7955a1e2cc11fc6948de.css",
"openassessment-ltr.js": "/openassessment-ltr.7955a1e2cc11fc6948de.js",
"openassessment-ltr.css.map": "/openassessment-ltr.7955a1e2cc11fc6948de.css.map",
Expand All @@ -18,6 +18,6 @@
"openassessment-rtl.js.map": "/openassessment-rtl.9de7c9bc7c1048c07707.js.map",
"openassessment-studio.js": "/openassessment-studio.d576fb212cefa2e4b720.js",
"openassessment-studio.js.map": "/openassessment-studio.d576fb212cefa2e4b720.js.map",
"fallback-default.png": "/1b90ce76fe01a1aa6e5ec289a5fb3799.png",
"fallback-default.png": "/4620b30a966533ace489dcc7afb151b9.png",
"default-avatar.svg": "/95ec738c0b7faac5b5c9126794446bbd.svg"
}

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import sinon from 'sinon';
import WaitingStepList from 'lms/components/WaitingStepList';

describe('OpenAssessment.WaitingStepList', () => {
window.gettext = sinon.fake((text) => text);

const IntlProviderWrapper = ({ children }) => (
<IntlProvider locale="en" messages={{}}>
{children}
</IntlProvider>
);

describe('With selectableLearnersEnabled as a prop', () => {
const WaitingStepListWrapper = ({ children }) => <div data-testid="learners-data-table">{children}</div>

it('should allow row selection when it is enabled', async () => {
const studentList = [
{
username: 'myusername',
graded: false,
graded_by: '2',
created_at: Date.now(),
staff_grade_status: 'waiting',
workflow_status: '',
},
];

render(
<IntlProviderWrapper>
<WaitingStepListWrapper>
<WaitingStepList
selectableLearnersEnabled
studentList={studentList}
/>
</WaitingStepListWrapper>
</IntlProviderWrapper>
);


await waitFor(() => {
const dataTable = screen.getByTestId('learners-data-table');
const [firstRowCheckbox] = dataTable.querySelectorAll('input[type="checkbox"]');
expect(dataTable).not.toBeNull();
expect(firstRowCheckbox).not.toBeNull();
});

});


it('should show review action button when a row is selected', async () => {

const studentList = [
{
username: 'myusername',
graded: false,
graded_by: '2',
created_at: Date.now(),
staff_grade_status: 'waiting',
workflow_status: '',
},
];

render(
<IntlProviderWrapper>
<WaitingStepListWrapper>
<WaitingStepList
selectableLearnersEnabled
studentList={studentList}
/>
</WaitingStepListWrapper>
</IntlProviderWrapper>
);

await waitFor(() => {
const dataTable = screen.getByTestId('learners-data-table');
const [firstRowCheckbox] = dataTable.querySelectorAll('input[type="checkbox"]');
fireEvent.change(firstRowCheckbox, { target: { checked: true } });
expect(firstRowCheckbox.checked).toBe(true);
});

});

});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import PropTypes from 'prop-types';
import { Alert } from '@openedx/paragon';
import WaitingStepList from './WaitingStepList';

const WaitingStepContent = ({ waitingStepDetails, refreshData }) => {
const WaitingStepContent = ({
waitingStepDetails,
refreshData,
findLearner,
selectableLearnersEnabled,
}) => {
const oraDescriptionText = gettext(
'The "{name}" problem is configured to require a minimum of {min_grades} '
+ 'peer grades, and asks to review {min_graded} peers.',
Expand Down Expand Up @@ -47,6 +52,8 @@ const WaitingStepContent = ({ waitingStepDetails, refreshData }) => {
<WaitingStepList
studentList={waitingStepDetails.student_data}
refreshData={refreshData}
findLearner={findLearner}
selectableLearnersEnabled={selectableLearnersEnabled}
/>
</div>
);
Expand All @@ -55,17 +62,21 @@ const WaitingStepContent = ({ waitingStepDetails, refreshData }) => {
WaitingStepContent.propTypes = {
waitingStepDetails: PropTypes.shape({
display_name: PropTypes.string,
must_be_graded_by: PropTypes.string,
must_grade: PropTypes.string,
waiting_count: PropTypes.string,
overwritten_count: PropTypes.string,
must_be_graded_by: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
must_grade: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
waiting_count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
overwritten_count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
student_data: PropTypes.arrayOf(PropTypes.object),
}).isRequired,
refreshData: PropTypes.func,
findLearner: PropTypes.func,
selectableLearnersEnabled: PropTypes.bool,
};

WaitingStepContent.defaultProps = {
refreshData: () => ({}),
findLearner: () => ({}),
selectableLearnersEnabled: false,
};

export default WaitingStepContent;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { Button, DataTable } from '@openedx/paragon';

const getReadableTime = (timestamp) => moment(timestamp).fromNow(true);

const WaitingStepList = ({ studentList, refreshData }) => {
const WaitingStepList = ({
studentList,
refreshData,
findLearner,
selectableLearnersEnabled,
}) => {
const studentListWithTimeAgo = studentList.map((item) => ({
...item,
created_at: getReadableTime(item.created_at),
Expand All @@ -15,15 +20,41 @@ const WaitingStepList = ({ studentList, refreshData }) => {
<Button onClick={() => refreshData()}>{gettext('Refresh')}</Button>
);

const reviewLearnerAction = (learnerUsername) => {
findLearner(learnerUsername);
};

return (
<DataTable
itemCount={studentListWithTimeAgo.length}
data={studentListWithTimeAgo}
isSelectable={selectableLearnersEnabled}
maxSelectedRows={1}
additionalColumns={
selectableLearnersEnabled
? [
{
id: 'action',
Header: gettext('Action'),
// eslint-disable-next-line react/prop-types
Cell: ({ row: { isSelected, original: { username: learnerUsername } } }) => (isSelected ? (
<Button
variant="link"
size="sm"
data-testid="review-learner-button"
onClick={() => reviewLearnerAction(learnerUsername)}
>
{gettext('Review')}
</Button>
) : null),
},
]
: []
}
columns={[
{
Header: gettext('Username'),
accessor: 'username',

},
{
Header: gettext('Peers Assessed'),
Expand All @@ -46,9 +77,7 @@ const WaitingStepList = ({ studentList, refreshData }) => {
accessor: 'workflow_status',
},
]}
tableActions={[
<RefreshAction />,
]}
tableActions={[<RefreshAction />]}
>
<DataTable.TableControlBar />
<DataTable.Table />
Expand All @@ -60,10 +89,14 @@ const WaitingStepList = ({ studentList, refreshData }) => {
WaitingStepList.propTypes = {
studentList: PropTypes.arrayOf(PropTypes.object).isRequired,
refreshData: PropTypes.func,
findLearner: PropTypes.func,
selectableLearnersEnabled: PropTypes.bool,
};

WaitingStepList.defaultProps = {
refreshData: () => ({}),
findLearner: () => ({}),
selectableLearnersEnabled: false,
};

export default WaitingStepList;
Loading
Loading