rename sync -> service & add logging
[qcg-portal.git] / qcg / views.py
1 # coding=utf-8
2 from datetime import datetime, timedelta
3 from django.conf import settings
4 from django.contrib.auth import REDIRECT_FIELD_NAME
5 from django.contrib.auth.decorators import login_required
6 from django.core.urlresolvers import reverse
7 from django.db.models import Q
8 from django.http import HttpResponse, QueryDict
9 from django.shortcuts import render, get_object_or_404
10 from django.utils.http import urlencode
11 from django.utils.timezone import UTC
12 from django_openid_auth.views import make_consumer
13 from openid.extensions import ax
14 from pyqcg import QCG
15 from pyqcg.description import JobDescription
16 from pyqcg.utils import Credential
17
18 from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet
19 from qcg.utils import paginator_context
20 from qcg.service import update_user_data
21
22
23 def index(request):
24     return render(request, 'qcg/base.html')
25
26
27 def openid_begin(request):
28     openid_request = make_consumer(request).begin(settings.OPENID_SSO_SERVER_URL)
29
30     fetch_request = ax.FetchRequest()
31     for (attr, alias) in [('http://axschema.org/namePerson/friendly', 'nickname'),
32                           ('http://axschema.org/contact/email', 'email'),
33                           ('http://axschema.org/namePerson', 'fullname'),
34                           ('http://openid.plgrid.pl/certificate/proxy', 'proxy'),
35                           ('http://openid.plgrid.pl/certificate/userCert', 'userCert'),
36                           ('http://openid.plgrid.pl/certificate/proxyPrivKey', 'proxyPrivKey'),
37                           ('http://openid.plgrid.pl/POSTresponse', 'POSTresponse')]:
38         fetch_request.add(ax.AttrInfo(attr, alias=alias, required=True))
39     openid_request.addExtension(fetch_request)
40
41     return_to = request.build_absolute_uri(reverse('openid-complete'))
42
43     redirect_to = request.GET.get(REDIRECT_FIELD_NAME)
44     if redirect_to:
45         return_to += '?' + urlencode({REDIRECT_FIELD_NAME: redirect_to})
46
47     return HttpResponse(openid_request.htmlMarkup(request.build_absolute_uri('/'), return_to))
48
49
50 search_fields = ('status_description', 'type', 'note', 'task_id', 'job__note', 'job__project', 'job__job_id',
51                  'allocations__status_description', 'allocations__processes_group_id', 'allocations__comment')
52
53
54 def parse_date(string):
55     return datetime.strptime(string.strip(), "%d.%m.%Y").replace(tzinfo=UTC())
56
57
58 @login_required
59 def jobs_list(request):
60     QCG.start()
61     update_user_data(request.user, request.session['proxy'])
62
63     tasks = request.user.tasks.order_by('-job__submission_time', '-submission_time') \
64         .select_related('job').prefetch_related('allocations__nodes')
65
66     filters = FiltersForm(request.GET)
67     selected_filters = []
68     if filters.is_valid():
69         keywords = filters.cleaned_data['keywords']
70         status = filters.cleaned_data['status']
71         host = filters.cleaned_data['host']
72         submission = filters.cleaned_data['submission']
73         finish = filters.cleaned_data['finish']
74
75         if keywords:
76             and_query = Q()
77
78             for q in keywords.split():
79                 or_query = Q()
80                 for field in search_fields:
81                     or_query |= Q(**{field + '__icontains': q})
82                 and_query &= or_query
83
84             tasks = tasks.filter(and_query)
85             selected_filters.append((keywords, 'keywords', keywords))
86
87         if status:
88             statuses = []
89             for s in status:
90                 si = int(s)
91                 statuses.extend(FiltersForm.STATUS_MAP[si])
92                 selected_filters.append((FiltersForm.STATUS_CHOICES_DICT[si], 'status', s))
93
94             tasks = tasks.filter(status__in=statuses)
95
96         if host:
97             tasks = tasks.filter(allocations__host_name__in=host)
98
99             host_dict = dict(filters.fields['host'].choices)
100             for h in host:
101                 selected_filters.append((host_dict[h], 'host', h))
102
103         if submission:
104             start, end = submission.split('-')
105
106             tasks = tasks.filter(submission_time__gte=parse_date(start),
107                                  submission_time__lte=parse_date(end) + timedelta(days=1))
108             selected_filters.append((u'Data zlecenia: ' + submission, 'submission', submission))
109
110         if finish:
111             start, end = finish.split('-')
112
113             tasks = tasks.filter(finish_time__gte=parse_date(start),
114                                  finish_time__lte=parse_date(end) + timedelta(days=1))
115             selected_filters.append((u'Data zakończenia: ' + finish, 'finish', finish))
116
117         tasks = tasks.distinct()
118
119     checked_status = {i: widget.is_checked() for i, widget in enumerate(filters['status'])}
120
121     columns = ColumnsForm(QueryDict(request.COOKIES.get('columns', '')))
122
123     displayed = None
124     if columns.is_valid():
125         displayed = {int(c) for c in columns.cleaned_data['columns']}
126
127     # if invalid or empty
128     if not displayed:
129         displayed = set(columns.fields['columns'].initial)
130
131     context = {'filters': filters, 'checked_status': checked_status, 'selected_filters': selected_filters,
132                'columns': ColumnsForm(initial={'columns': displayed}), 'displayed': displayed}
133
134     context.update(paginator_context(request, tasks))
135
136     return render(request, 'qcg/jobs.html', context)
137
138
139 @login_required
140 def job_details(request, job_id):
141     job = get_object_or_404(request.user.jobs.prefetch_related('tasks'), job_id=job_id)
142
143     return render(request, 'qcg/job.html', {'job': job})
144
145
146 @login_required
147 def task_details(request, job_id, task_id):
148     task = get_object_or_404(request.user.tasks.select_related('job').prefetch_related('allocations'),
149                              job__job_id=job_id, task_id=task_id)
150
151     return render(request, 'qcg/task.html', {'task': task})
152
153
154 @login_required
155 def job_new(request):
156     if request.method == 'POST':
157         QCG.start()
158
159         form = JobDescriptionForm(request.POST)
160         env_formset = EnvFormSet(request.POST)
161
162         if form.is_valid() and env_formset.is_valid():
163             print form.cleaned_data
164             print env_formset.cleaned_data
165
166             desc = JobDescription(Credential(request.session['proxy']))
167
168             direct_map = ('arguments', 'note', 'grant', 'hosts', 'properties', 'queue', 'procs', 'wall_time', 'memory',
169                           'memory_per_slot', 'modules', 'native', 'notify', 'preprocess', 'postprocess', 'persistent')
170
171             for name in direct_map:
172                 if form.cleaned_data[name]:
173                     setattr(desc, name, form.cleaned_data[name])
174
175             if form.cleaned_data['application']:
176                 desc.set_application(*form.cleaned_data['application'])
177             if form.cleaned_data['nodes']:
178                 desc.set_nodes(*form.cleaned_data['nodes'])
179             if form.cleaned_data['reservation']:
180                 desc.set_reservation(form.cleaned_data['reservation'])
181             if form.cleaned_data['watch_output']:
182                 desc.set_watch_output(form.cleaned_data['watch_output'], form.cleaned_data['watch_output_pattern'])
183             desc.env_variables += [(env['name'], env['value'])
184                                    for env in env_formset.cleaned_data if env and not env['DELETE']]
185             # TODO script
186             # TODO executable
187             # TODO input
188             # TODO stage_in
189             # TODO stage_out
190             # TODO monitoring
191
192             for prop in direct_map + ('application', 'nodes', 'env_variables', 'reservation', 'watch_output'):
193                 print prop, type(getattr(desc, prop)), repr(getattr(desc, prop))
194
195             print desc.xml_description
196
197         print repr(form.errors)
198         print repr(env_formset.errors)
199     else:
200         form = JobDescriptionForm()
201         env_formset = EnvFormSet()
202
203     errors = form.errors or (env_formset.is_bound and not env_formset.is_valid)
204
205     return render(request, 'qcg/job_new.html', {'form': form, 'env_formset': env_formset, 'errors': errors})
206
207
208 @login_required
209 def gridftp(request):
210     return render(request, 'qcg/gridftp.html')