]> git.llucax.com Git - software/sercom.git/commitdiff
mergeador de grupos
authortailor <ndimov@gmail.com>
Sun, 11 Mar 2007 22:36:04 +0000 (22:36 +0000)
committertailor <ndimov@gmail.com>
Sun, 11 Mar 2007 22:36:04 +0000 (22:36 +0000)
* Pantalla para mezclar los grupos. Solo se puede acceder desde la edicion de un
  curso, porque solo se puede trabajar con grupos de un solo curso.
* Crea nuevos cursos si es necesario, elimina los que quedan vacios.
* Todavia tiene algunos bugs.. Pero mas o menos anda.

12 files changed:
sercom/controllers.py
sercom/subcontrollers/__init__.py
sercom/subcontrollers/curso/templates/edit.kid
sercom/subcontrollers/grupo/__init__.py
sercom/subcontrollers/grupo/templates/list.kid
sercom/subcontrollers/grupo_admin/__init__.py [new file with mode: 0644]
sercom/subcontrollers/grupo_admin/templates/__init__.py [new file with mode: 0644]
sercom/subcontrollers/grupo_admin/templates/edit.kid [new file with mode: 0644]
sercom/subcontrollers/grupo_admin/templates/list.kid [new file with mode: 0644]
sercom/subcontrollers/grupo_admin/templates/new.kid [new file with mode: 0644]
sercom/subcontrollers/grupo_admin/templates/show.kid [new file with mode: 0644]
sercom/widgets.py

index 3bd0d8af0106f1c392d53eb64e3c607069cb16f2..9f8283867bb52b7980db2376b1bacf3f421d4718 100644 (file)
@@ -46,7 +46,7 @@ class Root(controllers.RootController):
                         .orderBy(InstanciaDeEntrega.q.fin))
             return dict(a_corregir=correcciones,
                 instancias_activas=instancias, now=now)
                         .orderBy(InstanciaDeEntrega.q.fin))
             return dict(a_corregir=correcciones,
                 instancias_activas=instancias, now=now)
-        
+
         if 'entregar' in identity.current.permissions:
             # Proximas instancias de entrega
             instancias = list(InstanciaDeEntrega.select(
         if 'entregar' in identity.current.permissions:
             # Proximas instancias de entrega
             instancias = list(InstanciaDeEntrega.select(
@@ -135,6 +135,8 @@ class Root(controllers.RootController):
 
     mis_correcciones = MisCorreccionesController()
 
 
     mis_correcciones = MisCorreccionesController()
 
+    grupo_admin = GrupoAdminController()
+
 #{{{ Agrega summarize a namespace tg de KID
 def summarize(text, size, concat=True, continuation='...'):
     """Summarize a string if it's length is greater than a specified size. This
 #{{{ Agrega summarize a namespace tg de KID
 def summarize(text, size, concat=True, continuation='...'):
     """Summarize a string if it's length is greater than a specified size. This
index 20d794831082aba37216999bed4d49b091f6a3e8..6ca2f5903c3fadcb0400bc01e0b5badd75e88f6e 100644 (file)
@@ -9,4 +9,5 @@ from grupo import GrupoController
 from correccion import CorreccionController
 from alumno_inscripto import AlumnoInscriptoController
 from misentregas import MisEntregasController
 from correccion import CorreccionController
 from alumno_inscripto import AlumnoInscriptoController
 from misentregas import MisEntregasController
+from grupo_admin import GrupoAdminController
 from miscorrecciones import MisCorreccionesController
 from miscorrecciones import MisCorreccionesController
index feda8f50789bc654a46efcca17aa64454b0ab9fa..294be12dd55008668fa77b06709c43a249b24545 100644 (file)
         <span py:for="d in record.docentes_to" py:strip="True">
             MochiKit.DOM.appendChildNodes("form_docentes_to", OPTION({"value":${d['id']}}, '${d['label']}'))
         </span>
         <span py:for="d in record.docentes_to" py:strip="True">
             MochiKit.DOM.appendChildNodes("form_docentes_to", OPTION({"value":${d['id']}}, '${d['label']}'))
         </span>
-        
+
         <span py:for="a in record.alumnos_inscriptos" py:strip="True">
             MochiKit.DOM.appendChildNodes("form_alumnos", OPTION({"value":${a['id']}}, '${a['label']}'))
         </span>
         <span py:for="a in record.alumnos_inscriptos" py:strip="True">
             MochiKit.DOM.appendChildNodes("form_alumnos", OPTION({"value":${a['id']}}, '${a['label']}'))
         </span>
-                               // Saco de FROM los que ya estan en TO
-                               replaceChildNodes('form_docentes_from', list(ifilterfalse(
-                                       partial(esta_en_to, $('form_docentes_to').options),
-                                       $('form_docentes_from').options
-                               )));
-               }
-               function esta_en_to (options, i) {
-                       for (j=0; j &lt; options.length; j++)
-                               if (options[j].value == i.value)
-                                       return true;
-                       return false;
-               }
+                // Saco de FROM los que ya estan en TO
+                replaceChildNodes('form_docentes_from', list(ifilterfalse(
+                    partial(esta_en_to, $('form_docentes_to').options),
+                    $('form_docentes_from').options
+                )));
+        }
+        function esta_en_to (options, i) {
+            for (j=0; j &lt; options.length; j++)
+                if (options[j].value == i.value)
+                    return true;
+            return false;
+        }
     MochiKit.DOM.addLoadEvent(init_data)
 </script>
 <body>
     MochiKit.DOM.addLoadEvent(init_data)
 </script>
 <body>
 <a href="${tg.url('/curso/from_file/%d' % record.id)}">Agregar Alumnos desde archivo</a>
     <br/>
     <br/>
 <a href="${tg.url('/curso/from_file/%d' % record.id)}">Agregar Alumnos desde archivo</a>
     <br/>
     <br/>
-<a href="${tg.url('/curso/show/%d' % record.id)}">Ver (cancela)</a> |
+<a href="${tg.url('/grupo_admin/new/%d' % record.id)}">Mezclar, Juntar, Separar Grupos</a>
+<br/>
+<br/>
+<a href="${tg.url('/curso/show/%d' % record.id)}">Ver (cancela)</a>
 <a href="${tg.url('/curso/list')}">Volver (cancela)</a>
 
 </body>
 <a href="${tg.url('/curso/list')}">Volver (cancela)</a>
 
 </body>
index 77e21c537575e4c274f7b54f84023cee17d5a594..6cb6cd1a71c055f7a47aecb70be95edb9f037ba6 100644 (file)
@@ -10,7 +10,7 @@ from turbogears import identity
 from turbogears import paginate
 from docutils.core import publish_parts
 from sercom.subcontrollers import validate as val
 from turbogears import paginate
 from docutils.core import publish_parts
 from sercom.subcontrollers import validate as val
-from sercom.model import Curso, AlumnoInscripto, Docente, Grupo, Alumno
+from sercom.model import Curso, AlumnoInscripto, Docente, Grupo, Alumno, Miembro
 from sqlobject import *
 
 from sercom.widgets import *
 from sqlobject import *
 
 from sercom.widgets import *
@@ -279,5 +279,28 @@ class GrupoController(controllers.Controller, identity.SecureResource):
             msg = u"""Se ha producido un error inesperado al buscar el registro:\n      %s""" % str(inst)
             error = True
         return dict(msg=msg, error=error)
             msg = u"""Se ha producido un error inesperado al buscar el registro:\n      %s""" % str(inst)
             error = True
         return dict(msg=msg, error=error)
+
+    @expose('json')
+    def get_alumnos(self, grupoid):
+        msg = u''
+        error=False
+        try:
+            # Busco los alumnos del grupo
+            grupo = Grupo.get(int(grupoid))
+            miembros = Miembro.selectBy(baja=None, grupo=grupo)
+            print miembros
+            integrantes = []
+            for m in miembros:
+                msg = {}
+                alumnoInscripto = AlumnoInscripto.get(m.alumno.id)
+                msg['id'] = alumnoInscripto.id
+                msg['label'] = alumnoInscripto.shortrepr()
+                integrantes.append(msg)
+        except Exception, (inst):
+            msg = u"""Se ha producido un error inesperado al buscar el registro:\n      %s""" % str(inst)
+            error = True
+            integrantes = []
+            integrantes.append(msg)
+        return dict(msg=integrantes, error=error)
 #}}}
 
 #}}}
 
index ab3c05667ff563caa0c68a6ad30588463c6a9ce4..a715913db4779c0f2e52380aa82c8face19e68f8 100644 (file)
         <th>Operaciones</th>
     </tr>
     <tr py:for="record in records">
         <th>Operaciones</th>
     </tr>
     <tr py:for="record in records">
-                       <td><a href="${tg.url('/curso/show/'+str(record.curso.id))}" py:content="record.curso.shortrepr()">curso</a></td>
+            <td><a href="${tg.url('/curso/show/'+str(record.curso.id))}" py:content="record.curso.shortrepr()">curso</a></td>
         <td><span py:replace="record.nombre">nombre</span></td>
         <td><span py:replace="record.nombre">nombre</span></td>
-                               <td><a py:if="record.responsable is not None" href="${tg.url('/alumno/show/'+str(record.responsable.alumno.id))}" py:content="record.responsable.alumno.shortrepr()"></a></td>
-                               <td>
-                                       <a href="${tg.url('/grupo/show/%d' % record.id)}">Ver</a>
-                                       <a href="${tg.url('/grupo/edit/%d' % record.id)}">Editar</a>
-                                       <a href="${tg.url('/grupo/delete/%d' % record.id)}" onclick="if (confirm('${_(u'Estás seguro? Tal vez sólo quieras desactivarlo mejor...')}')) { var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit(); };return false;">Eliminar</a>
-                               </td>
+                <td><a py:if="record.responsable is not None" href="${tg.url('/alumno/show/'+str(record.responsable.alumno.id))}" py:content="record.responsable.alumno.shortrepr()"></a></td>
+                <td>
+                    <a href="${tg.url('/grupo/show/%d' % record.id)}">Ver</a>
+                    <a href="${tg.url('/grupo/edit/%d' % record.id)}">Editar</a>
+                    <a href="${tg.url('/grupo/delete/%d' % record.id)}" onclick="if (confirm('${_(u'Estás seguro? Tal vez sólo quieras desactivarlo mejor...')}')) { var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit(); };return false;">Eliminar</a>
+                </td>
     </tr>
 </table>
 
 <br/>
 <a href="${tg.url('/grupo/new')}">Agregar</a>
     </tr>
 </table>
 
 <br/>
 <a href="${tg.url('/grupo/new')}">Agregar</a>
-
+<br/>
+<br/>
 <div py:for="page in tg.paginate.pages">
     <a py:if="page != tg.paginate.current_page"
         href="${tg.paginate.get_href(page)}">${page}</a>
 <div py:for="page in tg.paginate.pages">
     <a py:if="page != tg.paginate.current_page"
         href="${tg.paginate.get_href(page)}">${page}</a>
diff --git a/sercom/subcontrollers/grupo_admin/__init__.py b/sercom/subcontrollers/grupo_admin/__init__.py
new file mode 100644 (file)
index 0000000..b431e84
--- /dev/null
@@ -0,0 +1,335 @@
+# vim: set et sw=4 sts=4 encoding=utf-8 foldmethod=marker :
+
+#{{{ Imports
+import cherrypy
+from turbogears import controllers, expose, redirect
+from turbogears import validate, flash, error_handler
+from turbogears import validators as V
+from turbogears import widgets as W
+from turbogears import identity
+from turbogears import paginate
+from docutils.core import publish_parts
+from sercom.subcontrollers import validate as val
+from sercom.model import Curso, AlumnoInscripto, Docente, Grupo, Alumno, Miembro
+from sqlobject import *
+
+from sercom.widgets import *
+
+#}}}
+""" Administrador de grupos, mezclar, juntar, dividir"""
+#{{{ Configuración
+cls = Grupo
+name = 'grupo'
+namepl = 'grupos'
+
+#}}}
+
+#{{{ Validación
+def validate_fk(data):
+    fk = data.get(fkname + 'ID', None)
+    if fk == 0: fk = None
+    if fk is not None:
+        try:
+            fk = fkcls.get(fk)
+        except LookupError:
+            flash(_(u'No se pudo crear el nuevo %s porque el %s con '
+                'identificador %d no existe.' % (name, fkname, fk)))
+            raise redirect('new', **data)
+    data.pop(fkname + 'ID', None)
+    data[fkname] = fk
+    return fk
+
+def validate_get(id):
+    return val.validate_get(cls, name, id)
+
+def validate_set(id, data):
+    validate_fk(data)
+    return val.validate_set(cls, name, id, data)
+
+def validate_new(data):
+    validate_fk(data)
+    return val.validate_new(cls, name, data)
+
+def validate_del(id):
+    return val.validate_del(cls, name, id)
+#}}}
+
+#{{{ Formulario
+def get_docentes():
+    return [(fk1.id, fk1.shortrepr()) for fk1 in Docente.select()]
+
+def get_cursos():
+    return [(0, u'---')] + [(fk1.id, fk1.shortrepr()) for fk1 in Curso.select()]
+
+def get_gruposA():
+    return [(0, u'---')] + [(g.id, g.shortrepr()) for g in Grupo.select()]
+
+def get_gruposB():
+    return [(0, u'Nuevo Grupo')] + [(g.id, g.shortrepr()) for g in Grupo.select()]
+
+ajax = u"""
+    function alumnos_agregar_a_la_lista(texto, lista)
+    {
+        t = MochiKit.DOM.getElement(texto);
+
+        url = "/alumno/get_alumno?padron="+t.value;
+        t.value = "";
+        return url;
+    }
+
+    function err (err)
+    {
+        alert("The metadata for MochiKit.Async could not be fetched :(");
+    }
+
+    function procesar(result)
+    {
+        l = MochiKit.DOM.getElement('form_responsable_info');
+        if (result.error)
+            l.innerHTML = result.msg;
+        else
+            l.innerHTML = result.msg.value;
+    }
+
+    function onsubmit()
+    {
+        /* TODO : Validar datos y evitar el submit si no esta completo */
+
+        /* Selecciono todos los miembros si no, no llegan al controllere*/
+        l = MochiKit.DOM.getElement('form_grupos_to');
+        for (i=0; i<l.options.length; i++) {
+            l.options[i].selected = true;
+        }
+        /* Selecciono todos los miembros si no, no llegan al controllere*/
+        l = MochiKit.DOM.getElement('form_grupos_from');
+        for (i=0; i<l.options.length; i++) {
+            l.options[i].selected = true;
+        }
+
+        return true; // Dejo hacer el submit
+    }
+
+    function initWidgets(disabled) {
+        if ( disabled ) {
+            MochiKit.DOM.getElement('form_listaGrupoA').selectedIndex = 0;
+        }
+        MochiKit.DOM.getElement('form_listaGrupoB').selectedIndex = 0;
+        MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
+        MochiKit.DOM.getElement('form_grupos_from').options.length = 0;
+        MochiKit.DOM.getElement('form_listaGrupoB').disabled = disabled;
+        MochiKit.DOM.getElement('form_grupos_to').disabled = disabled;
+        MochiKit.DOM.getElement('form_grupos_from').disabled = disabled;
+    }
+
+    function onListaAChange() {
+        lista = MochiKit.DOM.getElement('form_listaGrupoA');
+        if ( lista.selectedIndex != '0' ) {
+            initWidgets(false);
+        } else {
+            initWidgets(true);
+            return;
+        }
+        // carga el grupo en el multiple select
+        grupoA = MochiKit.DOM.getElement('form_grupos_from');
+        id = lista.options[lista.selectedIndex].value
+        cargarGrupo(id, grupoA);
+        //carga la lista para seleccionar un responsable
+        responsableA = MochiKit.DOM.getElement('form_responsableA');
+        responsableA.options.length = 0;
+        MochiKit.DOM.appendChildNodes(responsableA, OPTION({"value":0}, "---"));
+        cargarGrupo(id, responsableA);
+    }
+
+    function onListaBChange() {
+        lista = MochiKit.DOM.getElement('form_listaGrupoB');
+        listaA =  MochiKit.DOM.getElement('form_listaGrupoA');
+        MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
+        if ( lista.selectedIndex == 0 ) {
+            return;
+        }
+        if ( lista.selectedIndex != '0' ) {
+            if ( lista.selectedIndex == listaA.selectedIndex ) {
+                window.alert('Debe seleccionar 2 grupos distintos');
+                MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
+                return;
+            }
+            grupoB = MochiKit.DOM.getElement('form_grupos_to');
+            id = lista.options[lista.selectedIndex].value
+            cargarGrupo(id, grupoB);
+
+            //carga la lista para seleccionar un responsable
+            responsableB = MochiKit.DOM.getElement('form_responsableB');
+            responsableB.options.length = 0;
+            MochiKit.DOM.appendChildNodes(responsableB, OPTION({"value":0}, "---"));
+            cargarGrupo(id, responsableB);
+        }
+    }
+
+    function cargarGrupo(grupoid, lista) {
+        //url = "/grupo/get_inscripto?cursoid="+cursoid+'&padron='+padron
+        var result = loadJSONDoc('/grupo/get_alumnos?grupoid='+id);
+        result.addCallbacks(partial(cargarLista, lista), err)
+    }
+
+    function err (err)
+    {
+        alert("The metadata for MochiKit.Async could not be fetched :(");
+    }
+
+    function cargarLista(lista, result) {
+        var alumnos = result.msg;
+        if (result.error) {
+            window.alert(result.msg);
+            return;
+        }
+        for (i in alumnos) {
+            id = alumnos[i].id;
+            label = alumnos[i].label;
+            MochiKit.DOM.appendChildNodes(lista, OPTION({"value":id}, label))
+        }
+    }
+
+"""
+def get_docentes():
+    return [(fk1.id, fk1.shortrepr()) for fk1 in Docente.select()]
+
+class GrupoAdminForm(W.TableForm):
+    class Fields(W.WidgetsList):
+        listaGrupoA = W.SingleSelectField(label=_(u'Grupo A'), options = get_gruposA, attrs = dict(onChange='onListaAChange()'), validator = V.Int(not_empty=True))
+        listaGrupoB = W.SingleSelectField(label=_(u'Grupo B'), options = get_gruposB, attrs = dict(onChange='onListaBChange()'), validator = V.Int(not_empty=True))
+        grupos = AjaxDosListasSelect(label=_(u'Grupos'),title_from="Grupo A", title_to="Grupo B", validator=V.Int(not_empty=True))
+        responsableA = W.SingleSelectField(label=_(u'Responsable A'), validator = V.Int())
+        responsableB = W.SingleSelectField(label=_(u'Responsable B'), validator = V.Int())
+        tutoresA = W.MultipleSelectField(label=_(u'Tutores A'), options = get_docentes, validator = V.Int(not_empty=True))
+        tutoresB = W.MultipleSelectField(label=_(u'Tutores B'), options = get_docentes, validator = V.Int(not_empty=True))
+
+    fields = Fields()
+    javascript = [W.JSSource("MochiKit.DOM.focusOnLoad('curso');"), W.JSSource(ajax)]
+    form_attrs = dict(onsubmit='return onsubmit()')
+
+form = GrupoAdminForm()
+
+#}}}
+
+#{{{ Controlador
+class GrupoAdminController(controllers.Controller, identity.SecureResource):
+    """Basic model admin interface"""
+    require = identity.has_permission('admin')
+
+    @expose()
+    def default(self, tg_errors=None):
+        """handle non exist urls"""
+        raise redirect('list')
+
+    @expose()
+    def index(self):
+        raise redirect('list')
+
+    @expose(template='kid:%s.templates.list' % __name__)
+    @paginate('records')
+    def list(self):
+        """List records in model"""
+        r = cls.select()
+        return dict(records=r, name=name, namepl=namepl)
+
+    @expose()
+    def activate(self, id, activo):
+        """Save or create record to model"""
+        r = validate_get(id)
+        raise redirect('../../list')
+
+    @expose(template='kid:%s.templates.new' % __name__)
+    def new(self, cursoId, **kw):
+        """Create new records in model"""
+        #form.fields[7].attrs['value'] = cursoId
+        return dict(name=name, namepl=namepl, form=form, values=kw, cursoId=int(cursoId))
+
+    @validate(form=form)
+    @error_handler(list)
+    @expose()
+    def update(self, cursoid, **kw):
+        """Save or create record to model"""
+
+        print kw
+        grupoAId = kw['listaGrupoA']
+        grupoBId = kw['listaGrupoB']
+        miembrosA = kw.get('grupos_from', [])
+        miembrosB = kw.get('grupos_to', [])
+        responsableA = kw['responsableA']
+        responsableB = kw['responsableB']
+        tutoresA = kw.get('tutoresA', [])
+        tutoresB = kw.get('tutoresB', [])
+
+        """ levanto los grupos originales """
+        grupoAorig = validate_get(int(grupoAId))
+
+        """ Si el grupo A quedo vacio deberia eliminarlo (primero
+            genero los otros para que no elimine los alumnos)"""
+        for mA in Miembro.selectBy(grupo=grupoAorig, baja=None):
+            if str(mA.alumno.id) not in miembrosA:
+                grupoAorig.remove_miembro(mA.alumno.id)
+
+        for a in miembrosA:
+            try:
+                grupoA = Grupo.get(grupoAId)
+                grupoA.add_miembro(a)
+            except:
+                continue
+        # seteo el reponsable del grupo
+        if int(responsableA) != 0:
+            grupoA.responsable = AlumnoInscripto.get(int(responsableA))
+
+
+        #Elimino el grupo si quedo vacio
+        if len(miembrosA) == 0:
+            try:
+                validate_del(grupoAId)
+            except:
+                pass
+
+        # si se selecciono un grupo nuevo
+        if int(grupoBId) == 0:
+            # creo un grupo nuevo
+            nuevosMiembros = []
+            for m in miembrosB:
+                nuevosMiembros.append(AlumnoInscripto.get(int(m)))
+            nuevosTutores = []
+            for t in tutoresB:
+                nuevosTutores.append(Docente.get(t))
+            #Creo el nuevo grupo
+            Grupo(miembros = nuevosMiembros, tutores = nuevosTutores, cursoID=cursoid, nombre='NuevoGrupo'+str(cursoid))
+        else:
+            grupoBorig = validate_get(int(grupoBId))
+            #borro todos y los vuelvo a agregar
+            for mB in Miembro.selectBy(grupo=grupoBorig, baja=None):
+                if str(mB.alumno.id) not in miembrosB:
+                    grupoBorig.remove_miembro(mB.alumno.id)
+            for b in miembrosB:
+                try:
+                    grupoB = Grupo.get(grupoBId)
+                    grupoB.add_miembro(b)
+                except:
+                    continue
+            # seteo el reponsable del grupo
+            if int(responsableB) != 0:
+                grupoB.responsable = AlumnoInscripto.get(int(responsableB))
+
+            #Elimino el grupo si quedo vacio
+            if len(miembrosB) == 0:
+                validate_del(grupoBId)
+
+
+        flash(_(u'Los grupos fueron actualizado.'))
+        raise redirect('/grupo/list')
+
+    @expose(template='kid:%s.templates.show' % __name__)
+    def show(self,id, **kw):
+        """Show record in model"""
+        r = validate_get(id)
+        return dict(name=name, namepl=namepl, record=r)
+
+    @expose()
+    def delete(self, id):
+        raise redirect('../grupo/list')
+#}}}
+
diff --git a/sercom/subcontrollers/grupo_admin/templates/__init__.py b/sercom/subcontrollers/grupo_admin/templates/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sercom/subcontrollers/grupo_admin/templates/edit.kid b/sercom/subcontrollers/grupo_admin/templates/edit.kid
new file mode 100644 (file)
index 0000000..8dbee1c
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+    py:extends="'../../../templates/master.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>edit</title>
+</head>
+<script type="text/javascript">
+       function init_data() {
+               MochiKit.DOM.getElement('form_responsable').focus();
+               MochiKit.DOM.getElement('form_cursoID').focus();
+               <span py:for="i in record.miembros" py:strip="True">
+                       MochiKit.DOM.appendChildNodes("form_miembros", OPTION({"value":${i['id']}}, '${i['label']}'))
+               </span>
+       }
+       MochiKit.DOM.addLoadEvent(init_data)
+</script>
+
+<body>
+<h1>Modificación de <span py:replace="name">Objeto</span></h1>
+
+<div py:replace="form(value=record, action=tg.url('/grupo/update/%d' % record.id),
+       submit_text=_(u'Guardar'))">Formulario</div>
+
+<br/>
+<a href="${tg.url('/grupo/list')}">Volver (cancela)</a>
+
+</body>
+</html>
diff --git a/sercom/subcontrollers/grupo_admin/templates/list.kid b/sercom/subcontrollers/grupo_admin/templates/list.kid
new file mode 100644 (file)
index 0000000..ab3c056
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+    py:extends="'../../../templates/master.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>list</title>
+</head>
+<body>
+
+<h1>Administración de <span py:replace="namepl">Objetos</span></h1>
+
+<table class="list">
+    <tr>
+        <th>Curso</th>
+        <th>Nombre</th>
+        <th>Responsable</th>
+        <th>Operaciones</th>
+    </tr>
+    <tr py:for="record in records">
+                       <td><a href="${tg.url('/curso/show/'+str(record.curso.id))}" py:content="record.curso.shortrepr()">curso</a></td>
+        <td><span py:replace="record.nombre">nombre</span></td>
+                               <td><a py:if="record.responsable is not None" href="${tg.url('/alumno/show/'+str(record.responsable.alumno.id))}" py:content="record.responsable.alumno.shortrepr()"></a></td>
+                               <td>
+                                       <a href="${tg.url('/grupo/show/%d' % record.id)}">Ver</a>
+                                       <a href="${tg.url('/grupo/edit/%d' % record.id)}">Editar</a>
+                                       <a href="${tg.url('/grupo/delete/%d' % record.id)}" onclick="if (confirm('${_(u'Estás seguro? Tal vez sólo quieras desactivarlo mejor...')}')) { var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit(); };return false;">Eliminar</a>
+                               </td>
+    </tr>
+</table>
+
+<br/>
+<a href="${tg.url('/grupo/new')}">Agregar</a>
+
+<div py:for="page in tg.paginate.pages">
+    <a py:if="page != tg.paginate.current_page"
+        href="${tg.paginate.get_href(page)}">${page}</a>
+    <b py:if="page == tg.paginate.current_page">${page}</b>
+</div>
+
+</body>
+</html>
+
+<!-- vim: set et sw=4 sts=4 : -->
diff --git a/sercom/subcontrollers/grupo_admin/templates/new.kid b/sercom/subcontrollers/grupo_admin/templates/new.kid
new file mode 100644 (file)
index 0000000..531f288
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+    py:extends="'../../../templates/master.kid'">
+<head>
+
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>new</title>
+</head>
+<body>
+
+<h1>Administrar <span py:replace="namepl">Objeto</span></h1>
+<p py:replace="form(action=tg.url('/grupo_admin/update/%d' % cursoId), value=values, submit_text=_('Actualizar'))">Formulario</p>
+
+<br/>
+<a href="${tg.url('/grupo/list')}">Cancelar</a>
+
+<script language="javascript">
+    initWidgets(true);
+</script>
+
+</body>
+</html>
diff --git a/sercom/subcontrollers/grupo_admin/templates/show.kid b/sercom/subcontrollers/grupo_admin/templates/show.kid
new file mode 100644 (file)
index 0000000..d0074ef
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+    py:extends="'../../../templates/master.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>show</title>
+</head>
+<body>
+
+<table class="show">
+    <tr>
+        <th>Nombre:</th>
+                               <td><span py:replace="record.nombre">nombre</span></td>
+    </tr>
+    <tr>
+        <th>Curso:</th>
+        <td><span py:replace="record.curso.shortrepr()">curso</span></td>
+    </tr>
+    <tr>
+        <th>Tutores:</th>
+                               <td>
+                                       <span py:for="a in record.tutores">
+                                               <span py:replace="a.docente.shortrepr()" />
+                                               <br />
+                                       </span>         
+                               </td>
+    </tr>
+    <tr>
+                       <th>Responsable:</th>
+                       <td><span py:if="record.responsable is not None" py:replace="record.responsable.shortrepr()">numero</span></td>
+    </tr>
+    <tr>
+        <th>Integrantes:</th>
+                               <td>
+                                       <span py:for="a in record.miembros">
+                                               <span py:replace="a.alumno.shortrepr()" />
+                                               <br />
+                                       </span>         
+                               </td>
+    </tr>
+</table>
+
+<br/>
+<a href="${tg.url('/grupo/edit/%d' % record.id)}">Editar</a> |
+<a href="${tg.url('/grupo/list')}">Volver</a>
+
+</body>
+</html>
index f642266fb0cb792a4977ed566060742c1993b0ff..c19f54c3b6ba168669413f06354a966f7fd60205 100644 (file)
@@ -7,13 +7,13 @@ class CustomTextField(widgets.TextField):
     """Un input con un div al lado para ponerle info"""
     template = '''
         <span xmlns:py="http://purl.org/kid/ns#">
     """Un input con un div al lado para ponerle info"""
     template = '''
         <span xmlns:py="http://purl.org/kid/ns#">
-        <input  
-            type="text"  
-            name="${name}"  
-            class="${field_class}"  
-            id="${field_id}"  
-            value="${value}"  
-            py:attrs="attrs"  
+        <input
+            type="text"
+            name="${name}"
+            class="${field_class}"
+            id="${field_id}"
+            value="${value}"
+            py:attrs="attrs"
          />
          <span id="${field_id}_info" />
         </span>
          />
          <span id="${field_id}_info" />
         </span>
@@ -72,7 +72,7 @@ MultiSelectAjax = '''
 
 class AjaxMultiSelect(widgets.MultipleSelectField):
     template = '''
 
 class AjaxMultiSelect(widgets.MultipleSelectField):
     template = '''
-    <div style="width:250px" xmlns:py="http://purl.org/kid/ns#">  
+    <div style="width:250px" xmlns:py="http://purl.org/kid/ns#">
     <div>
     <input type="text" id="${field_id}_nuevo" size="10" value="padron"
         style="color:#aaa;"
     <div>
     <input type="text" id="${field_id}_nuevo" size="10" value="padron"
         style="color:#aaa;"
@@ -83,25 +83,25 @@ class AjaxMultiSelect(widgets.MultipleSelectField):
         onClick=" _do_add(${on_add}, '${field_id}_nuevo', '${field_id}', '${name}_loading'); " />
     </div>
     <div>
         onClick=" _do_add(${on_add}, '${field_id}_nuevo', '${field_id}', '${name}_loading'); " />
     </div>
     <div>
-    <select  
-        multiple="multiple"  
-        size="${size}"  
-        name="${name}"  
-        class="${field_class}"  
-        id="${field_id}"  
-        py:attrs="attrs"  
+    <select
+        multiple="multiple"
+        size="${size}"
+        name="${name}"
+        class="${field_class}"
+        id="${field_id}"
+        py:attrs="attrs"
         style="width:250px;"
     >
         style="width:250px;"
     >
-        <optgroup py:for="group, options in grouped_options"  
-            label="${group}"  
-            py:strip="not group"  
-        >  
-        <option py:for="value, desc, attrs in options"  
-            value="${value}"  
-            py:attrs="attrs"  
-            py:content="desc"  
-        />  
-        </optgroup>  
+        <optgroup py:for="group, options in grouped_options"
+            label="${group}"
+            py:strip="not group"
+        >
+        <option py:for="value, desc, attrs in options"
+            value="${value}"
+            py:attrs="attrs"
+            py:content="desc"
+        />
+        </optgroup>
     </select>
     </div>
     <div align="center">
     </select>
     </div>
     <div align="center">
@@ -136,7 +136,7 @@ DosListasAjax = '''
 
 class AjaxDosListasSelect(widgets.MultipleSelectField):
     template = '''
 
 class AjaxDosListasSelect(widgets.MultipleSelectField):
     template = '''
-    <div xmlns:py="http://purl.org/kid/ns#">  
+    <div xmlns:py="http://purl.org/kid/ns#">
     <table style="border:0; margin:0px; border-spacing:0px 0px">
     <tr class="nada">
         <td style="padding:0 0 0 0;" align="center">${title_from}</td>
     <table style="border:0; margin:0px; border-spacing:0px 0px">
     <tr class="nada">
         <td style="padding:0 0 0 0;" align="center">${title_from}</td>
@@ -145,7 +145,7 @@ class AjaxDosListasSelect(widgets.MultipleSelectField):
     </tr>
     <tr class="nada">
     <td style="padding:0 0 0 0;">
     </tr>
     <tr class="nada">
     <td style="padding:0 0 0 0;">
-    <select  
+    <select
         multiple="multiple"
         size="${size}"
         class="${field_class}"
         multiple="multiple"
         size="${size}"
         class="${field_class}"
@@ -154,8 +154,8 @@ class AjaxDosListasSelect(widgets.MultipleSelectField):
         py:attrs="attrs"
         style="width:200px;">
         <optgroup py:for="group, options in grouped_options" label="${group}" py:strip="not group">
         py:attrs="attrs"
         style="width:200px;">
         <optgroup py:for="group, options in grouped_options" label="${group}" py:strip="not group">
-        <option py:for="value, desc, attrs in options" value="${value}" py:attrs="attrs" py:content="desc" />  
-        </optgroup>  
+        <option py:for="value, desc, attrs in options" value="${value}" py:attrs="attrs" py:content="desc" />
+        </optgroup>
     </select>
     </td>
     <td style="padding:0 10px 0 10px;" valign="center" align="center">
     </select>
     </td>
     <td style="padding:0 10px 0 10px;" valign="center" align="center">
@@ -165,7 +165,7 @@ class AjaxDosListasSelect(widgets.MultipleSelectField):
         <input type="button" value="&lt;&lt;&lt;" style="font-size:90%;" onClick="moveOption('${field_id}_to', '${field_id}_from');" />
     </td>
     <td style="padding:0 0 0 0;">
         <input type="button" value="&lt;&lt;&lt;" style="font-size:90%;" onClick="moveOption('${field_id}_to', '${field_id}_from');" />
     </td>
     <td style="padding:0 0 0 0;">
-    <select  
+    <select
         multiple="multiple"
         size="${size}"
         name="${name}_to"
         multiple="multiple"
         size="${size}"
         name="${name}_to"