Merge branch 'master' into workflows
This commit is contained in:
commit
3e6935490b
12
CHANGELOG.md
12
CHANGELOG.md
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [20.13.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v20.12.1...v20.13.0) (2020-10-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **allocations:** display participant counts to admins ([b79bac7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b79bac777c6d349a626ea4efa6c43141b7f669d0))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **allocations:** fix allocation-course-accept-substitutes ([b4df980](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b4df98069982752e36e69571f5557a6179b44cff))
|
||||||
|
|
||||||
### [20.12.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v20.12.0...v20.12.1) (2020-10-14)
|
### [20.12.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v20.12.0...v20.12.1) (2020-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
FAQNoCampusAccount: Ich habe keine LMU-Benutzerkennung (ehem. Campus-Kennung); kann ich trotzdem Zugang zum System erhalten?
|
FAQNoCampusAccount: Ich habe keine LMU-Benutzerkennung (ehem. Campus-Kennung); kann ich trotzdem Zugang zum System erhalten?
|
||||||
FAQForgottenPassword: Ich habe mein Passwort vergessen
|
FAQForgottenPassword: Ich habe mein Passwort vergessen
|
||||||
FAQCampusCantLogin: Ich kann mich mit meiner LMU-Benutzerkennung (ehem. Campus-Kennung) nicht anmelden
|
FAQCampusCantLogin: Ich kann mich mit meiner LMU-Benutzerkennung (ehem. Campus-Kennung) nicht anmelden
|
||||||
FAQCourseCorrectorsTutors: Wie kann ich Tutoren oder Korrektoren für meinen Kurs einstellen?
|
FAQCourseCorrectorsTutors: Wie kann ich Tutoren oder Korrektoren für meinen Kurs konfigurieren?
|
||||||
FAQNotLecturerHowToCreateCourses: Wie kann ich einen neuen Kurs anlegen?
|
FAQNotLecturerHowToCreateCourses: Wie kann ich einen neuen Kurs anlegen?
|
||||||
FAQExamPoints: Warum kann ich bei meiner Klausur keine Punkte eintragen?
|
FAQExamPoints: Warum kann ich bei meiner Klausur keine Punkte eintragen?
|
||||||
|
FAQInvalidCredentialsAdAccountDisabled: Ich kann mich nicht anmelden und bekomme die Meldung „Benutzereintrag gesperrt“
|
||||||
|
FAQAllocationNoPlaces: Ich habe über eine Zentralanmeldung keine Plätze/nicht die Plätze, die ich möchte, erhalten
|
||||||
@ -4,3 +4,5 @@ FAQCampusCantLogin: I can't log in using my LMU user ID (formerly Campus-ID)
|
|||||||
FAQCourseCorrectorsTutors: How can I add tutors or correctors to my course?
|
FAQCourseCorrectorsTutors: How can I add tutors or correctors to my course?
|
||||||
FAQNotLecturerHowToCreateCourses: How can I create new courses?
|
FAQNotLecturerHowToCreateCourses: How can I create new courses?
|
||||||
FAQExamPoints: Why can't I enter achievements for my exam as points?
|
FAQExamPoints: Why can't I enter achievements for my exam as points?
|
||||||
|
FAQInvalidCredentialsAdAccountDisabled: I can't log in and am instead given the message “Account disabled”
|
||||||
|
FAQAllocationNoPlaces: I did not receive any places/the places I wanted from a central allocation
|
||||||
@ -206,6 +206,7 @@ CourseAllocationMinCapacityTip: Wenn der Veranstaltung bei der Zentralanmeldung
|
|||||||
CourseAllocationMinCapacityMustBeNonNegative: Minimale Teilnehmeranzahl darf nicht negativ sein
|
CourseAllocationMinCapacityMustBeNonNegative: Minimale Teilnehmeranzahl darf nicht negativ sein
|
||||||
CourseAllocationCourseAcceptsSubstitutesUntil: Akzeptiert Nachrücker bis
|
CourseAllocationCourseAcceptsSubstitutesUntil: Akzeptiert Nachrücker bis
|
||||||
CourseAllocationCourseAcceptsSubstitutesNever: Akzeptiert keine Nachrücker
|
CourseAllocationCourseAcceptsSubstitutesNever: Akzeptiert keine Nachrücker
|
||||||
|
CourseAllocationCourseParticipants: Teilnehmer
|
||||||
CourseApplicationInstructions: Anweisungen zur Bewerbung/Anmeldung
|
CourseApplicationInstructions: Anweisungen zur Bewerbung/Anmeldung
|
||||||
CourseApplicationInstructionsTip: Wird den Studierenden angezeigt, wenn diese sich für Ihre Veranstaltung bewerben bzw. bei dieser anmelden
|
CourseApplicationInstructionsTip: Wird den Studierenden angezeigt, wenn diese sich für Ihre Veranstaltung bewerben bzw. bei dieser anmelden
|
||||||
CourseApplicationTemplate: Bewerbungsvorlagen
|
CourseApplicationTemplate: Bewerbungsvorlagen
|
||||||
@ -262,7 +263,9 @@ CourseApplicationsAllocatedDirectory: zentral
|
|||||||
CourseApplicationsNotAllocatedDirectory: direkt
|
CourseApplicationsNotAllocatedDirectory: direkt
|
||||||
|
|
||||||
CourseNoAllocationsAvailable: Es sind aktuell keine Zentralanmeldungen verfügbar
|
CourseNoAllocationsAvailable: Es sind aktuell keine Zentralanmeldungen verfügbar
|
||||||
AllocationStaffRegisterToExpired: Es dürfen keine Änderungen an der Eintragung des Kurses zur Zentralanmeldung mehr vorgenommen werden. Ihre Änderungen wurden ignoriert.
|
AllocationStaffRegisterToExpiredAllocation: Die Frist zur Eintrageng von Kursen in die Zentralanmeldung ist verstrichen. Die Teilnahme darf nicht mehr verändert werden.
|
||||||
|
AllocationStaffRegisterToExpiredMinCapacity: Die Frist zur Eintrageng von Kursen in die Zentralanmeldung ist verstrichen. Die minimale Kapazität darf nicht mehr verändert werden.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CourseFormSectionRegistration: Anmeldung zum Kurs
|
CourseFormSectionRegistration: Anmeldung zum Kurs
|
||||||
@ -1995,6 +1998,8 @@ ExamOccurrenceStartMustBeAfterExamStart eoName@ExamOccurrenceName: Beginn des Te
|
|||||||
ExamOccurrenceEndMustBeBeforeExamEnd eoName@ExamOccurrenceName: Ende des Termins #{eoName} liegt nach dem Ende der Prüfung
|
ExamOccurrenceEndMustBeBeforeExamEnd eoName@ExamOccurrenceName: Ende des Termins #{eoName} liegt nach dem Ende der Prüfung
|
||||||
ExamOccurrenceDuplicate eoRoom@Text eoRange@Text: Raum #{eoRoom}, Termin #{eoRange} kommt mehrfach mit der selben Beschreibung vor
|
ExamOccurrenceDuplicate eoRoom@Text eoRange@Text: Raum #{eoRoom}, Termin #{eoRange} kommt mehrfach mit der selben Beschreibung vor
|
||||||
ExamOccurrenceDuplicateName eoName@ExamOccurrenceName: Interne Terminbezeichnung #{eoName} kommt mehrfach vor
|
ExamOccurrenceDuplicateName eoName@ExamOccurrenceName: Interne Terminbezeichnung #{eoName} kommt mehrfach vor
|
||||||
|
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName@ExamOccurrenceName: Termin #{eoName} kann nicht gelöscht werden, da noch Teilnehmer diesem Termin zugewiesen sind. Über die Liste von Prüfungsteilnehmern können Sie zunächst die entsprechenden Terminzuweisungen entfernen.
|
||||||
|
ExamPartCannotBeDeletedDueToResults exampartnum@ExamPartNumber: Teil #{exampartnum} kann nicht gelöscht werden, da bereits Prüfungsergebnisse für diesen Teil eingetragen wurden.
|
||||||
|
|
||||||
VersionHistory: Versionsgeschichte
|
VersionHistory: Versionsgeschichte
|
||||||
KnownBugs: Bekannte Bugs
|
KnownBugs: Bekannte Bugs
|
||||||
@ -2317,6 +2322,7 @@ AllocationNotificationNewCourseCurrentlyOn: Aktuell würden Sie benachrichtigt w
|
|||||||
AllocationNotificationLoginFirst: Um Ihre Benachrichtigungseinstellungen zu ändern, loggen Sie sich bitte zunächst ein.
|
AllocationNotificationLoginFirst: Um Ihre Benachrichtigungseinstellungen zu ändern, loggen Sie sich bitte zunächst ein.
|
||||||
AllocationNextSubstitutesDeadline: Nächster Kurs akzeptiert Nachrücker bis
|
AllocationNextSubstitutesDeadline: Nächster Kurs akzeptiert Nachrücker bis
|
||||||
AllocationNextSubstitutesDeadlineNever: Keine Kurse akzeptieren mehr Nachrücker
|
AllocationNextSubstitutesDeadlineNever: Keine Kurse akzeptieren mehr Nachrücker
|
||||||
|
AllocationFreeCapacity: Freie Plätze
|
||||||
|
|
||||||
AllocationSchoolShort: Institut
|
AllocationSchoolShort: Institut
|
||||||
Allocation: Zentralanmeldung
|
Allocation: Zentralanmeldung
|
||||||
|
|||||||
@ -206,6 +206,7 @@ CourseAllocationMinCapacityTip: If fewer students than this number were to be as
|
|||||||
CourseAllocationMinCapacityMustBeNonNegative: Minimum number of participants must not be negative
|
CourseAllocationMinCapacityMustBeNonNegative: Minimum number of participants must not be negative
|
||||||
CourseAllocationCourseAcceptsSubstitutesUntil: Accepts substitutes until
|
CourseAllocationCourseAcceptsSubstitutesUntil: Accepts substitutes until
|
||||||
CourseAllocationCourseAcceptsSubstitutesNever: Does not accept substitutes
|
CourseAllocationCourseAcceptsSubstitutesNever: Does not accept substitutes
|
||||||
|
CourseAllocationCourseParticipants: Participants
|
||||||
CourseApplicationInstructions: Instructions for application
|
CourseApplicationInstructions: Instructions for application
|
||||||
CourseApplicationInstructionsTip: Will be shown to students if they decide to apply for this course
|
CourseApplicationInstructionsTip: Will be shown to students if they decide to apply for this course
|
||||||
CourseApplicationTemplate: Application template
|
CourseApplicationTemplate: Application template
|
||||||
@ -262,7 +263,8 @@ CourseApplicationsAllocatedDirectory: central
|
|||||||
CourseApplicationsNotAllocatedDirectory: direct
|
CourseApplicationsNotAllocatedDirectory: direct
|
||||||
|
|
||||||
CourseNoAllocationsAvailable: There are no ongoing central allocations
|
CourseNoAllocationsAvailable: There are no ongoing central allocations
|
||||||
AllocationStaffRegisterToExpired: You cannot change course properties concerning the central allocation after the course registration period. Your changes may have been discarded.
|
AllocationStaffRegisterToExpiredAllocation: The course registration period for the central allocation is over. Participation may not be changed.
|
||||||
|
AllocationStaffRegisterToExpiredMinCapacity: The course registration period for the central allocation is over. Minimum capacity may not be changed.
|
||||||
|
|
||||||
CourseFormSectionRegistration: Registration
|
CourseFormSectionRegistration: Registration
|
||||||
CourseFormSectionAdministration: Administration
|
CourseFormSectionAdministration: Administration
|
||||||
@ -1955,6 +1957,8 @@ ExamOccurrenceStartMustBeAfterExamStart eoName: Start of the occurrence #{eoName
|
|||||||
ExamOccurrenceEndMustBeBeforeExamEnd eoName: End of the occurrence #{eoName} must be before the exam end
|
ExamOccurrenceEndMustBeBeforeExamEnd eoName: End of the occurrence #{eoName} must be before the exam end
|
||||||
ExamOccurrenceDuplicate eoRoom eoRange: Combination of room #{eoRoom} and occurrence #{eoRange} occurs multiple times
|
ExamOccurrenceDuplicate eoRoom eoRange: Combination of room #{eoRoom} and occurrence #{eoRange} occurs multiple times
|
||||||
ExamOccurrenceDuplicateName eoName: Internal name #{eoName} occurs multiple times
|
ExamOccurrenceDuplicateName eoName: Internal name #{eoName} occurs multiple times
|
||||||
|
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName: Occurrence #{eoName} cannot be deleted because participants are registered for it. You can remove the offending registrations via the list of exam participants.
|
||||||
|
ExamPartCannotBeDeletedDueToResults exampartnum: Part #{exampartnum} cannot be deleted because some exam part results were already entered for it.
|
||||||
|
|
||||||
VersionHistory: Version history
|
VersionHistory: Version history
|
||||||
KnownBugs: Known bugs
|
KnownBugs: Known bugs
|
||||||
@ -2277,6 +2281,7 @@ AllocationNotificationNewCourseCurrentlyOn: Currently you would be notified.
|
|||||||
AllocationNotificationLoginFirst: To change your notification settings, please log in first.
|
AllocationNotificationLoginFirst: To change your notification settings, please log in first.
|
||||||
AllocationNextSubstitutesDeadline: Next course accepts substitutes until
|
AllocationNextSubstitutesDeadline: Next course accepts substitutes until
|
||||||
AllocationNextSubstitutesDeadlineNever: No course currently accepts substitutes
|
AllocationNextSubstitutesDeadlineNever: No course currently accepts substitutes
|
||||||
|
AllocationFreeCapacity: Free capacity
|
||||||
|
|
||||||
AllocationSchoolShort: Department
|
AllocationSchoolShort: Department
|
||||||
Allocation: Central allocation
|
Allocation: Central allocation
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
import ((nixpkgs {}).fetchFromGitHub {
|
import ((nixpkgs {}).fetchFromGitHub {
|
||||||
owner = "NixOS";
|
owner = "NixOS";
|
||||||
repo = "nixpkgs";
|
repo = "nixpkgs";
|
||||||
rev = "bc00ecedfa709f4fa91d445dd76ecd792cb2c728";
|
rev = "a7a1447e5d40a9ad90983d33e151f5474eddeed9";
|
||||||
sha256 = "0plhwb04srr4b0h7w8qlqi207a19szz2wqz6r4gmic856jlkchaa";
|
sha256 = "1zb8wgsq9grrsdcz81y08h45rj8i5r8ckjhg2cv1cqmam4dczcrf";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
})
|
})
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uni2work",
|
"name": "uni2work",
|
||||||
"version": "20.12.1",
|
"version": "20.13.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uni2work",
|
"name": "uni2work",
|
||||||
"version": "20.12.1",
|
"version": "20.13.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: uniworx
|
name: uniworx
|
||||||
version: 20.12.1
|
version: 20.13.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- base
|
- base
|
||||||
|
|||||||
@ -160,6 +160,7 @@ campusUserMatr' pool mode
|
|||||||
|
|
||||||
newtype ADInvalidCredentials = ADInvalidCredentials ADError
|
newtype ADInvalidCredentials = ADInvalidCredentials ADError
|
||||||
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
|
deriving newtype (Universe, Finite, Enum, Bounded, PathPiece, ToJSON, FromJSON, ToJSONKey, FromJSONKey)
|
||||||
|
|
||||||
isUnusualADError :: ADError -> Bool
|
isUnusualADError :: ADError -> Bool
|
||||||
isUnusualADError = flip notElem [ADNoSuchObject, ADLogonFailure]
|
isUnusualADError = flip notElem [ADNoSuchObject, ADLogonFailure]
|
||||||
@ -220,7 +221,7 @@ campusLogin pool mode = AuthPlugin{..}
|
|||||||
$logInfoS apName [st|#{campusIdent}: #{toPathPiece adError}|]
|
$logInfoS apName [st|#{campusIdent}: #{toPathPiece adError}|]
|
||||||
observeLoginOutcome apName LoginADInvalidCredentials
|
observeLoginOutcome apName LoginADInvalidCredentials
|
||||||
MsgRenderer mr <- liftHandler getMsgRenderer
|
MsgRenderer mr <- liftHandler getMsgRenderer
|
||||||
setSessionJson SessionError . PermissionDenied . mr $ ADInvalidCredentials adError
|
setSessionJson SessionError . PermissionDenied . toPathPiece $ ADInvalidCredentials adError
|
||||||
loginErrorMessage (tp LoginR) . mr $ ADInvalidCredentials adError
|
loginErrorMessage (tp LoginR) . mr $ ADInvalidCredentials adError
|
||||||
Right (Left bindErr) -> do
|
Right (Left bindErr) -> do
|
||||||
case bindErr of
|
case bindErr of
|
||||||
|
|||||||
@ -61,6 +61,8 @@ postAShowR tid ssh ash = do
|
|||||||
resultCourseVisible = _5 . _Value
|
resultCourseVisible = _5 . _Value
|
||||||
resultAllocationCourse :: _ => Lens' a AllocationCourse
|
resultAllocationCourse :: _ => Lens' a AllocationCourse
|
||||||
resultAllocationCourse = _6 . _entityVal
|
resultAllocationCourse = _6 . _entityVal
|
||||||
|
resultParticipantCount :: _ => Lens' a Int
|
||||||
|
resultParticipantCount = _7 . _Value
|
||||||
|
|
||||||
(Entity aId Allocation{..}, School{..}, isAnyLecturer, isAdmin, courses, registration, wouldNotifyNewCourse) <- runDB $ do
|
(Entity aId Allocation{..}, School{..}, isAnyLecturer, isAdmin, courses, registration, wouldNotifyNewCourse) <- runDB $ do
|
||||||
alloc@(Entity aId Allocation{allocationSchool}) <- getBy404 $ TermSchoolAllocationShort tid ssh ash
|
alloc@(Entity aId Allocation{allocationSchool}) <- getBy404 $ TermSchoolAllocationShort tid ssh ash
|
||||||
@ -81,12 +83,16 @@ postAShowR tid ssh ash = do
|
|||||||
E.orderBy [E.asc $ course E.^. CourseName]
|
E.orderBy [E.asc $ course E.^. CourseName]
|
||||||
let hasTemplate = E.exists . E.from $ \courseAppInstructionFile ->
|
let hasTemplate = E.exists . E.from $ \courseAppInstructionFile ->
|
||||||
E.where_ $ courseAppInstructionFile E.^. CourseAppInstructionFileCourse E.==. course E.^. CourseId
|
E.where_ $ courseAppInstructionFile E.^. CourseAppInstructionFileCourse E.==. course E.^. CourseId
|
||||||
|
participantCount = E.subSelectCount . E.from $ \courseParticipant ->
|
||||||
|
E.where_ $ courseParticipant E.^. CourseParticipantCourse E.==. course E.^. CourseId
|
||||||
|
E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive
|
||||||
return ( course
|
return ( course
|
||||||
, courseApplication
|
, courseApplication
|
||||||
, hasTemplate
|
, hasTemplate
|
||||||
, E.not_ . E.isNothing $ registration E.?. CourseParticipantId
|
, E.not_ . E.isNothing $ registration E.?. CourseParticipantId
|
||||||
, courseIsVisible now course . Just $ E.val aId
|
, courseIsVisible now course . Just $ E.val aId
|
||||||
, allocationCourse
|
, allocationCourse
|
||||||
|
, participantCount
|
||||||
)
|
)
|
||||||
|
|
||||||
registration <- fmap join . for muid $ getBy . UniqueAllocationUser aId
|
registration <- fmap join . for muid $ getBy . UniqueAllocationUser aId
|
||||||
@ -99,6 +105,7 @@ postAShowR tid ssh ash = do
|
|||||||
return (alloc, school, isAnyLecturer, isAdmin, nubOn (view $ resultCourse . _entityKey) courses, registration, wouldNotifyNewCourse)
|
return (alloc, school, isAnyLecturer, isAdmin, nubOn (view $ resultCourse . _entityKey) courses, registration, wouldNotifyNewCourse)
|
||||||
|
|
||||||
let nextSubstitutesDeadline = minimumOf (folded . resultAllocationCourse . _allocationCourseAcceptSubstitutes . _Just . filtered (>= now)) courses
|
let nextSubstitutesDeadline = minimumOf (folded . resultAllocationCourse . _allocationCourseAcceptSubstitutes . _Just . filtered (>= now)) courses
|
||||||
|
freeCapacity = fmap getSum . getAp . flip foldMap courses $ \cEntry -> Ap . fmap (Sum . max 0) $ subtract (cEntry ^. resultParticipantCount) <$> preview (resultCourse . _entityVal . _courseCapacity . _Just) cEntry
|
||||||
|
|
||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
let title = MsgAllocationTitle (mr . ShortTermIdentifier $ unTermKey allocationTerm) (unSchoolKey allocationSchool) allocationName
|
let title = MsgAllocationTitle (mr . ShortTermIdentifier $ unTermKey allocationTerm) (unSchoolKey allocationSchool) allocationName
|
||||||
@ -158,6 +165,7 @@ postAShowR tid ssh ash = do
|
|||||||
isRegistered = cEntry ^. resultIsRegistered
|
isRegistered = cEntry ^. resultIsRegistered
|
||||||
courseVisible = cEntry ^. resultCourseVisible
|
courseVisible = cEntry ^. resultCourseVisible
|
||||||
AllocationCourse{..} = cEntry ^. resultAllocationCourse
|
AllocationCourse{..} = cEntry ^. resultAllocationCourse
|
||||||
|
partCount = cEntry ^. resultParticipantCount
|
||||||
cID <- encrypt cid :: WidgetFor UniWorX CryptoUUIDCourse
|
cID <- encrypt cid :: WidgetFor UniWorX CryptoUUIDCourse
|
||||||
mayApply <- hasWriteAccessTo . AllocationR tid ssh ash $ AApplyR cID
|
mayApply <- hasWriteAccessTo . AllocationR tid ssh ash $ AApplyR cID
|
||||||
mayEdit <- hasWriteAccessTo $ CourseR tid ssh courseShorthand CEditR
|
mayEdit <- hasWriteAccessTo $ CourseR tid ssh courseShorthand CEditR
|
||||||
|
|||||||
@ -61,6 +61,9 @@ data AllocationCourseForm = AllocationCourseForm
|
|||||||
, acfDeregisterNoShow :: Bool
|
, acfDeregisterNoShow :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeLenses_ ''CourseForm
|
||||||
|
makeLenses_ ''AllocationCourseForm
|
||||||
|
|
||||||
courseToForm :: Entity Course -> [Lecturer] -> Map UserEmail (InvitationDBData Lecturer) -> Maybe (Entity AllocationCourse) -> CourseForm
|
courseToForm :: Entity Course -> [Lecturer] -> Map UserEmail (InvitationDBData Lecturer) -> Maybe (Entity AllocationCourse) -> CourseForm
|
||||||
courseToForm cEnt@(Entity cid Course{..}) lecs lecInvites alloc = CourseForm
|
courseToForm cEnt@(Entity cid Course{..}) lecs lecInvites alloc = CourseForm
|
||||||
{ cfCourseId = Just cid
|
{ cfCourseId = Just cid
|
||||||
@ -326,20 +329,28 @@ validateCourse = do
|
|||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
uid <- liftHandler requireAuthId
|
uid <- liftHandler requireAuthId
|
||||||
userAdmin <- hasWriteAccessTo $ SchoolR cfSchool SchoolEditR
|
userAdmin <- hasWriteAccessTo $ SchoolR cfSchool SchoolEditR
|
||||||
allocationTerm <- for (acfAllocation <$> cfAllocation) $ lift . fmap allocationTerm . getJust
|
newAllocationTerm <- for (acfAllocation <$> cfAllocation) $ lift . fmap allocationTerm . getJust
|
||||||
|
|
||||||
oldAllocatedCapacity <- fmap join . for cfCourseId $ \cid -> lift $ do
|
prevAllocationCourse <- join <$> traverse (lift . getBy . UniqueAllocationCourse) cfCourseId
|
||||||
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
prevAllocation <- fmap join . traverse (lift . getEntity) $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
||||||
prevAllocation <- fmap join . traverse get $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
|
||||||
|
|
||||||
fmap join . for prevAllocation $ \Allocation{allocationStaffAllocationTo, allocationRegisterByCourse} -> if
|
oldAllocatedCapacity <- if
|
||||||
| userAdmin
|
| Just (Entity _ Allocation{..}) <- prevAllocation
|
||||||
-> return Nothing
|
, Just (Entity _ AllocationCourse{..}) <- prevAllocationCourse
|
||||||
| NTop allocationStaffAllocationTo <= NTop (Just now)
|
, NTop allocationStaffAllocationTo <= NTop (Just now)
|
||||||
, NTop allocationRegisterByCourse > NTop (Just now)
|
, NTop allocationRegisterByCourse > NTop (Just now)
|
||||||
-> Just . courseCapacity <$> getJust cid
|
-> lift $ Just . courseCapacity <$> getJust allocationCourseCourse
|
||||||
| otherwise
|
| otherwise
|
||||||
-> return Nothing
|
-> return Nothing
|
||||||
|
let oldAllocation = do
|
||||||
|
Entity allocId Allocation{..} <- prevAllocation
|
||||||
|
guard $ NTop (Just now) > NTop allocationStaffRegisterTo
|
||||||
|
pure $ Just allocId
|
||||||
|
oldAllocatedMinCapacity = do
|
||||||
|
Entity _ Allocation{..} <- prevAllocation
|
||||||
|
Entity _ AllocationCourse{..} <- prevAllocationCourse
|
||||||
|
guard $ NTop (Just now) > NTop allocationStaffRegisterTo
|
||||||
|
pure $ Just allocationCourseMinCapacity
|
||||||
|
|
||||||
guardValidation MsgCourseVisibilityEndMustBeAfterStart
|
guardValidation MsgCourseVisibilityEndMustBeAfterStart
|
||||||
$ NTop cfVisFrom <= NTop cfVisTo
|
$ NTop cfVisFrom <= NTop cfVisTo
|
||||||
@ -347,15 +358,19 @@ validateCourse = do
|
|||||||
$ NTop cfRegFrom <= NTop cfRegTo
|
$ NTop cfRegFrom <= NTop cfRegTo
|
||||||
guardValidation MsgCourseDeregistrationEndMustBeAfterStart
|
guardValidation MsgCourseDeregistrationEndMustBeAfterStart
|
||||||
$ Just False /= ((<=) <$> cfRegFrom <*> cfDeRegUntil)
|
$ Just False /= ((<=) <$> cfRegFrom <*> cfDeRegUntil)
|
||||||
unless userAdmin $
|
|
||||||
guardValidation MsgCourseUserMustBeLecturer
|
|
||||||
$ anyOf (traverse . _Right . _1) (== uid) cfLecturers
|
|
||||||
guardValidation MsgCourseAllocationRequiresCapacity
|
guardValidation MsgCourseAllocationRequiresCapacity
|
||||||
$ is _Nothing cfAllocation || is _Just cfCapacity
|
$ is _Nothing cfAllocation || is _Just cfCapacity
|
||||||
guardValidation MsgCourseAllocationTermMustMatch
|
guardValidation MsgCourseAllocationTermMustMatch
|
||||||
$ maybe True (== cfTerm) allocationTerm
|
$ maybe True (== cfTerm) newAllocationTerm
|
||||||
guardValidation MsgCourseAllocationCapacityMayNotBeChanged
|
unless userAdmin $ do
|
||||||
$ maybe True (== cfCapacity) oldAllocatedCapacity
|
guardValidation MsgCourseUserMustBeLecturer
|
||||||
|
$ anyOf (traverse . _Right . _1) (== uid) cfLecturers
|
||||||
|
guardValidation MsgCourseAllocationCapacityMayNotBeChanged
|
||||||
|
$ maybe True (== cfCapacity) oldAllocatedCapacity
|
||||||
|
guardValidation MsgAllocationStaffRegisterToExpiredAllocation
|
||||||
|
$ maybe True (== fmap acfAllocation cfAllocation) oldAllocation
|
||||||
|
guardValidation MsgAllocationStaffRegisterToExpiredMinCapacity
|
||||||
|
$ maybe True (== fmap acfMinCapacity cfAllocation) oldAllocatedMinCapacity
|
||||||
|
|
||||||
warnValidation MsgCourseShorthandTooLong
|
warnValidation MsgCourseShorthandTooLong
|
||||||
$ length (CI.original cfShort) <= 10
|
$ length (CI.original cfShort) <= 10
|
||||||
@ -567,48 +582,23 @@ courseEditHandler miButtonAction mbCourseForm = do
|
|||||||
}
|
}
|
||||||
|
|
||||||
upsertAllocationCourse :: CourseId -> Maybe AllocationCourseForm -> YesodJobDB UniWorX ()
|
upsertAllocationCourse :: CourseId -> Maybe AllocationCourseForm -> YesodJobDB UniWorX ()
|
||||||
upsertAllocationCourse cid cfAllocation = do
|
upsertAllocationCourse cid = \case
|
||||||
now <- liftIO getCurrentTime
|
Just AllocationCourseForm{..} -> do
|
||||||
Course{} <- getJust cid
|
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
||||||
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
|
||||||
prevAllocation <- fmap join . traverse getEntity $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
|
||||||
userAdmin <- fromMaybe False <$> for prevAllocation (\(Entity _ Allocation{..}) -> hasWriteAccessTo $ SchoolR allocationSchool SchoolEditR)
|
|
||||||
|
|
||||||
doEdit <- if
|
void $ upsert AllocationCourse
|
||||||
| userAdmin
|
{ allocationCourseAllocation = acfAllocation
|
||||||
-> return True
|
, allocationCourseCourse = cid
|
||||||
| Just (Entity _ Allocation{allocationStaffRegisterTo}) <- prevAllocation
|
, allocationCourseMinCapacity = acfMinCapacity
|
||||||
, NTop allocationStaffRegisterTo <= NTop (Just now)
|
, allocationCourseAcceptSubstitutes = acfAcceptSubstitutes
|
||||||
-> let anyChanges
|
}
|
||||||
| Just AllocationCourseForm{..} <- cfAllocation
|
[ AllocationCourseAllocation =. acfAllocation
|
||||||
, Just (Entity _ AllocationCourse{..}) <- prevAllocationCourse
|
, AllocationCourseCourse =. cid
|
||||||
= or [ acfAllocation /= allocationCourseAllocation
|
, AllocationCourseMinCapacity =. acfMinCapacity
|
||||||
, acfMinCapacity /= allocationCourseMinCapacity
|
, AllocationCourseAcceptSubstitutes =. acfAcceptSubstitutes
|
||||||
]
|
]
|
||||||
| otherwise
|
|
||||||
= True
|
|
||||||
in False <$ when anyChanges (addMessageI Error MsgAllocationStaffRegisterToExpired)
|
|
||||||
| otherwise
|
|
||||||
-> return True
|
|
||||||
|
|
||||||
when doEdit $
|
when (Just acfAllocation /= fmap (allocationCourseAllocation . entityVal) prevAllocationCourse) $
|
||||||
case cfAllocation of
|
queueDBJob . JobQueueNotification $ NotificationAllocationNewCourse acfAllocation cid
|
||||||
Just AllocationCourseForm{..} -> do
|
Nothing ->
|
||||||
void $ upsert AllocationCourse
|
deleteWhere [ AllocationCourseCourse ==. cid ]
|
||||||
{ allocationCourseAllocation = acfAllocation
|
|
||||||
, allocationCourseCourse = cid
|
|
||||||
, allocationCourseMinCapacity = acfMinCapacity
|
|
||||||
, allocationCourseAcceptSubstitutes = acfAcceptSubstitutes
|
|
||||||
}
|
|
||||||
[ AllocationCourseAllocation =. acfAllocation
|
|
||||||
, AllocationCourseCourse =. cid
|
|
||||||
, AllocationCourseMinCapacity =. acfMinCapacity
|
|
||||||
, AllocationCourseAcceptSubstitutes =. acfAcceptSubstitutes
|
|
||||||
]
|
|
||||||
|
|
||||||
when (Just acfAllocation /= fmap entityKey prevAllocation) $
|
|
||||||
queueDBJob . JobQueueNotification $ NotificationAllocationNewCourse acfAllocation cid
|
|
||||||
Nothing
|
|
||||||
| Just (Entity prevId _) <- prevAllocationCourse
|
|
||||||
-> delete prevId
|
|
||||||
_other -> return ()
|
|
||||||
|
|||||||
@ -18,17 +18,14 @@ import Jobs.Queue
|
|||||||
getEEditR, postEEditR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Html
|
getEEditR, postEEditR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Html
|
||||||
getEEditR = postEEditR
|
getEEditR = postEEditR
|
||||||
postEEditR tid ssh csh examn = do
|
postEEditR tid ssh csh examn = do
|
||||||
(cid, Entity eId oldExam, template) <- runDB $ do
|
(template, (editExamAct, (editExamWidget, editExamEnctype))) <- runDBJobs $ do
|
||||||
(cid, exam) <- fetchCourseIdExam tid ssh csh examn
|
(cid, exam@(Entity eId oldExam)) <- fetchCourseIdExam tid ssh csh examn
|
||||||
|
|
||||||
template <- examFormTemplate exam
|
template <- examFormTemplate exam
|
||||||
|
|
||||||
return (cid, exam, template)
|
((editExamResult, editExamWidget), editExamEnctype) <- runFormPost . validateForm (validateExam cid $ Just exam) . examForm $ Just template
|
||||||
|
|
||||||
((editExamResult, editExamWidget), editExamEnctype) <- runFormPost . validateForm (validateExam cid $ Just oldExam) . examForm $ Just template
|
editExamAct <- formResultMaybe editExamResult $ \ExamForm{..} -> do
|
||||||
|
|
||||||
formResult editExamResult $ \ExamForm{..} -> do
|
|
||||||
insertRes <- runDBJobs $ do
|
|
||||||
insertRes <- myReplaceUnique eId Exam
|
insertRes <- myReplaceUnique eId Exam
|
||||||
{ examCourse = cid
|
{ examCourse = cid
|
||||||
, examName = efName
|
, examName = efName
|
||||||
@ -116,13 +113,15 @@ postEEditR tid ssh csh examn = do
|
|||||||
deleteWhere [ InvitationFor ==. invRef @ExamCorrector eId, InvitationEmail /<-. invites ]
|
deleteWhere [ InvitationFor ==. invRef @ExamCorrector eId, InvitationEmail /<-. invites ]
|
||||||
sinkInvitationsF examCorrectorInvitationConfig $ map (, eId, (InvDBDataExamCorrector, InvTokenDataExamCorrector)) invites
|
sinkInvitationsF examCorrectorInvitationConfig $ map (, eId, (InvDBDataExamCorrector, InvTokenDataExamCorrector)) invites
|
||||||
|
|
||||||
return insertRes
|
return . Just $ case insertRes of
|
||||||
|
Just _ -> addMessageI Error $ MsgExamNameTaken efName
|
||||||
|
Nothing -> do
|
||||||
|
addMessageI Success $ MsgExamEdited efName
|
||||||
|
redirect $ CExamR tid ssh csh efName EShowR
|
||||||
|
|
||||||
case insertRes of
|
return (template, (editExamAct, (editExamWidget, editExamEnctype)))
|
||||||
Just _ -> addMessageI Error $ MsgExamNameTaken efName
|
|
||||||
Nothing -> do
|
sequence_ editExamAct
|
||||||
addMessageI Success $ MsgExamEdited efName
|
|
||||||
redirect $ CExamR tid ssh csh efName EShowR
|
|
||||||
|
|
||||||
let heading = prependCourseTitle tid ssh csh . MsgExamEditHeading $ efName template
|
let heading = prependCourseTitle tid ssh csh . MsgExamEditHeading $ efName template
|
||||||
|
|
||||||
|
|||||||
@ -98,11 +98,14 @@ deriveJSON defaultOptions
|
|||||||
} ''ExamOccurrenceForm
|
} ''ExamOccurrenceForm
|
||||||
|
|
||||||
|
|
||||||
examForm :: Maybe ExamForm -> Form ExamForm
|
examForm :: ( MonadHandler m
|
||||||
examForm template html = do
|
, HandlerSite m ~ UniWorX
|
||||||
|
)
|
||||||
|
=> Maybe ExamForm -> (Html -> MForm m (FormResult ExamForm, Widget))
|
||||||
|
examForm template csrf = hoist liftHandler $ do
|
||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
|
||||||
flip (renderAForm FormStandard) html $ ExamForm
|
flip (renderAForm FormStandard) csrf $ ExamForm
|
||||||
<$> areq ciField (fslpI MsgExamName (mr MsgExamName) & setTooltip MsgExamNameTip) (efName <$> template)
|
<$> areq ciField (fslpI MsgExamName (mr MsgExamName) & setTooltip MsgExamNameTip) (efName <$> template)
|
||||||
<*> aopt htmlField (fslI MsgExamDescription) (efDescription <$> template)
|
<*> aopt htmlField (fslI MsgExamDescription) (efDescription <$> template)
|
||||||
<* aformSection MsgExamFormTimes
|
<* aformSection MsgExamFormTimes
|
||||||
@ -284,7 +287,11 @@ examPartsForm prev = wFormToAForm $ do
|
|||||||
miIdent' :: Text
|
miIdent' :: Text
|
||||||
miIdent' = "exam-parts"
|
miIdent' = "exam-parts"
|
||||||
|
|
||||||
examFormTemplate :: Entity Exam -> DB ExamForm
|
examFormTemplate :: ( MonadHandler m
|
||||||
|
, HandlerSite m ~ UniWorX
|
||||||
|
, MonadThrow m
|
||||||
|
)
|
||||||
|
=> Entity Exam -> SqlPersistT m ExamForm
|
||||||
examFormTemplate (Entity eId Exam{..}) = do
|
examFormTemplate (Entity eId Exam{..}) = do
|
||||||
examParts <- selectList [ ExamPartExam ==. eId ] []
|
examParts <- selectList [ ExamPartExam ==. eId ] []
|
||||||
occurrences <- selectList [ ExamOccurrenceExam ==. eId ] []
|
occurrences <- selectList [ ExamOccurrenceExam ==. eId ] []
|
||||||
@ -342,7 +349,8 @@ examFormTemplate (Entity eId Exam{..}) = do
|
|||||||
, efStaff = examStaff
|
, efStaff = examStaff
|
||||||
}
|
}
|
||||||
|
|
||||||
examTemplate :: CourseId -> DB (Maybe ExamForm)
|
examTemplate :: MonadHandler m
|
||||||
|
=> CourseId -> SqlPersistT m (Maybe ExamForm)
|
||||||
examTemplate cid = runMaybeT $ do
|
examTemplate cid = runMaybeT $ do
|
||||||
newCourse <- MaybeT $ get cid
|
newCourse <- MaybeT $ get cid
|
||||||
|
|
||||||
@ -393,7 +401,12 @@ examTemplate cid = runMaybeT $ do
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
validateExam :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseId -> Maybe Exam -> FormValidator ExamForm m ()
|
validateExam :: forall m.
|
||||||
|
( MonadHandler m
|
||||||
|
, HandlerSite m ~ UniWorX
|
||||||
|
, MonadThrow m
|
||||||
|
)
|
||||||
|
=> CourseId -> Maybe (Entity Exam) -> FormValidator ExamForm (SqlPersistT m) ()
|
||||||
validateExam cId oldExam = do
|
validateExam cId oldExam = do
|
||||||
ExamForm{..} <- State.get
|
ExamForm{..} <- State.get
|
||||||
|
|
||||||
@ -404,6 +417,7 @@ validateExam cId oldExam = do
|
|||||||
guardValidation MsgExamFinishedMustBeAfterEnd $ Just False /= ((>=) <$> efFinished <*> efEnd)
|
guardValidation MsgExamFinishedMustBeAfterEnd $ Just False /= ((>=) <$> efFinished <*> efEnd)
|
||||||
guardValidation MsgExamFinishedMustBeAfterStart $ Just False /= ((>=) <$> efFinished <*> efStart)
|
guardValidation MsgExamFinishedMustBeAfterStart $ Just False /= ((>=) <$> efFinished <*> efStart)
|
||||||
|
|
||||||
|
|
||||||
forM_ efOccurrences $ \ExamOccurrenceForm{..} -> do
|
forM_ efOccurrences $ \ExamOccurrenceForm{..} -> do
|
||||||
guardValidation (MsgExamOccurrenceEndMustBeAfterStart eofName) $ NTop eofEnd >= NTop (Just eofStart)
|
guardValidation (MsgExamOccurrenceEndMustBeAfterStart eofName) $ NTop eofEnd >= NTop (Just eofStart)
|
||||||
guardValidation (MsgExamOccurrenceStartMustBeAfterExamStart eofName) $ NTop (Just eofStart) >= NTop efStart
|
guardValidation (MsgExamOccurrenceStartMustBeAfterExamStart eofName) $ NTop (Just eofStart) >= NTop efStart
|
||||||
@ -421,6 +435,28 @@ validateExam cId oldExam = do
|
|||||||
|
|
||||||
guardValidation (MsgExamOccurrenceDuplicateName $ eofName a) $ ((/=) `on` eofName) a b
|
guardValidation (MsgExamOccurrenceDuplicateName $ eofName a) $ ((/=) `on` eofName) a b
|
||||||
|
|
||||||
|
oldOccurrencesWithRegistrations <- for oldExam $ \(Entity eId _) -> lift . E.select . E.from $ \examOccurrence -> do
|
||||||
|
E.where_ $ examOccurrence E.^. ExamOccurrenceExam E.==. E.val eId
|
||||||
|
E.where_ . E.exists . E.from $ \examRegistration ->
|
||||||
|
E.where_ $ examRegistration E.^. ExamRegistrationOccurrence E.==. E.just (examOccurrence E.^. ExamOccurrenceId)
|
||||||
|
return ( examOccurrence E.^. ExamOccurrenceId
|
||||||
|
, examOccurrence E.^. ExamOccurrenceName
|
||||||
|
)
|
||||||
|
forM_ (join $ hoistMaybe oldOccurrencesWithRegistrations) $ \(E.Value eoId, E.Value eoName) ->
|
||||||
|
guardValidationM (MsgExamOccurrenceCannotBeDeletedDueToRegistrations eoName) . anyM (otoList efOccurrences) $ \ExamOccurrenceForm{..} -> (== Just eoId) <$> traverse decrypt eofId
|
||||||
|
|
||||||
|
|
||||||
|
oldPartsWithResults <- for oldExam $ \(Entity eId _) -> lift . E.select . E.from $ \examPart -> do
|
||||||
|
E.where_ $ examPart E.^. ExamPartExam E.==. E.val eId
|
||||||
|
E.where_ . E.exists . E.from $ \examPartResult ->
|
||||||
|
E.where_ $ examPartResult E.^. ExamPartResultExamPart E.==. examPart E.^. ExamPartId
|
||||||
|
return ( examPart E.^. ExamPartId
|
||||||
|
, examPart E.^. ExamPartNumber
|
||||||
|
)
|
||||||
|
forM_ (join $ hoistMaybe oldPartsWithResults) $ \(E.Value epId, E.Value epNumber) ->
|
||||||
|
guardValidationM (MsgExamPartCannotBeDeletedDueToResults epNumber) . anyM (otoList efExamParts) $ \ExamPartForm{..} -> (== Just epId) <$> traverse decrypt epfId
|
||||||
|
|
||||||
|
|
||||||
mSchool <- liftHandler . runDB . E.selectMaybe . E.from $ \(course `E.InnerJoin` school) -> do
|
mSchool <- liftHandler . runDB . E.selectMaybe . E.from $ \(course `E.InnerJoin` school) -> do
|
||||||
E.on $ course E.^. CourseSchool E.==. school E.^. SchoolId
|
E.on $ course E.^. CourseSchool E.==. school E.^. SchoolId
|
||||||
E.where_ $ course E.^. CourseId E.==. E.val cId
|
E.where_ $ course E.^. CourseId E.==. E.val cId
|
||||||
@ -429,7 +465,7 @@ validateExam cId oldExam = do
|
|||||||
whenIsJust mSchool $ \(Entity _ School{..}) -> do
|
whenIsJust mSchool $ \(Entity _ School{..}) -> do
|
||||||
whenIsJust schoolExamMinimumRegisterBeforeStart $ \minSep -> do
|
whenIsJust schoolExamMinimumRegisterBeforeStart $ \minSep -> do
|
||||||
let doValidation
|
let doValidation
|
||||||
| Just Exam{..} <- oldExam
|
| Just (Entity _ Exam{..}) <- oldExam
|
||||||
, not . fromMaybe True $ (>=) <$> examStart <*> (addUTCTime minSep <$> examRegisterFrom)
|
, not . fromMaybe True $ (>=) <$> examStart <*> (addUTCTime minSep <$> examRegisterFrom)
|
||||||
= warnValidation
|
= warnValidation
|
||||||
| otherwise
|
| otherwise
|
||||||
@ -438,7 +474,7 @@ validateExam cId oldExam = do
|
|||||||
. fromMaybe True $ (>=) <$> efStart <*> (addUTCTime minSep <$> efRegisterFrom)
|
. fromMaybe True $ (>=) <$> efStart <*> (addUTCTime minSep <$> efRegisterFrom)
|
||||||
whenIsJust schoolExamMinimumRegisterDuration $ \minDur -> do
|
whenIsJust schoolExamMinimumRegisterDuration $ \minDur -> do
|
||||||
let doValidation
|
let doValidation
|
||||||
| Just Exam{..} <- oldExam
|
| Just (Entity _ Exam{..}) <- oldExam
|
||||||
, not . fromMaybe True $ (>=) <$> examRegisterTo <*> (addUTCTime minDur <$> examRegisterFrom)
|
, not . fromMaybe True $ (>=) <$> examRegisterTo <*> (addUTCTime minDur <$> examRegisterFrom)
|
||||||
= warnValidation
|
= warnValidation
|
||||||
| otherwise
|
| otherwise
|
||||||
@ -447,7 +483,7 @@ validateExam cId oldExam = do
|
|||||||
. fromMaybe True $ (>=) <$> efRegisterTo <*> (addUTCTime minDur <$> efRegisterFrom)
|
. fromMaybe True $ (>=) <$> efRegisterTo <*> (addUTCTime minDur <$> efRegisterFrom)
|
||||||
when schoolExamRequireModeForRegistration $ do
|
when schoolExamRequireModeForRegistration $ do
|
||||||
let doValidation
|
let doValidation
|
||||||
| Just Exam{ examExamMode = ExamMode{..}, .. } <- oldExam
|
| Just (Entity _ Exam{ examExamMode = ExamMode{..}, .. }) <- oldExam
|
||||||
, or [ is _Nothing examAids
|
, or [ is _Nothing examAids
|
||||||
, is _Nothing examOnline
|
, is _Nothing examOnline
|
||||||
, is _Nothing examSynchronicity
|
, is _Nothing examSynchronicity
|
||||||
@ -468,5 +504,5 @@ validateExam cId oldExam = do
|
|||||||
|
|
||||||
warnValidation MsgExamModeSchoolDiscouraged . not $ evalExamModeDNF schoolExamDiscouragedModes efExamMode
|
warnValidation MsgExamModeSchoolDiscouraged . not $ evalExamModeDNF schoolExamDiscouragedModes efExamMode
|
||||||
|
|
||||||
unless (has (_Just . _examStaff . _Nothing) oldExam) $
|
unless (has (_Just . _entityVal . _examStaff . _Nothing) oldExam) $
|
||||||
guardValidation MsgExamStaffRequired $ isn't _Nothing efStaff
|
guardValidation MsgExamStaffRequired $ isn't _Nothing efStaff
|
||||||
|
|||||||
@ -19,15 +19,13 @@ import qualified Data.Conduit.Combinators as C
|
|||||||
getCExamNewR, postCExamNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCExamNewR, postCExamNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getCExamNewR = postCExamNewR
|
getCExamNewR = postCExamNewR
|
||||||
postCExamNewR tid ssh csh = do
|
postCExamNewR tid ssh csh = do
|
||||||
(cid, template) <- runDB $ do
|
(newExamAct, (newExamWidget, newExamEnctype)) <- runDBJobs $ do
|
||||||
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
|
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
|
||||||
template <- examTemplate cid
|
template <- examTemplate cid
|
||||||
return (cid, template)
|
|
||||||
|
|
||||||
((newExamResult, newExamWidget), newExamEnctype) <- runFormPost . validateForm (validateExam cid Nothing) $ examForm template
|
((newExamResult, newExamWidget), newExamEnctype) <- runFormPost . validateForm (validateExam cid Nothing) $ examForm template
|
||||||
|
|
||||||
formResult newExamResult $ \ExamForm{..} -> do
|
newExamAct <- formResultMaybe newExamResult $ \ExamForm{..} -> do
|
||||||
insertRes <- runDBJobs $ do
|
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
|
|
||||||
insertRes <- insertUnique Exam
|
insertRes <- insertUnique Exam
|
||||||
@ -95,12 +93,15 @@ postCExamNewR tid ssh csh = do
|
|||||||
audit $ TransactionExamResultEdit examid courseParticipantUser
|
audit $ TransactionExamResultEdit examid courseParticipantUser
|
||||||
runConduit $ selectSource [ CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantInactive True ] [] .| C.mapM_ recordNoShow
|
runConduit $ selectSource [ CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantInactive True ] [] .| C.mapM_ recordNoShow
|
||||||
|
|
||||||
return insertRes
|
return . Just $ case insertRes of
|
||||||
case insertRes of
|
Nothing -> addMessageI Error $ MsgExamNameTaken efName
|
||||||
Nothing -> addMessageI Error $ MsgExamNameTaken efName
|
Just _ -> do
|
||||||
Just _ -> do
|
addMessageI Success $ MsgExamCreated efName
|
||||||
addMessageI Success $ MsgExamCreated efName
|
redirect $ CourseR tid ssh csh CExamListR
|
||||||
redirect $ CourseR tid ssh csh CExamListR
|
|
||||||
|
return (newExamAct, (newExamWidget, newExamEnctype))
|
||||||
|
|
||||||
|
sequence_ newExamAct
|
||||||
|
|
||||||
let heading = prependCourseTitle tid ssh csh MsgExamNew
|
let heading = prependCourseTitle tid ssh csh MsgExamNew
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import qualified Database.Esqueleto.Utils as E
|
|||||||
|
|
||||||
import Development.GitRev
|
import Development.GitRev
|
||||||
|
|
||||||
|
import Auth.LDAP (ADError(..), ADInvalidCredentials(..))
|
||||||
|
|
||||||
-- | Versionsgeschichte
|
-- | Versionsgeschichte
|
||||||
getVersionR :: Handler TypedContent
|
getVersionR :: Handler TypedContent
|
||||||
getVersionR = selectRep $ do
|
getVersionR = selectRep $ do
|
||||||
@ -181,6 +183,26 @@ showFAQ (CExamR tid ssh csh examn _) FAQExamPoints
|
|||||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||||
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
||||||
E.&&. exam E.^. ExamName E.==. E.val examn
|
E.&&. exam E.^. ExamName E.==. E.val examn
|
||||||
|
showFAQ _ FAQInvalidCredentialsAdAccountDisabled = maybeT (return False) $ do
|
||||||
|
guardM $ is _Nothing <$> maybeAuthId
|
||||||
|
sessionError <- MaybeT $ lookupSessionJson SessionError
|
||||||
|
guard $ sessionError == PermissionDenied (toPathPiece $ ADInvalidCredentials ADAccountDisabled)
|
||||||
|
return True
|
||||||
|
showFAQ _ FAQAllocationNoPlaces = maybeT (return False) $ do
|
||||||
|
uid <- MaybeT maybeAuthId
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
liftHandler . runDB . E.selectExists . E.from $ \allocation -> do
|
||||||
|
let doneSince = E.subSelectMaybe . E.from $ \participant -> do
|
||||||
|
E.where_ $ participant E.^. CourseParticipantAllocated E.==. E.just (allocation E.^. AllocationId)
|
||||||
|
return . E.max_ $ participant E.^. CourseParticipantRegistration
|
||||||
|
isAllocationUser = E.exists . E.from $ \allocationUser ->
|
||||||
|
E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. allocation E.^. AllocationId
|
||||||
|
E.&&. allocationUser E.^. AllocationUserUser E.==. E.val uid
|
||||||
|
isApplicant = E.exists . E.from $ \courseApplication ->
|
||||||
|
E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocation E.^. AllocationId)
|
||||||
|
E.&&. courseApplication E.^. CourseApplicationUser E.==. E.val uid
|
||||||
|
E.where_ $ isAllocationUser E.||. isApplicant
|
||||||
|
E.where_ $ E.maybe E.false (\done -> done E.>=. E.val (addUTCTime (-7 * nominalDay) now)) doneSince
|
||||||
showFAQ _ _ = return False
|
showFAQ _ _ = return False
|
||||||
|
|
||||||
prioFAQ :: Monad m
|
prioFAQ :: Monad m
|
||||||
@ -191,3 +213,5 @@ prioFAQ _ FAQForgottenPassword = return 1
|
|||||||
prioFAQ _ FAQNotLecturerHowToCreateCourses = return 1
|
prioFAQ _ FAQNotLecturerHowToCreateCourses = return 1
|
||||||
prioFAQ _ FAQCourseCorrectorsTutors = return 1
|
prioFAQ _ FAQCourseCorrectorsTutors = return 1
|
||||||
prioFAQ _ FAQExamPoints = return 2
|
prioFAQ _ FAQExamPoints = return 2
|
||||||
|
prioFAQ _ FAQAllocationNoPlaces = return 2
|
||||||
|
prioFAQ _ FAQInvalidCredentialsAdAccountDisabled = return 3
|
||||||
|
|||||||
@ -29,15 +29,16 @@ makePrisms ''ChangelogItemKind
|
|||||||
|
|
||||||
classifyChangelogItem :: ChangelogItem -> ChangelogItemKind
|
classifyChangelogItem :: ChangelogItem -> ChangelogItemKind
|
||||||
classifyChangelogItem = \case
|
classifyChangelogItem = \case
|
||||||
ChangelogHaskellCampusLogin -> ChangelogItemBugfix
|
ChangelogHaskellCampusLogin -> ChangelogItemBugfix
|
||||||
ChangelogTooltipsWithoutJavascript -> ChangelogItemBugfix
|
ChangelogTooltipsWithoutJavascript -> ChangelogItemBugfix
|
||||||
ChangelogButtonsWorkWithoutJavascript -> ChangelogItemBugfix
|
ChangelogButtonsWorkWithoutJavascript -> ChangelogItemBugfix
|
||||||
ChangelogTableFormsWorkAfterAjax -> ChangelogItemBugfix
|
ChangelogTableFormsWorkAfterAjax -> ChangelogItemBugfix
|
||||||
ChangelogPassingByPointsWorks -> ChangelogItemBugfix
|
ChangelogPassingByPointsWorks -> ChangelogItemBugfix
|
||||||
ChangelogErrorMessagesForTableItemVanish -> ChangelogItemBugfix
|
ChangelogErrorMessagesForTableItemVanish -> ChangelogItemBugfix
|
||||||
ChangelogExamAchievementParticipantDuplication -> ChangelogItemBugfix
|
ChangelogExamAchievementParticipantDuplication -> ChangelogItemBugfix
|
||||||
ChangelogFormsTimesReset -> ChangelogItemBugfix
|
ChangelogFormsTimesReset -> ChangelogItemBugfix
|
||||||
_other -> ChangelogItemFeature
|
ChangelogAllocationCourseAcceptSubstitutesFixed -> ChangelogItemBugfix
|
||||||
|
_other -> ChangelogItemFeature
|
||||||
|
|
||||||
changelogItemDays :: Map ChangelogItem Day
|
changelogItemDays :: Map ChangelogItem Day
|
||||||
changelogItemDays = Map.fromListWithKey (\k d1 d2 -> bool (error $ "Duplicate changelog days for " <> show k) d1 $ d1 /= d2)
|
changelogItemDays = Map.fromListWithKey (\k d1 d2 -> bool (error $ "Duplicate changelog days for " <> show k) d1 $ d1 /= d2)
|
||||||
|
|||||||
12
src/Utils.hs
12
src/Utils.hs
@ -822,14 +822,14 @@ and2M, or2M :: Monad m => m Bool -> m Bool -> m Bool
|
|||||||
and2M ma mb = ifM ma mb (return False)
|
and2M ma mb = ifM ma mb (return False)
|
||||||
or2M ma = ifM ma (return True)
|
or2M ma = ifM ma (return True)
|
||||||
|
|
||||||
andM, orM :: (Foldable f, Monad m) => f (m Bool) -> m Bool
|
andM, orM :: (MonoFoldable mono, Element mono ~ (m Bool), Monad m) => mono -> m Bool
|
||||||
andM = Fold.foldr and2M (return True)
|
andM = ofoldl' and2M (return True)
|
||||||
orM = Fold.foldr or2M (return False)
|
orM = ofoldl' or2M (return False)
|
||||||
|
|
||||||
-- | Short-circuiting monady any
|
-- | Short-circuiting monady any
|
||||||
allM, anyM :: (Functor f, Foldable f, Monad m) => f a -> (a -> m Bool) -> m Bool
|
allM, anyM :: (MonoFoldable mono, Monad m) => mono -> (Element mono -> m Bool) -> m Bool
|
||||||
allM xs f = andM $ fmap f xs
|
allM xs f = andM . fmap f $ otoList xs
|
||||||
anyM xs f = orM $ fmap f xs
|
anyM xs f = orM . fmap f $ otoList xs
|
||||||
|
|
||||||
ofoldr1M, ofoldl1M :: (MonoFoldable mono, Monad m) => (Element mono -> Element mono -> m (Element mono)) -> NonNull mono -> m (Element mono)
|
ofoldr1M, ofoldl1M :: (MonoFoldable mono, Monad m) => (Element mono -> Element mono -> m (Element mono)) -> NonNull mono -> m (Element mono)
|
||||||
ofoldr1M f (otoList -> x:xs) = foldrM f x xs
|
ofoldr1M f (otoList -> x:xs) = foldrM f x xs
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
|
||||||
module Utils.Files
|
module Utils.Files
|
||||||
( sinkFile, sinkFiles
|
( sinkFile, sinkFiles
|
||||||
, sinkFile', sinkFiles'
|
, sinkFile', sinkFiles'
|
||||||
@ -35,6 +37,8 @@ import qualified Database.Esqueleto.Utils as E
|
|||||||
|
|
||||||
import Data.Conduit.Algorithms.FastCDC (fastCDC)
|
import Data.Conduit.Algorithms.FastCDC (fastCDC)
|
||||||
|
|
||||||
|
import Control.Monad.Trans.Cont
|
||||||
|
|
||||||
|
|
||||||
sinkFileDB :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX)
|
sinkFileDB :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX)
|
||||||
=> Bool -- ^ Replace? Use only in serializable transaction
|
=> Bool -- ^ Replace? Use only in serializable transaction
|
||||||
@ -43,14 +47,16 @@ sinkFileDB :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnlif
|
|||||||
sinkFileDB doReplace fileContentContent = do
|
sinkFileDB doReplace fileContentContent = do
|
||||||
chunkingParams <- getsYesod $ view _appFileChunkingParams
|
chunkingParams <- getsYesod $ view _appFileChunkingParams
|
||||||
|
|
||||||
let sinkChunk fileContentChunkContent = do
|
let sinkChunk !fileContentChunkContent = do
|
||||||
fileChunkLockTime <- liftIO getCurrentTime
|
fileChunkLockTime <- liftIO getCurrentTime
|
||||||
fileChunkLockInstance <- getsYesod appInstanceID
|
fileChunkLockInstance <- getsYesod appInstanceID
|
||||||
|
|
||||||
observeSunkChunk StorageDB $ olength fileContentChunkContent
|
observeSunkChunk StorageDB $ olength fileContentChunkContent
|
||||||
|
|
||||||
tellM $ Set.singleton <$> insert FileChunkLock{ fileChunkLockHash = fileContentChunkHash, .. }
|
tellM $ Set.singleton <$> insert FileChunkLock{ fileChunkLockHash = fileContentChunkHash, .. }
|
||||||
|
|
||||||
existsChunk <- lift $ exists [FileContentChunkHash ==. fileContentChunkHash]
|
existsChunk <- lift $ exists [FileContentChunkHash ==. fileContentChunkHash]
|
||||||
|
|
||||||
let setContentBased = updateWhere [FileContentChunkHash ==. fileContentChunkHash] [FileContentChunkContentBased =. fileContentChunkContentBased]
|
let setContentBased = updateWhere [FileContentChunkHash ==. fileContentChunkHash] [FileContentChunkContentBased =. fileContentChunkContentBased]
|
||||||
if | existsChunk -> lift setContentBased
|
if | existsChunk -> lift setContentBased
|
||||||
| otherwise -> lift . handleIfSql isUniqueConstraintViolation (const setContentBased) $
|
| otherwise -> lift . handleIfSql isUniqueConstraintViolation (const setContentBased) $
|
||||||
@ -144,7 +150,22 @@ sinkFile File{ fileContent = Nothing, .. } = return FileReference
|
|||||||
, fileReferenceModified = fileModified
|
, fileReferenceModified = fileModified
|
||||||
}
|
}
|
||||||
sinkFile File{ fileContent = Just fileContentContent, .. } = do
|
sinkFile File{ fileContent = Just fileContentContent, .. } = do
|
||||||
(unsealConduitT -> fileContentContent', isEmpty) <- fileContentContent $$+ is _Nothing <$> C.peekE
|
chunk <- liftIO newEmptyTMVarIO
|
||||||
|
sourceAsync <- allocateLinkedAsync . runConduit $ fileContentContent .| C.mapM_ (atomically . putTMVar chunk)
|
||||||
|
|
||||||
|
isEmpty <- atomically $
|
||||||
|
False <$ readTMVar chunk
|
||||||
|
<|> True <$ waitSTM sourceAsync
|
||||||
|
|
||||||
|
let fileContentContent' = evalContT . callCC $ \finishConsume -> forever $ do
|
||||||
|
inpChunk <- atomically $
|
||||||
|
Right <$> takeTMVar chunk
|
||||||
|
<|> Left <$> waitCatchSTM sourceAsync
|
||||||
|
|
||||||
|
case inpChunk of
|
||||||
|
Right inpChunk' -> lift $ yield inpChunk'
|
||||||
|
Left (Left exc) -> throwM exc
|
||||||
|
Left (Right res) -> finishConsume res
|
||||||
|
|
||||||
fileContentHash <- if
|
fileContentHash <- if
|
||||||
| not isEmpty -> maybeT (sinkFileDB False fileContentContent') $ sinkFileMinio fileContentContent'
|
| not isEmpty -> maybeT (sinkFileDB False fileContentContent') $ sinkFileMinio fileContentContent'
|
||||||
|
|||||||
@ -62,6 +62,16 @@ $newline never
|
|||||||
^{formatTimeW SelFormatDateTime deadline}
|
^{formatTimeW SelFormatDateTime deadline}
|
||||||
$nothing
|
$nothing
|
||||||
_{MsgAllocationNextSubstitutesDeadlineNever}
|
_{MsgAllocationNextSubstitutesDeadlineNever}
|
||||||
|
<dt .deflist__dt>
|
||||||
|
_{MsgAllocationFreeCapacity} #
|
||||||
|
^{iconInvisible}
|
||||||
|
<dd .deflist__dd>
|
||||||
|
$maybe freeCap <- freeCapacity
|
||||||
|
#{freeCap}
|
||||||
|
$if freeCap <= 0
|
||||||
|
\ ^{iconOK}
|
||||||
|
$nothing
|
||||||
|
∞
|
||||||
$maybe fromT <- allocationRegisterByCourse
|
$maybe fromT <- allocationRegisterByCourse
|
||||||
<dt .deflist__dt>
|
<dt .deflist__dt>
|
||||||
_{MsgAllocationRegisterByCourseFrom}
|
_{MsgAllocationRegisterByCourseFrom}
|
||||||
|
|||||||
@ -24,6 +24,15 @@ $if isAdmin
|
|||||||
_{MsgCourseAllocationCourseAcceptsSubstitutesNever}
|
_{MsgCourseAllocationCourseAcceptsSubstitutesNever}
|
||||||
$if allocationCourseAcceptSubstitutes >= Just now
|
$if allocationCourseAcceptSubstitutes >= Just now
|
||||||
\ ^{iconOK}
|
\ ^{iconOK}
|
||||||
|
<p>
|
||||||
|
_{MsgCourseAllocationCourseParticipants}:
|
||||||
|
$maybe capacity <- courseCapacity
|
||||||
|
\ _{MsgCourseMembersCountLimited partCount capacity}
|
||||||
|
$if partCount < capacity
|
||||||
|
\ ^{iconProblem}
|
||||||
|
$nothing
|
||||||
|
\ _{MsgCourseMembersCount partCount}
|
||||||
|
\ ^{iconProblem}
|
||||||
$if hasApplicationTemplate || is _Just courseApplicationsInstructions
|
$if hasApplicationTemplate || is _Just courseApplicationsInstructions
|
||||||
<div .allocation-course__instructions-label .allocation__label>
|
<div .allocation-course__instructions-label .allocation__label>
|
||||||
_{MsgCourseApplicationInstructionsApplication}
|
_{MsgCourseApplicationInstructionsApplication}
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
Das Eintragen von Fristen bis zu denen Nachrücker aus Zentralanmeldungen akzeptiert werden ist nun möglich
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
It is now possible to specify deadlines up to which substitute registrations from central allocations are accepted
|
||||||
108
templates/i18n/faq/allocation-no-places.de-de-formal.hamlet
Normal file
108
templates/i18n/faq/allocation-no-places.de-de-formal.hamlet
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
Die Plätze in den Zentralanmeldungen werden nach den folgenden #
|
||||||
|
Kriterien verteilt (in grober Reihenfolge des Einfluss, den sie auf #
|
||||||
|
die Verteilung haben):
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Die eigene Priorisierung der Bewerbung (1. Wahl, etc.)
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Die Priorisierung hat jedoch nur eine ordnende Funktion und #
|
||||||
|
diese auch nur innerhalb der Bewerbungen eines einzelnen #
|
||||||
|
Bewerbers. #
|
||||||
|
|
||||||
|
Die genauen Zahlen sind also bedeutungslos und werden auch nicht #
|
||||||
|
unter den Bewerbern verglichen.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
Studienfortschritt (gemessen am Prozentsatz der für den Abschluss #
|
||||||
|
erforderlichen Veranstaltungen, die bereits bestanden wurden), #
|
||||||
|
nicht jedoch das Fach- oder Hochschulsemester
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Den aus dem Studienfortschritt errechnet Parameter nennt Uni2work #
|
||||||
|
die „zentrale Dringlichkeit“.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
Etwaige Bewertungen der Bewerbungen durch die Kursverwalter
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
Wenn Sie also keine Plätze in der Zentralanmeldung erhalten haben, #
|
||||||
|
liegt dies für gewöhnlich daran, dass Ihre zentrale Dringlichkeit in #
|
||||||
|
dieser Vergabe zu gering war und stattdessen andere Bewerber, mit #
|
||||||
|
weiter fortgeschrittenem Studium, Plätze erhalten haben.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Ebenso kann es sein, dass Sie nicht Ihre erste Wahl erhalten, wenn #
|
||||||
|
diese unter Studierenden mit höherer Dringlichkeit beliebt ist.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
So wird sichergestellt, dass der Studienabschluss nicht durch #
|
||||||
|
fehlende Credits verzögert wird, die nur in Kursen erreicht werden #
|
||||||
|
können, die an einer Zentralanmeldung teilnehmen.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
Für gewöhnlich gibt es zu jeder Zentralanmeldung auch ein #
|
||||||
|
Nachrückerverfahren. #
|
||||||
|
|
||||||
|
Es werden hierfür auf Basis der Bewerbungen für die #
|
||||||
|
Zentralanmeldungen Plätze, die wieder frei werden, erneut verteilt.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Die Kriterien für diese Verteilungen sind die selben, wie auch bei #
|
||||||
|
der ursprünglichen Verteilung. #
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Wenn Sie sich bereits in der Zentralanmeldung beworben haben, ist #
|
||||||
|
eine gesonderte Anmeldung oder Bewerbung als Nachrücker nicht #
|
||||||
|
erforderlich. #
|
||||||
|
|
||||||
|
Sie werden automatisch benachrichtigt, falls Sie über das #
|
||||||
|
Nachrückerverfahren doch noch einen Platz bzw. zusätzliche Plätze #
|
||||||
|
erhalten (außer Sie haben diese Benachrichtigung aktiv unter #
|
||||||
|
„Anpassen“ ausgeschaltet).
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Um in der nächsten Zentralanmeldung eine bessere Chance auf einen #
|
||||||
|
Platz zu haben können Sie folgende Schritte ergreifen:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Für möglichst viele der angebotenen Kurse bewerben
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Bei gleicher zentraler Dringlichkeit haben Bewerber, die mehr #
|
||||||
|
Bewerbungen einreichen, eine signifikant bessere Chance einen #
|
||||||
|
Platz zu erhalten.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Normal weiter studieren
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Durch zusätzliche bestandene Leistungen wird sich Ihr #
|
||||||
|
Studienfortschritt und somit Ihre zentrale Dringlichkeit erhöhen.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Bessere Bewerbungen einreichen
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Eine gute Bewertung der Bewerbung kann einen beträchtlichen #
|
||||||
|
Unterschied in zentraler Dringlichkeit ausgleichen. #
|
||||||
|
|
||||||
|
Wenn Ihre Bewerbungen von den Kursverwaltern gut bewertet werden, #
|
||||||
|
haben Sie eine bessere Chance auf einen Platz.
|
||||||
105
templates/i18n/faq/allocation-no-places.en-eu.hamlet
Normal file
105
templates/i18n/faq/allocation-no-places.en-eu.hamlet
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
Placements in central allocations are allocated according to the #
|
||||||
|
following criteria (ordered roughly by their impact on the #
|
||||||
|
allocation):
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The priority of the application (1st Choice, etc.)
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
The priority is only used to order the applications in the context #
|
||||||
|
of a single applicant. #
|
||||||
|
|
||||||
|
Therefore the exact numerical values are inconsequential and are #
|
||||||
|
not compared between applicants.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Study progress (measured by the number ECTS credits achieved as a #
|
||||||
|
percentage of those required for graduation) but not (university) #
|
||||||
|
semesters
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
The parameter calculated from study progress is referred to within #
|
||||||
|
Uni2work as “central priority”.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Ratings of applications by course administrators
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you were not allocated any placements this is usually because #
|
||||||
|
your central priority was too low. #
|
||||||
|
|
||||||
|
Instead other applicants with higher central priority, and thus a #
|
||||||
|
higher degree of study progress, have received placements.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Accordingly you may not have received the placements you wanted #
|
||||||
|
because the respective courses were popular among applicants with #
|
||||||
|
higher central priority.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
This method of allocation ensures that graduation is not impeded by #
|
||||||
|
missing credits which can only be gained through courses which #
|
||||||
|
participate in a central allocation.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
There usually is a process for substitute registrations. #
|
||||||
|
|
||||||
|
Places that become free after the initial allocation are assigned #
|
||||||
|
again on the basis of the existing applications.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
The criteria for the allocation of placements are the same as for #
|
||||||
|
the initial allocation.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
If you have already applied for the central allocation no further #
|
||||||
|
registration or application is necessary to be assigned a substitute #
|
||||||
|
registration. #
|
||||||
|
|
||||||
|
You will be notified automatically if you are assigned additional #
|
||||||
|
placements (unless you have actively disabled the notification under #
|
||||||
|
“Settings”).
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
To improve your chances of being allocated a placement during the #
|
||||||
|
next central allocation, you may try the following:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Apply for as many courses as possible
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Of two applicants with the same central priority, the one who #
|
||||||
|
applied for more courses has a significantly better chance of #
|
||||||
|
being allocated a placement.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Continue your studies normally
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Through achieving additional credits your degree of study progress #
|
||||||
|
will improve and thus your central priority will, too.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Write better applications
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
Having an application rated well can ameliorate a considerable #
|
||||||
|
difference in central priority. #
|
||||||
|
|
||||||
|
If your applications are rated well by course administrators your #
|
||||||
|
chances to be allocated a placement improve.
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
$newline never
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Gewöhnlicherweise wird Ihr Benutzereintrag gesperrt, wenn sie #
|
||||||
|
exmatrikuliert werden bzw. Ihr Beschäftigungsverhältnis endet. #
|
||||||
|
|
||||||
|
Es kommt gelegentlich vor, dass Ihr Benutzereintrag nicht korrekt #
|
||||||
|
entsperrt wird, wenn Sie wieder immatrikuliert bzw. eingestellt #
|
||||||
|
werden.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Falls Sie aktuell immatrikuliert bzw. eingestellt sind, oder Sie #
|
||||||
|
einen anderen triftigen Grund vorweisen können, warum Sie Zugang zu #
|
||||||
|
Uni2work brauchen, wenden Sie sich bitte über #
|
||||||
|
das <a href=@{HelpR}>Hilfe-Formular</a>, oben rechts auf jeder #
|
||||||
|
Seite, an die Uni2work-Administration und schildern Sie Ihre #
|
||||||
|
Situation.
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
$newline never
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Usually your account is disabled once you are no longer matriculated #
|
||||||
|
(i.e. registered as a student) or employed. #
|
||||||
|
|
||||||
|
Occasionally accounts are not correctly re-enabled once you are #
|
||||||
|
matriculated or employed, again.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you are currently matriculated, employed, or have another good #
|
||||||
|
reason why you should have access to Uni2work, please contact a #
|
||||||
|
Uni2work-Administrator using the <a href=@{HelpR}>Support form</a> #
|
||||||
|
(at the top right of every page) and describe your situation.
|
||||||
Reference in New Issue
Block a user