# -*- 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 PresentationPersonFormSet, PresentationForm from frontend.models import Presentation from extra_views import FormSetView 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 MyFormSetView(MyViewMixin, FormSetView): 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, MyFormSetView): 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' # TODO: Make this work! class HelloView(ProtectedCreateView): template_name = u'hello.html' page_title = u'Hello' model = Presentation form_class = PresentationForm success_url = u'/hello' def get(self, request, *args, **kwargs): """ Handles GET requests and instantiates blank versions of the form and its inline formsets. """ self.object = None form_class = self.get_form_class() form = self.get_form(form_class) presentationperson_form = PresentationPersonFormSet() return self.render_to_response( self.get_context_data(form=form, presentationperson_form=presentationperson_form)) def form_valid(self, form): pass 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)