Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / util.py @ 01c782a4

History | View | Annotate | Download (12.8 kB)

1
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import logging
35
import astakos.im.messages as astakos_messages
36

    
37
from astakos.im import settings
38
from django.contrib import messages
39
from django.contrib.auth.views import redirect_to_login
40
from django.core.xheaders import populate_xheaders
41
from django.http import HttpResponse
42
from django.shortcuts import redirect
43
from django.template import RequestContext, loader as template_loader
44
from django.utils.translation import ugettext as _
45
from django.views.generic.create_update import apply_extra_context, \
46
    get_model_and_form_class, lookup_object
47
from django.db import transaction
48

    
49
from synnefo.lib.ordereddict import OrderedDict
50

    
51
from astakos.im import presentation
52
from astakos.im.util import model_to_dict
53
from astakos.im import tables
54
from astakos.im.models import Resource, ProjectApplication, ProjectMembership
55
from astakos.im import functions
56
from astakos.im.util import get_context, restrict_next, restrict_reverse
57

    
58
logger = logging.getLogger(__name__)
59

    
60

    
61
class ExceptionHandler(object):
62
    def __init__(self, request):
63
        self.request = request
64

    
65
    def __enter__(self):
66
        pass
67

    
68
    def __exit__(self, exc_type, value, traceback):
69
        if value is not None:  # exception
70
            logger.exception(value)
71
            m = _(astakos_messages.GENERIC_ERROR)
72
            messages.error(self.request, m)
73
            return True  # suppress exception
74

    
75

    
76
def render_response(template, tab=None, status=200, context_instance=None,
77
                    **kwargs):
78
    """
79
    Calls ``django.template.loader.render_to_string`` with an additional
80
    ``tab`` keyword argument and returns an ``django.http.HttpResponse``
81
    with the specified ``status``.
82
    """
83
    if tab is None:
84
        tab = template.partition('_')[0].partition('.html')[0]
85
    kwargs.setdefault('tab', tab)
86
    html = template_loader.render_to_string(
87
        template, kwargs, context_instance=context_instance)
88
    response = HttpResponse(html, status=status)
89
    return response
90

    
91

    
92
def _create_object(request, model=None, template_name=None,
93
                   template_loader=template_loader, extra_context=None,
94
                   post_save_redirect=None, login_required=False,
95
                   context_processors=None, form_class=None, msg=None,
96
                   summary_template_name=None):
97
    """
98
    Based of django.views.generic.create_update.create_object which displays a
99
    summary page before creating the object.
100
    """
101

    
102
    if extra_context is None:
103
        extra_context = {}
104
    if login_required and not request.user.is_authenticated():
105
        return redirect_to_login(request.path)
106

    
107
    model, form_class = get_model_and_form_class(model, form_class)
108
    extra_context['edit'] = 0
109
    if request.method == 'POST':
110
        form = form_class(request.POST, request.FILES)
111

    
112
        if form.is_valid():
113
            verify = request.GET.get('verify')
114
            edit = request.GET.get('edit')
115
            if verify == '1':
116
                extra_context['show_form'] = False
117
                extra_context['form_data'] = form.cleaned_data
118
                template_name = summary_template_name
119
            elif edit == '1':
120
                extra_context['show_form'] = True
121
            else:
122
                new_object = form.save()
123
                if not msg:
124
                    msg = _(
125
                        "The %(verbose_name)s was created successfully.")
126
                msg = msg % model._meta.__dict__
127
                messages.success(request, msg, fail_silently=True)
128
                return redirect(post_save_redirect, new_object)
129
    else:
130
        form = form_class()
131

    
132
    # Create the template, context, response
133
    if not template_name:
134
        template_name = "%s/%s_form.html" % \
135
            (model._meta.app_label, model._meta.object_name.lower())
136
    t = template_loader.get_template(template_name)
137
    c = RequestContext(request, {
138
        'form': form
139
    }, context_processors)
140
    apply_extra_context(extra_context, c)
141
    return HttpResponse(t.render(c))
142

    
143

    
144
def _update_object(request, model=None, object_id=None, slug=None,
145
                   slug_field='slug', template_name=None,
146
                   template_loader=template_loader, extra_context=None,
147
                   post_save_redirect=None, login_required=False,
148
                   context_processors=None, template_object_name='object',
149
                   form_class=None, msg=None, summary_template_name=None):
150
    """
151
    Based of django.views.generic.create_update.update_object which displays a
152
    summary page before updating the object.
153
    """
154

    
155
    if extra_context is None:
156
        extra_context = {}
157
    if login_required and not request.user.is_authenticated():
158
        return redirect_to_login(request.path)
159

    
160
    model, form_class = get_model_and_form_class(model, form_class)
161
    obj = lookup_object(model, object_id, slug, slug_field)
162

    
163
    if request.method == 'POST':
164
        form = form_class(request.POST, request.FILES, instance=obj)
165
        if form.is_valid():
166
            verify = request.GET.get('verify')
167
            edit = request.GET.get('edit')
168
            if verify == '1':
169
                extra_context['show_form'] = False
170
                extra_context['form_data'] = form.cleaned_data
171
                template_name = summary_template_name
172
            elif edit == '1':
173
                extra_context['show_form'] = True
174
            else:
175
                obj = form.save()
176
                if not msg:
177
                    msg = _("The %(verbose_name)s was created successfully.")
178
                msg = msg % model._meta.__dict__
179
                messages.success(request, msg, fail_silently=True)
180
                return redirect(post_save_redirect, obj)
181
    else:
182
        form = form_class(instance=obj)
183

    
184
    if not template_name:
185
        template_name = "%s/%s_form.html" % \
186
            (model._meta.app_label, model._meta.object_name.lower())
187
    t = template_loader.get_template(template_name)
188
    c = RequestContext(request, {
189
        'form': form,
190
        template_object_name: obj,
191
    }, context_processors)
192
    apply_extra_context(extra_context, c)
193
    response = HttpResponse(t.render(c))
194
    populate_xheaders(request, response, model,
195
                      getattr(obj, obj._meta.pk.attname))
196
    return response
197

    
198

    
199
def sorted_resources(resource_grant_or_quota_set):
200
    meta = presentation.RESOURCES
201
    order = meta.get('resources_order', [])
202
    resources = list(resource_grant_or_quota_set)
203

    
204
    def order_key(item):
205
        name = item.resource.name
206
        if name in order:
207
            return order.index(name)
208
        return -1
209
    return sorted(resources, key=order_key)
210

    
211

    
212
def _resources_catalog(as_dict=False):
213
    """
214
    `resource_catalog` contains a list of tuples. Each tuple contains the group
215
    key the resource is assigned to and resources list of dicts that contain
216
    resource information.
217
    `resource_groups` contains information about the groups
218
    """
219
    # presentation data
220
    resources_meta = presentation.RESOURCES
221
    resource_groups = resources_meta.get('groups', {})
222
    resource_catalog = ()
223
    resource_keys = []
224

    
225
    # resources in database
226
    resource_details = map(lambda obj: model_to_dict(obj, exclude=[]),
227
                           Resource.objects.all())
228
    # initialize resource_catalog to contain all group/resource information
229
    for r in resource_details:
230
        if not r.get('group') in resource_groups:
231
            resource_groups[r.get('group')] = {'icon': 'unknown'}
232

    
233
    resource_keys = [r.get('str_repr') for r in resource_details]
234
    resource_catalog = [[g, filter(lambda r: r.get('group', '') == g,
235
                                   resource_details)] for g in resource_groups]
236

    
237
    # order groups, also include unknown groups
238
    groups_order = resources_meta.get('groups_order')
239
    for g in resource_groups.keys():
240
        if not g in groups_order:
241
            groups_order.append(g)
242

    
243
    # order resources, also include unknown resources
244
    resources_order = resources_meta.get('resources_order')
245
    for r in resource_keys:
246
        if not r in resources_order:
247
            resources_order.append(r)
248

    
249
    # sort catalog groups
250
    resource_catalog = sorted(resource_catalog,
251
                              key=lambda g: groups_order.index(g[0]))
252

    
253
    # sort groups
254
    def groupindex(g):
255
        return groups_order.index(g[0])
256
    resource_groups_list = sorted([(k, v) for k, v in resource_groups.items()],
257
                                  key=groupindex)
258
    resource_groups = OrderedDict(resource_groups_list)
259

    
260
    # sort resources
261
    def resourceindex(r):
262
        return resources_order.index(r['str_repr'])
263

    
264
    for index, group in enumerate(resource_catalog):
265
        resource_catalog[index][1] = sorted(resource_catalog[index][1],
266
                                            key=resourceindex)
267
        if len(resource_catalog[index][1]) == 0:
268
            resource_catalog.pop(index)
269
            for gindex, g in enumerate(resource_groups):
270
                if g[0] == group[0]:
271
                    resource_groups.pop(gindex)
272

    
273
    # filter out resources which user cannot request in a project application
274
    for group, resources in list(resource_catalog):
275
        for resource in resources:
276
            if not resource.get('ui_visible'):
277
                resources.remove(resource)
278

    
279
    # cleanup empty groups
280
    resource_catalog_new = []
281
    for group, resources in list(resource_catalog):
282
        if len(resources) == 0:
283
            resource_groups.pop(group)
284
        else:
285
            resource_catalog_new.append((group, resources))
286

    
287
    if as_dict:
288
        resource_catalog_new = OrderedDict(resource_catalog_new)
289
        for name, resources in resource_catalog_new.iteritems():
290
            _rs = OrderedDict()
291
            for resource in resources:
292
                _rs[resource.get('name')] = resource
293
            resource_catalog_new[name] = _rs
294
        resource_groups = OrderedDict(resource_groups)
295

    
296
    return resource_catalog_new, resource_groups
297

    
298

    
299
def get_user_projects_table(projects, user, prefix):
300
    apps = ProjectApplication.objects.pending_per_project(projects)
301
    memberships = user.projectmembership_set.one_per_project()
302
    objs = ProjectMembership.objects
303
    accepted_ms = objs.any_accepted_per_project(projects)
304
    requested_ms = objs.requested_per_project(projects)
305
    return tables.UserProjectsTable(projects, user=user,
306
                                    prefix=prefix,
307
                                    pending_apps=apps,
308
                                    memberships=memberships,
309
                                    accepted=accepted_ms,
310
                                    requested=requested_ms)
311

    
312

    
313
@transaction.commit_on_success
314
def handle_valid_members_form(request, project_id, addmembers_form):
315
    if addmembers_form.is_valid():
316
        try:
317
            users = addmembers_form.valid_users
318
            for user in users:
319
                functions.enroll_member_by_email(project_id, user.email,
320
                                                 request_user=request.user)
321
        except functions.ProjectError as e:
322
            messages.error(request, e)
323

    
324

    
325
def redirect_to_next(request, default_resolve, *args, **kwargs):
326
    next = kwargs.pop('next', None)
327
    if not next:
328
        default = restrict_reverse(default_resolve, *args,
329
                                   restrict_domain=settings.COOKIE_DOMAIN,
330
                                   **kwargs)
331
        next = request.GET.get('next', default)
332

    
333
    next = restrict_next(next, domain=settings.COOKIE_DOMAIN)
334
    return redirect(next)
335