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