From e6d1e37559beee0064df00956fda8b7e6c9d68b2 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Thu, 22 Feb 2007 14:15:56 +0000 Subject: [PATCH] =?utf8?q?Convertir=20parametros=20de=20CasoDePrueba=20en?= =?utf8?q?=20un=20campo=20de=20string=20con=20validador=20especial.=20Guar?= =?utf8?q?dar=20los=20par=C3=A1metros=20como=20una=20tupla=20puede=20ser?= =?utf8?q?=20c=C3=B3modo=20pero=20al=20usar=20Pickle,=20la=20base=20queda?= =?utf8?q?=20ilegible=20e=20inusable=20desde=20algo=20que=20no=20sea=20Pyt?= =?utf8?q?hon.=20Por=20lo=20tanto,=20y=20porque=20es=20m=C3=A1s=20natural?= =?utf8?q?=20ingresar=20los=20par=C3=A1metros=20como=20un=20string=20al=20?= =?utf8?q?estilo=20bash,=20se=20cre=C3=B3=20un=20nuevo=20tipo=20de=20colum?= =?utf8?q?na=20de=20SQLObject=20que=20valida/convierte=20el=20string=20alm?= =?utf8?q?acenado=20en=20una=20lista=20y=20viceversa,=20permitiendo=20usar?= =?utf8?q?=20string=20o=20listas=20indistintamente,=20pero=20siempre=20alm?= =?utf8?q?acen=C3=A1ndose=20como=20string.=20IMPORTANTE:=20Hay=20que=20reg?= =?utf8?q?enerar=20la=20DB=20y=20tiene=20un=20"bug",=20al=20editar,=20los?= =?utf8?q?=20datos=20de=20la=20DB=20se=20"renderizan"=20como=20una=20lista?= =?utf8?q?=20en=20el=20formulario,=20hay=20que=20ver=20una=20forma=20de=20?= =?utf8?q?renderizarlo=20como=20en=20el=20list/show.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- doc/schema/schema.sql | 2 +- doc/testdata.py | 2 +- sercom/model.py | 53 +++++++++-- .../subcontrollers/caso_de_prueba/__init__.py | 15 +++- .../caso_de_prueba/templates/list.kid | 2 +- .../caso_de_prueba/templates/show.kid | 14 ++- sercom/validators.py | 88 +++++++++++++++++++ 7 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 sercom/validators.py diff --git a/doc/schema/schema.sql b/doc/schema/schema.sql index c34f8f8..3cc2db8 100644 --- a/doc/schema/schema.sql +++ b/doc/schema/schema.sql @@ -64,7 +64,7 @@ CREATE TABLE caso_de_prueba ( 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) diff --git a/doc/testdata.py b/doc/testdata.py index 10fc64b..b98618b 100644 --- a/doc/testdata.py +++ b/doc/testdata.py @@ -21,7 +21,7 @@ e3 = d.add_enunciado(u'Más enunciados', u'Ejercicio anónimo') 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 diff --git a/sercom/model.py b/sercom/model.py index 06c132d..dff54ae 100644 --- a/sercom/model.py +++ b/sercom/model.py @@ -5,9 +5,11 @@ from turbogears.database import PackageHub 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 @@ -21,21 +23,19 @@ class TupleValidator(PickleValidator): 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) @@ -47,11 +47,48 @@ class SOTupleCol(SOPickleCol): 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)) @@ -364,14 +401,14 @@ class CasoDePrueba(SQLObject): #{{{ 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, diff --git a/sercom/subcontrollers/caso_de_prueba/__init__.py b/sercom/subcontrollers/caso_de_prueba/__init__.py index a65f41e..62435c6 100644 --- a/sercom/subcontrollers/caso_de_prueba/__init__.py +++ b/sercom/subcontrollers/caso_de_prueba/__init__.py @@ -10,6 +10,7 @@ from turbogears import paginate 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 @@ -64,6 +65,8 @@ class CasoDePruebaForm(W.TableForm): 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'), @@ -75,6 +78,10 @@ form = CasoDePruebaForm() #}}} #{{{ 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') @@ -97,7 +104,8 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): 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): @@ -117,7 +125,8 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): 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) @@ -136,7 +145,7 @@ class CasoDePruebaController(controllers.Controller, identity.SecureResource): 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): diff --git a/sercom/subcontrollers/caso_de_prueba/templates/list.kid b/sercom/subcontrollers/caso_de_prueba/templates/list.kid index c00f57c..62b9653 100644 --- a/sercom/subcontrollers/caso_de_prueba/templates/list.kid +++ b/sercom/subcontrollers/caso_de_prueba/templates/list.kid @@ -25,7 +25,7 @@ href="${tg.url('/enunciado/show/%d' % record.enunciado.id)}">enunciado descripción - --parametros + --parámetros retorno tiempo de cpu Editar diff --git a/sercom/subcontrollers/caso_de_prueba/templates/show.kid b/sercom/subcontrollers/caso_de_prueba/templates/show.kid index bafecc1..70f5060 100644 --- a/sercom/subcontrollers/caso_de_prueba/templates/show.kid +++ b/sercom/subcontrollers/caso_de_prueba/templates/show.kid @@ -22,6 +22,12 @@ Descripción: descripcion + + Parámetros: + + --parámetros: + + Código de retorno: retorno @@ -30,14 +36,6 @@ Tiempo de CPU: tiempo_cpu - - Parámetros: - - - parámetro - - -
diff --git a/sercom/validators.py b/sercom/validators.py new file mode 100644 index 0000000..4779006 --- /dev/null +++ b/sercom/validators.py @@ -0,0 +1,88 @@ +# 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) + -- 2.43.0