general: reorganize models in a simpler way

A source object (e.g. Mail) may now be attached to several Association objects,
each one has a formdef_reference attribute (at first), and a formdata_id
attribute (once submitted to wcs).
This commit is contained in:
Frédéric Péters 2015-10-09 14:01:59 +02:00
parent d9100fabac
commit 30e1c9cc43
18 changed files with 261 additions and 121 deletions

View File

@ -42,17 +42,21 @@ class ContactsZone(TemplateView):
def get_context_data(self, **kwargs):
context = super(ContactsZone, self).get_context_data(**kwargs)
if 'source_pk' in self.request.GET:
try:
association = Association.objects.get(
source_type_id=self.request.GET['source_type'],
source_pk=self.request.GET['source_pk'])
except Association.DoesNotExist:
pass
else:
context['contact_user_id'] = association.user_id
source_class = ContentType.objects.get(
id=self.request.GET['source_type']).model_class()
source_object = source_class.objects.get(id=self.request.GET['source_pk'])
context['contact_user_id'] = source_object.contact_id
return context
def post(self, request, *args, **kwargs):
if 'user_id' in request.POST:
source_class = ContentType.objects.get(
id=self.request.POST['source_type']).model_class()
source_object = source_class.objects.get(id=self.request.POST['source_pk'])
source_object.contact_id = request.POST['user_id']
source_object.save()
return HttpResponse('ok')
zone = csrf_exempt(ContactsZone.as_view())

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0001_initial'),
('alfortville', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='inbox',
name='qualif',
),
migrations.AddField(
model_name='inbox',
name='source_pk',
field=models.PositiveIntegerField(null=True),
preserve_default=True,
),
migrations.AddField(
model_name='inbox',
name='source_type',
field=models.ForeignKey(to='contenttypes.ContentType', null=True),
preserve_default=True,
),
]

View File

@ -16,7 +16,7 @@
from django.db import models
from welco.qualif.models import Association
from django.contrib.contenttypes.models import ContentType
class Inbox(models.Model):
@ -26,5 +26,6 @@ class Inbox(models.Model):
role_slug = models.CharField(max_length=50)
subtype = models.PositiveSmallIntegerField(
choices=((INFO, 'info'), (AVIS, 'avis')), default=INFO)
qualif = models.ForeignKey(Association)
source_type = models.ForeignKey(ContentType, null=True)
source_pk = models.PositiveIntegerField(null=True)
done = models.BooleanField(default=False)

View File

@ -17,6 +17,7 @@
import json
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext
@ -24,7 +25,8 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, DetailView
from .models import Inbox
from welco.qualif.models import FormdefReference, Association
from welco.sources.mail.models import Mail
from welco.utils import get_wcs_data
class Dgs(TemplateView):
@ -69,14 +71,14 @@ dgs = login_required(Dgs.as_view())
class Copies(DetailView):
model = Association
model = Mail
template_name = 'alfortville/copies.html'
def get_context_data(self, **kwargs):
context = super(Copies, self).get_context_data(**kwargs)
checked_info = {}
checked_avis = {}
for inbox in Inbox.objects.filter(qualif=self.object):
for inbox in Inbox.objects.filter(source_pk=self.object.id):
if inbox.subtype == inbox.INFO:
checked_info[inbox.role_slug] = True
else:
@ -92,10 +94,13 @@ class Copies(DetailView):
avis_list = request.POST.getlist('avis')
Inbox.objects.filter(subtype=Inbox.INFO).exclude(role_slug__in=info_list).delete()
Inbox.objects.filter(subtype=Inbox.AVIS).exclude(role_slug__in=avis_list).delete()
mail_content_type = ContentType.objects.get_for_model(Mail)
for info in info_list:
Inbox.objects.get_or_create(qualif_id=kwargs['pk'], role_slug=info, subtype=Inbox.INFO)
Inbox.objects.get_or_create(source_type=mail_content_type,
source_pk=kwargs['pk'], role_slug=info, subtype=Inbox.INFO)
for avis in avis_list:
Inbox.objects.get_or_create(qualif_id=kwargs['pk'], role_slug=avis, subtype=Inbox.AVIS)
Inbox.objects.get_or_create(source_type=mail_content_type,
source_pk=kwargs['pk'], role_slug=avis, subtype=Inbox.AVIS)
return HttpResponseRedirect('.')
copies = login_required(Copies.as_view())
@ -107,8 +112,8 @@ def copies_ajax(request, *args, **kwargs):
roles_by_slug[role['slug']] = role
avis = []
info = []
qualif = Association.objects.get(id=kwargs.get('pk'))
for inbox in Inbox.objects.filter(qualif=qualif):
mail = Mail.objects.get(id=kwargs.get('pk'))
for inbox in Inbox.objects.filter(source_pk=kwargs.get('pk')):
if inbox.subtype == inbox.AVIS:
avis.append(roles_by_slug[inbox.role_slug]['text'])
else:

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('qualif', '0004_association_user_id'),
]
operations = [
migrations.RemoveField(
model_name='association',
name='formdatas',
),
migrations.DeleteModel(
name='FormdataReference',
),
migrations.RemoveField(
model_name='association',
name='formdefs',
),
migrations.DeleteModel(
name='FormdefReference',
),
migrations.AddField(
model_name='association',
name='formdata_id',
field=models.CharField(max_length=250, null=True),
preserve_default=True,
),
migrations.AddField(
model_name='association',
name='formdef_reference',
field=models.CharField(max_length=250, null=True),
preserve_default=True,
),
]

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('qualif', '0005_auto_20151009_1110'),
]
operations = [
migrations.RemoveField(
model_name='association',
name='user_id',
),
]

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('qualif', '0006_remove_association_user_id'),
]
operations = [
migrations.RemoveField(
model_name='association',
name='triaged',
),
]

View File

@ -24,57 +24,30 @@ from django.utils.translation import ugettext_lazy as _
from welco.utils import get_wcs_formdef_details, push_wcs_formdata, get_wcs_services
class FormdefReference(models.Model):
reference = models.CharField(max_length=250)
@property
def name(self):
return get_wcs_formdef_details(self.reference).get('title')
class FormdataReference(models.Model):
reference = models.CharField(max_length=250)
def formdef(self):
return FormdefReference.objects.get(
reference=self.reference.rsplit(':', 1)[0])
@property
def url(self):
site, formdef, formdata = self.reference.split(':')
wcs_site = get_wcs_services().get(site)
return '%s%s/%s' % (wcs_site.get('url'), formdef, formdata)
class Association(models.Model):
source_type = models.ForeignKey(ContentType)
source_pk = models.PositiveIntegerField()
source = generic.GenericForeignKey('source_type', 'source_pk')
triaged = models.BooleanField(default=False)
comments = models.TextField(blank=True, verbose_name=_('Comments'))
user_id = models.CharField(max_length=50, null=True)
formdefs = models.ManyToManyField(FormdefReference)
formdatas = models.ManyToManyField(FormdataReference)
formdef_reference = models.CharField(max_length=250, null=True)
formdata_id = models.CharField(max_length=250, null=True)
def push(self, request, context):
# push validated request to wcs
if context:
context = context.copy()
context['user_id'] = self.user_id
for formdef in self.formdefs.all():
formdata_id = push_wcs_formdata(request, formdef.reference, context)
reference = '%s:%s' % (formdef.reference, formdata_id)
formdata_reference = FormdataReference(reference=reference)
formdata_reference.save()
self.formdatas.add(formdata_reference)
self.save()
if self.formdef_reference:
self.formdata_id = push_wcs_formdata(request, self.formdef_reference, context)
self.save()
@receiver(post_save)
def association_triaged(sender, instance, **kwargs):
if not sender is Association:
return
if instance.source.triaged != instance.triaged:
instance.source.triaged = instance.triaged
instance.source.save()
@property
def formdef_name(self):
return get_wcs_formdef_details(self.formdef_reference).get('title')
@property
def formdata_url(self):
site, formdef = self.formdef_reference.split(':')
wcs_site = get_wcs_services().get(site)
return '%s%s/%s' % (wcs_site.get('url'), formdef, self.formdata_id)

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('mail', '0002_auto_20150831_1538'),
]
operations = [
migrations.RemoveField(
model_name='mail',
name='triaged',
),
migrations.AddField(
model_name='mail',
name='status',
field=models.CharField(max_length=50, verbose_name='Status', blank=True),
preserve_default=True,
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('mail', '0003_auto_20151009_1144'),
]
operations = [
migrations.AddField(
model_name='mail',
name='contact_id',
field=models.CharField(max_length=50, null=True),
preserve_default=True,
),
]

View File

@ -28,10 +28,13 @@ class Mail(models.Model):
verbose_name = _('Mail')
content = models.FileField(_('Content'))
triaged = models.BooleanField(default=False)
post_date = models.DateField(_('Post Date'), null=True)
registered_mail = models.BooleanField(_('Registered Mail'), default=False)
# common to all source types:
status = models.CharField(_('Status'), blank=True, max_length=50)
contact_id = models.CharField(max_length=50, null=True)
creation_timestamp = models.DateTimeField(auto_now_add=True)
last_update_timestamp = models.DateTimeField(auto_now=True)

View File

@ -1,14 +1,23 @@
{% load i18n %}
<h2>{% trans 'Mails' %}</h2>
<div data-source-type="{{ source_type.id }}">
<div class="mails">
<div id="source-sidebar" class="mails">
<ul>
{% for mail in mails %}
<li data-source-pk="{{ mail.id }}" data-pdf-href="{{ mail.content.url }}">{{ mail.creation_timestamp }}</li>
<li data-source-pk="{{ mail.id }}"
data-pdf-href="{{ mail.content.url }}"
data-post-date="{{ mail.post_date|date:"d/m/Y" }}"
>{{ mail.creation_timestamp }}</li>
{% endfor %}
</ul>
</div>
<iframe id="pdf-viewer" src="{% url 'mail-viewer' %}">
</iframe>
<div id="source-mainarea">
<form>
{{form.as_p}}
<button data-action-url="{{source_form_url}}" class="save"></button>
</form>
<iframe id="pdf-viewer" src="{% url 'mail-viewer' %}">
</iframe>
</div>
</div>

View File

@ -57,8 +57,11 @@ class Home(object):
def render(self):
context = RequestContext(self.request)
context['mails'] = Mail.objects.filter(triaged=False)
context['mails'] = Mail.objects.exclude(status__startswith='done-'
).order_by('creation_timestamp')
context['source_type'] = ContentType.objects.get_for_model(Mail)
context['form'] = MailQualificationForm()
context['source_form_url'] = Mail.get_qualification_form_submit_url()
tmpl = template.loader.get_template('welco/mail_home.html')
return tmpl.render(context)

View File

@ -76,13 +76,21 @@ div#content .source-mail .cell.document > div {
height: calc(100% - 3em);
}
div#content #source-mainarea {
width: 100%;
}
div#content .cell.document iframe {
width: 100%;
flex-grow: 2;
}
div#content .cell.document ul {
div#content #source-sidebar {
min-width: 20em;
display: none; /* only displayed when on top */
}
div#content .cell.document ul {
list-style: none;
padding: 0;
margin: 0;
@ -105,7 +113,7 @@ div#content .cell.document ul li:hover {
background: #f0f0f0;
}
div#content .cell.document.top ul {
div#content .top #source-sidebar {
display: block;
}
@ -351,3 +359,13 @@ td.datetime {
padding-top: 1em;
display: inline-block;
}
#source-mainarea form {
float: right;
padding-right: 1em;
}
#source-mainarea form p,
#source-mainarea form p label {
display: inline-block;
}

View File

@ -12,6 +12,8 @@ $(function() {
$('.mails ul li[data-pdf-href]').on('click', function() {
$(this).parent().find('li').removeClass('active');
$(this).addClass('active');
$('#id_post_date').val($(this).data('post-date'));
//var registered_mail = $('#id_registered_mail').prop('checked');
var source_type = $('div.source div[data-source-type]').data('source-type');
var source_pk = $('div.source .active[data-source-pk]').data('source-pk');
$('.cell[data-zone-url]').each(function(idx, zone) {
@ -35,7 +37,7 @@ $(function() {
var source_type = $('div.source div[data-source-type]').data('source-type');
var source_pk = $('div.source .active[data-source-pk]').data('source-pk');
var selected_user_id = $('#current-selected-user').val();
$.ajax({url: $('.cell.qualif').data('zone-url'),
$.ajax({url: $('.cell.contacts').data('zone-url'),
data: {user_id: selected_user_id,
source_type: source_type,
source_pk: source_pk},
@ -43,8 +45,6 @@ $(function() {
dataType: 'html',
success: function(data) {
$('div.contacts h3').effect('highlight');
$('.cell.qualif > div').replaceWith(data);
$('.qualif').find('select').select2();
},
error: function(error) { console.log(':/', error); }
});
@ -89,7 +89,7 @@ $(function() {
return false;
});
$('.qualif').delegate('button.save', 'click', function() {
$('.document').delegate('button.save', 'click', function() {
var post_date = $('#id_post_date').val();
var registered_mail = $('#id_registered_mail').prop('checked');
var source_type = $('div.source div[data-source-type]').data('source-type');
@ -102,9 +102,8 @@ $(function() {
method: 'POST',
dataType: 'html',
success: function(data) {
$('.cell.qualif > div').replaceWith(data);
$('div.qualif-source').effect('highlight');
$('.qualif').find('select').select2();
$('div.source .active').data('post-date', post_date);
$('#source-mainarea form').effect('highlight');
},
error: function(error) { console.log(':(', error); }
});

View File

@ -27,7 +27,7 @@ $('div.cell h2').on('click', function() {
$('div.cell').removeClass('top');
$(this).parents('div.cell').addClass('top');
});
$('iframe').css('height', 'calc( ' + $('.document').height() + 'px - 3em)');
$('iframe').css('height', 'calc( ' + $('.document').height() + 'px - 7em)');
</script>
{% endblock %}

View File

@ -1,33 +1,22 @@
{% load i18n %}
<div>
<form>
<div class="qualif-source">
{% if source_form %}
{{source_form.as_p}}
<button data-action-url="{{source_form_url}}" class="save"></button>
{% endif %}
</div>
{% if association.user_id %}
<input id="association-user-id" type="hidden" value="{{association.user_id}}"/>
{% endif %}
{% if association.formdefs.count %}
{% if associations|length %}
<ul>
{% for formdef in association.formdefs.all %}
<li>{{formdef.name}}</li>
{% for association in associations %}
<li>{{association.formdef_name}}</li>
{% endfor %}
<li><a href="#" class="plus">{% trans 'Add another' %}</a></li>
</ul>
<p>(<a rel="popup" data-inplace-submit="true" href="{% url 'alfortville-copies' pk=association.id %}">{% trans 'Copies' %}</a>)</p>
{% endif %}
<div class="add-formdef-reference" {% if association.formdefs.count %}style="display: none"{% endif %}>
<p>(<a rel="popup" data-inplace-submit="true" href="{% url 'alfortville-copies' pk=source_pk %}">{% trans 'Copies' %}</a>)</p>
<div class="add-formdef-reference" {% if associations|length %}style="display: none"{% endif %}>
<div>
{{form.formdef_reference}}
</div>
<button class="add">{% trans 'Add' %}</button>
</div>
{% if association.formdefs.count %}
{% if associations|length %}
<button class="done" data-action-url="{% url 'qualif-done' %}">{% trans 'Done' %}</button>
{% endif %}
</form>

View File

@ -36,7 +36,7 @@ except ImportError:
from sources.mail.views import Home as MailHome
from sources.phone.views import Home as PhoneHome
from .qualif.models import Association, FormdefReference
from .qualif.models import Association
from .kb.views import HomeZone as KbHomeZone
from .contacts.views import HomeZone as ContactsHomeZone
from .forms import QualificationForm
@ -67,34 +67,19 @@ class Qualification(TemplateView):
def get_context_data(self, **kwargs):
context = super(Qualification, self).get_context_data(**kwargs)
context['form'] = QualificationForm()
source_type = ContentType.objects.get(id=self.request.GET['source_type'])
if source_type.model_class().get_qualification_form_class():
source_object = source_type.model_class().objects.get(id=self.request.GET['source_pk'])
context['source_form'] = source_object.get_qualification_form()
context['source_form_url'] = source_type.model_class().get_qualification_form_submit_url()
try:
context['association'] = Association.objects.get(
source_type=ContentType.objects.get(id=self.request.GET['source_type']),
source_pk=self.request.GET['source_pk'])
except Association.DoesNotExist:
pass
context['source_type'] = self.request.GET['source_type']
context['source_pk'] = self.request.GET['source_pk']
context['associations'] = Association.objects.filter(
source_type=ContentType.objects.get(id=self.request.GET['source_type']),
source_pk=self.request.GET['source_pk'])
return context
def post(self, request, *args, **kwargs):
association, created = Association.objects.get_or_create(
association = Association(
source_type=ContentType.objects.get(id=request.POST['source_type']),
source_pk=request.POST['source_pk'])
if created:
association.save()
if 'formdef_reference' in request.POST:
formdef_ref, created = FormdefReference.objects.get_or_create(
reference=request.POST['formdef_reference'])
if created:
formdef_ref.save()
association.formdefs.add(formdef_ref)
if 'user_id' in request.POST:
association.user_id = request.POST['user_id']
association.save()
association.formdef_reference = request.POST['formdef_reference']
association.save()
request.GET = request.POST
return self.get(request)
@ -123,11 +108,11 @@ home_phone = login_required(HomePhone.as_view())
@csrf_exempt
def qualification_done(request):
association = Association.objects.get(
source_type=ContentType.objects.get(id=request.POST['source_type']),
source_pk=request.POST['source_pk'])
association.triaged = True
association.save()
source_class = ContentType.objects.get(
id=request.POST['source_type']).model_class()
source_object = source_class.objects.get(id=request.POST['source_pk'])
source_object.status = 'done-qualif'
source_object.save()
response = HttpResponse(content_type='application/json')
json.dump({'result': 'ok'}, response, indent=2)
return response