feat: demand authorship statements
This commit is contained in:
parent
2d95f353c1
commit
34b3e6ae21
@ -1697,3 +1697,35 @@ video
|
||||
width: 90%
|
||||
margin: 0.5em auto
|
||||
background-color: var(--color-grey)
|
||||
|
||||
.authorship-statement
|
||||
& > dt
|
||||
font-weight: 600
|
||||
color: var(--color-fontsec)
|
||||
font-style: italic
|
||||
font-size: .9rem
|
||||
|
||||
& > dd
|
||||
margin-left: 1em
|
||||
|
||||
& + dt
|
||||
margin-top: .5em
|
||||
|
||||
.authorship-statement-accept__accept
|
||||
margin-top: 1em
|
||||
display: grid
|
||||
grid-template-columns: 25px 1fr
|
||||
grid-template-areas: 'checkbox label'
|
||||
|
||||
.authorship-statement-accept__container
|
||||
max-width: 600px
|
||||
max-height: 25vh
|
||||
overflow: auto
|
||||
|
||||
.authorship-statement-accept__accept-checkbox
|
||||
align-self: center
|
||||
grid-area: checkbox
|
||||
|
||||
.authorship-statement-accept__accept-label
|
||||
grid-area: label
|
||||
font-weight: 600
|
||||
|
||||
@ -316,6 +316,6 @@ ExamFinished: Ergebnisse sichtbar ab
|
||||
ExamAuthorshipStatementSection: Eigenständigkeitserklärung
|
||||
ExamAuthorshipStatementRequired: Eigenständigkeitserklärung für prüfungszugehörige Übungsblattabgaben einfordern?
|
||||
ExamAuthorshipStatementRequiredTip: Sollen für alle zu dieser Prüfung zugehörige Übungsblätter die Abgebenden (bei Abgabegruppen jedes Gruppenmitglied) aufgefordert werden, eine Eigenständigkeitserklärung zu akzeptieren?
|
||||
ExamAuthorshipStatementRequiredForcedTip: Für dieses Institut sind Eigenständigkeitserklärungen für prüfungszugehörige Übungsblätter vorgeschrieben.
|
||||
ExamAuthorshipStatementRequiredForcedTip: Für dieses Institut ist vorgeschrieben, dass für alle zu diese Prüfung zugehörigen Übungsblätter die Abgebenden (bei Abgabegruppen jedes Gruppenmitglied) aufgefordert werden, eine Eigenständigkeitserklärung zu akzeptieren.
|
||||
ExamAuthorshipStatementContent: Eigenständigkeitserklärung
|
||||
ExamAuthorshipStatementContentForcedTip: Für dieses Institut ist die institutsweit vorgegebene Eigenständigkeitserklärung für prüfungsrelevante Übungsblätter zu verwenden. Benutzerdefinierte Erklärungen sind nicht gestattet.
|
||||
ExamAuthorshipStatementContentForcedTip: Für dieses Institut ist die institutsweit vorgegebene Eigenständigkeitserklärung für prüfungsrelevante Übungsblätter zu verwenden. Benutzerdefinierte Erklärungen sind nicht gestattet. Für alle zu diese Prüfung zugehörigen Übungsblätter werden die Abgebenden (bei Abgabegruppen jedes Gruppenmitglied) aufgefordert, diese Eigenständigkeitserklärung zu akzeptieren.
|
||||
@ -154,8 +154,14 @@ SheetGradingPassAlways: Automatisch bestanden, sobald korrigiert
|
||||
|
||||
SheetAuthorshipStatementSection: Eigenständigkeitserklärung
|
||||
SheetAuthorshipStatementRequired: Eigenständigkeitserklärung für Übungsblattabgaben einfordern?
|
||||
SheetAuthorshipStatementRequiredTip: Sollen die Abgebenden (bei Abgabegruppen jedes Gruppenmitglied) aufgefordert werden, eine Eigenständigkeitserklärung zu akzeptieren?
|
||||
SheetAuthorshipStatementRequiredTip: Sollen die Abgebenden (bei Abgabegruppen jedes Gruppenmitglied) aufgefordert werden, eine Eigenständigkeitserklärung abzugeben?
|
||||
SheetAuthorshipStatementRequiredForcedTip: Für dieses Institut sind Eigenständigkeitserklärungen für nicht-prüfungszugehörige Übungsblätter vorgeschrieben.
|
||||
SheetAuthorshipStatementContent: Eigenständigkeitserklärung
|
||||
SheetAuthorshipStatementContentForcedTip: Für dieses Institut ist die institutsweit vorgegebene Eigenständigkeitserklärung für nicht-prüfungszugehörige Übungsblätter zu verwenden. Benutzerdefinierte Erklärungen sind nicht gestattet.
|
||||
SheetAuthorshipStatementContentOverridesExamTip: Gehört dieses Übungsblatt zu einer Prüfung mit einer prüfungsweit eingestellten Eigenständigkeitserklärung, so können Sie hier eine für dieses Übungsblatt abweichende Eigenständigkeitserklärung angeben.
|
||||
SheetAuthorshipStatementExamNone: Keine Prüfung
|
||||
SheetAuthorshipStatementExam: Zugeordnete Prüfung
|
||||
SheetAuthorshipStatementMode: Eigenständigkeitserklärung
|
||||
SheetAuthorshipStatementModeDisabled: Keine Eigenständigkeitserklärungen
|
||||
SheetAuthorshipStatementModeExam: Einstellung folgt Prüfung
|
||||
SheetAuthorshipStatementModeEnabled: Eigenständigkeitserklärungen fordern
|
||||
@ -158,3 +158,9 @@ SheetAuthorshipStatementRequiredForcedTip: This school enforces Statements of Au
|
||||
SheetAuthorshipStatementContent: Statement of Authorship
|
||||
SheetAuthorshipStatementContentForcedTip: The settings of this school dictate that the school-wide Statement of Authorship for exam-unrelated sheets must be used. Custom statements are prohibited.
|
||||
SheetAuthorshipStatementContentOverridesExamTip: If this exercise sheet is related to an exam with an exam-wide Statement of Authorship set, a sheet-specific adaptation can be given here.
|
||||
SheetAuthorshipStatementExamNone: No Exam
|
||||
SheetAuthorshipStatementExam: Related exam
|
||||
SheetAuthorshipStatementMode: Statements of Authorship
|
||||
SheetAuthorshipStatementModeDisabled: No Statements of Authorship
|
||||
SheetAuthorshipStatementModeExam: Setting follows exam
|
||||
SheetAuthorshipStatementModeEnabled: Demand Statements of Authorship
|
||||
|
||||
@ -192,4 +192,11 @@ SubmissionDoneByFile: Je nach Bewertungsdatei
|
||||
SubmissionDoneAlways: Immer
|
||||
SheetGroupNoGroups: Keine Gruppenabgabe
|
||||
|
||||
CorrDownloadVersion !ident-ok: Version
|
||||
CorrDownloadVersion !ident-ok: Version
|
||||
|
||||
SubmissionAuthorshipStatement: Eigenständigkeitserklärung
|
||||
SubmissionAuthorshipStatementTip: Um abgeben zu können, müssen Sie die vorgegebene Eigenständigkeitserklärung akzeptieren. Hierfür müssen Sie die Checkbox am Ende der Erklärung zu markieren.
|
||||
SubmissionLecturerAuthorshipStatement: Eigenständigkeitserklärung
|
||||
SubmissionLecturerAuthorshipStatementTip: Wenn Sie sich selbst als Mitabgebende/Mitabgebender eintragen müssen Sie eine Eigenständigkeitserklärung abgeben. Beachten Sie, dass Sie eine Eigenständigkeitserklärung nur für sich selbst abgeben können, nicht für etwaige andere Mitabgebende; falls Sie eine Eigenständigkeitserklärung abgeben, wird diese nur unter Ihrem Namen in Uni2work gespeichert.
|
||||
SubmissionLecturerAuthorshipStatementRequiredBecauseSubmittor: Da Sie sich selbst als Mitabgebende/Mitabgebender eingetragen haben, müssen Sie eine Eigenständigkeitserklärung abgeben.
|
||||
SubmissionCoSubmittorsInviteRequiredBecauseAuthorshipStatements: Da für die Abgabe zu diesem Übungsblatt die Abgabe einer Eigenständigkeitserklärung vorausgesetzt wird, werden bekannte E-Mail Adressen bekannter Benutzer nicht aufgelöst. Mitabgebende müssen stattdessen per E-Mail eingeladen werden.
|
||||
@ -191,4 +191,11 @@ SubmissionDoneByFile: According to correction file
|
||||
SubmissionDoneAlways: Always
|
||||
SheetGroupNoGroups: No group submission
|
||||
|
||||
CorrDownloadVersion !ident-ok: Version
|
||||
CorrDownloadVersion !ident-ok: Version
|
||||
|
||||
SubmissionAuthorshipStatement: Statement of Authorship
|
||||
SubmissionAuthorshipStatementTip: To submit you have to accept the provided statement of authership. To do so you have to check the box at the end of the statement.
|
||||
SubmissionLecturerAuthorshipStatement: Statement of Authorship
|
||||
SubmissionLecturerAuthorshipStatementTip: If you enter yourself as a submittor you have to confirm the Statement of Authorship. Note that you can only confirm the Statement of Authorship for yourself. If you confirm it, it will be recorded only under your name.
|
||||
SubmissionLecturerAuthorshipStatementRequiredBecauseSubmittor: Since you have entered yourself as a submittor you have to confirm the Statement of Authorship.
|
||||
SubmissionCoSubmittorsInviteRequiredBecauseAuthorshipStatements: Since Statements of Authorship are required to submit for this exercise sheet, e-mail addresses of known users are not resolved. Instead co-submittors will have to be invited via e-mail.
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
AuthorshipStatementStatementIsRequired: Sie müssen die Eigenständigkeitserklärung als zutreffend bestätigen
|
||||
AuthorshipStatementAccept: Ich habe die obenstehende Eigenständigkeitserklärung gelesen und verstanden und erkläre hiermit, dass die obenstehenden Aussagen zutreffen.
|
||||
2
messages/uniworx/utils/authorship_statement/en-eu.msg
Normal file
2
messages/uniworx/utils/authorship_statement/en-eu.msg
Normal file
@ -0,0 +1,2 @@
|
||||
AuthorshipStatementStatementIsRequired: You have to confirm the Statement of Authorship as true and correct
|
||||
AuthorshipStatementAccept: I have read and understood the above Statement of Authorship and state that the above-mentioned statements are true and correct.
|
||||
@ -1,17 +1,12 @@
|
||||
AuthorshipStatementDefinition
|
||||
content StoredMarkup -- must contain statements in all relevant languages for now, TODO: refactor (use translations as below)
|
||||
hash AuthorshipStatementReference
|
||||
content I18nStoredMarkup
|
||||
Primary hash
|
||||
deriving Generic
|
||||
-- AuthorshipStatementDefinitionTranslation
|
||||
-- definition AuthorshipStatementDefinitionId
|
||||
-- language Lang
|
||||
-- content StoredMarkup
|
||||
-- UniqueAuthorshipStatementDefinitionTranslation definition language
|
||||
-- deriving Generic
|
||||
|
||||
-- Statement of Authorship to be issued upon submitting a solution for an exercise sheet
|
||||
-- TODO: maybe move to SubmissionUser? (With statementSigned :: Bool, statement :: Maybe StoredMarkup)
|
||||
AuthorshipStatementSubmission
|
||||
submissionUser SubmissionUserId
|
||||
statement StoredMarkup -- stored as plain StoredMarkup as the "signed" statement needs to be persisted
|
||||
UniqueAuthorshipStatementSubmission submissionUser
|
||||
statement AuthorshipStatementDefinitionId
|
||||
submission SubmissionId
|
||||
user UserId
|
||||
time UTCTime
|
||||
deriving Generic
|
||||
|
||||
@ -15,6 +15,8 @@ Sheet -- exercise sheet for a given course
|
||||
anonymousCorrection Bool default=true
|
||||
requireExamRegistration ExamId Maybe -- Students may only submit if they are registered for the given exam
|
||||
allowNonPersonalisedSubmission Bool default=true
|
||||
authorshipStatementMode SheetAuthorshipStatementMode default='exam'
|
||||
authorshipStatementExam ExamId Maybe
|
||||
authorshipStatement AuthorshipStatementDefinitionId Maybe -- sheet-specific authorship statement; for exam-unrelated sheets and as exam setting overrides
|
||||
CourseSheet course name
|
||||
deriving Generic
|
||||
|
||||
@ -14,7 +14,8 @@ module Foundation.I18n
|
||||
, UniWorXMetricsMessage(..), UniWorXNewsMessage(..), UniWorXSchoolMessage(..), UniWorXSystemMessageMessage(..)
|
||||
, UniWorXTermMessage(..), UniWorXSendMessage(..), UniWorXSiteLayoutMessage(..), UniWorXErrorMessage(..)
|
||||
, UniWorXI18nMessage(..),UniWorXJobsHandlerMessage(..), UniWorXModelTypesMessage(..), UniWorXYesodMiddlewareMessage(..)
|
||||
, ShortTermIdentifier(..)
|
||||
, UniWorXAuthorshipStatementMessage(..)
|
||||
, ShortTermIdentifier(..)
|
||||
, MsgLanguage(..)
|
||||
, ShortSex(..)
|
||||
, ShortWeekDay(..)
|
||||
@ -190,6 +191,7 @@ mkMessageAddition ''UniWorX "TablePagination" "messages/uniworx/utils/table_pagi
|
||||
mkMessageAddition ''UniWorX "Util" "messages/uniworx/utils/utils" "de-de-formal"
|
||||
mkMessageAddition ''UniWorX "Rating" "messages/uniworx/utils/rating" "de-de-formal"
|
||||
mkMessageAddition ''UniWorX "SiteLayout" "messages/uniworx/utils/site_layout" "de-de-formal"
|
||||
mkMessageAddition ''UniWorX "AuthorshipStatement" "messages/uniworx/utils/authorship_statement" "de-de-formal"
|
||||
mkMessageVariant ''UniWorX ''CampusMessage "messages/auth/campus" "de"
|
||||
mkMessageVariant ''UniWorX ''DummyMessage "messages/auth/dummy" "de"
|
||||
mkMessageVariant ''UniWorX ''PWHashMessage "messages/auth/pw-hash" "de"
|
||||
@ -303,6 +305,9 @@ embedRenderMessage ''UniWorX ''UrlFieldMessage id
|
||||
|
||||
embedRenderMessageVariant ''UniWorX ''ADInvalidCredentials ("InvalidCredentials" <>)
|
||||
|
||||
embedRenderMessage ''UniWorX ''SchoolAuthorshipStatementMode id
|
||||
embedRenderMessage ''UniWorX ''SheetAuthorshipStatementMode id
|
||||
|
||||
newtype ShortSex = ShortSex Sex
|
||||
embedRenderMessageVariant ''UniWorX ''ShortSex ("Short" <>)
|
||||
|
||||
@ -406,15 +411,6 @@ instance RenderMessage UniWorX ExamCloseMode where
|
||||
mr :: RenderMessage UniWorX msg => msg -> Text
|
||||
mr = renderMessage foundation ls
|
||||
|
||||
instance RenderMessage UniWorX SchoolAuthorshipStatementMode where
|
||||
renderMessage foundation ls = \case
|
||||
SchoolAuthorshipStatementModeNone -> mr MsgSchoolAuthorshipStatementModeNone
|
||||
SchoolAuthorshipStatementModeOptional -> mr MsgSchoolAuthorshipStatementModeOptional
|
||||
SchoolAuthorshipStatementModeRequired -> mr MsgSchoolAuthorshipStatementModeRequired
|
||||
where
|
||||
mr :: RenderMessage UniWorX msg => msg -> Text
|
||||
mr = renderMessage foundation ls
|
||||
|
||||
-- ToMessage instances for converting raw numbers to Text (no internationalization)
|
||||
-- FIXME: Use RenderMessage always
|
||||
|
||||
|
||||
@ -40,19 +40,7 @@ postEEditR tid ssh csh examn = do
|
||||
|
||||
editExamAct <- formResultMaybe editExamResult $ \ExamForm{..} -> do
|
||||
res <- trySql @ExamEditException $ do
|
||||
mAuthorshipStatementId <- case efAuthorshipStatement of
|
||||
Nothing -> return Nothing
|
||||
Just newStatementContent -> do
|
||||
mPreviousStatement <- maybe (pure Nothing) getEntity (oldExam ^. _examAuthorshipStatement)
|
||||
if
|
||||
| Just (Entity previousStatementId AuthorshipStatementDefinition{authorshipStatementDefinitionContent=previousStatementContent}) <- mPreviousStatement
|
||||
, newStatementContent == previousStatementContent
|
||||
-> return $ Just previousStatementId
|
||||
| Just (Entity previousStatementId _) <- mPreviousStatement
|
||||
-> update previousStatementId [ AuthorshipStatementDefinitionContent =. newStatementContent ] >> return (Just previousStatementId)
|
||||
| otherwise
|
||||
-> Just <$> insert AuthorshipStatementDefinition { authorshipStatementDefinitionContent = newStatementContent }
|
||||
|
||||
examAuthorshipStatement <- traverse insertAuthorshipStatement efAuthorshipStatement
|
||||
insertRes <- myReplaceUnique eId Exam
|
||||
{ examCourse = cid
|
||||
, examName = efName
|
||||
@ -75,7 +63,7 @@ postEEditR tid ssh csh examn = do
|
||||
, examExamMode = efExamMode
|
||||
, examStaff = efStaff
|
||||
, examPartsFrom = efPartsFrom
|
||||
, examAuthorshipStatement = mAuthorshipStatementId
|
||||
, examAuthorshipStatement
|
||||
}
|
||||
|
||||
when (is _Just insertRes) $
|
||||
|
||||
@ -52,7 +52,7 @@ data ExamForm = ExamForm
|
||||
, efStaff :: Maybe Text
|
||||
, efCorrectors :: Set (Either UserEmail UserId)
|
||||
, efExamParts :: Set ExamPartForm
|
||||
, efAuthorshipStatement :: Maybe StoredMarkup
|
||||
, efAuthorshipStatement :: Maybe I18nStoredMarkup
|
||||
}
|
||||
|
||||
data ExamOccurrenceForm = ExamOccurrenceForm
|
||||
@ -111,7 +111,7 @@ examForm :: ( MonadHandler m
|
||||
)
|
||||
=> Entity Course -> Maybe ExamForm -> (Html -> MForm m (FormResult ExamForm, Widget))
|
||||
examForm (Entity _ Course{..}) template csrf = hoist liftHandler $ do
|
||||
MsgRenderer mr <- getMsgRenderer
|
||||
mr'@(MsgRenderer mr) <- getMsgRenderer
|
||||
(School{..}, mSchoolAuthorshipStatement) <- liftHandler . runDBRead $ do
|
||||
school@School{..} <- getJust courseSchool
|
||||
mSchoolAuthorshipStatement <- maybe (pure Nothing) getEntity schoolSheetExamAuthorshipStatementDefinition
|
||||
@ -148,24 +148,29 @@ examForm (Entity _ Course{..}) template csrf = hoist liftHandler $ do
|
||||
<* aformSection MsgExamFormParts
|
||||
<*> examPartsForm (efExamParts <$> template)
|
||||
<*> let
|
||||
reqContentField :: (FieldSettings UniWorX -> FieldSettings UniWorX) -> AForm Handler StoredMarkup
|
||||
reqContentField ttip = areq htmlField
|
||||
(fslI MsgExamAuthorshipStatementContent & ttip)
|
||||
( (efAuthorshipStatement =<< template)
|
||||
<|> (authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
reqContentField :: (FieldSettings UniWorX -> FieldSettings UniWorX) -> AForm Handler I18nStoredMarkup
|
||||
reqContentField ttip = fmapAForm (formResultUnOpt mr' MsgSheetAuthorshipStatementContent)
|
||||
$ i18nFieldA htmlField True (\_ -> Nothing) ("authorship-statement" :: Text)
|
||||
(fslI MsgSheetAuthorshipStatementContent & ttip)
|
||||
True
|
||||
( fmap Just $ (efAuthorshipStatement =<< template)
|
||||
<|> (authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
)
|
||||
forcedContentField = aforced htmlField
|
||||
forcedContentField = aforced forcedAuthorshipStatementField
|
||||
(fslI MsgExamAuthorshipStatementContent & setTooltip MsgExamAuthorshipStatementContentForcedTip)
|
||||
(maybe mempty (authorshipStatementDefinitionContent . entityVal) mSchoolAuthorshipStatement)
|
||||
contentField ttipReq = bool forcedContentField (reqContentField ttipReq) schoolSheetExamAuthorshipStatementAllowOther
|
||||
contentField ttipReq
|
||||
| not schoolSheetExamAuthorshipStatementAllowOther
|
||||
= traverse forcedContentField $ authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement
|
||||
| otherwise
|
||||
= Just <$> reqContentField ttipReq
|
||||
in case schoolSheetExamAuthorshipStatementMode of
|
||||
SchoolAuthorshipStatementModeNone -> pure Nothing -- suppress display of whole section incl. header
|
||||
otherMode -> aformSection MsgExamAuthorshipStatementSection
|
||||
*> case otherMode of
|
||||
SchoolAuthorshipStatementModeOptional -> optionalActionA (contentField id)
|
||||
SchoolAuthorshipStatementModeOptional -> optionalActionA (fmapAForm (formResultUnOpt mr' MsgSheetAuthorshipStatementContent) $ contentField id)
|
||||
(fslI MsgExamAuthorshipStatementRequired & setTooltip MsgExamAuthorshipStatementRequiredTip)
|
||||
(is _Just . efAuthorshipStatement <$> template)
|
||||
SchoolAuthorshipStatementModeRequired -> fmap Just . contentField $ setTooltip MsgExamAuthorshipStatementRequiredForcedTip
|
||||
SchoolAuthorshipStatementModeRequired -> contentField $ setTooltip MsgExamAuthorshipStatementRequiredForcedTip
|
||||
_none -> pure Nothing
|
||||
|
||||
officeSchoolsForm :: Maybe (Set SchoolId) -> AForm Handler (Set SchoolId)
|
||||
|
||||
@ -29,7 +29,7 @@ postCExamNewR tid ssh csh = do
|
||||
newExamAct <- formResultMaybe newExamResult $ \ExamForm{..} -> do
|
||||
now <- liftIO getCurrentTime
|
||||
|
||||
mAuthorshipStatementId <- maybe (return Nothing) (fmap Just . insert . AuthorshipStatementDefinition) efAuthorshipStatement
|
||||
examAuthorshipStatement <- traverse insertAuthorshipStatement efAuthorshipStatement
|
||||
|
||||
insertRes <- insertUnique Exam
|
||||
{ examName = efName
|
||||
@ -53,7 +53,7 @@ postCExamNewR tid ssh csh = do
|
||||
, examExamMode = efExamMode
|
||||
, examStaff = efStaff
|
||||
, examPartsFrom = efPartsFrom
|
||||
, examAuthorshipStatement = mAuthorshipStatementId
|
||||
, examAuthorshipStatement
|
||||
}
|
||||
whenIsJust insertRes $ \examid -> do
|
||||
insertMany_
|
||||
|
||||
@ -69,10 +69,10 @@ data SchoolForm = SchoolForm
|
||||
, sfExamDiscouragedModes :: ExamModeDNF
|
||||
, sfExamCloseMode :: ExamCloseMode
|
||||
, sfSheetAuthorshipStatementMode :: SchoolAuthorshipStatementMode
|
||||
, sfSheetAuthorshipStatementDefinition :: Maybe StoredMarkup -- TODO: Must contain statements in all relevant languages for now; later use `Maybe (Map Lang StoredMarkup)` instead
|
||||
, sfSheetAuthorshipStatementDefinition :: Maybe I18nStoredMarkup
|
||||
, sfSheetAuthorshipStatementAllowOther :: Bool
|
||||
, sfSheetExamAuthorshipStatementMode :: SchoolAuthorshipStatementMode
|
||||
, sfSheetExamAuthorshipStatementDefinition :: Maybe StoredMarkup -- TODO: Must contain statements in all relevant languages for now; later use `Maybe (Map Lang StoredMarkup)` instead
|
||||
, sfSheetExamAuthorshipStatementDefinition :: Maybe I18nStoredMarkup
|
||||
, sfSheetExamAuthorshipStatementAllowOther :: Bool
|
||||
}
|
||||
|
||||
@ -88,12 +88,13 @@ mkSchoolForm mSsh template = renderAForm FormStandard $ SchoolForm
|
||||
<*> (fromMaybe (ExamModeDNF predDNFFalse) <$> aopt pathPieceField (fslI MsgSchoolExamDiscouragedModes) (Just $ sfExamDiscouragedModes <$> template <|> pure (ExamModeDNF predDNFFalse)))
|
||||
<*> apopt (selectField optionsFinite) (fslI MsgExamCloseMode) (sfExamCloseMode <$> template <|> pure ExamCloseSeparate)
|
||||
<* aformSection MsgSchoolAuthorshipStatementSection
|
||||
<*> areq (selectField optionsFinite) (fslI MsgSchoolAuthorshipStatementSheetMode) (sfSheetAuthorshipStatementMode <$> template <|> pure SchoolAuthorshipStatementModeOptional) -- FIXME: SchoolAuthorshipStatementModeNone leads to FormFailure
|
||||
<*> aopt htmlField (fslI MsgSchoolAuthorshipStatementSheetDefinition & setTooltip MsgSchoolAuthorshipStatementSheetDefinitionTip) (sfSheetAuthorshipStatementDefinition <$> template)
|
||||
<*> apopt (selectField optionsFinite) (fslI MsgSchoolAuthorshipStatementSheetMode) (sfSheetAuthorshipStatementMode <$> template <|> pure SchoolAuthorshipStatementModeOptional)
|
||||
<*> i18nFieldA htmlField False (\_ -> Nothing) ("sheet-authorship-statement-definition" :: Text) (fslI MsgSchoolAuthorshipStatementSheetDefinition & setTooltip MsgSchoolAuthorshipStatementSheetDefinitionTip) False (sfSheetAuthorshipStatementDefinition <$> template)
|
||||
<*> apopt checkBoxField (fslI MsgSchoolAuthorshipStatementSheetAllowOther) (sfSheetAuthorshipStatementAllowOther <$> template <|> pure True)
|
||||
<*> areq (selectField optionsFinite) (fslI MsgSchoolAuthorshipStatementSheetExamMode) (sfSheetExamAuthorshipStatementMode <$> template <|> pure SchoolAuthorshipStatementModeOptional) -- FIXME: SchoolAuthorshipStatementModeNone leads to FormFailure
|
||||
<*> aopt htmlField (fslI MsgSchoolAuthorshipStatementSheetExamDefinition & setTooltip MsgSchoolAuthorshipStatementSheetExamDefinitionTip) (sfSheetExamAuthorshipStatementDefinition <$> template)
|
||||
<*> apopt (selectField optionsFinite) (fslI MsgSchoolAuthorshipStatementSheetExamMode) (sfSheetExamAuthorshipStatementMode <$> template <|> pure SchoolAuthorshipStatementModeOptional)
|
||||
<*> i18nFieldA htmlField False (\_ -> Nothing) ("exam-authorship-statement-definition" :: Text) (fslI MsgSchoolAuthorshipStatementSheetExamDefinition & setTooltip MsgSchoolAuthorshipStatementSheetExamDefinitionTip) False (sfSheetExamAuthorshipStatementDefinition <$> template)
|
||||
<*> apopt checkBoxField (fslI MsgSchoolAuthorshipStatementSheetExamAllowOther) (sfSheetExamAuthorshipStatementAllowOther <$> template <|> pure True)
|
||||
-- TODO(AuthorshipStatements): disallow not allowOther && is _Nothing definition
|
||||
where
|
||||
ldapOrgs :: HandlerFor UniWorX (OptionList (CI Text))
|
||||
ldapOrgs = fmap (mkOptionList . map (\t -> Option (CI.original t) t (CI.original t)) . Set.toAscList) . runDB $
|
||||
@ -104,11 +105,6 @@ schoolToForm ssh = do
|
||||
School{..} <- get404 ssh
|
||||
ldapFrags <- selectList [SchoolLdapSchool ==. Just ssh] []
|
||||
|
||||
-- TODO: allow for separate translations
|
||||
-- let getAuthorshipStatementDefs = maybe (return Nothing) (\definitionId -> Just <$> selectList [ AuthorshipStatementDefinitionTranslationDefinition ==. definitionId ] [])
|
||||
-- authorshipStatementDefs <- getAuthorshipStatementDefs schoolSheetAuthorshipStatementDefinition
|
||||
-- examAuthorshipStatementDefs <- getAuthorshipStatementDefs schoolSheetExamAuthorshipStatementDefinition
|
||||
|
||||
mSheetAuthorshipStatementDefinition <- maybe (return Nothing) get schoolSheetAuthorshipStatementDefinition
|
||||
mSheetExamAuthorshipStatementDefinition <- maybe (return Nothing) get schoolSheetExamAuthorshipStatementDefinition
|
||||
|
||||
@ -129,7 +125,6 @@ schoolToForm ssh = do
|
||||
, sfSheetExamAuthorshipStatementAllowOther = schoolSheetExamAuthorshipStatementAllowOther
|
||||
}
|
||||
|
||||
|
||||
getSchoolEditR, postSchoolEditR :: SchoolId -> Handler Html
|
||||
getSchoolEditR = postSchoolEditR
|
||||
postSchoolEditR ssh = do
|
||||
@ -139,9 +134,8 @@ postSchoolEditR ssh = do
|
||||
|
||||
formResult sfResult $ \SchoolForm{..} -> do
|
||||
runDB $ do
|
||||
let insertAuthorshipStatement = maybe (pure Nothing) $ fmap Just . insert . AuthorshipStatementDefinition
|
||||
mSheetAuthorshipStatementId <- insertAuthorshipStatement sfSheetAuthorshipStatementDefinition
|
||||
mSheetExamAuthorshipStatementId <- insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
mSheetAuthorshipStatementId <- traverse insertAuthorshipStatement sfSheetAuthorshipStatementDefinition
|
||||
mSheetExamAuthorshipStatementId <- traverse insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
update ssh
|
||||
[ SchoolName =. sfName
|
||||
, SchoolExamMinimumRegisterBeforeStart =. sfExamMinimumRegisterBeforeStart
|
||||
@ -189,9 +183,8 @@ postSchoolNewR = do
|
||||
formResult sfResult $ \SchoolForm{..} -> do
|
||||
let ssh = SchoolKey sfShorthand
|
||||
insertOkay <- runDB $ do
|
||||
let insertAuthorshipStatement = maybe (pure Nothing) $ fmap Just . insert . AuthorshipStatementDefinition
|
||||
mSheetAuthorshipStatementId <- insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
mSheetExamAuthorshipStatementId <- insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
mSheetAuthorshipStatementId <- traverse insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
mSheetExamAuthorshipStatementId <- traverse insertAuthorshipStatement sfSheetExamAuthorshipStatementDefinition
|
||||
didInsert <- is _Just <$> insertUnique School
|
||||
{ schoolShorthand = sfShorthand
|
||||
, schoolName = sfName
|
||||
|
||||
@ -61,6 +61,8 @@ postSEditR tid ssh csh shn = do
|
||||
, spffAllowNonPersonalisedSubmission = sheetAllowNonPersonalisedSubmission
|
||||
, spffFiles = Nothing
|
||||
}
|
||||
, sfAuthorshipStatementMode = sheetAuthorshipStatementMode
|
||||
, sfAuthorshipStatementExam = sheetAuthorshipStatementExam
|
||||
, sfAuthorshipStatement = authorshipStatementDefinitionContent . entityVal <$> mAuthorshipStatement
|
||||
}
|
||||
|
||||
@ -101,6 +103,8 @@ handleSheetEdit tid ssh csh msId template dbAction = do
|
||||
-- -- statement not modified: return id of old statement
|
||||
-- | otherwise -> return $ entityKey <$> mOldAuthorshipStatement
|
||||
-- mNewAuthorshipStatementId <- insertNewOrKeepStatement sfAuthorshipStatement
|
||||
|
||||
sheetAuthorshipStatement <- traverse insertAuthorshipStatement sfAuthorshipStatement
|
||||
|
||||
let newSheet = Sheet
|
||||
{ sheetCourse = cid
|
||||
@ -119,7 +123,9 @@ handleSheetEdit tid ssh csh msId template dbAction = do
|
||||
, sheetAnonymousCorrection = sfAnonymousCorrection
|
||||
, sheetRequireExamRegistration = sfRequireExamRegistration
|
||||
, sheetAllowNonPersonalisedSubmission = maybe True spffAllowNonPersonalisedSubmission sfPersonalF
|
||||
, sheetAuthorshipStatement = Nothing -- TODO: implement sheet-specific statements
|
||||
, sheetAuthorshipStatementMode = sfAuthorshipStatementMode
|
||||
, sheetAuthorshipStatementExam = sfAuthorshipStatementExam
|
||||
, sheetAuthorshipStatement
|
||||
}
|
||||
mbsid <- dbAction newSheet
|
||||
case mbsid of
|
||||
|
||||
@ -10,6 +10,7 @@ import Handler.Utils
|
||||
import Handler.Utils.Invitations
|
||||
|
||||
import qualified Database.Esqueleto.Legacy as E
|
||||
import qualified Database.Esqueleto.Utils as E
|
||||
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Map as Map
|
||||
@ -42,7 +43,9 @@ data SheetForm = SheetForm
|
||||
, sfMarkingText :: Maybe StoredMarkup
|
||||
, sfAnonymousCorrection :: Bool
|
||||
, sfCorrectors :: Loads
|
||||
, sfAuthorshipStatement :: Maybe StoredMarkup
|
||||
, sfAuthorshipStatementMode :: SheetAuthorshipStatementMode
|
||||
, sfAuthorshipStatementExam :: Maybe ExamId
|
||||
, sfAuthorshipStatement :: Maybe I18nStoredMarkup
|
||||
}
|
||||
|
||||
data SheetPersonalisedFilesForm = SheetPersonalisedFilesForm
|
||||
@ -64,7 +67,7 @@ makeSheetForm cId msId template = identifyForm FIDsheet . validateForm validateS
|
||||
oldFileIds <- (return.) <$> case msId of
|
||||
Nothing -> return $ partitionFileType mempty
|
||||
(Just sId) -> liftHandler $ runDB $ getFtIdMap sId
|
||||
MsgRenderer mr <- getMsgRenderer
|
||||
mr'@(MsgRenderer mr) <- getMsgRenderer
|
||||
ctime <- ceilingQuarterHour <$> liftIO getCurrentTime
|
||||
((School{..}, mSchoolAuthorshipStatement), _course) <- liftHandler . runDB $ do
|
||||
course@Course{courseSchool} <- get404 cId
|
||||
@ -74,59 +77,149 @@ makeSheetForm cId msId template = identifyForm FIDsheet . validateForm validateS
|
||||
MaybeT . getEntity $ statementId
|
||||
return ((school, mSchoolAuthorshipStatement), course)
|
||||
sheetPersonalisedFilesForm <- makeSheetPersonalisedFilesForm $ template >>= sfPersonalF
|
||||
flip (renderAForm FormStandard) html $ SheetForm
|
||||
<$> areq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template)
|
||||
<*> aopt htmlField (fslI MsgSheetDescription) (sfDescription <$> template)
|
||||
<*> optionalActionA (apreq (examField Nothing cId) (fslI MsgSheetRequiredExam) (sfRequireExamRegistration =<< template)) (fslI MsgSheetRequireExam & setTooltip MsgSheetRequireExamTip) (is _Just . sfRequireExamRegistration <$> template)
|
||||
<* aformSection MsgSheetFormFiles
|
||||
<*> aopt (multiFileField $ oldFileIds SheetExercise) (fslI MsgSheetExercise) (sfSheetF <$> template)
|
||||
<*> aopt (multiFileField $ oldFileIds SheetHint) (fslI MsgSheetHint) (sfHintF <$> template)
|
||||
<*> aopt (multiFileField $ oldFileIds SheetSolution) (fslI MsgSheetSolution) (sfSolutionF <$> template)
|
||||
<*> aopt (multiFileField $ oldFileIds SheetMarking) (fslI MsgSheetMarkingFiles
|
||||
& setTooltip MsgSheetMarkingTip) (sfMarkingF <$> template)
|
||||
<*> optionalActionA sheetPersonalisedFilesForm (fslI MsgSheetPersonalisedFiles & setTooltip MsgSheetPersonalisedFilesTip) (is _Just . sfPersonalF <$> template)
|
||||
<* aformSection MsgSheetFormTimes
|
||||
<*> aopt utcTimeField (fslI MsgSheetVisibleFrom
|
||||
& setTooltip MsgSheetVisibleFromTip)
|
||||
((sfVisibleFrom <$> template) <|> pure (Just ctime))
|
||||
<*> aopt utcTimeField (fslI MsgSheetActiveFrom
|
||||
& setTooltip MsgSheetActiveFromTip)
|
||||
(sfActiveFrom <$> template)
|
||||
<*> aopt utcTimeField (fslI MsgSheetActiveTo & setTooltip MsgSheetActiveToTip) (sfActiveTo <$> template)
|
||||
<*> aopt utcTimeField (fslpI MsgSheetHintFrom (mr MsgSheetHintFromPlaceholder)
|
||||
& setTooltip MsgSheetHintFromTip) (sfHintFrom <$> template)
|
||||
<*> aopt utcTimeField (fslpI MsgSheetSolutionFrom (mr MsgSheetSolutionFromPlaceholder)
|
||||
& setTooltip MsgSheetSolutionFromTip) (sfSolutionFrom <$> template)
|
||||
<* aformSection MsgSheetFormType
|
||||
<*> submissionModeForm ((sfSubmissionMode <$> template) <|> pure (SubmissionMode False . Just $ UploadAny True defaultExtensionRestriction False))
|
||||
<*> sheetGroupAFormReq (fslI MsgSheetGroup) ((sfGrouping <$> template) <|> pure NoGroups)
|
||||
<*> sheetTypeAFormReq cId (fslI MsgSheetSheetType) (sfType <$> template)
|
||||
<*> apopt checkBoxField (fslI MsgAutoAssignCorrs) (sfAutoDistribute <$> template)
|
||||
<*> aopt htmlField (fslI MsgSheetMarking) (sfMarkingText <$> template)
|
||||
<*> apopt checkBoxField (fslI MsgSheetAnonymousCorrection & setTooltip MsgSheetAnonymousCorrectionTip) (sfAnonymousCorrection <$> template)
|
||||
<*> correctorForm (maybe mempty sfCorrectors template)
|
||||
-- TODO: add info: define exam-unrelated/related, if exam-unrelated: applies to sheet, if exam-related: overrides exam-wide authship statement settings
|
||||
-- TODO: compare versions of current school statement and template statement: school > template if school statement is newer than template statement, template > school otherwise (TODO: add lastEdited to models?)
|
||||
<*> let
|
||||
reqContentField :: (FieldSettings UniWorX -> FieldSettings UniWorX) -> AForm Handler StoredMarkup
|
||||
reqContentField ttip = areq htmlField
|
||||
(fslI MsgSheetAuthorshipStatementContent & ttip)
|
||||
( (sfAuthorshipStatement =<< template)
|
||||
<|> (authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
)
|
||||
forcedContentField = aforced htmlField
|
||||
(fslI MsgSheetAuthorshipStatementContent & setTooltip MsgSheetAuthorshipStatementContentForcedTip)
|
||||
(maybe mempty (authorshipStatementDefinitionContent . entityVal) mSchoolAuthorshipStatement)
|
||||
contentField ttipReq = bool forcedContentField (reqContentField ttipReq) schoolSheetAuthorshipStatementAllowOther
|
||||
in case schoolSheetAuthorshipStatementMode of
|
||||
SchoolAuthorshipStatementModeNone -> pure Nothing -- suppress display of whole section incl. header
|
||||
otherMode -> aformSection MsgSheetAuthorshipStatementSection
|
||||
*> case otherMode of
|
||||
SchoolAuthorshipStatementModeOptional -> optionalActionA (contentField id)
|
||||
(fslI MsgSheetAuthorshipStatementRequired & setTooltip MsgSheetAuthorshipStatementRequiredTip)
|
||||
(is _Just . sfAuthorshipStatement <$> template)
|
||||
SchoolAuthorshipStatementModeRequired -> fmap Just . contentField $ setTooltip MsgSheetAuthorshipStatementRequiredForcedTip
|
||||
_none -> pure Nothing
|
||||
flip (renderWForm FormStandard) html $ do
|
||||
sfNameRes <- wreq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template)
|
||||
sfDescriptionRes <- wopt htmlField (fslI MsgSheetDescription) (sfDescription <$> template)
|
||||
sfRequireExamRegistrationRes <- optionalActionW (apreq (examField Nothing cId) (fslI MsgSheetRequiredExam) (sfRequireExamRegistration =<< template)) (fslI MsgSheetRequireExam & setTooltip MsgSheetRequireExamTip) (is _Just . sfRequireExamRegistration <$> template)
|
||||
|
||||
wformSection MsgSheetFormFiles
|
||||
sfSheetFRes <- wopt (multiFileField $ oldFileIds SheetExercise) (fslI MsgSheetExercise) (sfSheetF <$> template)
|
||||
sfHintFRes <- wopt (multiFileField $ oldFileIds SheetHint) (fslI MsgSheetHint) (sfHintF <$> template)
|
||||
sfSolutionFRes <- wopt (multiFileField $ oldFileIds SheetSolution) (fslI MsgSheetSolution) (sfSolutionF <$> template)
|
||||
sfMarkingFRes <- wopt (multiFileField $ oldFileIds SheetMarking) (fslI MsgSheetMarkingFiles & setTooltip MsgSheetMarkingTip) (sfMarkingF <$> template)
|
||||
sfPersonalFRes <- optionalActionW sheetPersonalisedFilesForm (fslI MsgSheetPersonalisedFiles & setTooltip MsgSheetPersonalisedFilesTip) (is _Just . sfPersonalF <$> template)
|
||||
|
||||
wformSection MsgSheetFormTimes
|
||||
sfVisibleFromRes <- wopt utcTimeField (fslI MsgSheetVisibleFrom & setTooltip MsgSheetVisibleFromTip) ((sfVisibleFrom <$> template) <|> pure (Just ctime))
|
||||
sfActiveFromRes <- wopt utcTimeField (fslI MsgSheetActiveFrom & setTooltip MsgSheetActiveFromTip) (sfActiveFrom <$> template)
|
||||
sfActiveToRes <- wopt utcTimeField (fslI MsgSheetActiveTo & setTooltip MsgSheetActiveToTip) (sfActiveTo <$> template)
|
||||
sfHintFromRes <- wopt utcTimeField (fslpI MsgSheetHintFrom (mr MsgSheetHintFromPlaceholder) & setTooltip MsgSheetHintFromTip) (sfHintFrom <$> template)
|
||||
sfSolutionFromRes <- wopt utcTimeField (fslpI MsgSheetSolutionFrom (mr MsgSheetSolutionFromPlaceholder) & setTooltip MsgSheetSolutionFromTip) (sfSolutionFrom <$> template)
|
||||
|
||||
wformSection MsgSheetFormType
|
||||
sfSubmissionModeRes <- aFormToWForm . submissionModeForm $ (sfSubmissionMode <$> template) <|> pure (SubmissionMode False . Just $ UploadAny True defaultExtensionRestriction False)
|
||||
sfGroupingRes <- aFormToWForm $ sheetGroupAFormReq (fslI MsgSheetGroup) ((sfGrouping <$> template) <|> pure NoGroups)
|
||||
sfTypeRes <- aFormToWForm $ sheetTypeAFormReq cId (fslI MsgSheetSheetType) (sfType <$> template)
|
||||
sfAutoDistributeRes <- wpopt checkBoxField (fslI MsgAutoAssignCorrs) (sfAutoDistribute <$> template)
|
||||
sfMarkingTextRes <- wopt htmlField (fslI MsgSheetMarking) (sfMarkingText <$> template)
|
||||
sfAnonymousCorrectionRes <- wpopt checkBoxField (fslI MsgSheetAnonymousCorrection & setTooltip MsgSheetAnonymousCorrectionTip) (sfAnonymousCorrection <$> template)
|
||||
sfCorrectorsRes <- aFormToWForm . correctorForm $ maybe mempty sfCorrectors template
|
||||
|
||||
let sfAuthorshipStatementExam' = sfAuthorshipStatementExam =<< template
|
||||
sfAuthorshipStatement' = sfAuthorshipStatement =<< template
|
||||
(sfAuthorshipStatementModeRes, sfAuthorshipStatementExamRes, sfAuthorshipStatementRes)
|
||||
<- if | isn't _SchoolAuthorshipStatementModeNone schoolSheetAuthorshipStatementMode -> do
|
||||
wformSection MsgSheetAuthorshipStatementSection
|
||||
|
||||
let
|
||||
reqContentField :: AForm Handler I18nStoredMarkup
|
||||
reqContentField = formResultUnOpt mr' MsgSheetAuthorshipStatementContent
|
||||
`fmapAForm` i18nFieldA htmlField True (\_ -> Nothing) ("authorship-statement" :: Text)
|
||||
(fslI MsgSheetAuthorshipStatementContent)
|
||||
True
|
||||
( fmap Just $ (sfAuthorshipStatement =<< template)
|
||||
<|> (authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
)
|
||||
forcedContentField = wforced forcedAuthorshipStatementField (fslI MsgSheetAuthorshipStatementContent & setTooltip MsgSheetAuthorshipStatementContentForcedTip)
|
||||
|
||||
if | not schoolSheetAuthorshipStatementAllowOther
|
||||
-> (pure SheetAuthorshipStatementModeEnabled, pure sfAuthorshipStatementExam', )
|
||||
<$> fmap sequenceA (traverse forcedContentField $ authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
| otherwise -> do
|
||||
examOpts <-
|
||||
let examFieldQuery = E.from $ \exam -> do
|
||||
E.where_ $ exam E.^. ExamCourse E.==. E.val cId
|
||||
when (is _SchoolAuthorshipStatementModeRequired schoolSheetAuthorshipStatementMode) $
|
||||
E.where_ . E.isJust $ exam E.^. ExamAuthorshipStatement
|
||||
return exam
|
||||
in liftHandler $ optionsCryptoIdE examFieldQuery examName
|
||||
|
||||
let modeOpts = case schoolSheetAuthorshipStatementMode of
|
||||
SchoolAuthorshipStatementModeNone -> Set.singleton SheetAuthorshipStatementModeDisabled
|
||||
SchoolAuthorshipStatementModeOptional -> Set.fromList universeF
|
||||
SchoolAuthorshipStatementModeRequired -> Set.fromList universeF
|
||||
& Set.delete SheetAuthorshipStatementModeDisabled
|
||||
& bool id (Set.delete SheetAuthorshipStatementModeExam) (hasn't (_olOptions . folded) examOpts)
|
||||
modeOpts' = explainOptionList (optionsPathPiece . map (id &&& id) $ Set.toList modeOpts) $ \case
|
||||
SheetAuthorshipStatementModeDisabled -> return $(i18nWidgetFile "sheet-authorship-statement-mode-tip/disabled")
|
||||
SheetAuthorshipStatementModeExam -> return $(i18nWidgetFile "sheet-authorship-statement-mode-tip/exam")
|
||||
SheetAuthorshipStatementModeEnabled -> return $(i18nWidgetFile "sheet-authorship-statement-mode-tip/enabled")
|
||||
examField' = selectField' (Just $ SomeMessage MsgSheetAuthorshipStatementExamNone) . return $ entityKey <$> examOpts
|
||||
examField'' :: AForm Handler (Maybe ExamId)
|
||||
examField''
|
||||
| isn't _SchoolAuthorshipStatementModeRequired schoolSheetAuthorshipStatementMode
|
||||
= aopt examField' (fslI MsgSheetAuthorshipStatementExam) (sfAuthorshipStatementExam <$> template)
|
||||
| otherwise
|
||||
= Just <$> apreq examField' (fslI MsgSheetAuthorshipStatementExam) (sfAuthorshipStatementExam =<< template)
|
||||
modeForms = flip Map.fromSet modeOpts $ \case
|
||||
SheetAuthorshipStatementModeDisabled -> pure
|
||||
( SheetAuthorshipStatementModeDisabled
|
||||
, sfAuthorshipStatementExam'
|
||||
, sfAuthorshipStatement'
|
||||
)
|
||||
SheetAuthorshipStatementModeExam -> (SheetAuthorshipStatementModeExam,, )
|
||||
<$> examField''
|
||||
<*> pure sfAuthorshipStatement'
|
||||
SheetAuthorshipStatementModeEnabled -> (SheetAuthorshipStatementModeEnabled, sfAuthorshipStatementExam', )
|
||||
<$> fmap Just reqContentField
|
||||
|
||||
massage res = (view _1 <$> res, view _2 <$> res, view _3 <$> res)
|
||||
|
||||
massage <$> explainedMultiActionW modeForms modeOpts' (fslI MsgSheetAuthorshipStatementRequired & setTooltip (bool MsgSheetAuthorshipStatementRequiredTip MsgSheetAuthorshipStatementRequiredForcedTip $ is _SchoolAuthorshipStatementModeRequired schoolSheetAuthorshipStatementMode)) (sfAuthorshipStatementMode <$> template)
|
||||
| otherwise -> return
|
||||
( pure SheetAuthorshipStatementModeDisabled
|
||||
, pure sfAuthorshipStatementExam'
|
||||
, pure sfAuthorshipStatement'
|
||||
)
|
||||
|
||||
return $ SheetForm
|
||||
<$> sfNameRes
|
||||
<*> sfDescriptionRes
|
||||
<*> sfRequireExamRegistrationRes
|
||||
<*> sfSheetFRes <*> sfHintFRes <*> sfSolutionFRes <*> sfMarkingFRes
|
||||
<*> sfPersonalFRes
|
||||
<*> sfVisibleFromRes
|
||||
<*> sfActiveFromRes
|
||||
<*> sfActiveToRes
|
||||
<*> sfHintFromRes
|
||||
<*> sfSolutionFromRes
|
||||
<*> sfSubmissionModeRes
|
||||
<*> sfGroupingRes
|
||||
<*> sfTypeRes
|
||||
<*> sfAutoDistributeRes
|
||||
<*> sfMarkingTextRes
|
||||
<*> sfAnonymousCorrectionRes
|
||||
<*> sfCorrectorsRes
|
||||
<*> sfAuthorshipStatementModeRes
|
||||
<*> sfAuthorshipStatementExamRes
|
||||
<*> sfAuthorshipStatementRes
|
||||
|
||||
-- <*> let
|
||||
-- reqContentField :: (FieldSettings UniWorX -> FieldSettings UniWorX) -> AForm Handler I18nStoredMarkup
|
||||
-- reqContentField ttip = fmapAForm (formResultUnOpt mr' MsgSheetAuthorshipStatementContent)
|
||||
-- $ i18nFieldA htmlField True (\_ -> Nothing) ("authorship-statement" :: Text)
|
||||
-- (fslI MsgSheetAuthorshipStatementContent & ttip)
|
||||
-- True
|
||||
-- ( fmap Just $ (sfAuthorshipStatement =<< template)
|
||||
-- <|> (authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement)
|
||||
-- )
|
||||
-- forcedContentField = aforced forcedAuthorshipStatementField
|
||||
-- (fslI MsgSheetAuthorshipStatementContent & setTooltip MsgSheetAuthorshipStatementContentForcedTip)
|
||||
-- contentField ttipReq
|
||||
-- | not schoolSheetAuthorshipStatementAllowOther
|
||||
-- = traverse forcedContentField $ authorshipStatementDefinitionContent . entityVal <$> mSchoolAuthorshipStatement
|
||||
-- | otherwise
|
||||
-- = Just <$> reqContentField ttipReq
|
||||
-- in case schoolSheetAuthorshipStatementMode of
|
||||
-- SchoolAuthorshipStatementModeNone -> pure Nothing -- suppress display of whole section incl. header
|
||||
-- otherMode -> aformSection MsgSheetAuthorshipStatementSection
|
||||
-- *> aformMessage authorshipStatementExamRelatedTipMsg
|
||||
-- *> case otherMode of
|
||||
-- SchoolAuthorshipStatementModeOptional -> optionalActionA (fmapAForm (formResultUnOpt mr' MsgSheetAuthorshipStatementContent) $ contentField id)
|
||||
-- (fslI MsgSheetAuthorshipStatementRequired & setTooltip MsgSheetAuthorshipStatementRequiredTip)
|
||||
-- (is _Just . sfAuthorshipStatement <$> template)
|
||||
-- SchoolAuthorshipStatementModeRequired -> contentField $ setTooltip MsgSheetAuthorshipStatementRequiredForcedTip
|
||||
-- _none -> pure Nothing
|
||||
where
|
||||
makeSheetPersonalisedFilesForm :: Maybe SheetPersonalisedFilesForm -> MForm Handler (AForm Handler SheetPersonalisedFilesForm)
|
||||
makeSheetPersonalisedFilesForm template' = do
|
||||
|
||||
@ -25,8 +25,9 @@ postSheetNewR tid ssh csh = do
|
||||
let searchShn sheet = case parShn of
|
||||
(FormSuccess (Just shn)) -> E.where_ $ sheet E.^. SheetName E.==. E.val shn
|
||||
_other -> return ()
|
||||
(lastSheets, loads) <- runDB $ do
|
||||
lSheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
||||
now <- liftIO getCurrentTime
|
||||
template <- runDB $ do
|
||||
lastSheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
||||
E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse
|
||||
E.where_ $ course E.^. CourseTerm E.==. E.val tid
|
||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||
@ -38,41 +39,37 @@ postSheetNewR tid ssh csh = do
|
||||
firstEdit = E.subSelectMaybe . E.from $ \sheetEdit -> do
|
||||
E.where_ $ sheetEdit E.^. SheetEditSheet E.==. sheet E.^. SheetId
|
||||
return . E.min_ $ sheetEdit E.^. SheetEditTime
|
||||
-- mAuthorshipStatement = E.subSelect . E.from $ \authorshipStatementDefinition -> do
|
||||
-- E.where_ $ E.just (authorshipStatementDefinition E.^. AuthorshipStatementDefinitionId) E.==. sheet E.^. SheetAuthorshipStatement
|
||||
-- return $ authorshipStatementDefinition E.^. AuthorshipStatementDefinitionContent
|
||||
return (sheet, firstEdit)
|
||||
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
|
||||
loads <- defaultLoads cid
|
||||
return (lSheets, loads)
|
||||
now <- liftIO getCurrentTime
|
||||
let template = case lastSheets of
|
||||
((Entity {entityVal=Sheet{..}}, E.Value fEdit):_) ->
|
||||
let addTime = addWeeks $ max 1 $ weeksToAdd (fromMaybe (UTCTime systemEpochDay 0) $ sheetActiveTo <|> fEdit) now
|
||||
in Just $ SheetForm
|
||||
{ sfName = stepTextCounterCI sheetName
|
||||
, sfDescription = sheetDescription
|
||||
, sfType = review _SqlKey <$> sheetType
|
||||
, sfGrouping = sheetGrouping
|
||||
, sfVisibleFrom = addTime <$> sheetVisibleFrom
|
||||
, sfActiveFrom = addTime <$> sheetActiveFrom
|
||||
, sfActiveTo = addTime <$> sheetActiveTo
|
||||
, sfSubmissionMode = sheetSubmissionMode
|
||||
, sfSheetF = Nothing
|
||||
, sfHintFrom = addTime <$> sheetHintFrom
|
||||
, sfHintF = Nothing
|
||||
, sfSolutionFrom = addTime <$> sheetSolutionFrom
|
||||
, sfSolutionF = Nothing
|
||||
, sfMarkingF = Nothing
|
||||
, sfMarkingText = sheetMarkingText
|
||||
, sfAutoDistribute = sheetAutoDistribute
|
||||
, sfCorrectors = loads
|
||||
, sfAnonymousCorrection = sheetAnonymousCorrection
|
||||
, sfRequireExamRegistration = Nothing
|
||||
, sfPersonalF = Nothing
|
||||
, sfAuthorshipStatement = Nothing -- TODO: implement sheet-specific statements
|
||||
}
|
||||
_other -> Nothing
|
||||
for (lastSheets ^? _head) $ \(Entity _ Sheet{..}, E.Value fEdit) -> do
|
||||
let addTime = addWeeks $ max 1 $ weeksToAdd (fromMaybe (UTCTime systemEpochDay 0) $ sheetActiveTo <|> fEdit) now
|
||||
mStmt <- traverse getJust sheetAuthorshipStatement
|
||||
return SheetForm
|
||||
{ sfName = stepTextCounterCI sheetName
|
||||
, sfDescription = sheetDescription
|
||||
, sfType = review _SqlKey <$> sheetType
|
||||
, sfGrouping = sheetGrouping
|
||||
, sfVisibleFrom = addTime <$> sheetVisibleFrom
|
||||
, sfActiveFrom = addTime <$> sheetActiveFrom
|
||||
, sfActiveTo = addTime <$> sheetActiveTo
|
||||
, sfSubmissionMode = sheetSubmissionMode
|
||||
, sfSheetF = Nothing
|
||||
, sfHintFrom = addTime <$> sheetHintFrom
|
||||
, sfHintF = Nothing
|
||||
, sfSolutionFrom = addTime <$> sheetSolutionFrom
|
||||
, sfSolutionF = Nothing
|
||||
, sfMarkingF = Nothing
|
||||
, sfMarkingText = sheetMarkingText
|
||||
, sfAutoDistribute = sheetAutoDistribute
|
||||
, sfCorrectors = loads
|
||||
, sfAnonymousCorrection = sheetAnonymousCorrection
|
||||
, sfRequireExamRegistration = Nothing
|
||||
, sfPersonalF = Nothing
|
||||
, sfAuthorshipStatementMode = sheetAuthorshipStatementMode
|
||||
, sfAuthorshipStatementExam = sheetAuthorshipStatementExam
|
||||
, sfAuthorshipStatement = authorshipStatementDefinitionContent <$> mStmt
|
||||
}
|
||||
let action = -- More specific error message for new sheet could go here, if insertUnique returns Nothing
|
||||
insertUnique
|
||||
handleSheetEdit tid ssh csh Nothing template action
|
||||
|
||||
@ -32,15 +32,40 @@ import Handler.Submission.SubmissionUserInvite
|
||||
import qualified Data.Conduit.Combinators as C
|
||||
|
||||
|
||||
makeSubmissionForm :: CourseId -> SheetId -> Maybe SubmissionId -> UploadMode -> SheetGroup -> Maybe FileUploads -> Bool -> Set (Either UserEmail UserId) -> Form (Maybe FileUploads, Set (Either UserEmail UserId))
|
||||
makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUsers = identifyForm FIDsubmission . renderAForm FormStandard $ (,)
|
||||
<$> uploadForm
|
||||
<*> wFormToAForm submittorsForm'
|
||||
makeSubmissionForm :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadThrow m)
|
||||
=> CourseId -> Entity Sheet -> Maybe SubmissionId -> UploadMode -> SheetGroup -> Maybe FileUploads -> Bool -> Set (Either UserEmail UserId)
|
||||
-> (Markup -> MForm (ReaderT SqlBackend m) (FormResult (Maybe FileUploads, Set (Either UserEmail UserId), Maybe AuthorshipStatementDefinitionId), Widget))
|
||||
makeSubmissionForm cid sheetEnt@(Entity shid _) msmid uploadMode grouping mPrev isLecturer prefillUsers = identifyForm FIDsubmission . renderWForm FormStandard $ do
|
||||
uploadRes <- aFormToWForm uploadForm
|
||||
submittorsRes <- submittorsForm'
|
||||
lecturerIsSubmittor <- case submittorsRes of
|
||||
FormSuccess subs -> maybe False ((`Set.member` subs) . Right) <$> maybeAuthId
|
||||
_other -> return False
|
||||
authorshipStatementRes <- authorshipStatementForm lecturerIsSubmittor
|
||||
return $ (,,) <$> uploadRes <*> submittorsRes <*> authorshipStatementRes
|
||||
where
|
||||
uploadForm
|
||||
| is _NoUpload uploadMode = pure Nothing
|
||||
| is _Nothing msmid = uploadForm'
|
||||
| otherwise = join <$> optionalActionNegatedA uploadForm' (fslI MsgSubmissionFilesUnchanged & setTooltip MsgSubmissionFilesUnchangedTip) (Just False)
|
||||
-- TODO(AuthorshipStatements): for lecturer: optionally only invite (co-)submittors instead of adding directly; so they will be forced to make authorship statements
|
||||
-- also take care that accepting the invite (optionally) remains possible even after the submission deadline (for creating submissions for course users as a lecturer)
|
||||
|
||||
authorshipStatementForm :: Bool -> WForm (ReaderT SqlBackend m) (FormResult (Maybe AuthorshipStatementDefinitionId))
|
||||
authorshipStatementForm lecturerIsSubmittor = maybeT (return $ FormSuccess Nothing) $ do
|
||||
asd <- MaybeT . lift . lift $ getSheetAuthorshipStatement sheetEnt
|
||||
let authorshipStatementForm' = apopt (acceptAuthorshipStatementField asd) (fslI MsgSubmissionAuthorshipStatement & setTooltip MsgSubmissionAuthorshipStatementTip) Nothing
|
||||
authorshipStatementRes <- lift . hoist (hoist liftHandler) $ if
|
||||
| isLecturer
|
||||
-> optionalActionW authorshipStatementForm' (fslI MsgSubmissionLecturerAuthorshipStatement & setTooltip MsgSubmissionLecturerAuthorshipStatementTip) (Just False)
|
||||
| otherwise
|
||||
-> fmap Just <$> aFormToWForm authorshipStatementForm'
|
||||
if
|
||||
| FormSuccess Nothing <- authorshipStatementRes
|
||||
, lecturerIsSubmittor -> formFailure [MsgSubmissionLecturerAuthorshipStatementRequiredBecauseSubmittor]
|
||||
| otherwise -> return authorshipStatementRes
|
||||
|
||||
uploadForm :: AForm (ReaderT SqlBackend m) (Maybe FileUploads)
|
||||
uploadForm = hoistAForm liftHandler $ if
|
||||
| is _NoUpload uploadMode -> pure Nothing
|
||||
| is _Nothing msmid -> uploadForm'
|
||||
| otherwise -> join <$> optionalActionNegatedA uploadForm' (fslI MsgSubmissionFilesUnchanged & setTooltip MsgSubmissionFilesUnchangedTip) (Just False)
|
||||
|
||||
uploadForm' = fileUploadForm (not isLecturer) (fslI . bool MsgSubmissionFile MsgSubmissionArchive) uploadMode mPrev
|
||||
|
||||
@ -98,9 +123,11 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
E.orderBy [E.asc $ user E.^. UserEmail]
|
||||
return user
|
||||
|
||||
addField :: (MonadHandler m, HandlerSite m ~ UniWorX) => UserId -> Field m (Set (Either UserEmail UserId))
|
||||
addField :: forall m'. (MonadHandler m', HandlerSite m' ~ UniWorX) => UserId -> Field m' (Set (Either UserEmail UserId))
|
||||
addField uid = multiUserInvitationField . MUILookupSuggested (SomeMessage MsgMultiUserFieldExplanationPrevCoSubmittors) $ previousCoSubmittors uid
|
||||
addFieldLecturer, addFieldAuthorshipStatements :: forall m'. (MonadHandler m', HandlerSite m' ~ UniWorX) => Field m' (Set (Either UserEmail UserId))
|
||||
addFieldLecturer = multiUserInvitationField $ MUILookupSuggested (SomeMessage MsgMultiUserFieldExplanationCourseParticipants) courseUsers
|
||||
addFieldAuthorshipStatements = multiUserInvitationField MUIAlwaysInvite
|
||||
|
||||
addFieldSettings :: (forall msg. RenderMessage UniWorX msg => msg -> Text) -> FieldSettings UniWorX
|
||||
addFieldSettings mr = fslpI MsgSubmissionMembers $ mr MsgLdapIdentificationOrEmail
|
||||
@ -119,6 +146,7 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
miButtonAction' :: forall p. PathPiece p => Maybe (Route UniWorX) -> p -> Maybe (SomeRoute UniWorX)
|
||||
miButtonAction' mCurrent frag = mCurrent <&> \current -> SomeRoute (current :#: frag)
|
||||
|
||||
submittorsForm' :: WForm (ReaderT SqlBackend m) (FormResult (Set (Either UserEmail UserId)))
|
||||
submittorsForm' = maybeT submittorsForm $ do
|
||||
restr <- MaybeT (liftHandler $ maybeCurrentBearerRestrictions @Value) >>= hoistMaybe . preview (_Object . ix "submittors" . _Array)
|
||||
let _Submittor = prism (either toJSON toJSON) $ \x -> first (const x) $ JSON.parseEither (\x' -> fmap Right (parseJSON x') <|> fmap Left (parseJSON x')) x
|
||||
@ -126,10 +154,11 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
fmap Set.fromList <$> forMOf (traverse . traverse . _Right) submittors decrypt
|
||||
|
||||
|
||||
submittorsForm :: WForm (ReaderT SqlBackend m) (FormResult (Set (Either UserEmail UserId)))
|
||||
submittorsForm
|
||||
| isLecturer = do -- Form is being used by lecturer; allow Everything™
|
||||
let
|
||||
miAdd :: (Text -> Text) -> FieldView UniWorX -> Form ([Either UserEmail UserId] -> FormResult [Either UserEmail UserId])
|
||||
miAdd :: (Text -> Text) -> FieldView UniWorX -> (Markup -> MForm (ReaderT SqlBackend m) (FormResult ([Either UserEmail UserId] -> FormResult [Either UserEmail UserId]), Widget))
|
||||
miAdd nudge btn csrf = do
|
||||
MsgRenderer mr <- getMsgRenderer
|
||||
(addRes, addView) <- mpreq addFieldLecturer (addFieldSettings mr & addName (nudge "emails")) Nothing
|
||||
@ -150,6 +179,12 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
| otherwise = do
|
||||
uid <- liftHandler requireAuthId
|
||||
mRoute <- getCurrentRoute
|
||||
doAuthorshipStatements <- lift . lift $ is _Just <$> getSheetAuthorshipStatement sheetEnt
|
||||
|
||||
prefillUsers' <- lift . lift . fmap catMaybes . for (Set.toList prefillUsers) $ \case
|
||||
Right uid' | doAuthorshipStatements
|
||||
-> fmap (Left . userEmail) <$> get uid'
|
||||
other -> return $ pure other
|
||||
|
||||
let
|
||||
miAdd :: ListPosition
|
||||
@ -157,10 +192,14 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
-> ListLength
|
||||
-> (Text -> Text)
|
||||
-> FieldView UniWorX
|
||||
-> Maybe (Form (Map ListPosition (Either UserEmail UserId) -> FormResult (Map ListPosition (Either UserEmail UserId))))
|
||||
-> Maybe (Markup -> MForm (ReaderT SqlBackend m) (FormResult (Map ListPosition (Either UserEmail UserId) -> FormResult (Map ListPosition (Either UserEmail UserId))), Widget))
|
||||
miAdd dim pos liveliness nudge btn = guardOn (miAllowAdd dim pos liveliness) $ \csrf -> do
|
||||
MsgRenderer mr <- getMsgRenderer
|
||||
(addRes, addView) <- mpreq (addField uid) (addFieldSettings mr & addName (nudge "emails")) Nothing
|
||||
(addRes, addView) <- if
|
||||
| doAuthorshipStatements
|
||||
-> mpreq addFieldAuthorshipStatements (addFieldSettings mr & addName (nudge "emails") & setTooltip MsgSubmissionCoSubmittorsInviteRequiredBecauseAuthorshipStatements) Nothing
|
||||
| otherwise
|
||||
-> mpreq (addField uid) (addFieldSettings mr & addName (nudge "emails")) Nothing
|
||||
let addRes' = addRes <&> \newData oldData -> if
|
||||
| existing <- newData `Set.intersection` setOf folded oldData
|
||||
, not $ Set.null existing
|
||||
@ -173,12 +212,12 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
-> Either UserEmail UserId
|
||||
-> Maybe ()
|
||||
-> (Text -> Text)
|
||||
-> Form ()
|
||||
-> (Markup -> MForm (ReaderT SqlBackend m) (FormResult (), Widget))
|
||||
miCell _ dat _ _ csrf = return (FormSuccess (), miCell' csrf dat)
|
||||
|
||||
miDelete :: Map ListPosition (Either UserEmail UserId)
|
||||
-> ListPosition
|
||||
-> MaybeT (MForm Handler) (Map ListPosition ListPosition)
|
||||
-> MaybeT (MForm (ReaderT SqlBackend m)) (Map ListPosition ListPosition)
|
||||
miDelete dat delPos = do
|
||||
guard mayEdit
|
||||
guard $ Map.size dat > 1
|
||||
@ -215,7 +254,7 @@ makeSubmissionForm cid shid msmid uploadMode grouping mPrev isLecturer prefillUs
|
||||
where resultUsers = setOf (folded . _1) valMap
|
||||
-- when (maxSize > Just 1) $
|
||||
-- wformMessage =<< messageI Info MsgCosubmittorTip
|
||||
fmap postProcess <$> massInputW MassInput{..} submittorSettings' True (Just . Map.fromList . zip [0..] . map (, ()) $ Set.toList prefillUsers)
|
||||
fmap postProcess <$> massInputW MassInput{..} submittorSettings' True (Just . Map.fromList . zip [0..] . map (, ()) $ prefillUsers')
|
||||
|
||||
submissionHelper :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Maybe CryptoFileNameSubmission -> Handler Html
|
||||
submissionHelper tid ssh csh shn mcid = do
|
||||
@ -329,10 +368,10 @@ submissionHelper tid ssh csh shn mcid = do
|
||||
|
||||
-- @submissionModeUser == Nothing@ below iff we are currently serving a user with elevated rights (lecturer, admin, ...)
|
||||
-- Therefore we do not restrict upload behaviour in any way in that case
|
||||
((res,formWidget'), formEnctype) <- do
|
||||
(Entity shid Sheet{..}, buddies, _, _, isLecturer, isOwner, _, _) <- runDB getSheetInfo
|
||||
((res,formWidget'), formEnctype) <- runDB $ do
|
||||
(sheet@(Entity _ Sheet{..}), buddies, _, _, isLecturer, isOwner, _, _) <- getSheetInfo
|
||||
let mPrevUploads = msmid <&> \smid -> runDBSource $ selectSource [SubmissionFileSubmission ==. smid, SubmissionFileIsUpdate ==. False] [Asc SubmissionFileTitle] .| C.map (view $ _FileReference . _1)
|
||||
runFormPost . makeSubmissionForm sheetCourse shid msmid (fromMaybe (UploadAny True Nothing True) . submissionModeUser $ sheetSubmissionMode) sheetGrouping mPrevUploads isLecturer $ bool id (maybe id (Set.insert . Right) muid) isOwner buddies
|
||||
runFormPost . makeSubmissionForm sheetCourse sheet msmid (fromMaybe (UploadAny True Nothing True) . submissionModeUser $ sheetSubmissionMode) sheetGrouping mPrevUploads isLecturer $ bool id (maybe id (Set.insert . Right) muid) isOwner buddies
|
||||
let formWidget = wrapForm' BtnHandIn formWidget' def
|
||||
{ formAction = Just $ SomeRoute actionUrl
|
||||
, formEncoding = formEnctype
|
||||
@ -369,7 +408,7 @@ submissionHelper tid ssh csh shn mcid = do
|
||||
res' <- case res of
|
||||
FormMissing -> return FormMissing
|
||||
(FormFailure failmsgs) -> return $ FormFailure failmsgs
|
||||
(FormSuccess res'@(_, groupMembers))
|
||||
(FormSuccess res'@(_, groupMembers, _))
|
||||
| groupMembers == subUsersOld -> return $ FormSuccess res'
|
||||
| isLecturer -> return $ FormSuccess res'
|
||||
| Arbitrary{..} <- sheetGrouping -> do -- Validate AdHoc Group Members
|
||||
@ -412,7 +451,9 @@ submissionHelper tid ssh csh shn mcid = do
|
||||
| otherwise -> return $ FormSuccess res'
|
||||
|
||||
|
||||
formResultMaybe res' $ \(mFiles, adhocMembers) -> do
|
||||
formResultMaybe res' $ \(mFiles, adhocMembers, mASDId) -> do
|
||||
now <- liftIO getCurrentTime
|
||||
|
||||
smid <- case (mFiles, msmid) of
|
||||
(Nothing, Just smid) -- no new files, existing submission partners updated
|
||||
-> return smid
|
||||
@ -430,7 +471,6 @@ submissionHelper tid ssh csh shn mcid = do
|
||||
}
|
||||
audit $ TransactionSubmissionEdit sid shid
|
||||
|
||||
now <- liftIO getCurrentTime
|
||||
insert_ $ SubmissionEdit muid now sid
|
||||
|
||||
return sid
|
||||
@ -485,6 +525,10 @@ submissionHelper tid ssh csh shn mcid = do
|
||||
unless (Just subUid == muid) $
|
||||
queueDBJob . JobQueueNotification $ NotificationSubmissionUserDeleted subUid shid smid
|
||||
|
||||
forM_ mASDId $ \asdId -> do
|
||||
uid <- maybe notAuthenticated return muid
|
||||
insert_ $ AuthorshipStatementSubmission asdId smid uid now
|
||||
|
||||
addMessageI Success $ if | Nothing <- msmid -> MsgSubmissionCreated
|
||||
| otherwise -> MsgSubmissionUpdated
|
||||
Just <$> encrypt smid
|
||||
|
||||
@ -7,8 +7,10 @@ module Handler.Submission.SubmissionUserInvite
|
||||
) where
|
||||
|
||||
import Import
|
||||
import Utils.Form
|
||||
|
||||
import Handler.Utils.Invitations
|
||||
import Handler.Utils.AuthorshipStatement
|
||||
|
||||
import Data.Aeson hiding (Result(..))
|
||||
|
||||
@ -79,8 +81,18 @@ submissionUserInvitationConfig = InvitationConfig{..}
|
||||
itStartsAt = Nothing
|
||||
return InvitationTokenConfig{..}
|
||||
invitationRestriction _ _ = return Authorized
|
||||
invitationForm _ _ _ = pure (JunctionSubmissionUser, ())
|
||||
invitationInsertHook _ _ _ _ _ = id
|
||||
invitationForm (Entity _ Submission{..}) _ _ = wFormToAForm $ do
|
||||
-- TODO(AuthorshipStatements): allow invitee to download submission files/see co-submittors iff authorship-statement is required
|
||||
authorshipStatementRes <- maybeT (return $ FormSuccess Nothing) . fmap (fmap Just) $ do
|
||||
sheetEnt <- lift . lift . lift $ getJustEntity submissionSheet
|
||||
asd <- MaybeT . lift . lift $ getSheetAuthorshipStatement sheetEnt
|
||||
lift $ wpopt (acceptAuthorshipStatementField asd) (fslI MsgSubmissionAuthorshipStatement & setTooltip MsgSubmissionAuthorshipStatementTip) Nothing
|
||||
return $ (JunctionSubmissionUser, ) <$> authorshipStatementRes
|
||||
invitationInsertHook _ (Entity smid _) _ SubmissionUser{..} masdId act = do
|
||||
for_ masdId $ \asdId -> do
|
||||
now <- liftIO getCurrentTime
|
||||
insert_ $ AuthorshipStatementSubmission asdId smid submissionUserUser now
|
||||
act
|
||||
invitationSuccessMsg (Entity _ Submission{..}) _ = do
|
||||
Sheet{..} <- getJust submissionSheet
|
||||
return . SomeMessage $ MsgSubmissionUserInvitationAccepted sheetName
|
||||
|
||||
@ -25,6 +25,7 @@ import Handler.Utils.Occurrences as Handler.Utils
|
||||
import Handler.Utils.Memcached as Handler.Utils hiding (manageMemcachedLocalInvalidations)
|
||||
import Handler.Utils.Files as Handler.Utils
|
||||
import Handler.Utils.Download as Handler.Utils
|
||||
import Handler.Utils.AuthorshipStatement as Handler.Utils
|
||||
|
||||
import Handler.Utils.Term as Handler.Utils
|
||||
|
||||
|
||||
116
src/Handler/Utils/AuthorshipStatement.hs
Normal file
116
src/Handler/Utils/AuthorshipStatement.hs
Normal file
@ -0,0 +1,116 @@
|
||||
module Handler.Utils.AuthorshipStatement
|
||||
( insertAuthorshipStatement
|
||||
, forcedAuthorshipStatementField
|
||||
, authorshipStatementWidget
|
||||
, getSheetAuthorshipStatement
|
||||
, acceptAuthorshipStatementField
|
||||
) where
|
||||
|
||||
import Import
|
||||
import Utils.Form
|
||||
|
||||
import qualified Data.Map.Strict as Map
|
||||
|
||||
import Handler.Utils.Form (i18nLangMap, I18nLang(..))
|
||||
|
||||
import qualified Database.Esqueleto.Legacy as E
|
||||
import qualified Database.Esqueleto.Utils as E
|
||||
|
||||
|
||||
insertAuthorshipStatement :: MonadIO m
|
||||
=> I18nStoredMarkup -> SqlWriteT m AuthorshipStatementDefinitionId
|
||||
insertAuthorshipStatement authorshipStatementDefinitionContent = withCompatibleBackend @SqlBackend $ do
|
||||
let authorshipStatementDefinitionHash = toAuthorshipStatementReference authorshipStatementDefinitionContent
|
||||
unlessM (exists [AuthorshipStatementDefinitionHash ==. authorshipStatementDefinitionHash]) $
|
||||
insert_ AuthorshipStatementDefinition{..}
|
||||
return $ AuthorshipStatementDefinitionKey authorshipStatementDefinitionHash
|
||||
|
||||
forcedAuthorshipStatementField :: (MonadHandler handler, HandlerSite handler ~ UniWorX)
|
||||
=> Field handler I18nStoredMarkup
|
||||
forcedAuthorshipStatementField = Field{..}
|
||||
where
|
||||
fieldParse _ _ = pure . Left $ SomeMessage ("Result of forcedAuthorshipStatementField inspected" :: Text)
|
||||
fieldEnctype = UrlEncoded
|
||||
fieldView theId _name attrs (preview _Right -> mVal) _isReq
|
||||
= [whamlet|
|
||||
$newline never
|
||||
<div ##{theId} *{attrs}>
|
||||
^{maybe mempty authorshipStatementWidget mVal}
|
||||
|]
|
||||
|
||||
authorshipStatementWidget :: I18nStoredMarkup -> Widget
|
||||
authorshipStatementWidget stmt
|
||||
= [whamlet|
|
||||
$newline never
|
||||
<dl .authorship-statement>
|
||||
$forall (I18nLang l, t) <- Map.toList (review i18nLangMap stmt)
|
||||
<dt>
|
||||
_{MsgLanguageEndonym l}
|
||||
<dd>
|
||||
#{markupOutput t}
|
||||
|]
|
||||
|
||||
acceptAuthorshipStatementField :: forall m.
|
||||
(MonadHandler m, HandlerSite m ~ UniWorX)
|
||||
=> Entity AuthorshipStatementDefinition
|
||||
-> Field m AuthorshipStatementDefinitionId
|
||||
acceptAuthorshipStatementField (Entity asdId AuthorshipStatementDefinition{..})
|
||||
= checkBoxField
|
||||
& _fieldView %~ adjFieldView
|
||||
& checkMap (bool (Left MsgAuthorshipStatementStatementIsRequired) (Right asdId)) (== asdId)
|
||||
where
|
||||
adjFieldView :: FieldViewFunc m Bool -> FieldViewFunc m Bool
|
||||
adjFieldView checkboxView theId theName attrs val isReq = do
|
||||
let checkboxWdgt = checkboxView checkboxId theName [] val isReq
|
||||
checkboxId = theId <> "__checkbox"
|
||||
$(widgetFile "widgets/authorship-statement-accept")
|
||||
|
||||
|
||||
getSheetAuthorshipStatement :: MonadIO m
|
||||
=> Entity Sheet
|
||||
-> SqlReadT m (Maybe (Entity AuthorshipStatementDefinition))
|
||||
getSheetAuthorshipStatement (Entity _ Sheet{..}) = withCompatibleBackend @SqlBackend $ traverse getJustEntity <=< runMaybeT $ do
|
||||
Entity _ School{..} <- MaybeT . E.selectMaybe . E.from $ \(school `E.InnerJoin` course) -> do
|
||||
E.on $ school E.^. SchoolId E.==. course E.^. CourseSchool
|
||||
E.where_ $ course E.^. CourseId E.==. E.val sheetCourse
|
||||
return school
|
||||
|
||||
let examId = sheetAuthorshipStatementExam
|
||||
<|> sheetType ^? _examPart . re _SqlKey
|
||||
<|> sheetRequireExamRegistration
|
||||
exam <- lift $ traverse getJust examId
|
||||
|
||||
let
|
||||
examAuthorshipStatement' = exam >>= examAuthorshipStatement
|
||||
sheetAuthorshipStatement' = guardOnM (is _SheetAuthorshipStatementModeEnabled sheetAuthorshipStatementMode) sheetAuthorshipStatement
|
||||
sheetDoAuthorshipStatements
|
||||
= is _SheetAuthorshipStatementModeEnabled sheetAuthorshipStatementMode
|
||||
|| (is _SheetAuthorshipStatementModeExam sheetAuthorshipStatementMode && is _Just examAuthorshipStatement')
|
||||
|
||||
if
|
||||
| is _Just exam
|
||||
, is _SchoolAuthorshipStatementModeNone schoolSheetExamAuthorshipStatementMode
|
||||
-> mzero
|
||||
| is _Just exam
|
||||
, not schoolSheetExamAuthorshipStatementAllowOther
|
||||
-> guardOnM (is _SchoolAuthorshipStatementModeRequired schoolSheetExamAuthorshipStatementMode || sheetDoAuthorshipStatements) $ hoistMaybe schoolSheetExamAuthorshipStatementDefinition
|
||||
| is _Just exam
|
||||
, is _SchoolAuthorshipStatementModeRequired schoolSheetExamAuthorshipStatementMode
|
||||
-> hoistMaybe $ sheetAuthorshipStatement'
|
||||
<|> guardOnM (is _SheetAuthorshipStatementModeExam sheetAuthorshipStatementMode) examAuthorshipStatement'
|
||||
<|> schoolSheetExamAuthorshipStatementDefinition
|
||||
|
||||
| is _Nothing exam
|
||||
, is _SchoolAuthorshipStatementModeNone schoolSheetAuthorshipStatementMode
|
||||
-> mzero
|
||||
| is _Nothing exam
|
||||
, not schoolSheetAuthorshipStatementAllowOther
|
||||
-> guardOnM (is _SchoolAuthorshipStatementModeRequired schoolSheetAuthorshipStatementMode || sheetDoAuthorshipStatements) $ hoistMaybe schoolSheetAuthorshipStatementDefinition
|
||||
| is _Nothing exam
|
||||
, is _SchoolAuthorshipStatementModeRequired schoolSheetAuthorshipStatementMode
|
||||
-> hoistMaybe $ sheetAuthorshipStatement' <|> schoolSheetAuthorshipStatementDefinition
|
||||
|
||||
| otherwise
|
||||
-> case exam of
|
||||
Just _ -> hoistMaybe $ sheetAuthorshipStatement' <|> examAuthorshipStatement'
|
||||
Nothing -> hoistMaybe sheetAuthorshipStatement'
|
||||
@ -462,6 +462,15 @@ explainedMultiActionA :: forall action a.
|
||||
-> AForm Handler a
|
||||
explainedMultiActionA acts mActsOpts fSettings defAction = formToAForm $ explainedMultiAction acts mActsOpts fSettings defAction mempty
|
||||
|
||||
explainedMultiActionW :: forall action a.
|
||||
Ord action
|
||||
=> Map action (AForm Handler a)
|
||||
-> Handler ([(Option action, Maybe Widget)], Text -> Maybe action)
|
||||
-> FieldSettings UniWorX
|
||||
-> Maybe action
|
||||
-> WForm Handler (FormResult a)
|
||||
explainedMultiActionW acts mActsOpts fSettings defAction = aFormToWForm $ explainedMultiActionA acts mActsOpts fSettings defAction
|
||||
|
||||
------------
|
||||
-- Fields --
|
||||
------------
|
||||
@ -2407,20 +2416,13 @@ i18nForm :: forall a ident handler.
|
||||
-> ident
|
||||
-> FieldSettings UniWorX
|
||||
-> Bool
|
||||
-> Maybe (I18n a)
|
||||
-> (Markup -> MForm handler (FormResult (I18n a), FieldView UniWorX))
|
||||
-> Maybe (Maybe (I18n a))
|
||||
-> (Markup -> MForm handler (FormResult (Maybe (I18n a)), FieldView UniWorX))
|
||||
i18nForm strForm onlyAppLanguages miButtonAction miIdent fSettings fRequired mPrev' csrf'
|
||||
= fmap (over _1 massageFormResult) . ($ csrf') . massInput MassInput{..} fSettings fRequired $ fmap ((), ) . review i18nLangMap <$> mPrev'
|
||||
= fmap (over _1 massageFormResult) . ($ csrf') . massInput MassInput{..} fSettings fRequired $ fmap ((), ) . review i18nLangMap <$> join mPrev'
|
||||
where
|
||||
massageFormResult :: FormResult (Map I18nLang ((), a)) -> FormResult (I18n a)
|
||||
massageFormResult = \case
|
||||
FormSuccess xs
|
||||
| Just xs' <- preview i18nLangMap $ map (view _2) xs
|
||||
-> FormSuccess xs'
|
||||
| otherwise
|
||||
-> FormMissing
|
||||
FormFailure errs -> FormFailure errs
|
||||
FormMissing -> FormMissing
|
||||
massageFormResult :: FormResult (Map I18nLang ((), a)) -> FormResult (Maybe (I18n a))
|
||||
massageFormResult = fmap $ preview i18nLangMap . map (view _2)
|
||||
|
||||
miAdd :: I18nLang -> Natural -> I18nLangs
|
||||
-> (Text -> Text) -> FieldView UniWorX
|
||||
@ -2494,6 +2496,36 @@ i18nField :: forall a ident handler.
|
||||
-> ident
|
||||
-> FieldSettings UniWorX
|
||||
-> Bool
|
||||
-> Maybe (I18n a)
|
||||
-> (Markup -> MForm handler (FormResult (I18n a), FieldView UniWorX))
|
||||
-> Maybe (Maybe (I18n a))
|
||||
-> (Markup -> MForm handler (FormResult (Maybe (I18n a)), FieldView UniWorX))
|
||||
i18nField strField = i18nForm $ \nudge mPrev csrf -> over _2 ((toWidget csrf <>) . fvWidget) <$> mpreq strField (def & addName (nudge "string")) mPrev
|
||||
|
||||
i18nFieldA :: forall a ident handler.
|
||||
( PathPiece ident
|
||||
, MonadHandler handler, HandlerSite handler ~ UniWorX
|
||||
, MonadThrow handler
|
||||
)
|
||||
=> Field handler a
|
||||
-> Bool -- ^ Allow only languages from `appLanguages`?
|
||||
-> (forall p. PathPiece p => p -> Maybe (SomeRoute UniWorX))
|
||||
-> ident
|
||||
-> FieldSettings UniWorX
|
||||
-> Bool
|
||||
-> Maybe (Maybe (I18n a))
|
||||
-> AForm handler (Maybe (I18n a))
|
||||
i18nFieldA strField onlyAppLanguages miButtonAction miIdent fSettings fRequired mPrev' = formToAForm $ over _2 pure <$> i18nField strField onlyAppLanguages miButtonAction miIdent fSettings fRequired mPrev' mempty
|
||||
|
||||
i18nFieldW :: forall a ident handler.
|
||||
( PathPiece ident
|
||||
, MonadHandler handler, HandlerSite handler ~ UniWorX
|
||||
, MonadThrow handler
|
||||
)
|
||||
=> Field handler a
|
||||
-> Bool -- ^ Allow only languages from `appLanguages`?
|
||||
-> (forall p. PathPiece p => p -> Maybe (SomeRoute UniWorX))
|
||||
-> ident
|
||||
-> FieldSettings UniWorX
|
||||
-> Bool
|
||||
-> Maybe (Maybe (I18n a))
|
||||
-> WForm handler (FormResult (Maybe (I18n a)))
|
||||
i18nFieldW strField onlyAppLanguages miButtonAction miIdent fSettings fRequired mPrev' = aFormToWForm $ i18nFieldA strField onlyAppLanguages miButtonAction miIdent fSettings fRequired mPrev'
|
||||
|
||||
@ -31,7 +31,7 @@ data MarkupFormat
|
||||
| MarkupHtml
|
||||
| MarkupPlaintext
|
||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||
deriving anyclass (Universe, Finite, NFData)
|
||||
deriving anyclass (Universe, Finite, Binary, Hashable, NFData)
|
||||
nullaryPathPiece ''MarkupFormat $ camelToPathPiece' 1
|
||||
pathPieceJSON ''MarkupFormat
|
||||
|
||||
@ -41,7 +41,7 @@ data StoredMarkup = StoredMarkup
|
||||
, markupOutput :: Html
|
||||
}
|
||||
deriving (Read, Show, Generic, Typeable)
|
||||
deriving anyclass (NFData)
|
||||
deriving anyclass (Binary, Hashable, NFData)
|
||||
|
||||
htmlToStoredMarkup :: Html -> StoredMarkup
|
||||
htmlToStoredMarkup html = StoredMarkup
|
||||
|
||||
@ -3,6 +3,16 @@ module Model.Types.School where
|
||||
import Import.NoModel
|
||||
import Model.Types.TH.PathPiece
|
||||
|
||||
import Database.Persist.Sql (PersistFieldSql(..))
|
||||
import Web.HttpApiData (ToHttpApiData, FromHttpApiData)
|
||||
import Data.ByteArray (ByteArrayAccess)
|
||||
|
||||
import qualified Crypto.Hash as Crypto
|
||||
import qualified Data.Binary as Binary
|
||||
|
||||
import Model.Types.Markup
|
||||
|
||||
|
||||
data SchoolFunction
|
||||
= SchoolAdmin
|
||||
| SchoolLecturer
|
||||
@ -25,8 +35,26 @@ data SchoolAuthorshipStatementMode
|
||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||
deriving anyclass (Universe, Finite, NFData)
|
||||
|
||||
finitePathPiece ''SchoolAuthorshipStatementMode [ "no-statement", "optional", "required" ]
|
||||
finitePathPiece ''SchoolAuthorshipStatementMode [ "no-statement", "optional", "required" ] -- avoid @none@ since it does not play nice with yesod-form (`selectField` etc.)
|
||||
pathPieceJSON ''SchoolAuthorshipStatementMode
|
||||
pathPieceJSONKey ''SchoolAuthorshipStatementMode
|
||||
derivePersistFieldPathPiece ''SchoolAuthorshipStatementMode
|
||||
pathPieceBinary ''SchoolAuthorshipStatementMode
|
||||
pathPieceHttpApiData ''SchoolAuthorshipStatementMode
|
||||
|
||||
newtype AuthorshipStatementReference = AuthorshipStatementReference (Digest SHA3_512)
|
||||
deriving (Eq, Ord, Read, Show, Lift, Generic, Typeable)
|
||||
deriving newtype ( PersistField
|
||||
, PathPiece, ToHttpApiData, FromHttpApiData, ToJSON, FromJSON
|
||||
, Hashable, NFData
|
||||
, ByteArrayAccess
|
||||
, Binary
|
||||
)
|
||||
|
||||
instance PersistFieldSql AuthorshipStatementReference where
|
||||
sqlType _ = sqlType $ Proxy @(Digest SHA3_512)
|
||||
|
||||
makeWrapped ''AuthorshipStatementReference
|
||||
|
||||
toAuthorshipStatementReference :: I18nStoredMarkup -> AuthorshipStatementReference
|
||||
toAuthorshipStatementReference = review _Wrapped . Crypto.hashlazy . Binary.encode
|
||||
|
||||
@ -12,6 +12,7 @@ module Model.Types.Sheet
|
||||
import Import.NoModel
|
||||
import Model.Types.Common
|
||||
import Utils.Lens.TH
|
||||
import Model.Types.TH.PathPiece
|
||||
|
||||
import Generics.Deriving.Monoid (memptydefault, mappenddefault)
|
||||
|
||||
@ -406,3 +407,17 @@ instance Csv.ToField (SheetType epid, Maybe Points) where
|
||||
= Csv.toField res
|
||||
toField (_, Just _)
|
||||
= "submitted"
|
||||
|
||||
data SheetAuthorshipStatementMode
|
||||
= SheetAuthorshipStatementModeDisabled
|
||||
| SheetAuthorshipStatementModeExam
|
||||
| SheetAuthorshipStatementModeEnabled
|
||||
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
|
||||
deriving anyclass (Universe, Finite, NFData)
|
||||
|
||||
nullaryPathPiece ''SheetAuthorshipStatementMode $ camelToPathPiece' 4
|
||||
derivePersistFieldPathPiece ''SheetAuthorshipStatementMode
|
||||
pathPieceJSON ''SheetAuthorshipStatementMode
|
||||
pathPieceJSONKey ''SheetAuthorshipStatementMode
|
||||
pathPieceBinary ''SheetAuthorshipStatementMode
|
||||
pathPieceHttpApiData ''SheetAuthorshipStatementMode
|
||||
|
||||
@ -15,6 +15,8 @@ import qualified Data.Aeson as Aeson
|
||||
|
||||
import qualified Data.Csv as Csv
|
||||
|
||||
import Data.Binary (Binary(..))
|
||||
|
||||
|
||||
instance Eq Markup where
|
||||
(==) = (==) `on` Text.renderMarkup
|
||||
@ -45,3 +47,7 @@ instance Csv.FromField Markup where
|
||||
|
||||
instance NFData Markup where
|
||||
rnf = rnf . Text.renderMarkup
|
||||
|
||||
instance Binary Markup where
|
||||
put = put . Text.renderMarkup
|
||||
get = preEscapedText <$> get
|
||||
|
||||
@ -1280,6 +1280,18 @@ aSetTooltip tip = wFormToAForm . wSetTooltip tip . aFormToWForm
|
||||
|
||||
data ValueRequired site = forall msg. RenderMessage site msg => ValueRequired msg
|
||||
|
||||
formResultUnOpt :: forall a site msg.
|
||||
( RenderMessage site msg
|
||||
, RenderMessage site (ValueRequired site)
|
||||
)
|
||||
=> MsgRendererS site -> msg -> FormResult (Maybe a) -> FormResult a
|
||||
formResultUnOpt (MsgRenderer mr) label = \case
|
||||
FormFailure errs -> FormFailure errs
|
||||
FormMissing -> FormMissing
|
||||
FormSuccess Nothing -> FormFailure . pure $ mr (ValueRequired label :: ValueRequired site)
|
||||
FormSuccess (Just x) -> FormSuccess x
|
||||
|
||||
|
||||
mreq :: forall m a.
|
||||
( MonadHandler m
|
||||
, RenderMessage (HandlerSite m) (ValueRequired (HandlerSite m))
|
||||
|
||||
@ -176,6 +176,7 @@ makeLenses_ ''StudyTermNameCandidate
|
||||
makeLenses_ ''StudySubTermParentCandidate
|
||||
makeLenses_ ''StudyTermStandaloneCandidate
|
||||
|
||||
makeLenses_ ''Field
|
||||
makeLenses_ ''FieldView
|
||||
makeLenses_ ''FieldSettings
|
||||
|
||||
@ -281,6 +282,9 @@ makeLenses_ ''JobMode
|
||||
|
||||
-- makeClassy_ ''Load
|
||||
|
||||
makePrisms ''SchoolAuthorshipStatementMode
|
||||
makePrisms ''SheetAuthorshipStatementMode
|
||||
|
||||
--------------------------
|
||||
-- Fields for `UniWorX` --
|
||||
--------------------------
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
$newline never
|
||||
Es kann die Abgabe einer Eigenständigkeitserklärung bei Anlegen einer Übungsblattabgabe gefordert werden.
|
||||
@ -0,0 +1,2 @@
|
||||
$newline never
|
||||
Submittors can be required to make a Statement of Authorship when creating their submission for an exercise sheet.
|
||||
@ -0,0 +1,7 @@
|
||||
$newline never
|
||||
Es werden keine Eigenständigkeitserklärungen gefordert.
|
||||
|
||||
$if is _SchoolAuthorshipStatementModeRequired schoolSheetExamAuthorshipStatementMode
|
||||
<br>
|
||||
|
||||
Wegen Regeln des Instituts, unter dem der Kurs angelegt wurde, wird trotz dieser Einstellung eine Eigenständigkeitserklärung gefordert, wenn das Übungsblatt Prüfungsbezug hat.
|
||||
@ -0,0 +1,7 @@
|
||||
$newline never
|
||||
No Statements of Authorship will be required.
|
||||
|
||||
$if is _SchoolAuthorshipStatementModeRequired schoolSheetExamAuthorshipStatementMode
|
||||
<br>
|
||||
|
||||
Due to rules of the school this course is associated with, Statements of Authorship will be required anyways if this exercise sheet is associated with an exam.
|
||||
@ -0,0 +1,2 @@
|
||||
$newline never
|
||||
Alle Abgebende müssen jeweils eine Eigenständigkeitserklärung abgeben.
|
||||
@ -0,0 +1,2 @@
|
||||
$newline never
|
||||
All submittors are required to make a Statement of Authorship.
|
||||
@ -0,0 +1,14 @@
|
||||
$newline never
|
||||
Falls das Übungsblatt Prüfungsbezug hat, greifen die Einstellungen der jeweiligen Prüfung.
|
||||
|
||||
<br>
|
||||
|
||||
Ein Übungsblatt steht im Bezug zu einer Prüfung, falls eine der folgenden Bedingungen erfüllt ist:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Es wird unter „_{MsgSheetAuthorshipStatementExam}“ manuell eine Prüfung eingestellt
|
||||
<li>
|
||||
Das Übungsblatt wird „_{MsgSheetTypeExamPartPoints}“ gewertet
|
||||
<li>
|
||||
Die Anmeldung zur Prüfung wird vorausgesetzt um für das Übungsblatt abgeben zu dürfen („_{MsgSheetRequireExam}“)
|
||||
@ -0,0 +1,14 @@
|
||||
$newline never
|
||||
If the exercise sheet is associated with an exam, the settings of the exam are applied.
|
||||
|
||||
<br>
|
||||
|
||||
An exercise sheet is associated with an exam if one of the following is true:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
An exam was manually configured under “_{MsgSheetAuthorshipStatementExam}”
|
||||
<li>
|
||||
The exercise sheet is valued “_{MsgSheetTypeExamPartPoints}”
|
||||
<li>
|
||||
Registration for an exam is required to submit for the exercise sheet (“_{MsgSheetRequireExam}”)
|
||||
@ -46,6 +46,8 @@ $if is _Just mcid
|
||||
$nothing
|
||||
<li>#{time}
|
||||
|
||||
$# TODO(AuthorshipStatements): show statements confirmed (iff display is not anonymous (lecturer/submittor/non-anonymous corrector)?)
|
||||
|
||||
$if maySubmit
|
||||
<section>
|
||||
<h2>_{MsgSubmissionReplace}
|
||||
|
||||
11
templates/widgets/authorship-statement-accept.hamlet
Normal file
11
templates/widgets/authorship-statement-accept.hamlet
Normal file
@ -0,0 +1,11 @@
|
||||
$newline never
|
||||
<div *{attrs} .authorship-statement-accept__container>
|
||||
<div .authorship-statement-accept__statement>
|
||||
^{authorshipStatementWidget authorshipStatementDefinitionContent}
|
||||
|
||||
<label for=#{checkboxId} .authorship-statement-accept__accept>
|
||||
<div .authorship-statement-accept__accept-checkbox>
|
||||
^{checkboxWdgt}
|
||||
|
||||
<div .authorship-statement-accept__accept-label>
|
||||
_{MsgAuthorshipStatementAccept}
|
||||
@ -18,6 +18,7 @@ import Data.Time.Calendar.WeekDate
|
||||
import Control.Applicative (ZipList(..))
|
||||
|
||||
import Handler.Utils.DateTime
|
||||
import Handler.Utils.AuthorshipStatement (insertAuthorshipStatement)
|
||||
|
||||
import Control.Monad.Random.Class (weighted)
|
||||
import System.Random.Shuffle (shuffleM)
|
||||
@ -411,7 +412,31 @@ fillDb = do
|
||||
, termLectureEnd
|
||||
}
|
||||
void . insert_ $ TermActive (TermKey term) (toMidnight $ addDays (-60) termStart) (Just . beforeMidnight $ addDays 60 termEnd) Nothing
|
||||
ifiAuthorshipStatement <- insert $ AuthorshipStatementDefinition "<strong>Erklärung über die eigenständige Bearbeitung</strong><p>Hiermit erkläre ich, dass ich die vorliegende Abgabe vollständig selbstständig angefertigt habe, bzw. dass bei einer Gruppen-Abgabe nur die bei der Abgabe benannten Personen mitgewirkt haben. Quellen und Hilfsmittel über den Rahmen der Lehrveranstaltung hinaus sind als solche markiert und angegeben. Direkte Zitate sind als solche kenntlich gemacht. Ich bin mir darüber im Klaren, dass Verstöße durch Plagiate oder Zusammenarbeit mit Dritten zum Ausschluss von der Veranstaltung führen.</p><strong>Statement of Authorship</strong><p>TODO English version</p>"
|
||||
ifiAuthorshipStatement <- insertAuthorshipStatement I18n
|
||||
{ i18nFallback = htmlToStoredMarkup
|
||||
[shamlet|
|
||||
$newline text
|
||||
<strong>
|
||||
Erklärung über die eigenständige Bearbeitung
|
||||
<p>
|
||||
Hiermit erkläre ich, dass ich die vorliegende Abgabe vollständig selbstständig angefertigt habe, bzw. dass bei einer Gruppen-Abgabe nur die bei der Abgabe benannten Personen mitgewirkt haben.
|
||||
Quellen und Hilfsmittel über den Rahmen der Lehrveranstaltung hinaus sind als solche markiert und angegeben.
|
||||
Direkte Zitate sind als solche kenntlich gemacht.
|
||||
Ich bin mir darüber im Klaren, dass Verstöße durch Plagiate oder Zusammenarbeit mit Dritten zum Ausschluss von der Veranstaltung führen.
|
||||
|]
|
||||
, i18nFallbackLang = Just "de-de-formal"
|
||||
, i18nTranslations = Map.singleton "en-eu" $ htmlToStoredMarkup
|
||||
[shamlet|
|
||||
$newline text
|
||||
<strong>
|
||||
Statement of Authorship
|
||||
<p>
|
||||
I hereby declare that the submission is my own unaided work or that in the case of a group submission only the group members configured in Uni2work were involved in the creation of the work.
|
||||
All direct and indirect sources and aids are acknowledged as sources within the work.
|
||||
Direct citations are made apparent as such.
|
||||
I am aware that violations in the form plagiarism or collaboration with third parties will lead to expulsion from the course.
|
||||
|]
|
||||
}
|
||||
ifi <- insert' $ School "Institut für Informatik" "IfI" (Just $ 14 * nominalDay) (Just $ 10 * nominalDay) True (ExamModeDNF predDNFFalse) (ExamCloseOnFinished True) SchoolAuthorshipStatementModeOptional (Just ifiAuthorshipStatement) True SchoolAuthorshipStatementModeRequired (Just ifiAuthorshipStatement) False
|
||||
mi <- insert' $ School "Institut für Mathematik" "MI" Nothing Nothing False (ExamModeDNF predDNFFalse) (ExamCloseOnFinished False) SchoolAuthorshipStatementModeNone Nothing True SchoolAuthorshipStatementModeOptional Nothing True
|
||||
void . insert' $ UserFunction gkleen ifi SchoolAdmin
|
||||
@ -657,6 +682,8 @@ fillDb = do
|
||||
, sheetAnonymousCorrection = True
|
||||
, sheetRequireExamRegistration = Nothing
|
||||
, sheetAllowNonPersonalisedSubmission = True
|
||||
, sheetAuthorshipStatementMode = SheetAuthorshipStatementModeExam
|
||||
, sheetAuthorshipStatementExam = Nothing
|
||||
, sheetAuthorshipStatement = Nothing
|
||||
}
|
||||
insert_ $ SheetEdit gkleen now adhoc
|
||||
@ -677,6 +704,8 @@ fillDb = do
|
||||
, sheetAnonymousCorrection = True
|
||||
, sheetRequireExamRegistration = Nothing
|
||||
, sheetAllowNonPersonalisedSubmission = True
|
||||
, sheetAuthorshipStatementMode = SheetAuthorshipStatementModeExam
|
||||
, sheetAuthorshipStatementExam = Nothing
|
||||
, sheetAuthorshipStatement = Nothing
|
||||
}
|
||||
insert_ $ SheetEdit gkleen now feste
|
||||
@ -697,6 +726,8 @@ fillDb = do
|
||||
, sheetAnonymousCorrection = True
|
||||
, sheetRequireExamRegistration = Nothing
|
||||
, sheetAllowNonPersonalisedSubmission = True
|
||||
, sheetAuthorshipStatementMode = SheetAuthorshipStatementModeExam
|
||||
, sheetAuthorshipStatementExam = Nothing
|
||||
, sheetAuthorshipStatement = Nothing
|
||||
}
|
||||
insert_ $ SheetEdit gkleen now keine
|
||||
@ -943,6 +974,8 @@ fillDb = do
|
||||
, sheetAnonymousCorrection = True
|
||||
, sheetRequireExamRegistration = Nothing
|
||||
, sheetAllowNonPersonalisedSubmission = True
|
||||
, sheetAuthorshipStatementMode = SheetAuthorshipStatementModeExam
|
||||
, sheetAuthorshipStatementExam = Nothing
|
||||
, sheetAuthorshipStatement = Nothing
|
||||
}
|
||||
void . insert $ SheetEdit jost now shId
|
||||
@ -1187,6 +1220,8 @@ fillDb = do
|
||||
, sheetAnonymousCorrection = True
|
||||
, sheetRequireExamRegistration = Nothing
|
||||
, sheetAllowNonPersonalisedSubmission = True
|
||||
, sheetAuthorshipStatementMode = SheetAuthorshipStatementModeExam
|
||||
, sheetAuthorshipStatementExam = Nothing
|
||||
, sheetAuthorshipStatement = Nothing
|
||||
}
|
||||
manyUsers' <- shuffleM $ take 1024 manyUsers
|
||||
|
||||
@ -342,6 +342,9 @@ instance Arbitrary UploadNonce where
|
||||
|
||||
instance Arbitrary SchoolAuthorshipStatementMode where
|
||||
arbitrary = genericArbitrary
|
||||
|
||||
instance Arbitrary SheetAuthorshipStatementMode where
|
||||
arbitrary = genericArbitrary
|
||||
|
||||
|
||||
spec :: Spec
|
||||
@ -452,7 +455,9 @@ spec = do
|
||||
lawsCheckHspec (Proxy @UploadNonce)
|
||||
[ eqLaws, ordLaws, showLaws, showReadLaws, pathPieceLaws, jsonLaws, jsonKeyLaws, persistFieldLaws ]
|
||||
lawsCheckHspec (Proxy @SchoolAuthorshipStatementMode)
|
||||
[ eqLaws, ordLaws, showLaws, showReadLaws, boundedEnumLaws, finiteLaws, pathPieceLaws, jsonLaws, jsonKeyLaws, persistFieldLaws, binaryLaws ]
|
||||
[ eqLaws, ordLaws, showLaws, showReadLaws, boundedEnumLaws, finiteLaws, pathPieceLaws, jsonLaws, jsonKeyLaws, persistFieldLaws, binaryLaws, httpApiDataLaws ]
|
||||
lawsCheckHspec (Proxy @SheetAuthorshipStatementMode)
|
||||
[ eqLaws, ordLaws, showLaws, showReadLaws, boundedEnumLaws, finiteLaws, pathPieceLaws, jsonLaws, jsonKeyLaws, persistFieldLaws, binaryLaws, httpApiDataLaws ]
|
||||
|
||||
describe "TermIdentifier" $ do
|
||||
it "has compatible encoding/decoding to/from Text" . property $
|
||||
|
||||
@ -70,8 +70,9 @@ instance Arbitrary Sheet where
|
||||
<*> arbitrary
|
||||
<*> pure Nothing
|
||||
<*> arbitrary
|
||||
<*> arbitrary
|
||||
<*> pure Nothing
|
||||
<*> pure Nothing
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Tutorial where
|
||||
arbitrary = Tutorial
|
||||
|
||||
Loading…
Reference in New Issue
Block a user