diff --git a/mayan/apps/acls/models.py b/mayan/apps/acls/models.py index 171938c8be..800773dc75 100644 --- a/mayan/apps/acls/models.py +++ b/mayan/apps/acls/models.py @@ -70,6 +70,9 @@ class AccessControlList(models.Model): ) def get_permission_titles(self): + """ + Returns the descriptibe labels for the permissions. + """ result = ', '.join( [force_text(permission) for permission in self.permissions.all()] ) diff --git a/mayan/apps/cabinets/models.py b/mayan/apps/cabinets/models.py index 014d304285..d6fbe493f3 100644 --- a/mayan/apps/cabinets/models.py +++ b/mayan/apps/cabinets/models.py @@ -19,6 +19,12 @@ from .search import cabinet_search # NOQA @python_2_unicode_compatible 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( blank=True, db_index=True, null=True, on_delete=models.CASCADE, related_name='children', to='self' @@ -41,6 +47,10 @@ class Cabinet(MPTTModel): return self.get_full_path() 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) event_cabinets_add_document.commit( action_object=self, actor=user, target=document @@ -50,14 +60,26 @@ class Cabinet(MPTTModel): return reverse('cabinets:cabinet_view', args=(self.pk,)) 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() 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( permission_document_view, user, queryset=self.documents ) def get_full_path(self): + """ + Returns a string that represents the path to the cabinet. The + path string starts from the root cabinet. + """ result = [] for node in self.get_ancestors(include_self=True): result.append(node.label) @@ -65,17 +87,22 @@ class Cabinet(MPTTModel): return ' / '.join(result) def remove_document(self, document, user=None): + """ + Remove a document from a cabinet. This method provides the + corresponding event commit. + """ self.documents.remove(document) event_cabinets_remove_document.commit( action_object=self, actor=user, target=document ) 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 - # when there is a FK in the unique_together tuple - # https://code.djangoproject.com/ticket/1751 - + """ + Explicit validation of uniqueness of parent+label as the provided + unique_together check in Meta is not working for all 100% cases + when there is a FK in the unique_together tuple + https://code.djangoproject.com/ticket/1751 + """ with transaction.atomic(): if connection.vendor == 'oracle': queryset = Cabinet.objects.filter(parent=self.parent, label=self.label) @@ -102,6 +129,11 @@ class Cabinet(MPTTModel): 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: proxy = True verbose_name = _('Document cabinet') diff --git a/mayan/apps/checkouts/models.py b/mayan/apps/checkouts/models.py index 43a38d8848..490d658152 100644 --- a/mayan/apps/checkouts/models.py +++ b/mayan/apps/checkouts/models.py @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) @python_2_unicode_compatible 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( on_delete=models.CASCADE, to=Document, verbose_name=_('Document') @@ -99,6 +99,9 @@ class DocumentCheckout(models.Model): class NewVersionBlock(models.Model): + """ + Model to keep track of which documents have new version upload restricted. + """ document = models.ForeignKey( on_delete=models.CASCADE, to=Document, verbose_name=_('Document') ) diff --git a/mayan/apps/common/models.py b/mayan/apps/common/models.py index 1f4209c79a..41f736c700 100644 --- a/mayan/apps/common/models.py +++ b/mayan/apps/common/models.py @@ -20,6 +20,10 @@ def upload_to(instance, filename): 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( max_length=128, verbose_name=_('Namespace') ) @@ -46,6 +50,10 @@ class ErrorLogEntry(models.Model): @python_2_unicode_compatible 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( storage=storage_sharedupload, upload_to=upload_to, verbose_name=_('File') @@ -76,6 +84,10 @@ class SharedUploadedFile(models.Model): @python_2_unicode_compatible class UserLocaleProfile(models.Model): + """ + Stores the locale preferences of an user. Stores timezone and language + at the moment. + """ user = models.OneToOneField( on_delete=models.CASCADE, related_name='locale_profile', to=settings.AUTH_USER_MODEL, verbose_name=_('User') diff --git a/mayan/apps/django_gpg/models.py b/mayan/apps/django_gpg/models.py index 5569ee692a..1731532efb 100644 --- a/mayan/apps/django_gpg/models.py +++ b/mayan/apps/django_gpg/models.py @@ -65,6 +65,9 @@ class Key(models.Model): return '{} - {}'.format(self.key_id, self.user_id) def clean(self): + """ + Validate the key before saving. + """ import_results = gpg_backend.import_key(key_data=self.key_data) if not import_results.count: @@ -78,6 +81,9 @@ class Key(models.Model): @property def key_id(self): + """ + Short form key ID (using the first 8 characters). + """ return self.fingerprint[-8:] def save(self, *args, **kwargs): @@ -101,13 +107,15 @@ class Key(models.Model): super(Key, self).save(*args, **kwargs) 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. - # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=55647 - # "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." - + """ + Digitally sign a file + WARNING: using clearsign=True and subsequent decryption corrupts the + file. Appears to be a problem in python-gnupg or gpg itself. + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=55647 + "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_object=file_object, key_data=self.key_data, passphrase=passphrase, clearsign=clearsign, detached=detached, diff --git a/mayan/apps/document_comments/models.py b/mayan/apps/document_comments/models.py index 016591aae2..743e2de588 100644 --- a/mayan/apps/document_comments/models.py +++ b/mayan/apps/document_comments/models.py @@ -18,6 +18,9 @@ logger = logging.getLogger(__name__) @python_2_unicode_compatible class Comment(models.Model): + """ + Model to store one comment per document per user per date & time. + """ document = models.ForeignKey( db_index=True, on_delete=models.CASCADE, related_name='comments', to=Document, verbose_name=_('Document') diff --git a/mayan/apps/document_indexing/models.py b/mayan/apps/document_indexing/models.py index 528d92e1ca..90b3afa2a5 100644 --- a/mayan/apps/document_indexing/models.py +++ b/mayan/apps/document_indexing/models.py @@ -77,6 +77,14 @@ class Index(models.Model): ) 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) with transaction.atomic(): @@ -131,10 +139,18 @@ class Index(models.Model): @property def template_root(self): + """ + Return the root node for this index. + """ return self.node_templates.get(parent=None) 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: proxy = True verbose_name = _('Index instance') @@ -286,6 +302,12 @@ class IndexTemplateNode(MPTTModel): @python_2_unicode_compatible 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( blank=True, null=True, on_delete=models.CASCADE, to='self', ) @@ -311,6 +333,9 @@ class IndexInstanceNode(MPTTModel): return self.value def delete_empty(self): + """ + Method to delete all empty node instances in a recursive manner. + """ # Prevent another process to delete this node. try: lock = locking_backend.acquire_lock( @@ -374,6 +399,9 @@ class IndexInstanceNode(MPTTModel): return 'indexing:index_instance_node_{}'.format(self.pk) def index(self): + """ + Return's the index instance of this node instance. + """ return IndexInstance.objects.get(pk=self.index_template_node.index.pk) def remove_document(self, document): @@ -399,6 +427,11 @@ class IndexInstanceNode(MPTTModel): 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() class Meta: diff --git a/mayan/apps/document_parsing/models.py b/mayan/apps/document_parsing/models.py index 066bc4996d..4fad137201 100644 --- a/mayan/apps/document_parsing/models.py +++ b/mayan/apps/document_parsing/models.py @@ -11,6 +11,9 @@ from .managers import DocumentPageContentManager, DocumentTypeSettingsManager @python_2_unicode_compatible class DocumentPageContent(models.Model): + """ + This model store's the parsed content of a document page. + """ document_page = models.OneToOneField( on_delete=models.CASCADE, related_name='content', to=DocumentPage, verbose_name=_('Document page') @@ -33,6 +36,9 @@ class DocumentPageContent(models.Model): class DocumentTypeSettings(models.Model): + """ + This model stores the parsing settings for a document type. + """ document_type = models.OneToOneField( on_delete=models.CASCADE, related_name='parsing_settings', to=DocumentType, unique=True, verbose_name=_('Document type') @@ -56,6 +62,10 @@ class DocumentTypeSettings(models.Model): @python_2_unicode_compatible class DocumentVersionParseError(models.Model): + """ + This module stores the errors captures when attempting to parse a + document version. + """ document_version = models.ForeignKey( on_delete=models.CASCADE, related_name='parsing_errors', to=DocumentVersion, verbose_name=_('Document version') diff --git a/mayan/apps/events/models.py b/mayan/apps/events/models.py index 565c5e21d8..af9988d160 100644 --- a/mayan/apps/events/models.py +++ b/mayan/apps/events/models.py @@ -17,6 +17,9 @@ from .managers import ( @python_2_unicode_compatible class StoredEventType(models.Model): + """ + Model to mirror the real event classes as database objects. + """ name = models.CharField( max_length=64, unique=True, verbose_name=_('Name') ) @@ -42,6 +45,10 @@ class StoredEventType(models.Model): @python_2_unicode_compatible class EventSubscription(models.Model): + """ + This model stores the event subscriptions of an user for the entire + system. + """ user = models.ForeignKey( db_index=True, on_delete=models.CASCADE, related_name='event_subscriptions', to=settings.AUTH_USER_MODEL, @@ -64,6 +71,11 @@ class EventSubscription(models.Model): @python_2_unicode_compatible 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( db_index=True, on_delete=models.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL, diff --git a/mayan/apps/linking/models.py b/mayan/apps/linking/models.py index 19af031945..06b2deaa52 100644 --- a/mayan/apps/linking/models.py +++ b/mayan/apps/linking/models.py @@ -16,6 +16,11 @@ from .managers import SmartLinkManager @python_2_unicode_compatible 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( db_index=True, max_length=96, verbose_name=_('Label') ) @@ -43,6 +48,10 @@ class SmartLink(models.Model): return self.label 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: context = Context({'document': document}) try: @@ -58,6 +67,10 @@ class SmartLink(models.Model): return None 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): raise Exception( _( @@ -100,6 +113,10 @@ class SmartLink(models.Model): class ResolvedSmartLink(SmartLink): + """ + Proxy model to represent an already resolved smart link. Used for easier + colums registration. + """ class Meta: proxy = True @@ -109,6 +126,10 @@ class ResolvedSmartLink(SmartLink): @python_2_unicode_compatible 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( on_delete=models.CASCADE, related_name='conditions', to=SmartLink, verbose_name=_('Smart link') diff --git a/mayan/apps/lock_manager/managers.py b/mayan/apps/lock_manager/managers.py index 01a7405501..0493292b31 100644 --- a/mayan/apps/lock_manager/managers.py +++ b/mayan/apps/lock_manager/managers.py @@ -14,6 +14,11 @@ logger = logging.getLogger(__name__) class LockManager(models.Manager): 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) lock = self.model(name=name, timeout=timeout) diff --git a/mayan/apps/lock_manager/models.py b/mayan/apps/lock_manager/models.py index a66e17a08a..d409036201 100644 --- a/mayan/apps/lock_manager/models.py +++ b/mayan/apps/lock_manager/models.py @@ -10,6 +10,9 @@ from .settings import setting_default_lock_timeout @python_2_unicode_compatible class Lock(models.Model): + """ + Model to provide distributed resource locking using the database. + """ creation_datetime = models.DateTimeField( auto_now_add=True, verbose_name=_('Creation datetime') ) @@ -30,6 +33,9 @@ class Lock(models.Model): return self.name def release(self): + """ + Release a previously held lock. + """ try: lock = Lock.objects.get( name=self.name, creation_datetime=self.creation_datetime diff --git a/mayan/apps/mailer/models.py b/mayan/apps/mailer/models.py index d51fac223d..caa4cfcef4 100644 --- a/mayan/apps/mailer/models.py +++ b/mayan/apps/mailer/models.py @@ -33,6 +33,12 @@ class LogEntry(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( max_length=128, unique=True, verbose_name=_('Label') ) @@ -122,6 +128,9 @@ class UserMailer(models.Model): self.error_log.all().delete() def send_document(self, document, to, subject='', body='', as_attachment=False): + """ + Send a document using this user mailing profile. + """ context_dictionary = { 'link': 'http://%s%s' % ( Site.objects.get_current().domain, @@ -153,6 +162,10 @@ class UserMailer(models.Model): ) 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) diff --git a/mayan/apps/metadata/models.py b/mayan/apps/metadata/models.py index 5ef095dcca..542b7ceb35 100644 --- a/mayan/apps/metadata/models.py +++ b/mayan/apps/metadata/models.py @@ -40,7 +40,9 @@ def parser_choices(): @python_2_unicode_compatible 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( max_length=48, @@ -127,6 +129,10 @@ class MetadataType(models.Model): return MetadataType.comma_splitter(template.render(context=context)) 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( required=True, metadata_type=self ).exists() @@ -183,8 +189,8 @@ class MetadataType(models.Model): @python_2_unicode_compatible class DocumentMetadata(models.Model): """ - Link a document to a specific instance of a metadata type with it's - current value + Model used to link an instance of a metadata type with a value to a + document. """ document = models.ForeignKey( 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): """ - enforce_required prevents deletion of required metadata at the - model level. It used set to False when deleting document metadata - on document type change. + Delete a metadata from a document. enforce_required which defaults + to True, prevents deletion of required metadata at the model level. + 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): raise ValidationError( @@ -241,6 +248,10 @@ class DocumentMetadata(models.Model): @property 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( document_type=self.document.document_type ) @@ -272,6 +283,10 @@ class DocumentMetadata(models.Model): @python_2_unicode_compatible class DocumentTypeMetadataType(models.Model): + """ + Model used to store the relationship between a metadata type and a + document type. + """ document_type = models.ForeignKey( on_delete=models.CASCADE, related_name='metadata', to=DocumentType, verbose_name=_('Document type') diff --git a/mayan/apps/motd/models.py b/mayan/apps/motd/models.py index a907e1af6d..2aeca9892b 100644 --- a/mayan/apps/motd/models.py +++ b/mayan/apps/motd/models.py @@ -9,6 +9,10 @@ from .managers import MessageManager @python_2_unicode_compatible 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( max_length=32, help_text=_('Short description of this message.'), verbose_name=_('Label') diff --git a/mayan/apps/ocr/models.py b/mayan/apps/ocr/models.py index 41b2a125b4..e06268f8f7 100644 --- a/mayan/apps/ocr/models.py +++ b/mayan/apps/ocr/models.py @@ -13,7 +13,7 @@ from .managers import ( 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( on_delete=models.CASCADE, related_name='ocr_settings', @@ -37,6 +37,9 @@ class DocumentTypeSettings(models.Model): @python_2_unicode_compatible class DocumentPageOCRContent(models.Model): + """ + This model stores the OCR results for a document page. + """ document_page = models.OneToOneField( on_delete=models.CASCADE, related_name='ocr_content', to=DocumentPage, verbose_name=_('Document page') @@ -59,6 +62,10 @@ class DocumentPageOCRContent(models.Model): @python_2_unicode_compatible class DocumentVersionOCRError(models.Model): + """ + This models keeps track of the errors captured during the OCR of a + document version. + """ document_version = models.ForeignKey( on_delete=models.CASCADE, related_name='ocr_errors', to=DocumentVersion, verbose_name=_('Document version') diff --git a/mayan/apps/permissions/models.py b/mayan/apps/permissions/models.py index 186442a6c1..c0a8463c26 100644 --- a/mayan/apps/permissions/models.py +++ b/mayan/apps/permissions/models.py @@ -16,6 +16,11 @@ logger = logging.getLogger(__name__) @python_2_unicode_compatible 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')) name = models.CharField(max_length=64, verbose_name=_('Name')) @@ -34,9 +39,17 @@ class StoredPermission(models.Model): return self.name 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) def get_volatile_permission(self): + """ + Returns the real class of the permission represented by this model + instance. + """ return Permission.get( pk=self.get_volatile_permission_id(), proxy_only=True ) @@ -45,6 +58,12 @@ class StoredPermission(models.Model): return (self.namespace, self.name) 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: logger.debug( 'Permission "%s" granted to user "%s" as superuser or staff', @@ -70,6 +89,13 @@ class StoredPermission(models.Model): @python_2_unicode_compatible 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( max_length=64, unique=True, verbose_name=_('Label') ) diff --git a/mayan/apps/tags/models.py b/mayan/apps/tags/models.py index 7437d74b1c..c7b677aa43 100644 --- a/mayan/apps/tags/models.py +++ b/mayan/apps/tags/models.py @@ -19,6 +19,10 @@ from .widgets import widget_single_tag @python_2_unicode_compatible 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( db_index=True, help_text=_( 'A short text used as the tag name.' @@ -41,6 +45,9 @@ class Tag(models.Model): return self.label def attach_to(self, document, user=None): + """ + Attach a tag to a document and commit the corresponding event. + """ self.documents.add(document) event_tag_attach.commit( 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),)) 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( permission_document_view, user, queryset=self.documents ) @@ -61,6 +72,9 @@ class Tag(models.Model): get_preview_widget.short_description = _('Preview') def remove_from(self, document, user=None): + """ + Remove a tag from a document and commit the corresponding event. + """ self.documents.remove(document) event_tag_remove.commit( action_object=self, actor=user, target=document diff --git a/mayan/apps/user_management/models.py b/mayan/apps/user_management/models.py index 1950332835..878a8c1bc6 100644 --- a/mayan/apps/user_management/models.py +++ b/mayan/apps/user_management/models.py @@ -8,6 +8,11 @@ from .managers import UserOptionsManager 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( on_delete=models.CASCADE, related_name='user_options', to=settings.AUTH_USER_MODEL, unique=True, verbose_name=_('User')