citavi_mapper/frontend/views.py

390 lines
15 KiB
Python
Raw Permalink Normal View History

# -*- 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
2014-11-26 11:56:18 +01:00
from frontend.forms import NewProjectForm, FileUploadForm, PersonMapForm
2014-12-11 16:30:54 +01:00
from frontend.forms import PresentationForm, PresentationSubmissionForm
2014-12-10 16:28:21 +01:00
from frontend.forms import PresentationEventFormHelper, PresentationPersonFormHelper
from frontend.models import Presentation, PresentationEvent, PresentationPerson
from frontend.formsets import PresentationEventInlineFormset, PresentationPersonInlineFormset
from frontend.models import Project
2014-09-29 15:01:35 +02:00
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. """
2014-09-29 15:01:35 +02:00
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. """
2014-09-29 15:01:35 +02:00
kwargs[u'extra_context'] = {
u'page': {
u'name': FRONTEND_PAGE_NAME,
u'title': 'Logout'
2014-09-19 11:05:14 +02:00
}
}
return logout(*args, **kwargs)
# My base classes for views
class MyViewMixin(object):
""" Basic view mixin to add global variables to all templates. """
2014-09-29 15:01:35 +02:00
template_name = u'base.html'
page_name = FRONTEND_PAGE_NAME
2014-09-29 15:01:35 +02:00
page_title = u'BASE'
2014-09-19 11:05:14 +02:00
def get_page_data(self):
return {
u'name': self.page_name,
u'title': self.page_title
}
2014-09-19 11:05:14 +02:00
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
2014-09-19 11:05:14 +02:00
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):
2014-11-26 11:56:18 +01:00
pass
class ProtectedCreateView(LoggedInMixin, MyCreateView):
pass
class ProtectedUpdateView(LoggedInMixin, MyUpdateView):
pass
# Actual Views
class IndexView(ProtectedTemplateView):
2014-09-29 15:01:35 +02:00
template_name = u'index.html'
page_title = u'Index'
2014-11-26 10:20:46 +01:00
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
2014-11-26 10:20:46 +01:00
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
2014-11-26 11:56:18 +01:00
class ProjectView(ProtectedFormView):
2014-09-29 15:01:35 +02:00
template_name = u'projects.html'
page_title = u'Projects'
2014-08-07 14:47:59 +02:00
form_class = NewProjectForm
2014-09-29 15:01:35 +02:00
success_url = u'/project'
2014-09-19 11:05:14 +02:00
def get_context_data(self, **kwargs):
2014-09-29 15:01:35 +02:00
kwargs[u'projects'] = Project.objects.order_by(u'id')
return super(ProjectView, self).get_context_data(**kwargs)
2014-09-19 11:05:14 +02:00
def form_valid(self, form):
form.save()
return super(ProjectView, self).form_valid(form)
2014-09-19 11:05:14 +02:00
class ProjectUpdateView(ProtectedFormView, SingleObjectMixin):
2014-09-29 15:01:35 +02:00
template_name = u'project/update.html'
page_title = u'Update project'
form_class = FileUploadForm
2014-09-29 15:01:35 +02:00
success_url = u'/project/'
2014-09-19 11:05:14 +02:00
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']
2014-09-29 15:01:35 +02:00
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)
2014-09-19 11:05:14 +02:00
def form_valid(self, form, *args, **kwargs):
""" This form_valid handles the file upload. """
original_file = form.files[u'file']
2014-09-29 15:01:35 +02:00
original_filename = unicode(original_file)
original_contentfile = ContentFile(original_file.read())
""" Put file into temporary folder for analysis """
2014-09-29 15:01:35 +02:00
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)
2014-09-19 11:05:14 +02:00
""" Test if SQLite is a valid citavi project. """
from service import Citavi
citavi_project = Citavi.Project(temp_sqlite)
2014-09-19 11:05:14 +02:00
citavi_project.open()
citavi_project_valid = citavi_project.is_valid()
""" Free temporary ressources. """
del citavi_project
default_storage.delete(temp_sqlite)
2014-09-19 11:05:14 +02:00
if citavi_project_valid == False:
""" TODO: Put up an error message or something. """
pass
else:
2014-09-29 15:01:35 +02:00
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()
2014-09-19 11:05:14 +02:00
""" Refresh identities from citavi project. """
# TODO
2014-09-19 11:05:14 +02:00
# citavi_project = Citavi.Project(sqlite_path)
# citavi_project.open()
# del citavi_project
return super(ProjectUpdateView, self).form_valid(form)
class ProjectPersonView(ProtectedFormView, SingleObjectMixin):
2014-09-29 15:01:35 +02:00
template_name = u'project/view-person.html'
page_title = u'Person List View'
form_class = FileUploadForm
2014-09-29 15:01:35 +02:00
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'. """
2014-09-29 15:01:35 +02:00
template_name = u'project/map-person.html'
page_title = u'Person Mapping'
form_class = PersonMapForm
2014-09-29 15:01:35 +02:00
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)