diff --git a/base/templatetags/__init__.py b/base/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/base/templatetags/common_tags.py b/base/templatetags/common_tags.py new file mode 100644 index 000000000..6bf2b7283 --- /dev/null +++ b/base/templatetags/common_tags.py @@ -0,0 +1,39 @@ +from compressor.utils import get_class +from django import template + +register = template.Library() + + +@register.simple_tag +def field_verbose_name(instance, field_name): + return instance._meta.get_field(field_name).verbose_name + + +@register.simple_tag +def fieldclass_verbose_name(class_name, field_name): + cls = get_class(class_name) + return cls._meta.get_field(field_name).verbose_name + + +@register.simple_tag +def model_verbose_name(class_name): + model = get_class(class_name) + return model._meta.verbose_name + + +@register.simple_tag +def model_verbose_name_plural(class_name): + model = get_class(class_name) + return model._meta.verbose_name_plural + + +@register.filter +def lookup(d, key): + skey = str(key) + return d[str(key)] if skey in d else [] + + +@register.filter +def isinst(value, class_str): + classe = value.__class__.__name__ + return classe == class_str diff --git a/compilacao/forms.py b/compilacao/forms.py index 851eec135..c3f1afdf7 100644 --- a/compilacao/forms.py +++ b/compilacao/forms.py @@ -12,7 +12,8 @@ from compilacao.models import (PARTICIPACAO_SOCIAL_CHOICES, Dispositivo, Nota, Publicacao, TextoArticulado, TipoNota, TipoPublicacao, TipoTextoArticulado, TipoVide, VeiculoPublicacao, Vide) -from compilacao.utils import YES_NO_CHOICES, FormLayout, to_column, to_row +from sapl.layout import SaplFormLayout, to_column, to_row +from sapl.utils import YES_NO_CHOICES class UpLoadImportFileForm(forms.Form): @@ -62,7 +63,7 @@ class TipoTaForm(ModelForm): ]) self.helper = FormHelper() - self.helper.layout = FormLayout( + self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, css_class="large-12")) super(TipoTaForm, self).__init__(*args, **kwargs) @@ -129,7 +130,7 @@ class TaForm(ModelForm): ]) self.helper = FormHelper() - self.helper.layout = FormLayout( + self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, css_class="large-12"), Fieldset( TextoArticulado._meta.get_field('ementa').verbose_name, @@ -428,7 +429,7 @@ class PublicacaoForm(ModelForm): ]) self.helper = FormHelper() - self.helper.layout = FormLayout( + self.helper.layout = SaplFormLayout( Fieldset(Publicacao._meta.verbose_name, row1, row2, row3, css_class="large-12")) diff --git a/compilacao/models.py b/compilacao/models.py index 88494093f..3155f1de5 100644 --- a/compilacao/models.py +++ b/compilacao/models.py @@ -9,8 +9,8 @@ from django.db.models.aggregates import Max from django.template import defaultfilters from django.utils.translation import ugettext_lazy as _ -from compilacao import utils -from compilacao.utils import YES_NO_CHOICES +from compilacao.utils import int_to_letter, int_to_roman +from sapl import utils class TimestampedMixin(models.Model): @@ -268,13 +268,13 @@ class TipoDispositivo(BaseModel): max_length=100, verbose_name=_('Sufixo html da nota automática')) contagem_continua = models.BooleanField( - choices=YES_NO_CHOICES, verbose_name=_('Contagem contínua')) + choices=utils.YES_NO_CHOICES, verbose_name=_('Contagem contínua')) dispositivo_de_articulacao = models.BooleanField( - choices=YES_NO_CHOICES, + choices=utils.YES_NO_CHOICES, default=False, verbose_name=_('Dispositivo de Articulação (Sem Texto)')) dispositivo_de_alteracao = models.BooleanField( - choices=YES_NO_CHOICES, + choices=utils.YES_NO_CHOICES, default=False, verbose_name=_('Dispositivo de Alteração')) formato_variacao0 = models.CharField( @@ -368,7 +368,7 @@ class PerfilEstruturalTextoArticulado(BaseModel): nome = models.CharField(max_length=50, verbose_name=_('Nome')) padrao = models.BooleanField( default=False, - choices=YES_NO_CHOICES, verbose_name=_('Padrão')) + choices=utils.YES_NO_CHOICES, verbose_name=_('Padrão')) class Meta: verbose_name = _('Perfil Estrutural de Texto Articulado') @@ -388,10 +388,12 @@ class TipoDispositivoRelationship(BaseModel): perfil = models.ForeignKey(PerfilEstruturalTextoArticulado) filho_de_insercao_automatica = models.BooleanField( default=False, - choices=YES_NO_CHOICES, verbose_name=_('Filho de Inserção Automática')) + choices=utils.YES_NO_CHOICES, + verbose_name=_('Filho de Inserção Automática')) permitir_variacao = models.BooleanField( default=True, - choices=YES_NO_CHOICES, verbose_name=_('Permitir Variação Numérica')) + choices=utils.YES_NO_CHOICES, + verbose_name=_('Permitir Variação Numérica')) quantidade_permitida = models.IntegerField( default=-1, @@ -549,12 +551,12 @@ class Dispositivo(BaseModel, TimestampedMixin): inconstitucionalidade = models.BooleanField( default=False, - choices=YES_NO_CHOICES, + choices=utils.YES_NO_CHOICES, verbose_name=_('Inconstitucionalidade')) # Relevant attribute only in altering norms visibilidade = models.BooleanField( default=False, - choices=YES_NO_CHOICES, + choices=utils.YES_NO_CHOICES, verbose_name=_('Visibilidade no Texto Articulado Publicado')) tipo_dispositivo = models.ForeignKey( @@ -839,16 +841,16 @@ class Dispositivo(BaseModel, TimestampedMixin): result = separadores[i] + str(numero[i]) + result elif formato[i] == TipoDispositivo.FNCI: result = separadores[i] + \ - utils.int_to_roman(numero[i]) + result + int_to_roman(numero[i]) + result elif formato[i] == TipoDispositivo.FNCi: result = separadores[i] + \ - utils.int_to_roman(numero[i]).lower() + result + int_to_roman(numero[i]).lower() + result elif formato[i] == TipoDispositivo.FNCA: result = separadores[i] + \ - utils.int_to_letter(numero[i]) + result + int_to_letter(numero[i]) + result elif formato[i] == TipoDispositivo.FNCa: result = separadores[i] + \ - utils.int_to_letter(numero[i]).lower() + result + int_to_letter(numero[i]).lower() + result elif formato[i] == TipoDispositivo.FNC8: result = separadores[i] + '*' + result elif formato[i] == TipoDispositivo.FNCN: diff --git a/compilacao/templatetags/compilacao_filters.py b/compilacao/templatetags/compilacao_filters.py index a8dfe91b9..b620ed307 100644 --- a/compilacao/templatetags/compilacao_filters.py +++ b/compilacao/templatetags/compilacao_filters.py @@ -1,4 +1,3 @@ -from compressor.utils import get_class from django import template from django.core.signing import Signer from django.db.models import Q @@ -22,12 +21,6 @@ def get_tipos_dispositivo(pk_atual): id__gte=pk_atual) -@register.filter -def lookup(d, key): - skey = str(key) - return d[str(key)] if skey in d else [] - - @register.simple_tag def dispositivo_desativado(dispositivo, inicio_vigencia, fim_vigencia): if inicio_vigencia and fim_vigencia: @@ -155,31 +148,6 @@ def nomenclatura_heranca(d, ignore_ultimo=0, ignore_primeiro=0): return result -@register.simple_tag -def field_verbose_name(instance, field_name): - return instance._meta.get_field(field_name).verbose_name - - -@register.simple_tag -def fieldclass_verbose_name(class_name, field_name): - cls = get_class( - 'compilacao.models.' + class_name) - return cls._meta.get_field( - field_name).verbose_name - - -@register.simple_tag -def model_verbose_name(class_name): - model = get_class('compilacao.models.' + class_name) - return model._meta.verbose_name - - -@register.simple_tag -def model_verbose_name_plural(class_name): - model = get_class('compilacao.models.' + class_name) - return model._meta.verbose_name_plural - - @register.filter def urldetail_content_type(obj): return '%s:detail' % obj.content_type.model diff --git a/compilacao/utils.py b/compilacao/utils.py index dd6b40f66..ca00f66b4 100644 --- a/compilacao/utils.py +++ b/compilacao/utils.py @@ -1,48 +1,3 @@ -from braces.views import FormMessagesMixin -from crispy_forms.helper import FormHelper -from crispy_forms_foundation.layout import Column, Fieldset, Row -from crispy_forms_foundation.layout.base import HTML, Div, Layout -from crispy_forms_foundation.layout.buttons import Submit -from django import forms -from django.conf.urls import url -from django.core.urlresolvers import reverse, reverse_lazy -from django.utils.functional import cached_property -from django.utils.translation import ugettext_lazy as _ -from django.views.generic.detail import DetailView -from django.views.generic.edit import CreateView, DeleteView, UpdateView -from django.views.generic.list import ListView - -NO_ENTRIES_MSG = _('Não existem registros') - - -def to_column(name_span): - fieldname, span = name_span - return Column(fieldname, css_class='large-%d' % span) - - -def to_row(names_spans): - return Row(*list(map(to_column, names_spans))) - - -def to_fieldsets(fields): - for field in fields: - if isinstance(field, list): - legend, *row_specs = field - rows = [to_row(name_span_list) for name_span_list in row_specs] - yield Fieldset(legend, *rows) - else: - yield field - - -def make_choices(*choice_pairs): - assert len(choice_pairs) % 2 == 0 - ipairs = iter(choice_pairs) - choices = list(zip(ipairs, ipairs)) - yield choices - for key, value in choices: - yield key - -YES_NO_CHOICES = [(True, _('Sim')), (False, _('Não'))] def int_to_roman(int_value): @@ -70,255 +25,3 @@ def int_to_letter(int_value): result = chr(rest + 65) + result result = chr(int_value + 65) + result return result - - -def from_to(start, end): - return list(range(start, end + 1)) - - -def make_pagination(index, num_pages): - '''Make a list of adjacent page ranges interspersed with "None"s - - The list starts with [1, 2] and end with [num_pages-1, num_pages]. - The list includes [index-1, index, index+1] - "None"s separate those ranges and mean ellipsis (...) - - Example: [1, 2, None, 10, 11, 12, None, 29, 30] - ''' - - PAGINATION_LENGTH = 10 - if num_pages <= PAGINATION_LENGTH: - return from_to(1, num_pages) - else: - if index - 1 <= 5: - tail = [num_pages - 1, num_pages] - head = from_to(1, PAGINATION_LENGTH - 3) - else: - if index + 1 >= num_pages - 3: - tail = from_to(index - 1, num_pages) - else: - tail = [index - 1, index, index + 1, - None, num_pages - 1, num_pages] - head = from_to(1, PAGINATION_LENGTH - len(tail) - 1) - return head + [None] + tail - - -def get_field_display(obj, fieldname): - field = obj._meta.get_field(fieldname) - verbose_name = str(field.verbose_name) - if field.choices: - value = getattr(obj, 'get_%s_display' % fieldname)() - else: - value = getattr(obj, fieldname) - - if value is None: - display = '' - elif 'date' in str(type(value)): - display = value.strftime("%d/%m/%Y") - elif 'bool' in str(type(value)): - display = 'Sim' if value else 'Não' - else: - display = str(value) - - return verbose_name, display - - -class FormLayout(Layout): - - def __init__(self, *fields): - buttons = Div( - HTML('%s' % _('Cancelar')), - Submit('submit', _('Enviar'), - css_class='button radius success right'), - css_class='radius clearfix' - ) - _fields = list(to_fieldsets(fields)) + \ - [Row(Column(buttons, css_class='clearfix'))] - super(FormLayout, self).__init__(*_fields) - - -class Crud(object): - pass - - -def build_crud(model, help_path, layout): - crud = Crud() - crud.model = model - crud.help_path = help_path - crud.namespace = model._meta.model_name - - class CrispyForm(forms.ModelForm): - - class Meta: - model = crud.model - exclude = [] - - def __init__(self, *args, **kwargs): - super(CrispyForm, self).__init__(*args, **kwargs) - self.helper = FormHelper() - self.helper.layout = FormLayout(*layout) - - crud.model_form = CrispyForm - - def in_namespace(url_name): - return '%s:%s' % (crud.namespace, url_name) - - def make_form_invalid_message(msg): - return '%s %s' % (_('Formulário inválido.'), msg) - - class BaseMixin(object): - model = crud.model - - verbose_name = crud.model._meta.verbose_name - verbose_name_plural = crud.model._meta.verbose_name_plural - - list_url = reverse_lazy(in_namespace('list')) - create_url = reverse_lazy(in_namespace('create')) - help_path = crud.help_path # FIXME - - def get_url_for_this_object(self, url_name): - return reverse(in_namespace(url_name), args=(self.object.id,)) - - @property - def detail_url(self): - return self.get_url_for_this_object('detail') - - @property - def update_url(self): - return self.get_url_for_this_object('update') - - @property - def delete_url(self): - return self.get_url_for_this_object('delete') - - def get_template_names(self): - names = super(BaseMixin, self).get_template_names() - names.append("compilacao/%s.html" % - self.template_name_suffix.lstrip('_')) - return names - - class CrudListView(BaseMixin, ListView): - title = BaseMixin.verbose_name_plural - paginate_by = 10 - no_entries_msg = NO_ENTRIES_MSG - - @cached_property - def field_names(self): - '''The list of field names to display on table - - This base implementation returns the field names - in the first fieldset of the layout. - ''' - rows = layout[0][1:] - return [fieldname for row in rows for fieldname, __ in row] - - def get_rows(self, object_list): - return [[(get_field_display(obj, name)[1], - obj.pk if i == 0 else None) - for i, name in enumerate(self.field_names)] - for obj in object_list - ] - - def get_context_data(self, **kwargs): - context = super(CrudListView, self).get_context_data(**kwargs) - paginator = context['paginator'] - page_obj = context['page_obj'] - context['page_range'] = make_pagination( - page_obj.number, paginator.num_pages) - object_list = context['object_list'] - context['headers'] = [ - self.model._meta.get_field(fieldname).verbose_name - for fieldname in self.field_names] - context['rows'] = self.get_rows(object_list) - context['NO_ENTRIES_MSG'] = NO_ENTRIES_MSG - return context - - class CrudCreateView(BaseMixin, FormMessagesMixin, CreateView): - form_class = crud.model_form - title = _('Adicionar %(verbose_name)s') % { - 'verbose_name': BaseMixin.verbose_name} - form_valid_message = _('Registro criado com sucesso!') - form_invalid_message = make_form_invalid_message( - _('O registro não foi criado.')) - cancel_url = BaseMixin.list_url - - def form_invalid(self, form): - """ - If the form is invalid, re-render the context data with the - data-filled form and errors. - """ - print(form.errors) - return self.render_to_response(self.get_context_data(form=form)) - - def get_success_url(self): - return self.detail_url - - class CrudDetailView(BaseMixin, DetailView): - - @property - def title(self): - return self.get_object() - - def get_column(self, fieldname, span): - obj = self.get_object() - verbose_name, text = get_field_display(obj, fieldname) - return { - 'id': fieldname, - 'span': span, - 'verbose_name': verbose_name, - 'text': text, - } - - @property - def fieldsets(self): - return [ - {'legend': legend, - 'rows': [[self.get_column(fieldname, span) - for fieldname, span in row] - for row in rows] - } for legend, *rows in layout] - - class CrudUpdateView(BaseMixin, FormMessagesMixin, UpdateView): - form_class = crud.model_form - form_valid_message = _('Registro alterado com sucesso!') - form_invalid_message = make_form_invalid_message( - _('Suas alterações não foram salvas.')) - - @property - def title(self): - return self.get_object() - - def get_success_url(self): - return self.detail_url - - def cancel_url(self): - return self.detail_url - - class CrudDeleteView(BaseMixin, FormMessagesMixin, DeleteView): - form_valid_message = _('Registro excluído com sucesso!') - form_invalid_message = make_form_invalid_message( - _('O registro não foi excluído.')) - - def get_success_url(self): - return self.list_url - - crud.CrudListView = CrudListView - crud.CrudCreateView = CrudCreateView - crud.CrudDetailView = CrudDetailView - crud.CrudUpdateView = CrudUpdateView - crud.CrudDeleteView = CrudDeleteView - - # XXX transform into a property of Crud to enable override - crud.urlpatterns = [ - url(r'^$', CrudListView.as_view(), name='list'), - url(r'^create$', CrudCreateView.as_view(), name='create'), - url(r'^(?P\d+)$', CrudDetailView.as_view(), name='detail'), - url(r'^(?P\d+)/edit$', - CrudUpdateView.as_view(), name='update'), - url(r'^(?P\d+)/delete$', - CrudDeleteView.as_view(), name='delete'), - ] - crud.urls = crud.urlpatterns, crud.namespace, crud.namespace - - return crud diff --git a/compilacao/views.py b/compilacao/views.py index c53085602..46d084842 100644 --- a/compilacao/views.py +++ b/compilacao/views.py @@ -20,7 +20,6 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.list import ListView -from compilacao import utils from compilacao.forms import (NotaForm, PublicacaoForm, TaForm, TipoTaForm, VideForm) from compilacao.models import (Dispositivo, Nota, @@ -28,7 +27,7 @@ from compilacao.models import (Dispositivo, Nota, TextoArticulado, TipoDispositivo, TipoNota, TipoPublicacao, TipoTextoArticulado, TipoVide, VeiculoPublicacao, Vide) -from compilacao.utils import NO_ENTRIES_MSG, build_crud +from sapl.crud import NO_ENTRIES_MSG, build_crud, make_pagination DISPOSITIVO_SELECT_RELATED = ( 'tipo_dispositivo', @@ -72,6 +71,57 @@ veiculo_publicacao_crud = build_crud( ]) +perfil_estr_txt_norm = build_crud( + PerfilEstruturalTextoArticulado, 'perfil_estrutural', [ + + [_('Perfil Estrutural de Textos Articulados'), + [('sigla', 2), ('nome', 10)]], + ]) + + +tipo_dispositivo_crud = build_crud( + TipoDispositivo, 'tipo_dispositivo', [ + + [_('Dados Básicos'), + [('nome', 8), ('class_css', 4)]], + + [_('Configurações para Edição do Rótulo'), + [('rotulo_prefixo_texto', 3), + ('rotulo_sufixo_texto', 3), + ('rotulo_ordinal', 3), + ('contagem_continua', 3)], + + ], + + [_('Configurações para Renderização de Rótulo e Texto'), + [('rotulo_prefixo_html', 6), + ('rotulo_sufixo_html', 6), ], + + [('texto_prefixo_html', 4), + ('dispositivo_de_articulacao', 4), + ('texto_sufixo_html', 4)], + ], + + [_('Configurações para Nota Automática'), + [('nota_automatica_prefixo_html', 6), + ('nota_automatica_sufixo_html', 6), + ], + ], + + [_('Configurações para Variações Numéricas'), + + [('formato_variacao0', 12)], + [('rotulo_separador_variacao01', 5), ('formato_variacao1', 7), ], + [('rotulo_separador_variacao12', 5), ('formato_variacao2', 7), ], + [('rotulo_separador_variacao23', 5), ('formato_variacao3', 7), ], + [('rotulo_separador_variacao34', 5), ('formato_variacao4', 7), ], + [('rotulo_separador_variacao45', 5), ('formato_variacao5', 7), ], + + ], + + ]) + + class IntegracaoTaView(TemplateView): def get(self, *args, **kwargs): @@ -259,7 +309,7 @@ class TaListView(ListView): context = super(TaListView, self).get_context_data(**kwargs) paginator = context['paginator'] page_obj = context['page_obj'] - context['page_range'] = utils.make_pagination( + context['page_range'] = make_pagination( page_obj.number, paginator.num_pages) return context @@ -1551,7 +1601,7 @@ class PublicacaoListView(ListView): @property def title(self): - return _('%s da %s' % ( + return _('%s de %s' % ( self.model._meta.verbose_name_plural, self.ta)) diff --git a/compilacao/views2.py b/compilacao/views2.py deleted file mode 100644 index 4c09763f3..000000000 --- a/compilacao/views2.py +++ /dev/null @@ -1,54 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -from compilacao.models import PerfilEstruturalTextoArticulado, TipoDispositivo -from sapl.crud import build_crud - -perfil_estr_txt_norm = build_crud( - PerfilEstruturalTextoArticulado, 'perfil_estrutural', [ - - [_('Perfil Estrutural de Textos Articulados'), - [('sigla', 2), ('nome', 10)]], - ]) - - -tipo_dispositivo_crud = build_crud( - TipoDispositivo, 'tipo_dispositivo', [ - - [_('Dados Básicos'), - [('nome', 8), ('class_css', 4)]], - - [_('Configurações para Edição do Rótulo'), - [('rotulo_prefixo_texto', 3), - ('rotulo_sufixo_texto', 3), - ('rotulo_ordinal', 3), - ('contagem_continua', 3)], - - ], - - [_('Configurações para Renderização de Rótulo e Texto'), - [('rotulo_prefixo_html', 6), - ('rotulo_sufixo_html', 6), ], - - [('texto_prefixo_html', 4), - ('dispositivo_de_articulacao', 4), - ('texto_sufixo_html', 4)], - ], - - [_('Configurações para Nota Automática'), - [('nota_automatica_prefixo_html', 6), - ('nota_automatica_sufixo_html', 6), - ], - ], - - [_('Configurações para Variações Numéricas'), - - [('formato_variacao0', 12)], - [('rotulo_separador_variacao01', 5), ('formato_variacao1', 7), ], - [('rotulo_separador_variacao12', 5), ('formato_variacao2', 7), ], - [('rotulo_separador_variacao23', 5), ('formato_variacao3', 7), ], - [('rotulo_separador_variacao34', 5), ('formato_variacao4', 7), ], - [('rotulo_separador_variacao45', 5), ('formato_variacao5', 7), ], - - ], - - ]) diff --git a/templates/compilacao/detail.html b/templates/compilacao/detail.html deleted file mode 100644 index f6b89bb8d..000000000 --- a/templates/compilacao/detail.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} - -{% block base_content %} - - {# FIXME is this the best markup to use? #} -
- {% block actions %} - - {% endblock actions %} - {% block sections_nav %}{% endblock %} -
- - {% block detail_content %} - {# TODO replace fieldset for something semantically correct, but with similar visual grouping style #} - {% for fieldset in view.fieldsets %} -
- {{ fieldset.legend }} - {% for row in fieldset.rows %} -
- {% for column in row %} -
-
- {# TODO replace labels, probably (are they correct here?) #} -

{{ column.text }}

-
-
- {% endfor %} -
- {% endfor %} -
- {% endfor %} - {% endblock detail_content %} - -{% endblock base_content %} diff --git a/templates/compilacao/list.html b/templates/compilacao/list.html deleted file mode 100644 index 97a1aa34b..000000000 --- a/templates/compilacao/list.html +++ /dev/null @@ -1,82 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} - -{% block base_content %} - -{# FIXME is this the best markup to use? #} - - -{% if not rows %} -

{{ NO_ENTRIES_MSG }}

-{% else %} - - - - {% for name in headers %} - - {% endfor %} - - - - {% for value_list in rows %} - - {% for value, href in value_list %} - - {% endfor %} - - {% endfor %} - -
{{ name }}
- {% if href %} - {{ value }} - {% else %} - {{ value }} - {% endif %} -
-{% endif %} - - -{% if is_paginated %} -
- -
-{% endif %} -{% endblock %} diff --git a/templates/compilacao/publicacao_detail.html b/templates/compilacao/publicacao_detail.html index 919ccc038..c4b3e8991 100644 --- a/templates/compilacao/publicacao_detail.html +++ b/templates/compilacao/publicacao_detail.html @@ -1,4 +1,5 @@ {% extends "base.html" %} {% load i18n %} {% load compilacao_filters %} +{% load common_tags %} {% block base_content %} {# FIXME is this the best markup to use? #}
diff --git a/templates/compilacao/publicacao_list.html b/templates/compilacao/publicacao_list.html index 386712f20..f0472f4ce 100644 --- a/templates/compilacao/publicacao_list.html +++ b/templates/compilacao/publicacao_list.html @@ -1,12 +1,13 @@ {% extends "base.html" %} {% load i18n %} {% load compilacao_filters %} +{% load common_tags %} {% block base_content %}