diff --git a/i18n/messages.en.js b/i18n/messages.en.js index cd540171..d88e8e40 100644 --- a/i18n/messages.en.js +++ b/i18n/messages.en.js @@ -141,10 +141,13 @@ module.exports = { course_valid_from: 'Valid from', course_main_subject: 'Main field of study', course_language: 'Language of instruction', + course_required_equipment: 'Equipment', course_level_code: 'Education cycle', course_decision_to_discontinue: 'Avvecklingsbeslut', course_transitional_reg: 'Transitional regulations', course_ethical: 'Ethical approach', + course_possibility_to_completions: 'Opportunity to complete the requirements via supplementary examination', + course_possibility_to_addition: 'Opportunity to raise an approved grade via renewed examination', course_short_semester: { 1: 'Spring ', 2: 'Autumn ', @@ -156,10 +159,12 @@ module.exports = { RESEARCH: 'Third cycle', }, course_department: 'Offered by', + course_contact_name: 'Contact ', course_prerequisites: 'Recommended prerequisites', course_prerequisites_description: 'Describes the knowledge and skills (in addition to the eligibility requirements) that you need to be able to take the course.', course_prerequisites_menu_aria_label: 'Information about recommended prerequisites', + course_suggested_addon_studies: 'Add-on studies', course_supplemental_information_url: 'Supplementary information link', course_supplemental_information_url_text: 'Supplementary information link text', course_supplemental_information: 'Supplementary information ', @@ -168,6 +173,7 @@ module.exports = { course_room_canvas: 'Course room in Canvas', course_room_canvas_info: 'Registered students find further information about the implementation of the course in the course room in Canvas. A link to the course room can be found under the tab Studies in the Personal menu at the start of the course.', + course_application_info: 'Information for research students about course offerings', }, courseRoundInformation: { round_header: 'Information for', diff --git a/i18n/messages.se.js b/i18n/messages.se.js index 23074437..9306f7b3 100644 --- a/i18n/messages.se.js +++ b/i18n/messages.se.js @@ -143,10 +143,13 @@ module.exports = { course_valid_from: 'Giltig från', course_main_subject: 'Huvudområde', course_language: 'Undervisningsspråk', + course_required_equipment: 'Utrustning', course_level_code: 'Utbildningsnivå', course_decision_to_discontinue: 'Avvecklingsbeslut', course_transitional_reg: 'Övergångsbestämmelser', course_ethical: 'Etiskt förhållningssätt', + course_possibility_to_completions: 'Möjlighet till komplettering', + course_possibility_to_addition: 'Möjlighet till plussning', course_short_semester: { 1: 'VT ', 2: 'HT ', @@ -158,10 +161,12 @@ module.exports = { RESEARCH: 'Forskarnivå', }, course_department: 'Ges av', + course_contact_name: 'Kontaktperson', course_prerequisites: 'Rekommenderade förkunskaper', course_prerequisites_description: 'Beskriver vilka kunskaper och färdigheter (utöver behörighetskraven) som du behöver för att kunna ta till dig kursen.', course_prerequisites_menu_aria_label: 'Information om rekommenderade förkunskaper', + course_suggested_addon_studies: 'Påbyggnad', course_supplemental_information_url: 'Övrig information - länk', course_supplemental_information_url_text: 'Övrig information - länk text', course_supplemental_information: 'Övrig information', @@ -170,6 +175,7 @@ module.exports = { course_room_canvas: 'Kursrum i Canvas', course_room_canvas_info: 'Registrerade studenter hittar information för genomförande av kursen i kursrummet i Canvas. En länk till kursrummet finns under fliken Studier i Personliga menyn vid kursstart.', + course_application_info: 'Information för forskarstuderande om när kursen ges', }, courseRoundInformation: { round_header: 'Information för', diff --git a/public/js/app/components/CourseSectionList.jsx b/public/js/app/components/CourseSectionList.jsx index 93a11e89..04cd51f9 100644 --- a/public/js/app/components/CourseSectionList.jsx +++ b/public/js/app/components/CourseSectionList.jsx @@ -53,6 +53,10 @@ function CourseSectionList({ courseInfo = {}, partToShow, syllabus = {}, syllabu literatureText = `${syllabus.course_literature_comment}` } + const courseRequiredEquipment = !isMissingInfoLabel(courseInfo.course_required_equipment) + ? courseInfo.course_required_equipment + : syllabus.course_required_equipment + const eligibility = getEligibility() const during = [ @@ -66,6 +70,7 @@ function CourseSectionList({ courseInfo = {}, partToShow, syllabus = {}, syllabu ariaLabel: translation.courseInformation.course_prerequisites_menu_aria_label, }, }, + { header: translation.courseInformation.course_required_equipment, text: courseRequiredEquipment }, { header: translation.courseInformation.course_literature, text: literatureText }, ] @@ -105,6 +110,14 @@ function CourseSectionList({ courseInfo = {}, partToShow, syllabus = {}, syllabu syllabusMarker: true, }) } + examination.push({ + header: translation.courseInformation.course_possibility_to_completions, + text: courseInfo.course_possibility_to_completions, + }) + examination.push({ + header: translation.courseInformation.course_possibility_to_addition, + text: courseInfo.course_possibility_to_addition, + }) examination.push({ header: translation.courseInformation.course_examiners, text: courseInfo.course_examiners }) examination.push({ header: translation.courseInformation.course_ethical, @@ -134,7 +147,13 @@ function CourseSectionList({ courseInfo = {}, partToShow, syllabus = {}, syllabu text: translation.courseInformation.course_level_code_label[courseInfo.course_level_code], syllabusMarker: true, }, + { + header: translation.courseInformation.course_suggested_addon_studies, + text: courseInfo.course_suggested_addon_studies, + }, ] + if (!isMissingInfoLabel(courseInfo.course_contact_name)) + prepare.push({ header: translation.courseInformation.course_contact_name, text: courseInfo.course_contact_name }) if (syllabus.course_transitional_reg !== '') prepare.push({ header: translation.courseInformation.course_transitional_reg, diff --git a/public/js/app/components/MainCourseInformation.jsx b/public/js/app/components/MainCourseInformation.jsx index a8212de5..9ca3a397 100644 --- a/public/js/app/components/MainCourseInformation.jsx +++ b/public/js/app/components/MainCourseInformation.jsx @@ -44,6 +44,12 @@ const MainCourseInformation = ({ courseCode, courseData, semesterRoundState }) = )} + {courseInfo.course_application_info.length > 0 && ( + + + + )} + {translation.courseLabels.header_contact} - + ) } diff --git a/public/js/app/components/RoundInformation/RoundInformationContacts.jsx b/public/js/app/components/RoundInformation/RoundInformationContacts.jsx index 3b669843..2c9a100e 100644 --- a/public/js/app/components/RoundInformation/RoundInformationContacts.jsx +++ b/public/js/app/components/RoundInformation/RoundInformationContacts.jsx @@ -2,7 +2,7 @@ import React from 'react' import { useLanguage } from '../../hooks/useLanguage' import { useMissingInfo } from '../../hooks/useMissingInfo' -function RoundInformationContacts({ courseRoundEmployees }) { +function RoundInformationContacts({ courseData, courseRoundEmployees }) { const { translation } = useLanguage() const { missingInfoLabel } = useMissingInfo() @@ -26,6 +26,15 @@ function RoundInformationContacts({ courseRoundEmployees }) {
{translation.courseRoundInformation.round_teacher}
+ + {courseData.course_contact_name && courseData.course_contact_name !== missingInfoLabel && ( +
+
{translation.courseInformation.course_contact_name}
+
+

{courseData.course_contact_name}

+
+ + )} ) } diff --git a/public/js/app/components/__tests__/RoundInformation.test.js b/public/js/app/components/__tests__/RoundInformation.test.js index 1878301a..67733250 100644 --- a/public/js/app/components/__tests__/RoundInformation.test.js +++ b/public/js/app/components/__tests__/RoundInformation.test.js @@ -42,6 +42,7 @@ describe('Component ', () => { const propsWithStudyPace = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseRound: { round_course_term: ['2018', '1'], round_study_pace: '25', @@ -66,6 +67,7 @@ describe('Component ', () => { const propsWithEmployees = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseCode: 'SF1624', courseRound: mockCourseRound, } @@ -95,6 +97,7 @@ describe('Component ', () => { const propsWithEmptyEmployees = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseCode: 'SF1624', courseRound: mockCourseRound, } @@ -112,6 +115,7 @@ describe('Component ', () => { const propsWithoutEmployees = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseCode: 'SF1624', courseRound: mockCourseRound, } @@ -167,6 +171,7 @@ describe('Component ', () => { const propsWithoutSeatsNum = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseRound: { round_course_term: ['2018', '1'], round_selection_criteria: '

English. Spicy jalapeno bacon ipsum

', @@ -195,6 +200,7 @@ describe('Component ', () => { const propsWithEmptyCriteria = { memoStorageUri: '', semesterRoundState: defaultSemesterRoundState, + courseData: {}, courseRound: { round_course_term: ['2018', '1'], round_selection_criteria: '

', diff --git a/public/js/app/components/__tests__/RoundInformationContacts.test.js b/public/js/app/components/__tests__/RoundInformationContacts.test.js index 264f0f7d..4871bcfe 100644 --- a/public/js/app/components/__tests__/RoundInformationContacts.test.js +++ b/public/js/app/components/__tests__/RoundInformationContacts.test.js @@ -22,7 +22,7 @@ const withinNextSibling = element => within(nextSibling(element)) describe('Component ', () => { describe('examiners, responsibles and teachers', () => { test('shoud show headers with "missing info" text when data is missing', () => { - render() + render() const examinerLabel = screen.getByText('Examiner') expect(nextSibling(examinerLabel)).toHaveTextContent('No information inserted') @@ -36,6 +36,7 @@ describe('Component ', () => { test('shoud show headers with data inserted as html', () => { render( Test examiners

', responsibles: '

Test responsibles

', @@ -59,4 +60,24 @@ describe('Component ', () => { expect(teacherLink).toHaveAttribute('href', '/profile/testteachers/') }) }) + + describe('cource contact', () => { + test('should show header and content for course contact name', () => { + render( + + ) + const contactLabel = screen.getByText('Contact') + expect(contactLabel).toBeInTheDocument() + }) + test.each([undefined, 'No information inserted'])( + "shoud NOT show header if contact name is '%s'", + contactNameArg => { + render( + + ) + const contactLabel = screen.queryByText('Contact') + expect(contactLabel).not.toBeInTheDocument() + } + ) + }) }) diff --git a/public/js/app/hooks/__tests__/useSemesterRoundState.test.js b/public/js/app/hooks/__tests__/useSemesterRoundState.test.js index 9c72a698..a8420778 100644 --- a/public/js/app/hooks/__tests__/useSemesterRoundState.test.js +++ b/public/js/app/hooks/__tests__/useSemesterRoundState.test.js @@ -158,6 +158,7 @@ const syllabusList = [ semesterNumber: 2, }, course_valid_to: [], + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: @@ -186,6 +187,7 @@ const syllabusList = [ year: 2019, semesterNumber: 1, }, + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: diff --git a/public/js/app/pages/CoursePage.jsx b/public/js/app/pages/CoursePage.jsx index 1600de0a..69acff8d 100644 --- a/public/js/app/pages/CoursePage.jsx +++ b/public/js/app/pages/CoursePage.jsx @@ -25,7 +25,7 @@ function CoursePage() { browserConfig, courseCode, courseData = { - courseInfo: {}, + courseInfo: { course_application_info: '' }, syllabusList: [], }, isCancelledOrDeactivated, @@ -59,6 +59,16 @@ function CoursePage() { courseImage = `${browserConfig.imageStorageUri}${courseImage}` const decisionToDiscontinue = hasSyllabus ? activeSyllabus.course_decision_to_discontinue : '' + const course_valid_from = hasSyllabus ? activeSyllabus.course_valid_from : '' + + const courseInformationToRounds = { + course_code: courseCode, + course_examiners: courseInfo.course_examiners, + course_contact_name: courseInfo.course_contact_name, + course_main_subject: courseInfo.course_main_subject, + course_level_code: courseInfo.course_level_code, + course_valid_from, + } useEffect(() => { let isMounted = true @@ -119,7 +129,12 @@ function CoursePage() { {showRoundData && ( - + )} ', () => { ], courseCode: 'MF1016', courseData: { - courseInfo: {}, + courseInfo: { + course_application_info: '', + }, courseTitleData: { course_code: 'MF1016', course_credits: 9, diff --git a/server/apiCalls/getFilteredData.js b/server/apiCalls/getFilteredData.js index 2f90ab6d..b0c28b72 100644 --- a/server/apiCalls/getFilteredData.js +++ b/server/apiCalls/getFilteredData.js @@ -15,10 +15,20 @@ const { const koppsCourseData = require('./koppsCourseData') const courseApi = require('./kursinfoApi') +function parceContactName(infoContactName, language) { + const courseContactName = parseOrSetEmpty(infoContactName, language) + if (courseContactName === INFORM_IF_IMPORTANT_INFO_IS_MISSING[language]) return courseContactName + const emailBracketsRexEx = /<|>/gi + const contact = courseContactName.replace(emailBracketsRexEx, '') + return contact +} + function _parseCourseDefaultInformation(courseDetails, language) { const { course, formattedGradeScales, mainSubjects } = courseDetails return { + course_application_info: parseOrSetEmpty(course.applicationInfo, language, true), // applicationInfo is info for research students (Label in Kopps: "Information for research students about course offerings") course_code: parseOrSetEmpty(course.courseCode), + course_contact_name: parceContactName(course.infoContactName, language), course_department: parseOrSetEmpty(course.department.name, language), course_department_code: parseOrSetEmpty(course.department.code, language), course_department_link: buildCourseDepartmentLink(course.department, language), @@ -32,7 +42,11 @@ function _parseCourseDefaultInformation(courseDetails, language) { mainSubjects && mainSubjects.length > 0 ? mainSubjects.join(', ') : INFORM_IF_IMPORTANT_INFO_IS_MISSING_ABOUT_MIN_FIELD_OF_STUDY[language], + course_possibility_to_addition: parseOrSetEmpty(course.possibilityToAddition, language), + course_possibility_to_completions: parseOrSetEmpty(course.possibilityToCompletion, language), course_recruitment_text: parseOrSetEmpty(course.recruitmentText, language, true), + course_required_equipment: parseOrSetEmpty(course.requiredEquipment, language), + course_suggested_addon_studies: parseOrSetEmpty(course.addOn, language), course_state: parseOrSetEmpty(course.state, language, true), } } diff --git a/server/controllers/__tests__/courseCtrl.test.js b/server/controllers/__tests__/courseCtrl.test.js index 0e4b31f1..66ce6126 100644 --- a/server/controllers/__tests__/courseCtrl.test.js +++ b/server/controllers/__tests__/courseCtrl.test.js @@ -102,7 +102,13 @@ describe('Discontinued course to test', () => { expect(response.render).toHaveBeenCalled() expect(testResponse.title).toBe(mockedDiscontinuedCourse.course.courseCode) expect(testResponse.compressedData.courseCode).toBe(mockedDiscontinuedCourse.course.courseCode) + expect(testResponse.compressedData.courseData.courseInfo.course_application_info).toBe( + mockedDiscontinuedCourse.course.applicationInfo + ) expect(testResponse.html.context.courseCode).toBe(mockedDiscontinuedCourse.course.courseCode) + expect(testResponse.html.context.courseData.courseInfo.course_application_info).toBe( + mockedDiscontinuedCourse.course.applicationInfo + ) expect(testResponse.html).toMatchInlineSnapshot(` { @@ -117,7 +123,9 @@ describe('Discontinued course to test', () => { "courseCode": "FCK3305", "courseData": { "courseInfo": { + "course_application_info": "

Kursen ges inte läsåret 22/23.

Kontakta examinator / kursansvarig för information.

", "course_code": "FCK3305", + "course_contact_name": "Ingen information tillagd", "course_department": "CBH/Kemi", "course_department_code": "CE", "course_department_link": "CBH/Kemi", @@ -129,9 +137,13 @@ describe('Discontinued course to test', () => { "course_level_code": "RESEARCH", "course_literature": "

Litteratur anvisas vid kursstart.

", "course_main_subject": "Denna kurs tillhör inget huvudområde.", + "course_possibility_to_addition": "Ingen information tillagd", + "course_possibility_to_completions": "Ingen information tillagd", "course_recommended_prerequisites": "", "course_recruitment_text": "

Teori och metoder inom glykovetenskap.

", + "course_required_equipment": "Ingen information tillagd", "course_state": "ESTABLISHED", + "course_suggested_addon_studies": "Ingen information tillagd", "course_supplemental_information": "", "imageFromAdmin": "own_image", "sellingText": "

Fantastisk kurs

", @@ -154,6 +166,7 @@ describe('Discontinued course to test', () => { "course_goals": "Ingen information tillagd", "course_literature": "Ingen information tillagd", "course_literature_comment": "Ingen information tillagd", + "course_required_equipment": "", "course_requirments_for_final_grade": "", "course_transitional_reg": "", "course_valid_from": undefined, @@ -185,6 +198,7 @@ describe('Discontinued course to test', () => { "course_goals": "

Efter fullföljande av kursen förväntas studenten kunna

  • Visa kunskap om kolhydraters mångfald, dess betydelse för biologiska system, samt hur de kan förändra struktur och funktion hos andra biologiska molekyler.
  • Visa kunskap om cellväggens struktur och funktion hos vedbildande växter, samt övergripande förståelse för hur dess sammansättning kan förändras för att möjliggöra nya tillämpningar, t.ex. för att underlätta bearbetning för energi- och biomaterialproduktion.
  • Visa förmåga att redogöra och reflektera över koncept och metoder som används för att producera byggstenar från växtbiomassa, och hur de kan sättas ihop till nya material med skräddarsydda egenskaper och funktionaliteter.
  • Visa förmåga att planera och utföra praktiska experiment inom kolhydratteknik, samt att analysera och redogöra resultaten i form av skriftliga rapporter.
  • Visa förmåga att identifiera och diskutera hur kolhydratteknik kan bidra till en hållbar samhällsutveckling inom konsumtion, produktion och material, t.ex. genom att återanvända redan existerande produkter, eller tillverkning av nya resurssmarta och förnyelsebara material.
", "course_literature": "Ingen information tillagd", "course_literature_comment": "Ingen information tillagd", + "course_required_equipment": "Ingen information tillagd", "course_requirments_for_final_grade": "

Godkänd skriftlig tentamen, godkända inlämningsuppgifter kopplade till föreläsningarna, 100% närvaro på laborationer och slutförande av laborationer, samt godkända laborationsrapporter.

", "course_transitional_reg": "", "course_valid_from": { diff --git a/server/controllers/__tests__/createSyllabusList.test.js b/server/controllers/__tests__/createSyllabusList.test.js index ea8a512d..0ebb46f3 100644 --- a/server/controllers/__tests__/createSyllabusList.test.js +++ b/server/controllers/__tests__/createSyllabusList.test.js @@ -13,6 +13,7 @@ const expectedSyllabusList = [ '

Announced no later than 4 weeks before the start of the course on the course web page.

', course_valid_from: { year: 2019, semesterNumber: 2 }, course_valid_to: undefined, + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: @@ -35,6 +36,7 @@ const expectedSyllabusList = [ course_literature_comment: 'No information inserted', course_valid_from: { year: 2010, semesterNumber: 2 }, course_valid_to: { year: 2019, semesterNumber: 1 }, + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: @@ -54,6 +56,7 @@ const expectedSyllabusList = [ course_literature_comment: 'No information inserted', course_valid_from: { year: 2009, semesterNumber: 2 }, course_valid_to: { year: 2010, semesterNumber: 1 }, + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: @@ -73,6 +76,7 @@ const expectedSyllabusList = [ course_literature_comment: 'No information inserted', course_valid_from: { year: 2008, semesterNumber: 2 }, course_valid_to: { year: 2009, semesterNumber: 1 }, + course_required_equipment: 'No information inserted', course_examination: "
  • TEN1 - \n Examination,\n 7.5 credits, \n grading scale: A, B, C, D, E, FX, F \n
", course_examination_comments: diff --git a/server/controllers/createSyllabusList.js b/server/controllers/createSyllabusList.js index a025988d..3ac4576b 100644 --- a/server/controllers/createSyllabusList.js +++ b/server/controllers/createSyllabusList.js @@ -43,6 +43,7 @@ const _createEmptySyllabusData = language => ({ course_literature_comment: INFORM_IF_IMPORTANT_INFO_IS_MISSING[language], course_valid_from: undefined, course_valid_to: undefined, + course_required_equipment: '', course_examination: INFORM_IF_IMPORTANT_INFO_IS_MISSING[language], course_examination_comments: '', course_ethical: '', @@ -74,6 +75,7 @@ const _parseSyllabusData = (courseDetails, semesterIndex = 0, language) => { course_literature_comment: parseOrSetEmpty(semesterSyllabus.courseSyllabus.literatureComment, language), course_valid_from: parseSemesterIntoYearSemesterNumber(parseOrSetEmpty(semesterSyllabus.validFromTerm.term)), course_valid_to: undefined, + course_required_equipment: parseOrSetEmpty(semesterSyllabus.courseSyllabus.requiredEquipment, language), course_examination: examinationSets && Object.keys(examinationSets).length > 0 ? _parseExamObject( diff --git a/server/controllers/mocks/mockedDiscontinuedCourse.js b/server/controllers/mocks/mockedDiscontinuedCourse.js index 9ed73633..c535ad6d 100644 --- a/server/controllers/mocks/mockedDiscontinuedCourse.js +++ b/server/controllers/mocks/mockedDiscontinuedCourse.js @@ -1,5 +1,7 @@ const mockedDiscontinuedCourse = { course: { + applicationInfo: + '

Kursen ges inte läsåret 22/23.

Kontakta examinator / kursansvarig för information.

', courseCode: 'FCK3305', versionNumber: 1, departmentCode: 'CE',