A idéia deste pequeno programa veio de uma conversa com um colega (Prof. Anibal, que já utiliza uma versão DOS para as suas provas).
O programa gera, a partir do banco de questões, tantas provas diferentes e com a quantidade de questões indicadas pelo usuário.
As questões devem estar em um arquivo texto com o seguinte layout:
Q: pergunta
A: resposta 1
A: resposta 2 [opcional]
Uma pergunta pode ter várias respostas (aspectos). Neste caso, a pergunta, para ter sentido, deve ser do tipo: "Dentre as características de XXXX, está" O programa selecionará aleatoriamente, para cada prova, uma das respostas da pergunta; poder-se-á ter então, provas que tenham uma pergunta com o mesmo enunciado mas com resposta diferente. Isto é bom! Dificulta a "cola".
As perguntas podem ser separadas por linhas em branco no arquivo de questões. Mas isto é opcional.
Atualmente, o programa gera um arquivo .tex com a prova (prova_NN.tex) e um arquivo gabarito com as respostas das provas (prova_gb.tex). Futuramente, outros formatos de saída deverão ser adicionados, bem como uma interface gráfica para facilitar o uso.
Cada prova é identificada por um código de barras que contém o gabarito. Veja no código fonte informações importantes sobre os identificadores.
No arquivo gabarito, as respostas são identificadas pelo mesmo código de barras. Veja no código fonte mais informações sobre os identificadores no gabarito.
Após gerados, os arquivos das provas podem ser editados para a inserção de outras questões ou ajuste fino na formatação.
O código está abaixo, e pode ser baixado daqui
#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-
import random, sys, os
# define a quantidade maxima de questoes/respostas: 702 por enquanto.
letras_1 = ['A','B','C','D','E','F','G','H','I','J', 'K','L', 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
letras_2 = [x+y for x in letras_1 for y in letras_1]
letras = letras_1 + letras_2
identificadores = ['.-' , ',+' , ':-' , '=+', ';.', '...', ';;;', '+++', '~~~', '***']
def lequestoes(arquivos):
""" funcao que le os arquivos e cria uma lista com todas as perguntas, que sao dicionarios"""
print 'crialista', arquivos
listao = []
respostas = []
d = dict()
# estados possiveis: fora de alguma questao
# dentro de uma questao - Q - pergunta
# dentro de uma questao - A - respostas
# quando esta fora de uma questao e encontra um Q, deve entrar em modo questao, adicionar a pergunta ao
# dicionario
# quando esta em modo questao e encontra um A, deve entrar em modo respostas, adicionar a resposta ao
# dicionario
# quando esta em modo respostas e encontra um A, adiciona a resposta ao dicionario
# quando esta em modo respostas e encontra um Q, deve entrar em modo questao, adicionar o dicionario
# ao listao de perguntas, zerar o dicionario e as respostas
# quando esta em qualquer modo e encontra uma linha em branco, passa a proxima linha
# quando esta em modo questao e encontra um Q, significa erro no arquivo: retorna ERRO.
for a in arquivos:
arq = open(a)
lista = []
todaslinhas = arq.readlines()
respostas = []
estado = 'OUT'
for linha in todaslinhas:
if linha.startswith('Q:') and estado == 'OUT': # nova questao
d = dict()
estado = 'Q' # entra em modo questao
linha = linha.lstrip(' ')
d["q"] = linha[3:]
d["t"] = linha[0]
elif linha.startswith('Q') and estado == 'Q': # outra linha com Q -> erro
print "Arquivo %s de questoes com erro linha %d " % (a, todaslinhas.index(linha))
sys.exit(-1)
if linha.startswith('A') and (estado == 'Q' or estado == 'A'):
estado = 'A' # entra em modo resposta
linha = linha.lstrip(' ')
if len(linha) > 4:
respostas.append(linha[3:])
d["a"] = respostas
else:
print "linha len < 4"
if linha.startswith('Q') and estado == 'A': # nova questao
listao.append(d)
respostas = []
d = dict()
estado = 'Q'
linha = linha.lstrip(' ')
d["q"] = linha[3:]
d["t"] = linha[0]
if linha.startswith('\n'): # linha em branco separando questao
if estado == 'A' and len(d):
listao.append(d)
respostas = []
d = dict()
estado = 'OUT'
pass
# fim do loop
if estado == 'A' and len(d): # terminaram as linhas e o dicionario contem dados
listao.append(d)
estado = 'OUT'
d = {}
respostas = []
arq.close()
return listao
def criasequencia(listao):
""" cria uma sequencia (lista) de tuplas (q,a) a partir do listao geral de questoes """
sequencia = [] # todas as possibilidades de perguntas (tuplas do tipo (Q,A))
for q in listao:
perg = q['q']
resp = random.choice(q['a'])
sequencia.append((perg,resp))
if q['t'] != 'QNI':
pass
return sequencia
def criaprovas(sequencia, numprovas,questaoporprova):
""" cada prova eh gerada aleatoriamente a partir da lista de tuplas com todas as questoes.
recebe como argumentos: uma lista de tuplas (q,a), o num de provas a serem geradas e
quantas questoes cada prova deve ter.
retorna as provas geradas e os gabaritos; as provas sao listas de tuplas (q,a) e os gabaritos sao listas de tuplas (num questao, letra resposta)
"""
provas = []
gabaritos = []
for np in range(numprovas):
prova = random.sample(sequencia,questaoporprova)
provas.append(prova)
perguntas = []
respostas = []
embaralha = range(len(prova))
random.shuffle(embaralha)
gabarito = []
gaba_str = ''
for i in range(len(prova)):
gabarito.append((i+1,letras[embaralha[i]]))
gabaritos.append(gabarito)
return provas, gabaritos
def crialatex(sequencia, numprovas,questaoporprova):
""" cria um arquivo .tex para cada prova e tb o arquivo gabarito para as provas
recebe como argumentos: uma lista de tuplas (q,a), o num de provas a serem geradas e
quantas questoes cada prova tera.
cada prova eh gerada aleatoriamente a partir da lista de tuplas com todas as questoes.
retorna as provas geradas.
"""
preambulo1 = """
\documentclass[10pt,brazil,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage[portuguese]{babel}
%\usepackage{graphicx}
%\usepackage{multicol}
%\usepackage{shadow}
%\usepackage{pifont}
%\usepackage{listings}
%\usepackage{fancyvrb}
%\usepackage{boxedminipage}
%\usepackage{theorem}
\usepackage{verbatim}
\usepackage{tabularx}
%\usepackage{moreverb}
\usepackage{times}
%\usepackage{relsize}
\usepackage{pst-barcode}
\setlength{\\textwidth}{180mm}
\setlength{\\oddsidemargin}{-0.5in}
\setlength{\\evensidemargin}{0in}
\setlength{\\columnsep}{8mm}
\setlength{\\topmargin}{-28mm}
\setlength{\\textheight}{265mm}
\setlength{\\itemsep}{0in}
\\begin{document}
\\pagestyle{empty}
%\lstset{language=python}
"""
preambulo2= """
\\textbf{Curso:}\\rule{11cm}{0.1pt}\hspace{0.5cm}\\textbf{Turma:}\\rule{3cm}{0.1pt}
\\textbf{Nome:}\\rule{11cm}{0.1pt} \hspace{0.5cm}\\textbf{nota:} \\rule{3cm}{0.1pt}
{\scriptsize
\\textbf{Instruções:}
\\begin{verbatim}
1. Proibida a consulta de livros ou anotações 2. Permitido o uso de calculadoras eletrônicas
3. Somente serão consideradas as respostas da Parte 1 na região "Gabarito" desta página
\end{verbatim}
}
\\begin{tabularx}{\linewidth}{X|X}
\\textbf{Parte 1 -- Questões} """
preambulo3 = """
& \\textbf{Respostas} \\\\ \hline \hline
%& \\\\
"""
finaltabela = """
\end{tabularx}
%\\vspace{0.3cm}
\\textbf{Parte 1 -- Gabarito:}
"""
fimdocumento = """
\end{document}
"""
gabatabela = """
\\begin{tabular}[t]{|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|b{10mm}|} \\\\ \hline
"""
titulo = "\\textbf{\\centering \\large %s}" % tituloprova
provas = []
arqgaba = open('prova_'+'gb.tex','w')
arqgaba.write(preambulo1.decode('utf-8').encode("latin1"))
titulogaba = "%s \\textbf{\\large GABARITO}" % titulo
arqgaba.write(titulogaba.decode('utf-8').encode('latin1'))
for np in range(numprovas):
prova = random.sample(sequencia,questaoporprova)
arqprova = open('prova_'+str(np)+'.tex','w')
arqprova.write(preambulo1.decode('utf-8').encode("latin1"))
arqprova.write(titulo.decode('utf-8').encode("latin1"))
arqprova.write(preambulo2.decode('utf-8').encode("latin1"))
arqprova.write("%s" % (identificadores[np]))
arqprova.write(preambulo3.decode('utf-8').encode("latin1"))
#arqprova.write(initabela)
provas.append(prova)
perguntas = []
respostas = []
embaralha = range(len(prova))
random.shuffle(embaralha)
gabarito = []
gaba_str = ''
for i in range(len(prova)):
arqprova.write("%d) %s & \n".encode('latin1') % (i+1, prova[embaralha[i]][0]))
gabarito.append((i+1,letras[embaralha[i]]))
gaba_str += letras[embaralha[i]]
arqprova.write("%s) %s \\\\ \n".encode("latin1") % (letras[i],prova[i][1]))
arqprova.write(finaltabela)
#arqprova.write("%s" % (identificadores[np]))
arqprova.write("\\hspace{1cm}\\begin{pspicture}(1,1in)\n")
# a linha abaixo gera o codigo de barras do identificador (duplicado) da prova,
# inclui o texto no codigo de barras
#arqprova.write("\\psbarcode[scalex=0.9,scaley=0.3]{%s%s}{includetext}{code39}\n" % (np,np))
# gera codigo de barras usando a string do gabarito, nao inclui o texto no codigo de barras.
arqprova.write("\\psbarcode[scalex=0.9,scaley=0.3]{%s}{}{code39}\n" % (gaba_str))
arqprova.write("\\end{pspicture}\n")
arqprova.write(gabatabela.encode('latin1'))
for l in range(len(prova)):
arqprova.write("{\\raggedright \\raisebox{0.8ex}[2.5ex][0.75ex]{%d}} ".encode("latin1") % (l+1))
if l > 0 and (l+1) % 10 == 0:
arqprova.write("\\\\ \hline \n")
else:
arqprova.write("&")
arqprova.write("\\end{tabular}")
arqprova.write(fimdocumento)
arqgaba.write("\n\n\n PROVA")
arqgaba.write("%s" % (identificadores[np]))
arqgaba.write("\\hspace{1cm}\\begin{pspicture}(1,1in)\n")
# a linha abaixo gera o codigo de barras do identificador (duplicado) da prova,
# inclui o texto no codigo de barras
#arqprova.write("\\psbarcode[scalex=0.9,scaley=0.3]{%s%s}{includetext}{code39}\n" % (np,np))
# gera codigo de barras usando a string do gabarito, nao inclui o texto no codigo de barras.
arqgaba.write("\\psbarcode[scalex=0.9,scaley=0.3]{%s}{}{code39}\n" % (gaba_str))
arqgaba.write("\\end{pspicture}\n")
arqgaba.write(gabatabela.encode('latin1'))
for l in range(len(prova)):
arqgaba.write("{\\raggedright \\raisebox{0.8ex}[2.5ex][0.75ex]{%d}} %s ".encode("latin1") % (gabarito[l]))
if l > 0 and (l+1) % 10 == 0:
arqgaba.write("\\\\ \hline \n")
else:
arqgaba.write("&")
arqgaba.write("\\end{tabular}")
arqprova.close()
arqgaba.write(fimdocumento)
arqgaba.close()
return provas
tituloprova = "Avaliação Bimestral"
def main():
if len(sys.argv) > 1:
arquivos = sys.argv[3:]
numprovas = int(sys.argv[1])
questaoporprova = int(sys.argv[2])
print arquivos
else:
numprovas = int(raw_input("entre o numero de provas: "))
questaoporprova = int(raw_input("entre o numero de questoes por prova: "))
entrada = raw_input("entre os nomes dos arquivos, separados por espaco: ")
entrada.rstrip('\n')
arquivos = entrada.split(' ')
global tituloprova
tituloprova= raw_input("Digite o titulo da prova: ")
lista = lequestoes(arquivos)
sequencia = criasequencia(lista)
provas = crialatex(sequencia, numprovas, questaoporprova)
if __name__ == '__main__':
main()
6 comentários:
Enviei a dica para alguns professores meus da Faculdade eles vão querer aprender python.
Uma boa seria um exemplo prático. Tipo uma prova com 3 questões e mostrar uma imagem de como fica o impresso dela (já transformado a partir do tex). Só uma sugestão para aumentar o interesse na ferramenta... ;-)
@bardo
Obrigado pela sugestão.
Veja a última atualização do script
criaprova
Tem um exemplo do resultado final.
Cara, muito bom mesmo..
Parabéns!! Queria ser seu aluno hehehe
@chackal_sjc
Obrigado!
Eu também achei fantástico, o dificil é desenvolver um script que ache as respostas corretas para a prova dele.
Arnaldo
Postar um comentário