Add docstrings for almost all models
Also adds docstring to some managers and model methods. Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -70,6 +70,9 @@ class AccessControlList(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_permission_titles(self):
|
def get_permission_titles(self):
|
||||||
|
"""
|
||||||
|
Returns the descriptibe labels for the permissions.
|
||||||
|
"""
|
||||||
result = ', '.join(
|
result = ', '.join(
|
||||||
[force_text(permission) for permission in self.permissions.all()]
|
[force_text(permission) for permission in self.permissions.all()]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ from .search import cabinet_search # NOQA
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Cabinet(MPTTModel):
|
class Cabinet(MPTTModel):
|
||||||
|
"""
|
||||||
|
Model to store a hierarchical tree of document containers. Each container
|
||||||
|
can store an unlimited number of documents using an M2M field. Only
|
||||||
|
the top level container is can have an ACL. All child container's
|
||||||
|
access is delegated to their corresponding root container.
|
||||||
|
"""
|
||||||
parent = TreeForeignKey(
|
parent = TreeForeignKey(
|
||||||
blank=True, db_index=True, null=True, on_delete=models.CASCADE,
|
blank=True, db_index=True, null=True, on_delete=models.CASCADE,
|
||||||
related_name='children', to='self'
|
related_name='children', to='self'
|
||||||
@@ -41,6 +47,10 @@ class Cabinet(MPTTModel):
|
|||||||
return self.get_full_path()
|
return self.get_full_path()
|
||||||
|
|
||||||
def add_document(self, document, user=None):
|
def add_document(self, document, user=None):
|
||||||
|
"""
|
||||||
|
Add a document to a container. This can be done without using this
|
||||||
|
method but this method provides the event commit already coded.
|
||||||
|
"""
|
||||||
self.documents.add(document)
|
self.documents.add(document)
|
||||||
event_cabinets_add_document.commit(
|
event_cabinets_add_document.commit(
|
||||||
action_object=self, actor=user, target=document
|
action_object=self, actor=user, target=document
|
||||||
@@ -50,14 +60,26 @@ class Cabinet(MPTTModel):
|
|||||||
return reverse('cabinets:cabinet_view', args=(self.pk,))
|
return reverse('cabinets:cabinet_view', args=(self.pk,))
|
||||||
|
|
||||||
def get_document_count(self, user):
|
def get_document_count(self, user):
|
||||||
|
"""
|
||||||
|
Return numeric count of the total documents in a cabinet. The count
|
||||||
|
is filtered by access.
|
||||||
|
"""
|
||||||
return self.get_documents_queryset(user=user).count()
|
return self.get_documents_queryset(user=user).count()
|
||||||
|
|
||||||
def get_documents_queryset(self, user):
|
def get_documents_queryset(self, user):
|
||||||
|
"""
|
||||||
|
Provide a queryset of the documents in a cabinet. The queryset is
|
||||||
|
filtered by access.
|
||||||
|
"""
|
||||||
return AccessControlList.objects.filter_by_access(
|
return AccessControlList.objects.filter_by_access(
|
||||||
permission_document_view, user, queryset=self.documents
|
permission_document_view, user, queryset=self.documents
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_full_path(self):
|
def get_full_path(self):
|
||||||
|
"""
|
||||||
|
Returns a string that represents the path to the cabinet. The
|
||||||
|
path string starts from the root cabinet.
|
||||||
|
"""
|
||||||
result = []
|
result = []
|
||||||
for node in self.get_ancestors(include_self=True):
|
for node in self.get_ancestors(include_self=True):
|
||||||
result.append(node.label)
|
result.append(node.label)
|
||||||
@@ -65,17 +87,22 @@ class Cabinet(MPTTModel):
|
|||||||
return ' / '.join(result)
|
return ' / '.join(result)
|
||||||
|
|
||||||
def remove_document(self, document, user=None):
|
def remove_document(self, document, user=None):
|
||||||
|
"""
|
||||||
|
Remove a document from a cabinet. This method provides the
|
||||||
|
corresponding event commit.
|
||||||
|
"""
|
||||||
self.documents.remove(document)
|
self.documents.remove(document)
|
||||||
event_cabinets_remove_document.commit(
|
event_cabinets_remove_document.commit(
|
||||||
action_object=self, actor=user, target=document
|
action_object=self, actor=user, target=document
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_unique(self, exclude=None):
|
def validate_unique(self, exclude=None):
|
||||||
# Explicit validation of uniqueness of parent+label as the provided
|
"""
|
||||||
# unique_together check in Meta is not working for all 100% cases
|
Explicit validation of uniqueness of parent+label as the provided
|
||||||
# when there is a FK in the unique_together tuple
|
unique_together check in Meta is not working for all 100% cases
|
||||||
# https://code.djangoproject.com/ticket/1751
|
when there is a FK in the unique_together tuple
|
||||||
|
https://code.djangoproject.com/ticket/1751
|
||||||
|
"""
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
if connection.vendor == 'oracle':
|
if connection.vendor == 'oracle':
|
||||||
queryset = Cabinet.objects.filter(parent=self.parent, label=self.label)
|
queryset = Cabinet.objects.filter(parent=self.parent, label=self.label)
|
||||||
@@ -102,6 +129,11 @@ class Cabinet(MPTTModel):
|
|||||||
|
|
||||||
|
|
||||||
class DocumentCabinet(Cabinet):
|
class DocumentCabinet(Cabinet):
|
||||||
|
"""
|
||||||
|
Represent a document's cabinet. This Model is a proxy model from Cabinet
|
||||||
|
and is used as an alias to map columns to it without having to map them
|
||||||
|
to the base Cabinet model.
|
||||||
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
verbose_name = _('Document cabinet')
|
verbose_name = _('Document cabinet')
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
|||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentCheckout(models.Model):
|
class DocumentCheckout(models.Model):
|
||||||
"""
|
"""
|
||||||
Model to store the state and information of a document checkout
|
Model to store the state and information of a document checkout.
|
||||||
"""
|
"""
|
||||||
document = models.OneToOneField(
|
document = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, to=Document, verbose_name=_('Document')
|
on_delete=models.CASCADE, to=Document, verbose_name=_('Document')
|
||||||
@@ -99,6 +99,9 @@ class DocumentCheckout(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class NewVersionBlock(models.Model):
|
class NewVersionBlock(models.Model):
|
||||||
|
"""
|
||||||
|
Model to keep track of which documents have new version upload restricted.
|
||||||
|
"""
|
||||||
document = models.ForeignKey(
|
document = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, to=Document, verbose_name=_('Document')
|
on_delete=models.CASCADE, to=Document, verbose_name=_('Document')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ def upload_to(instance, filename):
|
|||||||
|
|
||||||
|
|
||||||
class ErrorLogEntry(models.Model):
|
class ErrorLogEntry(models.Model):
|
||||||
|
"""
|
||||||
|
Class to store an error log for any object. Uses generic foreign keys to
|
||||||
|
reference the parent object.
|
||||||
|
"""
|
||||||
namespace = models.CharField(
|
namespace = models.CharField(
|
||||||
max_length=128, verbose_name=_('Namespace')
|
max_length=128, verbose_name=_('Namespace')
|
||||||
)
|
)
|
||||||
@@ -46,6 +50,10 @@ class ErrorLogEntry(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class SharedUploadedFile(models.Model):
|
class SharedUploadedFile(models.Model):
|
||||||
|
"""
|
||||||
|
Keep a database link to a stored file. Used to share files between code
|
||||||
|
that runs out of process.
|
||||||
|
"""
|
||||||
file = models.FileField(
|
file = models.FileField(
|
||||||
storage=storage_sharedupload, upload_to=upload_to,
|
storage=storage_sharedupload, upload_to=upload_to,
|
||||||
verbose_name=_('File')
|
verbose_name=_('File')
|
||||||
@@ -76,6 +84,10 @@ class SharedUploadedFile(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class UserLocaleProfile(models.Model):
|
class UserLocaleProfile(models.Model):
|
||||||
|
"""
|
||||||
|
Stores the locale preferences of an user. Stores timezone and language
|
||||||
|
at the moment.
|
||||||
|
"""
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='locale_profile',
|
on_delete=models.CASCADE, related_name='locale_profile',
|
||||||
to=settings.AUTH_USER_MODEL, verbose_name=_('User')
|
to=settings.AUTH_USER_MODEL, verbose_name=_('User')
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ class Key(models.Model):
|
|||||||
return '{} - {}'.format(self.key_id, self.user_id)
|
return '{} - {}'.format(self.key_id, self.user_id)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Validate the key before saving.
|
||||||
|
"""
|
||||||
import_results = gpg_backend.import_key(key_data=self.key_data)
|
import_results = gpg_backend.import_key(key_data=self.key_data)
|
||||||
|
|
||||||
if not import_results.count:
|
if not import_results.count:
|
||||||
@@ -78,6 +81,9 @@ class Key(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def key_id(self):
|
def key_id(self):
|
||||||
|
"""
|
||||||
|
Short form key ID (using the first 8 characters).
|
||||||
|
"""
|
||||||
return self.fingerprint[-8:]
|
return self.fingerprint[-8:]
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@@ -101,13 +107,15 @@ class Key(models.Model):
|
|||||||
super(Key, self).save(*args, **kwargs)
|
super(Key, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def sign_file(self, file_object, passphrase=None, clearsign=False, detached=False, binary=False, output=None):
|
def sign_file(self, file_object, passphrase=None, clearsign=False, detached=False, binary=False, output=None):
|
||||||
# WARNING: using clearsign=True and subsequent decryption corrupts the
|
"""
|
||||||
# file. Appears to be a problem in python-gnupg or gpg itself.
|
Digitally sign a file
|
||||||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=55647
|
WARNING: using clearsign=True and subsequent decryption corrupts the
|
||||||
# "The problems differ from run to run and file to
|
file. Appears to be a problem in python-gnupg or gpg itself.
|
||||||
# file, and appear to be due to random data being inserted in the
|
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=55647
|
||||||
# output data stream."
|
"The problems differ from run to run and file to
|
||||||
|
file, and appear to be due to random data being inserted in the
|
||||||
|
output data stream."
|
||||||
|
"""
|
||||||
file_sign_results = gpg_backend.sign_file(
|
file_sign_results = gpg_backend.sign_file(
|
||||||
file_object=file_object, key_data=self.key_data,
|
file_object=file_object, key_data=self.key_data,
|
||||||
passphrase=passphrase, clearsign=clearsign, detached=detached,
|
passphrase=passphrase, clearsign=clearsign, detached=detached,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Comment(models.Model):
|
class Comment(models.Model):
|
||||||
|
"""
|
||||||
|
Model to store one comment per document per user per date & time.
|
||||||
|
"""
|
||||||
document = models.ForeignKey(
|
document = models.ForeignKey(
|
||||||
db_index=True, on_delete=models.CASCADE, related_name='comments',
|
db_index=True, on_delete=models.CASCADE, related_name='comments',
|
||||||
to=Document, verbose_name=_('Document')
|
to=Document, verbose_name=_('Document')
|
||||||
|
|||||||
@@ -77,6 +77,14 @@ class Index(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def index_document(self, document):
|
def index_document(self, document):
|
||||||
|
"""
|
||||||
|
Method to start the indexing process for a document. The entire process
|
||||||
|
happens inside one transaction. The document is first removed from all
|
||||||
|
the index nodes to which it already belongs. The different index
|
||||||
|
templates that match this document's type are evaluated and for each
|
||||||
|
result a node is fetched or created and the document is added to that
|
||||||
|
node.
|
||||||
|
"""
|
||||||
logger.debug('Index; Indexing document: %s', document)
|
logger.debug('Index; Indexing document: %s', document)
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
@@ -131,10 +139,18 @@ class Index(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def template_root(self):
|
def template_root(self):
|
||||||
|
"""
|
||||||
|
Return the root node for this index.
|
||||||
|
"""
|
||||||
return self.node_templates.get(parent=None)
|
return self.node_templates.get(parent=None)
|
||||||
|
|
||||||
|
|
||||||
class IndexInstance(Index):
|
class IndexInstance(Index):
|
||||||
|
"""
|
||||||
|
Model that represents an evaluated index. This is an index whose nodes
|
||||||
|
have been evaluated against a series of documents. If is a proxy model
|
||||||
|
at the moment.
|
||||||
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
verbose_name = _('Index instance')
|
verbose_name = _('Index instance')
|
||||||
@@ -286,6 +302,12 @@ class IndexTemplateNode(MPTTModel):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class IndexInstanceNode(MPTTModel):
|
class IndexInstanceNode(MPTTModel):
|
||||||
|
"""
|
||||||
|
This model represent one instance node from a index template node. That is
|
||||||
|
a node template that has been evaluated against a document and the result
|
||||||
|
from that evaluation is this node's stored values. Instances of this
|
||||||
|
model also point to the original node template.
|
||||||
|
"""
|
||||||
parent = TreeForeignKey(
|
parent = TreeForeignKey(
|
||||||
blank=True, null=True, on_delete=models.CASCADE, to='self',
|
blank=True, null=True, on_delete=models.CASCADE, to='self',
|
||||||
)
|
)
|
||||||
@@ -311,6 +333,9 @@ class IndexInstanceNode(MPTTModel):
|
|||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def delete_empty(self):
|
def delete_empty(self):
|
||||||
|
"""
|
||||||
|
Method to delete all empty node instances in a recursive manner.
|
||||||
|
"""
|
||||||
# Prevent another process to delete this node.
|
# Prevent another process to delete this node.
|
||||||
try:
|
try:
|
||||||
lock = locking_backend.acquire_lock(
|
lock = locking_backend.acquire_lock(
|
||||||
@@ -374,6 +399,9 @@ class IndexInstanceNode(MPTTModel):
|
|||||||
return 'indexing:index_instance_node_{}'.format(self.pk)
|
return 'indexing:index_instance_node_{}'.format(self.pk)
|
||||||
|
|
||||||
def index(self):
|
def index(self):
|
||||||
|
"""
|
||||||
|
Return's the index instance of this node instance.
|
||||||
|
"""
|
||||||
return IndexInstance.objects.get(pk=self.index_template_node.index.pk)
|
return IndexInstance.objects.get(pk=self.index_template_node.index.pk)
|
||||||
|
|
||||||
def remove_document(self, document):
|
def remove_document(self, document):
|
||||||
@@ -399,6 +427,11 @@ class IndexInstanceNode(MPTTModel):
|
|||||||
|
|
||||||
|
|
||||||
class DocumentIndexInstanceNode(IndexInstanceNode):
|
class DocumentIndexInstanceNode(IndexInstanceNode):
|
||||||
|
"""
|
||||||
|
Proxy model of node instance. It is used to represent the node instance
|
||||||
|
in which a document is currently located. It is used to aid in column
|
||||||
|
registration. The inherited methods of this model should not be used.
|
||||||
|
"""
|
||||||
objects = DocumentIndexInstanceNodeManager()
|
objects = DocumentIndexInstanceNodeManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ from .managers import DocumentPageContentManager, DocumentTypeSettingsManager
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentPageContent(models.Model):
|
class DocumentPageContent(models.Model):
|
||||||
|
"""
|
||||||
|
This model store's the parsed content of a document page.
|
||||||
|
"""
|
||||||
document_page = models.OneToOneField(
|
document_page = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='content', to=DocumentPage,
|
on_delete=models.CASCADE, related_name='content', to=DocumentPage,
|
||||||
verbose_name=_('Document page')
|
verbose_name=_('Document page')
|
||||||
@@ -33,6 +36,9 @@ class DocumentPageContent(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class DocumentTypeSettings(models.Model):
|
class DocumentTypeSettings(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores the parsing settings for a document type.
|
||||||
|
"""
|
||||||
document_type = models.OneToOneField(
|
document_type = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='parsing_settings',
|
on_delete=models.CASCADE, related_name='parsing_settings',
|
||||||
to=DocumentType, unique=True, verbose_name=_('Document type')
|
to=DocumentType, unique=True, verbose_name=_('Document type')
|
||||||
@@ -56,6 +62,10 @@ class DocumentTypeSettings(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentVersionParseError(models.Model):
|
class DocumentVersionParseError(models.Model):
|
||||||
|
"""
|
||||||
|
This module stores the errors captures when attempting to parse a
|
||||||
|
document version.
|
||||||
|
"""
|
||||||
document_version = models.ForeignKey(
|
document_version = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, related_name='parsing_errors',
|
on_delete=models.CASCADE, related_name='parsing_errors',
|
||||||
to=DocumentVersion, verbose_name=_('Document version')
|
to=DocumentVersion, verbose_name=_('Document version')
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ from .managers import (
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class StoredEventType(models.Model):
|
class StoredEventType(models.Model):
|
||||||
|
"""
|
||||||
|
Model to mirror the real event classes as database objects.
|
||||||
|
"""
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64, unique=True, verbose_name=_('Name')
|
max_length=64, unique=True, verbose_name=_('Name')
|
||||||
)
|
)
|
||||||
@@ -42,6 +45,10 @@ class StoredEventType(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class EventSubscription(models.Model):
|
class EventSubscription(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores the event subscriptions of an user for the entire
|
||||||
|
system.
|
||||||
|
"""
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
db_index=True, on_delete=models.CASCADE,
|
db_index=True, on_delete=models.CASCADE,
|
||||||
related_name='event_subscriptions', to=settings.AUTH_USER_MODEL,
|
related_name='event_subscriptions', to=settings.AUTH_USER_MODEL,
|
||||||
@@ -64,6 +71,11 @@ class EventSubscription(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Notification(models.Model):
|
class Notification(models.Model):
|
||||||
|
"""
|
||||||
|
This model keeps track of the notifications for an user. Notifications are
|
||||||
|
created when an event to which this user has been subscribed, are
|
||||||
|
commited elsewhere in the system.
|
||||||
|
"""
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
db_index=True, on_delete=models.CASCADE,
|
db_index=True, on_delete=models.CASCADE,
|
||||||
related_name='notifications', to=settings.AUTH_USER_MODEL,
|
related_name='notifications', to=settings.AUTH_USER_MODEL,
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ from .managers import SmartLinkManager
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class SmartLink(models.Model):
|
class SmartLink(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores the basic fields for a smart link. Smart links allow
|
||||||
|
linking documents using a programmatic method of conditions that mirror
|
||||||
|
Django's database filter operations.
|
||||||
|
"""
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
db_index=True, max_length=96, verbose_name=_('Label')
|
db_index=True, max_length=96, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
@@ -43,6 +48,10 @@ class SmartLink(models.Model):
|
|||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
def get_dynamic_label(self, document):
|
def get_dynamic_label(self, document):
|
||||||
|
"""
|
||||||
|
If the smart links was created using a template label instead of a
|
||||||
|
static label, resolve the template and return the result.
|
||||||
|
"""
|
||||||
if self.dynamic_label:
|
if self.dynamic_label:
|
||||||
context = Context({'document': document})
|
context = Context({'document': document})
|
||||||
try:
|
try:
|
||||||
@@ -58,6 +67,10 @@ class SmartLink(models.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_linked_document_for(self, document):
|
def get_linked_document_for(self, document):
|
||||||
|
"""
|
||||||
|
Execute the corresponding smart links conditions for the document
|
||||||
|
provided and return the resulting document queryset.
|
||||||
|
"""
|
||||||
if document.document_type.pk not in self.document_types.values_list('pk', flat=True):
|
if document.document_type.pk not in self.document_types.values_list('pk', flat=True):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
_(
|
_(
|
||||||
@@ -100,6 +113,10 @@ class SmartLink(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class ResolvedSmartLink(SmartLink):
|
class ResolvedSmartLink(SmartLink):
|
||||||
|
"""
|
||||||
|
Proxy model to represent an already resolved smart link. Used for easier
|
||||||
|
colums registration.
|
||||||
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@@ -109,6 +126,10 @@ class ResolvedSmartLink(SmartLink):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class SmartLinkCondition(models.Model):
|
class SmartLinkCondition(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores a single smart link condition. A smart link is a
|
||||||
|
collection of one of more smart link conditions.
|
||||||
|
"""
|
||||||
smart_link = models.ForeignKey(
|
smart_link = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, related_name='conditions', to=SmartLink,
|
on_delete=models.CASCADE, related_name='conditions', to=SmartLink,
|
||||||
verbose_name=_('Smart link')
|
verbose_name=_('Smart link')
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class LockManager(models.Manager):
|
class LockManager(models.Manager):
|
||||||
def acquire_lock(self, name, timeout=None):
|
def acquire_lock(self, name, timeout=None):
|
||||||
|
"""
|
||||||
|
Attempts to acquire a lock. Return a LockError is the lock is already
|
||||||
|
held by someone else or if is not possible to acquire the lock due to
|
||||||
|
database or operational errors.
|
||||||
|
"""
|
||||||
logger.debug('trying to acquire lock: %s', name)
|
logger.debug('trying to acquire lock: %s', name)
|
||||||
lock = self.model(name=name, timeout=timeout)
|
lock = self.model(name=name, timeout=timeout)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ from .settings import setting_default_lock_timeout
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Lock(models.Model):
|
class Lock(models.Model):
|
||||||
|
"""
|
||||||
|
Model to provide distributed resource locking using the database.
|
||||||
|
"""
|
||||||
creation_datetime = models.DateTimeField(
|
creation_datetime = models.DateTimeField(
|
||||||
auto_now_add=True, verbose_name=_('Creation datetime')
|
auto_now_add=True, verbose_name=_('Creation datetime')
|
||||||
)
|
)
|
||||||
@@ -30,6 +33,9 @@ class Lock(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
|
"""
|
||||||
|
Release a previously held lock.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
lock = Lock.objects.get(
|
lock = Lock.objects.get(
|
||||||
name=self.name, creation_datetime=self.creation_datetime
|
name=self.name, creation_datetime=self.creation_datetime
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ class LogEntry(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class UserMailer(models.Model):
|
class UserMailer(models.Model):
|
||||||
|
"""
|
||||||
|
This model is used to create mailing profiles that can be used from inside
|
||||||
|
the system. These profiles differ from the system mailing profile in that
|
||||||
|
they can be created at runtime and can be assigned ACLs to restrict
|
||||||
|
their use.
|
||||||
|
"""
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=128, unique=True, verbose_name=_('Label')
|
max_length=128, unique=True, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
@@ -122,6 +128,9 @@ class UserMailer(models.Model):
|
|||||||
self.error_log.all().delete()
|
self.error_log.all().delete()
|
||||||
|
|
||||||
def send_document(self, document, to, subject='', body='', as_attachment=False):
|
def send_document(self, document, to, subject='', body='', as_attachment=False):
|
||||||
|
"""
|
||||||
|
Send a document using this user mailing profile.
|
||||||
|
"""
|
||||||
context_dictionary = {
|
context_dictionary = {
|
||||||
'link': 'http://%s%s' % (
|
'link': 'http://%s%s' % (
|
||||||
Site.objects.get_current().domain,
|
Site.objects.get_current().domain,
|
||||||
@@ -153,6 +162,10 @@ class UserMailer(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test(self, to):
|
def test(self, to):
|
||||||
|
"""
|
||||||
|
Send a test message to make sure the mailing profile settings are
|
||||||
|
correct.
|
||||||
|
"""
|
||||||
self.send(subject=_('Test email from Mayan EDMS'), to=to)
|
self.send(subject=_('Test email from Mayan EDMS'), to=to)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ def parser_choices():
|
|||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class MetadataType(models.Model):
|
class MetadataType(models.Model):
|
||||||
"""
|
"""
|
||||||
Define a type of metadata
|
Model to store a type of metadata. Metadata are user defined properties
|
||||||
|
that can be assigned a value for each document. Metadata types need
|
||||||
|
to be assigned to a document type before they can be used.
|
||||||
"""
|
"""
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=48,
|
max_length=48,
|
||||||
@@ -127,6 +129,10 @@ class MetadataType(models.Model):
|
|||||||
return MetadataType.comma_splitter(template.render(context=context))
|
return MetadataType.comma_splitter(template.render(context=context))
|
||||||
|
|
||||||
def get_required_for(self, document_type):
|
def get_required_for(self, document_type):
|
||||||
|
"""
|
||||||
|
Return a queryset of metadata types that are required for the
|
||||||
|
specified document type.
|
||||||
|
"""
|
||||||
return document_type.metadata.filter(
|
return document_type.metadata.filter(
|
||||||
required=True, metadata_type=self
|
required=True, metadata_type=self
|
||||||
).exists()
|
).exists()
|
||||||
@@ -183,8 +189,8 @@ class MetadataType(models.Model):
|
|||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentMetadata(models.Model):
|
class DocumentMetadata(models.Model):
|
||||||
"""
|
"""
|
||||||
Link a document to a specific instance of a metadata type with it's
|
Model used to link an instance of a metadata type with a value to a
|
||||||
current value
|
document.
|
||||||
"""
|
"""
|
||||||
document = models.ForeignKey(
|
document = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, related_name='metadata', to=Document,
|
on_delete=models.CASCADE, related_name='metadata', to=Document,
|
||||||
@@ -218,9 +224,10 @@ class DocumentMetadata(models.Model):
|
|||||||
|
|
||||||
def delete(self, enforce_required=True, *args, **kwargs):
|
def delete(self, enforce_required=True, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
enforce_required prevents deletion of required metadata at the
|
Delete a metadata from a document. enforce_required which defaults
|
||||||
model level. It used set to False when deleting document metadata
|
to True, prevents deletion of required metadata at the model level.
|
||||||
on document type change.
|
It used set to False when deleting document metadata on document
|
||||||
|
type change.
|
||||||
"""
|
"""
|
||||||
if enforce_required and self.metadata_type.pk in self.document.document_type.metadata.filter(required=True).values_list('metadata_type', flat=True):
|
if enforce_required and self.metadata_type.pk in self.document.document_type.metadata.filter(required=True).values_list('metadata_type', flat=True):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
@@ -241,6 +248,10 @@ class DocumentMetadata(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_required(self):
|
def is_required(self):
|
||||||
|
"""
|
||||||
|
Return a boolean value of True of this metadata instance's parent type
|
||||||
|
is required for the stored document type.
|
||||||
|
"""
|
||||||
return self.metadata_type.get_required_for(
|
return self.metadata_type.get_required_for(
|
||||||
document_type=self.document.document_type
|
document_type=self.document.document_type
|
||||||
)
|
)
|
||||||
@@ -272,6 +283,10 @@ class DocumentMetadata(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentTypeMetadataType(models.Model):
|
class DocumentTypeMetadataType(models.Model):
|
||||||
|
"""
|
||||||
|
Model used to store the relationship between a metadata type and a
|
||||||
|
document type.
|
||||||
|
"""
|
||||||
document_type = models.ForeignKey(
|
document_type = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, related_name='metadata', to=DocumentType,
|
on_delete=models.CASCADE, related_name='metadata', to=DocumentType,
|
||||||
verbose_name=_('Document type')
|
verbose_name=_('Document type')
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ from .managers import MessageManager
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Message(models.Model):
|
class Message(models.Model):
|
||||||
|
"""
|
||||||
|
Model to store an information message that will be displayed at the login
|
||||||
|
screen. Messages can have an activation and deactivation date.
|
||||||
|
"""
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=32, help_text=_('Short description of this message.'),
|
max_length=32, help_text=_('Short description of this message.'),
|
||||||
verbose_name=_('Label')
|
verbose_name=_('Label')
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .managers import (
|
|||||||
|
|
||||||
class DocumentTypeSettings(models.Model):
|
class DocumentTypeSettings(models.Model):
|
||||||
"""
|
"""
|
||||||
Define for OCR for a specific document should behave
|
Model to store the OCR settings for a document type.
|
||||||
"""
|
"""
|
||||||
document_type = models.OneToOneField(
|
document_type = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='ocr_settings',
|
on_delete=models.CASCADE, related_name='ocr_settings',
|
||||||
@@ -37,6 +37,9 @@ class DocumentTypeSettings(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentPageOCRContent(models.Model):
|
class DocumentPageOCRContent(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores the OCR results for a document page.
|
||||||
|
"""
|
||||||
document_page = models.OneToOneField(
|
document_page = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='ocr_content',
|
on_delete=models.CASCADE, related_name='ocr_content',
|
||||||
to=DocumentPage, verbose_name=_('Document page')
|
to=DocumentPage, verbose_name=_('Document page')
|
||||||
@@ -59,6 +62,10 @@ class DocumentPageOCRContent(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentVersionOCRError(models.Model):
|
class DocumentVersionOCRError(models.Model):
|
||||||
|
"""
|
||||||
|
This models keeps track of the errors captured during the OCR of a
|
||||||
|
document version.
|
||||||
|
"""
|
||||||
document_version = models.ForeignKey(
|
document_version = models.ForeignKey(
|
||||||
on_delete=models.CASCADE, related_name='ocr_errors',
|
on_delete=models.CASCADE, related_name='ocr_errors',
|
||||||
to=DocumentVersion, verbose_name=_('Document version')
|
to=DocumentVersion, verbose_name=_('Document version')
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class StoredPermission(models.Model):
|
class StoredPermission(models.Model):
|
||||||
|
"""
|
||||||
|
This model is the counterpart of the permissions.classes.Permission
|
||||||
|
class. Allows storing a database counterpart of a permission class.
|
||||||
|
It is used to store the permissions help by a role or in an ACL.
|
||||||
|
"""
|
||||||
namespace = models.CharField(max_length=64, verbose_name=_('Namespace'))
|
namespace = models.CharField(max_length=64, verbose_name=_('Namespace'))
|
||||||
name = models.CharField(max_length=64, verbose_name=_('Name'))
|
name = models.CharField(max_length=64, verbose_name=_('Name'))
|
||||||
|
|
||||||
@@ -34,9 +39,17 @@ class StoredPermission(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_volatile_permission_id(self):
|
def get_volatile_permission_id(self):
|
||||||
|
"""
|
||||||
|
Return the identifier of the real permission class represented by
|
||||||
|
this model instance.
|
||||||
|
"""
|
||||||
return '{}.{}'.format(self.namespace, self.name)
|
return '{}.{}'.format(self.namespace, self.name)
|
||||||
|
|
||||||
def get_volatile_permission(self):
|
def get_volatile_permission(self):
|
||||||
|
"""
|
||||||
|
Returns the real class of the permission represented by this model
|
||||||
|
instance.
|
||||||
|
"""
|
||||||
return Permission.get(
|
return Permission.get(
|
||||||
pk=self.get_volatile_permission_id(), proxy_only=True
|
pk=self.get_volatile_permission_id(), proxy_only=True
|
||||||
)
|
)
|
||||||
@@ -45,6 +58,12 @@ class StoredPermission(models.Model):
|
|||||||
return (self.namespace, self.name)
|
return (self.namespace, self.name)
|
||||||
|
|
||||||
def requester_has_this(self, user):
|
def requester_has_this(self, user):
|
||||||
|
"""
|
||||||
|
Helper method to check if an user has been granted this permission.
|
||||||
|
The check is done sequentially over all of the user's groups and
|
||||||
|
roles. The check is interrupted at the first positive result.
|
||||||
|
The check always returns True for superusers or staff users.
|
||||||
|
"""
|
||||||
if user.is_superuser or user.is_staff:
|
if user.is_superuser or user.is_staff:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Permission "%s" granted to user "%s" as superuser or staff',
|
'Permission "%s" granted to user "%s" as superuser or staff',
|
||||||
@@ -70,6 +89,13 @@ class StoredPermission(models.Model):
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Role(models.Model):
|
class Role(models.Model):
|
||||||
|
"""
|
||||||
|
This model represents a Role. Roles are permission units. They are the
|
||||||
|
only object to which permissions can be granted. They are themselves
|
||||||
|
containers too, containing Groups, which are organization units. Roles
|
||||||
|
are the basic method to grant a permission to a group. Permissions granted
|
||||||
|
to a group using a role, are granted for the entire system.
|
||||||
|
"""
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=64, unique=True, verbose_name=_('Label')
|
max_length=64, unique=True, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ from .widgets import widget_single_tag
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
|
"""
|
||||||
|
This model represents a binary property that can be applied to a document.
|
||||||
|
The tag can have a label and a color.
|
||||||
|
"""
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
db_index=True, help_text=_(
|
db_index=True, help_text=_(
|
||||||
'A short text used as the tag name.'
|
'A short text used as the tag name.'
|
||||||
@@ -41,6 +45,9 @@ class Tag(models.Model):
|
|||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
def attach_to(self, document, user=None):
|
def attach_to(self, document, user=None):
|
||||||
|
"""
|
||||||
|
Attach a tag to a document and commit the corresponding event.
|
||||||
|
"""
|
||||||
self.documents.add(document)
|
self.documents.add(document)
|
||||||
event_tag_attach.commit(
|
event_tag_attach.commit(
|
||||||
action_object=self, actor=user, target=document
|
action_object=self, actor=user, target=document
|
||||||
@@ -50,6 +57,10 @@ class Tag(models.Model):
|
|||||||
return reverse('tags:tag_tagged_item_list', args=(str(self.pk),))
|
return reverse('tags:tag_tagged_item_list', args=(str(self.pk),))
|
||||||
|
|
||||||
def get_document_count(self, user):
|
def get_document_count(self, user):
|
||||||
|
"""
|
||||||
|
Return the numeric count of documents that have this tag attached.
|
||||||
|
The count if filtered by access.
|
||||||
|
"""
|
||||||
queryset = AccessControlList.objects.filter_by_access(
|
queryset = AccessControlList.objects.filter_by_access(
|
||||||
permission_document_view, user, queryset=self.documents
|
permission_document_view, user, queryset=self.documents
|
||||||
)
|
)
|
||||||
@@ -61,6 +72,9 @@ class Tag(models.Model):
|
|||||||
get_preview_widget.short_description = _('Preview')
|
get_preview_widget.short_description = _('Preview')
|
||||||
|
|
||||||
def remove_from(self, document, user=None):
|
def remove_from(self, document, user=None):
|
||||||
|
"""
|
||||||
|
Remove a tag from a document and commit the corresponding event.
|
||||||
|
"""
|
||||||
self.documents.remove(document)
|
self.documents.remove(document)
|
||||||
event_tag_remove.commit(
|
event_tag_remove.commit(
|
||||||
action_object=self, actor=user, target=document
|
action_object=self, actor=user, target=document
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ from .managers import UserOptionsManager
|
|||||||
|
|
||||||
|
|
||||||
class UserOptions(models.Model):
|
class UserOptions(models.Model):
|
||||||
|
"""
|
||||||
|
This model stores administrative configurations for an user accounts.
|
||||||
|
At the moment it stores a boolean flag to restrict an user's
|
||||||
|
ability to change their password.
|
||||||
|
"""
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
on_delete=models.CASCADE, related_name='user_options',
|
on_delete=models.CASCADE, related_name='user_options',
|
||||||
to=settings.AUTH_USER_MODEL, unique=True, verbose_name=_('User')
|
to=settings.AUTH_USER_MODEL, unique=True, verbose_name=_('User')
|
||||||
|
|||||||
Reference in New Issue
Block a user