]> git.llucax.com Git - software/sercom.git/blob - sercom/subcontrollers/curso/grupo/__init__.py
4c21f586600d740e35e121316f57425d4738ebde
[software/sercom.git] / sercom / subcontrollers / curso / grupo / __init__.py
1 # vim: set et sw=4 sts=4 encoding=utf-8 foldmethod=marker :
2
3 #{{{ Imports
4 import cherrypy
5 from turbogears import controllers, expose, redirect
6 from turbogears import validate, flash, error_handler
7 from turbogears import validators as V
8 from turbogears import widgets as W
9 from turbogears import identity
10 from turbogears import paginate, url
11 from docutils.core import publish_parts
12 from sercom.subcontrollers import validate as val
13 from sercom.model import Curso, AlumnoInscripto, Docente, DocenteInscripto, Grupo, Alumno, Miembro
14 from sqlobject import *
15 from sqlobject.dberrors import *
16
17 from sercom.widgets import *
18
19 import logging
20
21 log = logging.getLogger('sercom.curso.grupo.admin')
22
23 #}}}
24
25 #{{{ Configuración
26 cls = Grupo
27 name = 'grupo'
28 namepl = 'grupos'
29 #}}}
30
31 #{{{ Validación
32 def validate_get(id):
33     return val.validate_get(cls, name, id)
34
35 def validate_set(id, data):
36     return val.validate_set(cls, name, id, data)
37
38 def validate_new(data):
39     return val.validate_new(cls, name, data)
40
41 def validate_del(id):
42     return val.validate_del(cls, name, id)
43 #}}}
44
45 #{{{ Formulario
46 def get_docentes():
47     return [(fk1.id, fk1.shortrepr()) for fk1 in Docente.select()]
48
49 def get_docentes_inscriptos(id):
50     return [(fk1.id, fk1.shortrepr()) for fk1 in DocenteInscripto.select(DocenteInscripto.q.cursoID==id)]
51
52 ajax = u"""
53     function alumnos_agregar_a_la_lista(texto, lista)
54     {
55         t = MochiKit.DOM.getElement(texto);
56
57         curso = MochiKit.DOM.getElement('form_cursoID');
58         if (!curso) {
59             alert("No deberias ver esto, y quiere decir que tu form esta roto.\\nTe falta un combo de curso");
60             return;
61         }
62         if (curso.value <= 0) {
63             alert('Debes seleccionar un curso primero');
64             return;
65         }
66         url = "/curso/grupo/get_inscripto?cursoid="+curso.value+"&padron="+t.value;
67         t.value = "";
68         return url;
69     }
70
71     function err (err)
72     {
73         alert("The metadata for MochiKit.Async could not be fetched :(");
74     }
75
76     function procesar(result)
77     {
78         l = MochiKit.DOM.getElement('form_responsable_info');
79         if (result.error)
80             l.innerHTML = result.msg;
81         else
82             l.innerHTML = result.msg.value;
83     }
84
85     function buscar_alumno()
86     {
87         /* Obtengo el padron ingresado */
88         p = MochiKit.DOM.getElement('form_responsable');
89         padron = p.value;
90         if (padron == '') {
91             return;
92         }
93         /* Obtengo el curso */
94         l = MochiKit.DOM.getElement('form_cursoID');
95         cursoid = l.value;
96         if (cursoid <= 0) {
97             alert('Debe seleccionar un curso');
98             return;
99         }
100         url = "/curso/grupo/get_inscripto?cursoid="+cursoid+'&padron='+padron;
101         var d = loadJSONDoc(url);
102         d.addCallbacks(procesar, err);
103     }
104
105     function prepare()
106     {
107         connect('form_responsable', 'onblur', buscar_alumno);
108     }
109
110     function doSubmit()
111     {
112         /* TODO : Validar datos y evitar el submit si no esta completo */
113
114         /* Selecciono todos los miembros si no, no llegan al controllere*/
115         l = MochiKit.DOM.getElement('form_miembros');
116         for (i=0; i<l.options.length; i++) {
117             l.options[i].selected = true;
118         }
119         return true; // Dejo hacer el submit
120     }
121
122     MochiKit.DOM.addLoadEvent(prepare)
123
124 """
125
126 class GrupoForm(W.TableForm):
127     class Fields(W.WidgetsList):
128         cursoID = W.HiddenField()
129         nombre = W.TextField(label=_(u'Nombre'), validator=V.UnicodeString(not_empty=True,strip=True))
130         responsable = CustomTextField(label=_(u'Responsable'), validator=V.UnicodeString(), attrs=dict(size='8'))
131         miembros = AjaxMultiSelect(label=_(u'Miembros'), validator=V.Int(), on_add="alumnos_agregar_a_la_lista")
132         tutores = W.MultipleSelectField(label=_(u'Tutores'), validator=V.Int())
133
134     fields = Fields()
135     javascript = [W.JSSource("MochiKit.DOM.focusOnLoad('form_nombre');"), W.JSSource(ajax)]
136     form_attrs = dict(onsubmit='return doSubmit()')
137
138 form = GrupoForm()
139
140 def get_gruposA(cursoID):
141     return [(0, u'---')] + [(g.id, g.shortrepr()) for g in Grupo.select(Grupo.q.cursoID==cursoID)]
142
143 def get_gruposB(cursoID):
144     return [(0, u'Nuevo Grupo')] + [(g.id, g.shortrepr()) for g in Grupo.select(Grupo.q.cursoID==cursoID)]
145
146 ajaxadmin = u"""
147     function alumnos_agregar_a_la_lista(texto, lista)
148     {
149         t = MochiKit.DOM.getElement(texto);
150
151         url = "/alumno/get_alumno?padron="+t.value;
152         t.value = "";
153         return url;
154     }
155
156     function err (err)
157     {
158         alert("The metadata for MochiKit.Async could not be fetched :(");
159     }
160
161     function procesar(result)
162     {
163         l = MochiKit.DOM.getElement('form_responsable_info');
164         if (result.error)
165             l.innerHTML = result.msg;
166         else
167             l.innerHTML = result.msg.value;
168     }
169
170     function doSubmit()
171     {
172         /* TODO : Validar datos y evitar el submit si no esta completo */
173
174         /* Selecciono todos los miembros si no, no llegan al controllere*/
175         l = MochiKit.DOM.getElement('form_grupos_to');
176         for (i=0; i<l.options.length; i++) {
177             l.options[i].selected = true;
178         }
179         /* Selecciono todos los miembros si no, no llegan al controllere*/
180         l = MochiKit.DOM.getElement('form_grupos_from');
181         for (i=0; i<l.options.length; i++) {
182             l.options[i].selected = true;
183         }
184
185         return true; // Dejo hacer el submit
186     }
187
188     function initWidgets(disabled) {
189         if ( disabled ) {
190             MochiKit.DOM.getElement('form_listaGrupoA').selectedIndex = 0;
191         }
192         MochiKit.DOM.getElement('form_listaGrupoB').selectedIndex = 0;
193         MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
194         MochiKit.DOM.getElement('form_grupos_from').options.length = 0;
195         MochiKit.DOM.getElement('form_listaGrupoB').disabled = disabled;
196         MochiKit.DOM.getElement('form_grupos_to').disabled = disabled;
197         MochiKit.DOM.getElement('form_grupos_from').disabled = disabled;
198     }
199
200     function onListaAChange() {
201         lista = MochiKit.DOM.getElement('form_listaGrupoA');
202         if ( lista.selectedIndex != '0' ) {
203             initWidgets(false);
204         } else {
205             initWidgets(true);
206             return;
207         }
208         // carga el grupo en el multiple select
209         grupoA = MochiKit.DOM.getElement('form_grupos_from');
210         id = lista.options[lista.selectedIndex].value
211         cargarGrupo(id, grupoA);
212         //carga la lista para seleccionar un responsable
213         responsableA = MochiKit.DOM.getElement('form_responsableA');
214         responsableA.options.length = 0;
215         MochiKit.DOM.appendChildNodes(responsableA, OPTION({"value":0}, "---"));
216         cargarGrupo(id, responsableA);
217     }
218
219     function onListaBChange() {
220         lista = MochiKit.DOM.getElement('form_listaGrupoB');
221         listaA =  MochiKit.DOM.getElement('form_listaGrupoA');
222         MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
223         if ( lista.selectedIndex == 0 ) {
224             return;
225         }
226         if ( lista.selectedIndex != '0' ) {
227             if ( lista.selectedIndex == listaA.selectedIndex ) {
228                 window.alert('Debe seleccionar 2 grupos distintos');
229                 MochiKit.DOM.getElement('form_grupos_to').options.length = 0;
230                 return;
231             }
232             grupoB = MochiKit.DOM.getElement('form_grupos_to');
233             id = lista.options[lista.selectedIndex].value
234             cargarGrupo(id, grupoB);
235
236             //carga la lista para seleccionar un responsable
237             responsableB = MochiKit.DOM.getElement('form_responsableB');
238             responsableB.options.length = 0;
239             MochiKit.DOM.appendChildNodes(responsableB, OPTION({"value":0}, "---"));
240             cargarGrupo(id, responsableB);
241         }
242     }
243
244     function cargarGrupo(grupoid, lista) {
245         //url = "/grupo/get_inscripto?cursoid="+cursoid+'&padron='+padron
246         var result = loadJSONDoc('/curso/grupo/get_alumnos?grupoid='+id);
247         result.addCallbacks(partial(cargarLista, lista), err)
248     }
249
250     function err (err)
251     {
252         alert("The metadata for MochiKit.Async could not be fetched :(");
253     }
254
255     function cargarLista(lista, result) {
256         var alumnos = result.msg;
257         if (result.error) {
258             window.alert(result.msg);
259             return;
260         }
261         for (i in alumnos) {
262             id = alumnos[i].id;
263             label = alumnos[i].label;
264             MochiKit.DOM.appendChildNodes(lista, OPTION({"value":id}, label))
265         }
266     }
267
268 """
269
270 class GrupoAdminForm(W.TableForm):
271     class Fields(W.WidgetsList):
272         cursoID = W.HiddenField()
273         listaGrupoA = W.SingleSelectField(label=_(u'Grupo A'), attrs = dict(onChange='onListaAChange()'), validator = V.Int(not_empty=True))
274         listaGrupoB = W.SingleSelectField(label=_(u'Grupo B'), attrs = dict(onChange='onListaBChange()'), validator = V.Int(not_empty=True))
275         grupos = AjaxDosListasSelect(label=_(u'Grupos'),title_from=u"Grupo A", size=8, title_to=u"Grupo B", validator=V.Int(not_empty=True))
276         responsableA = W.SingleSelectField(label=_(u'Responsable A'), validator = V.Int())
277         responsableB = W.SingleSelectField(label=_(u'Responsable B'), validator = V.Int())
278         tutoresA = W.MultipleSelectField(label=_(u'Tutores A'), validator = V.Int(not_empty=True))
279         tutoresB = W.MultipleSelectField(label=_(u'Tutores B'), validator = V.Int(not_empty=True))
280
281     fields = Fields()
282     javascript = [W.JSSource("MochiKit.DOM.focusOnLoad('listaGrupoA');"), W.JSSource(ajaxadmin)]
283     form_attrs = dict(onsubmit='return doSubmit();')
284
285 formadmin = GrupoAdminForm()
286
287 #}}}
288
289 #{{{ Controlador
290 class GrupoController(controllers.Controller, identity.SecureResource):
291     """Basic model admin interface"""
292     require = identity.has_permission('admin')
293
294     @expose()
295     def default(self, tg_errors=None):
296         """handle non exist urls"""
297         raise redirect(tg.url('/curso/list'))
298
299     @expose()
300     def index(self):
301         raise redirect(tg.url('/curso/list'))
302
303     @expose(template='kid:%s.templates.list' % __name__)
304     @paginate('records')
305     def list(self, cursoID):
306         """List records in model"""
307         r = cls.select(cls.q.cursoID == cursoID)
308         return dict(records=r, name=name, namepl=namepl, cursoID=int(cursoID))
309
310     @expose(template='kid:%s.templates.new' % __name__)
311     def new(self, cursoID, **kw):
312         """Create new records in model"""
313         form.fields[0].attrs['value'] = cursoID
314         options = dict(tutores=get_docentes_inscriptos(cursoID))
315         return dict(name=name, namepl=namepl, cursoID=int(cursoID), form=form, options=options, values=kw)
316
317     @validate(form=form)
318     @error_handler(new)
319     @expose()
320     def create(self, **kw):
321         """Save or create record to model"""
322         resp = kw['responsable']
323         try:
324             # Busco el alumno inscripto
325             resp = AlumnoInscripto.selectBy(cursoID=kw['cursoID'],
326                 alumno=Alumno.byPadron(kw['responsable'])).getOne()
327         except SQLObjectNotFound:
328             resp = None
329         kw['responsable'] = resp
330
331         r = validate_new(kw)
332         flash(_(u'Se creó un nuevo %s.') % name)
333         raise redirect('list/%d' % int(kw['cursoID']))
334
335     @expose(template='kid:%s.templates.edit' % __name__)
336     def edit(self, id, **kw):
337         """Edit record in model"""
338         r = validate_get(id)
339         # TODO : No encontre mejor forma de pasar cosas al form
340         # de manera comoda y facil de formatear segun lo que espera la UI (que
341         # en este caso es super particular). Ese EmptyClass no se si hay algo estandar
342         # en python que usar, puse {} y [] pero cuando quiero hacer values.id = XX explota.
343         options = dict(tutores=get_docentes_inscriptos(r.curso.id))
344         class EmptyClass:
345             pass
346         values = EmptyClass()
347         values.id = r.id
348         values.cursoID = r.cursoID
349         values.nombre = r.nombre
350         # TODO : Ver como llenar la lista primero :S
351         if r.responsable:
352             values.responsable = r.responsable.alumno.padron
353         values.miembros = [{"id":i.alumno.id, "label":i.alumno.alumno.nombre} for i in filter(lambda x: x.baja is None, r.miembros)]
354         values.tutores = [a.docenteID for a in r.tutores]
355         return dict(name=name, namepl=namepl, record=values, options=options, form=form)
356
357     @validate(form=form)
358     @error_handler(edit)
359     @expose()
360     def update(self, id, **kw):
361         """Save or create record to model"""
362         responsable = kw['responsable']
363         curso = kw['cursoID']
364         resp = kw['responsable']
365         try:
366             # Busco el alumno inscripto
367             resp = AlumnoInscripto.selectBy(cursoID=kw['cursoID'],
368                 alumno=Alumno.byPadron(kw['responsable'])).getOne()
369         except SQLObjectNotFound:
370             resp = None
371         kw['responsable'] = resp
372         r = validate_set(id, kw)
373         flash(_(u'El %s fue actualizado.') % name)
374         raise redirect('../list/%d' % r.curso.id)
375
376     @expose(template='kid:%s.templates.show' % __name__)
377     def show(self,id, **kw):
378         """Show record in model"""
379         r = validate_get(id)
380         return dict(name=name, namepl=namepl, record=r)
381
382     @expose()
383     def delete(self, cursoID, id):
384         """Destroy record in model"""
385         validate_del(id)
386         flash(_(u'El %s fue eliminado permanentemente.') % name)
387         raise redirect('../../list/%d' % int(cursoID))
388
389     @expose('json')
390     def get_inscripto(self, cursoid, padron):
391         msg = u''
392         error=False
393         try:
394             # Busco el alumno inscripto
395             alumno = AlumnoInscripto.selectBy(curso=cursoid, alumno=Alumno.byUsuario(padron)).getOne()
396             msg = {}
397             msg['id'] = alumno.id
398             msg['value'] = alumno.alumno.nombre
399         except SQLObjectNotFound:
400             msg = 'No existe el alumno %s en el curso seleccionado.' % padron
401             error=True
402         except Exception, (inst):
403             msg = u"""Se ha producido un error inesperado al buscar el registro:\n      %s""" % str(inst)
404             error = True
405         return dict(msg=msg, error=error)
406
407     @expose('json')
408     def get_alumnos(self, grupoid):
409         msg = u''
410         error=False
411         try:
412             # Busco los alumnos del grupo
413             grupo = Grupo.get(int(grupoid))
414             miembros = Miembro.selectBy(baja=None, grupo=grupo)
415             print miembros
416             integrantes = []
417             for m in miembros:
418                 msg = {}
419                 alumnoInscripto = AlumnoInscripto.get(m.alumno.id)
420                 msg['id'] = alumnoInscripto.id
421                 msg['label'] = alumnoInscripto.shortrepr()
422                 integrantes.append(msg)
423         except Exception, (inst):
424             msg = u"""Se ha producido un error inesperado al buscar el registro:\n      %s""" % str(inst)
425             error = True
426             integrantes = []
427             integrantes.append(msg)
428         return dict(msg=integrantes, error=error)
429
430     @expose(template='kid:%s.templates.admin' % __name__)
431     def admin(self, cursoID, **kw):
432         """Create new records in model"""
433         options = dict(
434             listaGrupoA=get_gruposA(cursoID),
435             listaGrupoB=get_gruposB(cursoID),
436             tutoresA=get_docentes_inscriptos(cursoID),
437             tutoresB=get_docentes_inscriptos(cursoID),
438         )
439         kw['cursoID'] = cursoID
440         return dict(name=name, namepl=namepl, options=options, form=formadmin, values=kw, cursoID=int(cursoID))
441
442     @validate(form=formadmin)
443     @error_handler(admin)
444     @expose()
445     def adminupdate(self, **kw):
446         """Save or create record to model"""
447         cursoID = int(kw['cursoID'])
448         log.debug(kw)
449         grupoAId = kw['listaGrupoA']
450         grupoBId = kw['listaGrupoB']
451         miembrosA = kw.get('grupos_from', [])
452         miembrosB = kw.get('grupos_to', [])
453         responsableA = kw['responsableA']
454         responsableB = kw['responsableB']
455         tutoresA = kw.get('tutoresA', [])
456         tutoresB = kw.get('tutoresB', [])
457
458         # por las dudas de que no sea una lista
459         if not isinstance(miembrosA, list):
460             miembrosA = [miembrosA]
461         if not isinstance(miembrosB, list):
462             miembrosB = [miembrosB]
463         if not isinstance(tutoresA, list):
464             tutoresA = [tutoresA]
465         if not isinstance(tutoresB, list):
466             tutoresB = [tutoresB]
467
468
469         """ levanto los grupos originales """
470         grupoAorig = validate_get(int(grupoAId))
471         log.debug(miembrosA)
472         log.debug(Miembro.selectBy(grupo=grupoAorig, baja=None))
473         """ Si el grupo A quedo vacio deberia eliminarlo (primero
474             genero los otros para que no elimine los alumnos)"""
475         for mA in Miembro.selectBy(grupo=grupoAorig, baja=None):
476             if str(mA.alumno.id) not in miembrosA:
477                 grupoAorig.remove_miembro(mA.alumno.id)
478
479         try:
480             grupoA = validate_get(grupoAId)
481             for a in miembrosA:
482                 try:
483                     grupoA.add_miembro(a, baja=None)
484                 except DuplicateEntryError:
485                     continue
486         except Exception, e:
487             log.debug(e)
488             flash(_(u'Error A %s.' % e))
489             raise redirect(url('/curso/grupo/list' % int(cursoID)))
490         # seteo el reponsable del grupo
491         if int(responsableA) != 0:
492             grupoA.responsable = AlumnoInscripto.get(int(responsableA))
493
494         for t in tutoresA:
495             try:
496                 grupoA.add_tutor(int(t))
497             except:
498                 #TODO ver por que no anda el duplicate error, por ahora cacheo silencioso
499                 pass
500
501
502         #Elimino el grupo si quedo vacio
503         if len(miembrosA) == 0:
504             try:
505                 validate_del(grupoAId)
506             except:
507                 pass
508
509         # si se selecciono un grupo nuevo
510         if int(grupoBId) == 0:
511             # creo un grupo nuevo
512             nuevosMiembros = []
513             for m in miembrosB:
514                 nuevosMiembros.append(AlumnoInscripto.get(int(m)))
515             nuevosTutores = []
516             for t in tutoresB:
517                 nuevosTutores.append(Docente.get(t))
518             #Creo el nuevo grupo
519             Grupo(miembros = nuevosMiembros, tutores = nuevosTutores, cursoID=cursoID, nombre='NuevoGrupo'+str(cursoID))
520         else:
521             grupoBorig = validate_get(int(grupoBId))
522             log.debug(miembrosB)
523             b = list(Miembro.selectBy(grupo=grupoBorig, baja=None))
524             log.debug(b)
525             #borro todos y los vuelvo a agregar
526             for mB in Miembro.selectBy(grupo=grupoBorig, baja=None):
527                 if str(mB.alumno.id) not in miembrosB:
528                     grupoBorig.remove_miembro(mB.alumno.id)
529             try:
530                 grupoB = validate_get(grupoBId)
531                 for b in miembrosB:
532                     try:
533                         grupoB.add_miembro(b, baja=None)
534                     except DuplicateEntryError:
535                         continue
536             except Exception, e:
537                 log.debug(e)
538                 flash(_(u'Error B %s.' % e))
539                 raise redirect(url('/curso/grupo/list/%d' % int(cursoID)))
540             # seteo el reponsable del grupo
541             if int(responsableB) != 0:
542                 grupoB.responsable = AlumnoInscripto.get(int(responsableB))
543
544             #Elimino el grupo si quedo vacio
545             if len(miembrosB) == 0:
546                 try:
547                     validate_del(grupoBId)
548                 except:
549                     pass
550
551             for t in tutoresB:
552                 try:
553                     grupoB.add_tutor(int(t))
554                 except:
555                     #TODO ver por que no anda el duplicate error, por ahora cahceo silencioso
556                     pass
557         flash(_(u'Los grupos fueron actualizado.'))
558         raise redirect(url('/curso/grupo/list/%d' % int(cursoID)))
559 #}}}
560