Publish a multiple choice questions to your student from a simple markdown file.
Since this project is aimed to French teachers, the rest of the description is in French.
Ce projet permet de créer des QCMs depuis un fichier source markdown et de les diffuser facilement aux élèves.
- Vous créez un fichier QCM
- Vous créez un compte, le validez via l'email envoyé et vous connectez
- Vous envoyez votre QCM
- Vous vérifiez qu'il est présenté comme vous l'imaginiez
- Vous présentez le numéro du QCM aux élèves
- Ils rejoignent, saisissent leur nom, prénom et le numéro en question...
- Ils répondent aux questions et valident
- Vous pouvez fermer le QCM et empecher les élèves d'y contribuer
- Vous récoltez leurs scores et leurs réponses
Le plus simple est de regarder cette vidéo ou de partir de l'exemple.
En gros :
# titre du QCM
## une partie
### question à choix multiple ?
sous texte de la question
- [x] bonne réponse
- [ ] mauvaise réponse a
- [ ] mauvaise réponse b
### question avec une zone de texte ?
- [t]
- avec une seule bonne réponse. L'élève ne peut en choisir plusieurs. Corrigées automatiquement.
- avec une zone de texte. L'élève écrit un texte pour répondre. Vous devez lire les réponses.
Vous pouvez intégrer du code en ligne en l'entourant d'accents graves `f(x)=3`
Pour du code dans le sous-texte :
```python
a = 1
def f(x):
return x ** 2
```
Attention seuls les "backticks" (accents graves) sont supportés, pas les tildes.
LaTex en ligne avec $\int_1^2 x^2 dx$
va produire
LaTex en bloc avec $$
$$
\int_1^2 x^2 dx
$$
va produire
Vous pouvez présenter une image à condition qu'elle soit hébergée en ligne.
Impossible de charger des images directement dans le fichier, je n'ai pas la place pour les héberger ni aucune envie de l'ajouter.
![sad panda](https://t4.ftcdn.net/jpg/04/77/53/75/360_F_477537591_3WXDC8zpsKBALlg8RBKejezg6SE7YbWh.jpg)
va produire :
Les tableaux markdown sont supportés nativement :
| nom | prénom | note |
| ----- | ------ | ---- |
| Jean | Dupont | 14 |
| Marie | Frank | 20 |
va produire :
nom | prénom | note |
---|---|---|
Jean | Dupont | 14 |
Marie | Frank | 20 |
- Les parties, questions et choix sont mélangés. C'est automatique et différent pour chaque élève.
- On ne peut pas cliquer sur les images (pour faire une recherche en ligne).
- Si l'élève quitte la page, ses réponses sont enregistrées et il ne peut plus avancer.
- Il peut toujours retenter... mais vous verrez à quelle heure il a répondu
Les points des questions à choix multiple sont calculés automatiquement
Un mode correction est proposé, permettant de consulter chaque devoir pour les réponses textuelles.
On peut exporter l'ensemble des travaux dans un fichier csv (notes et réponses).
Elles sont en Europe et je crois respecter le RGPD.
Les élèves n'ont pas de compte, ils indiquent leurs noms, prénom et numéro du devoir. Les réponses des élèves sont effacées après trois jours. Ne tardez pas 😄
Les enseignants doivent créer un compte, je n'en garde que l'email. Chaque QCM et travail d'élève est attribué à un enseignant, via le numéro du QCM.
Les QCM sont eux aussi effacés après trois jours, n'ayant pas la place pour en conserver beaucoup.
- Python 3
- Flask (création des pages)
- Extensions Flask : Flask-WTF, FlaskSQLAlchemy, FlaskLogin etc.
- Google API & Google Gmail API pour l'envoi d'emails
- Gunicorn (serveur web WSGI)
- PostgreSQL (bdd)
- Mathjax (latex)
- OVH(service en ligne dans le cloud)
N'hésitez pas à déposer une issue ! Je ferai de mon mieux 😄
Le code est là pour vous répondre...
- Je n'aime pas corriger.
- Les solutions existantes ne répondent à mon besoin. La plus approchante est doctools, consultez
DocEval
.
- mode anti triche
- chronomètre
- multiple valid answers
- text answers
- move something ??? geogebra like object
- QCM original
- Student
- Marks
- fichier .md -> model bdd
- mélanger
- regrouper les résultats
- cookies
- présenter une question par page
- lien entre les pages
- enregistrer les réponses de l'élève
- compter les points
- exporter les notes d'un QCM
- télécharger un csv
- nettoyer la bdd après l'envoi et régulièrement (RGPD)
- upload a .md (basic)
- validate upload
- qcm
- validate
- base
- custom for teacher
- custom for student
- latex
- shuffle les parts, questions, answers
- lorsqu'on envoie les answers à la vue, faut envoyer l'
id_question: id_answer
à la vue - ensuite on compare
id_question: id_answer
avecid_question: id(answer where is_correct)
- utiliser flask caching
- stratégie : quand on veut accéder à un QCM, le cacher après l'avoir retrieve, le drop si plus de x qcm ?
- gunicorn
- postgres
- heroku (i don't understand why I can't use GCP...)
- VPS + SSL + Domain name : OVH
With Flask-Migrate.
-
Ensure
flask app
is in environment -
flask db init
-
Everytime you modify a
db.Model
class, run :$ flask db stamp head # To set the revision in the database to the head, without performing any migrations. You can change head to the required change you want. $ flask db migrate -m "comment" # To detect automatically all the changes. $ flask db upgrade # To apply all the changes.
I think that's all.
Almost everything is done.
Last problem is QCM view for student. Check steps below
-
présenter un qcm
-
enregistrer les réponses
-
noter les réponses
-
afficher des récaps :
- ensemble des qcm
- notes d'un qcm
- notes d'un étudiant
-
style pour chaque page
-
latex
-
block content etc.
-
exporter en csv
-
nettoyer
- la bdd
- les fichiers downloadés
-
mélanger
- parts
- questions
- ajouter "je ne sais pas"
-
serve with gunicorn
-
séparer le modèle de la création des instances
-
déployer qq part : HEROKU : qcmqkzk
-
vues
-
upload
-
download
-
bdd
-
impossible de run heroku et sqlite, il faut switch : hero sqlite
-
Many fixes needed...
-
setup postgres locally following archwiki
-
add postgres to heroku with medium some steps are wrong :
- Flask-Migrate doesn't work like this anymore.
- the uri-database can't start with
postgres
but withpostgresql
so we have to ensure the setup is rectified IN THE CODE. Since changing it insetup.sh
does nothing. - Once everything is working you can log into the DB with
heroku pg:psql postgresql-clear-05212 --app casting-agency-xw
where postgresql-clear-05212 is the name of the DB (found here) - You can log into the dyno with
heroku run bash
, see the logs withheroku log --tail
-
-
-
Fix "bad CSRF token" or "CSRF token is missing", raising 400 errors When using a random secret_key in flask with gunicorn, AFAIK the page may be generated by a worker and the next request received by another. When they start, the key is changed. The solution is to setup a key in
env
, with :FLASK_SECRET_KEY: "a random secret key from secrets.urlsafe(16)"
-
-
styling : banner, radio, infos dans base, uniformité
-
test hosting
- hammering and intense testing. Sort of. Résultat : 13ms pour le contenu. QQ ms pour le rendu. trop jde JS.
-
limit strings, safe inputs and good practices
-
RGPD et tutoriel
-
login pour enseignant ???
- proposer un numero pour consulter les QCM notes
- demander le numéro lorsqu'on cherche à consulter les résultats
- trouver un moyen de consulter les notes pas élève... compliqué
- migrer la database de heroku
-
refactor app
-
refactor model
-
Text answers
-
light/dark mode
-
refactor views
-
remove
setup.sh
from git history. Password changed -
login
- flask login
- forcer les enseignants à se logguer
- empecher les enseignants de consulter des autres QCM que les leurs
- requirements.txt
- problème des librairies qui foirent voir l'import initial dans sendmail comment résoudre ça dans heroku ?
- comment stocker des token dans heroku ?
- refactor tous les nouveaux trucs, les textes etc.
- migrer la bdd de heroku
- supprimer un compte
- confirm real addresses
- change password
-
parser : now detects code blocs properly, allowing lines starting with
#
in code blocks. -
Flask-WTF
- simple views (login, reset password, change password, student)
- new qcms
- csrf everywhere
- qcm views for student. SO question doc must be dynamicly composed check for radio element insertion find a way to retrieve correct ids for question -> ansers and text-question -> text-answer
-
Flash instead of message passing to views
-
hosting
- créer une adresse expres ?
- utiliser une adresse du nom de domaine ???
-
creates review for students... ????
-
send unique mails per students
- student mailing list linked to group then to teacher models : teacher <- group <- student-email <-> student <- work
- option for the teacher
- associate email to work (?)
- send lots of unique mails at once... ressource hungry ???
- what to do when the FAIL
- how to deal with lags & whatever ???
-
open close a QCM
-
sort marks per column (doesn't work for dates...)
-
QR codes
-
auth students with google accounts...
-
give student list and allow them to pick their name. Won't exclude trolls
Solution pros cons unique links email unique difficult to do easy to track ressource hungry sign with google unique may be difficult pick from list easy trolls may pick another name read ip ? doesn't prevent open / close easy not much -
Heroku devient payant et le site est migré chez OVH : qcmqkzk.fr qui est lui même payant mais moins cher.
-
Permettre d'ajouter un texte en début de partie
-
Afficher la date prévue pour la suppression du QCM. Les durées de vie sont spécifiques à chaque modèle implémentant la suppression.
- docker compose pour la bdd et le site
- VPS debian OVH, configuré correctement (ssh passwordless etc.)
- nom de domaine OVH
- SSL proposé par OVH, changer la redirection vers 443
Pour l'instant j'ai 3 soucis
- la bdd est accessible depuis l'extérieur : bloquer dans ufw. source
- la bdd est reset à chaque
sudo docker-compose down
puissudo docker-compose up -d
- fix bizarre... ne pas faire
down
etup
maisstop
etstart
- fix bizarre... ne pas faire
- les identifiants de bdd ne sont pas sécurisés : pas grave inaccessible depuis l'extérieur
- utiliser un export local depuis un fichier transféré à la main
- lire les identifiants dans python et dans docker
- accéder à la bdd depuis VPS :
psql -h 172.17.0.1 -U quentin qcm
- debian vps
- apt-get update && apt-get upgrade
- apt-get install postgresql postgresql-client
- setup sshd pour login passwordless depuis PC, disable root login, disable password login, change default port
- install ufw, disable tout, ajouter port du ssh, ajouter route 443 pour flask
- installer docker & docker-compose
- git clone repo, branch docker
- ajouter à la main le dossier token
- docker compose build up, docker ps, docker-compose ps, check logs
- activer un nom de domain, router vers ip du VPS
- ativer ssh OVH, router vers 443
- accéder au vps depuis qkzk
ssh debian@54.38.243.9 -p 44444
- accéder à la bdd depuis vps
psql -h 172.17.0.1 -U quentin qcm
- relancer le firewall :
sudo ufw reload
- settings du firewall :
sudo vim /etc/ufw/after.rules
- relancer les serveurs :
sudo docker-compose stop
etsudo docker-compose start
- logs docker :
sudo docker-compose logs -f
- reconstruire les images sans effacer la bdd :
sudo docker-compose down
etsudo docker-compose up --build -d
sudo ufw status
Status: active
To Action From
-- ------ ----
44444 ALLOW Anywhere
22/tcp ALLOW Anywhere
44444 (v6) ALLOW Anywhere (v6)
22/tcp (v6) ALLOW Anywhere (v6)
443/tcp ALLOW FWD Anywhere
443/tcp (v6) ALLOW FWD Anywhere (v6)
sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
040e81c8632a qcm_alchemy_app "gunicorn --bind 0.0…" 39 minutes ago Up 8 minutes 0.0.0.0:443->443/tcp, :::443->443/tcp qcm_alchemy_app_1
1fde529fb39b postgres:latest "docker-entrypoint.s…" 39 minutes ago Up 8 minutes 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp postgres
sudo docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------
postgres docker-entrypoint.sh postgres Up 0.0.0.0:5432->5432/tcp,:::5432->5432/tcp
qcm_alchemy_app_1 gunicorn --bind 0.0.0.0:44 ... Up 0.0.0.0:443->443/tcp,:::443->443/tcp
debian@vps-659ff8f4:~/qcm_alchemy$ git diff
diff --git a/server.sh b/server.sh
index 2f2b62f..c56aee4 100755
--- a/server.sh
+++ b/server.sh
@@ -1,4 +1,4 @@
#!/usr/bin/sh
-gunicorn wsgi:app -w 3 --threads 1 -b 0.0.0.0:8000 --config gunicorn_hooks_config.py
+gunicorn wsgi:app -w 3 --threads 1 -b 0.0.0.0:443
# gunicorn wsgi:app --config gunicorn_config.py