diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9eab7de --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +local/epfl/cache/*.txt diff --git a/bin/bamc b/bin/bamc new file mode 100755 index 0000000..eb7d9f7 --- /dev/null +++ b/bin/bamc @@ -0,0 +1,29 @@ +#!/bin/bash + +#!/bin/bash + +# Set SNAME, SDIR and SPATH +if [ -h $0 ]; then SNAME=$(readlink $0); else SNAME=$0; fi +SDIR=$(dirname $SNAME) +if [[ "$SDIR" =~ ^/ ]]; then SPATH=$SDIR; else SPATH=$(realpath $(pwd)/$SDIR); fi + +# Set DIR_LIB +DIR_LIB=$(realpath $SPATH/../lib/bash) + +# Source libs +source $DIR_LIB/colors.sh +source $DIR_LIB/io.sh +source $DIR_LIB/cmdline_parser.sh +source $DIR_LIB/bamc_configuration.sh +source $DIR_LIB/bamc_help.sh +source $DIR_LIB/bamc_workspace.sh + +DEFAULT_ITEMS="" +DEFAULT_ACTIONS="help" +CALLBACK_PREFIX="action_" + +run $@ +RC=$? +debug "Return code: $RC" + +exit $RC diff --git a/conf/bamc.conf b/conf/bamc.conf new file mode 100644 index 0000000..5a616f9 --- /dev/null +++ b/conf/bamc.conf @@ -0,0 +1,49 @@ +#!/bin/bash + +# WORKSPACE +DIR_WORKSPACE="." +DIR_QUESTIONS='questions' +DIR_EXAMS='exams' +DIR_PROJECTS='projects' +FILE_WORKSPACE_ROOT=".workspace_root" + +# CONFIGURATION DIRS +DIR_DATA=$(realpath $SPATH/../data) +DIR_SKEL=$DIR_DATA/skel + +return 0 + +# INTPUT FILES +FILE_LANG='lang.conf' +FILE_SECTIONS='sections.conf' +FILE_PROF='prof.conf' +FILE_STUDENTS='students.csv' +FILE_SAMPLE_CSV='sample.csv' +FILE_EXTRA_CSV='extra.csv' +FILE_BLANK_CSV='blank.csv' +FILE_AMC_OPTIONS='options.xml' +FILE_AMC_MAILING='mailing.xml' +FILE_LIST_MAIN='list.tex' +FILE_LIST_TSTART='start_tab.tex' +FILE_LIST_TEND='end_tab.tex' + +# OUTPUT DIRS +DIR_EXAMS='exams' +DIR_SAMPLES='pdf-samples' +DIR_BLANKS='pdf-blanks' +DIR_PDF='pdf-exams' +DIR_LIST_OUT='pdf-lists' + +# OUTPUT FILES +FILE_PROF_TEX='professor.tex' +FILE_EXAM_BASENAME='exam' +FILE_RESULTS='raw' +FILE_EMAILS_SCRIPT='send_emails.sh' +FILE_LIST_ROWS='rows.tex' + +# VARIABLES +PDFLATEX_RUNS=1 +EXTRA_COPIES=9 +DEFAULT_EMAIL='pierre-olivier.valles@epfl.ch' +LIST_ROWS_PER_PAGE=50 +LIST_MAX_CHAR=30 diff --git a/data/CSV/blank.csv b/data/CSV/blank.csv new file mode 100644 index 0000000..ef45c47 --- /dev/null +++ b/data/CSV/blank.csv @@ -0,0 +1,3 @@ +ID,NOM,SCIPER,SECTION,EMAIL,TAILLE_NOM,NOM_COMPLET,SEMESTRE +n/a,n/a,999999,(n/a),do_not_reply@epfl.ch,3,n/a,1 + diff --git a/data/CSV/extra.csv b/data/CSV/extra.csv new file mode 100644 index 0000000..d5f9cbd --- /dev/null +++ b/data/CSV/extra.csv @@ -0,0 +1 @@ +#ID#,XXX-#NB#,#SCIPER#,XXX,#EMAIL# diff --git a/data/CSV/sample.csv b/data/CSV/sample.csv new file mode 100644 index 0000000..4d39d92 --- /dev/null +++ b/data/CSV/sample.csv @@ -0,0 +1,5 @@ +ID,NOM,SCIPER,SECTION,EMAIL,TAILLE_NOM,NOM_COMPLET,SEMESTRE +1,Lennon John,XXXXX1,XYZ,pierre-Olivier.valles@epfl.ch,11,Lennon John,1 +2,McCartney Paul,XXXXX2,XYZ,pierre-Olivier.valles@epfl.ch,14,McCartney Paul,1 +3,Harrisson George,XXXXX3,XYZ,pierre-Olivier.valles@epfl.ch,16,Harrisson George,1 +4,Starr Ringo,XXXXX4,XYZ,pierre-Olivier.valles@epfl.ch,11,Starr Ringo,1 diff --git a/data/base/exam.tex b/data/base/exam.tex new file mode 100644 index 0000000..3ec31bc --- /dev/null +++ b/data/base/exam.tex @@ -0,0 +1,48 @@ +\documentclass[a4paper]{article} + +\usepackage[utf8]{inputenc} %Unicode +\usepackage[T1]{fontenc} +\usepackage{fix-cm} + +\usepackage[lang=#LANG#,bloc,completemulti]{automultiplechoice} + +%%% Default packages +\input{./packages.tex} + +%%% Formating specific to EPFL +\input{./style_extra.tex} +\input{./true-false.tex} % Language dependent ! +\input{./question-text.tex} % Language dependent ! + +%%% Professor specific imports +\input{./professor.tex} +\input{./style_professor.tex} + +%%% Randomseed +\input{./random-seed.tex} + +\begin{document} + +%%% Import sections +\input{./sections.tex} + +\newcommand{\sujet}{ + \onecopy{1}{ + %%% debut de l'en-tête des copies : + \input{./first_page.tex} + + \input{./random-sections.tex} + + % #SPECIFIC# + + %\clearpage ~ + % Make sure that all the exams have the same number of pages (otherwise the repro will not be able to print!) + \loop \ifnum \thepage < \totalPages \clearpage ~ \repeat + + \AMCassociation{\ID} + } +} + +\csvreader[head to column names]{./students.csv}{}{\sujet} + +\end{document} diff --git a/data/base/packages.tex b/data/base/packages.tex new file mode 100644 index 0000000..11fbbe8 --- /dev/null +++ b/data/base/packages.tex @@ -0,0 +1,24 @@ +%% Extra packages + +%% CSV file access +\usepackage{csvsimple} + +%% Multicolumn formatting +\usepackage{multicol} + +%% Math packages +\usepackage{graphicx} +\usepackage{amsmath} +\usepackage{mathrsfs} +\usepackage{euscript} +\usepackage{epsfig} +\usepackage{ifthen} +\usepackage{amsfonts} +\usepackage{amssymb} + +%% Graphics +\usepackage{graphicx} +\graphicspath{ {media/} } + +%% Drawing +\usepackage{tikz} diff --git a/data/base/professor.tex b/data/base/professor.tex new file mode 100644 index 0000000..d46a652 --- /dev/null +++ b/data/base/professor.tex @@ -0,0 +1,6 @@ +\newcommand{\prof}{#PROF_NAME#} +\newcommand{\totalPages}{#TOTAL_PAGES#} +\newcommand{\StudentsPath}{./students.csv} +\newcommand{\Time}{#DURATION#} +\newcommand{\ExamDate}{#DATE#} +\newcommand{\ExamName}{#EXAM#} diff --git a/data/base/random-seed.tex b/data/base/random-seed.tex new file mode 100644 index 0000000..fb5b35b --- /dev/null +++ b/data/base/random-seed.tex @@ -0,0 +1 @@ +\AMCrandomseed{11051975} \ No newline at end of file diff --git a/data/base/style_extra.tex b/data/base/style_extra.tex new file mode 100644 index 0000000..00e1d16 --- /dev/null +++ b/data/base/style_extra.tex @@ -0,0 +1,87 @@ +%% Default values for open questions: +\AMCopenOpts{lines=10,lineheight=2ex,framerulecol=black,dots=false} + +\newcommand{\letter}{A} + +\makeatletter +\renewcommand{\theenumi}{\alph{enumi}} +\renewcommand{\labelenumi}{(\theenumi)} +\makeatother + +%% OpenBox commands ============================ +\newcommand{\OpenBox}[1]{ +\noindent + \fbox{ + \centering + \begin{minipage}{1.0\textwidth} +~ + \vspace{#1} +~ + \end{minipage} + } +} + +\newcommand{\FullPageOpenBox}[0]{ + \clearpage + \OpenBox{23cm} +} + +%% OpenGrid commands ============================ +\newcommand{\OpenGrid}[1]{ +\noindent +\begin{tikzpicture} +\draw[step=.5cm,lightgray] +(0cm,0cm) grid (15.5cm,#1); +\end{tikzpicture} +} + +\newcommand{\FullPageOpenGrid}[1]{ + \clearpage + \OpenGrid{23cm} +} + +%% ============================================== +% Single points + +\newcommand{\correctorChoices}{\correctchoice[0]{}\scoring{b=0}\correctchoice[Y]{}\scoring{b=0.25}\correctchoice[X]{}\scoring{b=0.5}\correctchoice[W]{}\scoring{b=0.75}\correctchoice[V]{\qquad}\scoring{b=1}} + +\newcommand{\correctorThree}[3][0]{ +\par +\begin{questionmultx}{#2} +#3 + +\noindent + \AMCOpen{lines=#1,framerulecol=white}{ + \correctorChoices\correctorChoices\correctorChoices +} +\end{questionmultx} +\vspace{-55pt}~ +} + +\newcommand{\correctorOne}[3][0]{ +\par +\noindent +\begin{questionmultx}{#2} +#3 + +\noindent + \AMCOpen{lines=#1,framerulecol=white}{ + \correctorChoices\makebox[220.3pt][c]{} +} +\end{questionmultx} +\vspace{-55pt}~ +} + +\newcommand{\correctorTwo}[3][0]{ +\par +\noindent +\begin{questionmultx}{#2} +#3 + +\noindent + \AMCOpen{lines=#1,framerulecol=white}{ + \correctorChoices\correctorChoices\makebox[108.5pt][c]{} +} +\end{questionmultx} +\vspace{-55pt}~ +} diff --git a/data/base/style_professor.tex b/data/base/style_professor.tex new file mode 100644 index 0000000..bac967f --- /dev/null +++ b/data/base/style_professor.tex @@ -0,0 +1,50 @@ +%% New commands, professor specific + +\newcommand\RR{\mathbb{R}} +\newcommand\PP{\mathbb{P}} +\newcommand{\Col}{\mathop{\rm Col}\nolimits} +\newcommand{\Nul}{\mathop{\rm Ker}\nolimits} +\newcommand{\Lign}{\mathop{\rm Lig}\nolimits} + +\newcommand{\Span}{\mathop{\rm Vect}\nolimits} +\renewcommand{\vec}[1]{\mathbf{#1}} + +\newcommand{\Vector}[1]{\left[ + \begin{array}{c} + #1 + \end{array} +\right] +} + +\newenvironment{Matrix}[1]{\left[ + \begin{array}[r]{#1}}{ + \end{array} +\right] +} + +% Some namenclature used: +\newcommand{\dimension}{taille} % used for size of a matrix. usually dimension ou taille + +%% Questions %%% +\baremeDefautS{b=3,m=-1} +\baremeDefautM{b=2,m=0} + +\newcommand{\VERO}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \bonne{VRAI} + \mauvaise{FAUX} + \end{choiceshoriz} + +\medskip +} + +\newcommand{\FALSO}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \mauvaise{VRAI} + \bonne{FAUX} + \end{choiceshoriz} + +\medskip +} diff --git a/data/config/de-mailing.xml b/data/config/de-mailing.xml new file mode 100644 index 0000000..f648bd0 --- /dev/null +++ b/data/config/de-mailing.xml @@ -0,0 +1,27 @@ + + + + + + --senderauto-multiple-choice <do-not-reply@epfl.ch> + --textHallo, + +Im Anhang finden Sie das korrigierte Prüfung. +Sollten Sie Fragen in Bezug auf Ihre Prüfung, kontaktieren Sie bitte Ihren Lehrer direkt. + +Mit freundlichen Grüßen. + --subjectIhre "Analyse 1" behoben Prüfung + + + --project./ + + + --students-list%FILE_STUDENTS% + --list-encodingutf-8 + --email-columnEMAIL + + + --transportsendmail + --sendmail-path/usr/bin/sendmail + + \ No newline at end of file diff --git a/data/config/en-mailing.xml b/data/config/en-mailing.xml new file mode 100644 index 0000000..063a7a9 --- /dev/null +++ b/data/config/en-mailing.xml @@ -0,0 +1,27 @@ + + + + + + --senderauto-multiple-choice <do-not-reply@epfl.ch> + --textHello, + +Please find attached your corrected exam. +Should you have any questions regarding your exam, please contact your teacher directly. + +Sincerely + --subjectYour Analyse 1 corrected exam + + + --project./ + + + --students-list%FILE_STUDENTS% + --list-encodingutf-8 + --email-columnEMAIL + + + --transportsendmail + --sendmail-path/usr/bin/sendmail + + \ No newline at end of file diff --git a/data/config/fr-mailing.xml b/data/config/fr-mailing.xml new file mode 100644 index 0000000..fbf124a --- /dev/null +++ b/data/config/fr-mailing.xml @@ -0,0 +1,27 @@ + + + + + + --senderauto-multiple-choice <do-not-reply@epfl.ch> + --textBonjour, + +Veuillez trouvez ci-jointe la correction de votre examen. +Pour toute question relative à votre copie, n'hésitez pas à contacter votre enseignant. + +Bonne réception. + --subjectCorrection de votre examen blanc d'Analyse 1 + + + --project./ + + + --students-list%FILE_STUDENTS% + --list-encodingutf-8 + --email-columnEMAIL + + + --transportsendmail + --sendmail-path/usr/bin/sendmail + + \ No newline at end of file diff --git a/data/config/options.xml b/data/config/options.xml new file mode 100644 index 0000000..86a7a2a --- /dev/null +++ b/data/config/options.xml @@ -0,0 +1,61 @@ + + + <_modifie> + <_modifie_ok>1 + + + marges + + <preassoc> + 0 + + cr + data + EXAM-catalog.pdf + EXAM-sujet.pdf + EXAM-calage.xy + EXAM-corrige.pdf + EMAIL + Linear Algebra: test result + Please find enclosed your annotated completed answer sheet. +Best Regards. + UTF-8 + UTF-8 + ID,NOM,SCIPER,SECTION,EMAIL,TAILLE_NOM,NOM_COMPLET,SEMESTRE + ; + + + 2 + student.copy,student.key,student.name + + + 1 + a4 + n + latex + EXAM-filtered.tex + CSV + SCIPER + %PROJET/students.csv + 1 + + pdflatex + + 0 + normal + 0.5 + 6 + 1 + 1 + notes-common.xml + 0 + 0 + + ALL + STUDENTS + 0.007 + %PROJET/exam.tex + %(ID) +Score: %S/%M + "%s/%m" + diff --git a/data/de/first_page.tex b/data/de/first_page.tex new file mode 100644 index 0000000..fc2aa56 --- /dev/null +++ b/data/de/first_page.tex @@ -0,0 +1,54 @@ +% First page - GERMAN + +\noindent +\begin{minipage}[c]{350pt} + \bf \prof~-~\ExamName~-~\SECTION \\ + ~ \\ + \bf \ExamDate~-~Dauer: \Time +\end{minipage} +\begin{minipage}[c]{100pt} + \includegraphics[scale=0.24]{Logo_EPFL} +\end{minipage} + +\vfill +\begin{minipage}[c]{\textwidth} +\noindent +\hfill\fontsize{100}{120}{\selectfont{\ID{}}}\hfill +\vspace{1cm} +\par +\noindent +\hfill\fontsize{40}{48}{\selectfont{\NOM{}}}\hfill~ +\end{minipage} +\vfill + +\noindent +\namefield{SCIPER: {\Large \bf \SCIPER{} }} \\ +~ \\ +\noindent +\textbf{Drehen Sie diese Seite nicht um, bevor Sie dazu aufgefordert werden.} +\textbf{Jedes Blatt hat eine Vorder- und eine R\"uckseite. Es gibt \totalPages\ Seiten, die letzten sind m\"oglicherweise leer.} +\textbf{L\"osen Sie nicht die Heftklammer.} + +\begin{itemize} +\item[-] Legen Sie Ihren Studentenausweis auf den Tisch. +\item[-] Es sind \textbf{keine} Unterlagen zugelassen. +\item[-] Die Nutzung eines \textbf{Taschenrechners} oder jedes anderen elektronischen Hilfsmittels ist w\"ahrend der Pr\"ufung nicht gestattet. +\item[-] F\"ur die \textbf{Multiple Choice} Fragen erh\"alt man: + \begin{itemize} + \item [$+3$] Punkte, wenn die Antwort korrekt ist, + \item [0] Punkte, wenn die Frage nicht beantwortet ist oder mehrere M\"oglichkeiten angekreuzt sind, und + \item [$-1$] Punkt, wenn die Antwort falsch ist. + \end{itemize} +\item [-] F\"ur die \textbf{Wahr/Falsch }Fragen erh\"alt man: + \begin{itemize} + \item [$+1$] Punkt, wenn die Antwort korrekt ist, + \item [0] Punkte, wenn die Frage nicht beantwortet ist oder mehrere M\"oglichkeiten angekreuzt sind, und + \item [$-1$] Punkt, wenn die Antwort falsch ist. + \end{itemize} +\item[-] Benutzen Sie einen \textbf{Bleistift} und radieren Sie, falls n\"otig, sorgf\"altig aus. +\item[-] Beachten Sie bitte diese Richtlinien bei der Markierung \textbf{der Antworten}: +\end{itemize} + +\hfill \includegraphics[scale=0.2]{good_bad_square} \hfill + +% End of first page diff --git a/data/de/header_1.tex b/data/de/header_1.tex new file mode 100644 index 0000000..9a41685 --- /dev/null +++ b/data/de/header_1.tex @@ -0,0 +1,12 @@ + +\newpage + +\subsection*{Erster Teil, Multiple-Choice-Fragen} + +\noindent +Kreuze bitte für jede Frage die Box an, die zu der richtigen Lösung gehört. Streiche nichts durch, sondern benutze stattdessen einen Radiergummi. +Es gibt \textbf{genau eine} richtige Antwort pro Frage. +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/de/header_2.tex b/data/de/header_2.tex new file mode 100644 index 0000000..7e98374 --- /dev/null +++ b/data/de/header_2.tex @@ -0,0 +1,12 @@ + +\newpage + +\subsection*{Zweiter Teil, Wahr/Falsch-Fragen} + +\noindent +Kreuze bitte für jede der folgenden Fragen die Box WAHR an, wenn die Aussage \textbf{immer korrekt} ist, oder die Box FALSCH, wenn sie \textbf{nicht immer korrekt} ist, d.h. wenn die Aussage +manchmal falsch ist. Streiche nichts durch, sondern benutze stattdessen einen Radiergummi. +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/de/question-text.tex b/data/de/question-text.tex new file mode 100644 index 0000000..ee7380b --- /dev/null +++ b/data/de/question-text.tex @@ -0,0 +1,11 @@ +%% Change Question text - FRENCH + +\renewcommand{\AMCbeginQuestion}[2]{\QuestionText{#1}} +\def\QuestionText{\TEXT} +\def\TEXT#1{\vspace{\AMCformVSpace}\par {\bf Frage #1 :} } +\def\NOTEXT#1{ } + +%% little text can be written in the marking area to tell the students not to tick these boxes, +\def\AMCotextReserved{\emph{Hier nicht schreiben.}} + +%% End of file \ No newline at end of file diff --git a/data/de/true-false.tex b/data/de/true-false.tex new file mode 100644 index 0000000..c06d683 --- /dev/null +++ b/data/de/true-false.tex @@ -0,0 +1,23 @@ +%% TRUE / FALSE commands - GERMAN + +\AMCtext{draft}{ENTWURF} + +\newcommand{\TRUE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \bonne{WAHR} + \mauvaise{FALSCH} + \end{choiceshoriz} +\medskip +} + +\newcommand{\FALSE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \mauvaise{WAHR} + \bonne{FALSCH} + \end{choiceshoriz} +\medskip +} + +%% End of file \ No newline at end of file diff --git a/data/en/first_page.tex b/data/en/first_page.tex new file mode 100644 index 0000000..4779243 --- /dev/null +++ b/data/en/first_page.tex @@ -0,0 +1,55 @@ +% First page - ENGLISH + +\noindent +\begin{minipage}[c]{350pt} + \bf \prof~-~\ExamName~-~\SECTION \\ + ~ \\ + \bf \ExamDate~-~duration : \Time +\end{minipage} +\begin{minipage}[c]{100pt} + \includegraphics[scale=0.24]{Logo_EPFL} +\end{minipage} + +\vfill +\begin{minipage}[c]{\textwidth} +\noindent +\hfill\fontsize{100}{120}{\selectfont{\ID{}}}\hfill +\vspace{1cm} +\par +\noindent +\hfill\fontsize{40}{48}{\selectfont{\NOM{}}}\hfill~ +\end{minipage} +\vfill + +\noindent +\namefield{SCIPER: {\Large \bf \SCIPER{} }} \\ +~ \\ +\noindent +\textbf{Do not turn the page before the start of the exam.} +\textbf{This document is double-sided, has \totalPages\ pages, the last ones possibly blank.} +\textbf{Do not unstaple.} + +\medskip +\begin{itemize} +\item[-] Place your student card on your table. +\item[-] \textbf{No other paper materials} are allowed to be used during the exam. +\item[-] Using a \textbf{calculator} or any electronic device is not permitted during the exam. +\item[-] For the \textbf{multiple choice} questions, we give + \begin{itemize} + \item [$+3$] points if your answer is correct, + \item [$0$ ] points if you give no answer or more than one, + \item [$-1$] points if your answer is incorrect. + \end{itemize} +\item[-] For the \textbf{true/false} questions, we give + \begin{itemize} + \item [$+1$] points if your answer is correct, + \item [$0$ ] points if you give no answer or more than one, + \item [$-1$] points if your answer is incorrect. + \end{itemize} +\item[-] \textbf{Use a pencil} and clearly erase with an eraser if necessary. +\item[-] Observe these guidelines when \textbf{recording your answers}: +\end{itemize} + +\hfill \includegraphics[scale=0.2]{good_bad_square} \hfill + +% End of first page diff --git a/data/en/header_1.tex b/data/en/header_1.tex new file mode 100644 index 0000000..a70fd0a --- /dev/null +++ b/data/en/header_1.tex @@ -0,0 +1,11 @@ + +\newpage + +\subsection*{First part: multiple choice questions} + +\noindent +For each question, cross the box corresponding to the correct answer. Each question has \textbf{exactly one} correct answer. +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/en/header_2.tex b/data/en/header_2.tex new file mode 100644 index 0000000..4874c14 --- /dev/null +++ b/data/en/header_2.tex @@ -0,0 +1,11 @@ + +\newpage + +\subsection*{Second part, true/false questions} + +\noindent +For each question, cross the box (without erasing) TRUE if the statement is \textbf{always true} and the box FALSE if it is \textbf{not always true} (i.e., it is sometimes false). +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/en/question-text.tex b/data/en/question-text.tex new file mode 100644 index 0000000..7f453c2 --- /dev/null +++ b/data/en/question-text.tex @@ -0,0 +1,12 @@ +%% Change Question text - FRENCH + +\renewcommand{\AMCbeginQuestion}[2]{\QuestionText{#1}} +\def\QuestionText{\TEXT} +\def\TEXT#1{\vspace{\AMCformVSpace}\par {\bf Question #1 :} } +\def\NOTEXT#1{ } + + +%% little text can be written in the marking area to tell the students not to tick these boxes, +\def\AMCotextReserved{\emph{Do not write here.}} + +%% End of file \ No newline at end of file diff --git a/data/en/true-false.tex b/data/en/true-false.tex new file mode 100644 index 0000000..9dbb04a --- /dev/null +++ b/data/en/true-false.tex @@ -0,0 +1,21 @@ +%% TRUE / FALSE commands - ENGLISH + +\newcommand{\TRUE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \bonne{TRUE} + \mauvaise{FALSE} + \end{choiceshoriz} +\medskip +} + +\newcommand{\FALSE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \mauvaise{TRUE} + \bonne{FALSE} + \end{choiceshoriz} +\medskip +} + +%% End of file \ No newline at end of file diff --git a/data/fr/first_page.tex b/data/fr/first_page.tex new file mode 100644 index 0000000..d4880f8 --- /dev/null +++ b/data/fr/first_page.tex @@ -0,0 +1,55 @@ +% First page - FRENCH + +\noindent +\begin{minipage}[c]{350pt} + \bf \prof~-~\ExamName~-~\SECTION \\ + ~ \\ + \bf \ExamDate~-~durée : \Time +\end{minipage} +\begin{minipage}[c]{100pt} + \includegraphics[scale=0.24]{Logo_EPFL} +\end{minipage} + +\vfill +\begin{minipage}[c]{\textwidth} +\noindent +\hfill\fontsize{100}{120}{\selectfont{\ID{}}}\hfill +\vspace{1cm} +\par +\noindent +\hfill\fontsize{40}{48}{\selectfont{\NOM{}}}\hfill~ +\end{minipage} +\vfill + +\noindent +\namefield{SCIPER: {\Large \bf \SCIPER{} }} \\ +~ \\ +\noindent +\textbf{Attendez le d\'ebut de l'\'epreuve avant de tourner la page.} +\textbf{Ce document est imprim\'e recto-verso, il contient \totalPages\ pages, les derni\`eres pouvant \^etre vides.} +\textbf{Ne pas d\'egrafer.} + +\medskip +\begin{itemize} +\item[-] Posez votre carte d'\'etudiant sur la table. +\item[-] \textbf{Aucun} document n'est autoris\'{e}. +\item[-] L'utilisation d'une \textbf{calculatrice} et de tout outil \'electronique est interdite pendant l'\'epreuve. +\item[-] Pour les questions \`a \textbf{choix multiple}, on comptera: + \begin{itemize} + \item [$+3$] points si la r\'eponse est correcte, + \item [0] point si la question n'est pas r\'epondue ou s'il y a plusieurs croix, + \item [$-1$] point si la r\'eponse est incorrecte. + \end{itemize} +\item [-]Pour les questions de type \textbf{vrai-faux}, on comptera: + \begin{itemize} + \item [$+1$] point si la r\'eponse est correcte, + \item [0] point si la question n'est pas r\'epondue ou s'il y a plusieurs croix, + \item [$-1$] point si la r\'eponse est incorrecte. + \end{itemize} +\item[-] Utilisez un \textbf{crayon} et effacez proprement avec une \textbf{gomme} si n\'ecessaire. +\item[-] Respectez les consignes suivantes pour \textbf{marquer vos réponses} : +\end{itemize} + +\hfill \includegraphics[scale=0.2]{good_bad_square} \hfill + +%% End of first page diff --git a/data/fr/header_1.tex b/data/fr/header_1.tex new file mode 100644 index 0000000..3a543f4 --- /dev/null +++ b/data/fr/header_1.tex @@ -0,0 +1,11 @@ + +\newpage + +\subsection*{Premi\`ere partie, questions \`a choix multiple} + +\noindent +Pour chaque question mettre une croix dans la case correspondante \`a la r\'eponse correcte sans faire de ratures. Il n'y a qu'\textbf{une seule} r\'{e}ponse correcte par question. +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/fr/header_2.tex b/data/fr/header_2.tex new file mode 100644 index 0000000..b20cd05 --- /dev/null +++ b/data/fr/header_2.tex @@ -0,0 +1,11 @@ + +\newpage + +\subsection*{Deuxi\`eme partie, questions du type Vrai ou Faux} + +\noindent +Pour chaque question, mettre une croix (sans faire de ratures) dans la case VRAI si l'affirmation est \textbf{toujours vraie} ou dans la case FAUX si elle \textbf{n'est pas toujours vraie} (c'est-\`a-dire, si elle est parfois fausse). +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip \ No newline at end of file diff --git a/data/fr/question-text.tex b/data/fr/question-text.tex new file mode 100644 index 0000000..124d80a --- /dev/null +++ b/data/fr/question-text.tex @@ -0,0 +1,12 @@ +%% Change Question text - FRENCH + +\renewcommand{\AMCbeginQuestion}[2]{\QuestionText{#1}} +\def\QuestionText{\TEXT} +\def\TEXT#1{\vspace{\AMCformVSpace}\par {\bf Question #1 :} } +\def\NOTEXT#1{ } + + +%% little text can be written in the marking area to tell the students not to tick these boxes, +\def\AMCotextReserved{\emph{R\'eserv\'e au correcteur}} + +%% End of file \ No newline at end of file diff --git a/data/fr/true-false.tex b/data/fr/true-false.tex new file mode 100644 index 0000000..753487d --- /dev/null +++ b/data/fr/true-false.tex @@ -0,0 +1,21 @@ +%% TRUE / FALSE commands - FRENCH + +\newcommand{\TRUE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \bonne{VRAI} + \mauvaise{FAUX} + \end{choiceshoriz} +\medskip +} + +\newcommand{\FALSE}{ + \bareme{b=1,m=-1} + \begin{choiceshoriz}[o] + \mauvaise{VRAI} + \bonne{FAUX} + \end{choiceshoriz} +\medskip +} + +%% End of file \ No newline at end of file diff --git a/data/list/de/end_tab.tex b/data/list/de/end_tab.tex new file mode 100644 index 0000000..dc2e4dd --- /dev/null +++ b/data/list/de/end_tab.tex @@ -0,0 +1,2 @@ +\hline +\end{tabularx} diff --git a/data/list/de/list.tex b/data/list/de/list.tex new file mode 100644 index 0000000..b7bd967 --- /dev/null +++ b/data/list/de/list.tex @@ -0,0 +1,24 @@ +\documentclass[a4paper,10pt]{report} +\usepackage[utf8]{inputenc} +\usepackage[table,xcdraw]{xcolor} +\usepackage{tabularx} +\usepackage{lastpage} +\usepackage{fancyhdr} +\usepackage[textwidth=17cm,textheight=22cm]{geometry} + +\pagestyle{fancy} +\renewcommand{\headrulewidth}{0pt} +\chead{#PROF_NAME# - #EXAM# - #DATE#} +\cfoot{\textit{\small{Page \thepage\ of \pageref{LastPage}}}} + +% Title Page +\title{Liste der zur Pr\"ufung registrierten Sch\"uler \\ #PROF_NAME#} +\author{#EXAM#} +\date{#DATE#} + +\begin{document} +\maketitle + +\input{./rows.tex} + +\end{document} diff --git a/data/list/de/start_tab.tex b/data/list/de/start_tab.tex new file mode 100644 index 0000000..4dd28b1 --- /dev/null +++ b/data/list/de/start_tab.tex @@ -0,0 +1,12 @@ +\noindent +\begin{tabularx}{\textwidth}{|r|r|l|l|l|l|X|} +\hline + \rowcolor[HTML]{E2001A} + {\color[HTML]{FFFFFF} \texttt{\#}} & + {\color[HTML]{FFFFFF} \texttt{SCIPER}} & + {\color[HTML]{FFFFFF} \texttt{Section}} & + {\color[HTML]{FFFFFF} \texttt{Name}} & + {\color[HTML]{FFFFFF} \texttt{~1~}}& + {\color[HTML]{FFFFFF} \texttt{~2~}}& \\ +\hline +\hline \ No newline at end of file diff --git a/data/list/en/end_tab.tex b/data/list/en/end_tab.tex new file mode 100644 index 0000000..dc2e4dd --- /dev/null +++ b/data/list/en/end_tab.tex @@ -0,0 +1,2 @@ +\hline +\end{tabularx} diff --git a/data/list/en/list.tex b/data/list/en/list.tex new file mode 100644 index 0000000..9ddfa02 --- /dev/null +++ b/data/list/en/list.tex @@ -0,0 +1,24 @@ +\documentclass[a4paper,10pt]{report} +\usepackage[utf8]{inputenc} +\usepackage[table,xcdraw]{xcolor} +\usepackage{tabularx} +\usepackage{lastpage} +\usepackage{fancyhdr} +\usepackage[textwidth=17cm,textheight=22cm]{geometry} + +\pagestyle{fancy} +\renewcommand{\headrulewidth}{0pt} +\chead{#PROF_NAME# - #EXAM# - #DATE#} +\cfoot{\textit{\small{Page \thepage\ of \pageref{LastPage}}}} + +% Title Page +\title{Students list \\ #PROF_NAME#} +\author{#EXAM#} +\date{#DATE#} + +\begin{document} +\maketitle + +\input{./rows.tex} + +\end{document} \ No newline at end of file diff --git a/data/list/en/start_tab.tex b/data/list/en/start_tab.tex new file mode 100644 index 0000000..4dd28b1 --- /dev/null +++ b/data/list/en/start_tab.tex @@ -0,0 +1,12 @@ +\noindent +\begin{tabularx}{\textwidth}{|r|r|l|l|l|l|X|} +\hline + \rowcolor[HTML]{E2001A} + {\color[HTML]{FFFFFF} \texttt{\#}} & + {\color[HTML]{FFFFFF} \texttt{SCIPER}} & + {\color[HTML]{FFFFFF} \texttt{Section}} & + {\color[HTML]{FFFFFF} \texttt{Name}} & + {\color[HTML]{FFFFFF} \texttt{~1~}}& + {\color[HTML]{FFFFFF} \texttt{~2~}}& \\ +\hline +\hline \ No newline at end of file diff --git a/data/list/fr/end_tab.tex b/data/list/fr/end_tab.tex new file mode 100644 index 0000000..dc2e4dd --- /dev/null +++ b/data/list/fr/end_tab.tex @@ -0,0 +1,2 @@ +\hline +\end{tabularx} diff --git a/data/list/fr/list.tex b/data/list/fr/list.tex new file mode 100644 index 0000000..e06b0e0 --- /dev/null +++ b/data/list/fr/list.tex @@ -0,0 +1,24 @@ +\documentclass[a4paper,10pt]{report} +\usepackage[utf8]{inputenc} +\usepackage[table,xcdraw]{xcolor} +\usepackage{tabularx} +\usepackage{lastpage} +\usepackage{fancyhdr} +\usepackage[textwidth=17cm,textheight=22cm]{geometry} + +\pagestyle{fancy} +\renewcommand{\headrulewidth}{0pt} +\chead{#PROF_NAME# - #EXAM# - #DATE#} +\cfoot{\textit{\small{Page \thepage\ of \pageref{LastPage}}}} + +% Title Page +\title{Liste des \'etudiants inscrits \\ #PROF_NAME#} +\author{#EXAM#} +\date{#DATE#} + +\begin{document} +\maketitle + +\input{./rows.tex} + +\end{document} \ No newline at end of file diff --git a/data/list/fr/start_tab.tex b/data/list/fr/start_tab.tex new file mode 100644 index 0000000..8679860 --- /dev/null +++ b/data/list/fr/start_tab.tex @@ -0,0 +1,12 @@ +\noindent +\begin{tabularx}{\textwidth}{|r|r|l|l|l|l|X|} +\hline + \rowcolor[HTML]{E2001A} + {\color[HTML]{FFFFFF} \texttt{\#}} & + {\color[HTML]{FFFFFF} \texttt{SCIPER}} & + {\color[HTML]{FFFFFF} \texttt{Section}} & + {\color[HTML]{FFFFFF} \texttt{Nom}} & + {\color[HTML]{FFFFFF} \texttt{~1~}}& + {\color[HTML]{FFFFFF} \texttt{~2~}}& \\ +\hline +\hline \ No newline at end of file diff --git a/data/media/Logo_EPFL.png b/data/media/Logo_EPFL.png new file mode 100644 index 0000000..929e1cd Binary files /dev/null and b/data/media/Logo_EPFL.png differ diff --git a/data/media/good_bad_square.png b/data/media/good_bad_square.png new file mode 100644 index 0000000..6d272b2 Binary files /dev/null and b/data/media/good_bad_square.png differ diff --git a/data/skel/workspace/exams/skel/exam.conf b/data/skel/workspace/exams/skel/exam.conf new file mode 100644 index 0000000..cbbc2d6 --- /dev/null +++ b/data/skel/workspace/exams/skel/exam.conf @@ -0,0 +1,6 @@ +PROF_NAME:Ens: M. Skeleton +EXAM:Analyse I +DURATION:3 heures +DATE:12 janvier 2015 +LANG:fr +TOTAL_PAGES:12 diff --git a/data/skel/workspace/exams/skel/extra_packages.tex b/data/skel/workspace/exams/skel/extra_packages.tex new file mode 100644 index 0000000..7350215 --- /dev/null +++ b/data/skel/workspace/exams/skel/extra_packages.tex @@ -0,0 +1,27 @@ +%% Extra packages + +%% CSV file access +\usepackage{csvsimple} + +%% Multicolumn formatting +\usepackage{multicol} + +%% Math packages +\usepackage{graphicx} +\usepackage{amsmath} +\usepackage{mathrsfs} +\usepackage{euscript} +\usepackage{epsfig} +\usepackage{ifthen} +\usepackage{amsfonts} +\usepackage{amssymb} + +%% Graphics +\usepackage{graphicx} +\graphicspath{ {media/} } + +%% Specific commands for Skel teacher (EXAMPLE) +\newcommand{\R}{\mathbb{R}} +\newcommand{\Q}{\mathbb{Q}} +\newcommand{\N}{\mathbb{N}} +\newcommand{\Z}{\mathbb{Z}} diff --git a/data/skel/workspace/exams/skel/extra_section.tex b/data/skel/workspace/exams/skel/extra_section.tex new file mode 100644 index 0000000..15cf964 --- /dev/null +++ b/data/skel/workspace/exams/skel/extra_section.tex @@ -0,0 +1,168 @@ + +\newpage + +\subsection*{Troisi\`eme partie, questions de type ouvert} + +\noindent +\begin{itemize} +\item Ceci est la plus petite partie de l'examen (elle compte {\bf 16 points}). +\item En g\'en\'eral, il ne s'agit pas de questions \`a choix multiple ; toutefois, il faut imp\'erativement que vous mettiez vos r\'eponses dans les cases pr\'evues \`a cet effet ! +\item Vous pouvez utiliser les feuilles (recto-verso) comme brouillon. Seules les r\'eponses dans les cases sont valides ! +\item Il est possible que parfois plusieurs r\'eponses soient valables ! +\item Laisser libre les cases \`a cocher : elles sont r\'eserv\'ees au correcteur. +\end{itemize} + +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +%% =============================== +\renewcommand{\correctorChoices}{ + \correctchoice[0]{}\scoring{b=0} + \correctchoice[Y]{}\scoring{b=0.5} + \correctchoice[X]{}\scoring{b=1} + \correctchoice[W]{}\scoring{b=1.5} + \correctchoice[V]{\qquad}\scoring{b=2} +} +\correctorTwo{q-sem-01}{ \textit{Cette question est notée sur 4 points. (2 fois 2 points)} } +%\renewcommand{\AMCbeginQuestion}[2]{\QuestionText{#1}} +%\def\QuestionText{\TEXT} +%\def\TEXT#1{} +%\def\NOTEXT#1{} +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +\noindent +\begin{enumerate} +\item Existe-t-il des fonctions surjectives $f: A\to B$ ? +\begin{table}[h] +\Large +\centering +\begin{tabular}{|c|c|c|c|} +\hline +$A$ & $B$ & Oui & Non \\ +\hline +$\{1,2\}$ & $\{1,2,3\}$ && \\ +\hline +$\{1,2,3\}$ & $\{1,2\}$ && \\ +\hline +$\N$ & $\Z$ && \\ +\hline +$]0,1[$ & $\R$ && \\ +\hline +\end{tabular} +\end{table} + +%% =============================== +\item Existe-t-il des fonctions surjectives \emph{et continues} $f: A \to B$ ? +\begin{table}[h] +\Large +\centering +\begin{tabular}{|c|c|c|c|} +\hline +$A$ & $B$ & Oui & Non \\ +\hline +$[0,1]$ & $[-1,2]$ && \\ +\hline +$[-1,1]$ & $\R$ && \\ +\hline +$]-1,1[$ & $\R\backslash \{0\}$ && \\ +\hline +$[-2,0] \cup [1,4]$ & $[-1,0[ \cup [1, 2]$ && \\ +\hline +\end{tabular} +\end{table} +\end{enumerate} +%% =============================== +\newpage +\correctorTwo{q-sem-02}{ \textit{Cette question est notée sur 4 points.} } +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +\noindent +Soit $A$ un ensemble ouvert et born\'e et $B$ un ensemble ferm\'{e} dans $\R$. On suppose que $A \cap B \neq \emptyset$. +Les sous-ensembles de $\R$ suivants sont-ils ouverts, ferm\'es, born\'es ? Justifier la r\'eponse ! +\begin{table}[h] +\Large +\centering +\begin{tabular}{|c|c|c|c|} +\hline +Ensemble & ouvert & ferm\'e & born\'e \\ +\hline +$A\setminus B$ &&& \\ +\hline +$B\setminus A$ &&& \\ +\hline +\end{tabular} +\end{table} +\\ +\noindent +\textit{Justification :} +\\ +\OpenBox{8cm} +\bigskip + + +%% =============================== +\correctorTwo{q-sem-03}{ \textit{Cette question est notée sur 4 points.} } +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +\noindent +Calculer les rayons de convergence des s\'eries enti\`eres suivantes. +\begin{align*} +i)\quad \sum_{k=0}^\infty k (x-1)^k && ii)\quad \sum_{k=0}^\infty \frac1{2^k}\, x^k && iii)\quad \sum_{k=0}^\infty k! x^k +\end{align*} +\begin{table}[h] +\Large \centering +\begin{tabular}{|c|c|} +\hline +S\'erie & Rayon de convergence \\ +\hline +$i)$ & \\ +\hline +$ii)$ & \\ +\hline +$iii)$ & \\ +\hline +\end{tabular} +\end{table} +%% =============================== +\newpage +\correctorTwo{q-sem-04}{ \textit{Cette question est notée sur 4 points. (2 fois 2 points)} } +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +\noindent +\begin{enumerate} +\item L'assertion suivante est-elle vraie ou fausse ? +\newline Soient $f: [a,b]\to \R$ et $g: [a,b]\to \R$ deux fonctions escaliers relatives aux +partitions $P, Q\subset [a,b]$ respectivement. Alors, le produit $fg$ est encore une fonction escalier. +\begin{table}[h] +\Large +\centering +\begin{tabular}{|c|c|} +\hline +Vraie & Fausse \\ +\hline +& \\ +\hline +\end{tabular} +\end{table} + +\item Si l'assertion est vraie, la fonction $fg$ est une fonction escalier relative \`a quelle partition? +\newline Si l'assertion est fausse, justifier la r\'eponse ! +\end{enumerate} +\bigskip + + +%% Leave at least 2 empty lines after the \bigskip +\noindent +\textit{Partition ou justification :} +\\ +\OpenBox{14cm} + diff --git a/data/skel/workspace/exams/skel/sections.conf b/data/skel/workspace/exams/skel/sections.conf new file mode 100644 index 0000000..9b81a5c --- /dev/null +++ b/data/skel/workspace/exams/skel/sections.conf @@ -0,0 +1,2 @@ +mc-common_01.tex,mc-common_02.tex +tf-common_01.tex,tf-common_02.tex diff --git a/data/skel/workspace/exams/skel/students.csv b/data/skel/workspace/exams/skel/students.csv new file mode 100644 index 0000000..0c536c1 --- /dev/null +++ b/data/skel/workspace/exams/skel/students.csv @@ -0,0 +1,5 @@ +ID,NOM,SCIPER,SECTION,EMAIL +1,Mick Jagger,XXXXX1,XYZ,mick.jagger@epfl.ch +2,Keith Richards,XXXXX2,XYZ,keith.richards@epfl.ch +3,Charlie Watts,XXXXX3,XYZ,charlie.watts@epfl.ch +4,Ronnie Wood,XXXXX4,XYZ,ronnie.wood@epfl.ch diff --git a/data/skel/workspace/questions/fr/mc-common_01.tex b/data/skel/workspace/questions/fr/mc-common_01.tex new file mode 100644 index 0000000..e016c9c --- /dev/null +++ b/data/skel/workspace/questions/fr/mc-common_01.tex @@ -0,0 +1,12 @@ +\begin{question}{q:mc-01} +Soit le sous-ensemble $E\subset \mathbb{R}$ d\'efini par +$\displaystyle E=\Bigg\{2\left(1+\frac 1 n\right) +^{\hspace{-1mm}n}: n\in \mathbb{N}\setminus\{0\}\Bigg\}$. +\\Alors +\begin{reponses} +\bonne{$10$ est un majorant de $E$} +\mauvaise{le minimum de $E$ est $2$} +\mauvaise{$E$ est ferm\'e} +\mauvaise{le supremum de $E$ appartient \`a $E$} +\end{reponses} +\end{question} diff --git a/data/skel/workspace/questions/fr/mc-common_02.tex b/data/skel/workspace/questions/fr/mc-common_02.tex new file mode 100644 index 0000000..b9ebf01 --- /dev/null +++ b/data/skel/workspace/questions/fr/mc-common_02.tex @@ -0,0 +1,10 @@ +\begin{question}{q:mc-02} +L'\'equation $~z^{-1}=\overline z\,$, o\`u $\overline z $ +est le complexe conjugu\'e de $z$, admet +\begin{reponses} + \mauvaise{exactement une solution dans $\mathbb{C}$} + \mauvaise{exactement deux solutions dans $\mathbb{C}$} + \mauvaise{aucune solution dans $\mathbb{C}$} + \bonne{une infinit\'e de solutions dans $\mathbb{C}$} +\end{reponses} +\end{question} diff --git a/data/skel/workspace/questions/fr/tf-common_01.tex b/data/skel/workspace/questions/fr/tf-common_01.tex new file mode 100644 index 0000000..d5c395c --- /dev/null +++ b/data/skel/workspace/questions/fr/tf-common_01.tex @@ -0,0 +1,5 @@ +\begin{question}{q:tf-01} +Soit $A$ un sous-ensemble born\'e et non vide de $\mathbb{R}$.\\ +Alors $\,\inf A\in A\,$ et $\,\sup A \in A\,$. +\FALSE +\end{question} diff --git a/data/skel/workspace/questions/fr/tf-common_02.tex b/data/skel/workspace/questions/fr/tf-common_02.tex new file mode 100644 index 0000000..7746a0a --- /dev/null +++ b/data/skel/workspace/questions/fr/tf-common_02.tex @@ -0,0 +1,8 @@ +\begin{question}{q:tf-02} +Soient $f\colon \mathbb{R}\rightarrow \mathbb{R}$ et +$g\colon\mathbb{R}\rightarrow \mathbb{R}$ deux fonctions d\'efinies sur tout +$\mathbb{R}$. Si +% $f$ et +$f\circ g$ est injective, alors $g$ est injective. +\TRUE +\end{question} diff --git a/install/install_bins.sh b/install/install_bins.sh new file mode 100755 index 0000000..6fd2357 --- /dev/null +++ b/install/install_bins.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Set SNAME, SDIR and SPATH +if [ -h $0 ]; then SNAME=$(readlink $0); else SNAME=$0; fi +SDIR=$(dirname $SNAME) +if [[ "$SDIR" =~ ^/ ]]; then SPATH=$SDIR; else SPATH=$(realpath $(pwd)/$SDIR); fi + +# Set DIR_LIB +DIR_LIB=$(realpath $SPATH/../lib/bash) + +# Source libs +source $DIR_LIB/colors.sh +source $DIR_LIB/io.sh + +# Set DESTINATION + +if [ -n "$1" ]; then + DESTINATION="$(realpath $1)" +else + DESTINATION=$(realpath ${HOME}/bin) +fi +if [ ! -d $DESTINATION/ ]; then + error_echo "'$DESTINATION' is not a valid directory." + exit 1 +fi +info_echo "Using '$DESTINATION' as binary directory." + +function link_to_binaries() { + for bin in $(echo $BINS); do + if [ -r $DESTINATION/$bin ]; then + if [ -h $DESTINATION/$bin ]; then + OLD=$(realpath $DESTINATION/$bin) + if [ "$OLD" == "$DIR_BIN/$bin" ]; then + color_echo_n "Command '$bin' already exists... " + else + color_echo_n "Moving command '$bin' from '$OLD' to '$DIR_BIN/$bin'... " + rm $DESTINATION/$bin + ln -s $DIR_BIN/$bin $DESTINATION/$bin + + + fi + else + error_echo "A similar command already exists: '$OLD'" + exit 2 + fi + else + color_echo_n "Installing command '$bin' to '$DESTINATION/'... " + ln -s $DIR_BIN/$bin $DESTINATION/$bin + fi + [ -h $DESTINATION/$bin ] + check_rc_echo $? + done +} + +DIR_BIN=$(realpath $SPATH/../bin) +BINS='bamc' +link_to_binaries + +DIR_BIN=$(realpath $SPATH/../local/epfl) +BINS='search-epfl' +link_to_binaries + +# Done +warning_echo "Make sure '$DESTINATION' is in your \$PATH variable."; + +# RIP +exit 0 diff --git a/lib/bash/bamc_actions.sh b/lib/bash/bamc_actions.sh new file mode 100644 index 0000000..168278b --- /dev/null +++ b/lib/bash/bamc_actions.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +function tell_if_verbose() { + if [ "$(get_param_value 'mode')" = 'verbose' ]; then echo "$@"; fi +} + +function do_list() { +} + +function check_project_exists() { +} + +function get_teachers() { +} + +function do_test() { +} + +function check_file_exists() { +} + +function get_lang() { +} + +function import_student_file() { +} + +function get_expected_page_per_exam() { +} + +function customize_tex_files() { +} + +function build_sections() { +} + +function do_sample() { +} + +function do_check() { +} + +function do_blank() { +} + +function do_pdf() { +} + +function build_amc_project() { +} + +function add_media_files() { +} + +function do_project() { +} + +function do_clean() { +} diff --git a/lib/bash/bamc_configuration.sh b/lib/bash/bamc_configuration.sh new file mode 100644 index 0000000..1e45e4f --- /dev/null +++ b/lib/bash/bamc_configuration.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +DIR_CONF=$SPATH/../conf/ +FILE_CONF=$DIR_CONF/$(basename $0).conf + +if [ ! -r $FILE_CONF ]; then + echo "Could not read configuration file ($FILE_CONF)" + exit 1; +fi +source $FILE_CONF + +# EOF diff --git a/lib/bash/bamc_help.sh b/lib/bash/bamc_help.sh new file mode 100644 index 0000000..bb651a0 --- /dev/null +++ b/lib/bash/bamc_help.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +function action_help() { + local item=$1 # Useless... + echo -e "${DarkGreen}USAGE:${NoColor} "$(basename $0)" [-p|--params p1=v1,p2-v2,...] [-o|--only item1,item2,...] action1 [action2 ...] + +e.g.: $(basename $0) --params workspace=~/AMC_Workspace init +e.g.: $(basename $0) show + +Parameters: +----------- +${DarkGreen}debug${NoColor} (GENERAL) print debugging messages +${DarkGreen}force${NoColor} (GENERAL) do not ask for user confirmation +${DarkGreen}workspace|w${NoColor} (GENERAL) name a specific workspace +${DarkGreen}exam|e${NoColor} (GENERAL) name a specific exam within the workspace + +Items: +----------- +${DarkGreen}item1${NoColor} (GENERAL) items are the existing exams (as printed by the 'list' action) + +Actions: +-------- +${DarkGreen}init${NoColor} (WORKSPACE) create a new workspace in the current directory + +${DarkGreen}add-exam${NoColor} (EXAMS) add a new exam in the current workspace +${DarkGreen}list${NoColor} (EXAMS) print list of exams in the current workspace + +${DarkGreen}clean${NoColor} (PROJECTS) remove project and output directories +${DarkGreen}project${NoColor} (PROJECTS) (re)build the AMC project + +${DarkGreen}check-latex${NoColor} (PROJECTS) check LaTeX syntax +${DarkGreen}check-cvs${NoColor} (PROJECTS) check CSV files syntax +${DarkGreen}check-amc${NoColor} (PROJECTS) check AMC specific syntax +${DarkGreen}check${NoColor} (PROJECTS) do all checks + +${DarkGreen}blank${NoColor} (PDF) build a blank (anonymous) exam +${DarkGreen}catalog${NoColor} (PDF) build a catalog of questions +${DarkGreen}sample${NoColor} (PDF) build a sample (4 exams) exam +${DarkGreen}exam${NoColor} (PDF) build PDF exam" + return 0 +} + diff --git a/lib/bash/bamc_workspace.sh b/lib/bash/bamc_workspace.sh new file mode 100644 index 0000000..3b6e013 --- /dev/null +++ b/lib/bash/bamc_workspace.sh @@ -0,0 +1,130 @@ +#/bin/bash + +function get_workspace() { + is_set 'workspace' + if [ $? -eq 1 ]; then DIR_WORKSPACE=$(get_parameter 'workspace'); fi + is_set 'w' + if [ $? -eq 1 ]; then DIR_WORKSPACE=$(get_parameter 'w'); fi + + if [ -z $DIR_WORKSPACE ]; then DIR_WORKSPACE="."; fi + + if [[ ! "$DIR_WORKSPACE" =~ ^/ ]]; then DIR_WORKSPACE=$(echo "$(pwd)/$DIR_WORKSPACE" | sed 's?/\.$??'); fi +} + +function check_workspace() { + if [ -r "$1/$FILE_WORKSPACE_ROOT" ]; then + return 1 + else + return 0 + fi +} + +function locate_nearest_workspace() { + local next_dir=$DIR_WORKSPACE + if [ -d $next_dir ]; then next_dir=$(realpath $next_dir); fi + for i in $(echo 1 2 3 4); do + check_workspace $next_dir + if [ $? -eq 1 ]; then + # Workspace found + echo $next_dir + return + else + next_dir=$next_dir/../ + if [ -d $next_dir ]; then next_dir=$(realpath $next_dir); fi + fi + done + echo "~none~" + return +} + +function assert_workspace() { + get_workspace + check_workspace $DIR_WORKSPACE + if [ $? -eq 1 ]; then return 0; fi + DIR_WORKSPACE=$(locate_nearest_workspace) + if [ "$DIR_WORKSPACE" == "~none~" ]; then + error_echo "No valid workspace found around here..." + return 1 + fi + return 0 +} + +function action_list() { + # Check workspace + assert_workspace; if [ $? -eq 1 ]; then return 1; fi + + # List exams + for exam in $(find $DIR_WORKSPACE/$DIR_EXAMS -mindepth 1 -maxdepth 1 -type d -exec basename {} \;); do + echo $exam + done + + # Done + return 0 +} + +function action_add-exam() { + # Check workspace + assert_workspace; if [ $? -eq 1 ]; then return 1; fi + + # Set source + DIR_EXAM=$DIR_SKEL/workspace/$DIR_EXAMS/skel + + if [ ! -d $DIR_EXAM ]; then + error_echo "Directory '$DIR_EXAM' not found." + return 1 + fi + + is_set 'exam' + if [ $? -eq 1 ]; then DESTINATION=$(get_parameter 'exam'); fi + is_set 'e' + if [ $? -eq 1 ]; then DESTINATION=$(get_parameter 'e'); fi + + if [ -z "$DESTINATION" ]; then error_echo "No truc provided."; fi + exit 1 + + # Set destination + DESTINATION=$DIR_WORKSPACE/$DIR_EXAMS/$DESTINATION + + if [ -d $DESTINATION ]; then + confirm "Do you want to write in existing exam '$(basename $DESTINATION)' ?" + if [ $? -eq 0 ]; then return 1; fi + fi + + # Copy + color_echo_n "Creating new exam '$(basename $DESTINATION)' from '$DIR_EXAM'... " + cp -rp $DIR_EXAM $DESTINATION + check_rc_echo $? + return $? +} + + +function action_init() { + get_workspace + EXISTING_WORKSPACE=$(locate_nearest_workspace) + if [ "$EXISTING_WORKSPACE" != "~none~" ]; then + error_echo "You cannot create a workspace within a workspace!" + error_echo "Detected workspace location: '$EXISTING_WORKSPACE'" + return 1 + fi + + confirm "Do you want to initialize workspace: '${DIR_WORKSPACE}'?" + if [ $? -eq 0 ]; then return 1; fi + + # Create directory + color_echo_n "Creating directory '${DIR_WORKSPACE}'... " + mkdir -p $DIR_WORKSPACE + check_rc_echo $? + if [ $? -ne 0 ]; then return 1; fi + + color_echo_n "Adding workspace flag... " + touch $DIR_WORKSPACE/$FILE_WORKSPACE_ROOT + check_rc_echo $? + if [ $? -ne 0 ]; then return 1; fi + + color_echo_n "Importing skeleton files... " + cp -rp $DIR_SKEL/workspace/* $DIR_WORKSPACE/ + check_rc_echo $? + if [ $? -ne 0 ]; then return 1; fi + + return 0 +} diff --git a/lib/bash/cmdline_parser.sh b/lib/bash/cmdline_parser.sh new file mode 100644 index 0000000..e787d80 --- /dev/null +++ b/lib/bash/cmdline_parser.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +# Command line parser +# USAGE: [in the calling script] +# 1) set the global variables: $DEFAULT_ITEMS and $DEFAULT_ACTIONS +# 2) set the global variable: $CALLBACK_PREFIX (default: cb_ ) +# 3) implement each the callback: e.g. function cb_list() { ... } to implement the list action +# 4) call: run $@ + +ACTIONS='' +PARAMS='' +ITEMS='' +CALLBACK_PREFIX=${CALLBACK_PREFIX:-cb_} + +function error() { + error_echo $@ +} + +function debug() { + is_set debug + if [ $? -eq 1 ]; then debug_echo $@; fi +} + +function parse_args() { + while [ $# -gt 0 ]; do + case $1 in + "-o"|"--only") + shift + ITEMS=$1 + shift + continue + ;; + "-p"|"--params") + shift + PARAMS=$1 + shift + continue + ;; + *) + if [ -z "$ACTIONS" ]; then ACTIONS=$1; else ACTIONS="$ACTIONS $1"; fi + shift + ;; + esac + done + + if [ -z "$ITEMS" ]; then ITEMS=$(get_default_items); fi + if [ -z "$ITEMS" ]; then ITEMS='~no~items~'; fi + if [ -z "$ACTIONS" ]; then ACTIONS=$(get_default_actions); fi + + debug "actions: '$ACTIONS' | items: '$ITEMS' | parameters: '$PARAMS'" +} + +function get_items() { + echo "$ITEMS" | sed "s/,/\n/g" +} + +function get_actions() { + echo "$ACTIONS" | sed "s/ /\n/g" +} + +function get_params() { + echo "$PARAMS" | sed "s/,/\n/g" +} + +function get_parameter() { + key=$1 + # Key alone + line=$(echo $PARAMS | sed "s/,/\n/g" | grep "^$key$") + if [ -n "$line" ]; then echo "set"; return; fi + # Key + value + line=$(echo $PARAMS | sed "s/,/\n/g" | grep "^$key=" | cut -d '=' -f 2-) + if [ -n "$line" ]; then echo $line; return; fi + # unset + echo "unset" +} + +function is_set() { + param=$1 + value=$(get_parameter $param) + if [ "$value" != "unset" ]; then return 1; else return 0; fi +} + +function run() { + local action + local item + local rc=0 + parse_args $@ + for action in $(get_actions); do + for item in $(get_items); do + debug "Running action '$action' on item '$item'" + action_switch $action $item + rc=$((rc+$?)) + done + + done + return $rc +} + +function get_default_items() { + echo $DEFAULT_ITEMS +} + +function get_default_actions() { + echo $DEFAULT_ACTIONS +} + +function action_switch() { + local action=$1 + local item=$2 + local function_to_call="${CALLBACK_PREFIX}${action}" + local function_exists=$(type "$function_to_call" 2>&1 | grep -c "$function_to_call"' is a function'); + if [ $function_exists -eq 1 ]; then + $function_to_call $item + return $? + else + error "invalid action: $action (not implemented ?)" + return 1 + fi +} + +# EOF diff --git a/lib/bash/colors.sh b/lib/bash/colors.sh new file mode 100644 index 0000000..e27bce2 --- /dev/null +++ b/lib/bash/colors.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +if [ ! -t 1 ]; then return; fi +if [ ! -t 2 ]; then return; fi + +# Define colors +Black="\e[0;30m" +DarkGray="\e[1;30m" +DarkBlue="\e[0;34m" +Blue="\e[1;34m" +DarkGreen="\e[0;32m" +Green="\e[1;32m" +Cyan="\e[0;36m" +LightCyan="\e[1;36m" +DarkRed="\e[0;31m" +Red="\e[1;31m" +Purple="\e[0;35m" +Pink="\e[1;35m" +Brown="\e[0;33m" +Yellow="\e[1;33m" +Gray="\e[0;37m" +White="\e[1;37m" +NoColor="\e[00m" + +COLORS=1; + +function colors() { + echo -e ${Black}Black$NoColor + echo -e ${DarkGray}DarkGray$NoColor + echo -e ${DarkBlue}DarkBlue$NoColor + echo -e ${LightBlue}Blue$NoColor + echo -e ${DarkGreen}DarkGreen$NoColor + echo -e ${Green}Green$NoColor + echo -e ${Cyan}Cyan$NoColor + echo -e ${LightCyan}LightCyan$NoColor + echo -e ${DarkRed}DarkRed$NoColor + echo -e ${Red}Red$NoColor + echo -e ${Purple}Purple$NoColor + echo -e ${Pink}Pink$NoColor + echo -e ${Brown}Brown$NoColor + echo -e ${Yellow}Yellow$NoColor + echo -e ${Gray}Gray$NoColor + echo -e ${White}White$NoColor + echo -e ${NoColor}NoColor$NoColor +} diff --git a/lib/bash/functions.sh b/lib/bash/functions.sh new file mode 100644 index 0000000..b47ad28 --- /dev/null +++ b/lib/bash/functions.sh @@ -0,0 +1,775 @@ +#!/bin/bash + +function get_targets() { + # TARGETS + TARGETS="check:do_check|\ +blank:do_blank|\ +test:do_test|\ +project:do_project|\ +clean:do_clean|\ +sample:do_sample|\ +pdf:do_pdf|\ +all:do_check,do_blank,do_sample,do_pdf,do_list|\ +webdav_catalog:do_webdav_catalog|\ +webdav_sample:do_webdav_sample|\ +webdav_list:do_webdav_list|\ +webdav_pdf:do_webdav_pdf|\ +webdav_all:do_webdav_catalog,do_webdav_sample,do_webdav_list,do_webdav_pdf|\ +list:do_list|\ +help:do_help|\ +scans:do_scans|\ +analyse:do_analyse|\ +associate:do_associate|\ +export:do_export|\ +annotate:do_annotate|\ +mailing:do_mailing" + echo $TARGETS | tr '|' '\n' | grep "^$1:" | cut -d ':' -f 2- | tr ',' ' ' +} + +function parse_arguments() { + if [ $# -lt 1 ]; then set 'test'; fi + + while [ $# -gt 0 ]; do + case "$1" in + '--only'|'-o') + shift + echo teacher_list:$1 | tr ',' ' ' + shift + ;; + '--params'|'-p') + shift + echo parameters:$1 | tr ',' ' ' + shift + ;; + *) + echo target:$(get_targets $1) + shift + ;; + esac + done +} + +function get_param_value() { + local IFS=' ' + for p in $(echo $PARAMETERS | tr ',' ' '); do + echo $p | grep "$1=" | cut -d '=' -f 2 + done +} + +function do_actions() { + ACTIONS=$(parse_arguments $@ | grep "^target:" | cut -d ':' -f 2-) + TEACHERS=$(parse_arguments $@ | grep "^teacher_list:" | cut -d ':' -f 2-) + PARAMETERS=$(parse_arguments $@ | grep "^parameters:" | cut -d ':' -f 2-) + local rc=0 + if [ -z "$ACTIONS" ]; then echo "Nothing to do!"; exit 0; fi + for ACTION in $(echo "$ACTIONS"); + do + tell_if_verbose "Processing : ($ACTION)" + process_action $ACTION + rc=$((rc+$?)) + done + if [ $rc -gt 0 ]; then + echo "RC=$rc" + fi + exit $rc +} + +function tell_if_verbose() { + if [ "$(get_param_value 'mode')" = 'verbose' ]; then echo "$@"; fi +} + +function do_help() { + echo $(basename $0)" [--only teacher,teacher] [--params ask=no,lang=xx] action [action ...] + +Options: +--only, -o: only process the following teachers (comma separated list) +--params, -p: set/force some parameters (e.g. ask=no,mode=verbose,lang=fr) + +Actions: + help: print this help + test: do nothing - usefull for testing with --only ... + + # Exam preparation + project: build AMC project + clean: clean output dirs, projects, etc + sample: build samples PDF (sample) + blank: build blank exam PDF (blank and catalog) + pdf: build final PDF (exam and correction) + list: build exam students list + all: build blank, sample, final and list PDF files + + # Exam publication + webdav_catalog: publish catalog PDF files to \$DIR_WEBDAV_CATALOGS + webdav_sample: publish sample PDF files to \$DIR_WEBDAV_SAMPLES + + + [...]" + #echo $TARGETS | tr '|' '\n' | sed "s/:/ => /g" | sed "s/,/, /g" +} + +function process_action() { + case "$1" in + "do_clean") do_clean;; + "do_project") do_project;; + "do_sample") do_sample;; + "do_pdf") do_pdf;; + "do_help") do_help;; + "do_scans") do_scans;; + "do_analyse") do_analyse;; + "do_associate") do_associate;; + "do_annotate") do_annotate;; + "do_export") do_export;; + "do_mailing") do_mailing;; + "do_test") do_test;; + "do_blank") do_blank;; + "do_check") do_check;; + "do_list") do_list;; + "do_webdav_catalog") do_webdav_catalog;; + "do_webdav_sample") do_webdav_sample;; + "do_webdav_list") do_webdav_list;; + "do_webdav_pdf") do_webdav_pdf;; + *) + echo "Unimplemented action: '$1'" + return 1 + ;; + esac + return $? +} + +function do_list() { + local rc=0 + local lang line id sciper name section IFS i tab FILE list_file + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + if [ ! -d $DIR_TEACHERS/$teacher ]; then + echo "ERROR: [$teacher] teacher not found." + ((rc++)) + else + lang=$(get_lang $teacher) + FILE=$DIR_TEACHERS/$teacher/$FILE_PROF + IFS=$'\n' + cp $DIR_LIST/$lang/list.tex $DIR_EXAMS/$teacher/$FILE_LIST_MAIN + list_file=$DIR_EXAMS/$teacher/$FILE_LIST_MAIN + check_file_exists $list_file + for replace in $(cat $FILE); do + pattern="#$(echo $replace | cut -d ':' -f 1)#" + string=$(echo $replace | cut -d ':' -f 2-) + sed -i "s/$pattern/$string/g" $list_file + done + rm -f $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + i=0 + tab=1 + prev_section='' + for line in $(tail -n +2 $DIR_EXAMS/$teacher/$FILE_STUDENTS); do + ((i++)) + if [ $tab -eq 1 ]; then + cat $DIR_LIST/$lang/$FILE_LIST_TSTART >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + tab=0 + fi + # id field + id=$(echo $line | cut -d ',' -f 1) + id=$(printf "%3s" $id) + id=$(echo $id | sed "s/ /~/g") + + name=$(echo $line | cut -d ',' -f 2) + if [ $(echo -n $name | wc -c) -gt $LIST_MAX_CHAR ]; then name=$(echo $name | cut -c 1-$LIST_MAX_CHAR)"…"; fi + sciper=$(echo $line | cut -d ',' -f 3) + section=$(echo $line | cut -d ',' -f 4) + comment=$(echo $line | cut -d ',' -f 10) + if [ "$comment" == "n/a" ]; then comment=''; fi + if [ "$section" == "XXX" ]; then section=''; name="SCIPER:"; fi + name=$(printf "%-${LIST_MAX_CHAR}s" $name) + name=$(echo $name | sed "s/ /~/g") + + if [ "$section" != "$prev_section" ]; then + if [ $i -ne 1 ]; then + cat $DIR_LIST/$lang/$FILE_LIST_TEND >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + cat $DIR_LIST/$lang/$FILE_LIST_TSTART >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + i=1 + fi + prev_section=$section + fi + + echo "\texttt{$id} & \texttt{$sciper} & \texttt{$section} & \texttt{$name} & & & {\tiny \texttt{$comment}} "'\\ \hline' >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + if [ $(($i % $LIST_ROWS_PER_PAGE)) -eq 0 ]; then + cat $DIR_LIST/$lang/$FILE_LIST_TEND >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + tab=1 + fi + done + if [ $(($i % $LIST_ROWS_PER_PAGE)) -ne 0 ]; then + cat $DIR_LIST/$lang/$FILE_LIST_TEND >> $DIR_EXAMS/$teacher/$FILE_LIST_ROWS + fi + pdf_list=$(echo $FILE_LIST_MAIN | cut -d '.' -f 1)'.pdf' + cd $DIR_EXAMS/$teacher/ + # Run pdflatex twice to get total number of pages + pdflatex ./$FILE_LIST_MAIN 2>&1 > /dev/null + pdflatex ./$FILE_LIST_MAIN 2>&1 > /dev/null + cd - > /dev/null + mkdir -p $DIR_LIST_OUT + cp $DIR_EXAMS/$teacher/$pdf_list $DIR_LIST_OUT/List-$teacher.pdf + cp $DIR_TEACHERS/$teacher/$FILE_STUDENTS $DIR_LIST_OUT/List-$teacher.csv + fi + done + return $rc +} + +function check_project_exists() { + local teacher=$1 + if [ ! -d $DIR_EXAMS/$teacher ]; then + echo "No project found for teacher: $1" + echo "Did you run \"project\" action ?" + exit 1 + fi +} + +function get_teachers() { + if [ -n "$TEACHERS" ]; then echo "$TEACHERS"; return; fi + find $DIR_TEACHERS -maxdepth 1 -mindepth 1 -type d -exec basename {} \; | grep -v ".ignore$" | sort +} + +function do_test() { + local rc=0 + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + if [ ! -d $DIR_TEACHERS/$teacher ]; then + echo "ERROR: [$teacher] teacher not found." + ((rc++)) + else + echo " Nothing done. just testing." + fi + done + return $rc +} + +function check_file_exists() { + if [ ! -r $1 ]; then + if [ $# -lt 2 ]; then echo "ERROR: File not found: $1"; fi + if [ -n "$2" ]; then return $2; else exit 1; fi + fi + return 0 +} + +function get_lang() { + local forced_lang=$(get_param_value 'lang') + if [ -n "$forced_lang" ]; then + echo $forced_lang; + else + local FILE=$DIR_TEACHERS/$1/$FILE_LANG + check_file_exists $FILE + cat $FILE + fi +} + +function import_tex_files() { + local teacher=$1 + local lang=$(get_lang $teacher) + cp $DIR_DATA/base/* $DIR_EXAMS/$teacher/ + cp $DIR_DATA/$lang/* $DIR_EXAMS/$teacher/ +} + +function override_tex_files() { + local teacher=$1 + find $DIR_TEACHERS/$teacher/ -name '*.tex' -exec cp {} $DIR_EXAMS/$teacher/ \; +} + +function import_student_file() { + local teacher=$1 + local lang=$(get_lang $teacher) + local FILE=$DIR_TEACHERS/$teacher/$FILE_STUDENTS + check_file_exists $FILE + cp $FILE $DIR_EXAMS/$teacher/$FILE_STUDENTS + + # Add extra students + local nb=0 + local IFS=$'\n' + local id=$(tail -n 1 $DIR_EXAMS/$teacher/$FILE_STUDENTS | cut -d ',' -f 1) + local sample=$(head -n 1 $DIR_CSV/extra.csv) + while [ $nb -lt $EXTRA_COPIES ]; do + ((nb++)) + ((id++)) + echo $sample | sed "s/#NB#/$nb/g" | sed "s/#ID#/$id/g" | sed "s/#SCIPER#/FAKE-$nb/g" | sed "s/#EMAIL#/$DEFAULT_EMAIL/g" | sed "s/#SEMESTER#/$SEMESTER/g" >> $DIR_EXAMS/$teacher/$FILE_STUDENTS + done +} + +function get_expected_page_per_exam() { + local teacher=$1 + local FILE=$DIR_TEACHERS/$teacher/$FILE_PROF + cat $FILE | grep '^TOTAL_PAGES:' | cut -d ':' -f 2 +} + +function customize_tex_files() { + local teacher=$1 replace + local lang=$(get_lang $teacher) + local FILE=$DIR_TEACHERS/$teacher/$FILE_PROF + check_file_exists $FILE + + # professor.tex + local prof_file=$DIR_EXAMS/$teacher/$FILE_PROF_TEX IFS=$'\n' + check_file_exists $prof_file + for replace in $(cat $FILE); do + pattern="#$(echo $replace | cut -d ':' -f 1)#" + string=$(echo $replace | cut -d ':' -f 2-) + sed -i "s/$pattern/$string/g" $prof_file + done + + # exam.tex + lang=$(echo $lang | tr '[a-z]' '[A-Z]') + sed -i "s/#LANG#/$lang/g" $DIR_EXAMS/$teacher/$FILE_EXAM_BASENAME.tex + + # Add specific.tex file + if [ -r $DIR_EXAMS/$teacher/specific.tex ]; then + sed -i 's?% #SPECIFIC#?\\input{./specific.tex}?g' $DIR_EXAMS/$teacher/$FILE_EXAM_BASENAME.tex + fi + +} + +function build_sections() { + local question_file question section_file section teacher=$1 + local lang=$(get_lang $teacher) IFS=$'\n' FILE=$DIR_TEACHERS/$teacher/$FILE_SECTIONS + mkdir -p $DIR_EXAMS/$teacher + check_file_exists $FILE + local section_num=0 + rm -f $DIR_EXAMS/$teacher/sections.tex $DIR_EXAMS/$teacher/random-sections.tex + for section in $(cat $FILE); do + ((section_num++)) + section_file=$DIR_EXAMS/$teacher/section_${section_num}.tex + rm -f $section_file + for question in $(echo $section | tr ',' '\n'); do + #echo "Adding $question to section $section_file..."; + question_file=$DIR_QUESTIONS/$lang/$question + check_file_exists $question_file + echo "%% From $lang/$question =======================================" >> $section_file + echo '\element{section'$section_num'}{' >> $section_file + cat $question_file >> $section_file + echo '}' >> $section_file + done + #echo "Updating 'sections.tex' file" + echo "\input{./section_${section_num}.tex}" >> $DIR_EXAMS/$teacher/sections.tex + #echo "Updating 'random-sections.tex' file" + echo "\input{./header_${section_num}.tex} +\melangegroupe{section${section_num}} +\restituegroupe{section${section_num}} +" >> $DIR_EXAMS/$teacher/random-sections.tex + + done +} + +function do_sample() { + local teacher + local lang + local runs + local ok + local rc=0 + for teacher in $(get_teachers); do + ok=0 + tell_if_verbose " - $teacher" + check_project_exists $teacher + if [ -r $DIR_EXAMS/$teacher/INVALID ]; then ((rc++)); break; fi + runs=$PDFLATEX_RUNS + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav + cp $DIR_CSV/$FILE_SAMPLE_CSV $DIR_EXAMS/$teacher/$FILE_STUDENTS + cd $DIR_EXAMS/$teacher + rm -f EXAM-sujet.pdf EXAM-corrige.pdf EXAM-catalog.pdf catalog.pdf + while [ $runs -gt 0 ]; do + auto-multiple-choice prepare --mode s --with pdflatex --filter latex --prefix ./ ./$FILE_EXAM_BASENAME.tex --out-sujet EXAM-sujet.pdf --out-corrige EXAM-corrige.pdf 2>&1 > /dev/null | grep -v 'deprecated' + rm -f $DIR_EXAMS/$teacher/catalog.pdf + check_file_exists EXAM-sujet.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + ((runs--)) + done + cd - 2>&1 > /dev/null + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav $DIR_EXAMS/$teacher/$FILE_STUDENTS + if [ $ok -eq 1 ]; then + mkdir -p $DIR_SAMPLES + lang=$(get_lang $teacher) + mv $DIR_EXAMS/$teacher/EXAM-sujet.pdf $DIR_SAMPLES/Sample-$teacher-$lang.pdf + rm -f $DIR_EXAMS/$teacher/catalog.pdf $DIR_EXAMS/$teacher/EXAM-corrige.pdf + else + ((rc++)) + echo "ERROR: [$teacher] Sample not generated." + fi + done; + return $rc +} + +function do_check() { + local teacher + local runs + local ok + local rc=0 + for teacher in $(get_teachers); do + ok=0 + tell_if_verbose " - $teacher" + check_project_exists $teacher + runs=$PDFLATEX_RUNS + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav + cp $DIR_CSV/$FILE_BLANK_CSV $DIR_EXAMS/$teacher/$FILE_STUDENTS + cd $DIR_EXAMS/$teacher + rm -f EXAM-sujet.pdf EXAM-corrige.pdf EXAM-catalog.pdf catalog.pdf + while [ $runs -gt 0 ]; do + auto-multiple-choice prepare --mode s --with pdflatex --filter latex --prefix ./ ./$FILE_EXAM_BASENAME.tex --out-sujet EXAM-sujet.pdf --out-corrige EXAM-corrige.pdf 2>&1 > /dev/null | grep -v 'deprecated' + rm -f catalog.pdf + if [ ! -r EXAM-sujet.pdf ]; then runs=0; continue; else ok=1; fi; + if [ ! -r EXAM-corrige.pdf ]; then runs=0; continue; else ok=1; fi; + ((runs--)) + done + cd - 2>&1 > /dev/null + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav $DIR_EXAMS/$teacher/$FILE_STUDENTS + if [ $ok -eq 0 ]; then + echo "ERROR: [$teacher] exam.tex does not compile." + touch $DIR_EXAMS/$teacher/INVALID + ((rc++)) + else + rm -f $DIR_EXAMS/$teacher/INVALID + fi + done; + return $rc +} + +function do_blank() { + local teacher + local lang + local runs + local ok + local rc=0 + for teacher in $(get_teachers); do + ok=0 + tell_if_verbose " - $teacher" + check_project_exists $teacher + if [ -r $DIR_EXAMS/$teacher/INVALID ]; then ((rc++)); break; fi + runs=$PDFLATEX_RUNS + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav + cp $DIR_CSV/$FILE_BLANK_CSV $DIR_EXAMS/$teacher/$FILE_STUDENTS + cd $DIR_EXAMS/$teacher + rm -f EXAM-sujet.pdf EXAM-corrige.pdf EXAM-catalog.pdf catalog.pdf + while [ $runs -gt 0 ]; do + auto-multiple-choice prepare --mode s --with pdflatex --filter latex --prefix ./ ./$FILE_EXAM_BASENAME.tex --out-sujet EXAM-sujet.pdf --out-corrige EXAM-corrige.pdf 2>&1 > /dev/null | grep -v 'deprecated' + check_file_exists EXAM-sujet.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + check_file_exists EXAM-corrige.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + check_file_exists catalog.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue ;else ok=1; fi; + ((runs--)) + done + cd - 2>&1 > /dev/null + mv $DIR_EXAMS/$teacher/$FILE_STUDENTS.sav $DIR_EXAMS/$teacher/$FILE_STUDENTS + if [ $ok -eq 1 ]; then + mkdir -p $DIR_BLANKS + lang=$(get_lang $teacher) + mv $DIR_EXAMS/$teacher/EXAM-sujet.pdf $DIR_BLANKS/Blank-$teacher-$lang.pdf + mv $DIR_EXAMS/$teacher/EXAM-corrige.pdf $DIR_BLANKS/Correction-Blank-$teacher-$lang.pdf + mv $DIR_EXAMS/$teacher/catalog.pdf $DIR_BLANKS/Catalog-$teacher-$lang.pdf + else + ((rc++)) + echo "ERROR: [$teacher] Blank not generated." + fi + done; + return $rc +} + +function do_pdf() { + local teacher + local lang + local runs + local ok=0 + local rc + for teacher in $(get_teachers); do + ok=0 + tell_if_verbose " - $teacher" + check_project_exists $teacher + if [ -r $DIR_EXAMS/$teacher/INVALID ]; then ((rc++)); break; fi + runs=$PDFLATEX_RUNS + cd $DIR_EXAMS/$teacher + while [ $runs -gt 0 ]; do + tell_if_verbose " (step 1/3)" + auto-multiple-choice prepare --mode s --with pdflatex --filter latex --prefix ./ ./$FILE_EXAM_BASENAME.tex --out-sujet EXAM-sujet.pdf --out-corrige EXAM-corrige.pdf --out-calage DOC-calage.xy 2>&1 > /dev/null | grep -v 'deprecated' + ((runs--)) + check_file_exists EXAM-sujet.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + check_file_exists EXAM-corrige.pdf 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + check_file_exists DOC-calage.xy 1 + if [ $? -ne 0 ]; then runs=0; continue; else ok=1; fi; + done + if [ $ok -eq 1 ]; then + tell_if_verbose " (step 2/3)" + auto-multiple-choice prepare --mode b --with pdflatex --filter latex --prefix ./ ./$FILE_EXAM_BASENAME.tex --data ./data/ 2>&1 > /dev/null | grep -v 'deprecated' + tell_if_verbose " (step 3/3)" + auto-multiple-choice meptex --src ./DOC-calage.xy --data ./data --debug /dev/null 2>&1 > /dev/null + fi + cd - 2>&1 > /dev/null + if [ $ok -eq 1 ]; then + mkdir -p $DIR_PDF + lang=$(get_lang $teacher) + cp $DIR_EXAMS/$teacher/EXAM-sujet.pdf $DIR_PDF/Exam-$teacher-$lang.pdf + cp $DIR_EXAMS/$teacher/EXAM-corrige.pdf $DIR_PDF/Exam-$teacher-$lang-CORRECTION.pdf + mv $DIR_EXAMS/$teacher/catalog.pdf $DIR_EXAMS/$teacher/EXAM-catalog.pdf + # Validate page count + nb_of_students=$(tail -n 1 $DIR_EXAMS/$teacher/$FILE_STUDENTS | cut -d ',' -f 1) + ppe=$(get_expected_page_per_exam $teacher) + expected_page_count=$(echo "$ppe * $nb_of_students" | bc) + actual_page_count=$(pdfinfo $DIR_PDF/Exam-$teacher-$lang.pdf | grep '^Pages:' | rev | cut -d ' ' -f 1 | rev) + if [ $expected_page_count -ne $actual_page_count ]; then + ((rc++)) + echo "Unexpected number of pages generated." + fi + echo "$actual_page_count page(s) generated for $nb_of_students students. $expected_page_count pag(s) were expected." > $DIR_PDF/Exam-$teacher-$lang.log + else + ((rc++)) + echo "Exam not generated for $teacher." + fi + done + return $rc +} + +function build_amc_project() { + local teacher=$1 + mkdir -p $DIR_EXAMS/$teacher/cr/corrections/jpg + mkdir -p $DIR_EXAMS/$teacher/cr/corrections/pdf + mkdir -p $DIR_EXAMS/$teacher/cr/diagnostic + mkdir -p $DIR_EXAMS/$teacher/cr/zooms + mkdir -p $DIR_EXAMS/$teacher/data + mkdir -p $DIR_EXAMS/$teacher/exports + mkdir -p $DIR_EXAMS/$teacher/scans + mkdir -p $DIR_EXAMS/$teacher/copies + mkdir -p $DIR_EXAMS/$teacher/media + cp $DIR_CONFIG/$FILE_AMC_OPTIONS $DIR_EXAMS/$teacher/$FILE_AMC_OPTIONS +} + +function add_media_files() { + local teacher=$1 + cp $DIR_MEDIA/* $DIR_EXAMS/$teacher/media/ +} + +function do_project() { + mkdir -p $DIR_EXAMS + local teacher + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + build_amc_project $teacher + build_sections $teacher + import_tex_files $teacher + override_tex_files $teacher + customize_tex_files $teacher + add_media_files $teacher + import_student_file $teacher + done +} + +function do_scans() { + local teacher + for teacher in $(get_teachers); do + echo "Processing teacher $teacher" + if [ ! -d $DIR_SCANS/$teacher ]; then + echo "No scans found! (looked in $DIR_SCANS/$teacher)" + break; + fi + rm -rf $DIR_EXAMS/$teacher/scans/ + mkdir $DIR_EXAMS/$teacher/scans/ + cp $DIR_SCANS/$teacher/* $DIR_EXAMS/$teacher/scans/ + local file_count=$(find $DIR_EXAMS/$teacher/scans/ -type f | wc -l) + echo "Number of files: $file_count" + local page_count=0 + for tif in $(find $DIR_EXAMS/$teacher/scans/ -type f); do + if [ $(tiffinfo $tif 2> /dev/null | grep 'Page Number:' | cut -d '-' -f 2 ) -eq 0 ]; then + ((page_count++)) + else + echo "Invalid page count for file: $tif"; + fi + done + echo "Number of pages: $page_count" + local students_count=$(cat $DIR_EXAMS/$teacher/$FILE_STUDENTS | grep -vc '^ID,') + echo "Number of students: $students_count" + local ppc=$(grep 'TOTAL_PAGES:' $DIR_TEACHERS/$teacher/$FILE_PROF | cut -d ':' -f 2) + local expected=$((ppc*students_count)) + echo "Expected number of pages: $expected" + if [ ! $expected -eq $page_count ]; then + echo "MISSING COPIES :" + cat $DIR_EXAMS/$teacher/$FILE_STUDENTS | grep -v '^ID,' | cut -d ',' -f 1 >> /tmp/$$ + find $DIR_EXAMS/$teacher/scans/ -type f | rev | cut -d '_' -f 1 | rev | cut -d '-' -f 1 | sed 's/^0*//g' | sort -u >> /tmp/$$ + for missing in $(sort -n /tmp/$$ | uniq -u); do + grep "^$missing," $DIR_EXAMS/$teacher/$FILE_STUDENTS + done + rm -f /tmp/$$ + fi + done +} + +function do_analyse() { + local teacher + for teacher in $(get_teachers); do + echo "Processing teacher $teacher" + cd $DIR_EXAMS/$teacher/ + auto-multiple-choice analyse --projet ./ ./scans/* + # auto-multiple-choice note --data ./data --seuil 0.05 # ignored anyway... check "seuil" in options.xml + cd - > /dev/null 2>&1 + done +} + +function do_associate() { + local teacher + for teacher in $(get_teachers); do + echo "Processing teacher $teacher" + cd $DIR_EXAMS/$teacher/ + for csv in $(grep -v '^ID,' $FILE_STUDENTS | cut -d ',' -f 1,3); do + local id=$(echo $csv | cut -d ',' -f 1) + local sciper=$(echo $csv | cut -d ',' -f 2) + auto-multiple-choice association --data ./data --set --student $id --id $sciper + done + cd - > /dev/null 2>&1 + done +} + +function do_annotate() { + local teacher + for teacher in $(get_teachers); do + echo "Processing teacher $teacher" + cd $DIR_EXAMS/$teacher/ + auto-multiple-choice annote --projet ./ --data ./data --fich-noms $FILE_STUDENTS + auto-multiple-choice regroupe --projet ./ --sujet EXAM-sujet.pdf --fich-noms $FILE_STUDENTS --tex-src $FILE_EXAM_BASENAME.tex --compose + cd - > /dev/null 2>&1 + done +} + +function do_mailing() { + local teacher + for teacher in $(get_teachers); do + echo "Processing teacher $teacher" + local lang=$(get_lang $teacher) + check_file_exists $DIR_CONFIG/$lang-$FILE_AMC_MAILING + cp $DIR_CONFIG/$lang-$FILE_AMC_MAILING $DIR_EXAMS/$teacher/$FILE_AMC_MAILING + sed -i "s?%FILE_STUDENTS%?./$FILE_STUDENTS?g" $DIR_EXAMS/$teacher/$FILE_AMC_MAILING + echo "#!/bin/bash +auto-multiple-choice mailing --xmlargs ./$FILE_AMC_MAILING +exit 0" > $DIR_EXAMS/$teacher/$FILE_EMAILS_SCRIPT + chmod +x $DIR_EXAMS/$teacher/$FILE_EMAILS_SCRIPT + done +} + +function do_export() { + local teacher + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + cd $DIR_EXAMS/$teacher/ + auto-multiple-choice export --data ./data --module CSV --fich-noms $FILE_STUDENTS --o $FILE_RESULTS.csv + cd - > /dev/null 2>&1 + done +} + +function check_webdav_mounted() { + if [ ! -d $DIR_WEBDAV_BASE ]; then echo "ERROR: webdav dir ($DIR_WEBDAV_BASE) is not accessible."; exit 1; fi +} + +function do_webdav_catalog() { + local teacher + local rc=0 + check_webdav_mounted + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + local lang=$(get_lang $teacher) + mkdir -p $DIR_WEBDAV_CATALOGS + for f in $(echo $DIR_BLANKS/Catalog-$teacher-$lang.pdf); do + check_file_exists $f 1 + if [ $? -eq 0 ]; then + cp $f $DIR_WEBDAV_CATALOGS/ + else + echo "ERROR: [$teacher] "$f" not found. Did you run 'blank' action?" + ((rc++)) + break; + fi + done + done + return $rc +} + +function do_webdav_sample() { + local teacher + local rc=0 + check_webdav_mounted + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + local lang=$(get_lang $teacher) + mkdir -p $DIR_WEBDAV_SAMPLES + for f in $(echo $DIR_SAMPLES/Sample-$teacher-$lang.pdf); do + check_file_exists $f 1 + if [ $? -eq 0 ]; then + cp $f $DIR_WEBDAV_SAMPLES/ + else + echo "ERROR: [$teacher] "$f" not found. Did you run 'sample' action?" + ((rc++)) + break; + fi + done + done + return $rc +} + +function do_webdav_pdf() { + local teacher + local rc=0 + check_webdav_mounted + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + local lang=$(get_lang $teacher) + mkdir -p $DIR_WEBDAV_EXAMS + for f in $(echo $DIR_PDF/Exam-$teacher-$lang.log $DIR_PDF/Exam-$teacher-$lang.pdf $DIR_PDF/Exam-$teacher-$lang-CORRECTION.pdf); do + check_file_exists $f 1 + if [ $? -eq 0 ]; then + cp $f $DIR_WEBDAV_EXAMS/ + else + echo "ERROR: [$teacher] "$f" not found. Did you run 'list' action?" + ((rc++)) + break; + fi + done + done + return $rc +} + +function do_webdav_list() { + local teacher + local rc=0 + check_webdav_mounted + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + local lang=$(get_lang $teacher) + mkdir -p $DIR_WEBDAV_LISTS + for f in $(echo $DIR_LIST_OUT/List-$teacher.pdf $DIR_LIST_OUT/List-$teacher.csv); do + check_file_exists $f 1 + if [ $? -eq 0 ]; then + cp $f $DIR_WEBDAV_LISTS/ + else + echo "ERROR: [$teacher] "$f" not found. Did you run 'list' action?" + ((rc++)) + break; + fi + done + done + return $rc +} + +function do_clean() { + local teacher + local ask=$(get_param_value 'ask') + for teacher in $(get_teachers); do + tell_if_verbose " - $teacher" + lang=$(get_lang $teacher) + if [ "$ask" != "no" ]; then + echo "Cleaning all generated data for teacher '$teacher' ? CTRL-C to abort." + echo "(Use '--params ask=no' to force)" + read a + fi + # Projects + rm -rf $DIR_EXAMS/$teacher + # Blanks + rm -f $DIR_BLANKS/Blank-$teacher-$lang.pdf $DIR_BLANKS/Correction-Blank-$teacher-$lang.pdf $DIR_BLANKS/Catalog-$teacher-$lang.pdf + # Samples + rm -f $DIR_SAMPLES/Sample-$teacher-$lang.pdf + # PDF + rm -f $DIR_PDF/Exam-$teacher-$lang.log $DIR_PDF/Exam-$teacher-$lang.pdf $DIR_PDF/Exam-$teacher-$lang-CORRECTION.pdf + # Lists + rm -f $DIR_LIST_OUT/List-$teacher.pdf $DIR_LIST_OUT/List-$teacher.csv + done + for dir_to_clean in $(echo "$DIR_EXAMS $DIR_BLANKS $DIR_SAMPLES $DIR_PDF $DIR_LIST_OUT"); do if [ -d $dir_to_clean ]; then rmdir --ignore-fail-on-non-empty $dir_to_clean; fi; done +} diff --git a/lib/bash/io.sh b/lib/bash/io.sh new file mode 100644 index 0000000..6556579 --- /dev/null +++ b/lib/bash/io.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +function color_echo() { + echo -e "$@" +} + +function color_echo_n() { + echo -en "$@" +} + +function debug_echo() { + color_echo "${White}DEBUG:${NoColor} $@" +} + +function warning_echo() { + color_echo "${Yellow}WARING:${NoColor} $@" 1>&2 +} + +function error_echo() { + color_echo "${Red}ERROR:${NoColor} $@" 1>&2 +} + +function info_echo() { + color_echo "${Green}INFO:${NoColor} $@" +} + +function OK_echo() { + color_echo "${Green}OK${NoColor}" +} + +function KO_echo() { + color_echo "${Red}KO${NoColor}" +} + +function check_rc_echo { + if [ $1 -eq 0 ]; then OK_echo; return 0; else KO_echo; return 1; fi +} + +function function_exists() { + function_to_test=$1 + function_exists=$(type "$function_to_test" 2>&1 | grep -c "$function_to_test"' is a function'); + if [ $function_exists -eq 1 ]; then return 1; else return 0; fi +} + +function confirm() { + function_exists get_parameter + if [ $? -eq 1 ]; then + forced=$(get_parameter 'force'); + if [ "$forced" == "set" ]; then + return 1 + fi + fi + color_echo "${White}Confirmation requested:${NoColor}" + color_echo "$@" + color_echo_n "Press ${Green}Y${NoColor}(es) to confirm or ${Red}CTRL-z${NoColor} to cancel. >>> " + local ans + read ans + case $ans in + Y|y|Yes|YES|yes) + color_echo "${Green}Confirmed.${NoColor}" + return 1 + break;; + *) + color_echo "${Red}Cancelled.${NoColor}" + return 0 + break;; + esac +} + +# EOF diff --git a/local/epfl/cache/readme b/local/epfl/cache/readme new file mode 100644 index 0000000..a4a2b14 --- /dev/null +++ b/local/epfl/cache/readme @@ -0,0 +1,2 @@ +Here is stored the cached data. +Feel free to clean-up this directory from time to time. diff --git a/local/epfl/check_list b/local/epfl/check_list new file mode 100755 index 0000000..d5b50ad --- /dev/null +++ b/local/epfl/check_list @@ -0,0 +1,39 @@ +#!/bin/bash + +function USAGE() { + echo "USAGE: $0 csv_list.csv"; + exit 1 +} + +SOURCE=$1 +if [ ! -r $SOURCE ]; then echo "File not found: $SOURCE" >2; USAGE; fi + +function fetch_data() { + local sciper=$1 + local cache_file="./cache/${sciper}.txt" + if [ ! -f $cache_file ]; then + ./search-epfl -s $sciper > $cache_file + DATA=$(./search-epfl -s $sciper | tr '\n' '~'); + fi + DATA=$(cat $cache_file | tr '\n' '~') +} + +function get_field() { + local field=$1 + echo $DATA | tr '~' '\n' | grep "^$field:" | cut -d ':' -f 2- +} + +IFS=$'\n' +id=0 +for l in $(cat $1); do + ((id++)) + sciper=$(echo $l | cut -d ',' -f 1) + rest=$(echo $l | cut -d ',' -f 2-) + fetch_data $sciper + section=$(get_field 'section') + name=$(get_field 'cname_usual') + status="OK"; + if [ -z "$section" ] ; then section=$(echo $l | cut -d ',' -f 3); status="NOT FOUND"; fi + if [ -z "$name" ] ; then name=$(echo $l | cut -d ',' -f 2); fi + echo "$id,$sciper,$name,$section,$status,$rest" +done diff --git a/local/epfl/lib/people_search.php b/local/epfl/lib/people_search.php new file mode 100755 index 0000000..25d10a1 --- /dev/null +++ b/local/epfl/lib/people_search.php @@ -0,0 +1,147 @@ +Http = new HttpRequest(); + $this->base_url = 'http://search.epfl.ch/psearch.action'; + $this->base_params = array( 'request_locale' => 'en', + 'q' => '', + 'origin' => 'header' ); + $headers = Array( 'Accept-Charset' => 'UTF-8' ); + $this->Http->setHeaders($headers); + + } + + public function SetLocale($locale) { + if ($locale == 'fr' or $locale == 'en' ) { + $this->base_params['request_locale'] = $locale; + } + } + + protected function SetStartUrl() { + $url = $this->base_url.'?'.http_build_str($this->base_params); + #echo $url."\n"; + $this->Http->setUrl($url); + $this->results = null; + $this->body = null; + } + + public function GetBody() { + return $this->body; + } + + public function FindBySciper($sciper) { + if (! is_numeric($sciper)) throw new Exception('Provided SCIPER is not numeric.'); + $this->base_params['q'] = $sciper; + $this->SetStartUrl(); + $this->DoSearch(); + return $this->results; + } + + public function FindByName($name) { + $this->base_params['q'] = $name; + $this->SetStartUrl(); + $this->DoSearch(); + return $this->results; + } + + protected function DoSearch() { + $final_url = null; + $redirected = false; + do { + $response = $this->Http->send(); + if ($response->getResponseCode() != 301 && $response->getResponseCode() != 302) break; + $final_url = $response->getHeader("Location"); + #echo $final_url."\n"; + $redirected = true; + $this->Http->setUrl($final_url); + } while (1); + $this->body = $this->Http->getResponseBody(); + $this->results = array('found' => (int)$redirected); + + if ($this->results['found']) { + + # Email + $matches = array(); + preg_match("/msgto\('(.*)','(.*)'\)/", $this->body, $matches ); + if (! empty($matches)) { + $this->results['email'] = $matches[1].'@'.$matches[2]; + $matches = explode('.', $matches[1]); + $this->results['fname_email'] = $matches[0]; + $this->results['lname_email'] = $matches[1]; + } else { + $this->results['email'] = ''; + $this->results['fname_email'] = ''; + $this->results['lname_email'] = ''; + } + + # Full names + $matches = array(); + preg_match("~\(.*)\~", $this->body, $matches ); + $matches = explode(' ', $matches[1]); + $this->results['fname_full'] = $matches[0]; + $this->results['lname_full'] = $matches[1]; + if (empty($this->results['email'])) { + $this->results['fname_email'] = $this->results['fname_full']; + $this->results['lname_email'] = $this->results['lname_full']; + } + + # Compute usual names + if (! empty($this->results['email'])) { + $fnames = explode(' ', $this->results['fname_full']); + $i = 0; + $this->results['fname_usual'] = ''; + while (strlen($this->results['fname_usual']) < strlen($this->results['fname_email']) and (array_key_exists($i, $fnames))) { + if ($i > 0) $this->results['fname_usual'] .= ' '; + $this->results['fname_usual'] .= $fnames[$i]; + $i++; + } + $lnames = explode(' ', $this->results['lname_full']); + $i = 0; + $this->results['lname_usual'] = ''; + while (strlen($this->results['lname_usual']) < strlen($this->results['lname_email']) and (array_key_exists($i, $lnames))) { + if ($i > 0) $this->results['lname_usual'] .= ' '; + $this->results['lname_usual'] .= $lnames[$i]; + $i++; + } + } else { + $this->results['fname_usual'] = $this->results['fname_full']; + $this->results['lname_usual'] = $this->results['lname_full']; + } + + # Compute complete name + $this->results['cname_full'] = $this->results['fname_full'].' '.$this->results['lname_full']; + $this->results['cname_usual'] = $this->results['fname_usual'].' '.$this->results['lname_usual']; + + # Compute complete reversed + $this->results['cname_full_reversed'] = $this->results['lname_full'].' '.$this->results['fname_full']; + $this->results['cname_usual_reversed'] = $this->results['lname_usual'].' '.$this->results['fname_usual']; + + # Sciper + preg_match("/vCard\?id=([0-9]+)&/", $this->body, $matches); + $this->results['SCIPER'] = $matches[1]; + # Section + preg_match('/a title="(.*)" href=.*>(.*)body, $matches); + $this->results['section_description'] = $matches[1]; + $this->results['section_acronym_full'] = $matches[2]; + $matches = explode('-', $matches[2]); + $this->results['section'] = $matches[0]; + if (array_key_exists(1, $matches)) { + $this->results['section_extended'] = $matches[1]; + } + } + } +} + +class BashWriter { + public function __construct($array = null) { if (!is_null($array)) $this->format($array); } + public function format($array) { if (is_array($array)) foreach($array as $key => $value) echo "$key:$value\n"; } +} + +?> diff --git a/local/epfl/liste.csv b/local/epfl/liste.csv new file mode 100644 index 0000000..64b308d --- /dev/null +++ b/local/epfl/liste.csv @@ -0,0 +1,219 @@ +247893,Abboud Robert,GC +240822,Albanese Leopoldo Costantino Ercole,GC +235706,Albutra Nasseem,GC +250646,Ansermet Robin Brian,GC +247777,Azélart Henri Bernard Vincent,GC +237979,Badarani Louise Simone Flore,GC +249696,Badoux Sophia Claire,GC +246138,Bahchouch Hamza,GC +245913,Barre Pierre Stéphane Denis,GC +236760,Baudry Inès Marie Anne Véronique,GC +238346,Bellamlih Mamou Ali,GC +236362,Bembarek Mohammed-Amine,GC +234779,Bensaid Younes,GC +224796,Benzakour Hamza,GC +249902,Berquand Cecile Anne,GC +247180,Bhouri Adem,GC +237985,Borremans Noé,GC +247453,Bösch Felix Samuel,GC +249807,Brault Victoire Thérèse Marie,GC +240894,Brunner Kim Tan Quang,GC +237074,Buqaj Gentian,GC +247815,Burckhardt Valérie Claudia,GC +233683,Casanova Michela,GC +246679,Cassina Lisa,GC +246210,Charif Linah,GC +246830,Couturier Alexia,GC +237591,Curis Gabriel François Marie Laurent,GC +250002,D'Annunzio Lorn Douglas,GC +228280,De Franceschini Cesare,GC +247244,Decoppet Jean-Baptiste Luc Hubert,GC +236097,Delannoy Louis Marc Bruno,GC +251757,Delfino Arnaud,GC +237169,Delvaille Guillaume André,GC +246687,Dépraz Mathieu,GC +246077,Descombes Albane Bénédicte,GC +237110,Dorrio Alves Gonzalo Paulo Enrique,GC +250747,Dorthe Titouan,GC +240604,Drouin Thomas Pierre Jacques,GC +239398,Durussel Shad Ali,GC +236135,El Kafil Youssef,GC +246556,El Ouazzani Touhami Yassine,GC +250790,Favre Simon,GC +237126,Ferrari Paolo Angelo,GC +247221,Fink Arthur Guillaume,GC +239433,Fiocconi Clara Sylvie Bernadette Gisèle,GC +247082,Florez Diego,GC +247749,Fontugne Augustin Jean Hugo,GC +237132,Fraigedo Marco,GC +240605,Framery Luis-Angel Charles,GC +247867,Freiburghaus Sylvain,GC +236718,Fuchs Michael Hubert,GC +247483,Fuselier Héloïse Manon,GC +233834,Gandy Lucas Christophe Mathieu,GC +235013,Gasparovic Sergej,GC +239503,Gautier Louis Pierre Robert,GC +231100,Gillet Maxime Loris,GC +236407,Giovanola Vincent,GC +246021,Glatz Tobias Bruno,GC +249835,Gratier De Saint-Louis Romain Essy Théo,GC +234738,Gros Julie Noémie Marie,GC +247030,Haan Julien Johan,GC +203117,Haddadi Sasan,GC +234847,Halleux Arthur,GC +250669,Hatem Nicolas Fernand Joseph,GC +239440,Herbin Océane Elisa Victoire Ildegarde,GC +244982,Heredia Rosa Diego Isidoro,GC +246698,Hourse Laetitia Marie Frédérique,GC +250855,Hoxha Hamid,GC +250446,Imperatori Lisa,GC +250895,Jaber Mehdi,GC +236654,Jacot-Descombes Fabien Jérôme,GC +238028,Jamet Gregoire Aurelien Marie,GC +239626,Joseph Quentin,GC +242532,Keller Paul Alexandre Mario,GC +235333,Kitane Youssef,GC +227442,Kohler David,GC +247288,Labrosse Lucas,GC +246884,Lafaye Chloé Gisèle Christiane,GC +249909,Lafkihi Youssef,GC +235210,Lamrani Alaoui Othmane,GC +237830,Lao Kevin Okynawa,GC +239394,Lemghari Salma,GC +247803,Liechti Simon Jean Marie,GC +236727,Mariller Nicolas Loic Dorian,GC +241602,Matthews Salmon Scott David,GC +246713,Matthey Valériane,GC +244962,Maye Robin Daniel Etienne,GC +250935,Mermod Paul Victor Augustin,GC +241337,Meroni Gabriele Leandro,GC +246972,Michel Stanislas Hugo Werner,GC +234759,Minault Edgar Paul,GC +235570,Mock Pascal Jérémie,GC +247210,Mockers Antoine Valentin,GC +234500,Mohr Mathieu,GC +247494,Monetta Fiona,GC +240666,Monneron Armand Quy Kim,GC +246619,Morim Dany,GC +247798,Mosena Dan,GC +250956,Mudry Grégoire Samuel,GC +246907,Némethy Nicolas Karol Doctoré,GC +245814,Nicolet Adrien,GC +247797,Olsen Nils Frédérik Heiden,GC +250613,Oswald Zora,GC +250587,Padovani Christopher Anthony Marius,GC +238531,Perard Arthur Stéphane,GC +246251,Perruchoud Valentin,GC +245360,Pfister Mélanie,GC +233770,Ponce De Leon Bezerra Amanda Cristina,GC +250632,Pouzere Marie-Lys Ella Dawa,GC +236070,Prébandier Christophe,GC +246979,Pugin Madeline,GC +238323,Rahmaty Abdu,GC +240671,Ramalhoto Penela Joel António,GC +225733,Ramusat Romain Patrick Stéphane,GC +229194,Reyes Samuel Emmanuel,GC +235832,Richard Nicolas Paul,GC +228105,Sahibi Ahmed Wassim,GC +247669,Saint-Supéry Santiago,GC +250866,Salvadé Nicolas Jean,GC +247792,Schneider Romain,GC +223539,Schöpfer Olivier Henri,GC +251192,Schucany Hervé Robert Eugène,GC +237729,Schürch Ursula Esther,GC +235750,Senglet Côme Roland Gérard,GC +247400,Senser Loic,GC +244989,Sessa Ludovica,GC +245842,Siganos Ioannis,GC +236194,Silva Brites Loris,GC +246529,Storz Jordi,GC +247911,Stuber Thomas,GC +249856,Tedeschi Julien Brian,GC +236330,Thillaye Du Boullay Cyril Marie Régis,GC +247038,Valentin Axel Olivier,GC +234512,Velasquez Emmanuel Pablo,GC +235259,Vermot Maeva Tran Tien Lac,GC +250392,Vernay François,GC +245828,Vernet Arthur Maximilien,GC +235606,Viviant Jean Lou Marin,GC +250429,Vuilleumier Violaine,GC +234761,Walid Mahmoud Helmy Nadeem Mohamed,GC +235072,Weibel Amine,GC +238719,Woehner Monia,GC +228073,Woerle Nicolas Pierre Antoine,GC +201838,Wojnarowicz Matthias,GC +238920,Yassafi Amine-Reda,GC +224808,Adel Sonia,SIE +237657,Alaoui Hiba,SIE +240569,Andreetti Alice,SIE +234661,Arnaudon Lucie,SIE +250155,Barbe Emile,SIE +246453,Barbey Léa,SIE +236496,Bergqvist Miriam Christine,SIE +250158,Bernegger Arline Andrea,SIE +250302,Berteaux Adrien Pierre-Alain Boris,SIE +236741,Bielser Florian Adrien,SIE +250598,Bissel Laura Emilie,SIE +238271,Bodmer Timon,SIE +235142,Bonzi Patrick,SIE +250252,Bouche Léopold Antoine Joanny,SIE +250264,Bouvresse Déborah Michèle Albertine,SIE +236472,Brand Florian,SIE +247108,Bretton Aude Marie Dorothée,SIE +236296,Brouillet Constance,SIE +250689,Brunner Lena,SIE +238204,Buchwalder Xavier Louis Maurice,SIE +247605,Burg David Joël,SIE +247225,Caby Theodore Leopold Marie,SIE +236711,Camplani Nathalie Teresina,SIE +235221,Chalak Chirine,SIE +247814,Chappelier Coralie,SIE +246680,Chatelan Elsa Jöelle,SIE +236714,Collins Fiona Claire,SIE +247326,Décosterd Bertil,SIE +239479,Delessert Pauline Charlotte,SIE +250337,Ellero Alicia Mélanie,SIE +250142,Fetzer Aline Sophie,SIE +247021,Franziskakis Johann,SIE +247727,Girardet Lina Elodie Klara,SIE +237757,Hariki Maryam,SIE +239763,Ismaïl Ayoub,SIE +245773,Joris Guillaume Philippe Benoit,SIE +247918,Jouberton Achille Pierre,SIE +247232,Jullien Nicolas,SIE +247701,Keller Cloé,SIE +213505,Klein Louis Xavier,SIE +238503,Kowalski Coralie Jazz,SIE +249748,Krey Vassiliki,SIE +234856,Krzyzostaniak Théo Marek,SIE +249920,Labenets Elena,SIE +235335,Laroui Yassine Klaus,SIE +250546,Locatelli Maxence Valentin,SIE +249236,Lugrin René,SIE +250915,Maeder Ivan,SIE +249690,Manetti Nicolas,SIE +246712,Matthey-Junod Anaïs,SIE +252025,Meskaldji Amir Sami,SIE +236271,Morandini Léonard Elliot,SIE +248153,Neuenschwander Philipp,SIE +240888,Nyffeler Cécile,SIE +249546,Obrecht Jolan César,SIE +250612,Ott Lucas,SIE +247430,Pellaton Louise Diane,SIE +250967,Perez Loïc,SIE +247863,Perna Michael,SIE +236801,Perret Antoine,SIE +250607,Petit Maximilien Jean André,SIE +239467,Quilici Andrea Vittorio,SIE +223888,Rabbath Laura,SIE +235752,Reymond-Joubin Maric Yannick Xavier,SIE +237732,Romero Grass Maëlle,SIE +248127,Sanchez Del Rio Kandel David,SIE +251722,Tahiri Reda,SIE +250383,Tournefier Alan Lucas,SIE +235649,Vallotton Amandine,SIE +245912,Vaucher Nadège,SIE +246644,Viennot-Bourgin Pauline Marie,SIE +247563,Vogel Mégane,SIE +249614,Vogel Michael,SIE +234516,Vruggink Marc,SIE diff --git a/local/epfl/search-epfl b/local/epfl/search-epfl new file mode 100755 index 0000000..72bf5b0 --- /dev/null +++ b/local/epfl/search-epfl @@ -0,0 +1,46 @@ +#!/usr/bin/env php +SetLocale($lang); +} + +$details = null; + +if (!empty($options['s']) or !empty($options['sciper'])) { + $sciper = empty($options['s'])? $options['sciper'] : $options['s'] ; + $details = $PeopleSearch->FindBySciper($sciper); +} elseif (!empty($options['n']) or !empty($options['name'])) { + $name = empty($options['n'])? $options['name'] : $options['n'] ; + $details = $PeopleSearch->FindByName($name); +} + +# Print results +new BashWriter($details); + +?>