feat(csv import): add explanation text
This commit is contained in:
parent
3c4172cbc2
commit
6d0a4c156b
@ -181,7 +181,7 @@ CourseApplicationRated: Bewertung erfolgreich angepasst
|
|||||||
CourseApplicationRatingDeleted: Bewertung erfolgreich entfernt
|
CourseApplicationRatingDeleted: Bewertung erfolgreich entfernt
|
||||||
CourseApplicationDeleted csh@CourseShorthand: Bewerbung zu #{csh} erfolgreich zurückgezogen
|
CourseApplicationDeleted csh@CourseShorthand: Bewerbung zu #{csh} erfolgreich zurückgezogen
|
||||||
|
|
||||||
CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName}
|
CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName}
|
||||||
|
|
||||||
CourseApplicationText: Text-Bewerbung
|
CourseApplicationText: Text-Bewerbung
|
||||||
CourseApplicationFollowInstructions: Beachten Sie die Anweisungen zur Bewerbung!
|
CourseApplicationFollowInstructions: Beachten Sie die Anweisungen zur Bewerbung!
|
||||||
@ -190,7 +190,7 @@ CourseRegistrationFollowInstructions: Beachten Sie die Anweisungen zur Anmeldung
|
|||||||
|
|
||||||
CourseApplicationFile: Bewerbung
|
CourseApplicationFile: Bewerbung
|
||||||
CourseApplicationFiles: Bewerbungsdatei(en)
|
CourseApplicationFiles: Bewerbungsdatei(en)
|
||||||
CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en)
|
CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en)
|
||||||
CourseRegistrationFile: Datei zur Anmeldung
|
CourseRegistrationFile: Datei zur Anmeldung
|
||||||
CourseRegistrationFiles: Datei(en) zur Anmeldung
|
CourseRegistrationFiles: Datei(en) zur Anmeldung
|
||||||
CourseRegistrationArchive: Zip-Archiv der Datei(en) zur Anmeldung
|
CourseRegistrationArchive: Zip-Archiv der Datei(en) zur Anmeldung
|
||||||
@ -1378,6 +1378,7 @@ CsvImportConfirmationTip: Durch den CSV-Import würden die unten aufgeführten
|
|||||||
CsvImportUnnecessary: Durch den CSV-Import würden keine Änderungen vorgenommen werden
|
CsvImportUnnecessary: Durch den CSV-Import würden keine Änderungen vorgenommen werden
|
||||||
CsvImportSuccessful n@Int: CSV-Import erfolgreich, es #{pluralDE n "wurde eine Aktion" (mappend (mappend "wurden " (toMessage n)) " Aktionen")} durchgeführt
|
CsvImportSuccessful n@Int: CSV-Import erfolgreich, es #{pluralDE n "wurde eine Aktion" (mappend (mappend "wurden " (toMessage n)) " Aktionen")} durchgeführt
|
||||||
CsvImportAborted: CSV-Import abgebrochen
|
CsvImportAborted: CSV-Import abgebrochen
|
||||||
|
CsvImportExplanationLabel: Hinweise zum CSV-Import
|
||||||
|
|
||||||
Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%)
|
Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%)
|
||||||
|
|
||||||
|
|||||||
@ -15,10 +15,6 @@ import Data.CaseInsensitive (original)
|
|||||||
-- import qualified Data.CaseInsensitive as CI
|
-- import qualified Data.CaseInsensitive as CI
|
||||||
import qualified Data.Conduit.List as Conduit
|
import qualified Data.Conduit.List as Conduit
|
||||||
|
|
||||||
import Language.Haskell.TH
|
|
||||||
import Language.Haskell.TH.Syntax (qRunIO)
|
|
||||||
-- import Language.Haskell.TH.Datatype
|
|
||||||
|
|
||||||
import Text.Hamlet (shamletFile)
|
import Text.Hamlet (shamletFile)
|
||||||
|
|
||||||
import Handler.Utils.DateTime as Handler.Utils
|
import Handler.Utils.DateTime as Handler.Utils
|
||||||
@ -32,12 +28,9 @@ import Handler.Utils.Rating as Handler.Utils hiding (extractRatings)
|
|||||||
import Handler.Utils.Sheet as Handler.Utils
|
import Handler.Utils.Sheet as Handler.Utils
|
||||||
import Handler.Utils.Mail as Handler.Utils
|
import Handler.Utils.Mail as Handler.Utils
|
||||||
import Handler.Utils.ContentDisposition as Handler.Utils
|
import Handler.Utils.ContentDisposition as Handler.Utils
|
||||||
|
import Handler.Utils.I18n as Handler.Utils
|
||||||
|
|
||||||
import System.Directory (listDirectory)
|
import System.FilePath.Posix (takeFileName)
|
||||||
import System.FilePath.Posix (takeBaseName, takeFileName)
|
|
||||||
|
|
||||||
import qualified Data.List as List
|
|
||||||
import qualified Data.List.NonEmpty as NonEmpty
|
|
||||||
|
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
|
|
||||||
@ -218,36 +211,6 @@ warnTermDays tid timeNames = do
|
|||||||
forM_ outoflecture $ warnI MsgDayIsOutOfLecture
|
forM_ outoflecture $ warnI MsgDayIsOutOfLecture
|
||||||
forM_ outoftermdays $ warnI MsgDayIsOutOfTerm
|
forM_ outoftermdays $ warnI MsgDayIsOutOfTerm
|
||||||
|
|
||||||
-- | Add language dependent template files
|
|
||||||
--
|
|
||||||
-- For large files which are translated as a whole.
|
|
||||||
--
|
|
||||||
-- Argument musst be a directory under @/templates@,
|
|
||||||
-- which contains a file for each language,
|
|
||||||
-- eg. @imprint@ for choosing between
|
|
||||||
-- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@,
|
|
||||||
-- and @/templates/imprint/en.hamlet@
|
|
||||||
--
|
|
||||||
-- Dependency detection cannot work properly (no `addDependentFile`-equivalent
|
|
||||||
-- for directories)
|
|
||||||
-- @$ stack clean@ is required so new translations show up
|
|
||||||
i18nWidgetFile :: FilePath -> Q Exp
|
|
||||||
i18nWidgetFile basename = do
|
|
||||||
-- Construct list of available translations (@de@, @en@, ...) at compile time
|
|
||||||
let i18nDirectory = "templates" </> "i18n" </> basename
|
|
||||||
availableFiles <- qRunIO $ listDirectory i18nDirectory
|
|
||||||
let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles
|
|
||||||
availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations
|
|
||||||
|
|
||||||
-- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time
|
|
||||||
ws <- newName "ws" -- Name for dispatch function
|
|
||||||
letE
|
|
||||||
[ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" </> basename </> l) []
|
|
||||||
| l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language
|
|
||||||
] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
|
|
||||||
] [e|selectLanguage availableTranslations' >>= $(varE ws)|]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | return a value only if the current user ist authorized for a given route
|
-- | return a value only if the current user ist authorized for a given route
|
||||||
guardAuthorizedFor :: ( HandlerSite h ~ UniWorX, MonadHandler h, MonadLogger h
|
guardAuthorizedFor :: ( HandlerSite h ~ UniWorX, MonadHandler h, MonadLogger h
|
||||||
|
|||||||
43
src/Handler/Utils/I18n.hs
Normal file
43
src/Handler/Utils/I18n.hs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
module Handler.Utils.I18n
|
||||||
|
where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Language.Haskell.TH
|
||||||
|
import Language.Haskell.TH.Syntax (qRunIO)
|
||||||
|
|
||||||
|
import qualified Data.List as List
|
||||||
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
|
||||||
|
import System.Directory (listDirectory)
|
||||||
|
import System.FilePath.Posix (takeBaseName)
|
||||||
|
|
||||||
|
|
||||||
|
-- | Add language dependent template files
|
||||||
|
--
|
||||||
|
-- For large files which are translated as a whole.
|
||||||
|
--
|
||||||
|
-- Argument musst be a directory under @/templates@,
|
||||||
|
-- which contains a file for each language,
|
||||||
|
-- eg. @imprint@ for choosing between
|
||||||
|
-- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@,
|
||||||
|
-- and @/templates/imprint/en.hamlet@
|
||||||
|
--
|
||||||
|
-- Dependency detection cannot work properly (no `addDependentFile`-equivalent
|
||||||
|
-- for directories)
|
||||||
|
-- @$ stack clean@ is required so new translations show up
|
||||||
|
i18nWidgetFile :: FilePath -> Q Exp
|
||||||
|
i18nWidgetFile basename = do
|
||||||
|
-- Construct list of available translations (@de@, @en@, ...) at compile time
|
||||||
|
let i18nDirectory = "templates" </> "i18n" </> basename
|
||||||
|
availableFiles <- qRunIO $ listDirectory i18nDirectory
|
||||||
|
let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles
|
||||||
|
availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations
|
||||||
|
|
||||||
|
-- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time
|
||||||
|
ws <- newName "ws" -- Name for dispatch function
|
||||||
|
letE
|
||||||
|
[ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" </> basename </> l) []
|
||||||
|
| l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language
|
||||||
|
] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
|
||||||
|
] [e|selectLanguage availableTranslations' >>= $(varE ws)|]
|
||||||
@ -42,6 +42,7 @@ import Handler.Utils.Table.Pagination.CsvColumnExplanations
|
|||||||
import Handler.Utils.Form
|
import Handler.Utils.Form
|
||||||
import Handler.Utils.Csv
|
import Handler.Utils.Csv
|
||||||
import Handler.Utils.ContentDisposition
|
import Handler.Utils.ContentDisposition
|
||||||
|
import Handler.Utils.I18n
|
||||||
import Utils
|
import Utils
|
||||||
import Utils.Lens
|
import Utils.Lens
|
||||||
|
|
||||||
@ -281,7 +282,7 @@ piIsUnset PaginationInput{..} = and
|
|||||||
, isNothing piPage
|
, isNothing piPage
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
data DBCsvActionMode = DBCsvActionNew | DBCsvActionExisting | DBCsvActionMissing
|
data DBCsvActionMode = DBCsvActionNew | DBCsvActionExisting | DBCsvActionMissing
|
||||||
deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic, Typeable)
|
deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic, Typeable)
|
||||||
instance Universe DBCsvActionMode
|
instance Universe DBCsvActionMode
|
||||||
@ -851,6 +852,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
, formSubmit = FormSubmit
|
, formSubmit = FormSubmit
|
||||||
, formAnchor = Nothing :: Maybe Text
|
, formAnchor = Nothing :: Maybe Text
|
||||||
}
|
}
|
||||||
|
csvImportExplanation = modal [whamlet|_{MsgCsvImportExplanationLabel}|] $ Right $(i18nWidgetFile "table/csv-import-explanation")
|
||||||
csvColExplanations = case dbtCsvEncode of
|
csvColExplanations = case dbtCsvEncode of
|
||||||
(Just (Dict, _) :: DBTCsvEncode _ csv) -> assertM' (not . null) . Map.toList . csvColumnsExplanations $ Proxy @csv
|
(Just (Dict, _) :: DBTCsvEncode _ csv) -> assertM' (not . null) . Map.toList . csvColumnsExplanations $ Proxy @csv
|
||||||
Nothing -> Nothing
|
Nothing -> Nothing
|
||||||
@ -921,7 +923,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
| otherwise
|
| otherwise
|
||||||
-> return $ DBCsvDiffNew rowKey row
|
-> return $ DBCsvDiffNew rowKey row
|
||||||
mapM_ fileSourceCsv dbCsvFiles .| C.mapM toDiff
|
mapM_ fileSourceCsv dbCsvFiles .| C.mapM toDiff
|
||||||
|
|
||||||
seen <- State.get
|
seen <- State.get
|
||||||
forM_ (Map.toList existing) $ \(rowKey, oldRow) -> if
|
forM_ (Map.toList existing) $ \(rowKey, oldRow) -> if
|
||||||
| Map.member rowKey seen -> return ()
|
| Map.member rowKey seen -> return ()
|
||||||
@ -938,7 +940,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
-> let doHandle
|
-> let doHandle
|
||||||
| Just inpCsv <- x ^? _dbCsvNew
|
| Just inpCsv <- x ^? _dbCsvNew
|
||||||
= handle $ throwM . (DBCsvException (toNamedRecord inpCsv) :: Text -> DBCsvException k') <=< dbtCsvRenderException
|
= handle $ throwM . (DBCsvException (toNamedRecord inpCsv) :: Text -> DBCsvException k') <=< dbtCsvRenderException
|
||||||
| otherwise
|
| otherwise
|
||||||
= id
|
= id
|
||||||
in C.sourceList <=< lift . doHandle . runConduit $ dbtCsvComputeActions x .| C.foldMap pure
|
in C.sourceList <=< lift . doHandle . runConduit $ dbtCsvComputeActions x .| C.foldMap pure
|
||||||
innerAct .| C.fold accActionMap Map.empty
|
innerAct .| C.fold accActionMap Map.empty
|
||||||
@ -954,7 +956,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
|
|
||||||
let
|
let
|
||||||
precomputeIdents :: forall f m'. (Eq (Element f), MonoFoldable f, MonadHandler m') => f -> m' (Element f -> Text)
|
precomputeIdents :: forall f m'. (Eq (Element f), MonoFoldable f, MonadHandler m') => f -> m' (Element f -> Text)
|
||||||
precomputeIdents = foldM (\f act -> (\id' x -> bool (f x) id' $ act == x) <$> newIdent) (\_ -> error "No id precomputed")
|
precomputeIdents = foldM (\f act -> (\id' x -> bool (f x) id' $ act == x) <$> newIdent) (\_ -> error "No id precomputed")
|
||||||
actionClassIdent <- precomputeIdents $ Map.keys actionMap
|
actionClassIdent <- precomputeIdents $ Map.keys actionMap
|
||||||
actionIdent <- precomputeIdents . Set.unions $ Map.elems actionMap
|
actionIdent <- precomputeIdents . Set.unions $ Map.elems actionMap
|
||||||
|
|
||||||
@ -980,7 +982,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
, formSubmit = FormSubmit
|
, formSubmit = FormSubmit
|
||||||
, formAnchor = Nothing :: Maybe Text
|
, formAnchor = Nothing :: Maybe Text
|
||||||
}
|
}
|
||||||
|
|
||||||
$(widgetFile "csv-import-confirmation-wrapper")
|
$(widgetFile "csv-import-confirmation-wrapper")
|
||||||
|
|
||||||
let defaultHeaderOrder = headerOrder (error "not to be forced" :: csv)
|
let defaultHeaderOrder = headerOrder (error "not to be forced" :: csv)
|
||||||
|
|||||||
21
templates/i18n/table/csv-import-explanation/de.hamlet
Normal file
21
templates/i18n/table/csv-import-explanation/de.hamlet
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<h3>Hinweise zum Import von CSV-Dateien
|
||||||
|
<dl .deflist>
|
||||||
|
<dt .deflist__dt>Änderungen
|
||||||
|
<dd .deflist__dd>
|
||||||
|
Alle Werte können durch den Import verändert werden,
|
||||||
|
auch bereits Beim Export bereits vorhandene Werte.
|
||||||
|
<dt .deflist__dt>Vorschau
|
||||||
|
<dd .deflist__dd>
|
||||||
|
Bevor Werte geändert werden, wird eine Vorschau der Änderungen angezeigt.
|
||||||
|
<dt .deflist__dt>Leere Zellen
|
||||||
|
<dd .deflist__dd>
|
||||||
|
Löschbare Zellen werden durch leere Zellen gelöscht oder auf eindeutige Werte gesetzt.
|
||||||
|
<dt .deflist__dt>Konsistenz
|
||||||
|
<dd .deflist__dd>
|
||||||
|
<p>
|
||||||
|
Es werden nur konsistente Änderungen akzeptiert!
|
||||||
|
<p>
|
||||||
|
Daraus folgt, dass es sinnvoll sein kann, gewisse Zellen frei zu lassen;
|
||||||
|
z.B. ändert man ein Studienfachzuordnung eines Teilnehmers ab,
|
||||||
|
dann müsste man auch Abschluss und Semesterzahl passend ändern.
|
||||||
|
Da diese jedoch eindeutig sind, kann man diese Zellen einfach frei lassen.
|
||||||
@ -4,6 +4,7 @@ $if is _Just dbtCsvDecode
|
|||||||
<h3 .csv-import__toggle uw-show-hide data-show-hide-id=table-csv-import data-show-hide-collapsed>
|
<h3 .csv-import__toggle uw-show-hide data-show-hide-id=table-csv-import data-show-hide-collapsed>
|
||||||
_{MsgTableHeadingCsvImport}
|
_{MsgTableHeadingCsvImport}
|
||||||
<div .csv-import__content>
|
<div .csv-import__content>
|
||||||
|
^{csvImportExplanation}
|
||||||
^{csvImportWdgt'}
|
^{csvImportWdgt'}
|
||||||
$if is _Just dbtCsvEncode
|
$if is _Just dbtCsvEncode
|
||||||
<div .csv-export>
|
<div .csv-export>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
.csv-export {
|
.csv-export {
|
||||||
margin-bottom: 13px;
|
margin-bottom: 13px;
|
||||||
|
|
||||||
.csv-export__content {
|
.csv-export__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: space-between;
|
align-content: space-between;
|
||||||
|
|||||||
Reference in New Issue
Block a user