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
|
||||
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
|
||||
CourseApplicationFollowInstructions: Beachten Sie die Anweisungen zur Bewerbung!
|
||||
@ -190,7 +190,7 @@ CourseRegistrationFollowInstructions: Beachten Sie die Anweisungen zur Anmeldung
|
||||
|
||||
CourseApplicationFile: Bewerbung
|
||||
CourseApplicationFiles: Bewerbungsdatei(en)
|
||||
CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en)
|
||||
CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en)
|
||||
CourseRegistrationFile: Datei zur Anmeldung
|
||||
CourseRegistrationFiles: 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
|
||||
CsvImportSuccessful n@Int: CSV-Import erfolgreich, es #{pluralDE n "wurde eine Aktion" (mappend (mappend "wurden " (toMessage n)) " Aktionen")} durchgeführt
|
||||
CsvImportAborted: CSV-Import abgebrochen
|
||||
CsvImportExplanationLabel: Hinweise zum CSV-Import
|
||||
|
||||
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.Conduit.List as Conduit
|
||||
|
||||
import Language.Haskell.TH
|
||||
import Language.Haskell.TH.Syntax (qRunIO)
|
||||
-- import Language.Haskell.TH.Datatype
|
||||
|
||||
import Text.Hamlet (shamletFile)
|
||||
|
||||
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.Mail 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 (takeBaseName, takeFileName)
|
||||
|
||||
import qualified Data.List as List
|
||||
import qualified Data.List.NonEmpty as NonEmpty
|
||||
import System.FilePath.Posix (takeFileName)
|
||||
|
||||
import Control.Monad.Logger
|
||||
|
||||
@ -218,36 +211,6 @@ warnTermDays tid timeNames = do
|
||||
forM_ outoflecture $ warnI MsgDayIsOutOfLecture
|
||||
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
|
||||
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.Csv
|
||||
import Handler.Utils.ContentDisposition
|
||||
import Handler.Utils.I18n
|
||||
import Utils
|
||||
import Utils.Lens
|
||||
|
||||
@ -281,7 +282,7 @@ piIsUnset PaginationInput{..} = and
|
||||
, isNothing piPage
|
||||
]
|
||||
|
||||
|
||||
|
||||
data DBCsvActionMode = DBCsvActionNew | DBCsvActionExisting | DBCsvActionMissing
|
||||
deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic, Typeable)
|
||||
instance Universe DBCsvActionMode
|
||||
@ -851,6 +852,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
||||
, formSubmit = FormSubmit
|
||||
, formAnchor = Nothing :: Maybe Text
|
||||
}
|
||||
csvImportExplanation = modal [whamlet|_{MsgCsvImportExplanationLabel}|] $ Right $(i18nWidgetFile "table/csv-import-explanation")
|
||||
csvColExplanations = case dbtCsvEncode of
|
||||
(Just (Dict, _) :: DBTCsvEncode _ csv) -> assertM' (not . null) . Map.toList . csvColumnsExplanations $ Proxy @csv
|
||||
Nothing -> Nothing
|
||||
@ -921,7 +923,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
||||
| otherwise
|
||||
-> return $ DBCsvDiffNew rowKey row
|
||||
mapM_ fileSourceCsv dbCsvFiles .| C.mapM toDiff
|
||||
|
||||
|
||||
seen <- State.get
|
||||
forM_ (Map.toList existing) $ \(rowKey, oldRow) -> if
|
||||
| Map.member rowKey seen -> return ()
|
||||
@ -938,7 +940,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
||||
-> let doHandle
|
||||
| Just inpCsv <- x ^? _dbCsvNew
|
||||
= handle $ throwM . (DBCsvException (toNamedRecord inpCsv) :: Text -> DBCsvException k') <=< dbtCsvRenderException
|
||||
| otherwise
|
||||
| otherwise
|
||||
= id
|
||||
in C.sourceList <=< lift . doHandle . runConduit $ dbtCsvComputeActions x .| C.foldMap pure
|
||||
innerAct .| C.fold accActionMap Map.empty
|
||||
@ -954,7 +956,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
||||
|
||||
let
|
||||
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
|
||||
actionIdent <- precomputeIdents . Set.unions $ Map.elems actionMap
|
||||
|
||||
@ -980,7 +982,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
||||
, formSubmit = FormSubmit
|
||||
, formAnchor = Nothing :: Maybe Text
|
||||
}
|
||||
|
||||
|
||||
$(widgetFile "csv-import-confirmation-wrapper")
|
||||
|
||||
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>
|
||||
_{MsgTableHeadingCsvImport}
|
||||
<div .csv-import__content>
|
||||
^{csvImportExplanation}
|
||||
^{csvImportWdgt'}
|
||||
$if is _Just dbtCsvEncode
|
||||
<div .csv-export>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
.csv-export {
|
||||
margin-bottom: 13px;
|
||||
|
||||
|
||||
.csv-export__content {
|
||||
display: flex;
|
||||
align-content: space-between;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user