summaryrefslogtreecommitdiff
path: root/ishtar_common/views.py
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2012-10-18 17:49:57 +0200
committerÉtienne Loks <etienne.loks@peacefrogs.net>2012-10-18 17:51:30 +0200
commitade7bd4b74d9ae42c54648cc7390d8c067b5c5e3 (patch)
tree4136673563f802d6de992512e3c4adde86ef2a4e /ishtar_common/views.py
parent9a3d3dcbca9395c00e55d6ee4909ba9b9a4752f8 (diff)
downloadIshtar-ade7bd4b74d9ae42c54648cc7390d8c067b5c5e3.tar.bz2
Ishtar-ade7bd4b74d9ae42c54648cc7390d8c067b5c5e3.zip
Djangoization - Major refactoring (step 1)
Diffstat (limited to 'ishtar_common/views.py')
-rw-r--r--ishtar_common/views.py1333
1 files changed, 1333 insertions, 0 deletions
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
new file mode 100644
index 000000000..9b06276f8
--- /dev/null
+++ b/ishtar_common/views.py
@@ -0,0 +1,1333 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+try:
+ import tidy
+except:
+ from tidylib import tidy_document as tidy
+
+import re
+import csv
+import json
+import datetime
+import optparse
+import cStringIO as StringIO
+from tempfile import NamedTemporaryFile
+import ho.pisa as pisa
+
+from django.conf import settings
+from django.contrib.formtools.wizard.views import NamedUrlWizardView
+from django.core import serializers
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.db.models import Q
+from django.http import HttpResponse, Http404
+from django.shortcuts import render_to_response, redirect
+from django.template import RequestContext, loader
+from django.template.defaultfilters import slugify
+from django.utils.translation import ugettext, ugettext_lazy as _
+
+if settings.XHTML2ODT_PATH:
+ import sys
+ sys.path.append(settings.XHTML2ODT_PATH)
+ from xhtml2odt import xhtml2odt
+
+from menus import menu
+import forms_main as ishtar_forms
+
+from ishtar_common.forms import FinalForm
+from ishtar_common.forms_common import PersonForm
+import models
+
+CSV_OPTIONS = {'delimiter':';', 'quotechar':'"', 'quoting':csv.QUOTE_ALL}
+ENCODING = settings.ENCODING or 'utf-8'
+
+def index(request):
+ """
+ Main page
+ """
+ dct = {}
+ return render_to_response('index.html', dct,
+ context_instance=RequestContext(request))
+
+class Wizard(NamedUrlWizardView):
+ model = None
+ modification = None # True when the wizard modify an item
+ storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
+
+ @staticmethod
+ def _check_right(step, condition=True):
+ '''Return a method to check the right for a specific step'''
+ """
+ def check_right(self, request, storage):
+ cond = condition
+ if callable(condition):
+ cond = condition(self, request, storage)
+ if not cond:
+ return False
+ person_type = request.user.ishtaruser.person.person_type
+ if person_type.txt_idx == 'administrator':
+ return True
+ if person_type.rights.filter(url_name=step).count():
+ return True"""
+ def check_right(self):
+ cond = condition
+ if callable(condition):
+ cond = condition(self)
+ if not cond:
+ return False
+ person_type = self.request.user.ishtaruser.person.person_type
+ if person_type.txt_idx == 'administrator':
+ return True
+ if person_type.rights.filter(url_name=step).count():
+ return True
+ return check_right
+
+ def __init__(self, *args, **kwargs):
+ """Check right for each step of the wizard"""
+ super(Wizard, self).__init__(*args, **kwargs)
+ for form_key in self.form_list.keys()[:-1]:
+ condition = True
+ if form_key in self.condition_dict:
+ condition = self.condition_dict.get(form_key, True)
+ cond = self._check_right(form_key, condition)
+ self.condition_dict[form_key] = cond
+ """
+ for form_key in self.form_list.keys()[:-1]:
+ condition = True
+ if form_key in self.condition_list:
+ condition = self.condition_list.get(form_key, True)
+ cond = self._check_right(form_key, condition)
+ self.condition_list[form_key] = cond"""
+
+ def get_wizard_name(self):
+ """As the class name can interfere when reused, use the url_name"""
+ return self.url_name
+
+ def get_template_names(self):
+ templates = ['ishtar/wizard/default_wizard.html']
+ current_step = self.steps.current
+ if current_step == self.steps.last:
+ templates = ['ishtar/wizard/confirm_wizard.html'] + templates
+ return templates
+
+ def get_context_data(self, form, **kwargs):
+ """Add previous, next and current steps to manage the wizard path"""
+ context = super(Wizard, self).get_context_data(form)
+ step = self.steps.first
+ current_step = self.steps.current
+ context.update({'current_step':self.form_list[current_step]})
+ if step == current_step:
+ return context
+ previous_steps, next_steps, previous_step_counter = [], [], 0
+ while step:
+ if step == current_step:
+ break
+ previous_steps.append(self.form_list[step])
+ step = self.steps.next
+ previous_step_counter += 1
+ context.update({'previous_steps':previous_steps,
+ 'previous_step_counter':previous_step_counter})
+ # if modification: show the next steps
+ if self.modification:
+ next_step = step
+ while next_step:
+ # check if the form is initialized otherwise initialize it
+ if not storage.get_step_data(next_step):
+ values = self.get_form_initial(request, storage, next_step)
+ prefixed_values = {}
+ if not isinstance(values, list):
+ for key in values:
+ form_key = next_step + '-' + key
+ prefixed_values[form_key] = values[key]
+ else:
+ for formset_idx, v in enumerate(values):
+ prefix = u"-%d-" % formset_idx
+ for key in v:
+ form_key = next_step + prefix + key
+ prefixed_values[form_key] = v[key]
+ storage.set_step_data(next_step, prefixed_values)
+ if step != next_step: # if not current step
+ next_steps.append(self.form_list[next_step])
+ next_step = self.get_next_step(request, storage, next_step)
+ context.update({'next_steps':next_steps})
+ # not last step: validation
+ if current_step != self.steps.last:
+ return context
+ final_form_list = []
+ for form_key in self.get_form_list().keys():
+ form_obj = self.get_form(step=form_key,
+ data=self.storage.get_step_data(form_key),
+ files=self.storage.get_step_files(form_key))
+ form_obj.is_valid()
+ final_form_list.append(form_obj)
+ last_form = final_form_list[-1]
+ context.update({'datas':self.get_formated_datas(final_form_list)})
+ if hasattr(last_form, 'confirm_msg'):
+ context.update({'confirm_msg':last_form.confirm_msg})
+ if hasattr(last_form, 'confirm_end_msg'):
+ context.update({'confirm_end_msg':last_form.confirm_end_msg})
+ return context
+
+ def get_formated_datas(self, forms):
+ """Get the data to present in the last page"""
+ datas = []
+ for form in forms:
+ form_datas = []
+ base_form = hasattr(form, 'forms') and form.forms[0] or form
+ associated_models = hasattr(base_form, 'associated_models') and \
+ base_form.associated_models or {}
+ if not hasattr(form, 'cleaned_data') and hasattr(form, 'forms'):
+ cleaned_datas = [frm.cleaned_data for frm in form.forms
+ if frm.is_valid()]
+ if not cleaned_datas:
+ continue
+ elif not hasattr(form, 'cleaned_data'):
+ continue
+ else:
+ cleaned_datas = type(form.cleaned_data) == list and \
+ form.cleaned_data \
+ or [form.cleaned_data]
+ for cleaned_data in cleaned_datas:
+ if not cleaned_data:
+ continue
+ if form_datas:
+ form_datas.append(("", "", "spacer"))
+ items = hasattr(base_form, 'fields') and \
+ base_form.fields.keyOrder or cleaned_data.keys()
+ for key in items:
+ lbl = None
+ if key.startswith('hidden_'):
+ continue
+ if hasattr(base_form, 'fields') and key in base_form.fields:
+ lbl = base_form.fields[key].label
+ if hasattr(base_form, 'associated_labels') \
+ and key in base_form.associated_labels:
+ lbl = base_form.associated_labels[key]
+ if not lbl:
+ continue
+ value = cleaned_data[key]
+ if not value and value != False:
+ continue
+ if type(value) == bool:
+ if value == True:
+ value = _(u"Yes")
+ elif value == False:
+ value = _(u"No")
+ elif key in associated_models:
+ values = []
+ if "," in unicode(value):
+ values = unicode(value).split(",")
+ else:
+ values = [value]
+ rendered_values = []
+ for val in values:
+ item = associated_models[key].objects.get(pk=val)
+ if hasattr(item, 'short_label'):
+ value = item.short_label()
+ else:
+ value = unicode(item)
+ rendered_values.append(value)
+ value = u" ; ".join(rendered_values)
+ form_datas.append((lbl, value, ''))
+ if form_datas:
+ datas.append((form.form_label, form_datas))
+ return datas
+
+ def get_extra_model(self, dct, form_list):
+ dct['history_modifier'] = self.request.user
+ return dct
+
+ def done(self, form_list, return_object=False, **kwargs):
+ """Save to the model"""
+ dct, m2m, whole_associated_models = {}, [], []
+ for form in form_list:
+ if not form.is_valid():
+ return self.render(form)
+ base_form = hasattr(form, 'forms') and form.forms[0] or form
+ associated_models = hasattr(base_form, 'associated_models') and \
+ base_form.associated_models or {}
+ if hasattr(form, 'forms'):
+ multi = False
+ if form.forms:
+ frm = form.forms[0]
+ if hasattr(frm, 'base_model') and frm.base_model:
+ whole_associated_models.append(frm.base_model)
+ else:
+ whole_associated_models += associated_models.keys()
+ fields = frm.fields.copy()
+ if 'DELETE' in fields:
+ fields.pop('DELETE')
+ multi = len(fields) > 1
+ if multi:
+ assert hasattr(frm, 'base_model'), \
+ u"Must define a base_model for " + unicode(frm.__class__)
+ for frm in form.forms:
+ if not frm.is_valid():
+ continue
+ vals = {}
+ if "DELETE" in frm.cleaned_data:
+ if frm.cleaned_data["DELETE"]:
+ continue
+ frm.cleaned_data.pop('DELETE')
+ for key in frm.cleaned_data:
+ value = frm.cleaned_data[key]
+ if not value and value != False:
+ continue
+ if key in associated_models:
+ value = associated_models[key].objects.get(pk=value)
+ if multi:
+ vals[key] = value
+ else:
+ m2m.append((key, value))
+ if multi and vals:
+ m2m.append((frm.base_model, vals))
+ elif type(form.cleaned_data) == dict:
+ for key in form.cleaned_data:
+ if key.startswith('hidden_'):
+ continue
+ value = form.cleaned_data[key]
+ if key in associated_models:
+ if value:
+ model = associated_models[key]
+ if isinstance(value, unicode) \
+ or isinstance(value, str) and "," in value:
+ value = value.split(",")
+ if isinstance(value, list) \
+ or isinstance(value, tuple):
+ value = [model.objects.get(pk=val)
+ for val in value if val]
+ if len(value) == 1:
+ value = value[0]
+ else:
+ value = model.objects.get(pk=value)
+ else:
+ value = None
+ dct[key] = value
+ return self.save_model(dct, m2m, whole_associated_models, form_list,
+ return_object)
+
+ def get_saved_model(self):
+ """Permit a distinguo when saved model is not the base selected model"""
+ return self.model
+
+ def get_current_saved_object(self):
+ """Permit a distinguo when saved model is not the base selected model"""
+ return self.get_current_object()
+
+ def save_model(self, dct, m2m, whole_associated_models, form_list,
+ return_object):
+ dct = self.get_extra_model(dct, form_list)
+ obj = self.get_current_saved_object()
+ # manage dependant items
+ other_objs = {}
+ for k in dct.keys():
+ if '__' not in k:
+ continue
+ vals = k.split('__')
+ assert len(vals) == 2, "Only one level of dependant item is managed"
+ dependant_item, key = vals
+ if dependant_item not in other_objs:
+ other_objs[dependant_item] = {}
+ other_objs[dependant_item][key] = dct.pop(k)
+ if obj:
+ for k in dct:
+ if k.startswith('pk'):
+ continue
+ setattr(obj, k, dct[k])
+ try:
+ obj.full_clean()
+ except forms.ValidationError, msg:
+ return self.render(form_list[-1])
+ for dependant_item in other_objs:
+ c_item = getattr(obj, dependant_item)
+ # manage ManyToMany if only one associated
+ if hasattr(c_item, "all"):
+ c_items = c_item.all()
+ if len(c_items) != 1:
+ continue
+ c_item = c_items[0]
+ if c_item:
+ # to check #
+ for k in other_objs[dependant_item]:
+ setattr(c_item, k, other_objs[dependant_item][k])
+ c_item.save()
+ else:
+ m = getattr(self.model, dependant_item)
+ if hasattr(m, 'related'):
+ c_item = m.related.model(**other_objs[dependant_item])
+ setattr(obj, dependant_item, c_item)
+ obj.save()
+ obj.save()
+ else:
+ adds = {}
+ for dependant_item in other_objs:
+ m = getattr(self.model, dependant_item)
+ model = m.field.rel.to
+ c_dct = other_objs[dependant_item].copy()
+ if issubclass(model, models.BaseHistorizedItem):
+ c_dct['history_modifier'] = self.request.user
+ c_item = model(**c_dct)
+ c_item.save()
+ if hasattr(m, 'through'):
+ adds[dependant_item] = c_item
+ elif hasattr(m, 'field'):
+ dct[dependant_item] = c_item
+ if 'pk' in dct:
+ dct.pop('pk')
+ obj = self.get_saved_model()(**dct)
+ try:
+ obj.full_clean()
+ except forms.ValidationError, msg:
+ return self.render(form_list[-1])
+ obj.save()
+ for k in adds:
+ getattr(obj, k).add(adds[k])
+ # necessary to manage interaction between models like
+ # material_index management for baseitems
+ obj.save()
+ m2m_items = {}
+ for model in whole_associated_models:
+ getattr(obj, model+'s').clear()
+ for key, value in m2m:
+ if key not in m2m_items:
+ if type(key) == dict:
+ vals = []
+ for item in getattr(obj, key+'s').all():
+ v = {}
+ for k in value.keys():
+ v[k] = getattr(item, k)
+ vals.append(v)
+ m2m_items[key] = vals
+ else:
+ m2m_items[key] = getattr(obj, key+'s').all()
+ if value not in m2m_items[key]:
+ if type(value) == dict:
+ model = getattr(obj, key+'s').model
+ if issubclass(model, models.BaseHistorizedItem):
+ value['history_modifier'] = self.request.user
+ value = model.objects.create(**value)
+ value.save()
+ getattr(obj, key+'s').add(value)
+ # necessary to manage interaction between models like
+ # material_index management for baseitems
+ obj.save()
+ res = render_to_response('wizard_done.html', {},
+ context_instance=RequestContext(self.request))
+ return return_object and (obj, res) or res
+
+ def get_deleted(self, keys):
+ """Get the deleted and non-deleted items in formsets"""
+ not_to_delete, to_delete = set(), set()
+ for key in keys:
+ items = key.split('-')
+ if len(items) < 2 or items[-2] in to_delete:
+ continue
+ idx = items[-2]
+ try:
+ int(idx)
+ except:
+ continue
+ if items[-1] == u'DELETE':
+ to_delete.add(idx)
+ if idx in not_to_delete:
+ not_to_delete.remove(idx)
+ elif idx not in not_to_delete:
+ not_to_delete.add(idx)
+ return (to_delete, not_to_delete)
+
+ def get_form(self, step=None, data=None, files=None):
+ """Manage formset"""
+ request = self.request
+ storage = self.storage
+ if data:
+ data = data.copy()
+ if not step:
+ step = self.steps.current
+ form = self.get_form_list()[step]
+ if hasattr(form, 'management_form'):
+ # manage deletion
+ to_delete, not_to_delete = self.get_deleted(data.keys())
+ # raz deleted fields
+ for key in data.keys():
+ items = key.split('-')
+ if len(items) < 2 or items[-2] not in to_delete:
+ continue
+ data.pop(key)
+ if to_delete:
+ # reorganize
+ for idx, number in enumerate(sorted(not_to_delete)):
+ idx = unicode(idx)
+ if idx == number:
+ continue
+ for key in data.keys():
+ items = key.split('-')
+ if len(items) > 2 and number == items[-2]:
+ items[-2] = unicode(idx)
+ k = u'-'.join(items)
+ data[k] = data.pop(key)[0]
+ # get a form key
+ base_key = form.form.base_fields.keys()[0]
+ init = self.get_form_initial(request, storage, step)
+ total_field = len([key for key in data.keys()
+ if base_key in key.split('-')
+ and data[key]])
+ if init and not to_delete:
+ total_field = max((total_field, len(init)))
+ data[step + u'-INITIAL_FORMS'] = unicode(total_field)
+ data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1)
+ data = data or None
+ form = super(Wizard, self).get_form(step, data, files)
+ return form
+
+ def render_next_step(self, form, **kwargs):
+ """
+ Manage:
+ - modify or delete button in formset: next step = current step
+ - validate and end: nextstep = last step
+ """
+ request = self.request
+ if request.POST.has_key('formset_modify') \
+ and request.POST['formset_modify'] \
+ or [key for key in request.POST.keys()
+ if key.endswith('DELETE') and request.POST[key]]:
+ return self.render(form)
+ elif request.POST.has_key('validate_and_end') \
+ and request.POST['validate_and_end']:
+ last_step = self.steps.last
+ new_form = self.get_form(last_step,
+ data=self.storage.get_step_data(last_step),
+ files=self.storage.get_step_files(last_step))
+ self.storage.current_step = last_step
+ return self.render(new_form)
+ return super(Wizard, self).render_next_step(form, **kwargs)
+
+ def post(self, *args, **kwargs):
+ """Convert numerical step number to step name"""
+ request = self.request
+ post_data = request.POST.copy()
+ if request.POST.has_key('form_prev_step'):
+ try:
+ step_number = int(request.POST['form_prev_step'])
+ post_data['wizard_goto_step'] = self.get_form_list().keys(
+ )[step_number]
+ except ValueError:
+ pass
+ request.POST = post_data
+ return super(Wizard, self).post(*args, **kwargs)
+
+ @classmethod
+ def session_has_key(cls, request, storage, form_key, key=None, multi=None):
+ """Check if the session has value of a specific form and (if provided)
+ of a key
+ """
+ test = storage.prefix in request.session \
+ and 'step_data' in request.session[storage.prefix] \
+ and form_key in request.session[storage.prefix]['step_data']
+ if not key or not test:
+ return test
+ key = key.startswith(form_key) and key or \
+ not multi and form_key + '-' + key or \
+ form_key + '-0-' + key #only check if the first field is available
+ return key in request.session[storage.prefix]['step_data'][form_key]
+
+ @classmethod
+ def session_get_value(cls, request, storage, form_key, key, multi=False):
+ """Get the value of a specific form"""
+ if not cls.session_has_key(request, storage, form_key, key, multi):
+ return
+ if not multi:
+ key = key.startswith(form_key) and key or form_key + '-' + key
+ return request.session[storage.prefix]['step_data'][form_key][key]
+ vals = []
+ for k in request.session[storage.prefix]['step_data'][form_key]:
+ if k.startswith(form_key) and k.endswith(key) and \
+ request.session[storage.prefix]['step_data'][form_key][k]:
+ vals.append(request.session[storage.prefix]['step_data']\
+ [form_key][k])
+ return vals
+
+ def get_current_object(self):
+ """Get the current object for an instancied wizard"""
+ current_obj = None
+ main_form_key = 'selec-' + self.url_name
+ try:
+ idx = int(self.session_get_value(self.request, self.storage,
+ main_form_key, 'pk'))
+ current_obj = self.model.objects.get(pk=idx)
+ except(TypeError, ValueError, ObjectDoesNotExist):
+ pass
+ return current_obj
+
+ def get_form_initial(self, step):
+ current_obj = self.get_current_object()
+ current_step = self.steps.current
+ if step.startswith('selec-') and step in self.form_list \
+ and 'pk' in self.form_list[step].associated_models:
+ model_name = self.form_list[step].associated_models['pk'
+ ].__name__.lower()
+ if step == current_step:
+ self.reset_wizard(request, storage)
+ val = model_name in request.session and request.session[model_name]
+ if val:
+ return {'pk':val}
+ elif current_obj:
+ return self.get_instanced_init(current_obj, step)
+ current_form = self.form_list[current_step]
+ if hasattr(current_form, 'currents'):
+ initial = {}
+ for key in current_form.currents:
+ model_name = current_form.currents[key].__name__.lower()
+ val = model_name in request.session and \
+ request.session[model_name]
+ if val:
+ initial[key] = val
+ if initial:
+ return initial
+ return super(Wizard, self).get_form_initial(step)
+
+ def get_instanced_init(self, obj, step=None):
+ """Get initial data from an init"""
+ current_step = step or self.steps.current
+ c_form = self.form_list[current_step]
+ # make the current object the default item for the session
+ obj_name = obj.__class__.__name__.lower()
+ # prefer a specialized name if available
+ prefixes = self.storage.prefix.split('_')
+ if len(prefixes) > 1 and prefixes[-2].startswith(obj_name):
+ obj_name = prefixes[-2]
+ self.request.session[obj_name] = unicode(obj.pk)
+ initial = {}
+ if self.request.POST or \
+ (step in self.request.session[self.storage.prefix] and\
+ self.request.session[self.storage.prefix]['step_data'][step]):
+ return {}
+ if hasattr(c_form, 'base_fields'):
+ for base_field in c_form.base_fields.keys():
+ fields = base_field.split('__')
+ value = obj
+ for field in fields:
+ if not hasattr(value, field) or \
+ getattr(value, field) == None:
+ value = obj
+ break
+ value = getattr(value, field)
+ if value == obj:
+ continue
+ if hasattr(value, 'pk'):
+ value = value.pk
+ if value in (True, False):
+ initial[base_field] = value
+ elif value != None:
+ initial[base_field] = unicode(value)
+ elif hasattr(c_form, 'management_form'):
+ initial = []
+ if hasattr(c_form.form, 'base_model'):
+ key = c_form.form.base_model + 's'
+ else:
+ key = current_step.split('-')[0]
+ if not hasattr(obj, key):
+ return initial
+ keys = c_form.form.base_fields.keys()
+ for child_obj in getattr(obj, key).order_by('pk').all():
+ if not keys:
+ break
+ vals = {}
+ if len(keys) == 1:
+ # only one field: must be the id of the object
+ vals[keys[0]] = unicode(child_obj.pk)
+ else:
+ for field in keys:
+ if hasattr(child_obj, field):
+ value = getattr(child_obj, field)
+ if hasattr(value, 'pk'):
+ value = value.pk
+ if value != None:
+ vals[field] = unicode(value)
+ if vals:
+ initial.append(vals)
+ return initial
+
+class PersonWizard(Wizard):
+ model = models.Person
+
+person_creation_wizard = PersonWizard.as_view([
+ ('identity-person_creation', PersonForm),
+ ('final-person_creation', FinalForm)],
+ url_name='person_creation_step',)
+
+
+def update_current_item(request):
+ if not request.is_ajax() and not request.method == 'POST':
+ raise Http404
+ if 'value' in request.POST and 'item' in request.POST:
+ request.session[request.POST['item']] = request.POST['value']
+ return HttpResponse('ok')
+
+def check_permission(request, action_slug, obj_id=None):
+ if action_slug not in menu.items:
+ #! TODO
+ return True
+ if obj_id:
+ return menu.items[action_slug].is_available(request.user, obj_id)
+ return menu.items[action_slug].can_be_available(request.user)
+
+def autocomplete_person(request, person_type=None):
+ person_types = request.user.ishtaruser.person.person_type
+ if (not request.user.has_perm('ishtar_common.view_person', models.Person) and
+ not request.user.has_perm('ishtar_common.view_own_person', models.Person)
+ and not person_types.rights.filter(wizard__url_name='person_search'
+ ).count()):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ limit = request.GET.get('limit', 20)
+ try:
+ limit = int(limit)
+ except ValueError:
+ return HttpResponseBadRequest()
+ query = Q()
+ for q in q.split(' '):
+ query = query & (Q(name__icontains=q) | Q(surname__icontains=q) | \
+ Q(email__icontains=q))
+ if person_type:
+ try:
+ typs = [int(tp) for tp in person_type.split('_') if tp]
+ typ = models.PersonType.objects.filter(pk__in=typs).all()
+ query = query & Q(person_type__in=typ)
+ except (ValueError, ObjectDoesNotExist):
+ pass
+ limit = 20
+ persons = models.Person.objects.filter(query)[:limit]
+ data = json.dumps([{'id':person.pk, 'value':unicode(person)}
+ for person in persons if person])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_town(request):
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q)
+ if settings.COUNTRY == 'fr':
+ extra = (extra | Q(numero_insee__istartswith=q) | \
+ Q(departement__label__istartswith=q))
+ query = query & extra
+ limit = 20
+ towns = models.Town.objects.filter(query)[:limit]
+ data = json.dumps([{'id':town.pk, 'value':unicode(town)}
+ for town in towns])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_file(request):
+ person_types = request.user.ishtaruser.person.person_type
+ if (not request.user.has_perm('ishtar_common.view_file', models.File) and \
+ not request.user.has_perm('ishtar_common.view_own_file', models.File)
+ and not person_types.rights.filter(wizard__url_name='file_search'
+ ).count()):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(internal_reference__icontains=q) | \
+ Q(towns__name__icontains=q)
+ try:
+ value = int(q)
+ extra = extra | Q(year=q) | Q(numeric_reference=q)
+ except ValueError:
+ pass
+ query = query & extra
+ limit = 20
+ files = models.File.objects.filter(query)[:limit]
+ data = json.dumps([{'id':file.pk, 'value':unicode(file)}
+ for file in files])
+ return HttpResponse(data, mimetype='text/plain')
+
+from types import NoneType
+
+def format_val(val):
+ if type(val) == NoneType:
+ return u""
+ if type(val) == bool:
+ if val:
+ return unicode(_(u"True"))
+ else:
+ return unicode(_(u"False"))
+ return unicode(val)
+
+HIERARCHIC_LEVELS = 5
+HIERARCHIC_FIELDS = ['period', 'unit', 'material_type']
+PRIVATE_FIELDS = ('id', 'history_modifier', 'order')
+def get_item(model, func_name, default_name, extra_request_keys=[],
+ base_request={}, bool_fields=[]):
+ """
+ Generic treatment of tables
+ """
+ def func(request, data_type='json', full=False, **dct):
+ if 'type' in dct:
+ data_type = dct.pop('type')
+ if not data_type:
+ data_type = 'json'
+ fields = [model._meta.get_field_by_name(k)[0]
+ for k in model._meta.get_all_field_names()]
+ request_keys = dict([(field.name,
+ field.name + (hasattr(field, 'rel') and field.rel and '__pk' or ''))
+ for field in fields])
+ request_keys.update(extra_request_keys)
+ request_items = request.method == 'POST' and request.POST or request.GET
+ dct = base_request.copy()
+ try:
+ old = 'old' in request_items and int(request_items['old'])
+ except ValueError:
+ return HttpResponse(None, mimetype='text/plain')
+ for k in request_keys:
+ q = request_items.get(k)
+ if not q:
+ continue
+ dct[request_keys[k]] = q
+ if not dct and 'submited' not in request_items:
+ if default_name in request.session and \
+ request.session[default_name]:
+ dct = {"pk":request.session[default_name]}
+ if (not dct or data_type == 'csv') and func_name in request.session:
+ dct = request.session[func_name]
+ else:
+ request.session[func_name] = dct
+ for k in bool_fields:
+ if k in dct:
+ if dct[k] == u"1":
+ dct.pop(k)
+ else:
+ dct[k] = dct[k] == u"2" and True or False
+
+ # manage hierarchic conditions
+ or_reqs = []
+ for req in dct.copy():
+ for k_hr in HIERARCHIC_FIELDS:
+ if req.endswith(k_hr + '__pk'):
+ val = dct.pop(req)
+ reqs = Q(**{req:val})
+ req = req[:-2] + '__'
+ for idx in xrange(HIERARCHIC_LEVELS):
+ req = req[:-2] + 'parent__pk'
+ q = Q(**{req:val})
+ reqs = reqs | q
+ or_reqs.append(reqs)
+ break
+ query = Q(**dct)
+ for or_req in or_reqs:
+ query = query & or_req
+ items = model.objects.filter(query)
+ q = request_items.get('sidx')
+
+ # manage sort tables
+ if q and q in request_keys:
+ ks = request_keys[q]
+ if type(ks) not in (list, tuple):
+ ks = [ks]
+ orders = []
+ for k in ks:
+ if k.endswith("__pk"):
+ k = k[:-len("__pk")] + "__label"
+ q = request_items.get('sord')
+ sign = q and q == u'desc' and "-" or ''
+ if '__' in k:
+ k = k.split('__')[0]
+ orders.append(sign+k)
+ items = items.order_by(*orders)
+
+ # pager management
+ start, end = 0, None
+ page_nb = 1
+ try:
+ row_nb = int(request_items.get('rows'))
+ except (ValueError, TypeError):
+ row_nb = None
+ if row_nb:
+ try:
+ page_nb = int(request_items.get('page'))
+ assert page_nb >= 1
+ except (ValueError, AssertionError):
+ pass
+ start = (page_nb-1)*row_nb
+ end = page_nb*row_nb
+ items_nb = items.count()
+ items = items[start:end]
+
+ datas = []
+ if old:
+ items = [item.get_previous(old) for item in items]
+ table_cols = full and [field.name for field in model._meta.fields
+ if field.name not in PRIVATE_FIELDS] \
+ or model.TABLE_COLS
+ for item in items:
+ data = [item.pk]
+ for k in table_cols:
+ vals = [item]
+ for ky in k.split('.'):
+ new_vals = []
+ for val in vals:
+ if hasattr(val, 'all'): # manage related objects
+ val = list(val.all())
+ for v in val:
+ new_vals.append(getattr(v, ky))
+ elif val:
+ new_vals.append(getattr(val, ky))
+ vals = new_vals
+ if vals and hasattr(vals[0], 'all'): # manage last related objects
+ new_vals = []
+ for val in vals:
+ new_vals += list(val.all())
+ vals = new_vals
+ data.append(", ".join([format_val(v) for v in vals]) or u"")
+ datas.append(data)
+ link_template = "<a href='#' onclick='load_window(\"%%s\")'>%s</a>" % \
+ (unicode(_("Details")))
+ if data_type == "json":
+ rows = []
+ for data in datas:
+ try:
+ lnk = link_template % reverse('show-'+default_name,
+ args=[data[0], ''])
+ except NoReverseMatch:
+ lnk = ''
+ res = {'id':data[0], 'link':lnk}
+ for idx, value in enumerate(data[1:]):
+ if value:
+ res[table_cols[idx].split('.')[-1]] = value
+ rows.append(res)
+ data = json.dumps({
+ "records":items_nb,
+ "rows":rows,
+ "page":page_nb,
+ "total":items_nb/row_nb + 1,
+ })
+ return HttpResponse(data, mimetype='text/plain')
+ elif data_type == "csv":
+ response = HttpResponse(mimetype='text/csv')
+ n = datetime.datetime.now()
+ filename = u'%s_%s.csv' % (default_name,
+ n.strftime('%Y%m%d-%H%M%S'))
+ response['Content-Disposition'] = 'attachment; filename=%s'%filename
+ writer = csv.writer(response, **CSV_OPTIONS)
+ col_names = []
+ for field_name in table_cols:
+ try:
+ field = model._meta.get_field(field_name)
+ except:
+ col_names.append(u"".encode(ENCODING))
+ continue
+ col_names.append(unicode(field.verbose_name).encode(ENCODING))
+ writer.writerow(col_names)
+ for data in datas:
+ writer.writerow([val.encode(ENCODING) for val in data[1:]])
+ return response
+ return HttpResponse(None, mimetype='text/plain')
+
+ return func
+
+def show_item(model, name):
+ def func(request, pk, **dct):
+ try:
+ item = model.objects.get(pk=pk)
+ except ObjectDoesNotExist:
+ return HttpResponse(None)
+ doc_type = 'type' in dct and dct.pop('type')
+ date = 'date' in dct and dct.pop('date')
+ dct['window_id'] = "%s-%d-%s" % (name, item.pk,
+ datetime.datetime.now().strftime('%M%s'))
+ if date:
+ try:
+ date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f')
+ item = item.get_previous(date=date)
+ assert item != None
+ except (ValueError, AssertionError):
+ return HttpResponse(None, mimetype='text/plain')
+ dct['previous'] = item._previous
+ dct['next'] = item._next
+ else:
+ historized = item.history.all()
+ if historized:
+ item.history_date = historized[0].history_date
+ if len(historized) > 1:
+ dct['previous'] = historized[1].history_date
+ dct['item'], dct['item_name'] = item, name
+ context_instance = RequestContext(request)
+ context_instance.update(dct)
+ n = datetime.datetime.now()
+ filename = u'%s_%s_%s' % (name, slugify(unicode(item)),
+ n.strftime('%Y%m%d-%H%M%S'))
+ if doc_type == "odt" and settings.XHTML2ODT_PATH and \
+ settings.ODT_TEMPLATE:
+ tpl = loader.get_template('sheet_%s.html' % name)
+ content = tpl.render(context_instance)
+ try:
+ tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1,
+ tidy_mark=0, output_encoding='utf8', doctype='auto',
+ wrap=0, char_encoding='utf8')
+ html = str(tidy.parseString(content.encode('utf-8'),
+ **tidy_options))
+ html = html.replace("&nbsp;", "&#160;")
+ html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html)
+
+ odt = NamedTemporaryFile()
+ options = optparse.Values()
+ options.with_network = True
+ for k, v in (('input', ''),
+ ('output', odt.name),
+ ('template', settings.ODT_TEMPLATE),
+ ('with_network', True),
+ ('top_header_level', 1),
+ ('img_width', '8cm'),
+ ('img_height', '6cm'),
+ ('verbose', False),
+ ('replace_keyword', 'ODT-INSERT'),
+ ('cut_start', 'ODT-CUT-START'),
+ ('htmlid', None),
+ ('url', "#")):
+ setattr(options, k, v)
+ odtfile = xhtml2odt.ODTFile(options)
+ odtfile.open()
+ odtfile.import_xhtml(html)
+ odtfile = odtfile.save()
+ except xhtml2odt.ODTExportError, ex:
+ return HttpResponse(content, content_type="application/xhtml")
+ response = HttpResponse(
+ mimetype='application/vnd.oasis.opendocument.text')
+ response['Content-Disposition'] = 'attachment; filename=%s.odt' % \
+ filename
+ response.write(odtfile)
+ return response
+ elif doc_type == 'pdf':
+ tpl = loader.get_template('sheet_%s_pdf.html' % name)
+ content = tpl.render(context_instance)
+ result = StringIO.StringIO()
+ html = content.encode('utf-8')
+ html = html.replace("<table", "<pdf:nextpage/><table repeat='1'")
+ pdf = pisa.pisaDocument(StringIO.StringIO(html), result)
+ response = HttpResponse(result.getvalue(),
+ mimetype='application/pdf')
+ response['Content-Disposition'] = 'attachment; filename=%s.pdf' % \
+ filename
+ if not pdf.err:
+ return response
+ return HttpResponse(content, content_type="application/xhtml")
+ else:
+ tpl = loader.get_template('sheet_%s_window.html' % name)
+ content = tpl.render(context_instance)
+ return HttpResponse(content, content_type="application/xhtml")
+ return func
+
+def revert_item(model):
+ def func(request, pk, date, **dct):
+ try:
+ item = model.objects.get(pk=pk)
+ date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f')
+ item.rollback(date)
+ except (ObjectDoesNotExist, ValueError, HistoryError):
+ return HttpResponse(None, mimetype='text/plain')
+ return HttpResponse("True", mimetype='text/plain')
+ return func
+
+
+get_file = get_item(models.File, 'get_file', 'file')
+show_file = show_item(models.File, 'file')
+revert_file = revert_item(models.File)
+
+def autocomplete_operation(request, non_closed=True):
+ person_types = request.user.ishtaruser.person.person_type
+ if (not request.user.has_perm('ishtar_common.view_operation', models.Operation)\
+ and not request.user.has_perm('ishtar_common.view_own_operation',
+ models.Operation)
+ and not person_types.rights.filter(wizard__url_name='operation_search'
+ ).count()):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(towns__name__icontains=q)
+ try:
+ value = int(q)
+ extra = extra | Q(year=q) | Q(operation_code=q)
+ except ValueError:
+ pass
+ query = query & extra
+ if non_closed:
+ query = query & Q(end_date__isnull=True)
+ limit = 15
+ operations = models.Operation.objects.filter(query)[:limit]
+ data = json.dumps([{'id':operation.pk, 'value':unicode(operation)}
+ for operation in operations])
+ return HttpResponse(data, mimetype='text/plain')
+
+def get_available_operation_code(request, year=None):
+ if not request.user.has_perm('ishtar_common.view_operation', models.Operation)\
+ and not request.user.has_perm('ishtar_common.view_own_operation',
+ models.Operation):
+ return HttpResponse(mimetype='text/plain')
+ data = json.dumps({'id':models.Operation.get_available_operation_code(year)})
+ return HttpResponse(data, mimetype='text/plain')
+
+get_operation = get_item(models.Operation, 'get_operation', 'operation',
+ bool_fields = ['end_date__isnull'],
+ extra_request_keys={'common_name':'common_name__icontains',
+ 'end_date':'end_date__isnull',
+ 'year_index':('year', 'operation_code')})
+show_operation = show_item(models.Operation, 'operation')
+revert_operation = revert_item(models.Operation)
+
+get_operationsource = get_item(models.OperationSource,
+ 'get_operationsource', 'operationsource',
+ extra_request_keys={'operation__towns':'operation__towns__pk',
+ 'operation__operation_type':'operation__operation_type__pk',
+ 'operation__year':'operation__year'})
+
+get_administrativeactfile = get_item(models.AdministrativeAct,
+ 'get_administrativeactfile', 'administrativeactfile',
+ extra_request_keys={'associated_file__towns':'associated_file__towns__pk',
+ 'operation__towns':'operation__towns__pk',
+ 'act_type__intented_to':'act_type__intented_to'})
+get_administrativeactop = get_item(models.AdministrativeAct,
+ 'get_administrativeactop', 'administrativeactop',
+ extra_request_keys={'associated_file__towns':'associated_file__towns__pk',
+ 'operation__towns':'operation__towns__pk',
+ 'act_type__intented_to':'act_type__intented_to'})
+
+def autocomplete_organization(request, orga_type=None):
+ person_types = request.user.ishtaruser.person.person_type
+ if (not request.user.has_perm('ishtar_common.view_organization',
+ models.Organization) and \
+ not request.user.has_perm('ishtar_common.view_own_organization',
+ models.Organization)
+ and not person_types.rights.filter(wizard__url_name='person_search'
+ ).count()):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q)
+ query = query & extra
+ if orga_type:
+ try:
+ typs = [int(tp) for tp in orga_type.split('_') if tp]
+ typ = models.OrganizationType.objects.filter(pk__in=typs).all()
+ query = query & Q(organization_type__in=typ)
+ except (ValueError, ObjectDoesNotExist):
+ pass
+ limit = 15
+ organizations = models.Organization.objects.filter(query)[:limit]
+ data = json.dumps([{'id':org.pk, 'value':unicode(org)}
+ for org in organizations])
+ return HttpResponse(data, mimetype='text/plain')
+
+show_contextrecord = show_item(models.ContextRecord, 'contextrecord')
+get_contextrecord = get_item(models.ContextRecord,
+ 'get_contextrecord', 'contextrecord',
+ extra_request_keys={'parcel__town':'parcel__town__pk',
+ 'operation__year':'operation__year__contains',
+ 'datings__period':'datings__period__pk'},)
+get_contextrecordsource = get_item(models.ContextRecordSource,
+ 'get_contextrecordsource', 'contextrecordsource',
+ extra_request_keys={
+ 'context_record__parcel__town':'context_record__parcel__town__pk',
+ 'context_record__operation__year':'context_record__operation__year',
+ 'context_record__datings__period':'context_record__datings__period__pk',
+ 'context_record__unit':'context_record__unit__pk',
+ })
+get_archaeologicalitem = get_item(models.Item,
+ 'get_archaeologicalitem', 'item',
+ bool_fields = ['base_items__is_isolated'],
+ base_request={'downstream_treatment__isnull':True},
+ extra_request_keys={
+'base_items__context_record__parcel__town':
+ 'base_items__context_record__parcel__town',
+'base_items__context_record__operation__year':
+ 'base_items__context_record__operation__year__contains',
+'base_items__context_record__operation__code_patriarche':
+ 'base_items__context_record__operation__code_patriarche',
+'dating__period':'dating__period__pk',
+'base_items__item__description':'base_items__item__description__icontains',
+'base_items__is_isolated':'base_items__is_isolated'})
+get_itemsource = get_item(models.ItemSource,
+ 'get_itemsource', 'itemsource',
+ extra_request_keys={
+'item__context_record__operation__year':'item__context_record__operation__year',
+'item__dating__period':'item__dating__period__pk',
+'item__description':'item__description__icontains',
+ })
+get_container = get_item(models.Container,
+ 'get_container', 'container',
+ extra_request_keys={
+'location':'location__pk',
+'container_type':'container_type__pk',
+'reference':'reference__icontains',
+ })
+
+def autocomplete_warehouse(request):
+ if not request.user.has_perm('ishtar_common.view_warehouse', models.Warehouse)\
+ and not request.user.has_perm('ishtar_common.view_own_warehouse',
+ models.Warehouse) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q) | \
+ Q(warehouse_type__label__icontains=q)
+ query = query & extra
+ limit = 15
+ warehouses = models.Warehouse.objects.filter(query)[:limit]
+ data = json.dumps([{'id':warehouse.pk, 'value':unicode(warehouse)}
+ for warehouse in warehouses])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_author(request):
+ if not request.user.has_perm('ishtar_common.view_author', models.Author)\
+ and not request.user.has_perm('ishtar_common.view_own_author',
+ models.Warehouse) :
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(person__name__icontains=q) | \
+ Q(person__surname__icontains=q) | \
+ Q(person__email__icontains=q) | \
+ Q(author_type__label__icontains=q)
+ query = query & extra
+ limit = 15
+ authors = models.Author.objects.filter(query)[:limit]
+ data = json.dumps([{'id':author.pk, 'value':unicode(author)}
+ for author in authors])
+ return HttpResponse(data, mimetype='text/plain')
+
+def autocomplete_container(request):
+ if not request.user.has_perm('ishtar_common.view_warehouse',
+ models.Warehouse)\
+ and not request.user.has_perm('ishtar_common.view_own_warehouse',
+ models.Warehouse):
+ return HttpResponse(mimetype='text/plain')
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(container_type__label__icontains=q) | \
+ Q(container_type__reference__icontains=q) | \
+ Q(reference__icontains=q) | \
+ Q(location__name=q) | \
+ Q(location__town=q)
+ query = query & extra
+ limit = 15
+ containers = models.Container.objects.filter(query)[:limit]
+ data = json.dumps([{'id':container.pk, 'value':unicode(container)}
+ for container in containers])
+ return HttpResponse(data, mimetype='text/plain')
+
+def new_item(model):
+ def func(request, parent_name):
+ model_name = model._meta.object_name
+ if not check_permission(request, 'add_'+model_name.lower()):
+ not_permitted_msg = ugettext(u"Operation not permitted.")
+ return HttpResponse(not_permitted_msg)
+ frm = getattr(ishtar_forms, model_name + 'Form')
+ dct = {'title':unicode(_(u'New %s' % model_name.lower()))}
+ if request.method == 'POST':
+ dct['form'] = frm(request.POST)
+ if dct['form'].is_valid():
+ new_item = dct['form'].save(request.user)
+ dct['new_item_label'] = unicode(new_item)
+ dct['new_item_pk'] = new_item.pk
+ dct['parent_name'] = parent_name
+ dct['parent_pk'] = parent_name
+ if dct['parent_pk'] and '_select_' in dct['parent_pk']:
+ parents = dct['parent_pk'].split('_')
+ dct['parent_pk'] = "_".join([parents[0]] + parents[2:])
+ return render_to_response('window.html', dct,
+ context_instance=RequestContext(request))
+ else:
+ dct['form'] = frm()
+ return render_to_response('window.html', dct,
+ context_instance=RequestContext(request))
+ return func
+
+new_warehouse = new_item(models.Warehouse)
+new_person = new_item(models.Person)
+new_organization = new_item(models.Organization)
+new_author = new_item(models.Author)
+new_container = new_item(models.Container)
+
+def action(request, action_slug, obj_id=None, *args, **kwargs):
+ """
+ Action management
+ """
+ if not check_permission(request, action_slug, obj_id):
+ not_permitted_msg = ugettext(u"Operation not permitted.")
+ return HttpResponse(not_permitted_msg)
+ request.session['CURRENT_ACTION'] = action_slug
+ associated_wizard = action_slug + '_wizard'
+ dct = {}
+ globals_dct = globals()
+ if action_slug in globals_dct:
+ return globals_dct[action_slug](request, dct, obj_id, *args, **kwargs)
+ elif hasattr(ishtar_forms, action_slug + "_wizard"):
+ return getattr(ishtar_forms, action_slug+"_wizard")(request, *args,
+ **kwargs)
+ return render_to_response('index.html', dct,
+ context_instance=RequestContext(request))
+
+def dashboard_main(request, dct, obj_id=None, *args, **kwargs):
+ """
+ Main dashboard
+ """
+ dct = {'items':[
+ (_(u"Archaeological files"), models.Dashboard(models.File)),
+ (_(u"Operations"), models.Dashboard(models.Operation)),
+ (_(u"Context records"), models.Dashboard(models.ContextRecord)),
+ (_(u"Archaeological items"), models.Dashboard(models.Item)),
+ ],
+ 'ishtar_users':models.UserDashboard()}
+ return render_to_response('dashboard_main.html', dct,
+ context_instance=RequestContext(request))
+
+def dashboard_file(request, dct, obj_id=None, *args, **kwargs):
+ """
+ Main dashboard
+ """
+ dct = {'dashboard': models.FileDashboard()}
+ return render_to_response('dashboard_file.html', dct,
+ context_instance=RequestContext(request))
+
+def dashboard_operation(request, dct, obj_id=None, *args, **kwargs):
+ """
+ Operation dashboard
+ """
+ dct = {'dashboard': models.OperationDashboard()}
+ return render_to_response('dashboard_operation.html', dct,
+ context_instance=RequestContext(request))