diff --git a/.tx/config b/.tx/config index b4cdcd1b61..d1e3202680 100644 --- a/.tx/config +++ b/.tx/config @@ -13,18 +13,18 @@ source_lang = en source_file = mayan/apps/appearance/locale/en/LC_MESSAGES/django.po type = PO -[mayan-edms.autoadmin-2-0] -file_filter = mayan/apps/autoadmin/locale//LC_MESSAGES/django.po -source_lang = en -source_file = mayan/apps/autoadmin/locale/en/LC_MESSAGES/django.po -type = PO - [mayan-edms.authentication-2-0] file_filter = mayan/apps/authentication/locale//LC_MESSAGES/django.po source_lang = en source_file = mayan/apps/authentication/locale/en/LC_MESSAGES/django.po type = PO +[mayan-edms.autoadmin-2-0] +file_filter = mayan/apps/autoadmin/locale//LC_MESSAGES/django.po +source_lang = en +source_file = mayan/apps/autoadmin/locale/en/LC_MESSAGES/django.po +type = PO + [mayan-edms.cabinets-2-0] file_filter = mayan/apps/cabinets/locale//LC_MESSAGES/django.po source_lang = en @@ -103,6 +103,12 @@ source_lang = en source_file = mayan/apps/events/locale/en/LC_MESSAGES/django.po type = PO +[mayan-edms.file_caching-2-0] +file_filter = mayan/apps/file_caching/locale//LC_MESSAGES/django.po +source_lang = en +source_file = mayan/apps/file_caching/locale/en/LC_MESSAGES/django.po +type = PO + [mayan-edms.linking-2-0] file_filter = mayan/apps/linking/locale//LC_MESSAGES/django.po source_lang = en diff --git a/contrib/scripts/process_messages.py b/contrib/scripts/process_messages.py index afdbdd33a1..a63e87516c 100755 --- a/contrib/scripts/process_messages.py +++ b/contrib/scripts/process_messages.py @@ -12,10 +12,11 @@ APP_LIST = ( 'acls', 'appearance', 'authentication', 'autoadmin', 'cabinets', 'checkouts', 'common', 'converter', 'django_gpg', 'document_comments', 'document_indexing', 'document_parsing', 'document_signatures', - 'document_states', 'documents', 'dynamic_search', 'events', 'linking', - 'lock_manager', 'mayan_statistics', 'mailer', 'metadata', 'mirroring', - 'motd', 'navigation', 'ocr', 'permissions', 'rest_api', 'smart_settings', - 'sources', 'storage', 'tags', 'task_manager', 'user_management' + 'document_states', 'documents', 'dynamic_search', 'events', + 'file_caching', 'linking', 'lock_manager', 'mayan_statistics', 'mailer', + 'metadata', 'mirroring', 'motd', 'navigation', 'ocr', 'permissions', + 'rest_api', 'smart_settings', 'sources', 'storage', 'tags', + 'task_manager', 'user_management' ) LANGUAGE_LIST = ( diff --git a/mayan/apps/common/migrations/0012_auto_20181203_0812.py b/mayan/apps/common/migrations/0012_auto_20181203_0812.py deleted file mode 100644 index 13cfca2bac..0000000000 --- a/mayan/apps/common/migrations/0012_auto_20181203_0812.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import unicode_literals - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0011_auto_20180429_0758'), - ] - - operations = [ - migrations.CreateModel( - name='Cache', - fields=[ - ( - 'id', models.AutoField( - auto_created=True, primary_key=True, serialize=False, - verbose_name='ID' - ) - ), - ( - 'name', models.CharField( - max_length=128, unique=True, verbose_name='Name' - ) - ), - ( - 'label', models.CharField( - max_length=128, verbose_name='Label' - ) - ), - ( - 'maximum_size', models.PositiveIntegerField( - verbose_name='Maximum size' - ) - ), - ( - 'storage_instance_path', models.CharField( - max_length=255, unique=True, - verbose_name='Storage instance path' - ) - ), - ], - options={ - 'verbose_name': 'Cache', - 'verbose_name_plural': 'Caches', - }, - ), - migrations.CreateModel( - name='CachePartition', - fields=[ - ( - 'id', models.AutoField( - auto_created=True, primary_key=True, serialize=False, - verbose_name='ID' - ) - ), - ( - 'name', models.CharField( - max_length=128, verbose_name='Name' - ) - ), - ( - 'cache', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='partitions', to='common.Cache', - verbose_name='Cache' - ) - ), - ], - options={ - 'verbose_name': 'Cache partition', - 'verbose_name_plural': 'Cache partitions', - }, - ), - migrations.CreateModel( - name='CachePartitionFile', - fields=[ - ( - 'id', models.AutoField( - auto_created=True, primary_key=True, serialize=False, - verbose_name='ID' - ) - ), - ( - 'datetime', models.DateTimeField( - auto_now_add=True, db_index=True, - verbose_name='Date time' - ) - ), - ( - 'filename', models.CharField( - max_length=255, verbose_name='Filename' - ) - ), - ( - 'file_size', models.PositiveIntegerField( - default=0, verbose_name='File size' - ) - ), - ( - 'partition', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='files', to='common.CachePartition', - verbose_name='Cache partition' - ) - ), - ], - options={ - 'get_latest_by': 'datetime', - 'verbose_name': 'Cache partition file', - 'verbose_name_plural': 'Cache partition files', - }, - ), - migrations.AlterUniqueTogether( - name='cachepartitionfile', - unique_together=set([('partition', 'filename')]), - ), - migrations.AlterUniqueTogether( - name='cachepartition', - unique_together=set([('cache', 'name')]), - ), - ] diff --git a/mayan/apps/common/models.py b/mayan/apps/common/models.py index 9edd690708..7d8fc62a18 100644 --- a/mayan/apps/common/models.py +++ b/mayan/apps/common/models.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -from contextlib import contextmanager import logging import uuid @@ -9,17 +8,10 @@ from pytz import common_timezones from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType -from django.core.files.base import ContentFile -from django.db import models, transaction -from django.db.models import Sum +from django.db import models from django.utils.encoding import force_text, python_2_unicode_compatible -from django.utils.functional import cached_property -from django.utils.module_loading import import_string from django.utils.translation import ugettext_lazy as _ -from mayan.apps.lock_manager import LockError -from mayan.apps.lock_manager.runtime import locking_backend - from .managers import ErrorLogEntryManager, UserLocaleProfileManager from .storages import storage_sharedupload @@ -30,171 +22,6 @@ def upload_to(instance, filename): return 'shared-file-{}'.format(uuid.uuid4().hex) -@python_2_unicode_compatible -class Cache(models.Model): - name = models.CharField( - max_length=128, unique=True, verbose_name=_('Name') - ) - label = models.CharField(max_length=128, verbose_name=_('Label')) - maximum_size = models.PositiveIntegerField(verbose_name=_('Maximum size')) - storage_instance_path = models.CharField( - max_length=255, unique=True, verbose_name=_('Storage instance path') - ) - - class Meta: - verbose_name = _('Cache') - verbose_name_plural = _('Caches') - - def __str__(self): - return self.label - - def get_files(self): - return CachePartitionFile.objects.filter(partition__cache__id=self.pk) - - def get_total_size(self): - return self.get_files().aggregate( - file_size__sum=Sum('file_size') - )['file_size__sum'] or 0 - - def prune(self): - while self.get_total_size() > self.maximum_size: - self.get_files().earliest().delete() - - def purge(self): - for partition in self.partitions.all(): - partition.purge() - - def save(self, *args, **kwargs): - result = super(Cache, self).save(*args, **kwargs) - self.prune() - return result - - @cached_property - def storage(self): - return import_string(self.storage_instance_path) - - -class CachePartition(models.Model): - cache = models.ForeignKey( - on_delete=models.CASCADE, related_name='partitions', - to=Cache, verbose_name=_('Cache') - ) - name = models.CharField( - max_length=128, verbose_name=_('Name') - ) - - class Meta: - unique_together = ('cache', 'name') - verbose_name = _('Cache partition') - verbose_name_plural = _('Cache partitions') - - @staticmethod - def get_combined_filename(parent, filename): - return '{}-{}'.format(parent, filename) - - @contextmanager - def create_file(self, filename): - lock_id = 'cache_partition-create_file-{}-{}'.format(self.pk, filename) - try: - logger.debug('trying to acquire lock: %s', lock_id) - lock = locking_backend.acquire_lock(lock_id) - logger.debug('acquired lock: %s', lock_id) - try: - self.cache.prune() - - # Since open "wb+" doesn't create files force the creation of an - # empty file. - self.cache.storage.delete( - name=self.get_full_filename(filename=filename) - ) - self.cache.storage.save( - name=self.get_full_filename(filename=filename), - content=ContentFile(content='') - ) - - try: - with transaction.atomic(): - partition_file = self.files.create(filename=filename) - yield partition_file.open(mode='wb') - partition_file.update_size() - except Exception as exception: - logger.error( - 'Unexpected exception while trying to save new ' - 'cache file; %s', exception - ) - self.cache.storage.delete( - name=self.get_full_filename(filename=filename) - ) - raise - finally: - lock.release() - except LockError: - logger.debug('unable to obtain lock: %s' % lock_id) - raise - - def get_file(self, filename): - try: - return self.files.get(filename=filename) - except self.files.model.DoesNotExist: - return None - - def get_full_filename(self, filename): - return CachePartition.get_combined_filename( - parent=self.name, filename=filename - ) - - def purge(self): - for parition_file in self.files.all(): - parition_file.delete() - - -class CachePartitionFile(models.Model): - partition = models.ForeignKey( - on_delete=models.CASCADE, related_name='files', - to=CachePartition, verbose_name=_('Cache partition') - ) - datetime = models.DateTimeField( - auto_now_add=True, db_index=True, verbose_name=_('Date time') - ) - filename = models.CharField(max_length=255, verbose_name=_('Filename')) - file_size = models.PositiveIntegerField( - default=0, verbose_name=_('File size') - ) - - class Meta: - get_latest_by = 'datetime' - unique_together = ('partition', 'filename') - verbose_name = _('Cache partition file') - verbose_name_plural = _('Cache partition files') - - def delete(self, *args, **kwargs): - self.partition.cache.storage.delete(name=self.full_filename) - return super(CachePartitionFile, self).delete(*args, **kwargs) - - @cached_property - def full_filename(self): - return CachePartition.get_combined_filename( - parent=self.partition.name, filename=self.filename - ) - - def open(self, mode='rb'): - try: - return self.partition.cache.storage.open( - name=self.full_filename, mode=mode - ) - except Exception as exception: - logger.error( - 'Unexpected exception opening the cache file; %s', exception - ) - raise - - def update_size(self): - self.file_size = self.partition.cache.storage.size( - name=self.full_filename - ) - self.save() - - class ErrorLogEntry(models.Model): """ Class to store an error log for any object. Uses generic foreign keys to diff --git a/mayan/apps/documents/handlers.py b/mayan/apps/documents/handlers.py index d0e45f35d2..bec52a4443 100644 --- a/mayan/apps/documents/handlers.py +++ b/mayan/apps/documents/handlers.py @@ -27,7 +27,7 @@ def create_default_document_type(sender, **kwargs): def handler_create_document_cache(sender, **kwargs): - Cache = apps.get_model(app_label='common', model_name='Cache') + Cache = apps.get_model(app_label='file_caching', model_name='Cache') Cache.objects.update_or_create( defaults={ 'label': _('Document images'), diff --git a/mayan/apps/documents/models/document_version_models.py b/mayan/apps/documents/models/document_version_models.py index 94a0ceb068..148155b77f 100644 --- a/mayan/apps/documents/models/document_version_models.py +++ b/mayan/apps/documents/models/document_version_models.py @@ -112,7 +112,7 @@ class DocumentVersion(models.Model): @cached_property def cache(self): - Cache = apps.get_model(app_label='common', model_name='Cache') + Cache = apps.get_model(app_label='file_caching', model_name='Cache') return Cache.objects.get(name=DOCUMENT_IMAGES_CACHE_NAME) @cached_property diff --git a/mayan/apps/file_caching/__init__.py b/mayan/apps/file_caching/__init__.py new file mode 100644 index 0000000000..606c594dcf --- /dev/null +++ b/mayan/apps/file_caching/__init__.py @@ -0,0 +1,3 @@ +from __future__ import unicode_literals + +default_app_config = 'mayan.apps.file_caching.apps.FileCachingConfig' diff --git a/mayan/apps/file_caching/admin.py b/mayan/apps/file_caching/admin.py new file mode 100644 index 0000000000..a807f197c9 --- /dev/null +++ b/mayan/apps/file_caching/admin.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals + +from django.contrib import admin + +from .models import Cache + + +@admin.register(Cache) +class CacheAdmin(admin.ModelAdmin): + list_display = ('name', 'label', 'storage_instance_path', 'maximum_size') diff --git a/mayan/apps/file_caching/apps.py b/mayan/apps/file_caching/apps.py new file mode 100644 index 0000000000..ee2a3c57d7 --- /dev/null +++ b/mayan/apps/file_caching/apps.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals + +from mayan.apps.common import MayanAppConfig + + +class FileCachingConfig(MayanAppConfig): + has_tests = False + name = 'mayan.apps.file_caching' diff --git a/mayan/apps/file_caching/locale/ar/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000000..173a9d5ee2 --- /dev/null +++ b/mayan/apps/file_caching/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,72 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/bg/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/bg/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/bg/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/bs_BA/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/bs_BA/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/bs_BA/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/da/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/da/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/da/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/de_DE/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/de_DE/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/de_DE/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/en/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/es/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/fa/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/fa/LC_MESSAGES/django.po new file mode 100644 index 0000000000..09b38b103d --- /dev/null +++ b/mayan/apps/file_caching/locale/fa/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/fr/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c65665a79d --- /dev/null +++ b/mayan/apps/file_caching/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/hu/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/hu/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/hu/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/id/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/id/LC_MESSAGES/django.po new file mode 100644 index 0000000000..09b38b103d --- /dev/null +++ b/mayan/apps/file_caching/locale/id/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/it/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/nl_NL/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/nl_NL/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/nl_NL/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/pl/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..2f8fc8f37b --- /dev/null +++ b/mayan/apps/file_caching/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,73 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n" +"%100<12 || n%100>=14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n" +"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/pt/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/pt/LC_MESSAGES/django.po new file mode 100644 index 0000000000..4812952c38 --- /dev/null +++ b/mayan/apps/file_caching/locale/pt/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/pt_BR/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c65665a79d --- /dev/null +++ b/mayan/apps/file_caching/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,71 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/ro_RO/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/ro_RO/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/ro_RO/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/ru/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b73a01bbe4 --- /dev/null +++ b/mayan/apps/file_caching/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,73 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/sl_SI/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/sl_SI/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/sl_SI/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/tr_TR/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/tr_TR/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/tr_TR/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/vi_VN/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/vi_VN/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/vi_VN/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/locale/zh_CN/LC_MESSAGES/django.po b/mayan/apps/file_caching/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000000..b3bd609bb3 --- /dev/null +++ b/mayan/apps/file_caching/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,70 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-12-07 21:32-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: models.py:23 models.py:70 +msgid "Name" +msgstr "" + +#: models.py:25 +msgid "Label" +msgstr "" + +#: models.py:26 +msgid "Maximum size" +msgstr "" + +#: models.py:28 +msgid "Storage instance path" +msgstr "" + +#: models.py:32 models.py:67 +msgid "Cache" +msgstr "" + +#: models.py:33 +msgid "Caches" +msgstr "" + +#: models.py:75 models.py:141 +msgid "Cache partition" +msgstr "" + +#: models.py:76 +msgid "Cache partitions" +msgstr "" + +#: models.py:144 +msgid "Date time" +msgstr "" + +#: models.py:146 +msgid "Filename" +msgstr "" + +#: models.py:148 +msgid "File size" +msgstr "" + +#: models.py:154 +msgid "Cache partition file" +msgstr "" + +#: models.py:155 +msgid "Cache partition files" +msgstr "" diff --git a/mayan/apps/file_caching/migrations/0001_initial.py b/mayan/apps/file_caching/migrations/0001_initial.py new file mode 100644 index 0000000000..8c3a5f3226 --- /dev/null +++ b/mayan/apps/file_caching/migrations/0001_initial.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.16 on 2018-12-08 01:28 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Cache', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('label', models.CharField(max_length=128, verbose_name='Label')), + ('maximum_size', models.PositiveIntegerField(verbose_name='Maximum size')), + ('storage_instance_path', models.CharField(max_length=255, unique=True, verbose_name='Storage instance path')), + ], + options={ + 'verbose_name': 'Cache', + 'verbose_name_plural': 'Caches', + }, + ), + migrations.CreateModel( + name='CachePartition', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('cache', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='partitions', to='file_caching.Cache', verbose_name='Cache')), + ], + options={ + 'verbose_name': 'Cache partition', + 'verbose_name_plural': 'Cache partitions', + }, + ), + migrations.CreateModel( + name='CachePartitionFile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('datetime', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Date time')), + ('filename', models.CharField(max_length=255, verbose_name='Filename')), + ('file_size', models.PositiveIntegerField(default=0, verbose_name='File size')), + ('partition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='file_caching.CachePartition', verbose_name='Cache partition')), + ], + options={ + 'get_latest_by': 'datetime', + 'verbose_name': 'Cache partition file', + 'verbose_name_plural': 'Cache partition files', + }, + ), + migrations.AlterUniqueTogether( + name='cachepartitionfile', + unique_together=set([('partition', 'filename')]), + ), + migrations.AlterUniqueTogether( + name='cachepartition', + unique_together=set([('cache', 'name')]), + ), + ] diff --git a/mayan/apps/file_caching/migrations/__init__.py b/mayan/apps/file_caching/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mayan/apps/file_caching/models.py b/mayan/apps/file_caching/models.py new file mode 100644 index 0000000000..7ba02b69cc --- /dev/null +++ b/mayan/apps/file_caching/models.py @@ -0,0 +1,182 @@ +from __future__ import unicode_literals + +from contextlib import contextmanager +import logging + +from django.core.files.base import ContentFile +from django.db import models, transaction +from django.db.models import Sum +from django.utils.encoding import python_2_unicode_compatible +from django.utils.functional import cached_property +from django.utils.module_loading import import_string +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.lock_manager import LockError +from mayan.apps.lock_manager.runtime import locking_backend + +logger = logging.getLogger(__name__) + + +@python_2_unicode_compatible +class Cache(models.Model): + name = models.CharField( + max_length=128, unique=True, verbose_name=_('Name') + ) + label = models.CharField(max_length=128, verbose_name=_('Label')) + maximum_size = models.PositiveIntegerField(verbose_name=_('Maximum size')) + storage_instance_path = models.CharField( + max_length=255, unique=True, verbose_name=_('Storage instance path') + ) + + class Meta: + verbose_name = _('Cache') + verbose_name_plural = _('Caches') + + def __str__(self): + return self.label + + def get_files(self): + return CachePartitionFile.objects.filter(partition__cache__id=self.pk) + + def get_total_size(self): + return self.get_files().aggregate( + file_size__sum=Sum('file_size') + )['file_size__sum'] or 0 + + def prune(self): + while self.get_total_size() > self.maximum_size: + self.get_files().earliest().delete() + + def purge(self): + for partition in self.partitions.all(): + partition.purge() + + def save(self, *args, **kwargs): + result = super(Cache, self).save(*args, **kwargs) + self.prune() + return result + + @cached_property + def storage(self): + return import_string(self.storage_instance_path) + + +class CachePartition(models.Model): + cache = models.ForeignKey( + on_delete=models.CASCADE, related_name='partitions', + to=Cache, verbose_name=_('Cache') + ) + name = models.CharField( + max_length=128, verbose_name=_('Name') + ) + + class Meta: + unique_together = ('cache', 'name') + verbose_name = _('Cache partition') + verbose_name_plural = _('Cache partitions') + + @staticmethod + def get_combined_filename(parent, filename): + return '{}-{}'.format(parent, filename) + + @contextmanager + def create_file(self, filename): + lock_id = 'cache_partition-create_file-{}-{}'.format(self.pk, filename) + try: + logger.debug('trying to acquire lock: %s', lock_id) + lock = locking_backend.acquire_lock(lock_id) + logger.debug('acquired lock: %s', lock_id) + try: + self.cache.prune() + + # Since open "wb+" doesn't create files force the creation of an + # empty file. + self.cache.storage.delete( + name=self.get_full_filename(filename=filename) + ) + self.cache.storage.save( + name=self.get_full_filename(filename=filename), + content=ContentFile(content='') + ) + + try: + with transaction.atomic(): + partition_file = self.files.create(filename=filename) + yield partition_file.open(mode='wb') + partition_file.update_size() + except Exception as exception: + logger.error( + 'Unexpected exception while trying to save new ' + 'cache file; %s', exception + ) + self.cache.storage.delete( + name=self.get_full_filename(filename=filename) + ) + raise + finally: + lock.release() + except LockError: + logger.debug('unable to obtain lock: %s' % lock_id) + raise + + def get_file(self, filename): + try: + return self.files.get(filename=filename) + except self.files.model.DoesNotExist: + return None + + def get_full_filename(self, filename): + return CachePartition.get_combined_filename( + parent=self.name, filename=filename + ) + + def purge(self): + for parition_file in self.files.all(): + parition_file.delete() + + +class CachePartitionFile(models.Model): + partition = models.ForeignKey( + on_delete=models.CASCADE, related_name='files', + to=CachePartition, verbose_name=_('Cache partition') + ) + datetime = models.DateTimeField( + auto_now_add=True, db_index=True, verbose_name=_('Date time') + ) + filename = models.CharField(max_length=255, verbose_name=_('Filename')) + file_size = models.PositiveIntegerField( + default=0, verbose_name=_('File size') + ) + + class Meta: + get_latest_by = 'datetime' + unique_together = ('partition', 'filename') + verbose_name = _('Cache partition file') + verbose_name_plural = _('Cache partition files') + + def delete(self, *args, **kwargs): + self.partition.cache.storage.delete(name=self.full_filename) + return super(CachePartitionFile, self).delete(*args, **kwargs) + + @cached_property + def full_filename(self): + return CachePartition.get_combined_filename( + parent=self.partition.name, filename=self.filename + ) + + def open(self, mode='rb'): + try: + return self.partition.cache.storage.open( + name=self.full_filename, mode=mode + ) + except Exception as exception: + logger.error( + 'Unexpected exception opening the cache file; %s', exception + ) + raise + + def update_size(self): + self.file_size = self.partition.cache.storage.size( + name=self.full_filename + ) + self.save() diff --git a/mayan/settings/base.py b/mayan/settings/base.py index 9bd6a487ae..2070e1e61e 100644 --- a/mayan/settings/base.py +++ b/mayan/settings/base.py @@ -89,6 +89,7 @@ INSTALLED_APPS = ( 'mayan.apps.django_gpg', 'mayan.apps.dynamic_search', 'mayan.apps.events', + 'mayan.apps.file_caching', 'mayan.apps.lock_manager', 'mayan.apps.mimetype', 'mayan.apps.navigation',