citavi_mapper/frontend/views.py

390 lines
15 KiB
Python

# -*- 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, PresentationSubmissionForm
from frontend.forms import PresentationEventFormHelper, PresentationPersonFormHelper
from frontend.models import Presentation, PresentationEvent, PresentationPerson
from frontend.formsets import PresentationEventInlineFormset, PresentationPersonInlineFormset
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 PresentationAddView(ProtectedTemplateView):
template_name = u'presentation-add.html'
page_title = u'Vortrag anlegen'
success_url = u'presentation-add'
""" These guys are passed through using get_context_data(). """
presentation_form = None
submission_form = None
person_formset = None
event_formset = None
person_formhelper = PresentationPersonFormHelper
event_formhelper = PresentationEventFormHelper
success = False
error = False
def __init__(self, *args, **kwargs):
super(PresentationAddView, self).__init__(*args, **kwargs)
def get(self, request, *args, **kwargs):
""" Create empty form + formsets by default. """
self.create_blank_forms()
return super(PresentationAddView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
""" Just use the same template and go on by default. Also, trigger handling the forms. """
self.handle_forms(request, *args, **kwargs)
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def create_blank_forms(self):
""" Create blank form + formsets for the user to fill. """
self.presentation_form = PresentationForm(prefix='presentation')
self.submission_form = PresentationSubmissionForm(prefix='submission')
self.person_formset = PresentationPersonInlineFormset(prefix='person')
self.event_formset = PresentationEventInlineFormset(prefix='event')
def get_context_data(self, **kwargs):
""" Add form + formsets to template context. """
kwargs[u'presentation_form'] = self.presentation_form
kwargs[u'submission_form'] = self.submission_form
kwargs[u'person_formset'] = self.person_formset
kwargs[u'event_formset'] = self.event_formset
kwargs[u'person_formhelper'] = self.person_formhelper
kwargs[u'event_formhelper'] = self.event_formhelper
kwargs[u'success'] = self.success
kwargs[u'error'] = self.error
return super(PresentationAddView, self).get_context_data(**kwargs)
def handle_forms(self, request, *args, **kwargs):
""" Once a POST came in, try and validate the forms, saving the data. """
presentation = None
presentation_ok = False
person_ok = False
event_ok = False
submission_ok = False
""" Create all the forms and formsets based on request params to do validation. """
self.presentation_form = PresentationForm(request.POST, request.FILES, prefix='presentation')
self.person_formset = PresentationPersonInlineFormset(request.POST, request.FILES, prefix='person')
self.event_formset = PresentationEventInlineFormset(request.POST, request.FILES, prefix='event')
self.submission_form = PresentationSubmissionForm(request.POST, request.FILES, prefix='submission')
""" Check all for validity """
presentation_ok = self.presentation_form.is_valid()
submission_ok = self.submission_form.is_valid()
person_ok = self.person_formset.is_valid()
""" Only other event type needs a valid event formset. """
print(presentation_ok, person_ok, event_ok, submission_ok)
if presentation_ok and self.presentation_form.cleaned_data[u'type'] == 'other':
event_ok = self.event_formset.is_valid()
else:
event_ok = True
""" Only if everything is alright, store it."""
if presentation_ok == True and person_ok == True and event_ok == True and submission_ok == True:
presentation = self.presentation_form.save()
""" Now we can create the other ones based on the saved presentation to save them, too. """
submission_form = PresentationSubmissionForm(request.POST, request.FILES, instance=presentation, prefix='submission')
submission_form.save()
person_formset = PresentationPersonInlineFormset(request.POST, request.FILES, instance=presentation, prefix='person')
person_formset.save()
""" Conditionally save the events depending on whether they are needed. """
if presentation.type == 'other':
event_formset = PresentationEventInlineFormset(request.POST, request.FILES, instance=presentation, prefix='event')
event_formset.save()
""" we're done. """
self.success = True
self.create_blank_forms()
else:
self.error = True
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)