diff --git a/Dockerfile b/Dockerfile index d1a9ca29f..95782b591 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,7 @@ COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env # compilescss - Precompile all occurrences of your SASS/SCSS files for the whole project into css files -RUN python3 manage.py bower_install -- --allow-root --no-input && \ +RUN python3 manage.py bower_install --allow-root && \ python3 manage.py compilescss RUN python3 manage.py collectstatic --noinput --clear diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index 49ded787e..dba8788b2 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -2,7 +2,7 @@ autopep8==1.2.4 beautifulsoup4==4.6.0 -django-debug-toolbar==1.5 +django-debug-toolbar==1.8 ipdb==0.10.1 pdbpp==0.9.2 pip-review==0.4 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 6d9d79d66..f93a0ee00 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,24 +1,21 @@ dj-database-url==0.4.1 django-haystack==2.6.0 -django>=1.9,<1.10 -# TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite -# a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma -# nova versão do django-admin-bootstrapped for lançada -git+git://github.com/django-admin-bootstrapped/django-admin-bootstrapped.git +django>=1.10,<1.11 +git+git://github.com/rubgombar1/django-admin-bootstrapped.git django-bootstrap3==7.0.1 -django-bower==5.1.0 +django-bower==5.2.0 django-braces==1.9.0 django-compressor==2.0 -django-crispy-forms==1.6.0 -django-extensions==1.6.7 -django-extra-views==0.8.0 +django-crispy-forms==1.6.1 +django-extensions==1.9.8 +django-extra-views==0.11.0 django-filter==0.15.3 django-floppyforms==1.6.2 -django-model-utils==2.5 -django-sass-processor==0.5.4 +django-model-utils==3.1.1 +django-sass-processor==0.5.8 djangorestframework==3.4.0 -drfdocs==0.0.11 -easy-thumbnails==2.3 +git+git://github.com/jasperlittle/django-rest-framework-docs +easy-thumbnails==2.5 django-image-cropping==1.1.0 git+git://github.com/interlegis/trml2pdf.git libsass==0.11.1 diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py old mode 100644 new mode 100755 index 9f55d3e6d..4bd265b72 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -2,7 +2,11 @@ from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import transaction from django.utils.translation import ugettext_lazy as _ -from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica +from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica, AnexoAudienciaPublica +from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout + +from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.utils import timezone @@ -102,3 +106,26 @@ class AudienciaForm(forms.ModelForm): raise ValidationError(msg) return cleaned_data + + +class AnexoAudienciaPublicaForm(forms.ModelForm): + + class Meta: + model = AnexoAudienciaPublica + fields = ['arquivo', + 'assunto'] + + def __init__(self, *args, **kwargs): + + row1 = to_row( + [('arquivo', 4)]) + + row2 = to_row( + [('assunto', 12)]) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + Fieldset(_('Identificação Básica'), + row1, row2)) + super(AnexoAudienciaPublicaForm, self).__init__( + *args, **kwargs) diff --git a/sapl/audiencia/migrations/0007_anexoaudienciapublica.py b/sapl/audiencia/migrations/0007_anexoaudienciapublica.py new file mode 100644 index 000000000..b322cc741 --- /dev/null +++ b/sapl/audiencia/migrations/0007_anexoaudienciapublica.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-10-17 20:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import sapl.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0006_auto_20180808_0856'), + ] + + operations = [ + migrations.CreateModel( + name='AnexoAudienciaPublica', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nome', models.CharField(max_length=30, verbose_name='Nome')), + ('arquivo', models.FileField(blank=True, null=True, upload_to=sapl.utils.texto_upload_path, verbose_name='Arquivo')), + ('data', models.DateField(blank=True, null=True, verbose_name='Data')), + ('assunto', models.TextField(blank=True, verbose_name='Assunto')), + ('indexacao', models.TextField(blank=True)), + ('audiencia', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='audiencia.AudienciaPublica')), + ], + options={ + 'verbose_name': 'Documento Acessório', + 'verbose_name_plural': 'Documentos Acessórios', + }, + ), + ] diff --git a/sapl/audiencia/migrations/0008_auto_20181023_1051.py b/sapl/audiencia/migrations/0008_auto_20181023_1051.py new file mode 100644 index 000000000..70c491a4c --- /dev/null +++ b/sapl/audiencia/migrations/0008_auto_20181023_1051.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 13:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0007_anexoaudienciapublica'), + ] + + operations = [ + migrations.AlterModelOptions( + name='anexoaudienciapublica', + options={'verbose_name': 'Anexo de Documento Acessório', 'verbose_name_plural': 'Anexo de Documentos Acessórios'}, + ), + migrations.RemoveField( + model_name='anexoaudienciapublica', + name='nome', + ), + migrations.AlterField( + model_name='anexoaudienciapublica', + name='data', + field=models.DateField(auto_now=True, null=True), + ), + ] diff --git a/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py b/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py new file mode 100644 index 000000000..e35a7a2a7 --- /dev/null +++ b/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 13:55 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0008_auto_20181023_1051'), + ] + + operations = [ + migrations.RemoveField( + model_name='anexoaudienciapublica', + name='indexacao', + ), + ] diff --git a/sapl/audiencia/models.py b/sapl/audiencia/models.py old mode 100644 new mode 100755 index 477a442d9..684f27625 --- a/sapl/audiencia/models.py +++ b/sapl/audiencia/models.py @@ -1,5 +1,6 @@ import reversion from django.db import models +from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from model_utils import Choices from sapl.materia.models import MateriaLegislativa @@ -143,6 +144,51 @@ class AudienciaPublica(models.Model): self.upload_ata = upload_ata self.upload_anexo = upload_anexo + return models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + + +@reversion.register() +class AnexoAudienciaPublica(models.Model): + audiencia = models.ForeignKey(AudienciaPublica, + on_delete=models.PROTECT) + arquivo = models.FileField( + blank=True, + null=True, + upload_to=texto_upload_path, + verbose_name=_('Arquivo')) + data = models.DateField(auto_now=timezone.now,blank=True, null=True) + assunto = models.TextField( + blank=True, verbose_name=_('Assunto')) + + class Meta: + verbose_name = _('Anexo de Documento Acessório') + verbose_name_plural = _('Anexo de Documentos Acessórios') + + def __str__(self): + return self.assunto + + def delete(self, using=None, keep_parents=False): + if self.arquivo: + self.arquivo.delete() + + return models.Model.delete( + self, using=using, keep_parents=keep_parents) + + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + + if not self.pk and self.arquivo: + arquivo = self.arquivo + self.arquivo = None + models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + self.arquivo = arquivo + return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, diff --git a/sapl/audiencia/urls.py b/sapl/audiencia/urls.py old mode 100644 new mode 100755 index ece3bf1fb..bdf02beca --- a/sapl/audiencia/urls.py +++ b/sapl/audiencia/urls.py @@ -1,10 +1,11 @@ from django.conf.urls import include, url -from sapl.audiencia.views import (index, AudienciaCrud) +from sapl.audiencia.views import (index, AudienciaCrud,AnexoAudienciaPublicaCrud) from .apps import AppConfig app_name = AppConfig.name urlpatterns = [ - url(r'^audiencia/', include(AudienciaCrud.get_urls())), + url(r'^audiencia/', include(AudienciaCrud.get_urls() + + AnexoAudienciaPublicaCrud.get_urls())), ] \ No newline at end of file diff --git a/sapl/audiencia/views.py b/sapl/audiencia/views.py old mode 100644 new mode 100755 index 0a8da8af1..93d214683 --- a/sapl/audiencia/views.py +++ b/sapl/audiencia/views.py @@ -1,11 +1,13 @@ +import sapl + from django.http import HttpResponse from django.core.urlresolvers import reverse from django.views.decorators.clickjacking import xframe_options_exempt from django.views.generic import UpdateView -from sapl.crud.base import RP_DETAIL, RP_LIST, Crud +from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, MasterDetailCrud -from .forms import AudienciaForm -from .models import AudienciaPublica +from .forms import AudienciaForm, AnexoAudienciaPublicaForm +from .models import AudienciaPublica, AnexoAudienciaPublica def index(request): @@ -18,7 +20,7 @@ class AudienciaCrud(Crud): class BaseMixin(Crud.BaseMixin): list_field_names = ['numero', 'nome', 'tipo', 'materia', - 'data'] + 'data'] ordering = 'nome', 'numero', 'tipo', 'data' class ListView(Crud.ListView): @@ -69,4 +71,40 @@ class AudienciaCrud(Crud): def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) + +class AudienciaPublicaMixin: + + def has_permission(self): + app_config = sapl.base.models.AppConfig.objects.last() + if app_config and app_config.documentos_administrativos == 'O': + return True + + return super().has_permission() + + +class AnexoAudienciaPublicaCrud(MasterDetailCrud): + model = AnexoAudienciaPublica + parent_field = 'audiencia' + help_topic = 'numeracao_docsacess' + + class BaseMixin(MasterDetailCrud.BaseMixin): + list_field_names = ['assunto'] + + class CreateView(MasterDetailCrud.CreateView): + form_class = AnexoAudienciaPublicaForm + layout_key = None + + class UpdateView(MasterDetailCrud.UpdateView): + form_class = AnexoAudienciaPublicaForm + + class ListView(AudienciaPublicaMixin, MasterDetailCrud.ListView): + + def get_queryset(self): + qs = super(MasterDetailCrud.ListView, self).get_queryset() + kwargs = {self.crud.parent_field: self.kwargs['pk']} + return qs.filter(**kwargs).order_by('-data', '-id') + + class DetailView(AudienciaPublicaMixin, + MasterDetailCrud.DetailView): + pass \ No newline at end of file diff --git a/sapl/base/tests/teststub_urls.py b/sapl/base/tests/teststub_urls.py index 9796768f2..8b66d6586 100644 --- a/sapl/base/tests/teststub_urls.py +++ b/sapl/base/tests/teststub_urls.py @@ -1,11 +1,9 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from django.views.generic.base import TemplateView from sapl.urls import urlpatterns as original_patterns -ptrn = patterns('', - url(r'^zzzz$', +ptrn = [url(r'^zzzz$', TemplateView.as_view( - template_name='index.html'), name='zzzz')) - + template_name='index.html'), name='zzzz')] urlpatterns = original_patterns + ptrn diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py index 4d12ba810..d2f8b0bd1 100644 --- a/sapl/comissoes/tests/test_comissoes.py +++ b/sapl/comissoes/tests/test_comissoes.py @@ -74,6 +74,7 @@ def test_incluir_comissao_submit(admin_client): 'nome': 'Comissão Teste', 'sigla': 'CT', 'data_criacao': '2016-03-22', + 'unidade_deliberativa': True, 'salvar': 'salvar'}, follow=True) assert response.status_code == 200 diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index b244af69a..80ceff110 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -1180,7 +1180,7 @@ class TipoProposicaoSelect(Select): str(data_has_perfil), force_text(option_label)) - def render_options(self, choices, selected_choices): + def render_options(self, selected_choices): # Normalize to strings. selected_choices = set(force_text(v) for v in selected_choices) output = [] diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index 4d143922f..f8f5d5256 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -66,6 +66,7 @@ rules_group_audiencia = { 'rules': [ (audiencia.AudienciaPublica, __base__), (audiencia.TipoAudienciaPublica, __base__), + (audiencia.AnexoAudienciaPublica, __base__), ] } diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index b691ca968..9d3257333 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -431,7 +431,7 @@ class VotacaoForm(forms.Form): class VotacaoNominalForm(forms.Form): resultado_votacao = forms.ModelChoiceField(label='Resultado da Votação', - required=True, + required=False, queryset=TipoResultadoVotacao.objects.all()) diff --git a/sapl/sessao/migrations/0027_auto_20181023_1239.py b/sapl/sessao/migrations/0027_auto_20181023_1239.py new file mode 100644 index 000000000..962ceaf01 --- /dev/null +++ b/sapl/sessao/migrations/0027_auto_20181023_1239.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 15:39 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0026_auto_20181016_1944'), + ] + + operations = [ + migrations.AlterField( + model_name='sessaoplenaria', + name='finalizada', + field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Sessão finalizada?'), + ), + migrations.AlterField( + model_name='sessaoplenaria', + name='iniciada', + field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=True, verbose_name='Sessão iniciada?'), + ), + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index f1a9365a3..92d4c38b0 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -160,10 +160,12 @@ class SessaoPlenaria(models.Model): verbose_name=_('Anexo da Sessão')) iniciada = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, - verbose_name=_('Sessão iniciada?')) + verbose_name=_('Sessão iniciada?'), + default=True) finalizada = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, - verbose_name=_('Sessão finalizada?')) + verbose_name=_('Sessão finalizada?'), + default=False) interativa = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, verbose_name=_('Sessão interativa')) diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 4c471e4e9..1a3cda69a 100644 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -119,7 +119,8 @@ def verifica_sessao_iniciada(request, spk): if not sessao.iniciada or sessao.finalizada: msg = _('Não é possível abrir matérias para votação. ' - 'Esta Sessão Plenária não foi iniciada ou está finalizada.') + 'Esta Sessão Plenária não foi iniciada ou está finalizada.' + ' Vá em "Abertura"->"Dados Básicos" e altere os valores dos campos necessários.') messages.add_message(request, messages.INFO, msg) return False @@ -1900,9 +1901,6 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): except ObjectDoesNotExist: raise Http404() - if 'cancelar-votacao' in request.POST: - fechar_votacao_materia(materia_votacao) - return self.form_valid(form) if form.is_valid(): votos_sim = 0 @@ -1910,6 +1908,21 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): abstencoes = 0 nao_votou = 0 + if 'cancelar-votacao' in request.POST: + fechar_votacao_materia(materia_votacao) + if self.ordem: + return HttpResponseRedirect(reverse( + 'sapl.sessao:ordemdia_list', kwargs={'pk': kwargs['pk']})) + else: + return HttpResponseRedirect(reverse( + 'sapl.sessao:expedientemateria_list', + kwargs={'pk': kwargs['pk']})) + else: + if form.cleaned_data['resultado_votacao'] == None: + form.add_error(None, 'Não é possível finalizar a votação sem ' + 'nenhum resultado da votação') + return self.form_invalid(form) + for votos in request.POST.getlist('voto_parlamentar'): v = votos.split(':') voto = v[0] diff --git a/sapl/settings.py b/sapl/settings.py index 7413ecb81..6cc34401e 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -115,11 +115,7 @@ HAYSTACK_CONNECTIONS = { }, } - -if DEBUG: - INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',) - -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = [ 'reversion.middleware.RevisionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', @@ -130,8 +126,13 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', - # 'speedinfo.middleware.ProfilerMiddleware', # Bug na versão 1.9 -) + 'speedinfo.middleware.ProfilerMiddleware', +] +if DEBUG: + INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',) + MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware', ] + INTERNAL_IPS = ('127.0.0.1') + CACHES = { 'default': { diff --git a/sapl/templates/audiencia/layouts.yaml b/sapl/templates/audiencia/layouts.yaml index 90433db63..8e93b9f3a 100644 --- a/sapl/templates/audiencia/layouts.yaml +++ b/sapl/templates/audiencia/layouts.yaml @@ -21,4 +21,9 @@ AudienciaPublicaDetail: - url_audio url_video - upload_pauta upload_ata upload_anexo - observacao - - audiencia_cancelada \ No newline at end of file + - audiencia_cancelada + +AnexoAudienciaPublica: + {% trans 'Documento Acessório' %}: + - assunto + - arquivo \ No newline at end of file diff --git a/sapl/templates/audiencia/subnav.yaml b/sapl/templates/audiencia/subnav.yaml new file mode 100644 index 000000000..e6dce83bf --- /dev/null +++ b/sapl/templates/audiencia/subnav.yaml @@ -0,0 +1,5 @@ +{% load i18n common_tags %} +- title: {% trans 'Início' %} + url: audienciapublica_detail +- title: {% trans 'Documento Acessório' %} + url: anexoaudienciapublica_list \ No newline at end of file diff --git a/sapl/templates/materia/impressos/ficha_pdf.html b/sapl/templates/materia/impressos/ficha_pdf.html index 07ad4092a..faa7163e2 100644 --- a/sapl/templates/materia/impressos/ficha_pdf.html +++ b/sapl/templates/materia/impressos/ficha_pdf.html @@ -70,7 +70,7 @@ body PROCESSO Nº {{ materia.numeracao_set.first.numero_materia }} / {{ materia.numeracao_set.first.ano_materia }}
  {% else %} - PROCESSO Nº: {{ materia.numero }}
+ PROCESSO Nº: {{ materia.numero }} / {{materia.ano}}
  {% endif %} @@ -96,6 +96,14 @@ body Autor: {{materia.autoria_set.first.autor.nome}}
+ {% if materia.numero_protocolo%} + + +
+
+ Protocolo: {{materia.numero_protocolo}} / {{materia.ano}}
+
+ {% endif %} diff --git a/sapl/test_urls.py b/sapl/test_urls.py index 851b1995b..23e0c9544 100644 --- a/sapl/test_urls.py +++ b/sapl/test_urls.py @@ -56,7 +56,7 @@ def create_perms_post_migrate(sapl_app_config): ctype = ContentType.objects.get_for_model(klass) ctypes.add(ctype) - for perm in _get_all_permissions(klass._meta, ctype): + for perm in _get_all_permissions(klass._meta): searched_perms.append((ctype, perm)) all_perms = set(Permission.objects.filter( diff --git a/sapl/urls.py b/sapl/urls.py index 07f382013..904b4357e 100644 --- a/sapl/urls.py +++ b/sapl/urls.py @@ -70,6 +70,12 @@ urlpatterns = [ # http://stackoverflow.com/questions/35510373/ if settings.DEBUG: + import debug_toolbar + + urlpatterns += [ + url(r'^__debug__/', include(debug_toolbar.urls)), + + ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/setup.py b/setup.py index c11a354b1..0fffd52ef 100644 --- a/setup.py +++ b/setup.py @@ -11,29 +11,30 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) install_requires = [ 'dj-database-url==0.4.1', 'django-haystack==2.6.0', - 'django>=1.9,<1.10', + 'django>=1.10,<1.11', # TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite # a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma # nova versão do django-admin-bootstrapped for lançada # 'git+git://github.com/django-admin-bootstrapped/ # django-admin-bootstrapped.git', 'django-bootstrap3==7.0.1', - 'django-bower==5.1.0', + 'django-bower==5.2.0', 'django-braces==1.9.0', 'django-compressor==2.0', - 'django-crispy-forms==1.6.0', - 'django-extensions==1.6.7', - 'django-extra-views==0.8.0', + 'django-crispy-forms==1.6.1', + 'django-extensions==1.9.8', + 'django-extra-views==0.11.0', 'django-filter==0.15.3', 'django-floppyforms==1.6.2', - 'django-model-utils==2.5', - 'django-sass-processor==0.5.4', - 'djangorestframework', + 'django-model-utils==3.1.1', + 'django-sass-processor==0.5.8', + 'djangorestframework==3.4.0', 'drfdocs', - 'easy-thumbnails==2.3', + 'easy-thumbnails==2.5', + 'django-image-cropping==1.1.0', # 'git+git://github.com/interlegis/trml2pdf.git', 'libsass==0.11.1', - 'psycopg2==2.7.3', + 'psycopg2==2.7.4', 'python-decouple==3.0', 'pytz==2016.4', 'pyyaml==3.11', @@ -45,7 +46,9 @@ install_requires = [ 'gunicorn==19.6.0', 'django-reversion==2.0.8', 'WeasyPrint==0.42', - 'whoosh==2.7.4' + 'whoosh==2.7.4', + 'django-speedinfo==1.3.5', + 'django-reversion-compare==0.8.4' ] setup( name='interlegis-sapl',