class CmjAuditoriaModelMixin(CmjModelMixin): owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+', on_delete=PROTECT) modifier = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('modifier'), related_name='+', on_delete=PROTECT) class Meta: abstract = True def save(self, force_insert=False, force_update=False, using=None, update_fields=None, clean=True): field = 'modifier' if self.id else 'owner' u = get_user_connected_stack() if u: setattr(self, field, u) return CmjModelMixin.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields, clean=clean)
class Notificacao(CmjModelMixin): objects = NotificacaoManager() user = models.ForeignKey(get_settings_auth_user_model(), blank=True, null=True, default=None, verbose_name=_('Usuário Notificado'), related_name='notificacao_set', on_delete=CASCADE) user_origin = models.ForeignKey(get_settings_auth_user_model(), blank=True, null=True, default=None, on_delete=PROTECT, verbose_name=_('owner'), related_name='notificacao_origem_set') content_type = models.ForeignKey(ContentType, blank=True, null=True, default=None, on_delete=PROTECT) object_id = models.PositiveIntegerField(blank=True, null=True, default=None) content_object = GenericForeignKey('content_type', 'object_id') areatrabalho = models.ForeignKey('AreaTrabalho', blank=True, null=True, default=None, on_delete=PROTECT, verbose_name=_('Área de Trabalho'), related_name='notificacao_set') read = models.BooleanField(_('Lida'), choices=YES_NO_CHOICES, default=False) class Meta: verbose_name = _('Notificação') verbose_name_plural = _('Notificações') permissions = (( 'popup_notificacao', _('Visualização das notificações em Popup no Avatar do Usuário')), ) @property def user_name(self): return '%s - %s' % (self.user.get_display_name(), self.user.email) def __str__(self): return self.user_name
class CmjAuditoriaModelMixin(CmjModelMixin): owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+', on_delete=PROTECT) modifier = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('modifier'), related_name='+', on_delete=PROTECT) class Meta: abstract = True
class PermissionsUserClasse(CMSMixin): user = models.ForeignKey(get_settings_auth_user_model(), blank=True, null=True, default=None, verbose_name=_('Usuário')) classe = models.ForeignKey(Classe, verbose_name=_('Classe'), related_name='permissions_user_set') permission = models.ForeignKey(Permission, blank=True, null=True, default=None, verbose_name=_('Permissão')) def __str__(self): return '%s - %s' % (self.permission, self.user or '') def validate_unique(self, exclude=None): if 'classe' in exclude: exclude.remove('classe') CMSMixin.validate_unique(self, exclude=exclude) class Meta: unique_together = (('user', 'classe', 'permission'), ) verbose_name = _('Permissão de Usuário para Classe') verbose_name_plural = _('Permissões de Usuários para Classes')
class MensagemSolicitacao(models.Model): created = models.DateTimeField(verbose_name=_('created'), editable=False, auto_now_add=True) owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+') descricao = models.TextField(default='', verbose_name=_('Descrição')) solicitacao = models.ForeignKey(Solicitacao, verbose_name=_('Solicitação'), related_name='mensagemsolicitacao_set') notificacoes = GenericRelation(Notificacao, related_query_name='notificacoes') class Meta: ordering = ('created', ) verbose_name = _('Mensagem de Solicitação') verbose_name_plural = _('Mensagens de Solicitação') @property def email_notify(self): return { 'subject': self.solicitacao.email_notify['subject'], } """'body': ('Solicitação: ' + self.solicitacao.titulo, self.descricao), 'owner': self.owner, 'created': self.created""" def __str__(self): return self.descricao
class OperadorAreaTrabalho(CmjAuditoriaModelMixin): user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Operador da Área de Trabalho'), related_name='operadorareatrabalho_set', on_delete=CASCADE) areatrabalho = models.ForeignKey(AreaTrabalho, related_name='operadorareatrabalho_set', verbose_name=_('Área de Trabalho'), on_delete=CASCADE) grupos_associados = models.ManyToManyField( Group, verbose_name=_('Grupos Associados'), related_name='operadorareatrabalho_set') @property def user_name(self): return '%s - %s' % (self.user.get_display_name(), self.user.email) class Meta: verbose_name = _('Operador') verbose_name_plural = _('Operadores') def __str__(self): return self.user_name
class AreaTrabalho(CmjAuditoriaModelMixin): from sapl.parlamentares.models import Parlamentar objects = AreaTrabalhoManager() TIPO_GABINETE = 10 TIPO_ADMINISTRATIVO = 20 TIPO_INSTITUCIONAL = 30 TIPO_PROCURADORIA = 90 TIPO_PUBLICO = 99 TIPO_AREATRABALHO_CHOICE = CmjChoices( (TIPO_GABINETE, 'tipo_gabinete', _('Gabinete Parlamentar')), (TIPO_ADMINISTRATIVO, 'tipo_administrativo', _('Setor Administrativo')), (TIPO_INSTITUCIONAL, 'tipo_institucional', _('Institucional')), (TIPO_PROCURADORIA, 'tipo_procuradoria', _('Procuradoria Jurídica')), (TIPO_PUBLICO, 'tipo_publico', _('Documentos Públicos')), ) nome = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Nome')) descricao = models.CharField(default='', max_length=254, verbose_name=_('Descrição')) operadores = models.ManyToManyField(get_settings_auth_user_model(), through='OperadorAreaTrabalho', through_fields=('areatrabalho', 'user'), symmetrical=False, related_name='areatrabalho_set') parlamentar = models.ForeignKey(Parlamentar, verbose_name=_('Parlamentar'), related_name='areatrabalho_set', blank=True, null=True, on_delete=SET_NULL) tipo = models.IntegerField(_('Tipo da Área de Trabalho'), choices=TIPO_AREATRABALHO_CHOICE, default=TIPO_GABINETE) ativo = models.BooleanField(_('Área de trabalho Ativa'), choices=YES_NO_CHOICES, default=True) class Meta: ordering = ('-ativo', '-tipo', 'parlamentar') verbose_name = _('Área de Trabalho') verbose_name_plural = _('Áreas de Trabalho') def __str__(self): return self.nome
class Revisao(models.Model): STATUS_PRIVATE = 99 STATUS_RESTRICT_PERMISSION = 2 STATUS_RESTRICT_USER = 1 STATUS_PUBLIC = 0 VISIBILIDADE_STATUS = ( (STATUS_RESTRICT_PERMISSION, _('Restrição por Permissão')), (STATUS_RESTRICT_USER, _('Restrição por Usuário')), (STATUS_PRIVATE, _('Privado')), (STATUS_PUBLIC, _('Público')), ) data = models.DateTimeField( verbose_name=_('created'), editable=False, auto_now_add=True) user = models.ForeignKey( get_settings_auth_user_model(), verbose_name=_('user'), related_name='+') json = JSONField(verbose_name=_('Json')) content_type = models.ForeignKey( ContentType, blank=True, null=True, default=None) object_id = models.PositiveIntegerField( blank=True, null=True, default=None) content_object = GenericForeignKey('content_type', 'object_id') visibilidade = models.IntegerField( _('Visibilidade'), choices=VISIBILIDADE_STATUS, blank=True, null=True, default=None) class Meta: ordering = ('-data',) verbose_name = _('Revisão') verbose_name_plural = _('Revisões') @classmethod def gerar_revisao(cls, instance_model, user): revisao = Revisao() if user: revisao.user = user else: revisao.user_id = 1 # FIXME: necessário para execuções manual... # resolver no model revisao.content_object = instance_model revisao.json = serializers.serialize("json", (instance_model,)) if hasattr(instance_model, 'visibilidade'): revisao.visibilidade = instance_model.visibilidade revisao.save() return revisao
class Aluno(EscolaMixin): user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Usuário de Acesso'), related_name='+', on_delete=PROTECT) class Meta: verbose_name = _('Aluno') verbose_name_plural = _('Alunos')
class Autor(models.Model): operadores = models.ManyToManyField(get_settings_auth_user_model(), through='OperadorAutor', through_fields=('autor', 'user'), symmetrical=False, related_name='autor_set', verbose_name='Operadores') tipo = models.ForeignKey(TipoAutor, verbose_name=_('Tipo do Autor'), on_delete=models.PROTECT) content_type = models.ForeignKey(ContentType, blank=True, null=True, default=None, on_delete=PROTECT) object_id = models.PositiveIntegerField(blank=True, null=True, default=None) autor_related = GenericForeignKey('content_type', 'object_id') nome = models.CharField(max_length=120, blank=True, verbose_name=_('Nome do Autor')) cargo = models.CharField(max_length=50, blank=True) class Meta: verbose_name = _('Autor') verbose_name_plural = _('Autores') unique_together = (('content_type', 'object_id'), ) ordering = ('nome', ) def __str__(self): if self.autor_related: return str(self.autor_related) else: if self.nome: if self.cargo: return '{} - {}'.format(self.nome, self.cargo) else: return str(self.nome) if self.operadores.exists(): return str(self.operadores.first()) return '?'
class PermissionsUserDocumento(CMSMixin): user = models.ForeignKey(get_settings_auth_user_model(), blank=True, null=True, default=None, verbose_name=_('Usuário')) documento = models.ForeignKey(Documento, related_name='permissions_user_set', verbose_name=_('Documento')) permission = models.ForeignKey(Permission, blank=True, null=True, default=None, verbose_name=_('Permissão')) class Meta: unique_together = ( ('user', 'documento', 'permission'), ) verbose_name = _('Permissão de Usuário para Documento') verbose_name_plural = _('Permissões de Usuários para Documentos')
class OperadorAutor(CmjAuditoriaModelMixin): user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Operador do Autor'), related_name='operadorautor_set', on_delete=CASCADE) autor = models.ForeignKey(Autor, related_name='operadorautor_set', verbose_name=_('Autor'), on_delete=CASCADE) operador_principal = models.BooleanField( verbose_name=_('Operador Principal do Autor'), choices=YES_NO_CHOICES, default=False) enviar_email = models.BooleanField( verbose_name=_('Enviar emails de notificação a este usuário'), choices=YES_NO_CHOICES, default=False) @property def user_name(self): return '%s - %s' % (self.user.get_display_name(), self.user.email) class Meta: verbose_name = _('Operador do Autor') verbose_name_plural = _('Operadores do Autor') unique_together = (( 'user', 'autor', ), ) def __str__(self): return self.user_name
class MensagemSolicitacao(models.Model): created = models.DateTimeField(verbose_name=_('created'), editable=False, auto_now_add=True) owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+') descricao = models.TextField(default='', verbose_name=_('Descrição')) solicitacao = models.ForeignKey(Solicitacao, verbose_name=_('Solicitação'), related_name='mensagemsolicitacao_set') notificacoes = GenericRelation(Notificacao, related_query_name='notificacoes') anexo = models.FileField( blank=True, null=True, storage=media_protected_storage, upload_to=anexo_ouvidoria_path, verbose_name=_('Anexo'), validators=[restringe_tipos_de_arquivo_midias], help_text=_('Envie um arquivo em anexo a sua mensagem.')) content_type = models.CharField(max_length=250, default='') class Meta: ordering = ('created', ) verbose_name = _('Mensagem de Solicitação') verbose_name_plural = _('Mensagens de Solicitação') def delete(self, using=None, keep_parents=False): if self.anexo: self.anexo.delete() return models.Model.delete(self, using=using, keep_parents=keep_parents) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.pk and self.anexo: anexo = self.anexo self.anexo = None models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) self.anexo = anexo self.content_type = self.anexo.file.content_type if self.anexo.file.content_type in TIPOS_MIDIAS_PERMITIDOS: name_file = 'anexo.%s' % TIPOS_MIDIAS_PERMITIDOS[ self.content_type] self.anexo.save(name_file, self.anexo.file) models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) @property def email_notify(self): return { 'subject': self.solicitacao.email_notify['subject'], } """'body': ('Solicitação: ' + self.solicitacao.titulo, self.descricao), 'owner': self.owner, 'created': self.created""" def __str__(self): return self.descricao
class Solicitacao(models.Model): STATUS_RESTRICT = 1 STATUS_PUBLIC = 0 VISIBILIDADE_STATUS = CmjChoices( (STATUS_RESTRICT, 'status_restrict', _('Restrito')), (STATUS_PUBLIC, 'status_public', _('Público')), ) TIPO_ACESSO_INFORMACAO = 10 TIPO_ELOGIO = 20 TIPO_SUGESTAO = 30 TIPO_RECLAMACAO = 40 TIPO_DENUNCIA = 900 TIPO_SOLICITACAO_CHOICE = CmjChoices( (TIPO_ACESSO_INFORMACAO, 'tipo_acesso', _('Acesso a Informação')), (TIPO_ELOGIO, 'tipo_elogio', _('Elogio')), (TIPO_SUGESTAO, 'tipo_sugestao', _('Sugestão')), (TIPO_RECLAMACAO, 'tipo_reclamacao', _('Reclamação')), (TIPO_DENUNCIA, 'tipo_denuncia', _('Denúncia')), ) created = models.DateTimeField(verbose_name=_('created'), editable=False, auto_now_add=True) owner = models.ForeignKey(get_settings_auth_user_model(), blank=True, null=True, default=None, on_delete=PROTECT, verbose_name=_('owner'), related_name='+') titulo = models.CharField(default='', max_length=254, verbose_name=_('Título')) descricao = models.TextField(default='', verbose_name=_('Descrição')) visibilidade = models.IntegerField(_('Visibilidade'), choices=VISIBILIDADE_STATUS, default=STATUS_RESTRICT) areatrabalho = models.ForeignKey(AreaTrabalho, verbose_name=_('Área de Trablho')) tipo = models.IntegerField(_('Tipo de Solicitação'), choices=TIPO_SOLICITACAO_CHOICE, default=TIPO_ACESSO_INFORMACAO) notificacoes = GenericRelation(Notificacao, related_query_name='notificacoes') class Meta: verbose_name = _('Solicitação') verbose_name_plural = _('Solicitações') def __str__(self): return self.titulo @property def email_notify(self): if self.owner: return { 'subject': _('Solicitação (%s) de %s') % (self.get_tipo_display(), self.owner), } else: return { 'subject': _('Denuncia Anônima'), }
class Contato(CmjSearchMixin, CmjAuditoriaModelMixin): nome = models.CharField(max_length=100, verbose_name=_('Nome')) nome_social = models.CharField(blank=True, default='', max_length=100, verbose_name=_('Nome Social')) apelido = models.CharField(blank=True, default='', max_length=100, verbose_name=_('Apelido')) data_nascimento = models.DateField(blank=True, null=True, verbose_name=_('Data de Nascimento')) sexo = models.CharField(max_length=1, blank=True, verbose_name=_('Sexo Biológico'), choices=SEXO_CHOICE) identidade_genero = models.CharField(blank=True, default='', max_length=100, verbose_name=_('Como se reconhece?')) tem_filhos = models.NullBooleanField(choices=NONE_YES_NO_CHOICES, default=None, verbose_name=_('Tem Filhos?')) quantos_filhos = models.PositiveSmallIntegerField( default=0, blank=True, verbose_name=_('Quantos Filhos?')) estado_civil = models.ForeignKey(EstadoCivil, related_name='contato_set', blank=True, null=True, on_delete=SET_NULL, verbose_name=_('Estado Civil')) nivel_instrucao = models.ForeignKey(NivelInstrucao, related_name='contato_set', blank=True, null=True, on_delete=SET_NULL, verbose_name=_('Nivel de Instrução')) naturalidade = models.CharField(max_length=50, blank=True, verbose_name=_('Naturalidade')) nome_pai = models.CharField(max_length=100, blank=True, verbose_name=_('Nome do Pai')) nome_mae = models.CharField(max_length=100, blank=True, verbose_name=_('Nome da Mãe')) numero_sus = models.CharField(max_length=100, blank=True, verbose_name=_('Número do SUS')) cpf = models.CharField(max_length=15, blank=True, verbose_name=_('CPF')) titulo_eleitor = models.CharField(max_length=15, blank=True, verbose_name=_('Título de Eleitor')) rg = models.CharField(max_length=30, blank=True, verbose_name=_('RG')) rg_orgao_expedidor = models.CharField(max_length=20, blank=True, verbose_name=_('Órgão Expedidor')) rg_data_expedicao = models.DateField(blank=True, null=True, verbose_name=_('Data de Expedição')) ativo = models.BooleanField(choices=YES_NO_CHOICES, default=True, verbose_name=_('Ativo?')) workspace = models.ForeignKey(AreaTrabalho, verbose_name=_('Área de Trabalho'), related_name='contato_set', blank=True, null=True, on_delete=PROTECT) perfil_user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Perfil do Usuário'), related_name='contato_set', blank=True, null=True, on_delete=CASCADE) profissao = models.CharField(max_length=254, blank=True, verbose_name=_('Profissão')) tipo_autoridade = models.ForeignKey( TipoAutoridade, verbose_name=TipoAutoridade._meta.verbose_name, related_name='contato_set', blank=True, null=True, on_delete=SET_NULL) cargo = models.CharField(max_length=254, blank=True, default='', verbose_name=_('Cargo/Função')) pronome_tratamento = models.ForeignKey( PronomeTratamento, verbose_name=PronomeTratamento._meta.verbose_name, related_name='contato_set', blank=True, null=True, on_delete=SET_NULL, help_text=_('O pronome de tratamento é opcional, mas será \ obrigatório caso seja selecionado um tipo de autoridade.')) observacoes = models.TextField( blank=True, default='', verbose_name=_('Outros observações sobre o Contato')) @property def fields_search(self): return ['nome', 'nome_social', 'apelido'] class Meta: verbose_name = _('Contato') verbose_name_plural = _('Contatos') ordering = ['nome'] permissions = ( ('print_impressoenderecamento', _('Pode Imprimir Impressos de Endereçamento')), ('print_rel_contato_agrupado_por_processo', _('Pode Imprimir Relatório de Contatos Agrupados por Processo')), ('print_rel_contato_agrupado_por_grupo', _('Pode Imprimir Relatório de Contatos Agrupados ' 'Grupos de Contato')), ) unique_together = (('nome', 'data_nascimento', 'workspace', 'perfil_user'), ) def __str__(self): return self.nome
class CMSMixin(models.Model): STATUS_PRIVATE = 99 STATUS_RESTRICT = 1 STATUS_PUBLIC = 0 VISIBILIDADE_STATUS = CmjChoices( (STATUS_RESTRICT, 'status_restrict', _('Restrito')), (STATUS_PUBLIC, 'status_public', _('Público')), (STATUS_PRIVATE, 'status_private', _('Privado')), ) ALINHAMENTO_LEFT = 0 ALINHAMENTO_JUSTIFY = 1 ALINHAMENTO_RIGHT = 2 ALINHAMENTO_CENTER = 3 alinhamento_choice = CmjChoices( (ALINHAMENTO_LEFT, 'alinhamento_left', _('Alinhamento Esquerdo')), (ALINHAMENTO_JUSTIFY, 'alinhamento_justify', _('Alinhamento Completo')), (ALINHAMENTO_RIGHT, 'alinhamento_right', _('Alinhamento Direito')), (ALINHAMENTO_CENTER, 'alinhamento_center', _('Alinhamento Centralizado')), ) TD_NEWS = 0 TD_DOC = 5 TD_BI = 10 TD_GALERIA_PUBLICA = 20 TD_AUDIO_NEWS = 30 TD_VIDEO_NEWS = 40 TPD_TEXTO = 100 TPD_FILE = 200 TPD_CONTAINER_SIMPLES = 700 TPD_CONTAINER_EXTENDIDO = 701 TPD_CONTAINER_FILE = 750 TPD_VIDEO = 800 TPD_AUDIO = 850 TPD_IMAGE = 900 TPD_GALLERY = 901 # Documentos completos TDs = (TD_NEWS, TD_DOC, TD_BI, TD_GALERIA_PUBLICA, TD_AUDIO_NEWS, TD_VIDEO_NEWS) # Containers TDc = (TPD_CONTAINER_SIMPLES, TPD_CONTAINER_EXTENDIDO, TPD_CONTAINER_FILE) # Partes TDp = (TPD_TEXTO, TPD_FILE, TPD_VIDEO, TPD_AUDIO, TPD_IMAGE, TPD_GALLERY) # Tipos não acessiveis diretamente via URL TDp_exclude_render = (TPD_TEXTO, TPD_CONTAINER_SIMPLES, TPD_CONTAINER_EXTENDIDO, TPD_VIDEO, TPD_AUDIO) tipo_parte_doc = { 'documentos': CmjChoices( (TD_NEWS, 'td_news', _('Notícia')), (TD_DOC, 'td_doc', _('Documento')), (TD_BI, 'td_bi', _('Banco de Imagem')), (TD_GALERIA_PUBLICA, 'td_galeria_publica', _('Galeria Pública')), (TD_AUDIO_NEWS, 'td_audio_news', _('Áudio Notícia')), (TD_VIDEO_NEWS, 'td_video_news', _('Vídeo Notícia')), ), 'containers': CmjChoices( (TPD_CONTAINER_SIMPLES, 'container', _('Container Simples')), (TPD_CONTAINER_EXTENDIDO, 'container_fluid', _('Container Extendido')), (TPD_CONTAINER_FILE, 'container_file', _('Container de Imagens para Arquivo PDF')), ), 'subtipos': CmjChoices( (TPD_TEXTO, 'tpd_texto', _('Texto')), (TPD_FILE, 'tpd_file', _('Arquivo')), (TPD_VIDEO, 'tpd_video', _('Vídeo')), (TPD_AUDIO, 'tpd_audio', _('Áudio')), (TPD_IMAGE, 'tpd_image', _('Imagem')), (TPD_GALLERY, 'tpd_gallery', _('Galeria de Imagens')), ) } tipo_parte_doc_choice = (tipo_parte_doc['documentos'] + tipo_parte_doc['containers'] + tipo_parte_doc['subtipos']) created = models.DateTimeField(verbose_name=_('created'), editable=False, auto_now_add=True) public_date = models.DateTimeField( null=True, default=None, verbose_name=_('Data de Início de Publicação')) public_end_date = models.DateTimeField( null=True, default=None, verbose_name=_('Data de Fim de Publicação')) owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+') descricao = models.TextField(verbose_name=_('Descrição'), blank=True, null=True, default=None) autor = models.TextField(verbose_name=_('Autor'), blank=True, null=True, default=None) visibilidade = models.IntegerField(_('Visibilidade'), choices=VISIBILIDADE_STATUS, default=STATUS_PRIVATE) listar = models.BooleanField(_('Aparecer nas Listagens'), choices=YES_NO_CHOICES, default=True) class Meta: abstract = True @property def revisoes(self): # implementado como property, e não como GR, devido a necessidade # de manter a Revisão se o Documento for excluido. concret_model = None for kls in reversed(self.__class__.__mro__): if issubclass(kls, CMSMixin) and not kls._meta.abstract: concret_model = kls qs = Revisao.objects.filter( content_type=ContentType.objects.get_for_model(concret_model), object_id=self.pk) return qs def clean(self): """ Check for instances with null values in unique_together fields. """ from django.core.exceptions import ValidationError super(CMSMixin, self).clean() for field_tuple in self._meta.unique_together[:]: unique_filter = {} unique_fields = [] null_found = False for field_name in field_tuple: field_value = getattr(self, field_name) if getattr(self, field_name) is None: unique_filter['%s__isnull' % field_name] = True null_found = True else: unique_filter['%s' % field_name] = field_value unique_fields.append(field_name) if null_found: unique_queryset = self.__class__.objects.filter( **unique_filter) if self.pk: unique_queryset = unique_queryset.exclude(pk=self.pk) if unique_queryset.exists(): msg = self.unique_error_message(self.__class__, tuple(unique_fields)) raise ValidationError(msg)
class VersaoDeMidia(models.Model): created = models.DateTimeField(verbose_name=_('created'), editable=False, auto_now_add=True) owner = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('owner'), related_name='+') file = models.FileField(blank=True, null=True, storage=media_protected, upload_to=media_path, verbose_name=_('Mídia'), validators=[restringe_tipos_de_arquivo_midias]) content_type = models.CharField(max_length=250, default='') midia = models.ForeignKey(Midia, verbose_name=_('Mídia Versionada'), related_name='versions') def delete(self, using=None, keep_parents=False): if self.file: self.file.delete() return models.Model.delete(self, using=using, keep_parents=keep_parents) def save(self, force_insert=False, force_update=False, using=None, update_fields=None, with_file=None): _ret = models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) if not with_file: return _ret mime, ext = restringe_tipos_de_arquivo_midias(with_file) name_file = 'midia.%s' % ext self.content_type = mime self.file.save(name_file, File(with_file)) @cached_property def simple_name(self): return self.file.name.split('/')[-1] @cached_property def width(self): try: nf = '%s/%s' % (media_protected.location, self.file.name) im = Image.open(nf) return im.width except: return 0 @cached_property def height(self): try: nf = '%s/%s' % (media_protected.location, self.file.name) im = Image.open(nf) return im.height except: return 0 @cached_property def is_paisagem(self): return self.height < self.width def rotate(self, rotate): import os try: nf = '%s/%s' % (media_protected.location, self.file.name) im = Image.open(nf) im = im.rotate(rotate, resample=LANCZOS, expand=True) im.save(nf, dpi=(300, 300)) im.close() dirname = os.path.dirname(self.file.path) for f in os.listdir(dirname): filename = '%s/%s' % (dirname, f) if filename == nf: continue os.remove(filename) except Exception as e: pass def thumbnail(self, width='thumb'): sizes = { '24': (24, 24), '48': (48, 48), '96': (96, 96), '128': (128, 128), '256': (256, 256), '512': (512, 512), '768': (768, 768), '1024': (1024, 1024), } if width not in sizes: width = '96' nf = '%s/%s' % (media_protected.location, self.file.name) nft = nf.split('/') nft = '%s/%s.%s' % ('/'.join(nft[:-1]), width, nft[-1]) if os.path.exists(nft): file = io.open(nft, 'rb') return file im = Image.open(nf) if sizes[width][0] >= im.width: file = io.open(nf, 'rb') else: if sizes[width][0] < 512: if im.width > im.height: im.thumbnail(sizes[width]) else: size = (int(sizes[width][0] * (im.width / im.height)), int(sizes[width][1] * (im.width / im.height))) im.thumbnail(size) else: im.thumbnail(sizes[width], resample=LANCZOS) im.save(nft) im.close() file = io.open(nft, 'rb') return file def icon(self): return self.get_filename.split('.')[-1] class Meta: ordering = ('-created', ) verbose_name = _('Mídia') verbose_name_plural = _('Mídias')
class AuditLog(models.Model): objects = AuditLogManager() operation_choice = ('C', 'D', 'U') user = models.ForeignKey(get_settings_auth_user_model(), verbose_name=_('Usuário'), on_delete=SET_NULL, blank=True, null=True, default=None) email = models.CharField(max_length=100, verbose_name=_('email'), blank=True, db_index=True) operation = models.CharField(max_length=1, verbose_name=_('operation'), db_index=True) timestamp = models.DateTimeField(verbose_name=_('timestamp'), editable=False, auto_now_add=True) obj = JSONField(verbose_name=_('Object'), blank=True, null=True, default=None, encoder=DjangoJSONEncoder) content_type = models.ForeignKey(ContentType, blank=True, null=True, default=None, on_delete=PROTECT) object_id = models.PositiveIntegerField(blank=True, null=True, default=None) content_object = GenericForeignKey('content_type', 'object_id') obj_id = models.PositiveIntegerField(verbose_name=_('object_id'), db_index=True) model_name = models.CharField(max_length=100, verbose_name=_('model'), db_index=True) app_name = models.CharField(max_length=100, verbose_name=_('app'), db_index=True) class Meta: verbose_name = _('AuditLog') verbose_name_plural = _('AuditLogs') ordering = ('-id', ) def __str__(self): return "[%s] %s %s.%s %s" % ( self.timestamp, self.operation, self.app_name, self.model_name, self.user, )