Skip to content

Commit f8cf953

Browse files
added question for searching for courses that span over multiple periods
1 parent 6201002 commit f8cf953

File tree

9 files changed

+79
-22
lines changed

9 files changed

+79
-22
lines changed

scripts/get_prod_db.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ fi
3131
echo "Creating backups folder"
3232
mkdir -p ${BACKUPS}
3333

34-
# --- Norppa Selection ---
34+
# --- Selection ---
3535
echo "Listing available Apparaatti backups in S3 bucket..."
3636
backup_files=$(s3cmd -c "$S3_CONF" ls "s3://psyduck/${FOLDER_NAME}/" | awk '{print $4}' | grep '\.sql\.gz$')
3737

3838
if [ -z "$backup_files" ]; then
39-
echo "No Norppa backup files found in S3 bucket!"
39+
echo "No backup files found in S3 bucket!"
4040
exit 1
4141
fi
4242

src/client/V2/filterContext.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ interface FilterContextType {
6464
setMooc: (s: string) => void
6565
collaboration: string
6666
setCollaboration: (s: string) => void
67+
multiPeriod: string
68+
setMultiPeriod: (s: string) => void
6769
strictFilters: string[]
6870
setStrictFilters: (s: string[]) => void
6971
}
@@ -165,6 +167,11 @@ export const filterConfigMap = (filters: any) => new Map([
165167
setState: filters.setCollaboration,
166168
superToggle: false
167169
}],
170+
['multi-period', {
171+
state: filters.multiPeriod,
172+
setState: filters.setMultiPeriod,
173+
superToggle: false
174+
}],
168175
])
169176

170177
// Map coordinate keys to filter IDs for recommendation reasons
@@ -183,6 +190,7 @@ export const coordinateKeyToFilterId: { [key: string]: string } = {
183190
mooc: 'mooc',
184191
finmu: 'finmu',
185192
collaboration: 'collaboration',
193+
multiPeriod: 'multi-period',
186194
}
187195

188196
// Get translated short name for a coordinate key
@@ -260,6 +268,7 @@ export const FilterContextProvider = ({ children }: { children: ReactNode }) =>
260268
const [independent, setIndependent] = useState('')
261269
const [mooc, setMooc] = useState('')
262270
const [collaboration, setCollaboration] = useState('')
271+
const [multiPeriod, setMultiPeriod] = useState('')
263272
const [strictFilters, setStrictFilters] = useState<string[]>(['collaboration'])
264273

265274

@@ -345,6 +354,7 @@ export const FilterContextProvider = ({ children }: { children: ReactNode }) =>
345354
'independent': getTrueFilterValue(independent, 'independent'),
346355
'mooc': getTrueFilterValue(mooc, 'mooc'),
347356
'collaboration': getTrueFilterValue(collaboration, 'collaboration'),
357+
'multi-period': getTrueFilterValue(multiPeriod, 'multi-period'),
348358
}
349359

350360
const answerData = Object.fromEntries(
@@ -387,6 +397,7 @@ export const FilterContextProvider = ({ children }: { children: ReactNode }) =>
387397
independent,
388398
mooc,
389399
collaboration,
400+
multiPeriod,
390401
strictFilters,
391402
])
392403

@@ -446,6 +457,8 @@ export const FilterContextProvider = ({ children }: { children: ReactNode }) =>
446457
setMooc,
447458
collaboration,
448459
setCollaboration,
460+
multiPeriod,
461+
setMultiPeriod,
449462
strictFilters,
450463
setStrictFilters,
451464
}}

src/client/hooks/useQuestions.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,21 @@ const useQuestions = () => {
268268
},
269269
],
270270
},
271+
{
272+
number: '',
273+
mandatory: false,
274+
effects: 'multiPeriod',
275+
id: 'multi-period',
276+
shortName: t('filterShortName:Kurssinpituus'),
277+
type: 'multi',
278+
variants: [
279+
{
280+
name: 'default',
281+
question: t('form:multiPeriodQuestion'),
282+
options: generateGeneralYesNoOptions(),
283+
},
284+
],
285+
},
271286
{
272287
number: '',
273288
mandatory: false,

src/client/locales/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default {
2626
'flexibleQuestion': 'I want to take a course with a flexible schedule',
2727
'moocQuestion': 'Do you want to search for MOOCs (Massive Open Online Courses)?',
2828
'collaborationQuestion': 'Show collaboration partner courses',
29+
'multiPeriodQuestion': 'Search for courses that span multiple periods',
2930
'finnish': 'finnish',
3031
'swedish': 'swedish',
3132
'english': 'english',
@@ -125,6 +126,7 @@ export default {
125126
'flexible': 'Want flexible schedule',
126127
'mooc': 'Search for MOOCs',
127128
'finmu': 'FinMU related',
129+
'multiPeriod': 'Course duration',
128130
},
129131
'filterShortName': {
130132
'Opinto-oikeus': 'Study Right',
@@ -144,5 +146,6 @@ export default {
144146
'Periodi': 'Period',
145147
'MOOC': 'MOOC',
146148
'Yhteistyo': 'Collaboration',
149+
'Kurssinpituus': 'Course Duration',
147150
}
148151
}

src/client/locales/fi.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default {
2626
'flexibleQuestion': 'Haluan osallistua kurssille, jonka aikataulu on joustava.',
2727
'moocQuestion': 'Haluatko etsiä MOOC-kursseja (Massive Open Online Courses)?',
2828
'collaborationQuestion': 'Näytä yhteistyökumppanien kursseja',
29+
'multiPeriodQuestion': 'Hae kursseja jotka kestävät usean periodin ajan',
2930
'finnish': 'suomi',
3031
'swedish': 'ruotsi',
3132
'english': 'englanti',
@@ -125,6 +126,7 @@ export default {
125126
'flexible': 'Haluan joustavan aikataulun',
126127
'mooc': 'Etsi MOOC-kursseja',
127128
'finmu': 'FinMU liittyvä',
129+
'multiPeriod': 'Kurssin pituus',
128130
},
129131
'filterShortName': {
130132
'Opinto-oikeus': 'Opinto-oikeus',
@@ -144,5 +146,6 @@ export default {
144146
'Periodi': 'Periodi',
145147
'MOOC': 'MOOC',
146148
'Yhteistyo': 'Yhteistyö',
149+
'Kurssinpituus': 'Kurssin pituus',
147150
}
148151
}

src/client/locales/sv.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ export default {
2525
'independentQuestion': 'Jag vill arbeta självständigt eller autonomt.',
2626
'flexibleQuestion': 'Jag vill ta en kurs med ett flexibelt schema.',
2727
'moocQuestion': 'Vill du söka efter MOOCs (Massive Open Online Courses)?',
28-
'collaborationQuestion': 'Visa samarbetspartnerkurser',
29-
'finnish': 'finska',
28+
'collaborationQuestion': 'Visa samarbetspartnerkurser', 'multiPeriodQuestion': 'Sök kurser som sträcker sig över flera perioder', 'finnish': 'finska',
3029
'swedish': 'svenska',
3130
'english': 'engelska',
3231
'yes': 'Ja',
@@ -121,6 +120,7 @@ export default {
121120
'flexible': 'Vill ha flexibelt schema',
122121
'mooc': 'Sök MOOC-kurser',
123122
'finmu': 'FinMU relaterad',
123+
'multiPeriod': 'Kurslängd',
124124
},
125125
'filterShortName': {
126126
'Opinto-oikeus': 'Studierätt',
@@ -140,5 +140,6 @@ export default {
140140
'Periodi': 'Period',
141141
'MOOC': 'MOOC',
142142
'Yhteistyo': 'Samarbete',
143+
'Kurssinpituus': 'Kurslängd',
143144
}
144145
}

src/common/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export type CourseCoordinates = {
6969
collaboration?: number | null;
7070
studyYear?: string | null;
7171
studyPeriod?: string[] | null;
72+
multiPeriod?: number | null;
7273
}
7374

7475
export type UserCoordinates = CourseCoordinates;
@@ -94,7 +95,7 @@ export type CourseData = {
9495
name: LocalizedString
9596
startDate: Date
9697
endDate: Date
97-
period: null | Period
98+
period: Period[] | null
9899
customCodeUrns: Record<string, string[]>
99100
courseUnitRealisationTypeUrn: string
100101
courseCodes: string[]
@@ -152,6 +153,7 @@ export type AnswerData = {
152153
'lang': string;
153154
'primary-language': string;
154155
'primary-language-specification': string;
156+
'multi-period': string;
155157
};
156158

157159

src/server/util/pointRecommendCourses.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ function pointRecommendedCourses(courses: CourseRecommendation[], userCoordinate
125125
filterOnFail: true,
126126
f: (c: CourseRecommendation, userCoordinates: UserCoordinates) => {
127127
if (!userCoordinates.studyYear || userCoordinates.studyYear === 'neutral') return true
128-
return userCoordinates.studyYear == c.course.period?.startYear
128+
129+
if (!c.course.period || c.course.period.length === 0) return false
130+
131+
return c.course.period.some(p => userCoordinates.studyYear === p.startYear)
129132
}
130133
},
131134
{
@@ -142,6 +145,10 @@ function pointRecommendedCourses(courses: CourseRecommendation[], userCoordinate
142145
return match
143146
}
144147
},
148+
{
149+
field: 'multiPeriod',
150+
filterOnFail: strictFields.includes('multi-period'),
151+
},
145152
]
146153

147154
const recommendationWithPoints = noExams.map((c) => {

src/server/util/recommender.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { uniqueVals } from './misc.ts'
33
import type { OrganisationRecommendation } from './organisationCourseRecommmendations.ts'
44
import {challegeCourseCodes, codesInOrganisations, courseHasAnyOfCodes, courseHasAnyRealisationCodeUrn, courseHasCustomCodeUrn, courseMatches, finmuMentroingCourseCodes, getUserOrganisationRecommendations, languageSpesificCodes, languageToStudy, mentoringCourseCodes, readOrganisationRecommendationData } from './organisationCourseRecommmendations.ts'
55
import { dateObjToPeriod, getStudyPeriod, parseDate, getStudyYear } from './studyPeriods.ts'
6+
import studyPeriods from './studyPeriods.ts'
67
import { curcusWithUnitIdOf, curWithIdOf, cuWithCourseCodeOf, organisationWithGroupIdOf } from './dbActions.ts'
78
import pointRecommendedCourses from './pointRecommendCourses.ts'
89
import { allowedStudyPlaces, collaborationOrganisationNames, collaborationOrganisationCourseNameIncludes, correctValue, incorrectValue, notAnsweredValue, organisationCodeToUrn } from './constants.ts'
@@ -74,6 +75,7 @@ function calculateUserCoordinates(answerData: AnswerData) {
7475
collaboration: commonCoordinateFromAnswerData(readAnswer(answerData, 'collaboration'), correctValue, incorrectValue, incorrectValue),
7576
studyYear: readAnswer(answerData, 'study-year'),
7677
studyPeriod: readAsStringArr(readAnswer(answerData, 'study-period')),
78+
multiPeriod: commonCoordinateFromAnswerData(readAnswer(answerData, 'multi-period'), correctValue, incorrectValue, null),
7779
}
7880
return userCoordinates
7981
}
@@ -207,6 +209,7 @@ async function calculateCourseCoordinates(course: CourseData, userCoordinates: U
207209

208210
const isChallengeCourse = courseMatches(course, challegeCourseCodes, courseLanguageType)
209211
const isCollaboration = await courseIsCollaboration(course)
212+
const isMultiPeriod = courseSpansMultiplePeriods(course)
210213

211214
const courseCoordinates = {
212215
date: course.startDate.getTime(),
@@ -223,7 +226,8 @@ async function calculateCourseCoordinates(course: CourseData, userCoordinates: U
223226
independent: isIndependent ? correctValue : incorrectValue,
224227
flexible: hasFlexibleCodeUrn ? correctValue : incorrectValue,
225228
mooc: hasMoocCodeUrn ? correctValue : incorrectValue,
226-
collaboration: isCollaboration ? correctValue : incorrectValue
229+
collaboration: isCollaboration ? correctValue : incorrectValue,
230+
multiPeriod: isMultiPeriod ? correctValue : incorrectValue
227231
}
228232

229233

@@ -295,24 +299,33 @@ export async function getRealisationsWithCourseUnitCodes(courseCodeStrings: stri
295299
return courseRealisationsWithCodes
296300
}
297301

298-
const getPeriodForCourse = (cur) => {
299-
300-
const studyPeriods = dateObjToPeriod(cur.startDate)
301-
302-
const studyPeriod = studyPeriods[0]
303-
if(!studyPeriod){
304-
dateObjToPeriod(cur.startDate, true)
302+
const getPeriodForCourse = (cur): Period[] | null => {
303+
const courseStart = cur.startDate
304+
const courseEnd = cur.endDate
305+
306+
const overlappingPeriods = studyPeriods.periods.filter(periodData => {
307+
const periodStart = parseDate(periodData.start_date)
308+
const periodEnd = parseDate(periodData.end_date)
309+
return periodStart <= courseEnd && periodEnd >= courseStart
310+
})
311+
312+
if (overlappingPeriods.length === 0) {
305313
return null
306314
}
315+
316+
const periods: Period[] = overlappingPeriods.map(periodData => ({
317+
name: periodData.name,
318+
startDate: parseDate(periodData.start_date),
319+
endDate: parseDate(periodData.end_date),
320+
startYear: periodData.start_year,
321+
endYear: periodData.end_year
322+
}))
323+
324+
return periods
325+
}
307326

308-
const period: Period = {
309-
name: studyPeriod.name,
310-
startDate: parseDate(studyPeriod.start_date),
311-
endDate: parseDate(studyPeriod.end_date),
312-
startYear: studyPeriod.start_year,
313-
endYear: studyPeriod.end_year
314-
}
315-
return period
327+
function courseSpansMultiplePeriods(course: CourseData): boolean {
328+
return (course.period?.length ?? 0) > 1
316329
}
317330

318331
const getPeriodsWantedByUser = (periodsArg) => {

0 commit comments

Comments
 (0)