You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
606 lines
16 KiB
606 lines
16 KiB
module CalculateurDeNotes exposing (..)
|
|
|
|
import Array exposing (..)
|
|
import Browser
|
|
import Element exposing (..)
|
|
import Element.Input as Input
|
|
import File.Download
|
|
import Html exposing (Html)
|
|
import Parser exposing (..)
|
|
import Set
|
|
|
|
|
|
|
|
{-
|
|
███ ███ █████ ██ ███ ██
|
|
████ ████ ██ ██ ██ ████ ██
|
|
██ ████ ██ ███████ ██ ██ ██ ██
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██ ██ ██ ██ ██ ██ ████
|
|
-}
|
|
|
|
|
|
main =
|
|
Browser.element
|
|
{ init = init
|
|
, update = update
|
|
, subscriptions = subscriptions
|
|
, view = view
|
|
}
|
|
|
|
|
|
|
|
{-
|
|
███ ███ ██████ ██████ ███████ ██
|
|
████ ████ ██ ██ ██ ██ ██ ██
|
|
██ ████ ██ ██ ██ ██ ██ █████ ██
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██ ██ ██████ ██████ ███████ ███████
|
|
-}
|
|
|
|
|
|
type alias Model =
|
|
{ bareme : String
|
|
, reponsesCorrectes : String
|
|
, reponsesEleves : String
|
|
, eleves : Eleves
|
|
}
|
|
|
|
|
|
init : () -> ( Model, Cmd Msg )
|
|
init _ =
|
|
( { bareme = ""
|
|
, reponsesCorrectes = ""
|
|
, reponsesEleves = ""
|
|
, eleves = []
|
|
}
|
|
, Cmd.none
|
|
)
|
|
|
|
|
|
|
|
{-
|
|
██ ██ ██████ ██████ █████ ████████ ███████
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██ ██ ██████ ██ ██ ███████ ██ █████
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██████ ██ ██████ ██ ██ ██ ███████
|
|
-}
|
|
|
|
|
|
type Msg
|
|
= NouveauBareme String
|
|
| NouvellesReponsesCorrectes String
|
|
| NouvellesReponsesEleves String
|
|
| TelechargerNotes
|
|
|
|
|
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
update msg model =
|
|
let
|
|
brms =
|
|
unsafeRun baremeSujet [] model.bareme
|
|
|
|
rpnCorrectes =
|
|
unsafeRun reponsesCorrectes Array.empty model.reponsesCorrectes
|
|
|
|
rpnEleves =
|
|
unsafeRun reponsesEleves [] model.reponsesEleves
|
|
in
|
|
case msg of
|
|
NouveauBareme nouveauBareme ->
|
|
let
|
|
brmss =
|
|
unsafeRun baremeSujet [] nouveauBareme
|
|
in
|
|
( { model
|
|
| bareme = nouveauBareme
|
|
, eleves = notes brmss rpnCorrectes rpnEleves
|
|
}
|
|
, Cmd.none
|
|
)
|
|
|
|
NouvellesReponsesCorrectes nouvellesReponsesCorrectes ->
|
|
let
|
|
rpnCorrectess =
|
|
unsafeRun reponsesCorrectes Array.empty nouvellesReponsesCorrectes
|
|
in
|
|
( { model
|
|
| reponsesCorrectes = nouvellesReponsesCorrectes
|
|
, eleves = notes brms rpnCorrectess rpnEleves
|
|
}
|
|
, Cmd.none
|
|
)
|
|
|
|
NouvellesReponsesEleves nouvellesReponsesEleves ->
|
|
let
|
|
rpnElevess =
|
|
unsafeRun reponsesEleves [] nouvellesReponsesEleves
|
|
in
|
|
( { model
|
|
| reponsesEleves = nouvellesReponsesEleves
|
|
, eleves = notes brms rpnCorrectes rpnElevess
|
|
}
|
|
, Cmd.none
|
|
)
|
|
|
|
TelechargerNotes ->
|
|
( model
|
|
, File.Download.string "Notes.org" "text/org" <| voirNotesOrg model.eleves
|
|
)
|
|
|
|
|
|
unsafeRun prsr defaut texte =
|
|
case run prsr texte of
|
|
Ok x ->
|
|
x
|
|
|
|
_ ->
|
|
defaut
|
|
|
|
|
|
|
|
-- SUBSCRIPTIONS
|
|
|
|
|
|
subscriptions : Model -> Sub Msg
|
|
subscriptions model =
|
|
Sub.none
|
|
|
|
|
|
|
|
{-
|
|
|
|
██ ██ ██ ███████ ██ ██
|
|
██ ██ ██ ██ ██ ██
|
|
██ ██ ██ █████ ██ █ ██
|
|
██ ██ ██ ██ ██ ███ ██
|
|
████ ██ ███████ ███ ███
|
|
-}
|
|
|
|
|
|
view : Model -> Html Msg
|
|
view model =
|
|
layout [ width fill, height fill ] <|
|
|
row
|
|
[ spacing grandEspacement
|
|
, padding tresGrandEspacement
|
|
, height fill
|
|
, width fill
|
|
, clip
|
|
, scrollbars
|
|
]
|
|
[ column
|
|
[ spacing petitEspacement
|
|
, height fill
|
|
, width fill
|
|
, clip
|
|
, scrollbars
|
|
]
|
|
[ Input.multiline [ height <| maximum 300 fill, clip, scrollbars ]
|
|
{ onChange = NouveauBareme
|
|
, label = Input.labelAbove [] <| text "Barème"
|
|
, placeholder =
|
|
Just <|
|
|
Input.placeholder [] <|
|
|
text "Entrer le barème sous la forme +3 -1, +2 -1"
|
|
, text = model.bareme
|
|
, spellcheck = False
|
|
}
|
|
, Input.multiline [ height <| maximum 300 fill, clip, scrollbars ]
|
|
{ onChange = NouvellesReponsesCorrectes
|
|
, label = Input.labelAbove [] <| text "Réponses correctes"
|
|
, placeholder =
|
|
Just <|
|
|
Input.placeholder [] <|
|
|
text "Entrer les réponses correctes pour chaque sujet"
|
|
, text = model.reponsesCorrectes
|
|
, spellcheck = False
|
|
}
|
|
, Input.multiline [ height <| maximum 300 fill, clip, scrollbars ]
|
|
{ onChange = NouvellesReponsesEleves
|
|
, label = Input.labelAbove [] <| text "Réponses des élèves"
|
|
, placeholder =
|
|
Just <|
|
|
Input.placeholder [] <|
|
|
text "Entrer les réponses des élèves"
|
|
, text = model.reponsesEleves
|
|
, spellcheck = False
|
|
}
|
|
]
|
|
, column [ spacing petitEspacement, height fill, width fill ]
|
|
[ text <|
|
|
"Moyenne : "
|
|
++ String.fromFloat (moyenne model.eleves)
|
|
++ " Écart type : "
|
|
++ String.fromFloat (ecartType model.eleves)
|
|
, Input.button []
|
|
{ onPress = Just TelechargerNotes
|
|
, label = text "Télécharger le fichier de notes"
|
|
}
|
|
, voirNotes model.eleves
|
|
]
|
|
]
|
|
|
|
|
|
voirNotes : Eleves -> Element Msg
|
|
voirNotes rpnsEleves =
|
|
table []
|
|
{ data = rpnsEleves
|
|
, columns =
|
|
[ { header = Element.text "Numéro étudiant"
|
|
, width = fill
|
|
, view =
|
|
\rpns ->
|
|
Element.text rpns.numeroEtudiant
|
|
}
|
|
, { header = Element.text "Note"
|
|
, width = fill
|
|
, view =
|
|
\rpns ->
|
|
case rpns.note of
|
|
Nothing ->
|
|
Element.text ""
|
|
|
|
Just nt ->
|
|
Element.text <| String.fromFloat nt
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
voirNotesOrg : Eleves -> String
|
|
voirNotesOrg rpnsEleves =
|
|
let
|
|
numero rpns =
|
|
rpns.numeroEtudiant
|
|
|
|
voirNote rpns =
|
|
case rpns.note of
|
|
Nothing ->
|
|
""
|
|
|
|
Just nt ->
|
|
String.fromFloat nt
|
|
|
|
ligne rpns =
|
|
"|"
|
|
++ rpns.numeroEtudiant
|
|
++ "|"
|
|
++ rpns.nomEtudiant
|
|
++ "|"
|
|
++ rpns.prenomEtudiant
|
|
++ "|"
|
|
++ voirNote rpns
|
|
++ "|\n"
|
|
in
|
|
"|Numéro|Nom|Prénom|Note|\n"
|
|
++ String.concat (List.map ligne rpnsEleves)
|
|
|
|
|
|
petitEspacement =
|
|
20
|
|
|
|
|
|
grandEspacement =
|
|
5 * petitEspacement // 4
|
|
|
|
|
|
tresGrandEspacement =
|
|
25 * petitEspacement // 16
|
|
|
|
|
|
|
|
{-
|
|
██████ █████ ██████ ███████ ███ ███ ███████
|
|
██ ██ ██ ██ ██ ██ ██ ████ ████ ██
|
|
██████ ███████ ██████ █████ ██ ████ ██ █████
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██████ ██ ██ ██ ██ ███████ ██ ██ ███████
|
|
-}
|
|
{--
|
|
baremeEtReponses =
|
|
succeed (\brm rpn -> BaremeEtReponses brm (List.map (String.split "") rpn))
|
|
|= baremeSujet
|
|
|= reponses
|
|
--}
|
|
|
|
|
|
type alias BaremeSujet =
|
|
List BaremeQuestion
|
|
|
|
|
|
baremeSujet =
|
|
sequence
|
|
{ start = ""
|
|
, separator = ","
|
|
, end = ""
|
|
, spaces = espaces
|
|
, item = baremeQuestion
|
|
, trailing = Forbidden
|
|
}
|
|
|
|
|
|
espaces =
|
|
chompWhile <| (==) ' '
|
|
|
|
|
|
type alias BaremeQuestion =
|
|
{ bonneReponse : Float
|
|
, mauvaiseReponse : Float
|
|
}
|
|
|
|
|
|
baremeQuestion =
|
|
succeed BaremeQuestion
|
|
|= nombre
|
|
|. spaces
|
|
|= nombre
|
|
|
|
|
|
nombre : Parser Float
|
|
nombre =
|
|
oneOf
|
|
[ succeed negate
|
|
|. symbol "-"
|
|
|= float
|
|
, succeed identity
|
|
|. symbol "+"
|
|
|= float
|
|
]
|
|
|
|
|
|
|
|
{-
|
|
██████ ███████ ██████ ██████ ███ ██ ███████ ███████ ███████
|
|
██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██
|
|
██████ █████ ██████ ██ ██ ██ ██ ██ ███████ █████ ███████
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██ ██ ███████ ██ ██████ ██ ████ ███████ ███████ ███████
|
|
-}
|
|
|
|
|
|
type alias ReponsesCorrectes =
|
|
Array Reponses
|
|
|
|
|
|
reponsesCorrectes : Parser ReponsesCorrectes
|
|
reponsesCorrectes =
|
|
succeed Array.fromList
|
|
|= sequence
|
|
{ start = ""
|
|
, separator = "\n"
|
|
, end = ""
|
|
, spaces = espaces
|
|
, item = reponses
|
|
, trailing = Optional
|
|
}
|
|
|
|
|
|
type alias Reponses =
|
|
List String
|
|
|
|
|
|
reponses =
|
|
sequence
|
|
{ start = ""
|
|
, separator = ""
|
|
, end = ""
|
|
, spaces = espaces
|
|
, item =
|
|
variable
|
|
{ start = \x -> x /= '\n' && x /= ';'
|
|
, inner = \_ -> False
|
|
, reserved = Set.fromList []
|
|
}
|
|
, trailing = Optional
|
|
}
|
|
|
|
|
|
type alias Eleves =
|
|
List Eleve
|
|
|
|
|
|
reponsesEleves : Parser Eleves
|
|
reponsesEleves =
|
|
sequence
|
|
{ start = ""
|
|
, separator = "\n"
|
|
, end = ""
|
|
, spaces = espaces
|
|
, item = reponsesEleve
|
|
, trailing = Optional
|
|
}
|
|
|
|
|
|
|
|
{-
|
|
███████ ██ ███████ ██ ██ ███████ ███████
|
|
██ ██ ██ ██ ██ ██ ██
|
|
█████ ██ █████ ██ ██ █████ ███████
|
|
██ ██ ██ ██ ██ ██ ██
|
|
███████ ███████ ███████ ████ ███████ ███████
|
|
-}
|
|
|
|
|
|
type alias Eleve =
|
|
{ numeroEtudiant : String
|
|
, numeroSujet : Int
|
|
, nomEtudiant : String
|
|
, prenomEtudiant : String
|
|
, reponses : Reponses
|
|
, note : Maybe Float
|
|
}
|
|
|
|
|
|
reponsesEleve =
|
|
succeed Eleve
|
|
|= etudiant
|
|
|= int
|
|
|= champ
|
|
|= champ
|
|
|= reponsesQuizScan
|
|
|= champzInteret
|
|
|
|
|
|
etudiant =
|
|
getChompedString <|
|
|
chompIf Char.isDigit
|
|
|. chompIf Char.isDigit
|
|
|. chompIf Char.isDigit
|
|
|. chompIf Char.isDigit
|
|
|. chompIf Char.isDigit
|
|
|
|
|
|
champ =
|
|
succeed identity
|
|
|. symbol ";"
|
|
|= getChompedString (chompWhile ((/=) ';'))
|
|
|
|
|
|
champzInteret =
|
|
succeed Nothing
|
|
|. symbol ";"
|
|
|. chompWhile (\x -> x /= '\n' && x /= ';')
|
|
|
|
|
|
reponsesQuizScan =
|
|
sequence
|
|
{ start = ";"
|
|
, separator = ";"
|
|
, end = ""
|
|
, spaces = espaces
|
|
, item =
|
|
variable
|
|
{ start = \x -> x /= '\n' && x /= ';'
|
|
, inner = \_ -> False
|
|
, reserved = Set.fromList []
|
|
}
|
|
, trailing = Mandatory
|
|
}
|
|
|
|
|
|
notes : BaremeSujet -> ReponsesCorrectes -> Eleves -> Eleves
|
|
notes brms rpnCorrectes rpnEleves =
|
|
let
|
|
f rpnEleve =
|
|
case Array.get (rpnEleve.numeroSujet - 11) rpnCorrectes of
|
|
Nothing ->
|
|
rpnEleve
|
|
|
|
Just bonneRpns ->
|
|
{ rpnEleve | note = noteSujet brms bonneRpns rpnEleve.reponses }
|
|
in
|
|
List.map f rpnEleves
|
|
|
|
|
|
noteSujet : BaremeSujet -> Reponses -> Reponses -> Maybe Float
|
|
noteSujet brms bonneRpns rpnsEleve =
|
|
case brms of
|
|
[] ->
|
|
Just 0
|
|
|
|
brm :: brmss ->
|
|
let
|
|
bonneRpn =
|
|
List.head bonneRpns
|
|
|
|
bonneRpnSuite =
|
|
List.tail bonneRpns
|
|
|
|
rpnEleve =
|
|
List.head rpnsEleve
|
|
|
|
rpnEleveSuite =
|
|
List.tail rpnsEleve
|
|
in
|
|
case ( ( bonneRpn, bonneRpnSuite ), ( rpnEleve, rpnEleveSuite ) ) of
|
|
( ( Just bnRpn, Just bnRpnSuite ), ( Just rpnElv, Just rpnElvSuite ) ) ->
|
|
noteSujet brmss bnRpnSuite rpnElvSuite
|
|
|> Maybe.andThen (Just << (+) (noteQuestion brm bnRpn rpnElv))
|
|
|
|
_ ->
|
|
Nothing
|
|
|
|
|
|
noteQuestion brm bonneRpn rpnEleve =
|
|
case bonneRpn of
|
|
"V" ->
|
|
if rpnEleve == "A" then
|
|
brm.bonneReponse
|
|
|
|
else if rpnEleve == "B" then
|
|
2 * brm.bonneReponse / 3
|
|
|
|
else if rpnEleve == "C" then
|
|
brm.mauvaiseReponse / 3
|
|
|
|
else if rpnEleve == "D" then
|
|
brm.mauvaiseReponse
|
|
|
|
else
|
|
0
|
|
|
|
"F" ->
|
|
if rpnEleve == "D" then
|
|
brm.bonneReponse
|
|
|
|
else if rpnEleve == "C" then
|
|
2 * brm.bonneReponse / 3
|
|
|
|
else if rpnEleve == "B" then
|
|
brm.mauvaiseReponse / 3
|
|
|
|
else if rpnEleve == "A" then
|
|
brm.mauvaiseReponse
|
|
|
|
else
|
|
0
|
|
|
|
_ ->
|
|
if bonneRpn == rpnEleve then
|
|
brm.bonneReponse
|
|
|
|
else if rpnEleve == "-" then
|
|
0
|
|
|
|
else
|
|
brm.mauvaiseReponse
|
|
|
|
|
|
moyenne elvs =
|
|
let
|
|
moy nts =
|
|
List.sum nts / toFloat (List.length nts)
|
|
in
|
|
List.map .note elvs
|
|
|> expurgerNotesManquantes
|
|
|> moy
|
|
|
|
|
|
ecartType elvs =
|
|
let
|
|
moy nts =
|
|
List.sum nts / toFloat (List.length nts)
|
|
|
|
moyCarre =
|
|
moy << List.map (\x -> x ^ 2)
|
|
|
|
ecTp nts =
|
|
sqrt <| moyCarre nts - moy nts ^ 2
|
|
in
|
|
List.map .note elvs
|
|
|> expurgerNotesManquantes
|
|
|> ecTp
|
|
|
|
|
|
expurgerNotesManquantes nts =
|
|
case nts of
|
|
[] ->
|
|
[]
|
|
|
|
Nothing :: ntss ->
|
|
expurgerNotesManquantes ntss
|
|
|
|
(Just nt) :: ntss ->
|
|
nt :: expurgerNotesManquantes ntss
|
|
|