Files
mayan-edms/mayan/apps/linking/models.py
Roberto Rosario 36a51eeb73 Switch to full app paths
Instead of inserting the path of the apps into the Python app,
the apps are now referenced by their full import path.

This solves name clashes with external or native Python libraries.
Example: Mayan statistics app vs. Python new statistics library.

Every app reference is now prepended with 'mayan.apps'.

Existing config.yml files need to be updated manually.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2019-04-05 02:02:57 -04:00

150 lines
5.0 KiB
Python

from __future__ import unicode_literals
from django.db import models
from django.db.models import Q
from django.template import Context, Template
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from mayan.apps.documents.models import Document, DocumentType
from .literals import (
INCLUSION_AND, INCLUSION_CHOICES, INCLUSION_OR, OPERATOR_CHOICES
)
from .managers import SmartLinkManager
@python_2_unicode_compatible
class SmartLink(models.Model):
label = models.CharField(
db_index=True, max_length=96, verbose_name=_('Label')
)
dynamic_label = models.CharField(
blank=True, max_length=96, help_text=_(
'Enter a template to render. '
'Use Django\'s default templating language '
'(https://docs.djangoproject.com/en/1.11/ref/templates/builtins/). '
'The {{ document }} context variable is available.'
), verbose_name=_('Dynamic label')
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
document_types = models.ManyToManyField(
to=DocumentType, verbose_name=_('Document types')
)
objects = SmartLinkManager()
class Meta:
ordering = ('label',)
verbose_name = _('Smart link')
verbose_name_plural = _('Smart links')
def __str__(self):
return self.label
def get_dynamic_label(self, document):
if self.dynamic_label:
context = Context({'document': document})
try:
template = Template(self.dynamic_label)
return template.render(context=context)
except Exception as exception:
return _(
'Error generating dynamic label; %s' % force_text(
exception
)
)
else:
return None
def get_linked_document_for(self, document):
if document.document_type.pk not in self.document_types.values_list('pk', flat=True):
raise Exception(
_(
'This smart link is not allowed for the selected '
'document\'s type.'
)
)
smart_link_query = Q()
context = Context({'document': document})
for condition in self.conditions.filter(enabled=True):
template = Template(condition.expression)
condition_query = Q(**{
'%s__%s' % (
condition.foreign_document_data, condition.operator
): template.render(context=context)
})
if condition.negated:
condition_query = ~condition_query
if condition.inclusion == INCLUSION_AND:
smart_link_query &= condition_query
elif condition.inclusion == INCLUSION_OR:
smart_link_query |= condition_query
if smart_link_query:
return Document.objects.filter(smart_link_query)
else:
return Document.objects.none()
def resolve_for(self, document):
return ResolvedSmartLink(
smart_link=self, queryset=self.get_linked_document_for(
document=document
)
)
class ResolvedSmartLink(SmartLink):
class Meta:
proxy = True
def get_label_for(self, document):
return self.get_dynamic_label(document=document) or self.label
@python_2_unicode_compatible
class SmartLinkCondition(models.Model):
smart_link = models.ForeignKey(
on_delete=models.CASCADE, related_name='conditions', to=SmartLink,
verbose_name=_('Smart link')
)
inclusion = models.CharField(
choices=INCLUSION_CHOICES, default=INCLUSION_AND,
help_text=_('The inclusion is ignored for the first item.'),
max_length=16
)
foreign_document_data = models.CharField(
help_text=_('This represents the metadata of all other documents.'),
max_length=128, verbose_name=_('Foreign document attribute')
)
operator = models.CharField(choices=OPERATOR_CHOICES, max_length=16)
expression = models.TextField(
help_text=_(
'Enter a template to render. '
'Use Django\'s default templating language '
'(https://docs.djangoproject.com/en/1.11/ref/templates/builtins/). '
'The {{ document }} context variable is available.'
), verbose_name=_('Expression')
)
negated = models.BooleanField(
default=False, help_text=_('Inverts the logic of the operator.'),
verbose_name=_('Negated')
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
class Meta:
verbose_name = _('Link condition')
verbose_name_plural = _('Link conditions')
def __str__(self):
return '%s foreign %s %s %s %s' % (
self.get_inclusion_display(),
self.foreign_document_data, _('not') if self.negated else '',
self.get_operator_display(), self.expression
)