Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / util.py @ 9efd0075

History | View | Annotate | Download (12.9 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
            import traceback as tb
71
            tb.print_exception(exc_type, value, traceback)
72
            logger.exception(value)
73
            m = _(astakos_messages.GENERIC_ERROR)
74
            messages.error(self.request, m)
75
            return True  # suppress exception
76

    
77

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

    
93

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

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

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

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

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

    
145

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

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

    
162
    model, form_class = get_model_and_form_class(model, form_class)
163
    obj = lookup_object(model, object_id, slug, slug_field)
164

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

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

    
200

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

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

    
213

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

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

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

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

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

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

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

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

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

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

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

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

    
298
    return resource_catalog_new, resource_groups
299

    
300

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

    
314

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

    
326

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

    
335
    next = restrict_next(next, domain=settings.COOKIE_DOMAIN)
336
    return redirect(next)
337