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: introduce group activity management and grading #4073

Merged
merged 13 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Klicker automated testing with cypress
on:
push:
branches: ['v3', 'v3*']
branches: ["v3", "v3*"]
pull_request:
branches: ['v3', 'v3*']
branches: ["v3", "v3*"]
types: [opened, synchronize, reopened]
# workflow_dispatch:

Expand Down Expand Up @@ -130,7 +130,7 @@ jobs:
timeout-minutes: 20
with:
install: false
wait-on: 'http://127.0.0.1:3000/api/graphql, http://127.0.0.1:3001, http://127.0.0.1:3002, http://127.0.0.1:3003, http://127.0.0.1:3010'
wait-on: "http://127.0.0.1:3000/api/graphql, http://127.0.0.1:3001, http://127.0.0.1:3002, http://127.0.0.1:3003, http://127.0.0.1:3010"
wait-on-timeout: 300
record: true
browser: electron
Expand Down
3 changes: 2 additions & 1 deletion apps/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@klicker-uzh/prisma": "workspace:*",
"@klicker-uzh/shared-components": "workspace:*",
"@next-auth/prisma-adapter": "1.0.7",
"@uzh-bf/design-system": "2.5.2",
"@uzh-bf/design-system": "2.7.0",
"axios": "1.4.0",
"bcryptjs": "2.4.3",
"js-cookie": "3.0.5",
Expand Down Expand Up @@ -49,6 +49,7 @@
"build:test": "cross-env NODE_ENV=test next build",
"dev": "next dev --port 3010",
"dev:offline": "next dev --port 3010",
"dev:test": "cross-env NODE_ENV=test next dev --port 3010",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "next lint",
Expand Down
3 changes: 2 additions & 1 deletion apps/backend-docker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@types/passport-jwt": "^3.0.6",
"@types/ramda": "^0.29.3",
"@types/ws": "^8.5.9",
"@uzh-bf/design-system": "2.5.2",
"@uzh-bf/design-system": "2.7.0",
"axios": "1.4.0",
"cross-env": "7.0.3",
"eslint": "8.45.0",
Expand All @@ -76,6 +76,7 @@
"dev": "npm-run-all --parallel dev:build dev:run",
"dev:build": "tsup --watch",
"dev:offline": "pnpm run dev",
"dev:test": "cross-env NODE_ENV=test pnpm run dev",
"dev:run": "nodemon -w dist/ -w '../../packages/graphql/dist' --exec 'node --env-file=.env ./dist/index.js'",
"format": "prettier --write .",
"format:check": "prettier --check .",
Expand Down
4 changes: 3 additions & 1 deletion apps/backend-docker/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ function prepareApp({ prisma, redisExec, pubSub, cache, emitter }: any) {
requestHeaders: ['x-graphql-yoga-csrf'], // default
}),
usePersistedOperations({
allowArbitraryOperations: process.env.NODE_ENV === 'development',
allowArbitraryOperations:
process.env.NODE_ENV === 'development' ||
process.env.NODE_ENV === 'test',
getPersistedOperation(sha256Hash: string) {
return persistedOperations[sha256Hash]
},
Expand Down
20 changes: 18 additions & 2 deletions apps/docs/docs/student_tutorials/groups_activities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,24 @@
title: Groups and Group Activities - Student View
---

## Creating / Joining Groups and Group Activities

While students compete in leaderboards against each other when collecting points from live quizzes, practice quizzes and microlearnings, KlickerUZH also covers efforts to promote collaboration and teamwork. This section describes the student view of groups and group activities. To create them from the lecturer view, check out the [corresponding section](/tutorials/group_activity/).

Through a corresponding tab on the course page, students can either create a new group or join an existing one, using the corresponding PIN. Groups can be formed until a pre-defined deadline, specified by the lecturer. These groups can solve group activities together, which are created by the lecturer and consist of a set of questions, explanation texts and hints, which are distributed across all participants, promoting collaboration. Group activities are only available in a limited time window and can only be solved once. Points are awarded for answering questions correctly. The points are added to the total participant score of the course and the group score on the group leaderboard.
Through a tab on the course page, students can either create a new group or join an existing one, using the provided PIN. Groups can be formed until a pre-defined deadline, specified by the lecturer. These groups can solve group activities together, which are created by the lecturer and consist of a set of questions, explanation texts and hints, distributed across all participants, promoting collaboration. Group activities are only available in a limited time window and can only be solved once. Points are awarded for answering questions correctly.

![Group Activity in Mobile View](/img_v3/10_group_overview.png)

## Solve a Group Activity

Once a group activity is published, students can access it from the course overview shown above. After the group activity and given that the group formation has been finalized, the students can "start" the group activity. Through this, the available hints will automatically be distributed between all group members. The students can then solve the questions together and submit their answers. The group activity will be automatically closed at the specified end date. After this date, participants will no longer be able to submit their answers.

Please note that each group can only submit their answers once. After a successful submission, all group members will be able to see the submitted answers and grading decisions (once available) through their own account.

![Group Activity Student View](/img_v3/15_group_activity_student_view.png)

## View Group Activity Results

After the group activity has ended and the lecturer has finalized the grading process, students can view the results of the group activity. On the evaluation view, students will be able to see all hints (not only their assigned ones) and the provided feedbacks and points as shown below. Answers will automatically be categorized into correct (full points), partially correct (partial points) and incorrect (no points).

![Group Activity in Mobile View](/img_v3/10_group_activity.png)
![Group Activity Evaluation](/img_v3/16_group_activity_graded_students.png)
59 changes: 58 additions & 1 deletion apps/docs/docs/tutorials/group_activity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,61 @@ import CatalystTitle from '../../src/components/CatalystTitle'

<CatalystTitle title="Activity - Group Activity" />

> COMING SOON
## What is a Group Activity?

Group activities are a new collaborative learning activity in KlickerUZH. They are designed to promote teamwork and collaboration among students. Group activities consist of a set of questions, explanation texts, and hints that are distributed across all participants. Participants work together to solve the questions and earn points for correct answers. Group activities are only available in a limited time window and can only be solved once. To be able to participate in a group activity, students need to be part of a group in the course. Groups can be created through the course overview on the student frontend.

Currrently, student submissions need to be graded manually, but we are evaluating possibilities to use automated grading approaches (possibly including AI components). To see how group activities look like from the student view, check out the [corresponding section](/student_tutorials/groups_activities/).

## How can I create a Group Activity?

To create a group activity, navigate to the question pool and use the button at the top to start the group creation wizard. The wizard will guide you through the process of creating a group activity. You can add a general introduction text, hints and questions. The different components / settings are explained in the following sections.

In a first step, you will be asked to provide general information about the group activity. The following fields are available for customization:

- **Name**: The name of the group activity allows the user to distinguish the particular session from others. It is therefore only visible to the users themselves.
- **Display Name**: This name will be shown to the participants while the group activity is being performed.
- **Description**: A description of the group activity can optionally be added and will be displayed to the participants as an introduction to the group activity. While not required, we highly recommend to provide general information about the group activity here.

During the second step, you can adjust the settings of the group activity and define the hints, which will be required to solve the questions. The following fields are available for customization:

- **Course**: In this field, the group activity has to be assigned to a course. Note that this is different from the Live Quiz, which can be run independently from a course.
- **Multiplier**: By default, each question in the group activity is worth 25 points. This value is multiplied with the question multiplier and the group activity multiplier, which can be specified here.
- **Start Date**: Once the group activity has been published from the course menu, it will automatically be available to the participants at the specified start date.
- **End Date**: The group activity will be automatically closed at the specified end date. After this date, participants will no longer be able to submit their answers.
- **Hints**: Group activities require you to provide at least two hints to solve the questions (specified in the next step). These hints will be equally distributed between the participants of the group, encouraging them to work together to solve the questions. You can add as many hints as you like and select between "text hints" and "numerical hints" with an optional unit value.

![Group Activity Options](/img_v3/11_group_activity_options.png)

In the third and last step, you can add questions and content elements to the group activity. These will be shown to all students in the order provided and should required the information from the hints to be solved.

![Group Activity Questions](/img_v3/12_group_activity_questions.png)

## How can I publish a Group Activity and make it accessible to participants?

From the course overview, you can execute a set of actions on the group activity. Depending on the current status of the activity and the start and end times, the following actions are available:

- **Edit**: Group activities, which are still in the draft state can be edited. Find the corresponding button under the `other actions` dropdown.
- **Delete**: If you want to remove a group activity, you can do so by clicking the delete button before the group activity is published. Deleted group activities cannot be restored.
- **Publish**: Once you are satisfied with the group activity, you can publish it. This will make it available to the participants at the specified start date or immediately, if the start date is in the past. As a preview is not yet available for group activities, we recommend to check out the corresponding [student documentation](/student_tutorials/groups_activities/) to get an impression of how the group activity will look like for the participants.
- **Unpublish**: If the start date of a published group activity is in the future, you can unpublish it. This will hide the group activity from the participants and allow you to make further changes.
- **Grading**: After the group activity has ended, you can grade the submissions of the participants through the dedicated grading view (described below).

<img
src="/img_v3/13_group_activity_list.png"
alt="Microlearning in Mobile View"
width="800"
/>

## How can I grade the submissions to a Group Activity

![Group Activity Grading View](/img_v3/14_group_activity_grading.png)

After the group activity has ended, you can grade the submissions of the participants. To do so, navigate to the course overview and select the grading on the corresponding group activity. This will open the grading view, where you can see the submissions of the participants and grade them (as illustrated above). The following feedback options are available:

- **Points** (required): Points need to be awarded to the group's answer to each question. The maximum number of points for this question (computed as the product of the question and group activity multipliers and the default 25 points) is shown as a reference. The total number of points will automatically be computed for you.
- **Answer Feedback** (optional): You can provide feedback to the group's answer to each question. This feedback will be shown to the participants after the grading has been completed.
- **Pass / Fail** (required): Please specify at the bottom, if the group has passed or failed the group activity. This information will be shown to the participants after the grading has been completed.
- **Submission Feedback** (optional): Right below the pass / fail decision, you can provide feedback to the group's submission as a whole.

Please notice, that **grading a submission will not make it immediately visible to the students!** Only once all the submissions for a group activity have a valid grading decision, the grading can be **finalized** using the corresponding button in the grading view. After finalizing the grading, the results will be visible to the participants.
2 changes: 1 addition & 1 deletion apps/docs/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const config = {
announcementBar: {
id: 'release_v3',
content:
'We are now offering introductory courses through UZH Central IT. For more details see <a target="_blank" href="https://community.klicker.uzh.ch/t/2024-01-10-2024-02-08-klickeruzh-v3-0-introduction-and-didactic-use-cases/257">the following page</a>.',
'We are now regularly offering introductory courses through UZH Central IT. For more details see <a target="_blank" href="https://community.klicker.uzh.ch/t/2024-01-10-2024-02-08-klickeruzh-v3-0-introduction-and-didactic-use-cases/257">the following page</a>.',
backgroundColor: '#fafbfc',
textColor: '#091E42',
isCloseable: false,
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@gabrielcsapo/docusaurus-plugin-matomo": "0.1.2",
"@mdx-js/react": "2.3.0",
"@tsconfig/docusaurus": "1.0.5",
"@uzh-bf/design-system": "2.5.2",
"@uzh-bf/design-system": "2.7.0",
"autoprefixer": "10.4.14",
"cross-env": "7.0.3",
"nodemon": "3.0.1",
Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ function TitleImage() {
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-xl">
<div className="hidden sm:mb-10 sm:flex">
<div className="relative rounded-full px-3 py-1 text-md leading-6 text-gray-500 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
KlickerUZH v3.0 has been released with brand new use cases{' '}
KlickerUZH v3.1 has been released with brand new features{' '}
<a
href="https://community.klicker.uzh.ch/t/klickeruzh-v3-0-release-announcement-26-08-2023/79"
href="https://community.klicker.uzh.ch/t/klickeruzh-v3-1-release-information/310"
className="whitespace-nowrap font-semibold"
target="_blank"
style={{ marginLeft: '0.75rem' }}
Expand Down Expand Up @@ -59,8 +59,8 @@ function TitleImage() {
</a>
</div>
<div className="border border-solid rounded-md px-3 py-2 mt-4 bg-slate-100 border-slate-200 shadow">
We are now offering introductory courses through UZH Central
IT. For more details see{' '}
We are now regularly offering introductory courses through UZH
Central IT. For more details see{' '}
<a
target="_blank"
href="https://community.klicker.uzh.ch/t/2024-01-10-2024-02-08-klickeruzh-v3-0-introduction-and-didactic-use-cases/257"
Expand Down
Binary file removed apps/docs/static/img_v3/10_group_activity.png
Binary file not shown.
Binary file added apps/docs/static/img_v3/10_group_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion apps/frontend-control/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@sentry/nextjs": "7.61.1",
"@socialgouv/matomo-next": "1.6.1",
"@types/js-cookie": "^3.0.3",
"@uzh-bf/design-system": "2.5.2",
"@uzh-bf/design-system": "2.7.0",
"body-parser": "1.20.2",
"cross-env": "7.0.3",
"dayjs": "1.11.9",
Expand Down Expand Up @@ -74,6 +74,7 @@
"check": "tsc --noEmit",
"dev": "cross-env NODE_ENV=development next dev --port 3003",
"dev:offline": "cross-env NODE_ENV=development next dev --port 3003",
"dev:test": "cross-env NODE_ENV=test next dev --port 3003",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "next lint",
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend-control/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function Index() {
<div>
<H4>{t('control.home.selectCourse')}</H4>
<div className="flex flex-col gap-2">
{dataCourses.controlCourses
{[...dataCourses?.controlCourses]
.sort((a, b) => (a.isArchived ? 1 : -1))
.map((course) => (
<ListButton
Expand Down
4 changes: 3 additions & 1 deletion apps/frontend-manage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@klicker-uzh/prisma": "workspace:*",
"@klicker-uzh/shared-components": "workspace:*",
"@socialgouv/matomo-next": "1.6.1",
"@uzh-bf/design-system": "2.5.2",
"@uzh-bf/design-system": "2.7.0",
"d3": "7.8.4",
"dayjs": "1.11.9",
"deepmerge": "4.3.1",
Expand All @@ -45,6 +45,7 @@
"react-dom": "18.2.0",
"react-dropzone": "14.2.3",
"react-qrcode-logo": "2.9.0",
"react-resizable-panels": "2.0.12",
"react-select": "5.7.4",
"react-sizeme": "3.0.2",
"react-tagcloud": "2.3.1",
Expand Down Expand Up @@ -91,6 +92,7 @@
"check": "tsc --noEmit",
"dev": "cross-env NODE_ENV=development next dev --port 3002",
"dev:offline": "cross-env NODE_ENV=development next dev --port 3002",
"dev:test": "cross-env NODE_ENV=test next dev --port 3002",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "next lint",
Expand Down
43 changes: 43 additions & 0 deletions apps/frontend-manage/src/components/common/Resizeable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { faUpDown } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import * as ResizablePrimitive from 'react-resizable-panels'
import { twMerge } from 'tailwind-merge'

const ResizablePanelGroup = ({
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
className={twMerge(
'flex h-full w-full data-[panel-group-direction=vertical]:flex-col',
className
)}
{...props}
/>
)

const ResizablePanel = ResizablePrimitive.Panel

const ResizableHandle = ({
withHandle,
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
withHandle?: boolean
}) => (
<ResizablePrimitive.PanelResizeHandle
className={twMerge(
'relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90',
className
)}
{...props}
>
{withHandle && (
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<FontAwesomeIcon icon={faUpDown} />
</div>
)}
</ResizablePrimitive.PanelResizeHandle>
)

export { ResizableHandle, ResizablePanel, ResizablePanelGroup }
Loading
Loading