From: Emilie Date: Fri, 30 Sep 2016 12:54:40 +0000 (+0200) Subject: Candidacy: done. Need more info about buttons but candidacies can be templated. X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=c8909ae17fbcf6d9b8e84174cc5a7a7098b6c0f2;p=mezzo.git Candidacy: done. Need more info about buttons but candidacies can be templated. --- diff --git a/app/local_settings.py b/app/local_settings.py index 4ddbb111..4f643841 100644 --- a/app/local_settings.py +++ b/app/local_settings.py @@ -115,7 +115,7 @@ ADMIN_MENU_ORDER = ( 'shop.DiscountCode', 'shop.Sale', )), - (_('Jobs'), ('organization-job.JobOffer',)), + (_('Jobs'), ('organization-job.JobOffer','organization-job.Candidacy')), (_('Festival'), ('organization-festival.Artist',)), (_('Users'), ('auth.User', 'auth.Group',)), (_('Site'), ('sites.Site', 'redirects.Redirect', 'conf.Setting')), diff --git a/app/organization/job/admin.py b/app/organization/job/admin.py index 9b82ec75..31723ac4 100644 --- a/app/organization/job/admin.py +++ b/app/organization/job/admin.py @@ -2,7 +2,8 @@ from django.contrib import admin from mezzanine.utils.static import static_lazy as static from copy import deepcopy from mezzanine.core.admin import * -from organization.job.models import JobOffer, JobResponse +from organization.job.models import * +from organization.job.forms import * class JobResponseInline(TabularDynamicInlineAdmin): @@ -16,4 +17,24 @@ class JobOfferAdminDisplayable(BaseTranslationModelAdmin): inlines = [JobResponseInline,] +class CandidacyImageInline(TabularDynamicInlineAdmin): + + model = CandidacyImage + + +class CandidacyAdmin(admin.ModelAdmin): + + model = Candidacy + + +class CandidacyAdminDisplayable(BaseTranslationModelAdmin,): + + list_display = ('title', 'external_content', 'content_object', ) + form = CandidacyForm + fieldsets = deepcopy(CandidacyAdmin.fieldsets) + inlines = [CandidacyImageInline,] + exclude = ("short_url", "keywords", "description", "slug", ) + + admin.site.register(JobOffer, JobOfferAdminDisplayable) +admin.site.register(Candidacy, CandidacyAdminDisplayable) diff --git a/app/organization/job/forms.py b/app/organization/job/forms.py index fca4a411..ed49dc86 100644 --- a/app/organization/job/forms.py +++ b/app/organization/job/forms.py @@ -1,9 +1,13 @@ from dal import autocomplete - +import dal_queryset_sequence +import dal_select2_queryset_sequence from django import forms from django.forms.widgets import HiddenInput from django.forms import ModelForm -from organization.job.models import JobResponse +from organization.job.models import * +from organization.magazine.models import Article +from organization.pages.models import CustomPage +from mezzanine_agenda.models import Event class JobResponseForm(ModelForm): @@ -15,3 +19,20 @@ class JobResponseForm(ModelForm): class Meta: model = JobResponse fields = ['first_name', 'last_name', 'email', 'message', 'curriculum_vitae', 'cover_letter', 'job_offer'] + + +class CandidacyForm(autocomplete.FutureModelForm): + + content_object = dal_queryset_sequence.fields.QuerySetSequenceModelField( + queryset=autocomplete.QuerySetSequence( + Article.objects.all(), + Event.objects.all(), + CustomPage.objects.all(), + ), + required=False, + widget=dal_select2_queryset_sequence.widgets.QuerySetSequenceSelect2('candidacy-autocomplete'), + ) + + class Meta: + model = Candidacy + fields = ('__all__') diff --git a/app/organization/job/migrations/0004_candidacy.py b/app/organization/job/migrations/0004_candidacy.py new file mode 100644 index 00000000..c808c5f2 --- /dev/null +++ b/app/organization/job/migrations/0004_candidacy.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-30 10:50 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('sites', '0002_alter_domain_unique'), + ('organization-job', '0003_auto_20160929_1833'), + ] + + operations = [ + migrations.CreateModel( + name='Candidacy', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('text_button', models.CharField(blank=True, max_length=150, verbose_name='text button')), + ('external_content', models.URLField(blank=True, max_length=1000, verbose_name='external content')), + ('object_id', models.PositiveIntegerField(editable=False, null=True, verbose_name='related object')), + ('content_type', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='local content')), + ('site', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'verbose_name': 'candidacy', + }, + ), + ] diff --git a/app/organization/job/migrations/0005_auto_20160930_1254.py b/app/organization/job/migrations/0005_auto_20160930_1254.py new file mode 100644 index 00000000..2faa6b76 --- /dev/null +++ b/app/organization/job/migrations/0005_auto_20160930_1254.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-30 10:54 +from __future__ import unicode_literals + +from django.db import migrations, models +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-job', '0004_candidacy'), + ] + + operations = [ + migrations.AlterModelOptions( + name='candidacy', + options={'verbose_name': 'candidacy', 'verbose_name_plural': 'candidacies'}, + ), + migrations.AddField( + model_name='candidacy', + name='content_en', + field=mezzanine.core.fields.RichTextField(null=True, verbose_name='Content'), + ), + migrations.AddField( + model_name='candidacy', + name='content_fr', + field=mezzanine.core.fields.RichTextField(null=True, verbose_name='Content'), + ), + migrations.AddField( + model_name='candidacy', + name='text_button_en', + field=models.CharField(blank=True, max_length=150, null=True, verbose_name='text button'), + ), + migrations.AddField( + model_name='candidacy', + name='text_button_fr', + field=models.CharField(blank=True, max_length=150, null=True, verbose_name='text button'), + ), + migrations.AddField( + model_name='candidacy', + name='title_en', + field=models.CharField(max_length=500, null=True, verbose_name='Title'), + ), + migrations.AddField( + model_name='candidacy', + name='title_fr', + field=models.CharField(max_length=500, null=True, verbose_name='Title'), + ), + ] diff --git a/app/organization/job/migrations/0006_candidacyimage.py b/app/organization/job/migrations/0006_candidacyimage.py new file mode 100644 index 00000000..b7ede0ff --- /dev/null +++ b/app/organization/job/migrations/0006_candidacyimage.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-30 12:21 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-job', '0005_auto_20160930_1254'), + ] + + operations = [ + migrations.CreateModel( + name='CandidacyImage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('_order', mezzanine.core.fields.OrderField(null=True, verbose_name='Order')), + ('title', models.CharField(max_length=1024, verbose_name='title')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('file', mezzanine.core.fields.FileField(max_length=1024, verbose_name='Image')), + ('credits', models.CharField(blank=True, max_length=256, null=True, verbose_name='credits')), + ('type', models.CharField(choices=[('logo', 'logo'), ('slider', 'slider'), ('card', 'card'), ('page_slider', 'page - slider'), ('page_featured', 'page - featured')], max_length=64, verbose_name='type')), + ('candidacy', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='images', to='organization-job.Candidacy', verbose_name='candidacy')), + ], + options={ + 'ordering': ('_order',), + }, + ), + ] diff --git a/app/organization/job/models.py b/app/organization/job/models.py index 7dfaacaf..f445aeaf 100644 --- a/app/organization/job/models.py +++ b/app/organization/job/models.py @@ -32,3 +32,39 @@ class JobOffer(Displayable, RichText): class Meta: verbose_name = _('job offer') verbose_name_plural = _("job offers") + + +class Candidacy(Displayable, RichText): + + text_button = models.CharField(blank=True, max_length=150, null=False, verbose_name=_('text button')) + external_content = models.URLField(blank=True, max_length=1000, null=False, verbose_name=_('external content')) + + # used for autocomplete but hidden in admin + content_type = models.ForeignKey( + ContentType, + verbose_name=_('local content'), + null=True, + blank=True, + editable=False, + ) + + # used for autocomplete but hidden in admin + object_id = models.PositiveIntegerField( + verbose_name=_('related object'), + null=True, + editable=False, + ) + + content_object = GenericForeignKey('content_type', 'object_id') + + def get_absolute_url(self): + return self.external_content + + class Meta: + verbose_name = _('candidacy') + verbose_name_plural = _("candidacies") + + +class CandidacyImage(Image): + + candidacy = models.ForeignKey(Candidacy, verbose_name=_('candidacy'), related_name='images', blank=True, null=True, on_delete=models.SET_NULL) diff --git a/app/organization/job/translation.py b/app/organization/job/translation.py index e22b5947..e2d6885e 100644 --- a/app/organization/job/translation.py +++ b/app/organization/job/translation.py @@ -14,3 +14,14 @@ class JobOfferTranslationOptions(TranslationOptions): class JobResponseTranslationOptions(TranslationOptions): pass + +@register(Candidacy) +class JobResponseTranslationOptions(TranslationOptions): + + fields = ('title', 'content', 'text_button', ) + + +@register(CandidacyImage) +class JobResponseTranslationOptions(TranslationOptions): + + pass diff --git a/app/organization/job/urls.py b/app/organization/job/urls.py index e852861b..b9144115 100644 --- a/app/organization/job/urls.py +++ b/app/organization/job/urls.py @@ -6,7 +6,7 @@ from django.contrib import admin from mezzanine.core.views import direct_to_template from mezzanine.conf import settings -from organization.job.views import JobOfferDetailView, JobOfferListView +from organization.job.views import * _slash = "/" if settings.APPEND_SLASH else "" @@ -14,5 +14,6 @@ _slash = "/" if settings.APPEND_SLASH else "" urlpatterns = [ url("^job-offer/(?P.*)%s$" % _slash, JobOfferDetailView.as_view(), name='organization-job-offer-detail'), url("^job-offer/$", JobOfferListView.as_view(), name='organization-job-offer-list'), - #url(r'job-response/add/$', JobResponseCreate.as_view(), name='job-response-add'), + url("^candidacies/$", CandidacyListView.as_view(), name='candidacies-list'), + url("^candidacy-autocomplete/$", CandidacyAutocomplete.as_view(), name='candidacy-autocomplete'), ] diff --git a/app/organization/job/views.py b/app/organization/job/views.py index e991cb57..1dd4d0f4 100644 --- a/app/organization/job/views.py +++ b/app/organization/job/views.py @@ -1,6 +1,8 @@ import os import mimetypes import humanize +from dal import autocomplete +from dal_select2_queryset_sequence.views import Select2QuerySetSequenceView from django import forms from django.shortcuts import redirect from django.shortcuts import render @@ -15,7 +17,9 @@ from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponse from django.shortcuts import get_object_or_404 from mezzanine.conf import settings -from organization.job.models import JobOffer, JobResponse +from organization.pages.models import CustomPage +from organization.magazine.models import Article +from organization.job.models import * from organization.job.forms import JobResponseForm mime_types = ['pdf', 'msword', 'vnd.oasis.opendocument.text', 'vnd.openxmlformats-officedocument.wordprocessingml.document'] @@ -90,3 +94,39 @@ def email_application_notification(request, job_offer, data): msg.send() return HttpResponse('email_application_notification') + + +class CandidacyListView(ListView): + + model = Candidacy + template_name='job/candidacy_list.html' + context_object_name = 'candidacy' + + def get_context_data(self, **kwargs): + context = super(CandidacyListView, self).get_context_data(**kwargs) + return context + + +class CandidacyAutocomplete(Select2QuerySetSequenceView): + def get_queryset(self): + + articles = Article.objects.all() + custompage = CustomPage.objects.all() + events = Event.objects.all() + + if self.q: + articles = articles.filter(title__icontains=self.q) + custompage = custompage.filter(title__icontains=self.q) + events = events.filter(title__icontains=self.q) + + qs = autocomplete.QuerySetSequence(articles, custompage, events ) + + if self.q: + # This would apply the filter on all the querysets + qs = qs.filter(title__icontains=self.q) + + # This will limit each queryset so that they show an equal number + # of results. + qs = self.mixup_querysets(qs) + + return qs diff --git a/app/templates/job/candidacy_list.html b/app/templates/job/candidacy_list.html new file mode 100644 index 00000000..6dc1f4f7 --- /dev/null +++ b/app/templates/job/candidacy_list.html @@ -0,0 +1,32 @@ +{% extends "pages/page.html" %} +{% load i18n mezzanine_tags keyword_tags pages_tags organization_tags %} + +{% block meta_title %}{% trans "Candidacies" %}{% endblock %} + +{% block meta_description %}{% metablock %} +{{ candidacy.description }} +{% endmetablock %}{% endblock %} + +{% block page_class %} + candidacy +{% endblock %} + +{% block page_title %} +

{% trans "Candidacies" %}

+{% endblock %} + +{% block page_content %} + + {% if candidacy %} + + {% for content in candidacy %} + {% include "job/inc/candidacy_card.html" %} + {% endfor %} + + {% else %} + +

{% trans "Please come back later. There is no candidacy at the moment." %}

+ + {% endif %} + +{% endblock %} diff --git a/app/templates/job/inc/candidacy_card.html b/app/templates/job/inc/candidacy_card.html new file mode 100644 index 00000000..4fc0f3ec --- /dev/null +++ b/app/templates/job/inc/candidacy_card.html @@ -0,0 +1,19 @@ +{% load i18n mezzanine_tags keyword_tags pages_tags organization_tags %} +
+ {% with content.images.all|get_type:'card' as images %} + {% if images %} + {{ content.title }} + {% endif %} + {% endwith %} +

{{ content.title }}

+
+ {{ content.publish_date|date:"DATE_FORMAT" }}
+ {{ content.description|slice:":500" }} +
+
+ {% if content.external_content %} + {{ content.text_button }} + {% elif content.content_object %} + {{ content.text_button }} + {% endif %} +