Modify MCQ module to follow changes on the website part
Add channel MCQ
This commit is contained in:
parent
eb82d0898d
commit
61ac7b11f5
291
modules/qcm.py
291
modules/qcm.py
@ -1,8 +1,14 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import http.client
|
||||||
|
import hashlib
|
||||||
import re
|
import re
|
||||||
import random
|
import random
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from module_state import ModuleState
|
from module_state import ModuleState
|
||||||
import module_states_file as xmlparser
|
import module_states_file as xmlparser
|
||||||
@ -11,10 +17,10 @@ nemubotversion = 3.0
|
|||||||
|
|
||||||
def help_tiny ():
|
def help_tiny ():
|
||||||
"""Line inserted in the response to the command !help"""
|
"""Line inserted in the response to the command !help"""
|
||||||
return "MCQ module"
|
return "MCQ module, working with http://bot.nemunai.re/"
|
||||||
|
|
||||||
def help_full ():
|
def help_full ():
|
||||||
return "todo"
|
return "!qcm [/nbQuest/] [/theme/]"
|
||||||
|
|
||||||
class QuestionFile:
|
class QuestionFile:
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
@ -27,6 +33,60 @@ class QuestionFile:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
class Course:
|
||||||
|
def __init__(self, iden):
|
||||||
|
global COURSES
|
||||||
|
if iden in COURSES.index:
|
||||||
|
self.node = COURSES.index[iden]
|
||||||
|
else:
|
||||||
|
self.node = { "code":"N/A", "name":"N/A", "branch":"N/A" }
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.node["xml:id"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code(self):
|
||||||
|
return self.node["code"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.node["name"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def branch(self):
|
||||||
|
return self.node["branch"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def validated(self):
|
||||||
|
return int(self.node["validated"]) > 0
|
||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, iden):
|
||||||
|
global USERS
|
||||||
|
if iden in USERS.index:
|
||||||
|
self.node = USERS.index[iden]
|
||||||
|
else:
|
||||||
|
self.node = { "username":"N/A", "email":"N/A" }
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.node["xml:id"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username(self):
|
||||||
|
return self.node["username"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def email(self):
|
||||||
|
return self.node["email"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def validated(self):
|
||||||
|
return int(self.node["validated"]) > 0
|
||||||
|
|
||||||
|
|
||||||
class Question:
|
class Question:
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.node = node
|
self.node = node
|
||||||
@ -45,7 +105,7 @@ class Question:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def course(self):
|
def course(self):
|
||||||
return self.node["course"]
|
return Course(self.node["course"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def answers(self):
|
def answers(self):
|
||||||
@ -53,7 +113,11 @@ class Question:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def validator(self):
|
def validator(self):
|
||||||
return self.node["validator"]
|
return User(self.node["validator"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def writer(self):
|
||||||
|
return User(self.node["writer"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def validated(self):
|
def validated(self):
|
||||||
@ -61,15 +125,26 @@ class Question:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def addedtime(self):
|
def addedtime(self):
|
||||||
return datetime.fromtimestamp(time.mktime(self.node["addedtime"]))
|
return datetime.fromtimestamp(float(self.node["addedtime"]))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def author(self):
|
def author(self):
|
||||||
return "N/A"
|
return User(self.node["writer"])
|
||||||
|
|
||||||
|
def report(self):
|
||||||
|
conn = http.client.HTTPConnection(CONF.getNode("server")["url"])
|
||||||
|
try:
|
||||||
|
conn.request("GET", "report.php?id=" + hashlib.md5(self.id.encode()).hexdigest())
|
||||||
|
except socket.gaierror:
|
||||||
|
print ("[%s] impossible de récupérer la page %s."%(s, p))
|
||||||
|
return False
|
||||||
|
res = conn.getresponse()
|
||||||
|
conn.close()
|
||||||
|
return (res.status == http.client.OK)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tupleInfo(self):
|
def tupleInfo(self):
|
||||||
return (self.author, self.validator, self.addedtime)
|
return (self.author.username, self.validator.username, self.addedtime)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bestAnswer(self):
|
def bestAnswer(self):
|
||||||
@ -80,25 +155,31 @@ class Question:
|
|||||||
return best["answer"]
|
return best["answer"]
|
||||||
|
|
||||||
def isCorrect(self, msg):
|
def isCorrect(self, msg):
|
||||||
|
msg = msg.lower().replace(" ", "")
|
||||||
for answer in self.answers:
|
for answer in self.answers:
|
||||||
if msg == answer["answer"]:
|
if msg == answer["answer"].lower().replace(" ", ""):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getScore(self, msg):
|
def getScore(self, msg):
|
||||||
|
msg = msg.lower().replace(" ", "")
|
||||||
for answer in self.answers:
|
for answer in self.answers:
|
||||||
if msg.lower() == answer["answer"].lower():
|
if msg == answer["answer"].lower().replace(" ", ""):
|
||||||
return answer.getInt("score")
|
return answer.getInt("score")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
def __init__(self):
|
def __init__(self, srv, chan, sender):
|
||||||
self.questions = list()
|
self.questions = list()
|
||||||
self.current = -1
|
self.current = -1
|
||||||
self.score = 0
|
self.score = 0
|
||||||
self.good = 0
|
self.good = 0
|
||||||
self.bad = 0
|
self.bad = 0
|
||||||
self.trys = 0
|
self.trys = 0
|
||||||
|
self.timer = None
|
||||||
|
self.server = srv
|
||||||
|
self.channel = chan
|
||||||
|
self.sender = sender
|
||||||
|
|
||||||
def addQuestion(self, ident):
|
def addQuestion(self, ident):
|
||||||
if ident not in self.questions:
|
if ident not in self.questions:
|
||||||
@ -119,77 +200,197 @@ class Session:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def askNext(self, bfr = ""):
|
||||||
|
global SESSIONS
|
||||||
|
self.timer = None
|
||||||
|
nextQ = self.next_question()
|
||||||
|
if nextQ is not None:
|
||||||
|
if self.sender != self.channel:
|
||||||
|
self.server.send_msg(self.channel, "%s: %s%s" % (self.sender, bfr, nextQ.question))
|
||||||
|
else:
|
||||||
|
self.server.send_msg(self.channel, "%s%s" % (bfr, nextQ.question))
|
||||||
|
else:
|
||||||
|
if self.good > 1:
|
||||||
|
goodS = "s"
|
||||||
|
else:
|
||||||
|
goodS = ""
|
||||||
|
if self.sender != self.channel:
|
||||||
|
self.server.send_msg(self.channel, "%s: %sFini, tu as donné %d bonne%s réponse%s sur %d questions." % (self.sender, bfr, self.good, goodS, goodS, len(self.questions)))
|
||||||
|
else:
|
||||||
|
self.server.send_msg(self.channel, "%sFini, vous avez donné %d bonne%s réponse%s sur %d questions." % (bfr, self.good, goodS, goodS, len(self.questions)))
|
||||||
|
del SESSIONS[self.sender]
|
||||||
|
|
||||||
|
def prepareNext(self, lag = 3):
|
||||||
|
if self.timer is None:
|
||||||
|
self.timer = threading.Timer(lag, self.askNext)
|
||||||
|
self.timer.start()
|
||||||
|
|
||||||
QUESTIONS = None
|
QUESTIONS = None
|
||||||
|
COURSES = None
|
||||||
|
USERS = None
|
||||||
SESSIONS = dict()
|
SESSIONS = dict()
|
||||||
|
|
||||||
def buildSession(user, categ = None, nbQuest = 5):
|
def load():
|
||||||
global QUESTIONS
|
CONF.setIndex("name", "file")
|
||||||
|
|
||||||
|
def buildSession(msg, categ = None, nbQuest = 5, channel = False):
|
||||||
|
global QUESTIONS, COURSES, USERS
|
||||||
if QUESTIONS is None:
|
if QUESTIONS is None:
|
||||||
QUESTIONS = xmlparser.parse_file(CONF.getNode("file")["url"])
|
QUESTIONS = xmlparser.parse_file(CONF.index["main"]["url"])
|
||||||
QUESTIONS.setIndex("xml:id")
|
QUESTIONS.setIndex("xml:id")
|
||||||
|
COURSES = xmlparser.parse_file(CONF.index["courses"]["url"])
|
||||||
|
COURSES.setIndex("xml:id")
|
||||||
|
USERS = xmlparser.parse_file(CONF.index["users"]["url"])
|
||||||
|
USERS.setIndex("xml:id")
|
||||||
#Remove no validated questions
|
#Remove no validated questions
|
||||||
keys = list()
|
keys = list()
|
||||||
for k in QUESTIONS.index.keys():
|
for k in QUESTIONS.index.keys():
|
||||||
keys.append(k)
|
keys.append(k)
|
||||||
for ques in keys:
|
for ques in keys:
|
||||||
if QUESTIONS.index[ques]["validated"] != "1":
|
if QUESTIONS.index[ques]["validated"] != "1" or QUESTIONS.index[ques]["reported"] == "1":
|
||||||
del QUESTIONS.index[ques]
|
del QUESTIONS.index[ques]
|
||||||
|
|
||||||
nbQuest = min(nbQuest, len(QUESTIONS.index))
|
#Apply filter
|
||||||
|
QS = list()
|
||||||
|
if categ is not None and len(categ) > 0:
|
||||||
|
#Find course id corresponding to categ
|
||||||
|
courses = list()
|
||||||
|
for c in COURSES.childs:
|
||||||
|
if c["code"] in categ:
|
||||||
|
courses.append(c["xml:id"])
|
||||||
|
|
||||||
sess = Session()
|
#Keep only questions matching course or branch
|
||||||
maxQuest = len(QUESTIONS.childs) - 1
|
for q in QUESTIONS.index.keys():
|
||||||
|
if (QUESTIONS.index[q]["branch"] is not None and QUESTIONS.index[q]["branch"].find(categ)) or QUESTIONS.index[q]["course"] in courses:
|
||||||
|
QS.append(q)
|
||||||
|
else:
|
||||||
|
for q in QUESTIONS.index.keys():
|
||||||
|
QS.append(q)
|
||||||
|
|
||||||
|
nbQuest = min(nbQuest, len(QS))
|
||||||
|
|
||||||
|
if channel:
|
||||||
|
sess = Session(msg.srv, msg.channel, msg.channel)
|
||||||
|
else:
|
||||||
|
sess = Session(msg.srv, msg.channel, msg.sender)
|
||||||
|
maxQuest = len(QS) - 1
|
||||||
for i in range(0, nbQuest):
|
for i in range(0, nbQuest):
|
||||||
while True:
|
while True:
|
||||||
q = QUESTIONS.childs[random.randint(0, maxQuest)]
|
q = QS[random.randint(0, maxQuest)]
|
||||||
if q["xml:id"] is not None and q["validated"] == "1" and sess.addQuestion(q["xml:id"]):
|
if sess.addQuestion(q):
|
||||||
break
|
break
|
||||||
SESSIONS[user] = sess
|
if channel:
|
||||||
|
SESSIONS[msg.channel] = sess
|
||||||
|
else:
|
||||||
|
SESSIONS[msg.sender] = sess
|
||||||
|
|
||||||
|
|
||||||
def askQuestion(msg, bfr = ""):
|
def askQuestion(msg, bfr = ""):
|
||||||
nextQ = SESSIONS[msg.sender].next_question()
|
SESSIONS[msg.sender].askNext(bfr)
|
||||||
if nextQ is not None:
|
|
||||||
msg.send_chn("%s: %s%s" % (msg.sender, bfr, nextQ.question))
|
|
||||||
else:
|
|
||||||
sess = SESSIONS[msg.sender]
|
|
||||||
if sess.good > 1:
|
|
||||||
goodS = "s"
|
|
||||||
else:
|
|
||||||
goodS = ""
|
|
||||||
msg.send_chn("%s: %sFini, tu as donné %d bonne%s réponse%s sur %d questions." % (msg.sender, bfr, sess.good, goodS, goodS, len(sess.questions)))
|
|
||||||
del SESSIONS[msg.sender]
|
|
||||||
|
|
||||||
|
|
||||||
def parseanswer(msg):
|
def parseanswer(msg):
|
||||||
global DATAS
|
global DATAS, SESSIONS
|
||||||
if msg.cmd[0] == "qcm":
|
if msg.cmd[0] == "qcm" or msg.cmd[0] == "qcmchan" or msg.cmd[0] == "simulateqcm":
|
||||||
if msg.sender in SESSIONS:
|
if msg.sender in SESSIONS:
|
||||||
msg.send_chn("%s: tu as déjà une session de QCM en cours, finis-la avant d'en commencer une nouvelle." % msg.sender)
|
if len(msg.cmd) > 1:
|
||||||
else:
|
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
||||||
buildSession(msg.sender)
|
sess = SESSIONS[msg.sender]
|
||||||
|
if sess.good > 1: goodS = "s"
|
||||||
|
else: goodS = ""
|
||||||
|
msg.send_chn("%s: Fini, tu as donné %d bonne%s réponse%s sur %d questions." % (msg.sender, sess.good, goodS, goodS, sess.current))
|
||||||
|
del SESSIONS[msg.sender]
|
||||||
|
return True
|
||||||
|
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
||||||
askQuestion(msg)
|
askQuestion(msg)
|
||||||
return True
|
return True
|
||||||
|
msg.send_chn("%s: tu as déjà une session de QCM en cours, finis-la avant d'en commencer une nouvelle." % msg.sender)
|
||||||
|
elif msg.channel in SESSIONS:
|
||||||
|
if len(msg.cmd) > 1:
|
||||||
|
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
||||||
|
sess = SESSIONS[msg.channel]
|
||||||
|
if sess.good > 1: goodS = "s"
|
||||||
|
else: goodS = ""
|
||||||
|
msg.send_chn("Fini, vous avez donné %d bonne%s réponse%s sur %d questions." % (sess.good, goodS, goodS, sess.current))
|
||||||
|
del SESSIONS[msg.channel]
|
||||||
|
return True
|
||||||
|
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
||||||
|
SESSIONS[msg.channel].prepareNext(1)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
nbQuest = 5
|
||||||
|
filtre = list()
|
||||||
|
if len(msg.cmd) > 1:
|
||||||
|
for cmd in msg.cmd[1:]:
|
||||||
|
try:
|
||||||
|
tmp = int(cmd)
|
||||||
|
nbQuest = tmp
|
||||||
|
except ValueError:
|
||||||
|
filtre.append(cmd.upper())
|
||||||
|
if len(filtre) == 0:
|
||||||
|
filtre = None
|
||||||
|
if msg.channel in SESSIONS:
|
||||||
|
msg.send_snd("Il y a deja une session de QCM sur ce chan.")
|
||||||
|
else:
|
||||||
|
buildSession(msg, filtre, nbQuest, msg.cmd[0] == "qcmchan")
|
||||||
|
if msg.cmd[0] == "qcm":
|
||||||
|
askQuestion(msg)
|
||||||
|
elif msg.cmd[0] == "qcmchan":
|
||||||
|
SESSIONS[msg.channel].askNext()
|
||||||
|
else:
|
||||||
|
msg.send_chn("QCM de %d questions" % len(SESSIONS[msg.sender].questions))
|
||||||
|
del SESSIONS[msg.sender]
|
||||||
|
return True
|
||||||
elif msg.sender in SESSIONS:
|
elif msg.sender in SESSIONS:
|
||||||
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
||||||
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % SESSIONS[msg.sender].question.tupleInfo)
|
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % SESSIONS[msg.sender].question.tupleInfo)
|
||||||
|
return True
|
||||||
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
||||||
msg.send_chn("%s: fonction non implémentée" % msg.sender)
|
if SESSIONS[msg.sender].question.report():
|
||||||
|
msg.send_chn("Cette question vient vient d'etre signalée.")
|
||||||
|
askQuestion(msg)
|
||||||
|
else:
|
||||||
|
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
||||||
|
return True
|
||||||
|
elif msg.channel in SESSIONS:
|
||||||
|
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
||||||
|
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % SESSIONS[msg.channel].question.tupleInfo)
|
||||||
|
return True
|
||||||
|
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
||||||
|
if SESSIONS[msg.channel].question.report():
|
||||||
|
msg.send_chn("Cette question vient vient d'etre signalée.")
|
||||||
|
askQuestion(msg)
|
||||||
|
else:
|
||||||
|
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def parseask(msg):
|
def parseask(msg):
|
||||||
if msg.sender in SESSIONS:
|
if msg.sender in SESSIONS:
|
||||||
if SESSIONS[msg.sender].question.isCorrect(msg.content):
|
dest = msg.sender
|
||||||
SESSIONS[msg.sender].good += 1
|
|
||||||
SESSIONS[msg.sender].score += SESSIONS[msg.sender].question.getScore(msg.content)
|
if SESSIONS[dest].question.isCorrect(msg.content):
|
||||||
|
SESSIONS[dest].good += 1
|
||||||
|
SESSIONS[dest].score += SESSIONS[dest].question.getScore(msg.content)
|
||||||
askQuestion(msg, "correct ; ")
|
askQuestion(msg, "correct ; ")
|
||||||
else:
|
else:
|
||||||
SESSIONS[msg.sender].bad += 1
|
SESSIONS[dest].bad += 1
|
||||||
if SESSIONS[msg.sender].trys == 0:
|
if SESSIONS[dest].trys == 0:
|
||||||
SESSIONS[msg.sender].trys = 1
|
SESSIONS[dest].trys = 1
|
||||||
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
||||||
else:
|
else:
|
||||||
askQuestion(msg, "non, la bonne reponse était : %s ; " % SESSIONS[msg.sender].question.bestAnswer)
|
askQuestion(msg, "non, la bonne reponse était : %s ; " % SESSIONS[dest].question.bestAnswer)
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif msg.channel in SESSIONS:
|
||||||
|
dest = msg.channel
|
||||||
|
|
||||||
|
if SESSIONS[dest].question.isCorrect(msg.content):
|
||||||
|
SESSIONS[dest].good += 1
|
||||||
|
SESSIONS[dest].score += SESSIONS[dest].question.getScore(msg.content)
|
||||||
|
msg.send_chn("%s: correct :)" % msg.sender)
|
||||||
|
SESSIONS[dest].prepareNext()
|
||||||
|
else:
|
||||||
|
SESSIONS[dest].bad += 1
|
||||||
|
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<nemubotmodule name="qcm">
|
<nemubotmodule name="qcm">
|
||||||
<file url="/var/www/nemunai.re/bot/htdocs/questions.xml"/>
|
<file name="main" url="/var/www/nemunai.re/bot/htdocs/questions.xml"/>
|
||||||
|
<file name="courses" url="/var/www/nemunai.re/bot/htdocs/courses.xml"/>
|
||||||
|
<file name="users" url="/var/www/nemunai.re/bot/htdocs/users.xml"/>
|
||||||
|
<server url="bot.nemunai.re" />
|
||||||
</nemubotmodule>
|
</nemubotmodule>
|
@ -398,7 +398,6 @@ class GameUpdater(threading.Thread):
|
|||||||
|
|
||||||
if rnd != 0:
|
if rnd != 0:
|
||||||
QUESTIONS = CONF.getNodes("question")
|
QUESTIONS = CONF.getNodes("question")
|
||||||
print (QUESTIONS)
|
|
||||||
|
|
||||||
if self.msg.channel == "#nemutest":
|
if self.msg.channel == "#nemutest":
|
||||||
quest = 9
|
quest = 9
|
||||||
|
Loading…
Reference in New Issue
Block a user