From 8410871a06968fdb58278009a36edb16e41db268 Mon Sep 17 00:00:00 2001 From: outisa Date: Wed, 7 Oct 2026 21:39:43 +0300 Subject: [PATCH] Make update consent for research possible --- backend/controllers/users.js | 20 +++++ .../20261007130200-user-research-consent.js | 15 ++++ backend/models/user.js | 4 + .../components/RegistrationDetailsPage.jsx | 75 ++++++++++++++++++- frontend/src/reducers/actions/userActions.js | 12 +++ frontend/src/reducers/loginReducer.js | 5 ++ frontend/src/services/user.js | 15 +++- 7 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 backend/migrations/20261007130200-user-research-consent.js diff --git a/backend/controllers/users.js b/backend/controllers/users.js index e479995..d771f5d 100644 --- a/backend/controllers/users.js +++ b/backend/controllers/users.js @@ -2,6 +2,26 @@ const usersRouter = require('express').Router() const db = require('../models/index') const { checkLogin, checkInstructor } = require('../middleware') +usersRouter.put('/consent/:studentNumber', checkLogin, async (req, res) => { + const { research_consent } = req.body + const { studentNumber } = req.params + + try { + const user = await db.User.findOne({ + where: { student_number: studentNumber }, + }) + if (!user) { + return res.status(400).json({ error: 'user does not exist' }) + } + const updatedUser = await user.update({ research_consent }) + const refreshedUser = await updatedUser.reload() + res.status(200).json({ user: refreshedUser }) + } catch (error) { + console.log(error) + res.status(500).json({ error: 'Something is wrong... try reloading the page' }) + } +}) + usersRouter.put('/:studentNumber', checkLogin, async (req, res) => { const { email } = req.body const { studentNumber } = req.params diff --git a/backend/migrations/20261007130200-user-research-consent.js b/backend/migrations/20261007130200-user-research-consent.js new file mode 100644 index 0000000..580c5de --- /dev/null +++ b/backend/migrations/20261007130200-user-research-consent.js @@ -0,0 +1,15 @@ +'use strict' + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('users', 'research_consent', { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false, + }) + }, + // eslint-disable-next-line no-unused-vars + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('users', 'research_consent') + }, +} diff --git a/backend/models/user.js b/backend/models/user.js index c68fb45..a84fed3 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -23,6 +23,10 @@ module.exports = (sequelize, Sequelize) => { type: Sequelize.BOOLEAN, defaultValue: false, }, + research_consent: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, }, { underscored: true, diff --git a/frontend/src/components/RegistrationDetailsPage.jsx b/frontend/src/components/RegistrationDetailsPage.jsx index a23d71c..c2395ab 100644 --- a/frontend/src/components/RegistrationDetailsPage.jsx +++ b/frontend/src/components/RegistrationDetailsPage.jsx @@ -1,15 +1,26 @@ import { withRouter } from '../utils/withRouter' -import React from 'react' +import React, { useState } from 'react' import { Link } from 'react-router-dom' -import { connect } from 'react-redux' +import { connect, useDispatch } from 'react-redux' import registrationActions from '../reducers/actions/registrationActions' import { setError } from '../reducers/actions/notificationActions' - +import { updateConsent } from '../reducers/actions/userActions' import peerReviewService from '../services/peerReview' import Typography from '@mui/material/Typography' -import { Input, Card, CardContent, Select, MenuItem } from '@mui/material' +import { + Input, + Card, + CardContent, + Select, + MenuItem, + RadioGroup, + Radio, + FormControl, + FormControlLabel, + Button, +} from '@mui/material' import CourseMaterial from './common/CourseMaterial' import SortableTopicList from './SortableTopicList' @@ -67,6 +78,59 @@ class PeerReviewInfo extends React.Component { } } +const ResearchConsent = ({ student }) => { + const [consent, setConsent] = useState(student.research_consent) + const dispatch = useDispatch() + const handleUpdate = () => { + dispatch(updateConsent(consent, student)) + } + return ( +
+ Tutkimuslupa + + Yliopiston tehtäviin kuuluvat opetuksen lisäksi myös tutkimuksen teko. Te opiskelijat olette + tärkeä osa prosessia, ilman teitä laadukasta yliopisto-opetuksen tutkimusta on vaikea tehdä. + Olemme tekemässä Helsingin yliopistossa tutkimusta generatiivisen tekoälyn käytöstä + opintojen aikana. Mikäli osallistut tutkimukseen, vastauksesi kysymyksiin voidaan yhdistää + arvosana- ja kurssisuoritustietoihisi, mutta sinua tai ryhmääsi ei voida tunnistaa + julkaistusta tutkimuksesta. Lisätietoja voi kysyä Matti Luukkaiselta (vastuuopettaja) tai + Outi Savolaiselta (opettaja). + +
+ + Vertaisarviossa tai tuntikirjanpidossa antamasi vastaukset tekoälynkäytöstä eivät vaikuta + arvosanaasi tai kohteluusi kurssilla. + + + setConsent(!consent)} + > + } + label="Osallistun oppimiseen liittyvään tutkimukseen." + /> + } + label="En osallistu oppimiseen liittyvään tutkimukseen." + /> + + + +
+ ) +} + const GroupDetails = ({ groupDetails }) => { return (
@@ -187,6 +251,7 @@ class RegistrationDetailsPage extends React.Component { projectRegistrationConf, peerReviewOpen, peerReviewRound, + user, } = this.props if (groupDetails) { @@ -198,6 +263,7 @@ class RegistrationDetailsPage extends React.Component { peerReviewRound={peerReviewRound} groupDetails={groupDetails} /> +
) } @@ -249,6 +315,7 @@ const mapStateToProps = (state) => { peerReviewRound: state.registrationManagement.peerReviewRound, peerReviewConf: state.registrationManagement.peerReviewConf, projectRegistrationConf: state.registrationManagement.projectRegistrationConf, + user: state.login.user.user, } } diff --git a/frontend/src/reducers/actions/userActions.js b/frontend/src/reducers/actions/userActions.js index d7e0fd3..89eaa70 100644 --- a/frontend/src/reducers/actions/userActions.js +++ b/frontend/src/reducers/actions/userActions.js @@ -7,6 +7,18 @@ const updateIsInstructor = (user, isInstructor) => ({ instructor: isInstructor, }) +export const updateConsent = (consent, user) => { + return async (dispatch) => { + const { user: updatedUser } = await userService.updateUserConsent(consent, user) + dispatch({ + type: 'UPDATE_CONSENT', + payload: { + research_consent: updatedUser.research_consent, + }, + }) + } +} + export const loginUser = (userCredentials) => { return async (dispatch) => { const { user, token } = await loginService.login(userCredentials) diff --git a/frontend/src/reducers/loginReducer.js b/frontend/src/reducers/loginReducer.js index d7acd45..fa5382a 100644 --- a/frontend/src/reducers/loginReducer.js +++ b/frontend/src/reducers/loginReducer.js @@ -17,6 +17,11 @@ const loginReducer = (state = initialState, action) => { ...state, user: null, } + case 'UPDATE_CONSENT': + return { + ...state, + research_consent: action.payload, + } case 'UPDATE_USERNAME': return { ...state, diff --git a/frontend/src/services/user.js b/frontend/src/services/user.js index 8e7c4b7..f95b5b7 100644 --- a/frontend/src/services/user.js +++ b/frontend/src/services/user.js @@ -20,6 +20,19 @@ const update = async (user) => { return response.data } +const updateUserConsent = async (consent, user) => { + const config = { + headers: { Authorization: 'bearer ' + getUserToken() }, + } + const studentNumber = user.student_number + const response = await axios.put( + url + '/consent/' + studentNumber, + { research_consent: consent }, + config, + ) + return response.data +} + const checkInstructor = async (token) => { const config = { headers: { Authorization: 'bearer ' + token }, @@ -28,4 +41,4 @@ const checkInstructor = async (token) => { return response.data } -export default { get, update, checkInstructor } +export default { get, update, checkInstructor, updateUserConsent }