Modify MCQ module to follow changes on the website part

Add channel MCQ
This commit is contained in:
Némunaire 2012-06-19 22:23:25 +02:00
parent eb82d0898d
commit 61ac7b11f5
3 changed files with 250 additions and 47 deletions

View File

@ -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

View File

@ -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>

View File

@ -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