id INTEGER PRIMARY KEY,
enunciado_id INT CONSTRAINT enunciado_id_exists REFERENCES enunciado(id),
nombre VARCHAR(40) NOT NULL,
- parametros TEXT NOT NULL,
+ parametros VARCHAR(255) NOT NULL,
retorno INT,
tiempo_cpu FLOAT,
descripcion VARCHAR(255)
c = Curso(2007, 1, 1, u'Martes', [d], [e1, e2])
cp1 = e1.add_caso_de_prueba(u'Sin parámetros', retorno=0, descripcion=u'Un caso')
-cp2 = e1.add_caso_de_prueba(u'2 parámetross', retorno=0, parametros=('--test', '-c'))
+cp2 = e1.add_caso_de_prueba(u'2 parámetross', retorno=0, parametros='--test -c')
ej1 = c.ejercicios[0]
ej1.grupal = True
from sqlobject import *
from sqlobject.sqlbuilder import *
from sqlobject.inheritance import InheritableSQLObject
-from sqlobject.col import PickleValidator
+from sqlobject.col import PickleValidator, UnicodeStringValidator
from turbogears import identity
from turbogears.identity import encrypt_password as encryptpw
+from sercom.validators import params_to_list, ParseError
+from formencode import Invalid
hub = PackageHub("sercom")
__connection__ = hub
Validator for tuple types. A tuple type is simply a pickle type
that validates that the represented type is a tuple.
"""
-
def to_python(self, value, state):
value = super(TupleValidator, self).to_python(value, state)
if value is None:
return None
if isinstance(value, tuple):
return value
- raise validators.Invalid("expected a tuple in the TupleCol '%s', got %s %r instead" % \
+ raise Invalid("expected a tuple in the TupleCol '%s', got %s %r instead" % \
(self.name, type(value), value), value, state)
-
def from_python(self, value, state):
if value is None:
return None
if not isinstance(value, tuple):
- raise validators.Invalid("expected a tuple in the TupleCol '%s', got %s %r instead" % \
+ raise Invalid("expected a tuple in the TupleCol '%s', got %s %r instead" % \
(self.name, type(value), value), value, state)
return super(TupleValidator, self).from_python(value, state)
class TupleCol(PickleCol):
baseClass = SOTupleCol
+class ParamsValidator(UnicodeStringValidator):
+ def to_python(self, value, state):
+ if isinstance(value, basestring):
+ value = super(ParamsValidator, self).to_python(value, state)
+ try:
+ value = params_to_list(value)
+ except ParseError, e:
+ raise Invalid("invalid parameters in the ParamsCol '%s', parse "
+ "error: %s" % (self.name, e), value, state)
+ elif not isinstance(value, (list, tuple)):
+ raise Invalid("expected a tuple, list or valid string in the "
+ "ParamsCol '%s', got %s %r instead"
+ % (self.name, type(value), value), value, state)
+ return value
+ def from_python(self, value, state):
+ if isinstance(value, (list, tuple)):
+ value = ' '.join([repr(p) for p in value])
+ elif isinstance(value, basestring):
+ value = super(ParamsValidator, self).to_python(value, state)
+ try:
+ params_to_list(value)
+ except ParseError, e:
+ raise Invalid("invalid parameters in the ParamsCol '%s', parse "
+ "error: %s" % (self.name, e), value, state)
+ else:
+ raise Invalid("expected a tuple, list or valid string in the "
+ "ParamsCol '%s', got %s %r instead"
+ % (self.name, type(value), value), value, state)
+ return value
+
+class SOParamsCol(SOUnicodeCol):
+ def createValidators(self):
+ return [ParamsValidator(db_encoding=self.dbEncoding, name=self.name)] \
+ + super(SOParamsCol, self).createValidators()
+
+class ParamsCol(UnicodeCol):
+ baseClass = SOParamsCol
+
#}}}
#{{{ Tablas intermedias
-
# BUG en SQLObject, SQLExpression no tiene cálculo de hash pero se usa como
# key de un dict. Workarround hasta que lo arreglen.
SQLExpression.__hash__ = lambda self: hash(str(self))
pk = DatabaseIndex(enunciado, nombre, unique=True)
# Campos
# privado = IntCol(default=None) TODO iria en instancia_de_entrega_caso_de_prueba
- parametros = TupleCol(notNone=True, default=())
+ parametros = ParamsCol(length=255)
retorno = IntCol(default=None)
tiempo_cpu = FloatCol(default=None)
descripcion = UnicodeCol(length=255, default=None)
# Joins
pruebas = MultipleJoin('Prueba')
- def __init__(self, enunciado=None, nombre=None, parametros=(),
+ def __init__(self, enunciado=None, nombre=None, parametros=None,
retorno=None, tiempo_cpu=None, descripcion=None, **kargs):
SQLObject.__init__(self, enunciadoID=enunciado and enunciado.id,
nombre=nombre, parametros=parametros, retorno=retorno,
from docutils.core import publish_parts
from sercom.subcontrollers import validate as val
from sercom.model import CasoDePrueba, Enunciado
+from sercom.validators import ParamsValidator
#}}}
#{{{ Configuración
W.TextField(name='descripcion', label=_(u'Descripción'),
validator=V.UnicodeString(not_empty=False, max=255,
strip=True)),
+ W.TextField(name='parametros', label=_(u'Parámetros'),
+ validator=ParamsValidator(not_empty=False, strip=True)),
W.TextField(name='retorno', label=_(u'Código de retorno'),
validator=V.Int(not_empty=False, strip=True)),
W.TextField(name='tiempo_cpu', label=_(u'Tiempo de CPU'),
#}}}
#{{{ Controlador
+
+def params2str(params):
+ return ' '.join([repr(p)[1:] for p in params])
+
class CasoDePruebaController(controllers.Controller, identity.SecureResource):
"""Basic model admin interface"""
require = identity.has_permission('admin')
r = cls.select()
else:
r = cls.selectBy(enunciadoID=enunciado)
- return dict(records=r, name=name, namepl=namepl, parcial=enunciado)
+ return dict(records=r, name=name, namepl=namepl, parcial=enunciado,
+ params2str=params2str)
@expose(template='kid:%s.templates.new' % __name__)
def new(self, **kw):
def edit(self, id, **kw):
"""Edit record in model"""
r = validate_get(id)
- return dict(name=name, namepl=namepl, record=r, form=form)
+ return dict(name=name, namepl=namepl, record=r, form=form,
+ params2str=params2str)
@validate(form=form)
@error_handler(edit)
r.desc = ''
else:
r.desc = publish_parts(r.descripcion, writer_name='html')['html_body']
- return dict(name=name, namepl=namepl, record=r)
+ return dict(name=name, namepl=namepl, record=r, params2str=params2str)
@expose()
def delete(self, id):
href="${tg.url('/enunciado/show/%d' % record.enunciado.id)}"><span
py:replace="tg.summarize(record.enunciado.shortrepr(), 30)">enunciado</span></a></td>
<td><span py:replace="tg.summarize(record.descripcion, 30)">descripción</span></td>
- <td><span py:if="record.parametros" py:replace="tg.summarize(' '.join([repr(p) for p in record.parametros]), 30)">--parametros</span></td>
+ <td><span py:if="record.parametros" py:replace="tg.summarize(params2str(record.parametros), 30)">--parámetros</span></td>
<td><span py:replace="record.retorno">retorno</span></td>
<td><span py:replace="record.tiempo_cpu">tiempo de cpu</span></td>
<td><a href="${tg.url('/caso_de_prueba/edit/%d' % record.id)}">Editar</a>
<th>Descripción:</th>
<td><span py:replace="XML(record.desc)">descripcion</span></td>
</tr>
+ <tr>
+ <th>Parámetros:</th>
+ <td>
+ <span py:if="record.parametros" py:replace="params2str(record.parametros)">--parámetros</span>:
+ </td>
+ </tr>
<tr>
<th>Código de retorno:</th>
<td><span py:replace="record.retorno">retorno</span></td>
<th>Tiempo de CPU:</th>
<td><span py:replace="record.tiempo_cpu">tiempo_cpu</span></td>
</tr>
- <tr>
- <th>Parámetros:</th>
- <td>
- <span py:for="p in record.parametros">
- <span py:replace="p">parámetro</span>
- </span>
- </td>
- </tr>
</table>
<br/>
--- /dev/null
+# vim: set et sw=4 sts=4 encoding=utf-8 :
+
+from turbogears.validators import *
+
+class ParseError(ValueError): pass
+
+def params_to_list(params):
+ r"""Parsea un string de forma similar al bash, separando por espacios y
+ teniendo en cuenta comillas simples y dobles para agrupar. Para poner
+ comillas se puede usar el \ como caracter de escape (\' y \") y también
+ interpreta \n y \t. Devuelve una lista con los parámetros encontrados.
+ >>> param2seq('--prueba')
+ ['--prueba']
+ >>> params_to_list('--prueba larga "con espacios"')
+ ['--prueba', 'larga', 'con espacios']
+ >>> params_to_list(u'''"con enter\\nentre 'comillas \\"dobles\\"'" --unicode''')
+ [u'con enter\nentre \'comillas "dobles"\'', u'--unicode']
+ >>> params_to_list('"archivo\\tseparado\\tpor\\ttabs" -h')
+ ['archivo\tseparado\tpor\ttabs', '-h']
+ """
+ # Constantes
+ SEP, TOKEN, DQUOTE, SQUOTE = ' ', None, '"', "'"
+ seq = []
+ buff = ''
+ escape = False
+ state = SEP
+ if not params: return seq
+ for c in params:
+ # Es un caracter escapado
+ if escape:
+ if c == 'n':
+ buff += '\n'
+ elif c == 't':
+ buff += '\t'
+ else:
+ buff += c
+ escape = False
+ continue
+ # Es una secuencia de escape
+ if c == '\\':
+ escape = True
+ continue
+ # Si está buscando espacios
+ if state == SEP:
+ if c == SEP:
+ continue
+ else:
+ state = TOKEN # Encontró
+ if state == TOKEN:
+ if c == DQUOTE:
+ state = DQUOTE
+ continue
+ if c == SQUOTE:
+ state = SQUOTE
+ continue
+ if c == SEP:
+ state = SEP
+ seq.append(buff)
+ buff = ''
+ continue
+ buff += c
+ continue
+ if state == DQUOTE:
+ if c == DQUOTE:
+ state = TOKEN
+ continue
+ buff += c
+ continue
+ if state == SQUOTE:
+ if c == SQUOTE:
+ state = TOKEN
+ continue
+ buff += c
+ continue
+ raise ParseError, 'Invalid syntax'
+ if state == DQUOTE or state == SQUOTE:
+ raise ParseError, 'Missing closing quote %s' % state
+ if buff:
+ seq.append(buff)
+ return seq
+
+class ParamsValidator(UnicodeString):
+ def validate_python(self, value, state):
+ try:
+ params_to_list(value)
+ except ParseError, e:
+ raise Invalid(str(e), value, state)
+