diff --git a/compilacao/forms.py b/compilacao/forms.py index 68d74191e..e98b5cc53 100644 --- a/compilacao/forms.py +++ b/compilacao/forms.py @@ -1,18 +1,25 @@ -from crispy_forms.bootstrap import FieldWithButtons, FormActions, StrictButton +from datetime import timedelta + +from crispy_forms.bootstrap import (Alert, FieldWithButtons, FormActions, + InlineRadios, StrictButton) from crispy_forms.helper import FormHelper from crispy_forms.layout import (HTML, Button, Column, Div, Field, Fieldset, Layout, Row) from django import forms -from django.core.exceptions import NON_FIELD_ERRORS +from django.core.exceptions import NON_FIELD_ERRORS, ValidationError +from django.forms.forms import Form from django.forms.models import ModelForm +from django.template import defaultfilters from django.utils.translation import ugettext_lazy as _ from compilacao.models import (NOTAS_PUBLICIDADE_CHOICES, PARTICIPACAO_SOCIAL_CHOICES, Dispositivo, Nota, - Publicacao, TextoArticulado, TipoNota, - TipoPublicacao, TipoTextoArticulado, TipoVide, - VeiculoPublicacao, Vide) + Publicacao, TextoArticulado, TipoDispositivo, + TipoNota, TipoPublicacao, TipoTextoArticulado, + TipoVide, VeiculoPublicacao, Vide) +from compilacao.utils import DISPOSITIVO_SELECT_RELATED from crispy_layout_mixin import SaplFormLayout, to_column, to_row +from sapl import utils from sapl.utils import YES_NO_CHOICES error_messages = { @@ -136,6 +143,17 @@ class TaForm(ModelForm): ) super(TaForm, self).__init__(*args, **kwargs) + instance = getattr(self, 'instance', None) + if instance and instance.pk: + self.fields['tipo_ta'].widget.attrs['disabled'] = True + self.fields['tipo_ta'].required = False + + def clean_tipo_ta(self): + instance = getattr(self, 'instance', None) + if instance and instance.pk: + return instance.tipo_ta + else: + return self.cleaned_data['tipo_ta'] class NotaForm(ModelForm): @@ -250,39 +268,24 @@ class VideForm(ModelForm): dispositivo_base = forms.ModelChoiceField( queryset=Dispositivo.objects.all(), widget=forms.HiddenInput()) - dispositivo_ref = forms.ModelChoiceField( - queryset=Dispositivo.objects.all(), - widget=forms.HiddenInput()) - tipo_ta = forms.ModelChoiceField( - label=_('Tipo do Texto Articulado'), - queryset=TipoTextoArticulado.objects.all(), - required=False) - - tipo_model = forms.ChoiceField( - choices=[], - label=_('Tipos de...'), required=False) - - num_ta = forms.IntegerField( - label=_('Núm Texto Articulado'), required=False) - ano_ta = forms.IntegerField( - label=_('Ano Texto Articulado'), required=False) + dispositivo_ref = forms.ModelChoiceField( + label=Vide._meta.get_field( + 'dispositivo_ref').verbose_name, + queryset=Dispositivo.objects.all()) - texto = forms.CharField( - label='', - widget=forms.Textarea, - required=False) tipo = forms.ModelChoiceField( label=TipoVide._meta.verbose_name, queryset=TipoVide.objects.all(), required=True, error_messages=error_messages) + texto = forms.CharField( + required=False, + label=Vide._meta.get_field( + 'texto').verbose_name, + widget=forms.Textarea()) - busca_dispositivo = forms.CharField( - label=_('Buscar Dispositivo a Referenciar'), - required=False) - pk = forms.IntegerField(widget=forms.HiddenInput(), - required=False) + pk = forms.IntegerField(widget=forms.HiddenInput(), required=False) class Meta: model = Vide @@ -309,56 +312,47 @@ class VideForm(ModelForm): css_class='btn-primary pull-right') ) - fields_form = Div( + dispositivo_ref = Field( + 'dispositivo_ref', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_ref', + data_type_selection='radio', + template="compilacao/layout/dispositivo_radio.html") + + fields_form = [] + fields_form.append(Div( Row(to_column((Field( 'tipo', placeholder=_('Selecione um Tipo de Vide')), 12))), + Row(to_column((dispositivo_ref, 12))), + Row(to_column((buttons, 12))))) + + fields_form.append(Div( Row(to_column((Field( 'texto', - placeholder=_('Texto Adicional ao Vide')), 12))), - Row(to_column((buttons, 12)))) - - fields_search = Div( - Row( - to_column(('tipo_ta', 6)), - to_column(('tipo_model', 6))), - Row( - to_column(('num_ta', 6)), - to_column(('ano_ta', 6))), - Row(to_column((FieldWithButtons( - Field( - 'busca_dispositivo', - placeholder=_('Digite palavras, letras, ' - 'números ou algo' - ' que estejam ' - 'no rótulo ou no texto.')), - StrictButton("Buscar", css_class='btn-busca')), 12))), - Row(to_column( - (Div(css_class='container-busca'), 12))) - ) + placeholder=_('Texto Adicional ao Vide')), 12))))) self.helper = FormHelper() self.helper.layout = Layout( Div( Div(HTML(_('Vides')), css_class='panel-heading'), Div( - to_column(( - fields_form, 4)), - to_column(( - fields_search, 8)), css_class="panel-body" + to_column((fields_form[0], 6)), + to_column((fields_form[1], 6)), + css_class="panel-body" ), css_class="panel panel-primary" ) ) - if 'choice_model_type_foreignkey_in_extenal_views' in kwargs: - ch = kwargs.pop('choice_model_type_foreignkey_in_extenal_views') - if 'data' in kwargs: - choice = ch(kwargs['data']['tipo_ta']) - self.base_fields['tipo_model'].choices = choice - super(VideForm, self).__init__(*args, **kwargs) + self.fields['dispositivo_ref'].choices = [] + if self.instance and self.instance.dispositivo_ref_id: + self.fields['dispositivo_ref'].choices = [ + (self.instance.dispositivo_ref.pk, + self.instance.dispositivo_ref)] + class PublicacaoForm(ModelForm): @@ -451,3 +445,632 @@ class PublicacaoForm(ModelForm): super(PublicacaoForm, self).__init__(*args, **kwargs) pass + + +class DispositivoIntegerField(forms.IntegerField): + + def __init__(self, field_name=None, *args, **kwargs): + + if 'required' not in kwargs: + kwargs.setdefault('required', False) + + self.widget = forms.NumberInput( + attrs={'title': Dispositivo._meta.get_field( + field_name).verbose_name, + 'onchange': 'atualizaRotulo()'}) + + super(DispositivoIntegerField, self).__init__( + min_value=0, *args, **kwargs) + + +class DispositivoEdicaoBasicaForm(ModelForm): + + class Meta: + model = Dispositivo + fields = [] + + error_messages = { + NON_FIELD_ERRORS: { + 'unique_together': + _("Já existe um Dispositivo com características idênticas."), + } + } + + def __init__(self, *args, **kwargs): + + layout = [] + + inst = kwargs['instance'] if 'instance' in kwargs else None + + if inst and inst.tipo_dispositivo.formato_variacao0 in [ + TipoDispositivo.FNC8, TipoDispositivo.FNCN]: + if 'rotulo' in DispositivoEdicaoBasicaForm.Meta.fields: + DispositivoEdicaoBasicaForm.Meta.fields.remove('rotulo') + for i in range(6): + DispositivoEdicaoBasicaForm.Meta.fields.remove( + 'dispositivo%s' % i) + else: + if 'rotulo' not in DispositivoEdicaoBasicaForm.Meta.fields: + DispositivoEdicaoBasicaForm.Meta.fields.append('rotulo') + for i in range(6): + DispositivoEdicaoBasicaForm.Meta.fields.append( + 'dispositivo%s' % i) + # adiciona campos de rótulo no formulário + self.dispositivo0 = forms.IntegerField( + min_value=0, + label=Dispositivo._meta.get_field('dispositivo0').verbose_name, + widget=forms.NumberInput( + attrs={'title': _('Valor 0(zero) é permitido apenas para ' + 'Dispositivos com tipos variáveis.'), + 'onchange': 'atualizaRotulo()'})) + self.dispositivo1 = DispositivoIntegerField( + label=('1ª %s' % _('Variação')), + field_name='dispositivo1') + self.dispositivo2 = DispositivoIntegerField( + label=('2ª'), + field_name='dispositivo2') + self.dispositivo3 = DispositivoIntegerField( + label=('3ª'), + field_name='dispositivo3') + self.dispositivo4 = DispositivoIntegerField( + label=('4ª'), + field_name='dispositivo4') + self.dispositivo5 = DispositivoIntegerField( + label=('5ª'), + field_name='dispositivo5') + + self.rotulo = forms.CharField( + required=False, label=_('Rótulo Resultante')) + + rotulo_fieldset = to_row([ + ('dispositivo0', 3), + ('dispositivo1', 2), + ('dispositivo2', 1), + ('dispositivo3', 1), + ('dispositivo4', 1), + ('dispositivo5', 1), + ('rotulo', 3)]) + + layout.append(Fieldset(_('Construção do Rótulo'), rotulo_fieldset, + css_class="col-md-12")) + + if inst and inst.tipo_dispositivo.dispositivo_de_articulacao: + if 'texto' in DispositivoEdicaoBasicaForm.Meta.fields: + DispositivoEdicaoBasicaForm.Meta.fields.remove('texto') + else: + if 'texto' not in DispositivoEdicaoBasicaForm.Meta.fields: + DispositivoEdicaoBasicaForm.Meta.fields.append('texto') + + self.texto = forms.CharField(required=False, + label='', + widget=forms.Textarea()) + row_texto = to_row([('texto', 12)]) + layout.append( + Fieldset(Dispositivo._meta.get_field('texto').verbose_name, + row_texto, + css_class="col-md-12")) + + fields = DispositivoEdicaoBasicaForm.Meta.fields + if fields: + self.base_fields.clear() + for f in fields: + self.base_fields.update({f: getattr(self, f)}) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + *layout, + label_cancel=_('Retornar para o Editor Sequencial')) + + super(DispositivoEdicaoBasicaForm, self).__init__(*args, **kwargs) + + +class DispositivoSearchModalForm(Form): + + tipo_ta = forms.ModelChoiceField( + label=_('Tipo do Texto Articulado'), + queryset=TipoTextoArticulado.objects.all(), + required=False) + + tipo_model = forms.ChoiceField( + choices=[], + label=_('Tipos de...'), required=False) + + num_ta = forms.IntegerField( + label=_('Número do Documento'), required=False) + ano_ta = forms.IntegerField( + label=_('Ano do Documento'), required=False) + + dispositivos_internos = forms.ChoiceField( + label=_('Dispositivos Internos?'), + choices=utils.YES_NO_CHOICES, + widget=forms.RadioSelect(), + required=False) + + max_results = forms.ChoiceField( + label=_('Limite de Listagem'), + choices=[(10, _('Dez Dispositivos')), + (30, _('Trinta Dispositivos')), + (50, _('Cinquenta Dispositivos')), + (0, _('Tudo que atender aos Critérios da Busca'))], + widget=forms.Select(), + required=False) + + rotulo_dispositivo = forms.CharField( + label=_('Rótulo'), + required=False) + + texto_dispositivo = forms.CharField( + label=_('Pesquisa Textual'), + required=False) + + def __init__(self, *args, **kwargs): + + fields_search = Fieldset( + _('Busca por um Dispositivo'), + Row( + to_column(('num_ta', 4)), + to_column(('ano_ta', 4)), + to_column(('max_results', 4))), + Row( + to_column(('tipo_ta', 6)), + to_column(('tipo_model', 6))), + Row(to_column((InlineRadios('dispositivos_internos'), 3)), + to_column(('rotulo_dispositivo', 2)), + to_column((FieldWithButtons( + Field( + 'texto_dispositivo', + placeholder=_('Digite palavras, letras, ' + 'números ou algo' + ' que estejam no texto.')), + StrictButton(_('Buscar'), css_class='btn-busca')), 7)) + ) + ) + + self.helper = FormHelper() + self.helper.layout = Layout( + fields_search, + Row(to_column((Div(css_class='result-busca-dispositivo'), 12)))) + + if 'choice_model_type_foreignkey_in_extenal_views' in kwargs: + ch = kwargs.pop('choice_model_type_foreignkey_in_extenal_views') + if 'data' in kwargs: + choice = ch(kwargs['data']['tipo_ta']) + self.base_fields['tipo_model'].choices = choice + elif 'instance' in kwargs and\ + isinstance(kwargs['instance'], Dispositivo): + choice = ch(kwargs['instance'].ta.tipo_ta_id) + self.base_fields['tipo_model'].choices = choice + + kwargs['initial'].update({'dispositivos_internos': False}) + super(DispositivoSearchModalForm, self).__init__(*args, **kwargs) + + +class DispositivoEdicaoVigenciaForm(ModelForm): + inconstitucionalidade = forms.ChoiceField( + label=Dispositivo._meta.get_field( + 'inconstitucionalidade').verbose_name, + choices=utils.YES_NO_CHOICES, + widget=forms.RadioSelect()) + + dispositivo_vigencia = forms.ModelChoiceField( + label=Dispositivo._meta.get_field( + 'dispositivo_vigencia').verbose_name, + required=False, + queryset=Dispositivo.objects.all()) + + extensao = forms.ChoiceField( + label=_('Extender a seleção abaixo como Dispositivo de Vigência ' + 'para todos dependentes originais ' + 'deste Dispositivo em edição?'), + choices=utils.YES_NO_CHOICES, + widget=forms.RadioSelect(), + required=False) + + class Meta: + model = Dispositivo + fields = ['inicio_vigencia', + 'fim_vigencia', + 'inicio_eficacia', + 'fim_eficacia', + 'publicacao', + 'inconstitucionalidade', + 'dispositivo_vigencia' + ] + + error_messages = { + NON_FIELD_ERRORS: { + 'unique_together': + _("Já existe um Dispositivo com características idênticas."), + } + } + + def __init__(self, *args, **kwargs): + + layout = [] + + row_publicacao = to_row([ + ('publicacao', 6), + (InlineRadios('inconstitucionalidade'), 3), ]) + row_publicacao.fields.append( + Alert( + css_class='alert-info col-md-3', + content='%s %s' % ( + _('Dica!'), _('Inclua uma Nota de Dispositivo informando ' + 'sobre a Inconstitucionalidade.')))) + layout.append( + Fieldset(_('Registro de Publicação e Validade'), + row_publicacao, + css_class="col-md-12")) + + row_datas = to_row([ + ('inicio_vigencia', 3), + ('fim_vigencia', 3), + ('inicio_eficacia', 3), + ('fim_eficacia', 3), ]) + layout.append( + Fieldset(_('Datas de Controle de Vigência'), + row_datas, + css_class="col-md-12")) + + row_vigencia = Field( + 'dispositivo_vigencia', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_vigencia', + data_type_selection='radio', + template="compilacao/layout/dispositivo_radio.html") + layout.append( + Fieldset(_('Dispositivo de Vigência'), + to_row([(InlineRadios('extensao'), 12)]), + row_vigencia, + css_class="col-md-12")) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + *layout, + label_cancel=_('Retornar para o Editor Sequencial')) + + super(DispositivoEdicaoVigenciaForm, self).__init__(*args, **kwargs) + + pubs = Publicacao.objects.order_by( + '-data', '-hora').filter(ta=self.instance.ta) + self.fields['publicacao'].choices = [("", "---------")] + [( + p.pk, _('%s realizada em %s') % ( + p.tipo_publicacao, + defaultfilters.date( + p.data, "d \d\e F \d\e Y"))) for p in pubs] + + dvs = Dispositivo.objects.order_by('ordem').filter( + pk=self.instance.dispositivo_vigencia_id) + self.fields['dispositivo_vigencia'].choices = [(d.pk, d) for d in dvs] + + def clean_dispositivo_vigencia(self): + dv = self.cleaned_data['dispositivo_vigencia'] + + if dv and dv.is_relative_auto_insert(): + dv = dv.dispositivo_pai + + return dv + + def save(self): + super(DispositivoEdicaoVigenciaForm, self).save() + + data = self.cleaned_data + + extensao = 'extensao' in data and data['extensao'] == 'True' + + if extensao: + dv = data['dispositivo_vigencia'] + + if dv and dv.is_relative_auto_insert(): + dv = dv.dispositivo_pai + + dv_pk = dv.pk if dv else None + instance = self.instance + + def extenderPara(dpt_pk): + + Dispositivo.objects.filter( + dispositivo_pai_id=dpt_pk, + ta_publicado__isnull=True).update( + dispositivo_vigencia_id=dv_pk) + + filhos = Dispositivo.objects.filter( + dispositivo_pai_id=dpt_pk).values_list('pk', flat=True) + + for d in filhos: + extenderPara(d) + + extenderPara(instance.pk) + + +class MultipleChoiceWithoutValidationField(forms.MultipleChoiceField): + + def validate(self, value): + if self.required and not value: + raise ValidationError( + self.error_messages['required'], code='required') + + +class DispositivoDefinidorVigenciaForm(Form): + + dispositivo_vigencia = MultipleChoiceWithoutValidationField( + label=Dispositivo._meta.get_field( + 'dispositivo_vigencia').verbose_name, + required=False) + + def __init__(self, *args, **kwargs): + + layout = [] + + row_vigencia = Field( + 'dispositivo_vigencia', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_vigencia', + data_type_selection='checkbox', + template="compilacao/layout/dispositivo_checkbox.html") + layout.append( + Fieldset(_('Definidor de Vigência dos Dispositívos abaixo'), + row_vigencia, + css_class="col-md-12")) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + *layout, + label_cancel=_('Retornar para o Editor Sequencial')) + + pk = kwargs.pop('pk') + super(DispositivoDefinidorVigenciaForm, self).__init__(*args, **kwargs) + + dvs = Dispositivo.objects.order_by('ta', 'ordem').filter( + dispositivo_vigencia_id=pk).select_related( + *DISPOSITIVO_SELECT_RELATED) + self.initial['dispositivo_vigencia'] = [d.pk for d in dvs] + + TA_TA_PUB = 'ta_id', 'ta_publicado_id' + tas = Dispositivo.objects.order_by( + *TA_TA_PUB).filter(dispositivo_vigencia_id=pk).distinct( + *TA_TA_PUB).values_list( + *TA_TA_PUB) + + tas = list(set().union(*list(map(list, zip(*tas))))) + + if not tas: + tas = Dispositivo.objects.filter(pk=pk).values_list('ta_id') + + dvs = Dispositivo.objects.order_by( + '-ta__data', '-ta__ano', '-ta__numero', 'ta', 'ordem').filter( + ta__in=tas).select_related(*DISPOSITIVO_SELECT_RELATED) + self.fields['dispositivo_vigencia'].choices = [ + (d.pk, d) + for d in dvs + if d.pk in self.initial['dispositivo_vigencia']] + + +class DispositivoEdicaoAlteracaoForm(ModelForm): + + class Meta: + model = Dispositivo + fields = [ + 'dispositivo_atualizador', + 'dispositivo_substituido', + 'dispositivo_subsequente', + ] + error_messages = { + NON_FIELD_ERRORS: { + 'unique_together': + _("Já existe um Dispositivo com características idênticas."), + } + } + + def __init__(self, *args, **kwargs): + + layout = [] + + self.dispositivo_substituido = forms.ModelChoiceField( + label=Dispositivo._meta.get_field( + 'dispositivo_substituido').verbose_name, + required=False, + queryset=Dispositivo.objects.all()) + self.dispositivo_subsequente = forms.ModelChoiceField( + label=Dispositivo._meta.get_field( + 'dispositivo_subsequente').verbose_name, + required=False, + queryset=Dispositivo.objects.all()) + self.dispositivo_atualizador = forms.ModelChoiceField( + label=Dispositivo._meta.get_field( + 'dispositivo_atualizador').verbose_name, + required=False, + queryset=Dispositivo.objects.all()) + + substituido = Field( + 'dispositivo_substituido', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_substituido', + data_type_selection='radio', + template="compilacao/layout/dispositivo_radio.html") + subsequente = Field( + 'dispositivo_subsequente', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_subsequente', + data_type_selection='radio', + template="compilacao/layout/dispositivo_radio.html") + alterador = Field( + 'dispositivo_atualizador', + data_sapl_ta='DispositivoSearch', + data_field='dispositivo_atualizador', + data_type_selection='radio', + data_function='alterador', + template="compilacao/layout/dispositivo_radio.html") + + layout.append( + to_row([ + (Fieldset(_('Dispositivo Subsitituido'), substituido), 6), + (Fieldset(_('Dispositivo Subsequente'), subsequente), 6)])) + + layout.append( + Fieldset( + _('Dispositivo Alterador'), + Div(alterador), + css_class="col-md-12")) + + inst = kwargs['instance'] if 'instance' in kwargs else None + if inst and inst.tipo_dispositivo.dispositivo_de_articulacao: + if 'texto_atualizador' in\ + DispositivoEdicaoAlteracaoForm.Meta.fields: + DispositivoEdicaoAlteracaoForm.Meta.fields.remove( + 'texto_atualizador') + DispositivoEdicaoAlteracaoForm.Meta.fields.remove( + 'visibilidade') + else: + if 'texto_atualizador' not in\ + DispositivoEdicaoAlteracaoForm.Meta.fields: + DispositivoEdicaoAlteracaoForm.Meta.fields.append( + 'texto_atualizador') + DispositivoEdicaoAlteracaoForm.Meta.fields.append( + 'visibilidade') + + self.texto_atualizador = forms.CharField(required=False, + label='', + widget=forms.Textarea()) + self.visibilidade = forms.ChoiceField( + label=Dispositivo._meta.get_field( + 'visibilidade').verbose_name, + choices=utils.YES_NO_CHOICES, + widget=forms.RadioSelect()) + + layout.append( + Fieldset(Dispositivo._meta.get_field( + 'texto_atualizador').verbose_name, + to_row([(InlineRadios('visibilidade'), 12)]), + to_row([('texto_atualizador', 12)]), + css_class="col-md-12")) + + fields = DispositivoEdicaoAlteracaoForm.Meta.fields + if fields: + self.base_fields.clear() + for f in fields: + if hasattr(self, f): + self.base_fields.update({f: getattr(self, f)}) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + *layout, + label_cancel=_('Retornar para o Editor Sequencial')) + + super(DispositivoEdicaoAlteracaoForm, self).__init__(*args, **kwargs) + + self.fields['dispositivo_substituido'].choices = [] + self.fields['dispositivo_subsequente'].choices = [] + self.fields['dispositivo_atualizador'].choices = [] + if inst.dispositivo_substituido: + self.fields['dispositivo_substituido'].choices = [ + (inst.dispositivo_substituido.pk, + inst.dispositivo_substituido)] + + if inst.dispositivo_subsequente: + self.fields['dispositivo_subsequente'].choices = [ + (inst.dispositivo_subsequente.pk, + inst.dispositivo_subsequente)] + + if inst.dispositivo_atualizador: + self.fields['dispositivo_atualizador'].choices = [ + (inst.dispositivo_atualizador.pk, + inst.dispositivo_atualizador)] + + def clean_dispositivo_substituido(self): + dst = self.cleaned_data['dispositivo_substituido'] + + if dst and dst.ta != self.instance.ta: + raise ValidationError(_('Não é permitido selecionar um ' + 'Dispositivo de outro Texto Articulado.')) + if dst and dst.tipo_dispositivo != self.instance.tipo_dispositivo: + raise ValidationError(_('Não é permitido selecionar um ' + 'Dispositivo de outro Tipo.')) + return dst + + def clean_dispositivo_subsequente(self): + dsq = self.cleaned_data['dispositivo_subsequente'] + + if dsq and dsq.ta != self.instance.ta: + raise ValidationError(_('Não é permitido selecionar um ' + 'Dispositivo de outro Texto Articulado.')) + if dsq and dsq.tipo_dispositivo != self.instance.tipo_dispositivo: + raise ValidationError(_('Não é permitido selecionar um ' + 'Dispositivo de outro Tipo.')) + return dsq + + def clean_dispositivo_atualizador(self): + da = self.cleaned_data['dispositivo_atualizador'] + + if da and not da.tipo_dispositivo.dispositivo_de_alteracao and\ + not da.tipo_dispositivo.dispositivo_de_articulacao: + raise ValidationError(_('O Dispositivo de Atualização selecionado ' + 'não é um Bloco de Alteração.')) + return da + + def clean(self): + data = self.cleaned_data + ndst = data['dispositivo_substituido'] + nda = data['dispositivo_atualizador'] + + if not nda and ndst: + raise ValidationError(_('Não é permitido substituir um ' + 'Dispositivo sem haver um ' + 'Dispositivo Alterador.')) + + def save(self): + data = self.cleaned_data + + od = Dispositivo.objects.get(pk=self.instance.pk) + + nd = self.instance + ndst = data['dispositivo_substituido'] + ndsq = data['dispositivo_subsequente'] + nda = data['dispositivo_atualizador'] + + if ndst != od.dispositivo_substituido: + if od.dispositivo_substituido: + odst = od.dispositivo_substituido + + odst.dispositivo_subsequente = None + odst.fim_vigencia = None + odst.fim_eficacia = None + odst.save() + + if ndst: + if ndst.dispositivo_subsequente: + ndst.dispositivo_subsequente.dispositivo_substituido = None + ndst.dispositivo_subsequente.save() + + ndst.dispositivo_subsequente = nd + ndst.fim_vigencia = nd.inicio_vigencia - timedelta(days=1) + ndst.fim_eficacia = nd.inicio_eficacia - timedelta(days=1) + ndst.save() + + if ndsq != od.dispositivo_subsequente: + if od.dispositivo_subsequente: + odsq = od.dispositivo_subsequente + + odsq.dispositivo_substituido = None + odsq.save() + + if ndsq: + if ndsq.dispositivo_substituido: + ndsq.dispositivo_substituido.dispositivo_subsequente = None + ndsq.dispositivo_substituido.fim_vigencia = None + ndsq.dispositivo_substituido.fim_eficacia = None + ndsq.dispositivo_substituido.save() + + ndsq.dispositivo_substituido = nd + ndsq.save() + + nd.ta_publicado = nda.ta if nda else None + + super(DispositivoEdicaoAlteracaoForm, self).save() + + if nd.dispositivo_subsequente: + nd.fim_vigencia = nd.dispositivo_subsequente.inicio_vigencia - \ + timedelta(days=1) + nd.fim_eficacia = nd.dispositivo_subsequente.inicio_eficacia - \ + timedelta(days=1) + nd.save() diff --git a/compilacao/migrations/0045_auto_20160311_1117.py b/compilacao/migrations/0045_auto_20160311_1117.py new file mode 100644 index 000000000..07645df62 --- /dev/null +++ b/compilacao/migrations/0045_auto_20160311_1117.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-11 14:17 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0044_auto_20160307_0918'), + ] + + operations = [ + migrations.AlterField( + model_name='dispositivo', + name='dispositivo_subsequente', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='compilacao.Dispositivo', verbose_name='Dispositivo Subsequente'), + ), + migrations.AlterField( + model_name='dispositivo', + name='dispositivo_substituido', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='compilacao.Dispositivo', verbose_name='Dispositivo Substituido'), + ), + migrations.AlterField( + model_name='dispositivo', + name='inconstitucionalidade', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Declaração de Inconstitucionalidade'), + ), + migrations.AlterField( + model_name='dispositivo', + name='texto', + field=models.TextField(blank=True, default='', verbose_name='Texto na Norma Original'), + ), + ] diff --git a/compilacao/migrations/0046_auto_20160319_1542.py b/compilacao/migrations/0046_auto_20160319_1542.py new file mode 100644 index 000000000..c3fc05c14 --- /dev/null +++ b/compilacao/migrations/0046_auto_20160319_1542.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-19 18:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0045_auto_20160311_1117'), + ] + + operations = [ + migrations.AlterField( + model_name='dispositivo', + name='inconstitucionalidade', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Declarado Inconstitucional'), + ), + migrations.AlterField( + model_name='dispositivo', + name='texto', + field=models.TextField(blank=True, default='', verbose_name='Texto Original'), + ), + ] diff --git a/compilacao/migrations/0047_auto_20160330_0027.py b/compilacao/migrations/0047_auto_20160330_0027.py new file mode 100644 index 000000000..920c70c2a --- /dev/null +++ b/compilacao/migrations/0047_auto_20160330_0027.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-30 03:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0046_auto_20160319_1542'), + ] + + operations = [ + migrations.AlterField( + model_name='dispositivo', + name='dispositivo_vigencia', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dispositivos_vigencias_set', to='compilacao.Dispositivo', verbose_name='Dispositivo de Vigência'), + ), + ] diff --git a/compilacao/migrations/0048_auto_20160404_2309.py b/compilacao/migrations/0048_auto_20160404_2309.py new file mode 100644 index 000000000..9c25badfc --- /dev/null +++ b/compilacao/migrations/0048_auto_20160404_2309.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-04-05 02:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0047_auto_20160330_0027'), + ] + + operations = [ + migrations.AlterField( + model_name='dispositivo', + name='dispositivo_vigencia', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dispositivos_vigencias_set', to='compilacao.Dispositivo', verbose_name='Dispositivo de Vigência'), + ), + ] diff --git a/compilacao/migrations/0049_merge.py b/compilacao/migrations/0049_merge.py new file mode 100644 index 000000000..beaedb5ff --- /dev/null +++ b/compilacao/migrations/0049_merge.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-04-06 17:43 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0045_auto_20160404_1411'), + ('compilacao', '0048_auto_20160404_2309'), + ] + + operations = [ + ] diff --git a/compilacao/models.py b/compilacao/models.py index 8ce953a98..90a3c5c35 100644 --- a/compilacao/models.py +++ b/compilacao/models.py @@ -1,4 +1,3 @@ -from datetime import datetime from django.contrib.auth.models import User from django.contrib.contenttypes.fields import GenericForeignKey @@ -59,6 +58,17 @@ class BaseModel(models.Model): self.__class__, tuple(unique_fields)) raise ValidationError(msg) + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None, clean=True): + if clean: + self.clean() + return models.Model.save( + self, + force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + class TipoTextoArticulado(models.Model): sigla = models.CharField(max_length=3, verbose_name=_('Sigla')) @@ -124,6 +134,24 @@ class TextoArticulado(TimestampedMixin): 'numero': self.numero, 'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")} + def organizar_ordem_de_dispositivos(self): + + dpts = Dispositivo.objects.filter(ta=self) + + if not dpts.exists(): + return + + ordem_max = dpts.last().ordem + dpts.update(ordem=F('ordem') + ordem_max) + + dpts = Dispositivo.objects.filter( + ta=self).values_list('pk', flat=True).order_by('ordem') + + count = 0 + for d in dpts: + count += Dispositivo.INTERVALO_ORDEM + Dispositivo.objects.filter(pk=d).update(ordem=count) + class TipoNota(models.Model): sigla = models.CharField( @@ -532,7 +560,7 @@ class Dispositivo(BaseModel, TimestampedMixin): texto = models.TextField( blank=True, default='', - verbose_name=_('Texto')) + verbose_name=_('Texto Original')) texto_atualizador = models.TextField( blank=True, default='', @@ -551,7 +579,7 @@ class Dispositivo(BaseModel, TimestampedMixin): inconstitucionalidade = models.BooleanField( default=False, choices=utils.YES_NO_CHOICES, - verbose_name=_('Inconstitucionalidade')) + verbose_name=_('Declarado Inconstitucional')) # Relevant attribute only in altering norms visibilidade = models.BooleanField( default=False, @@ -581,11 +609,13 @@ class Dispositivo(BaseModel, TimestampedMixin): 'self', blank=True, null=True, default=None, related_name='+', + on_delete=models.SET_NULL, verbose_name=_('Dispositivo Subsequente')) dispositivo_substituido = models.ForeignKey( 'self', blank=True, null=True, default=None, related_name='+', + on_delete=models.SET_NULL, verbose_name=_('Dispositivo Substituido')) dispositivo_pai = models.ForeignKey( 'self', @@ -595,7 +625,8 @@ class Dispositivo(BaseModel, TimestampedMixin): dispositivo_vigencia = models.ForeignKey( 'self', blank=True, null=True, default=None, - related_name='+', + on_delete=models.SET_NULL, + related_name='dispositivos_vigencias_set', verbose_name=_('Dispositivo de Vigência')) dispositivo_atualizador = models.ForeignKey( 'self', @@ -702,10 +733,26 @@ class Dispositivo(BaseModel, TimestampedMixin): 'º' if self.tipo_dispositivo. rotulo_ordinal >= 0 else '',) + elif irmaos_mesmo_tipo.count() == 1 and\ + irmaos_mesmo_tipo[0].dispositivo0 == 0 and\ + self.dispositivo0 == 1: + irmao = irmaos_mesmo_tipo[0] + irmao.dispositivo0 = 1 + rr = prefixo[0] + rr += irmao.get_nomenclatura_completa() + irmao.rotulo = rr + t.rotulo_sufixo_texto + irmao.save() + r += prefixo[0] + + self.dispositivo0 = 2 + r += self.get_nomenclatura_completa() + else: r += prefixo[0] r += self.get_nomenclatura_completa() else: + if self.dispositivo0 == 0: + self.dispositivo0 = 1 r += prefixo[0] r += self.get_nomenclatura_completa() @@ -777,6 +824,27 @@ class Dispositivo(BaseModel, TimestampedMixin): return (flag_direcao, flag_variacao) + def transform_in_prior(self, profundidade=-1): + numero = self.get_numero_completo() + + numero.reverse() + + if profundidade != -1: + profundidade = len(numero) - profundidade - 1 + + for i in range(len(numero)): + if not numero[i]: + continue + + if i > profundidade: + continue + + numero[i] -= 1 + break + + numero.reverse() + self.set_numero_completo(numero) + def set_numero_completo(self, *numero): numero = numero[0] self.dispositivo0 = numero[0] @@ -913,7 +981,7 @@ class Dispositivo(BaseModel, TimestampedMixin): def get_parents(self, ordem='desc'): dp = self p = [] - while dp.dispositivo_pai is not None: + while dp.dispositivo_pai: dp = dp.dispositivo_pai if ordem == 'desc': p.append(dp) @@ -925,7 +993,7 @@ class Dispositivo(BaseModel, TimestampedMixin): def get_parents_asc(self): return self.get_parents(ordem='asc') - def incrementar_irmaos(self, variacao=0, tipoadd=[]): + def incrementar_irmaos(self, variacao=0, tipoadd=[], force=True): if not self.tipo_dispositivo.contagem_continua: irmaos = list(Dispositivo.objects.filter( @@ -956,6 +1024,10 @@ class Dispositivo(BaseModel, TimestampedMixin): dp_profundidade = self.get_profundidade() + if (not force and not variacao and len(irmaos) > 0 and + irmaos[0].get_numero_completo() > self.get_numero_completo()): + return + irmaos_a_salvar = [] ultimo_irmao = None for irmao in irmaos: @@ -1029,15 +1101,18 @@ class Dispositivo(BaseModel, TimestampedMixin): irmao.save() def get_proximo_nivel_zero(self): - proxima_articulacao = Dispositivo.objects.filter( + proxima_articulacao = Dispositivo.objects.order_by('ordem').filter( ordem__gt=self.ordem, nivel=0, - ta_id=self.ta_id)[:1] - - if not proxima_articulacao.exists(): - return None + ta_id=self.ta_id).first() + return proxima_articulacao - return proxima_articulacao[0] + def get_nivel_zero_anterior(self): + anterior_articulacao = Dispositivo.objects.order_by('ordem').filter( + ordem__lt=self.ordem, + nivel=0, + ta_id=self.ta_id).last() + return anterior_articulacao def is_relative_auto_insert(self, perfil_pk=None): if self.dispositivo_pai is not None: @@ -1076,10 +1151,20 @@ class Dispositivo(BaseModel, TimestampedMixin): dp.texto = '' dp.ta = dispositivo_base.ta dp.dispositivo_pai = dispositivo_base.dispositivo_pai - dp.inicio_eficacia = dispositivo_base.inicio_eficacia - dp.inicio_vigencia = dispositivo_base.inicio_vigencia dp.publicacao = dispositivo_base.publicacao - dp.timestamp = datetime.now() + + dp.dispositivo_vigencia = dispositivo_base.dispositivo_vigencia + if dp.dispositivo_vigencia: + dp.inicio_eficacia = dp.dispositivo_vigencia.inicio_eficacia + dp.inicio_vigencia = dp.dispositivo_vigencia.inicio_vigencia + dp.fim_eficacia = dp.dispositivo_vigencia.fim_eficacia + dp.fim_vigencia = dp.dispositivo_vigencia.fim_vigencia + else: + dp.inicio_eficacia = dispositivo_base.inicio_eficacia + dp.inicio_vigencia = dispositivo_base.inicio_vigencia + dp.fim_eficacia = dispositivo_base.inicio_eficacia + dp.fim_vigencia = dispositivo_base.fim_vigencia + dp.ordem = dispositivo_base.ordem return dp diff --git a/compilacao/templatetags/compilacao_filters.py b/compilacao/templatetags/compilacao_filters.py index ac2d103dc..7a8bd5398 100644 --- a/compilacao/templatetags/compilacao_filters.py +++ b/compilacao/templatetags/compilacao_filters.py @@ -1,9 +1,11 @@ + from django import template from django.core.signing import Signer from django.db.models import Q +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ -from compilacao.models import Dispositivo, TipoDispositivo +from compilacao.models import Dispositivo register = template.Library() @@ -15,13 +17,6 @@ def get_bloco_atualizador(pk_atualizador): Q(dispositivo_atualizador_id=pk_atualizador)).select_related() -@register.filter -def get_tipos_dispositivo(pk_atual): - - return TipoDispositivo.objects.filter( - id__gte=pk_atual) - - @register.simple_tag def dispositivo_desativado(dispositivo, inicio_vigencia, fim_vigencia): if inicio_vigencia and fim_vigencia: @@ -38,17 +33,23 @@ def dispositivo_desativado(dispositivo, inicio_vigencia, fim_vigencia): @register.simple_tag def nota_automatica(dispositivo, ta_pub_list): + if dispositivo.ta_publicado is not None: d = dispositivo.dispositivo_atualizador.dispositivo_pai + + ta_publicado = ta_pub_list[dispositivo.ta_publicado_id] if\ + ta_pub_list else dispositivo.ta_publicado + if dispositivo.texto == Dispositivo.TEXTO_PADRAO_DISPOSITIVO_REVOGADO: return _('Revogado pelo %s - %s.') % ( - d, ta_pub_list[dispositivo.ta_publicado_id]) + d, ta_publicado) elif not dispositivo.dispositivo_substituido_id: return _('Inclusão feita pelo %s - %s.') % ( - d, ta_pub_list[dispositivo.ta_publicado_id]) + d, ta_publicado) else: return _('Alteração feita pelo %s - %s.') % ( - d, ta_pub_list[dispositivo.ta_publicado_id]) + d, ta_publicado) + return '' @@ -60,12 +61,15 @@ def set_nivel_old(view, value): @register.simple_tag def close_div(value_max, value_min, varr): - return '' * (int(value_max) - int(value_min) + 1 + varr) + return mark_safe('' * (int(value_max) - int(value_min) + 1 + varr)) @register.filter def get_sign_vigencia(value): - string = "%s,%s" % (value.inicio_vigencia, value.fim_vigencia) + string = "%s,%s,%s" % ( + value.ta_publicado_id if value.ta_publicado_id else 0, + value.inicio_vigencia, + value.fim_vigencia) signer = Signer() return signer.sign(str(string)) @@ -89,7 +93,7 @@ def isinst(value, class_str): @register.filter def render_actions_head(view, d_atual): - if view.__class__.__name__ != 'DispositivoEditView': + if view.__class__.__name__ != 'DispositivoSimpleEditView': return False # Menu @@ -121,8 +125,96 @@ def nomenclatura(d): result = '(' + d.tipo_dispositivo.nome + ' ' + \ d.rotulo + ')' else: - result = '(' + d.tipo_dispositivo.nome + \ - d.rotulo_padrao() + ')' + r = d.rotulo_padrao() + if r: + r += ' ' + result = '(' + d.tipo_dispositivo.nome + r + ')' + return result + + +def update_dispositivos_parents(dpts_parents, ta_id): + + dpts = Dispositivo.objects.order_by('ordem').filter( + ta_id=ta_id).values_list( + 'pk', 'dispositivo_pai_id', 'rotulo', 'tipo_dispositivo__nome', + 'tipo_dispositivo__rotulo_prefixo_texto') + + for d in dpts: + dpts_parents[str(d[0])] = { + 'd': d, 'p': [], 'h': None} + + def parents(k): + pai = dpts_parents[str(k)]['d'][1] + p = dpts_parents[str(k)]['p'] + if not p: + if pai: + parent_k = [pai, ] + parents(pai) + else: + parent_k = [] + else: + parent_k = p + + return parent_k + + for k in dpts_parents: + dpts_parents[str(k)]['p'] = parents(k) + + +@register.simple_tag +def heranca(request, d, ignore_ultimo=0, ignore_primeiro=0): + ta_dpts_parents = request.session.get('herancas') + + if not ta_dpts_parents: + ta_dpts_parents = {} + + ta_id = str(d.ta_id) + if ta_id not in ta_dpts_parents: + dpts_parents = {} + ta_dpts_parents[ta_id] = dpts_parents + update_dispositivos_parents(dpts_parents, ta_id) + + herancas_fila = request.session.get('herancas_fila') + if not herancas_fila: + herancas_fila = [] + + herancas_fila.append(ta_id) + if len(herancas_fila) > 100: + ta_remove = herancas_fila.pop(0) + del ta_dpts_parents[str(ta_remove)] + + request.session['herancas_fila'] = herancas_fila + request.session['herancas'] = ta_dpts_parents + + d_pk = str(d.pk) + h = ta_dpts_parents[ta_id][d_pk]['h'] + + if h: + return h + + dpts_parents = ta_dpts_parents[ta_id] + parents = dpts_parents[d_pk]['p'] + result = '' + + if parents: + pk_last = parents[-1] + for pk in parents: + + if ignore_ultimo and pk == pk_last: + break + + if ignore_primeiro: + ignore_primeiro = 0 + continue + + p = dpts_parents[str(pk)]['d'] + + if p[4] != '': + result = p[2] + ' ' + result + else: + result = '(' + p[3] + ' ' + \ + p[2] + ')' + ' ' + result + + dpts_parents[d_pk]['h'] = result return result @@ -154,4 +246,10 @@ def nomenclatura_heranca(d, ignore_ultimo=0, ignore_primeiro=0): @register.filter def urldetail_content_type(obj): - return '%s:detail' % obj.content_type.model + return '%s:%s_detail' % ( + obj.content_type.app_label, obj.content_type.model) + + +@register.filter +def list(obj): + return [obj, ] diff --git a/compilacao/urls.py b/compilacao/urls.py index b3329a16d..0a3c93222 100644 --- a/compilacao/urls.py +++ b/compilacao/urls.py @@ -31,12 +31,25 @@ urlpatterns_compilacao = [ views.DispositivoView.as_view(), name='dispositivo'), url(r'^(?P[0-9]+)/text/(?P[0-9]+)/refresh', - views.DispositivoEditView.as_view(), name='dispositivo_edit'), + views.DispositivoSimpleEditView.as_view(), name='dispositivo_refresh'), - url(r'^(?P[0-9]+)/text/(?P[0-9]+)/actions', - views.ActionsEditView.as_view(), name='dispositivo_actions'), + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit$', + views.DispositivoEdicaoBasicaView.as_view(), name='dispositivo_edit'), + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit/vigencia', + views.DispositivoEdicaoVigenciaView.as_view(), + name='dispositivo_edit_vigencia'), + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit/alteracao', + views.DispositivoEdicaoAlteracaoView.as_view(), + name='dispositivo_edit_alteracao'), + + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/edit/definidor_vigencia', + views.DispositivoDefinidorVigenciaView.as_view(), + name='dispositivo_edit_definidor_vigencia'), + + url(r'^(?P[0-9]+)/text/(?P[0-9]+)/actions', + views.ActionsEditView.as_view(), name='dispositivo_actions'), url(r'^(?P[0-9]+)/text/' '(?P[0-9]+)/nota/create$', @@ -62,9 +75,13 @@ urlpatterns_compilacao = [ '(?P[0-9]+)/vide/(?P[0-9]+)/delete$', views.VideDeleteView.as_view(), name='vide_delete'), - url(r'^(?P[0-9]+)/text/search$', + url(r'^search_fragment_form$', views.DispositivoSearchFragmentFormView.as_view(), - name='search_dispositivo'), + name='dispositivo_fragment_form'), + + url(r'^search_form$', + views.DispositivoSearchModalView.as_view(), + name='dispositivo_search_form'), url(r'^(?P[0-9]+)/publicacao$', @@ -94,7 +111,7 @@ urlpatterns_compilacao = [ ] urlpatterns = [ - url(r'^ta/', include(urlpatterns_compilacao, 'compilacao', 'compilacao')), + url(r'^ta/', include(urlpatterns_compilacao)), url(r'^ta/config/tipo-nota/', include(TipoNotaCrud.get_urls())), diff --git a/compilacao/utils.py b/compilacao/utils.py index ca00f66b4..38c55239a 100644 --- a/compilacao/utils.py +++ b/compilacao/utils.py @@ -1,4 +1,17 @@ +DISPOSITIVO_SELECT_RELATED = ( + 'tipo_dispositivo', + 'ta_publicado', + 'ta', + 'dispositivo_atualizador', + 'dispositivo_atualizador__dispositivo_pai', + 'dispositivo_atualizador__dispositivo_pai__ta', + 'dispositivo_atualizador__dispositivo_pai__ta__tipo_ta', + 'dispositivo_pai', + 'dispositivo_pai__tipo_dispositivo', + 'ta_publicado', + 'ta',) + def int_to_roman(int_value): # if isinstance(int_value, type(1)): @@ -18,6 +31,8 @@ def int_to_roman(int_value): def int_to_letter(int_value): result = '' + if not int_value: + return '0' int_value -= 1 while int_value >= 26: rest = int_value % 26 diff --git a/compilacao/views.py b/compilacao/views.py index a331705f1..582c4ca70 100644 --- a/compilacao/views.py +++ b/compilacao/views.py @@ -8,6 +8,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType from django.core.signing import Signer from django.core.urlresolvers import reverse_lazy +from django.db import transaction from django.db.models import Q from django.http.response import (HttpResponse, HttpResponseRedirect, JsonResponse) @@ -17,31 +18,24 @@ from django.utils.decorators import method_decorator from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import TemplateView from django.views.generic.detail import DetailView -from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.edit import (CreateView, DeleteView, FormView, + UpdateView) from django.views.generic.list import ListView -from compilacao.forms import (NotaForm, PublicacaoForm, TaForm, TipoTaForm, - VideForm) +from compilacao.forms import (DispositivoDefinidorVigenciaForm, + DispositivoEdicaoAlteracaoForm, + DispositivoEdicaoBasicaForm, + DispositivoEdicaoVigenciaForm, + DispositivoSearchModalForm, NotaForm, + PublicacaoForm, TaForm, TipoTaForm, VideForm) from compilacao.models import (Dispositivo, Nota, PerfilEstruturalTextoArticulado, Publicacao, TextoArticulado, TipoDispositivo, TipoNota, TipoPublicacao, TipoTextoArticulado, TipoVide, VeiculoPublicacao, Vide) +from compilacao.utils import DISPOSITIVO_SELECT_RELATED from crud.base import Crud, CrudListView, make_pagination -DISPOSITIVO_SELECT_RELATED = ( - 'tipo_dispositivo', - 'ta_publicado', - 'ta', - 'dispositivo_atualizador', - 'dispositivo_atualizador__dispositivo_pai', - 'dispositivo_atualizador__dispositivo_pai__ta', - 'dispositivo_atualizador__dispositivo_pai__ta__tipo_ta', - 'dispositivo_pai', - 'dispositivo_pai__tipo_dispositivo', - 'ta_publicado', - 'ta',) - TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota') TipoVideCrud = Crud.build(TipoVide, 'tipo_vide') TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao') @@ -162,7 +156,7 @@ class TipoTaListView(ListView): class TipoTaCreateView(FormMessagesMixin, CreateView): model = TipoTextoArticulado form_class = TipoTaForm - template_name = "compilacao/form.html" + template_name = "crud/form.html" form_valid_message = _('Registro criado com sucesso!') form_invalid_message = _('O registro não foi criado.') @@ -191,7 +185,7 @@ class TipoTaDetailView(CompMixin, DetailView): class TipoTaUpdateView(CompMixin, UpdateView): model = TipoTextoArticulado form_class = TipoTaForm - template_name = "compilacao/form.html" + template_name = "crud/form.html" def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -264,7 +258,7 @@ class TaDetailView(DetailView): class TaCreateView(FormMessagesMixin, CreateView): model = TextoArticulado form_class = TaForm - template_name = "compilacao/form.html" + template_name = "crud/form.html" form_valid_message = _('Registro criado com sucesso!') form_invalid_message = _('O registro não foi criado.') @@ -280,7 +274,7 @@ class TaCreateView(FormMessagesMixin, CreateView): class TaUpdateView(CompMixin, UpdateView): model = TextoArticulado form_class = TaForm - template_name = "compilacao/form.html" + template_name = "crud/form.html" def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -313,6 +307,265 @@ class TaDeleteView(CompMixin, DeleteView): return reverse_lazy('compilacao:ta_list') +class DispositivoSuccessUrlMixin: + + def get_success_url(self): + return reverse_lazy( + 'compilacao:dispositivo', kwargs={ + 'ta_id': self.kwargs[ + 'ta_id'], + 'dispositivo_id': self.kwargs[ + 'dispositivo_id']}) + + +class NotaMixin(DispositivoSuccessUrlMixin): + + def get_modelo_nota(self, request): + if 'action' in request.GET and request.GET['action'] == 'modelo_nota': + tn = TipoNota.objects.get(pk=request.GET['id_tipo']) + return True, tn.modelo + return False, '' + + def get_initial(self): + dispositivo = get_object_or_404( + Dispositivo, pk=self.kwargs.get('dispositivo_id')) + initial = {'dispositivo': dispositivo} + + if 'pk' in self.kwargs: + initial['pk'] = self.kwargs.get('pk') + + return initial + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(NotaMixin, self).dispatch(*args, **kwargs) + + +class NotasCreateView(NotaMixin, CreateView): + template_name = 'compilacao/ajax_form.html' + form_class = NotaForm + + def get(self, request, *args, **kwargs): + flag_action, modelo_nota = self.get_modelo_nota(request) + if flag_action: + return HttpResponse(modelo_nota) + + return super(NotasCreateView, self).get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.object = None + try: + ta_id = kwargs.pop('ta_id') + dispositivo_id = kwargs.pop('dispositivo_id') + form = NotaForm(request.POST, request.FILES, **kwargs) + kwargs['ta_id'] = ta_id + kwargs['dispositivo_id'] = dispositivo_id + + if form.is_valid(): + nt = form.save(commit=False) + nt.owner_id = request.user.pk + nt.save() + self.kwargs['pk'] = nt.pk + return self.form_valid(form) + else: + return self.form_invalid(form) + except Exception as e: + print(e) + return HttpResponse("error post") + + +class NotasEditView(NotaMixin, UpdateView): + model = Nota + template_name = 'compilacao/ajax_form.html' + form_class = NotaForm + + def get(self, request, *args, **kwargs): + flag_action, modelo_nota = self.get_modelo_nota(request) + if flag_action: + return HttpResponse(modelo_nota) + + return super(NotasEditView, self).get(request, *args, **kwargs) + + +class NotasDeleteView(NotaMixin, TemplateView): + + def get(self, request, *args, **kwargs): + nt = Nota.objects.get(pk=self.kwargs['pk']) + nt.delete() + return HttpResponseRedirect(self.get_success_url()) + + +class VideMixin(DispositivoSuccessUrlMixin): + + def get_initial(self): + dispositivo_base = get_object_or_404( + Dispositivo, pk=self.kwargs.get('dispositivo_id')) + initial = {'dispositivo_base': dispositivo_base, } + + if 'pk' in self.kwargs: + initial['pk'] = self.kwargs.get('pk') + + return initial + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(VideMixin, self).dispatch(*args, **kwargs) + + +def choice_model_type_foreignkey_in_extenal_views(id_tipo_ta=None): + yield None, '-------------' + + if not id_tipo_ta: + return + + tipo_ta = TipoTextoArticulado.objects.get(pk=id_tipo_ta) + + integrations_view_names = get_integrations_view_names() + for item in integrations_view_names: + if hasattr(item, 'model_type_foreignkey'): + if (tipo_ta.content_type.model == item.model.__name__.lower() and + tipo_ta.content_type.app_label == + item.model._meta.app_label): + for i in item.model_type_foreignkey.objects.all(): + yield i.pk, i + + +class VideCreateView(VideMixin, CreateView): + model = Vide + template_name = 'compilacao/ajax_form.html' + form_class = VideForm + + def get(self, request, *args, **kwargs): + self.object = None + form = self.get_form() + return self.render_to_response(self.get_context_data(form=form)) +""" + def get_form_kwargs(self): + + kwargs = super(VideCreateView, self).get_form_kwargs() + + if 'choice_model_type_foreignkey_in_extenal_views' not in kwargs: + kwargs.update({ + 'choice_model_type_foreignkey_in_extenal_views': + choice_model_type_foreignkey_in_extenal_views + }) + + return kwargs""" + + +class VideEditView(VideMixin, UpdateView): + model = Vide + template_name = 'compilacao/ajax_form.html' + form_class = VideForm + + +class VideDeleteView(VideMixin, TemplateView): + + def get(self, request, *args, **kwargs): + vd = Vide.objects.get(pk=self.kwargs['pk']) + vd.delete() + return HttpResponseRedirect(self.get_success_url()) + + +class PublicacaoListView(ListView): + model = Publicacao + verbose_name = model._meta.verbose_name + + @property + def title(self): + return _('%s de %s' % ( + self.model._meta.verbose_name_plural, + self.ta)) + + @property + def ta(self): + ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) + return ta + + @property + def create_url(self): + return reverse_lazy( + 'compilacao:ta_pub_create', + kwargs={'ta_id': self.kwargs['ta_id']}) + + def get_queryset(self): + pubs = Publicacao.objects.filter(ta_id=self.kwargs['ta_id']) + return pubs + + def get_context_data(self, **kwargs): + context = super(PublicacaoListView, self).get_context_data(**kwargs) + context['NO_ENTRIES_MSG'] = CrudListView.no_entries_msg + return context + + +class PublicacaoCreateView(FormMessagesMixin, CreateView): + model = Publicacao + form_class = PublicacaoForm + template_name = "crud/form.html" + form_valid_message = _('Registro criado com sucesso!') + form_invalid_message = _('O registro não foi criado.') + + def get_success_url(self): + return reverse_lazy( + 'compilacao:ta_pub_detail', + kwargs={ + 'pk': self.object.id, + 'ta_id': self.kwargs['ta_id']}) + + @property + def cancel_url(self): + return reverse_lazy( + 'compilacao:ta_pub_list', + kwargs={'ta_id': self.kwargs['ta_id']}) + + def get_initial(self): + return {'ta': self.kwargs['ta_id']} + + +class PublicacaoDetailView(CompMixin, DetailView): + model = Publicacao + + +class PublicacaoUpdateView(CompMixin, UpdateView): + model = Publicacao + form_class = PublicacaoForm + template_name = "crud/form.html" + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + form = self.get_form() + # if self.object and self.object.content_object: + # form.fields['tipo_ta'].required = False + # form.fields['tipo_ta'].widget.attrs['disabled'] = 'disabled' + return self.render_to_response(self.get_context_data(form=form)) + + def get_success_url(self): + return reverse_lazy('compilacao:ta_pub_detail', + kwargs={ + 'pk': self.object.id, + 'ta_id': self.kwargs['ta_id']}) + + @property + def cancel_url(self): + return self.get_success_url() + + +class PublicacaoDeleteView(CompMixin, DeleteView): + model = Publicacao + template_name = "crud/confirm_delete.html" + + @property + def detail_url(self): + return reverse_lazy('compilacao:ta_pub_detail', + kwargs={ + 'pk': self.object.id, + 'ta_id': self.kwargs['ta_id']}) + + def get_success_url(self): + return reverse_lazy('compilacao:ta_pub_list', + kwargs={'ta_id': self.kwargs['ta_id']}) + + class TextView(ListView, CompMixin): template_name = 'compilacao/text_list.html' @@ -325,6 +578,7 @@ class TextView(ListView, CompMixin): inicio_vigencia = None fim_vigencia = None + ta_vigencia = None def get(self, request, *args, **kwargs): ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) @@ -427,14 +681,19 @@ class TextView(ListView, CompMixin): self.inicio_vigencia = None self.fim_vigencia = None + self.ta_vigencia = None if 'sign' in self.kwargs: signer = Signer() try: string = signer.unsign(self.kwargs['sign']).split(',') - self.inicio_vigencia = parse_date(string[0]) - self.fim_vigencia = parse_date(string[1]) + self.ta_vigencia = int(string[0]) + self.inicio_vigencia = parse_date(string[1]) + self.fim_vigencia = parse_date(string[2]) except: - return{} + return Dispositivo.objects.filter( + ordem__gt=0, + ta_id=self.kwargs['ta_id'], + ).select_related(*DISPOSITIVO_SELECT_RELATED) return Dispositivo.objects.filter( inicio_vigencia__lte=self.fim_vigencia, @@ -488,7 +747,8 @@ class TextView(ListView, CompMixin): continue if idx + 1 < length: - ano = item.ta_publicado.ano + ano = item.ta_publicado.ano if item.ta_publicado else\ + item.ta.ano if ano in self.itens_de_vigencia: self.itens_de_vigencia[ano].append(item) else: @@ -606,7 +866,6 @@ class TextEditView(ListView, CompMixin): a.tipo_dispositivo = td a.inicio_vigencia = ta.data a.inicio_eficacia = ta.data - a.timestamp = datetime.now() a.save() td = TipoDispositivo.objects.filter(class_css='ementa')[0] @@ -619,7 +878,6 @@ class TextEditView(ListView, CompMixin): e.tipo_dispositivo = td e.inicio_vigencia = ta.data e.inicio_eficacia = ta.data - e.timestamp = datetime.now() e.texto = ta.ementa e.dispositivo_pai = a e.save() @@ -629,7 +887,6 @@ class TextEditView(ListView, CompMixin): a.ordem = e.ordem + Dispositivo.INTERVALO_ORDEM a.ordem_bloco_atualizador = 0 a.set_numero_completo([2, 0, 0, 0, 0, 0, ]) - a.timestamp = datetime.now() a.save() result = Dispositivo.objects.filter( @@ -656,7 +913,7 @@ class TextEditView(ListView, CompMixin): request.session['perfil_estrutural'] = perfis[0].pk -class DispositivoEditView(TextEditView): +class DispositivoSimpleEditView(TextEditView): template_name = 'compilacao/text_edit_bloco.html' def post(self, request, *args, **kwargs): @@ -1006,27 +1263,356 @@ class ActionsEditMixin: action = getattr(self, context['action']) return JsonResponse(action(context), safe=False) - def delete_item_dispositivo(self, context): - return self.delete_bloco_dispositivo(context) + def set_dvt(self, context): + # Dispositivo de Vigência do Texto Original e de Dpts Alterados + dvt = Dispositivo.objects.get(pk=context['dispositivo_id']) - def delete_bloco_dispositivo(self, context): - base = Dispositivo.objects.get(pk=context['dispositivo_id']) + if dvt.is_relative_auto_insert(): + dvt = dvt.dispositivo_pai - base_anterior = Dispositivo.objects.order_by('-ordem').filter( - ta_id=base.ta_id, + try: + Dispositivo.objects.filter( + (Q(ta=dvt.ta) & Q(ta_publicado__isnull=True)) | + Q(ta_publicado=dvt.ta) + ).update( + dispositivo_vigencia=dvt, + inicio_vigencia=dvt.inicio_vigencia, + inicio_eficacia=dvt.inicio_eficacia) + + dps = Dispositivo.objects.filter(dispositivo_vigencia_id=dvt.pk, + ta_publicado_id=dvt.ta_id) + with transaction.atomic(): + for d in dps: + if d.dispositivo_substituido: + ds = d.dispositivo_substituido + ds.fim_vigencia = d.inicio_vigencia - timedelta(days=1) + ds.fim_eficacia = d.inicio_eficacia - timedelta(days=1) + d.save() + + if d.dispositivo_subsequente: + ds = d.dispositivo_subsequente + d.fim_vigencia = ds.inicio_vigencia - timedelta(days=1) + d.fim_eficacia = ds.inicio_eficacia - timedelta(days=1) + d.save() + + return {'message': str(_('Dispositivo de Vigência atualizado ' + 'com sucesso!!!'))} + except: + return {'message': str(_('Ocorreu um erro na atualização do ' + 'Dispositivo de Vigência'))} + + def delete_item_dispositivo(self, context): + return self.delete_bloco_dispositivo(context, bloco=False) + + def delete_bloco_dispositivo(self, context, bloco=True): + base = Dispositivo.objects.get(pk=context['dispositivo_id']) + + base_anterior = Dispositivo.objects.order_by('-ordem').filter( + ta_id=base.ta_id, ordem__lt=base.ordem - )[:1] - base.delete() + ).first() - if base_anterior.exists(): - if base_anterior[0].dispositivo_pai_id: - data = {'pk': base_anterior[0].pk, 'pai': [ - base_anterior[0].dispositivo_pai_id, ]} + data = {} + if base_anterior: + data = self.get_json_for_refresh(base_anterior) + else: + base_anterior = Dispositivo.objects.order_by('ordem').filter( + ta_id=base.ta_id, + ordem__lt=base.ordem + ).first() + if base_anterior: + data = self.get_json_for_refresh(base_anterior) else: - data = {'pk': base_anterior[0].pk, 'pai': [-1, ]} - return data + data['pk'] = '' + + ta_base = base.ta + + # TODO: a linha abaixo causa atualização da tela inteira... + # retirar a linha abaixo e identificar atualizações pontuais + data['pai'] = [-1, ] + + try: + with transaction.atomic(): + data['message'] = str(self.remover_dispositivo(base, bloco)) + ta_base.organizar_ordem_de_dispositivos() + except Exception as e: + print(e) + data['pk'] = context['dispositivo_id'] + data['message'] = str(_('Ocorreu um erro ao ' + 'excluir esse Dispositivo')) + + return data + + def remover_dispositivo(self, base, bloco): + base_ordem = base.ordem + if base.dispositivo_subsequente or base.dispositivo_substituido: + p = base.dispositivo_substituido + n = base.dispositivo_subsequente + + if n: + # print(n.id, n) + n.dispositivo_substituido = p + n.save() + + if p: + # print(p.id, p) + p.dispositivo_subsequente = n + if n: + p.fim_vigencia = n.ini_vigencia - timedelta(days=1) + p.fim_eficacia = n.ini_eficacia - timedelta(days=1) + else: + p.fim_vigencia = None + p.fim_eficacia = None + + for d in base.dispositivos_filhos_set.all(): + if d.is_relative_auto_insert(): + self.remover_dispositivo(d, bloco) + elif not bloco: + p.dispositivos_filhos_set.add(d) + p.save() + base.delete() else: - return {} + proxima_articulacao = base.get_proximo_nivel_zero() + if not bloco: + # tranferir filhos para primeiro pai possível acima da base + # de exclusão + for d in base.dispositivos_filhos_set.all(): + # inserções automáticas são excluidas junto com sua base, + # independente da escolha do usuário + + """ TODO: Criar possibilidade de transferência de filhos + de dispositivos automáticos + ex: na exclusão de artigos, na versão atual, + os caputs serão excluidos automáticamente mesmo que a + exclusão não seja em bloco. O que fazer com os incisos? + transferir para o caput imediatamente acima visto se + tratar de uma exclusão de item?""" + d_nivel_old = d.nivel + if d.is_relative_auto_insert(): + d.delete() + continue + + # encontrar possível pai que será o primeiro parent + # possível dos parents do dispostivo + # imediatamente anterior ao dispositivo base + + anterior = Dispositivo.objects.order_by('-ordem').filter( + ta_id=base.ta_id, + ordem__lt=d.ordem).exclude( + pk=base.pk).exclude( + dispositivo_pai=base).first() + + if not anterior: + return _('Não é possível excluir este Dispositivo sem' + ' excluir toda a sua estrutura!!!') + + if anterior.tipo_dispositivo == d.tipo_dispositivo: + d.dispositivo_pai = anterior.dispositivo_pai + d.nivel = anterior.nivel + if not d.tipo_dispositivo.contagem_continua: + d.set_numero_completo( + anterior.get_numero_completo()) + + if d.dispositivo_substituido != anterior: + d.transform_in_next() + d.rotulo = d.rotulo_padrao() + else: + parents = [anterior, ] + anterior.get_parents() + + for candidato in parents: + if candidato == base: + return _('Não é possível excluir este ' + 'Dispositivo sem ' + 'excluir toda a sua estrutura!!!') + if (candidato.tipo_dispositivo == + d.tipo_dispositivo): + d.dispositivo_pai = candidato.dispositivo_pai + d.nivel = candidato.nivel + if not d.tipo_dispositivo.contagem_continua: + d.set_numero_completo( + candidato.get_numero_completo()) + if d.dispositivo_substituido != candidato: + d.transform_in_next() + d.rotulo = d.rotulo_padrao() + break + + elif (candidato.tipo_dispositivo == + d.dispositivo_pai.tipo_dispositivo): + d.dispositivo_pai = candidato + d.nivel = candidato.nivel + 1 + break + + elif d.tipo_dispositivo.possiveis_pais.filter( + pai=candidato.tipo_dispositivo, + perfil__padrao=True).exists(): + d.dispositivo_pai = candidato + if ';' in d.tipo_dispositivo.\ + rotulo_prefixo_texto: + d.set_numero_completo([0, 0, 0, 0, 0, 0, ]) + else: + d.set_numero_completo([1, 0, 0, 0, 0, 0, ]) + d.nivel = candidato.nivel + 1 + d.rotulo = d.rotulo_padrao() + break + + if not parents: + d.dispositivo_pai = anterior + d.nivel = anterior.nivel + 1 + + d.save(clean=False) + if d.nivel != d_nivel_old: + d.organizar_niveis() + + pai_base = base.dispositivo_pai + if pai_base: + # Localizar irmaos posteriores do mesmo tipo de base + # se não DCC + if not base.tipo_dispositivo.contagem_continua: + irmaos_posteriores = pai_base.dispositivos_filhos_set.\ + filter( + ordem__gt=base_ordem, + tipo_dispositivo=base.tipo_dispositivo) + + # se DCC + else: + irmaos_posteriores = Dispositivo.objects.order_by( + 'ordem').filter( + ta_id=base.ta_id, + ordem__gt=base_ordem, + tipo_dispositivo_id=base.tipo_dispositivo_id) + + if proxima_articulacao: + irmaos_posteriores = irmaos_posteriores.exclude( + ordem__gte=proxima_articulacao.ordem) + + # excluir e renumerar irmaos + profundidade_base = base.get_profundidade() + base.delete() + + for irmao in irmaos_posteriores: + irmao.transform_in_prior( + profundidade=profundidade_base) + irmao.rotulo = irmao.rotulo_padrao() + irmao.save() + + irmaos = pai_base.dispositivos_filhos_set.\ + filter(tipo_dispositivo=base.tipo_dispositivo) + + if (irmaos.count() == 1 and + ';' in irmaos[0]. + tipo_dispositivo.rotulo_prefixo_texto): + i = irmaos[0] + i.set_numero_completo([0, 0, 0, 0, 0, 0, ]) + i.rotulo = i.rotulo_padrao(local_insert=1) + i.save() + else: + # Renumerar Dispostivos de Contagem Contínua + # de dentro da base se pai + dcc = Dispositivo.objects.order_by('ordem').filter( + ta_id=base.ta_id, + ordem__gt=base.ordem, + tipo_dispositivo__contagem_continua=True) + + if proxima_articulacao: + dcc = dcc.exclude( + ordem__gte=proxima_articulacao.ordem) + + base_adicao = {} + + nivel_zero_anterior = base.get_nivel_zero_anterior() + if nivel_zero_anterior: + nivel_zero_anterior = nivel_zero_anterior.ordem + else: + nivel_zero_anterior = 0 + + dcc = list(dcc) + for d in dcc: # ultimo DCC do tipo encontrado + + if d.tipo_dispositivo.class_css not in base_adicao: + ultimo_dcc = Dispositivo.objects.order_by( + 'ordem').filter( + ta_id=base.ta_id, + ordem__lt=base.ordem, + ordem__gt=nivel_zero_anterior, + tipo_dispositivo__contagem_continua=True, + tipo_dispositivo=d.tipo_dispositivo).last() + + if not ultimo_dcc: + break + + base_adicao[ + d.tipo_dispositivo.class_css] = ultimo_dcc.\ + dispositivo0 + + d.dispositivo0 += base_adicao[ + d.tipo_dispositivo.class_css] + + d.rotulo = d.rotulo_padrao() + dcc.reverse() + for d in dcc: + d.save() + + base.delete() + + # em Bloco + else: + + # Religar numeração de dispositivos de contagem contínua + # que serão excluidos + # pbi - proxima base independente + pbi = Dispositivo.objects.\ + order_by('ordem').filter( + ta_id=base.ta_id, + ordem__gt=base_ordem, + nivel__lte=base.nivel).first() + + if not pbi: + base.delete() + else: + dcc_a_excluir = Dispositivo.objects.order_by( + 'ordem').filter( + ta_id=base.ta_id, + ordem__gte=base_ordem, + ordem__lt=pbi.ordem, + tipo_dispositivo__contagem_continua=True) + + if proxima_articulacao: + dcc_a_excluir = dcc_a_excluir.exclude( + ordem__gte=proxima_articulacao.ordem) + + religado = {} + + for d in dcc_a_excluir: + if d.tipo_dispositivo.class_css in religado: + continue + religado[ + d.tipo_dispositivo.class_css] = d.dispositivo0 + + dcc_a_religar = Dispositivo.objects.filter( + ta_id=d.ta_id, + ordem__gte=pbi.ordem, + tipo_dispositivo=d.tipo_dispositivo) + + if proxima_articulacao: + dcc_a_religar = dcc_a_religar.exclude( + ordem__gte=proxima_articulacao.ordem) + + primeiro_a_religar = 0 + for dr in dcc_a_religar: + if not primeiro_a_religar: + primeiro_a_religar = dr.dispositivo0 + base.delete() + + dr.dispositivo0 = ( + dr.dispositivo0 - + primeiro_a_religar + d.dispositivo0) + dr.rotulo = dr.rotulo_padrao() + + dr.save(clean=base != dr) + if base.pk: + base.delete() + + return '' def add_prior(self, context): return {} @@ -1036,11 +1622,24 @@ class ActionsEditMixin: def add_next(self, context, local_add='add_next'): try: + + dp_auto_insert = None base = Dispositivo.objects.get(pk=context['dispositivo_id']) tipo = TipoDispositivo.objects.get(pk=context['tipo_pk']) + pub_last = Publicacao.objects.order_by( + 'data', 'hora').filter(ta=base.ta).last() + variacao = int(context['variacao']) parents = [base, ] + base.get_parents() + if 'perfil_pk' not in context: + perfil_padrao = PerfilEstruturalTextoArticulado.objects.filter( + padrao=True).first() + if perfil_padrao: + context['perfil_pk'] = perfil_padrao.pk + else: + raise Exception('Não existe perfil padrão!') + tipos_dp_auto_insert = tipo.filhos_permitidos.filter( filho_de_insercao_automatica=True, perfil_id=context['perfil_pk']) @@ -1084,6 +1683,7 @@ class ActionsEditMixin: ultimo_irmao = Dispositivo.objects.order_by( '-ordem').filter( ordem__lte=base.ordem, + ordem__gte=parents[-1].ordem, tipo_dispositivo_id=tipo.pk, ta_id=base.ta_id)[:1] @@ -1114,9 +1714,9 @@ class ActionsEditMixin: if qtd_existente >= pp[0].quantidade_permitida: return {'pk': base.pk, 'pai': [base.dispositivo_pai.pk, ], - 'alert': str(_('Limite de inserções de ' - 'dispositivos deste tipo ' - 'foi excedido.')) + 'message': str(_('Limite de inserções de ' + 'dispositivos deste tipo ' + 'foi excedido.')) } ordem = base.criar_espaco( @@ -1124,13 +1724,11 @@ class ActionsEditMixin: dp.rotulo = dp.rotulo_padrao() dp.ordem = ordem - dp.incrementar_irmaos(variacao, [local_add, ]) + dp.incrementar_irmaos(variacao, [local_add, ], force=False) - dp.clean() + dp.publicacao = pub_last dp.save() - dp_auto_insert = None - # Inserção automática if count_auto_insert: dp_pk = dp.pk @@ -1146,7 +1744,8 @@ class ActionsEditMixin: dp.rotulo = dp.rotulo_padrao() dp.texto = '' dp.ordem = dp.ordem + Dispositivo.INTERVALO_ORDEM - dp.clean() + + dp.publicacao = pub_last dp.save() dp_auto_insert = dp dp = Dispositivo.objects.get(pk=dp_pk) @@ -1168,7 +1767,8 @@ class ActionsEditMixin: if filho.nivel > nivel: continue - if filho.dispositivo_pai.ordem >= dp.ordem: + if not filho.dispositivo_pai or\ + filho.dispositivo_pai.ordem >= dp.ordem: continue nivel = filho.nivel @@ -1179,7 +1779,6 @@ class ActionsEditMixin: continue filho.dispositivo_pai = dp - filho.clean() filho.save() flag_niveis = True @@ -1223,58 +1822,38 @@ class ActionsEditMixin: filho.tipo_dispositivo.class_css] filho.rotulo = filho.rotulo_padrao() - filho.clean() filho.save() ''' Renumerar dispositivos de contagem continua, caso a inserção seja uma articulação''' - numtipos = {} if dp.nivel == 0: - proxima_articulacao = Dispositivo.objects.filter( - ordem__gt=dp.ordem, - nivel=0, - ta_id=dp.ta_id)[:1] + proxima_articulacao = dp.get_proximo_nivel_zero() - if not proxima_articulacao.exists(): + if not proxima_articulacao: filhos_continuos = list(Dispositivo.objects.filter( ordem__gt=dp.ordem, ta_id=dp.ta_id, tipo_dispositivo__contagem_continua=True)) else: filhos_continuos = list(Dispositivo.objects.filter( - Q(ordem__gt=dp.ordem) & - Q(ordem__lt=proxima_articulacao[0].ordem), + ordem__gt=dp.ordem, + ordem__lt=proxima_articulacao.ordem, ta_id=dp.ta_id, tipo_dispositivo__contagem_continua=True)) - for filho in filhos_continuos: - - if filho.tipo_dispositivo.class_css in numtipos: - if filho.dispositivo_substituido is None: - numtipos[filho.tipo_dispositivo.class_css] += 1 - else: - t = filho.tipo_dispositivo - prefixo = t.rotulo_prefixo_texto.split(';') - if len(prefixo) > 1: - count_irmaos_m_tipo = Dispositivo.objects.filter( - ~Q(pk=filho.pk), - tipo_dispositivo=t, - dispositivo_pai=filho.dispositivo_pai)[:1] + base_reducao = {} - if count_irmaos_m_tipo.exists(): - numtipos[filho.tipo_dispositivo.class_css] = 1 - else: - numtipos[filho.tipo_dispositivo.class_css] = 0 - else: - numtipos[filho.tipo_dispositivo.class_css] = 1 + for filho in filhos_continuos: + if filho.tipo_dispositivo.class_css not in base_reducao: + base_reducao[filho.tipo_dispositivo.class_css] = \ + filho.dispositivo0 - 1 - filho.dispositivo0 = numtipos[ + filho.dispositivo0 -= base_reducao[ filho.tipo_dispositivo.class_css] filho.rotulo = filho.rotulo_padrao() - filho.clean() filho.save() except Exception as e: @@ -1340,141 +1919,17 @@ class ActionsEditView(ActionsEditMixin, TemplateView): if 'perfil_estrutural' in self.request.session: context['perfil_pk'] = self.request.session['perfil_estrutural'] - return self.render_to_json_response(context, **response_kwargs) + if 'herancas' in self.request.session: + del self.request.session['herancas'] + del self.request.session['herancas_fila'] - -class DispositivoSuccessUrlMixin: - - def get_success_url(self): - return reverse_lazy( - 'compilacao:dispositivo', kwargs={ - 'ta_id': self.kwargs[ - 'ta_id'], - 'dispositivo_id': self.kwargs[ - 'dispositivo_id']}) - - -class NotaMixin(DispositivoSuccessUrlMixin): - - def get_modelo_nota(self, request): - if 'action' in request.GET and request.GET['action'] == 'modelo_nota': - tn = TipoNota.objects.get(pk=request.GET['id_tipo']) - return True, tn.modelo - return False, '' - - def get_initial(self): - dispositivo = get_object_or_404( - Dispositivo, pk=self.kwargs.get('dispositivo_id')) - initial = {'dispositivo': dispositivo} - - if 'pk' in self.kwargs: - initial['pk'] = self.kwargs.get('pk') - - return initial - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super(NotaMixin, self).dispatch(*args, **kwargs) - - -class NotasCreateView(NotaMixin, CreateView): - template_name = 'compilacao/ajax_form.html' - form_class = NotaForm - - def get(self, request, *args, **kwargs): - flag_action, modelo_nota = self.get_modelo_nota(request) - if flag_action: - return HttpResponse(modelo_nota) - - return super(NotasCreateView, self).get(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - self.object = None - try: - ta_id = kwargs.pop('ta_id') - dispositivo_id = kwargs.pop('dispositivo_id') - form = NotaForm(request.POST, request.FILES, **kwargs) - kwargs['ta_id'] = ta_id - kwargs['dispositivo_id'] = dispositivo_id - - if form.is_valid(): - nt = form.save(commit=False) - nt.owner_id = request.user.pk - nt.save() - self.kwargs['pk'] = nt.pk - return self.form_valid(form) - else: - return self.form_invalid(form) - except Exception as e: - print(e) - return HttpResponse("error post") - - -class NotasEditView(NotaMixin, UpdateView): - model = Nota - template_name = 'compilacao/ajax_form.html' - form_class = NotaForm - - def get(self, request, *args, **kwargs): - flag_action, modelo_nota = self.get_modelo_nota(request) - if flag_action: - return HttpResponse(modelo_nota) - - return super(NotasEditView, self).get(request, *args, **kwargs) - - -class NotasDeleteView(NotaMixin, TemplateView): - - def get(self, request, *args, **kwargs): - nt = Nota.objects.get(pk=self.kwargs['pk']) - nt.delete() - return HttpResponseRedirect(self.get_success_url()) - - -class VideMixin(DispositivoSuccessUrlMixin): - - def get_initial(self): - dispositivo_base = get_object_or_404( - Dispositivo, pk=self.kwargs.get('dispositivo_id')) - - initial = {'dispositivo_base': dispositivo_base} - - if 'pk' in self.kwargs: - initial['pk'] = self.kwargs.get('pk') - - return initial - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super(VideMixin, self).dispatch(*args, **kwargs) - - -def choice_model_type_foreignkey_in_extenal_views(id_tipo_ta=None): - result = [(None, '-------------'), ] - - if not id_tipo_ta: - return result - - tipo_ta = TipoTextoArticulado.objects.get(pk=id_tipo_ta) - - integrations_view_names = get_integrations_view_names() - for item in integrations_view_names: - if hasattr(item, 'model_type_foreignkey'): - if (tipo_ta.content_type.model == item.model.__name__.lower() and - tipo_ta.content_type.app_label == - item.model._meta.app_label): - for i in item.model_type_foreignkey.objects.all(): - result.append((i.pk, i)) - return result + return self.render_to_json_response(context, **response_kwargs) -class VideCreateView(VideMixin, CreateView): - model = Vide - template_name = 'compilacao/ajax_form.html' - form_class = VideForm +class DispositivoSearchFragmentFormView(ListView): + template_name = 'compilacao/dispositivo_form_search_fragment.html' def get(self, request, *args, **kwargs): - self.object = None if 'action' in request.GET and request.GET['action'] == 'get_tipos': result = choice_model_type_foreignkey_in_extenal_views( @@ -1487,106 +1942,113 @@ class VideCreateView(VideMixin, CreateView): itens.append(item) return JsonResponse(itens, safe=False) - form = self.get_form() - return self.render_to_response(self.get_context_data(form=form)) - - def get_form_kwargs(self): - - kwargs = super(VideCreateView, self).get_form_kwargs() - - if 'choice_model_type_foreignkey_in_extenal_views' not in kwargs: - kwargs.update({ - 'choice_model_type_foreignkey_in_extenal_views': - choice_model_type_foreignkey_in_extenal_views - }) - - return kwargs - - -class VideEditView(VideMixin, UpdateView): - model = Vide - template_name = 'compilacao/ajax_form.html' - form_class = VideForm - + return ListView.get(self, request, *args, **kwargs) -class VideDeleteView(VideMixin, TemplateView): + def get_queryset(self): + try: - def get(self, request, *args, **kwargs): - vd = Vide.objects.get(pk=self.kwargs['pk']) - vd.delete() - return HttpResponseRedirect(self.get_success_url()) + n = 10 + if 'max_results' in self.request.GET: + n = int(self.request.GET['max_results']) + q = Q() + if 'initial_ref' in self.request.GET: + initial_ref = self.request.GET['initial_ref'] + if initial_ref: + q = q & Q(pk=initial_ref) -class DispositivoSearchFragmentFormView(ListView): - template_name = 'compilacao/dispositivo_search_fragment_form.html' + result = Dispositivo.objects.filter(q).select_related( + 'ta').exclude( + tipo_dispositivo__dispositivo_de_alteracao=True) - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super( - DispositivoSearchFragmentFormView, - self).dispatch(*args, **kwargs) + return result[:n] - def get_queryset(self): - try: - busca = '' + str_texto = '' + texto = '' + rotulo = '' + num_ta = '' + ano_ta = '' - if 'busca' in self.request.GET: - busca = self.request.GET['busca'] + if 'texto' in self.request.GET: + str_texto = self.request.GET['texto'] - q = Q(nivel__gt=0) - busca = busca.split(' ') - n = 10 + texto = str_texto.split(' ') - for item in busca: + if 'rotulo' in self.request.GET: + rotulo = self.request.GET['rotulo'] + if rotulo: + q = q & Q(rotulo__icontains=rotulo) + for item in texto: if not item: continue - if q: - q = q & (Q(dispositivo_pai__rotulo__icontains=item) | - Q(rotulo__icontains=item) | - Q(texto__icontains=item) | + q = q & (Q(texto__icontains=item) | Q(texto_atualizador__icontains=item)) - n = 50 else: - q = (Q(dispositivo_pai__rotulo__icontains=item) | - Q(rotulo__icontains=item) | - Q(texto__icontains=item) | + q = (Q(texto__icontains=item) | Q(texto_atualizador__icontains=item)) - n = 50 if 'tipo_ta' in self.request.GET: tipo_ta = self.request.GET['tipo_ta'] if tipo_ta: q = q & Q(ta__tipo_ta_id=tipo_ta) - n = 50 if 'num_ta' in self.request.GET: num_ta = self.request.GET['num_ta'] if num_ta: q = q & Q(ta__numero=num_ta) - n = 50 if 'ano_ta' in self.request.GET: ano_ta = self.request.GET['ano_ta'] if ano_ta: q = q & Q(ta__ano=ano_ta) - n = 50 - if 'initial_ref' in self.request.GET: - initial_ref = self.request.GET['initial_ref'] - if initial_ref: - q = q & Q(pk=initial_ref) - n = 50 + if not q.children and not n: + n = 10 + q = q & Q(nivel__gt=0) + + result = Dispositivo.objects.order_by( + '-ta__data', + '-ta__ano', + '-ta__numero', + 'ta', + 'ordem').filter(q).select_related('ta') + + if 'data_type_selection' in self.request.GET and\ + self.request.GET['data_type_selection'] == 'checkbox': + result = result.exclude( + tipo_dispositivo__dispositivo_de_alteracao=True) + else: + if 'data_function' in self.request.GET and\ + self.request.GET['data_function'] == 'alterador': + result = result.exclude( + tipo_dispositivo__dispositivo_de_alteracao=False, + ) + result = result.exclude( + tipo_dispositivo__dispositivo_de_articulacao=False, + ) + print(str(result.query)) + + def resultados(r): + if n: + return r[:n] + else: + return r - result = Dispositivo.objects.filter(q).select_related('ta') + """if num_ta and ano_ta and not rotulo and not str_texto and\ + 'data_type_selection' in self.request.GET and\ + self.request.GET['data_type_selection'] == 'checkbox': + return r + else: + return r[:n]""" if 'tipo_model' not in self.request.GET: - return result[:n] + return resultados(result) tipo_model = self.request.GET['tipo_model'] if not tipo_model: - return result[:n] + return resultados(result) integrations_view_names = get_integrations_view_names() @@ -1608,7 +2070,7 @@ class DispositivoSearchFragmentFormView(ListView): break if not model_class: - return result[:n] + return resultados(result) column_field = '' for field in model_class._meta.fields: @@ -1617,7 +2079,7 @@ class DispositivoSearchFragmentFormView(ListView): break if not column_field: - return result[:n] + return resultados(result) r = [] @@ -1629,7 +2091,8 @@ class DispositivoSearchFragmentFormView(ListView): if tipo_model.pk == getattr(d.ta.content_object, column_field): r.append(d) - if len(r) == n: + if (len(r) == n and (not num_ta or + not ano_ta or rotulo or str_texto)): break return r @@ -1637,100 +2100,194 @@ class DispositivoSearchFragmentFormView(ListView): print(e) -class PublicacaoListView(ListView): - model = Publicacao - verbose_name = model._meta.verbose_name +class DispositivoSearchModalView(FormView): + template_name = 'compilacao/dispositivo_form_search.html' + form_class = DispositivoSearchModalForm - @property - def title(self): - return _('%s de %s' % ( - self.model._meta.verbose_name_plural, - self.ta)) - @property - def ta(self): - ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) - return ta +class DispositivoEdicaoBasicaView(FormMessagesMixin, UpdateView): + model = Dispositivo + template_name = 'compilacao/dispositivo_form_edicao_basica.html' + form_class = DispositivoEdicaoBasicaForm + form_valid_message = _('Alterações no Dispositivo realizadas com sucesso!') + form_invalid_message = _('Houve erro em registrar ' + 'as alterações no Dispositivo') @property - def create_url(self): + def cancel_url(self): return reverse_lazy( - 'compilacao:ta_pub_create', - kwargs={'ta_id': self.kwargs['ta_id']}) + 'compilacao:ta_text_edit', + kwargs={'ta_id': self.kwargs['ta_id']}) + '#' + str(self.object.pk) - def get_queryset(self): - pubs = Publicacao.objects.filter(ta_id=self.kwargs['ta_id']) - return pubs + def get_success_url(self): + return reverse_lazy( + 'compilacao:dispositivo_edit', + kwargs={'ta_id': self.kwargs['ta_id'], 'pk': self.kwargs['pk']}) - def get_context_data(self, **kwargs): - context = super(PublicacaoListView, self).get_context_data(**kwargs) - context['NO_ENTRIES_MSG'] = CrudListView.no_entries_msg - return context + def get_url_this_view(self): + return 'compilacao:dispositivo_edit' + def run_actions(self, request): + if 'action' in request.GET and\ + request.GET['action'] == 'atualiza_rotulo': + try: + d = Dispositivo.objects.get(pk=self.kwargs['pk']) + d.dispositivo0 = int(request.GET['dispositivo0']) + d.dispositivo1 = int(request.GET['dispositivo1']) + d.dispositivo2 = int(request.GET['dispositivo2']) + d.dispositivo3 = int(request.GET['dispositivo3']) + d.dispositivo4 = int(request.GET['dispositivo4']) + d.dispositivo5 = int(request.GET['dispositivo5']) + d.rotulo = d.rotulo_padrao() -class PublicacaoCreateView(FormMessagesMixin, CreateView): - model = Publicacao - form_class = PublicacaoForm - template_name = "compilacao/form.html" - form_valid_message = _('Registro criado com sucesso!') - form_invalid_message = _('O registro não foi criado.') + numero = d.get_numero_completo()[1:] + + zerar = False + for i in range(len(numero)): + if not numero[i]: + zerar = True + + if zerar: + numero[i] = 0 + + if zerar: + d.set_numero_completo([d.dispositivo0, ] + numero) + d.rotulo = d.rotulo_padrao() + + except: + return True, JsonResponse({'message': str( + _('Ocorreu erro na atualização do rótulo'))}, safe=False) + return True, JsonResponse({ + 'rotulo': d.rotulo, + 'dispositivo0': d.dispositivo0, + 'dispositivo1': d.dispositivo1, + 'dispositivo2': d.dispositivo2, + 'dispositivo3': d.dispositivo3, + 'dispositivo4': d.dispositivo4, + 'dispositivo5': d.dispositivo5}, safe=False) + + return False, '' + + def get(self, request, *args, **kwargs): + + flag_action, render_json_response = self.run_actions(request) + if flag_action: + return render_json_response + + return UpdateView.get(self, request, *args, **kwargs) + + +class DispositivoEdicaoVigenciaView(FormMessagesMixin, UpdateView): + model = Dispositivo + template_name = 'compilacao/dispositivo_form_vigencia.html' + form_class = DispositivoEdicaoVigenciaForm + form_valid_message = _('Alterações no Dispositivo realizadas com sucesso!') + form_invalid_message = _('Houve erro em registrar ' + 'as alterações no Dispositivo') + + @property + def cancel_url(self): + return reverse_lazy( + 'compilacao:ta_text_edit', + kwargs={'ta_id': self.kwargs['ta_id']}) + '#' + str(self.object.pk) + + def get_url_this_view(self): + return 'compilacao:dispositivo_edit_vigencia' def get_success_url(self): return reverse_lazy( - 'compilacao:ta_pub_detail', - kwargs={ - 'pk': self.object.id, - 'ta_id': self.kwargs['ta_id']}) + 'compilacao:dispositivo_edit_vigencia', + kwargs={'ta_id': self.kwargs['ta_id'], 'pk': self.kwargs['pk']}) + + +class DispositivoDefinidorVigenciaView(FormMessagesMixin, FormView): + model = Dispositivo + template_name = 'compilacao/dispositivo_form_definidor_vigencia.html' + form_class = DispositivoDefinidorVigenciaForm + form_valid_message = _('Alterações no Dispositivo realizadas com sucesso!') + form_invalid_message = _('Houve erro em registrar ' + 'as alterações no Dispositivo') + + def get_form_kwargs(self): + kwargs = FormView.get_form_kwargs(self) + kwargs.update({ + 'pk': self.kwargs['pk'], + }) + return kwargs @property def cancel_url(self): return reverse_lazy( - 'compilacao:ta_pub_list', - kwargs={'ta_id': self.kwargs['ta_id']}) + 'compilacao:ta_text_edit', + kwargs={'ta_id': self.kwargs['ta_id']}) + '#' + str(self.object.pk) - def get_initial(self): - return {'ta': self.kwargs['ta_id']} + def get_url_this_view(self): + return 'compilacao:dispositivo_edit_definidor_vigencia' + def get_success_url(self): + return reverse_lazy( + 'compilacao:dispositivo_edit_definidor_vigencia', + kwargs={'ta_id': self.kwargs['ta_id'], 'pk': self.kwargs['pk']}) -class PublicacaoDetailView(CompMixin, DetailView): - model = Publicacao + def get(self, request, *args, **kwargs): + self.object = get_object_or_404(Dispositivo, pk=kwargs['pk']) + return FormView.get(self, request, *args, **kwargs) + def get_context_data(self, **kwargs): + context = FormView.get_context_data(self, **kwargs) + context.update({'object': self.object}) + return context -class PublicacaoUpdateView(CompMixin, UpdateView): - model = Publicacao - form_class = PublicacaoForm - template_name = "compilacao/form.html" + def post(self, request, *args, **kwargs): + self.object = get_object_or_404(Dispositivo, pk=kwargs['pk']) - def get(self, request, *args, **kwargs): - self.object = self.get_object() form = self.get_form() - # if self.object and self.object.content_object: - # form.fields['tipo_ta'].required = False - # form.fields['tipo_ta'].widget.attrs['disabled'] = 'disabled' - return self.render_to_response(self.get_context_data(form=form)) + if form.is_valid(): + dvs = form.cleaned_data['dispositivo_vigencia'] + try: + with transaction.atomic(): + self.object.dispositivos_vigencias_set.clear() + for item in dvs: + d = Dispositivo.objects.get(pk=item) + self.object.dispositivos_vigencias_set.add(d) + return self.form_valid(form) + except: + return self.form_invalid(form) + else: + return self.form_invalid(form) - def get_success_url(self): - return reverse_lazy('compilacao:ta_pub_detail', - kwargs={ - 'pk': self.object.id, - 'ta_id': self.kwargs['ta_id']}) + +class DispositivoEdicaoAlteracaoView(FormMessagesMixin, UpdateView): + model = Dispositivo + template_name = 'compilacao/dispositivo_form_alteracao.html' + form_class = DispositivoEdicaoAlteracaoForm + form_valid_message = _('Alterações no Dispositivo realizadas com sucesso!') + form_invalid_message = _('Houve erro em registrar ' + 'as alterações no Dispositivo') @property def cancel_url(self): - return self.get_success_url() + return reverse_lazy( + 'compilacao:ta_text_edit', + kwargs={'ta_id': self.kwargs['ta_id']}) + '#' + str(self.object.pk) + def get_url_this_view(self): + return 'compilacao:dispositivo_edit_alteracao' -class PublicacaoDeleteView(CompMixin, DeleteView): - model = Publicacao - template_name = "crud/confirm_delete.html" + def get_success_url(self): + return reverse_lazy( + 'compilacao:dispositivo_edit_alteracao', + kwargs={'ta_id': self.kwargs['ta_id'], 'pk': self.kwargs['pk']}) - @property - def detail_url(self): - return reverse_lazy('compilacao:ta_pub_detail', - kwargs={ - 'pk': self.object.id, - 'ta_id': self.kwargs['ta_id']}) + def post(self, request, *args, **kwargs): + self.object = get_object_or_404(Dispositivo, pk=kwargs['pk']) - def get_success_url(self): - return reverse_lazy('compilacao:ta_pub_list', - kwargs={'ta_id': self.kwargs['ta_id']}) + form = self.get_form() + if form.is_valid(): + try: + with transaction.atomic(): + return self.form_valid(form) + except: + return self.form_invalid(form) + else: + return self.form_invalid(form) diff --git a/crispy_layout_mixin.py b/crispy_layout_mixin.py index 46dadf7a7..ddff0283c 100644 --- a/crispy_layout_mixin.py +++ b/crispy_layout_mixin.py @@ -39,10 +39,10 @@ def form_actions(more=[], save_label=_('Salvar')): class SaplFormLayout(Layout): - def __init__(self, *fields): + def __init__(self, *fields, label_cancel=_('Cancelar')): buttons = form_actions(more=[ HTML('%s' % _('Cancelar'))]) + ' class="btn btn-inverse">%s' % label_cancel)]) _fields = list(to_fieldsets(fields)) + [to_row([(buttons, 12)])] super(SaplFormLayout, self).__init__(*_fields) diff --git a/materia/forms.py b/materia/forms.py index 8cd09b4ea..e5c801c71 100644 --- a/materia/forms.py +++ b/materia/forms.py @@ -1,18 +1,16 @@ -import crispy_layout_mixin import django_filters -import sapl - from crispy_forms.helper import FormHelper -from crispy_forms.layout import Button, Column, Fieldset, HTML, Layout, Submit -from crispy_layout_mixin import form_actions - +from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Submit from django import forms -from django.core.exceptions import ValidationError, ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.models import Max from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ from django_filters import FilterSet +import crispy_layout_mixin +import sapl +from crispy_layout_mixin import form_actions from norma.models import LegislacaoCitada, TipoNormaJuridica from parlamentares.models import Parlamentar, Partido from sapl.settings import MAX_DOC_UPLOAD_SIZE diff --git a/materia/views.py b/materia/views.py index 4b4996747..03c9443f4 100644 --- a/materia/views.py +++ b/materia/views.py @@ -1,11 +1,7 @@ import os - from datetime import datetime - -from base.models import CasaLegislativa -from comissoes.models import Comissao, Composicao -from compilacao.views import IntegracaoTaView -from crud.base import Crud, make_pagination +from random import choice +from string import ascii_letters, digits from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist @@ -24,18 +20,14 @@ from compilacao.views import IntegracaoTaView from crud.base import Crud, make_pagination from norma.models import LegislacaoCitada, NormaJuridica, TipoNormaJuridica from parlamentares.models import Partido -from random import choice from sapl.utils import get_base_url -from string import ascii_letters, digits - from .forms import (AcompanhamentoMateriaForm, AutoriaForm, DespachoInicialForm, DocumentoAcessorioForm, FormularioCadastroForm, FormularioSimplificadoForm, LegislacaoCitadaForm, MateriaAnexadaForm, - MateriaLegislativaFilterSet, NumeracaoForm, - ProposicaoForm, RelatoriaForm, TramitacaoForm, - filtra_tramitacao_destino, + MateriaLegislativaFilterSet, NumeracaoForm, ProposicaoForm, + RelatoriaForm, TramitacaoForm, filtra_tramitacao_destino, filtra_tramitacao_destino_and_status, filtra_tramitacao_status) from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, @@ -45,7 +37,6 @@ from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) - OrigemCrud = Crud.build(Origem, 'origem') TipoMateriaCrud = Crud.build(TipoMateriaLegislativa, 'tipo_materia_legislativa') diff --git a/norma/urls.py b/norma/urls.py index d68fd36a5..199888813 100644 --- a/norma/urls.py +++ b/norma/urls.py @@ -19,7 +19,7 @@ app_name = AppConfig.name # url(r'^norma/(?P[0-9]+)/ta$', NormaTaView.as_view(), name='ta') # bem como a classe NormaTaView que está em norma.views norma_url_patterns = NormaTemporarioCrud.get_urls() + [ - url(r'^norma/(?P[0-9]+)/ta$', + url(r'^(?P[0-9]+)/ta$', NormaTaView.as_view(), name='ta') ] diff --git a/static/js/app.js b/static/js/app.js index 2ae9c1c57..03537f188 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,8 +1,31 @@ +function initTinymce(elements) { + removeTinymce(); + var config_tinymce = { + force_br_newlines : false, + force_p_newlines : false, + forced_root_block : '', + plugins: ["table save code"], + menubar: "edit format table tools", + toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent", + tools: "inserttable", + border_css: "/static/styles/style_tinymce.css", + content_css: "/static/styles/style_tinymce.css", + } + if (elements != null) { + config_tinymce['elements'] = elements; + config_tinymce['mode'] = "exact"; + } + else + config_tinymce['mode'] = "textareas"; + + tinymce.init(config_tinymce); +} -tinymce.init({ - mode : "exact", - elements : "biografia-parlamentar,casa-informacoes" - }); +function removeTinymce() { + while (tinymce.editors.length > 0) { + tinymce.remove(tinymce.editors[0]); + } +} function refreshDatePicker() { $.datepicker.setDefaults($.datepicker.regional['pt-BR']); @@ -29,7 +52,7 @@ function autorModal() { height: 300, show: { effect: "blind", - duration: 500 }, + duration: 500}, hide: { effect: "explode", duration: 500 @@ -96,4 +119,5 @@ $(document).ready(function(){ refreshDatePicker(); refreshMask(); autorModal(); + initTinymce("biografia-parlamentar,casa-informacoes"); }); diff --git a/static/js/compilacao.js b/static/js/compilacao.js index b36783c66..22093eed5 100644 --- a/static/js/compilacao.js +++ b/static/js/compilacao.js @@ -1,20 +1,3 @@ -function initTinymce() { - - tinymce.init({ - mode : "textareas", - force_br_newlines : false, - force_p_newlines : false, - forced_root_block : '', - plugins: ["table save code"], - menubar: "edit format table tools", - toolbar: "save | undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent", - tools: "inserttable", - save_onsavecallback: onSubmitEditForm, - border_css: "/static/styles/compilacao_tinymce.css", - content_css: "/static/styles/compilacao_tinymce.css" - }); -} - function SetCookie(cookieName,cookieValue,nDays) { var today = new Date(); var expire = new Date(); @@ -38,3 +21,211 @@ function insertWaitAjax(element) { //jQuery(element).append('
'); jQuery(element).append('
'); } + + + +function DispostivoSearch(opts) { + + $(function() { + + var container_ds = $('body').children("#container_ds"); + if (container_ds.length > 0) + $(container_ds).remove(); + container_ds = $('
'); + $('body').prepend(container_ds); + + var fields = $("[data-sapl-ta='DispositivoSearch']"); + fields.each(function() { + var field = $(this); + var data_type_selection = field.attr('data-type-selection'); + var data_field = field.attr('data-field'); + var data_function = field.attr('data-function'); + + var onChangeFieldSelects = function(event) { + if (data_type_selection == 'checkbox') { + var tas = field.find('input[name="ta_select_all"]'); //tas - Textos Articulados + tas.off(); + + tas.on('change', function(event) { + $(this).closest('ul').find('input[name="'+data_field+'"]').prop("checked", this.checked); + //$(this).prop("checked", false); + }); + + + } + else { + var dpts = field.find('input'); + dpts.off() + dpts.attr('type', 'hidden'); + $('') + .insertBefore(dpts) + .append($('')) + .on('click', function() { + if ($(this).closest('ul').find('li').length == 2) + $(this).closest('ul').remove(); + else + $(this).closest('li').remove(); + }); + } + } + onChangeFieldSelects(); + + var onChangeParamTA = function(event) { + var tipo_ta = $("select[name='tipo_ta']").val(); + var tipo_model = $("select[name='tipo_model']").val(); + var num_ta = $("input[name='num_ta']").val(); + var ano_ta = $("input[name='ano_ta']").val(); + var dispositivos_internos = $("input[name='dispositivos_internos']:checked").val(); + var rotulo_dispositivo = $("input[name='rotulo_dispositivo']").val(); + var texto_dispositivo = $("input[name='texto_dispositivo']").val(); + var max_results = $("select[name='max_results']").val(); + var url = ''; + + if (rotulo_dispositivo.length > 0 || texto_dispositivo.length > 0) { + $("input[name='dispositivos_internos']").prop('disabled', false); + $("input[name='dispositivos_internos']").closest('#div_id_dispositivos_internos').css('opacity','1'); + } + else { + $("input[name='dispositivos_internos']").filter('[value="False"]').prop('checked', true); + $("input[name='dispositivos_internos']").prop('disabled', true); + $("input[name='dispositivos_internos']").closest('#div_id_dispositivos_internos').css('opacity','0.3'); + dispositivos_internos = 'False'; + } + var formData = { + 'tipo_ta' : tipo_ta, + 'tipo_model' : tipo_model, + 'num_ta' : num_ta, + 'ano_ta' : ano_ta, + 'texto' : texto_dispositivo, + 'rotulo' : rotulo_dispositivo, + 'dispositivos_internos' : dispositivos_internos, + 'max_results' : max_results, + 'data_type_selection' : data_type_selection, + 'data_field' : data_field, + 'data_function' : data_function, + }; + + url = '/ta/search_fragment_form'; + $('.result-busca-dispositivo').html(''); + insertWaitAjax('.result-busca-dispositivo') + $.get(url, formData).done(function( data ) { + $('.result-busca-dispositivo').html(data); + + if (data_type_selection == 'checkbox') { + var tas = $('.result-busca-dispositivo').find('input[name="ta_select_all"]'); + tas.off(); + tas.on('change', function(event) { + $(this).closest('ul').find('input[name="'+data_field+'"]').prop("checked", this.checked); + }); + } + + }); + } + + var onKeyPressRotuloBuscaTextual = function(event) { + var rotulo_dispositivo = $("input[name='rotulo_dispositivo']").val(); + var texto_dispositivo = $("input[name='texto_dispositivo']").val(); + var dispositivos_internos = $("input[name='dispositivos_internos']:checked").val(); + + if (rotulo_dispositivo.length > 0 || texto_dispositivo.length > 0) { + $("input[name='dispositivos_internos']").prop('disabled', false); + $("input[name='dispositivos_internos']").closest('#div_id_dispositivos_internos').css('opacity','1'); + } + else { + $("input[name='dispositivos_internos']").filter('[value="False"]').prop('checked', true); + $("input[name='dispositivos_internos']").prop('disabled', true); + $("input[name='dispositivos_internos']").closest('#div_id_dispositivos_internos').css('opacity','0.3'); + dispositivos_internos = 'False'; + } + } + + var button_ds = field.children("#button_ds"); + if (button_ds.length > 0) + $(button_ds).remove(); + button_ds = $('
'); + field.prepend(button_ds); + + var btn_open_search = $(' + +
+ + +
+ + diff --git a/templates/compilacao/dispositivo_form_search_fragment.html b/templates/compilacao/dispositivo_form_search_fragment.html new file mode 100644 index 000000000..b80a5c1ec --- /dev/null +++ b/templates/compilacao/dispositivo_form_search_fragment.html @@ -0,0 +1,125 @@ +{% load i18n compilacao_filters %} + +{% if object_list.count >= 100 %} +
+ {% trans 'Use argumentos para simplificar listagem...' %} +
+{% endif %} + +{% for dpt in object_list %} + {% ifchanged dpt.ta%} + {% if not forloop.first %}{% endif %} + {% endif %} +{% endfor %} diff --git a/templates/compilacao/dispositivo_form_vigencia.html b/templates/compilacao/dispositivo_form_vigencia.html new file mode 100644 index 000000000..a894a6da2 --- /dev/null +++ b/templates/compilacao/dispositivo_form_vigencia.html @@ -0,0 +1,28 @@ +{% extends "compilacao/dispositivo_form.html" %} +{% load i18n %} +{% block extra_js %}{{block.super}} + +{% endblock %} diff --git a/templates/compilacao/dispositivo_search_fragment_form.html b/templates/compilacao/dispositivo_search_fragment_form.html deleted file mode 100644 index 385096057..000000000 --- a/templates/compilacao/dispositivo_search_fragment_form.html +++ /dev/null @@ -1,39 +0,0 @@ -{% load i18n %} -{% load compilacao_filters %} - -{% if object_list.count >= 100 %} -
- {% trans 'Use argumentos para simplificar listagem...' %} -
-{% endif %} - -{% for dpt in object_list %} - {% ifchanged dpt.ta%} - {% if not forloop.first %}{% endif %} -
{{dpt.ta}}
-
    - {% endifchanged %} - - {% if dpt.is_relative_auto_insert and dpt.dispositivo_pai.nivel != 0 %} -
  • -
    - -
    -
    - - {% nomenclatura_heranca dpt 1 1 %} -
    -
  • - {% elif not dpt.tipo_dispositivo.dispositivo_de_articulacao %} -
  • -
    - -
    -
    - - {% nomenclatura_heranca dpt 1 1 %} -
    -
  • - {% endif%} - {% if forloop.last %}
{% endif %} -{% endfor %} diff --git a/templates/compilacao/form.html b/templates/compilacao/form.html deleted file mode 100644 index e25d5d3ab..000000000 --- a/templates/compilacao/form.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "base.html" %} -{% load i18n crispy_forms_tags %} - -{% block base_content %} - {% crispy form %} -{% endblock %} diff --git a/templates/compilacao/layout/dispositivo_checkbox.html b/templates/compilacao/layout/dispositivo_checkbox.html new file mode 100644 index 000000000..b31e2fa70 --- /dev/null +++ b/templates/compilacao/layout/dispositivo_checkbox.html @@ -0,0 +1,54 @@ +{% load crispy_forms_filters %} +{% load i18n compilacao_filters common_tags%} + +
+ {% include 'bootstrap/layout/field_errors_block.html' %} + + {% for choice in field.field.choices %} + + {% ifchanged choice.1.ta%} + {% if not forloop.first %}{% endif %} + {% endif %} + {% endfor %} + + + + {% include 'bootstrap/layout/help_text.html' %} +
diff --git a/templates/compilacao/layout/dispositivo_radio.html b/templates/compilacao/layout/dispositivo_radio.html new file mode 100644 index 000000000..6f0891280 --- /dev/null +++ b/templates/compilacao/layout/dispositivo_radio.html @@ -0,0 +1,43 @@ +{% load crispy_forms_filters %} +{% load i18n compilacao_filters common_tags%} + + +
+ {% include 'bootstrap/layout/field_errors_block.html' %} + + {% for choice, dpt in field.field.choices %} + {% ifchanged dpt.ta%} + {% if not forloop.first %}{% endif %} +
    +
  • {{dpt.ta}}
  • + {% endifchanged %} + +
  • +
    + +
    +
    + {% if dpt.nivel > 1 %} + {% trans "Herança:" %} {% heranca request dpt 1 0 %} + {% endif %} +
    +
    + {{ dpt.tipo_dispositivo.rotulo_prefixo_html|safe }} + {% if dpt.rotulo or dpt.nivel = 1 %}{{ dpt.rotulo }}{%else%}[{{ dpt|nomenclatura}} {% trans "de" %} {{ dpt.dispositivo_pai.rotulo }}] - {% endif %} + {{ dpt.tipo_dispositivo.rotulo_sufixo_html|safe }} + {{ dpt.tipo_dispositivo.texto_prefixo_html|safe }}{%if dpt.texto %}{{ dpt.texto|safe }}{%else%}{%if not dpt.tipo_dispositivo.dispositivo_de_articulacao %} {% endif %}{% endif %} +
    +
    +
    +
  • + + {% if forloop.last %}
{% endif %} + {% endfor %} + + + + {% include 'bootstrap/layout/help_text.html' %} +
diff --git a/templates/compilacao/publicacao_list.html b/templates/compilacao/publicacao_list.html index fb80695b6..1c56a343a 100644 --- a/templates/compilacao/publicacao_list.html +++ b/templates/compilacao/publicacao_list.html @@ -9,9 +9,6 @@ {% trans 'Adicionar'%} {%model_verbose_name 'compilacao.models.Publicacao'%} - - {% trans 'Voltar' %} - {% if not object_list %} diff --git a/templates/compilacao/text_edit.html b/templates/compilacao/text_edit.html index 41df0d905..bab5b89de 100644 --- a/templates/compilacao/text_edit.html +++ b/templates/compilacao/text_edit.html @@ -14,7 +14,6 @@

Edição: {{ view.title }} - {% trans 'Texto Multivigente' %}

{% endblock %} -
{% trans 'Aguarde... Atualizando informações!!!'%}
{% block actions %}