# -*- coding: utf-8 -*- # File upload related imports import os from django.core.files.storage import default_storage from django.core.files.base import ContentFile from django.conf import settings from django.views.generic import TemplateView, FormView, CreateView, UpdateView from django.views.generic.detail import SingleObjectMixin from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.contrib.auth.views import login, logout from django.core.urlresolvers import reverse from frontend.forms import NewProjectForm, FileUploadForm, PersonMapForm from frontend.forms import PresentationForm, PresentationEventForm, PresentationPersonForm from frontend.models import Presentation, PresentationEvent, PresentationPerson from frontend.formsets import PresentationEventInlineFormset, PresentationPersonInlineFormset from fancy_formsets.views import FormsetsView from frontend.models import Project FRONTEND_PAGE_NAME = u'Citavi Mapper' from service.Mapper import person_mapper # Login wrapper functions def login_wrap(*args, **kwargs): """ Wrapper function for login page. """ kwargs[u'extra_context'] = { u'page': { u'name': FRONTEND_PAGE_NAME, u'title': 'Login' } } return login(*args, **kwargs) def logout_wrap(*args, **kwargs): """ Wrapper function for logout page. """ kwargs[u'extra_context'] = { u'page': { u'name': FRONTEND_PAGE_NAME, u'title': 'Logout' } } return logout(*args, **kwargs) # My base classes for views class MyViewMixin(object): """ Basic view mixin to add global variables to all templates. """ template_name = u'base.html' page_name = FRONTEND_PAGE_NAME page_title = u'BASE' def get_page_data(self): return { u'name': self.page_name, u'title': self.page_title } def get_context_data(self, **kwargs): kwargs[u'page'] = self.get_page_data() return super(MyViewMixin, self).get_context_data(**kwargs) class MyTemplateView(MyViewMixin, TemplateView): pass class MyFormView(MyViewMixin, FormView): pass class MyCreateView(MyViewMixin, CreateView): pass class MyUpdateView(MyViewMixin, UpdateView): pass # Mixin to protect pages class LoggedInMixin(object): """ Mixin to force a valid login for protected pages. """ @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(LoggedInMixin, self).dispatch(*args, **kwargs) # My protected view classes class ProtectedTemplateView(LoggedInMixin, MyTemplateView): pass class ProtectedFormView(LoggedInMixin, MyFormView): pass class ProtectedFormSetView(LoggedInMixin, MyTemplateView): pass class ProtectedCreateView(LoggedInMixin, MyCreateView): pass class ProtectedUpdateView(LoggedInMixin, MyUpdateView): pass # Actual Views class IndexView(ProtectedTemplateView): template_name = u'index.html' page_title = u'Index' class HelloView(ProtectedTemplateView): template_name = u'hello.html' page_title = u'Hello' success_url = u'/hello' def __init__(self, *args, **kwargs): super(HelloView, self).__init__(*args, **kwargs) def get_context_data(self, **kwargs): """ Add form + formsets to template context. """ kwargs[u'presentation_form'] = PresentationForm(); kwargs[u'person_formset'] = PresentationPersonInlineFormset(prefix='person'); kwargs[u'event_formset'] = PresentationEventInlineFormset(prefix='event'); kwargs[u'person_form'] = PresentationPersonForm(); kwargs[u'event_form'] = PresentationEventForm(); return super(HelloView, self).get_context_data(**kwargs) class ProjectView(ProtectedFormView): template_name = u'projects.html' page_title = u'Projects' form_class = NewProjectForm success_url = u'/project' def get_context_data(self, **kwargs): kwargs[u'projects'] = Project.objects.order_by(u'id') return super(ProjectView, self).get_context_data(**kwargs) def form_valid(self, form): form.save() return super(ProjectView, self).form_valid(form) class ProjectUpdateView(ProtectedFormView, SingleObjectMixin): template_name = u'project/update.html' page_title = u'Update project' form_class = FileUploadForm success_url = u'/project/' def get(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] self.object = Project.objects.get(pk=self.project_id) return super(ProjectUpdateView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] self.object = Project.objects.get(pk=self.project_id) self.success_url = self.success_url + self.project_id + u'/update' return super(ProjectUpdateView, self).post(request, *args, **kwargs) def form_valid(self, form, *args, **kwargs): """ This form_valid handles the file upload. """ original_file = form.files[u'file'] original_filename = unicode(original_file) original_contentfile = ContentFile(original_file.read()) """ Put file into temporary folder for analysis """ target_filename = u'tmp/project_' + unicode(self.project_id) + u'.ctt4' relative_path = default_storage.save(target_filename, original_contentfile) temp_sqlite = os.path.join(settings.MEDIA_ROOT, relative_path) """ Test if SQLite is a valid citavi project. """ from service import Citavi citavi_project = Citavi.Project(temp_sqlite) citavi_project.open() citavi_project_valid = citavi_project.is_valid() """ Free temporary ressources. """ del citavi_project default_storage.delete(temp_sqlite) if citavi_project_valid == False: """ TODO: Put up an error message or something. """ pass else: target_filename = u'citavi/project_' + unicode(self.project_id) + u'.ctt4' """ Remove eventually pre-existing citavi file. """ if default_storage.exists(target_filename): default_storage.delete(target_filename) """ Actually store file in citavi folder """ relative_path = default_storage.save(target_filename, original_contentfile) sqlite_path = os.path.join(settings.MEDIA_ROOT, relative_path) """ Store new original filename in Project """ project = Project.objects.get(id=self.project_id) project.associated_filename = original_filename project.save() """ Refresh identities from citavi project. """ # TODO # citavi_project = Citavi.Project(sqlite_path) # citavi_project.open() # del citavi_project return super(ProjectUpdateView, self).form_valid(form) class ProjectPersonView(ProtectedFormView, SingleObjectMixin): template_name = u'project/view-person.html' page_title = u'Person List View' form_class = FileUploadForm success_url = u'/project/' def get_context_data(self, **kwargs): project = self.object kwargs[u'unmapped_persons'] = person_mapper.get_unmapped_identities(project) kwargs[u'mapped_persons'] = person_mapper.get_mapped_identities(project) return super(ProjectPersonView, self).get_context_data(**kwargs) def get(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] self.object = Project.objects.get(pk=self.project_id) return super(ProjectPersonView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] # self.success_url = self.success_url + self.project_id + '/update' return super(ProjectPersonView, self).post(request, *args, **kwargs) def form_valid(self, form, *args, **kwargs): return super(ProjectPersonView, self).form_valid(form) class ProjectMapPersonView(ProtectedFormView, SingleObjectMixin): """ View containing the person mapping 'wizard'. """ template_name = u'project/map-person.html' page_title = u'Person Mapping' form_class = PersonMapForm success_url = u'/project' _unmapped_persons = None def _refresh_unmapped(self): """ Refresh data about unmapped persons. """ self._unmapped_persons = person_mapper.get_unmapped_identities(self.object) def _is_unmapped(self, person_uuid): """ Returns True if the given person is not mapped yet. """ if not self._unmapped_persons: self._refresh_unmapped() return person_uuid in self._unmapped_persons def get_context_data(self, **kwargs): project = self.object kwargs[u'person'] = person_mapper.get_person_by_uuid(project, self.person_uuid) return super(ProjectMapPersonView, self).get_context_data(**kwargs) def get_success_url(self): """ TODO: Make this work! - Update success uri to next unmapped person. """ last_uuid = unicode(self.person_uuid) self._refresh_unmapped() unmapped = self._unmapped_persons.items() first_person_uuid = unmapped[0][0] next_uuid = first_person_uuid unmapped.reverse() while True: try: current_person = unmapped.pop() if current_person[1].ID == last_uuid: next_uuid = unmapped.pop()[0] break except IndexError: break kwargs = { u"project_id": self.object.id, u"person_uuid": next_uuid } return reverse('frontend-project-map-person', kwargs=kwargs) def get(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] self.person_uuid = kwargs[u'person_uuid'] self.object = Project.objects.get(pk=self.project_id) if self._is_unmapped(self.person_uuid) == False: raise Exception("Sorry, this person was already mapped. Try deleting the existing mapping and move on. TODO: Make this more beautiful.") self._refresh_unmapped() return super(ProjectMapPersonView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.project_id = kwargs[u'project_id'] self.person_uuid = kwargs[u'person_uuid'] self.object = Project.objects.get(pk=self.project_id) if self._is_unmapped(self.person_uuid) == False: raise Exception("Sorry, this person was already mapped. Try deleting the existing mapping and move on. TODO: Make this more beautiful.") self._refresh_unmapped() return super(ProjectMapPersonView, self).post(request, *args, **kwargs) def form_valid(self, form, *args, **kwargs): person = person_mapper.get_person_by_uuid(self.object, self.person_uuid) if form.data.get(u'skip', False): """ Nothing to do here, just go on ... """ pass elif form.data.get(u'save-continue', False): # TODO: do mapping according to parameters, override success_url to point to next person! if form.cleaned_data[u'action'] == u'new': person_mapper.create_new_identity(self.object, person) elif form.cleaned_data[u'action'] == u'existing': global_identity = form.cleaned_data[u'global_identity'] person_mapper.map_identity_to_existing(global_identity, self.object, person.ID, form.cleaned_data[u'preferred_identity']) return super(ProjectMapPersonView, self).form_valid(form)