Code source du site TeacherCorner.lamdera.app, contenant une suite d'outils permettant d'automatiser la production de documents pédagogiques.
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.
 
 
TeacherCorner/src/CalculateurDeNotes.elm

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