]> git.llucax.com Git - software/sercom-old.git/blob - src/sc_suwi
Se agrega un link a los fuentes hecho por gonza.
[software/sercom-old.git] / src / sc_suwi
1 #!/usr/bin/env python2.4
2 # -*- encoding: iso-8859-1 -*-
3 # vim: set et sw=4 sts=4 :
4
5 # Módulos estándar
6 import os
7 import sys
8 import cgi
9 # Módulos externos
10 import sqlobject
11 # Módulos locales
12 import sercom
13 from sercom.sqlo import *
14
15 # Inicializo
16 conf, conn, log = sercom.init('cgi')
17
18 # Para debug web
19 import cgitb; cgitb.enable()
20
21 #XXX HORRIBLE
22 PASSWD = conf.get('general', 'cgipw')
23
24 def cmp_correccion_padron(e1, e2):
25     'Compara 2 entregas, según el padrón del alumno.'
26     return cmp(e1.inscripto.padron, e2.inscripto.padron)
27
28 def http_header_html(req):
29     return 'Content-type: text/html\r\n\r\n'
30
31 def http_header_zip(req, filename):
32     return 'Content-type: application/zip\r\n' \
33         'Content-Disposition: attachment;filename=%s\r\n' \
34         '\r\n' % filename
35
36 def header(req):
37     return '''<!DOCTYPE html
38     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
39     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
40 <html>
41     <head>
42         <title>SUWI - Sercom Ugly Web Interface</title>
43         <style type="text/css">
44             <!--
45             body
46             {
47                 font-family: sans-serif;
48                 background-color: rgb(255, 255, 255);
49             }
50             table
51             {
52                 border: medium black solid;
53                 border-collapse: collapse;
54             }
55             caption
56             {
57                 margin: 5pt;
58                 font-weight: bold;
59             }
60             th
61             {
62                 border: thin black solid;
63                 color: white;
64                 background-color: navy;
65                 padding: 3pt;
66                 text-align: center;
67                 vertical-align: middle;
68             }
69             td
70             {
71                 border: thin black solid;
72                 padding: 3pt;
73                 vertical-align: middle;
74             }
75             .warn
76             {
77                 color: red;
78             }
79             // -->
80         </style>
81     </head>
82     <body>
83 '''
84     pass
85
86 def footer(req):
87     return '''
88     </body>
89 </html>
90 '''
91     pass
92
93 def form(req, submit, str=None):
94     r = submit
95     if submit is not None:
96         r = '<form method="post">\n'
97         if str: r += str
98         r += '<input type="submit" value="%s">\n' % submit
99         r += '</form>\n'
100     return r
101
102 def pre(s):
103     return '<pre>%s</pre>' % cgi.escape(str(s))
104
105 def input_login(req):
106     return '<input type="hidden" name="pw" value="%s" />\n' % PASSWD
107
108 def input_curso(req, curso_id):
109     return input_login(req) \
110         + '<input type="hidden" name="curso" value="%d" />\n' % curso_id
111
112 def input_entrega(req, entrega_id):
113     e = Entrega.get(entrega_id, connection=conn)
114     return input_curso(req, e.curso.id) \
115         + '<input type="hidden" name="entrega" value="%d" />\n' % entrega_id
116
117 def input_zip(req, entrega_id):
118     return input_entrega(req, entrega_id) \
119         + '<input type="hidden" name="zip" value="1" />\n'
120
121 def input_zip_rechazados(req, entrega_id):
122     return input_entrega(req, entrega_id) \
123         + '<input type="hidden" name="zip" value="1" />\n' \
124         + '<input type="hidden" name="rechazados" value="1" />\n'
125
126 def input_inscripto(req, inscripto_id=None):
127     if inscripto_id is not None:
128         return '<input type="hidden" name="inscripto" value="%d" />\n' \
129             % inscripto_id
130     return ''
131
132 def input_pruebas(req, intento_id, inscripto_id=None):
133     i = Intento.get(intento_id, connection=conn)
134     r = input_entrega(req, i.entrega.id)
135     r += '<input type="hidden" name="intento" value="%d" />\n' % intento_id
136     r += input_inscripto(req, inscripto_id)
137     return r
138
139 def input_intentos(req, entrega_id, inscripto_id):
140     return input_entrega(req, entrega_id) + input_inscripto(req, inscripto_id)
141
142 def login(req):
143     r = '<h1>Bienvenido a ' \
144         '<acronym title="Sercom Ugly Web Interface">SUWI</acronym></h1>\n'
145     r += '<p>Debe ingresar la contraseña para seguir...</p>\n'
146     return r + form(req, 'Entrar',
147         '<p>Contraseña: <input type="password" name="pw" /></p>')
148
149 def curso(req):
150     cursos = [c for c in Curso.select(connection=conn) if len(c.entregas)]
151     r = '<h1>Elegir curso</h1>\n'
152     if cursos:
153         r += '<p>Curso: <select name="curso">\n'
154         for c in cursos:
155             r += '\t<option value="%d">%d-%d-%d</option>\n' \
156                 % (c.id, c.anio, c.cuatrimestre, c.curso)
157         r += '</select></p>\n'
158     else:
159         r += '<p>No hay cursos con entregas</p>\n'
160     return form(req, 'Ver', r + input_login(req))
161
162 def entrega(req, curso_id):
163     c = Curso.get(curso_id, connection=conn)
164     r = '<h1>Elegir entrega del curso %d-%d-%d</h1>\n' \
165         % (c.anio, c.cuatrimestre, c.curso)
166     r += '<p>Entrega: <select name="entrega">\n'
167     for e in c.entregas:
168         r += '\t<option value="%d">%d-%d</option>\n' \
169             % (e.id, e.nroEjercicio, e.entrega)
170     r += '</select></p>\n'
171     return form(req, 'Ver', r + input_curso(req, curso_id))
172
173 def pruebas(req, intento_id, inscripto_id=None):
174     def header():
175         return '''<table>
176     <tr>
177         <th>Nombre</th>
178         <th>Pasada</th>
179         <th>Privada</th>
180         <th>Activa</th>
181         <th>Inicio</th>
182         <th>Fin</th>
183         <th>Observaciones</th>
184     <tr>
185 '''
186         pass
187     def footer():
188         r = '</table>\n<p>\n'
189         r += form(req, 'Volver', input_entrega(req, i.entrega.id)
190             + input_inscripto(req, inscripto_id))
191         #XXX Hack feo y muy hardcodeado para ver los fuentes (morirá con suwi)
192         r += '<a href="/intentos/%s.%s/%s.%s.%s/%s.%s/">ver fuentes</a></p>' \
193             % (i.inscripto.curso.anio, i.inscripto.curso.cuatrimestre,
194             i.inscripto.curso.curso, i.entrega.nroEjercicio, i.entrega.entrega,
195             i.inscripto.padron, i.numero)
196         r += '</p>\n'
197         return r
198     def row(p):
199         return '''
200     <tr>
201         <td>%s</td>
202         <td>%s</td>
203         <td>%s</td>
204         <td>%s</td>
205         <td>%s</td>
206         <td>%s</td>
207         <td>%s</td>
208     </tr>
209 ''' % (p.casoDePrueba.nombre, p.pasada, p.casoDePrueba.privado,
210         p.casoDePrueba.activo, p.inicio, p.fin,
211         pre(p.observaciones))
212
213     i = Intento.get(intento_id, connection=conn)
214     r = '<h1>Pruebas del intento %d del alumno %d</h1>\n' \
215         % (i.numero, i.inscripto.padron)
216     r += header()
217     for p in i.pruebas:
218         r += row(p)
219     r += footer()
220     return r
221
222 def intentos(req, entrega_id, inscripto_id):
223     def header():
224         return '''<table>
225     <tr>
226         <th>Nro</th>
227         <th>Llegada</th>
228         <th>Compila</th>
229         <th>Pruebas</th>
230         <th>Observaciones</th>
231     <tr>
232 '''
233         pass
234     def footer():
235         r = '</table>\n<p>\n'
236         r += form(req, 'Volver', input_entrega(req, entrega_id))
237         r += '</p>\n'
238         return r
239     def row(i):
240         if i.pruebasPasadas: pruebas = 'BIEN'
241         elif not i.compila: pruebas = None
242         else: pruebas = 'MAL'
243         return '''
244     <tr>
245         <td>%d</td>
246         <td>%s</td>
247         <td>%s</td>
248         <td>%s</td>
249         <td>%s</td>
250     </tr>
251 ''' % (i.numero, i.llegada, i.compila,
252         form(req, pruebas, input_pruebas(req, i.id, inscripto_id)),
253         pre(i.observaciones))
254
255     r = '<h1>Intentos del alumno %d</h1>\n' \
256         % Inscripto.get(inscripto_id, connection=conn).padron
257     r += header()
258     for i in Intento.selectBy(entregaID=entrega_id, inscriptoID=inscripto_id,
259             connection=conn):
260         r += row(i)
261     r += footer()
262     return r
263
264 def zip(req, entrega_id, rechazados=False):
265     def zip_path(path, base, zipfd):
266         paths = os.listdir(path)
267         for p in paths:
268             if os.path.isdir(os.path.join(path, p)):
269                 zip_path(os.path.join(path, p), os.path.join(base, p), zipfd)
270             else:
271                 zipfd.write(os.path.join(path, p), os.path.join(base, p))
272
273     from zipfile import ZipFile, ZIP_DEFLATED
274     e = Entrega.get(entrega_id, connection=conn)
275     c = e.curso
276     filename = 'entrega-%d.%d.%d-%d.%d' \
277         % (c.anio, c.cuatrimestre, c.curso, e.nroEjercicio, e.entrega)
278     if rechazados:
279         filename += '-rechazados'
280     filename += '.zip'
281     req.write(http_header_zip(req, filename))
282     tmpfname = os.tmpnam()
283     zipfd = ZipFile(tmpfname, 'w', ZIP_DEFLATED)
284     if rechazados:
285         inscriptos_ok = set([c.inscripto for c in e.correcciones])
286         inscriptos = set([i.inscripto for i in e.intentos])
287         for i in inscriptos - inscriptos_ok:
288             intento = list(Intento.selectBy(entregaID=e.id, inscriptoID=i.id,
289                 connection=conn))[-1]
290             zip_path(os.path.join(conf.get('general', 'data_dir'),
291                 intento.path), str(i.padron), zipfd)
292     else:
293         for c in e.correcciones:
294             zip_path(os.path.join(conf.get('general', 'data_dir'),
295                 c.intento.path), str(c.intento.inscripto.padron), zipfd)
296     zipfd.close()
297     req.write(file(tmpfname, 'r').read())
298     os.unlink(tmpfname)
299
300 def correcciones(req, entrega_id):
301     def correccion_header():
302         return '''<table>
303     <caption>Entregas aceptadas</caption>
304     <thead>
305         <tr>
306             <th>Padrón</th>
307             <th>Pruebas</th>
308             <th>Intentos</th>
309         <tr>
310     </thead>
311     <tbody>
312 '''
313         pass
314     def correccion_footer():
315         return '    </tbody>\n</table>\n'
316     def correccion_row(c):
317         if c.intento.pruebasPasadas: pruebas = 'BIEN'
318         elif not c.intento.compila: prubas = None
319         else: pruebas = 'MAL'
320         intentos = int(Intento.selectBy(inscriptoID=c.inscriptoID,
321             entregaID=c.entregaID, connection=conn).count())
322         return '''
323             <tr>
324                 <td>%d</td>
325                 <td>%s</td>
326                 <td>%s</td>
327             </tr>
328 ''' % (c.inscripto.padron,
329         form(req, pruebas, input_pruebas(req, c.intento.id)),
330         form(req, intentos, input_intentos(req, c.entrega.id, c.inscripto.id)))
331     def problematico_header():
332         return '''<table>
333     <caption>Entregas rechazadas</caption>
334     <thead>
335         <tr>
336             <th>Padrón</th>
337             <th>Intentos</th>
338         <tr>
339     </thead>
340     <tbody>
341 '''
342         pass
343     def problematico_footer():
344         return '    </tbody>\n</table>\n'
345     def problematico_row(inscripto, entrega):
346         intentos = int(Intento.selectBy(inscriptoID=inscripto.id,
347             entregaID=entrega.id, connection=conn).count())
348         return '''
349         <tr>
350             <td>%d</td>
351             <td>%s</td>
352         </tr>
353 ''' % (inscripto.padron,
354         form(req, intentos, input_intentos(req, entrega.id, inscripto.id)))
355     def footer():
356         r = '<p>\n'
357         r += form(req, 'Elegir curso', input_login(req))
358         r += form(req, 'Elegir entrega', input_curso(req, e.curso.id))
359         r += form(req, 'Bajar entrega en .zip', input_zip(req, entrega_id))
360         r += form(req, 'Bajar entrega rechazadas en .zip',
361             input_zip_rechazados(req, entrega_id))
362         r += '</p>\n'
363         return r
364
365     e = Entrega.get(entrega_id, connection=conn)
366     c = e.curso
367     r = '<h1>Entrega %d.%d del curso %d-%d-%d</h1>\n' \
368         % (e.nroEjercicio, e.entrega, c.anio, c.cuatrimestre, c.curso)
369     correcciones = list(e.correcciones)
370     correcciones.sort(cmp_correccion_padron)
371     r += correccion_header()
372     for c in correcciones:
373         r += correccion_row(c)
374     r += correccion_footer()
375     inscriptos_ok = set([c.inscripto for c in correcciones])
376     inscriptos = set([i.inscripto for i in e.intentos])
377     r += problematico_header()
378     for i in inscriptos - inscriptos_ok:
379         r += problematico_row(i, e)
380     r += problematico_footer()
381     r += footer()
382     return r
383
384 #
385 # MAIN
386 #
387
388 req = sys.stdout
389 f = cgi.FieldStorage()
390 raw = f.has_key('zip')
391
392
393 if not raw:
394     print http_header_html(req)
395     print header(req)
396
397 path = os.getenv('PATH_INFO')
398
399 if not f.has_key('pw'):
400     print login(req)
401 elif f.has_key('pw') and f.getfirst('pw') <> PASSWD:
402     print login(req)
403     print '<p class="warn">Password Incorrecto!</p>\n'
404 elif not f.has_key('curso'):
405     print curso(req)
406 elif not f.has_key('entrega'):
407     print entrega(req, int(f.getfirst('curso')))
408 elif f.has_key('intento'):
409     inscripto_id = None
410     if f.has_key('inscripto'):
411         inscripto_id = int(f.getfirst('inscripto'))
412     print pruebas(req, int(f.getfirst('intento')), inscripto_id)
413 elif f.has_key('inscripto'):
414     print intentos(req, int(f.getfirst('entrega')), int(f.getfirst('inscripto')))
415 elif f.has_key('zip'):
416     zip(req, int(f.getfirst('entrega')), f.getfirst('rechazados', False))
417 else:
418     print correcciones(req, int(f.getfirst('entrega')))
419
420 if not raw:
421     print footer(req)
422