diff --git a/mayan/apps/acls/apps.py b/mayan/apps/acls/apps.py index e673c67b48..fefd0b0376 100644 --- a/mayan/apps/acls/apps.py +++ b/mayan/apps/acls/apps.py @@ -19,4 +19,6 @@ class ACLsApp(MayanAppConfig): links=[link_acl_permissions, link_acl_delete], sources=[AccessControlList] ) - menu_sidebar.bind_links(links=[link_acl_new], sources=['acls:acl_list']) + menu_sidebar.bind_links( + links=[link_acl_new], sources=['acls:acl_list'] + ) diff --git a/mayan/apps/acls/links.py b/mayan/apps/acls/links.py index b5d847a989..0171778592 100644 --- a/mayan/apps/acls/links.py +++ b/mayan/apps/acls/links.py @@ -11,7 +11,11 @@ from .permissions import permission_acl_view, permission_acl_edit def get_kwargs_factory(variable_name): def get_kwargs(context): content_type = ContentType.objects.get_for_model(context[variable_name]) - return {'app_label': '"{}"'.format(content_type.app_label), 'model': '"{}"'.format(content_type.model), 'object_id': '{}.pk'.format(variable_name)} + return { + 'app_label': '"{}"'.format(content_type.app_label), + 'model': '"{}"'.format(content_type.model), + 'object_id': '{}.pk'.format(variable_name) + } return get_kwargs diff --git a/mayan/apps/acls/managers.py b/mayan/apps/acls/managers.py index 706525670f..871d26feba 100644 --- a/mayan/apps/acls/managers.py +++ b/mayan/apps/acls/managers.py @@ -92,7 +92,13 @@ class AccessControlListManager(models.Manager): content_type=parent_content_type, role__in=user_roles, permissions=permission.stored_permission ) - parent_acl_query = Q(**{'{}__pk__in'.format(parent_accessor): parent_queryset.values_list('object_id', flat=True)}) + parent_acl_query = Q( + **{ + '{}__pk__in'.format( + parent_accessor + ): parent_queryset.values_list('object_id', flat=True) + } + ) else: parent_acl_query = Q() diff --git a/mayan/apps/acls/migrations/0001_initial.py b/mayan/apps/acls/migrations/0001_initial.py index 3fc1e6190f..48932b6683 100644 --- a/mayan/apps/acls/migrations/0001_initial.py +++ b/mayan/apps/acls/migrations/0001_initial.py @@ -15,12 +15,34 @@ class Migration(migrations.Migration): migrations.CreateModel( name='AccessEntry', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('holder_id', models.PositiveIntegerField()), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), + ( + 'holder_id', models.PositiveIntegerField() + ), ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(related_name='object_content_type', to='contenttypes.ContentType')), - ('holder_type', models.ForeignKey(related_name='access_holder', to='contenttypes.ContentType')), - ('permission', models.ForeignKey(verbose_name='Permission', to='permissions.StoredPermission')), + ( + 'content_type', models.ForeignKey( + related_name='object_content_type', + to='contenttypes.ContentType' + ) + ), + ( + 'holder_type', models.ForeignKey( + related_name='access_holder', + to='contenttypes.ContentType' + ) + ), + ( + 'permission', models.ForeignKey( + verbose_name='Permission', + to='permissions.StoredPermission' + ) + ), ], options={ 'verbose_name': 'Access entry', @@ -31,7 +53,12 @@ class Migration(migrations.Migration): migrations.CreateModel( name='CreatorSingleton', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), ], options={ 'verbose_name': 'Creator', @@ -42,11 +69,31 @@ class Migration(migrations.Migration): migrations.CreateModel( name='DefaultAccessEntry', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), ('holder_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(related_name='default_access_entry_class', to='contenttypes.ContentType')), - ('holder_type', models.ForeignKey(related_name='default_access_entry_holder', to='contenttypes.ContentType')), - ('permission', models.ForeignKey(verbose_name='Permission', to='permissions.StoredPermission')), + ( + 'content_type', models.ForeignKey( + related_name='default_access_entry_class', + to='contenttypes.ContentType' + ) + ), + ( + 'holder_type', models.ForeignKey( + related_name='default_access_entry_holder', + to='contenttypes.ContentType' + ) + ), + ( + 'permission', models.ForeignKey( + verbose_name='Permission', + to='permissions.StoredPermission' + ) + ), ], options={ 'verbose_name': 'Default access entry', diff --git a/mayan/apps/acls/migrations/0002_auto_20150703_0513.py b/mayan/apps/acls/migrations/0002_auto_20150703_0513.py index 0592d6e65c..6ff7c6def5 100644 --- a/mayan/apps/acls/migrations/0002_auto_20150703_0513.py +++ b/mayan/apps/acls/migrations/0002_auto_20150703_0513.py @@ -16,11 +16,31 @@ class Migration(migrations.Migration): migrations.CreateModel( name='AccessControlList', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, auto_created=True, + primary_key=True + ) + ), ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(related_name='object_content_type', to='contenttypes.ContentType')), - ('permissions', models.ManyToManyField(related_name='acls', verbose_name='Permissions', to='permissions.StoredPermission', blank=True)), - ('role', models.ForeignKey(related_name='acls', verbose_name='Role', to='permissions.Role')), + ( + 'content_type', models.ForeignKey( + related_name='object_content_type', + to='contenttypes.ContentType' + ) + ), + ( + 'permissions', models.ManyToManyField( + related_name='acls', verbose_name='Permissions', + to='permissions.StoredPermission', blank=True + ) + ), + ( + 'role', models.ForeignKey( + related_name='acls', verbose_name='Role', + to='permissions.Role' + ) + ), ], options={ 'verbose_name': 'Access entry', diff --git a/mayan/apps/acls/models.py b/mayan/apps/acls/models.py index 8fc4cafcb3..0b669eccb1 100644 --- a/mayan/apps/acls/models.py +++ b/mayan/apps/acls/models.py @@ -31,7 +31,10 @@ class AccessControlList(models.Model): fk_field='object_id', ) # TODO: limit choices to the permissions valid for the content_object - permissions = models.ManyToManyField(StoredPermission, blank=True, related_name='acls', verbose_name=_('Permissions')) + permissions = models.ManyToManyField( + StoredPermission, blank=True, related_name='acls', + verbose_name=_('Permissions') + ) role = models.ForeignKey(Role, related_name='acls', verbose_name=_('Role')) objects = AccessControlListManager() @@ -45,4 +48,6 @@ class AccessControlList(models.Model): return '{} <=> {}'.format(self.content_object, self.role) def get_inherited_permissions(self): - return AccessControlList.objects.get_inherited_permissions(role=self.role, obj=self.content_object) + return AccessControlList.objects.get_inherited_permissions( + role=self.role, obj=self.content_object + ) diff --git a/mayan/apps/acls/permissions.py b/mayan/apps/acls/permissions.py index f9b23ae728..da7a5ddce9 100644 --- a/mayan/apps/acls/permissions.py +++ b/mayan/apps/acls/permissions.py @@ -6,5 +6,9 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('acls', _('Access control lists')) -permission_acl_edit = namespace.add_permission(name='acl_edit', label=_('Edit ACLs')) -permission_acl_view = namespace.add_permission(name='acl_view', label=_('View ACLs')) +permission_acl_edit = namespace.add_permission( + name='acl_edit', label=_('Edit ACLs') +) +permission_acl_view = namespace.add_permission( + name='acl_view', label=_('View ACLs') +) diff --git a/mayan/apps/acls/test_models.py b/mayan/apps/acls/test_models.py index b66f245189..5a5c7acbfd 100644 --- a/mayan/apps/acls/test_models.py +++ b/mayan/apps/acls/test_models.py @@ -17,26 +17,36 @@ from .models import AccessControlList class PermissionTestCase(TestCase): def setUp(self): - self.document_type_1 = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type_1 = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) ocr_settings = self.document_type_1.ocr_settings ocr_settings.auto_ocr = False ocr_settings.save() - self.document_type_2 = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE + '2') + self.document_type_2 = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + '2' + ) ocr_settings = self.document_type_2.ocr_settings ocr_settings.auto_ocr = False ocr_settings.save() with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document_1 = self.document_type_1.new_document(file_object=File(file_object), label='document 1') + self.document_1 = self.document_type_1.new_document( + file_object=File(file_object), label='document 1' + ) with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document_2 = self.document_type_1.new_document(file_object=File(file_object), label='document 2') + self.document_2 = self.document_type_1.new_document( + file_object=File(file_object), label='document 2' + ) with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document_3 = self.document_type_2.new_document(file_object=File(file_object), label='document 3') + self.document_3 = self.document_type_2.new_document( + file_object=File(file_object), label='document 3' + ) self.user = get_user_model().objects.create(username='test user') self.group = Group.objects.create(name='test group') @@ -52,23 +62,35 @@ class PermissionTestCase(TestCase): def test_check_access_without_permissions(self): with self.assertRaises(PermissionDenied): - AccessControlList.objects.check_access(permissions=(permission_document_view,), user=self.user, obj=self.document_1) + AccessControlList.objects.check_access( + permissions=(permission_document_view,), + user=self.user, obj=self.document_1 + ) def test_filtering_without_permissions(self): self.assertEqual( - list(AccessControlList.objects.filter_by_access(permission=permission_document_view, user=self.user, queryset=Document.objects.all())), - [] + list( + AccessControlList.objects.filter_by_access( + permission=permission_document_view, user=self.user, + queryset=Document.objects.all() + ) + ), [] ) def test_check_access_with_acl(self): self.group.user_set.add(self.user) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) try: - AccessControlList.objects.check_access(permissions=(permission_document_view,), user=self.user, obj=self.document_1) + AccessControlList.objects.check_access( + permissions=(permission_document_view,), user=self.user, + obj=self.document_1 + ) except PermissionDenied: self.fail('PermissionDenied exception was not expected.') @@ -77,23 +99,34 @@ class PermissionTestCase(TestCase): self.role.permissions.add(permission_document_view.stored_permission) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) self.assertEqual( - list(AccessControlList.objects.filter_by_access(permission=permission_document_view, user=self.user, queryset=Document.objects.all())), - [self.document_1] + list( + AccessControlList.objects.filter_by_access( + permission=permission_document_view, user=self.user, + queryset=Document.objects.all() + ) + ), [self.document_1] ) def test_check_access_with_inherited_acl(self): self.group.user_set.add(self.user) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_type_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_type_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) try: - AccessControlList.objects.check_access(permissions=(permission_document_view,), user=self.user, obj=self.document_1) + AccessControlList.objects.check_access( + permissions=(permission_document_view,), user=self.user, + obj=self.document_1 + ) except PermissionDenied: self.fail('PermissionDenied exception was not expected.') @@ -101,14 +134,21 @@ class PermissionTestCase(TestCase): self.group.user_set.add(self.user) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_type_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_type_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) - acl = AccessControlList.objects.create(content_object=self.document_3, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_3, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) try: - AccessControlList.objects.check_access(permissions=(permission_document_view,), user=self.user, obj=self.document_3) + AccessControlList.objects.check_access( + permissions=(permission_document_view,), user=self.user, + obj=self.document_3 + ) except PermissionDenied: self.fail('PermissionDenied exception was not expected.') @@ -117,10 +157,15 @@ class PermissionTestCase(TestCase): self.role.permissions.add(permission_document_view.stored_permission) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_type_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_type_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) - result = AccessControlList.objects.filter_by_access(permission=permission_document_view, user=self.user, queryset=Document.objects.all()) + result = AccessControlList.objects.filter_by_access( + permission=permission_document_view, user=self.user, + queryset=Document.objects.all() + ) self.assertTrue(self.document_1 in result) self.assertTrue(self.document_2 in result) self.assertTrue(self.document_3 not in result) @@ -130,13 +175,20 @@ class PermissionTestCase(TestCase): self.role.permissions.add(permission_document_view.stored_permission) self.role.groups.add(self.group) - acl = AccessControlList.objects.create(content_object=self.document_type_1, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_type_1, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) - acl = AccessControlList.objects.create(content_object=self.document_3, role=self.role) + acl = AccessControlList.objects.create( + content_object=self.document_3, role=self.role + ) acl.permissions.add(permission_document_view.stored_permission) - result = AccessControlList.objects.filter_by_access(permission=permission_document_view, user=self.user, queryset=Document.objects.all()) + result = AccessControlList.objects.filter_by_access( + permission=permission_document_view, user=self.user, + queryset=Document.objects.all() + ) self.assertTrue(self.document_1 in result) self.assertTrue(self.document_2 in result) self.assertTrue(self.document_3 in result) diff --git a/mayan/apps/acls/urls.py b/mayan/apps/acls/urls.py index 19c8d5c08d..135091e26d 100644 --- a/mayan/apps/acls/urls.py +++ b/mayan/apps/acls/urls.py @@ -6,8 +6,17 @@ from .views import ACLCreateView, ACLDeleteView, ACLListView, ACLPermissionsView urlpatterns = patterns( 'acls.views', - url(r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/new/$', ACLCreateView.as_view(), name='acl_new'), - url(r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/list/$', ACLListView.as_view(), name='acl_list'), + url( + r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/new/$', + ACLCreateView.as_view(), name='acl_new' + ), + url( + r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/list/$', + ACLListView.as_view(), name='acl_list' + ), url(r'^(?P\d+)/delete/$', ACLDeleteView.as_view(), name='acl_delete'), - url(r'^(?P\d+)/permissions/$', ACLPermissionsView.as_view(), name='acl_permissions'), + url( + r'^(?P\d+)/permissions/$', ACLPermissionsView.as_view(), + name='acl_permissions' + ), ) diff --git a/mayan/apps/acls/views.py b/mayan/apps/acls/views.py index 9d6ac25960..81de6c0f41 100644 --- a/mayan/apps/acls/views.py +++ b/mayan/apps/acls/views.py @@ -28,25 +28,38 @@ logger = logging.getLogger(__name__) class ACLListView(SingleObjectListView): @staticmethod def permission_titles(permission_list): - return ', '.join([unicode(permission) for permission in permission_list]) + return ', '.join( + [unicode(permission) for permission in permission_list] + ) def dispatch(self, request, *args, **kwargs): - self.content_type = get_object_or_404(ContentType, app_label=self.kwargs['app_label'], model=self.kwargs['model']) + self.content_type = get_object_or_404( + ContentType, app_label=self.kwargs['app_label'], + model=self.kwargs['model'] + ) try: - self.content_object = self.content_type.get_object_for_this_type(pk=self.kwargs['object_id']) + self.content_object = self.content_type.get_object_for_this_type( + pk=self.kwargs['object_id'] + ) except self.content_type.model_class().DoesNotExist: raise Http404 try: - Permission.check_permissions(request.user, permissions=(permission_acl_view,)) + Permission.check_permissions( + request.user, permissions=(permission_acl_view,) + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_acl_view, request.user, self.content_object) + AccessControlList.objects.check_access( + permission_acl_view, request.user, self.content_object + ) return super(ACLListView, self).dispatch(request, *args, **kwargs) def get_queryset(self): - return AccessControlList.objects.filter(content_type=self.content_type, object_id=self.content_object.pk) + return AccessControlList.objects.filter( + content_type=self.content_type, object_id=self.content_object.pk + ) def get_extra_context(self): return { @@ -60,7 +73,11 @@ class ACLListView(SingleObjectListView): }, { 'name': _('Permissions'), - 'attribute': encapsulate(lambda entry: ACLListView.permission_titles(entry.permissions.all())) + 'attribute': encapsulate( + lambda entry: ACLListView.permission_titles( + entry.permissions.all() + ) + ) }, ], } @@ -71,17 +88,26 @@ class ACLCreateView(SingleObjectCreateView): model = AccessControlList def dispatch(self, request, *args, **kwargs): - content_type = get_object_or_404(ContentType, app_label=self.kwargs['app_label'], model=self.kwargs['model']) + content_type = get_object_or_404( + ContentType, app_label=self.kwargs['app_label'], + model=self.kwargs['model'] + ) try: - self.content_object = content_type.get_object_for_this_type(pk=self.kwargs['object_id']) + self.content_object = content_type.get_object_for_this_type( + pk=self.kwargs['object_id'] + ) except content_type.model_class().DoesNotExist: raise Http404 try: - Permission.check_permissions(request.user, permissions=(permission_acl_edit,)) + Permission.check_permissions( + request.user, permissions=(permission_acl_edit,) + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_acl_edit, request.user, self.content_object) + AccessControlList.objects.check_access( + permission_acl_edit, request.user, self.content_object + ) return super(ACLCreateView, self).dispatch(request, *args, **kwargs) @@ -98,7 +124,9 @@ class ACLCreateView(SingleObjectCreateView): def get_extra_context(self): return { 'object': self.content_object, - 'title': _('New access control lists for: %s' % self.content_object), + 'title': _( + 'New access control lists for: %s' % self.content_object + ), } @@ -109,9 +137,13 @@ class ACLDeleteView(SingleObjectDeleteView): acl = get_object_or_404(AccessControlList, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, permissions=(permission_acl_edit,)) + Permission.check_permissions( + request.user, permissions=(permission_acl_edit,) + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_acl_edit, request.user, acl.content_object) + AccessControlList.objects.check_access( + permission_acl_edit, request.user, acl.content_object + ) return super(ACLDeleteView, self).dispatch(request, *args, **kwargs) @@ -136,8 +168,12 @@ class ACLPermissionsView(AssignRemoveView): results = [] for namespace, permissions in itertools.groupby(entries, lambda entry: entry.namespace): - permission_options = [(unicode(permission.pk), permission) for permission in permissions] - results.append((PermissionNamespace.get(namespace), permission_options)) + permission_options = [ + (unicode(permission.pk), permission) for permission in permissions + ] + results.append( + (PermissionNamespace.get(namespace), permission_options) + ) return results @@ -149,15 +185,23 @@ class ACLPermissionsView(AssignRemoveView): acl = get_object_or_404(AccessControlList, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, permissions=(permission_acl_edit,)) + Permission.check_permissions( + request.user, permissions=(permission_acl_edit,) + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_acl_edit, request.user, acl.content_object) + AccessControlList.objects.check_access( + permission_acl_edit, request.user, acl.content_object + ) - return super(ACLPermissionsView, self).dispatch(request, *args, **kwargs) + return super( + ACLPermissionsView, self + ).dispatch(request, *args, **kwargs) def get_right_list_help_text(self): if self.get_object().get_inherited_permissions(): - return _('Disabled permissions are inherited from a parent object.') + return _( + 'Disabled permissions are inherited from a parent object.' + ) return None @@ -165,14 +209,24 @@ class ACLPermissionsView(AssignRemoveView): return get_object_or_404(AccessControlList, pk=self.kwargs['pk']) def get_available_list(self): - return ModelPermission.get_for_instance(instance=self.get_object().content_object).exclude(id__in=self.get_granted_list().values_list('pk', flat=True)) + return ModelPermission.get_for_instance( + instance=self.get_object().content_object + ).exclude(id__in=self.get_granted_list().values_list('pk', flat=True)) def get_disabled_choices(self): """ Get permissions from a parent's acls but remove the permissions we already hold for this object """ - return map(str, set(self.get_object().get_inherited_permissions().values_list('pk', flat=True)).difference(self.get_object().permissions.values_list('pk', flat=True))) + return map( + str, set( + self.get_object().get_inherited_permissions().values_list( + 'pk', flat=True + ) + ).difference( + self.get_object().permissions.values_list('pk', flat=True) + ) + ) def get_extra_context(self): return { diff --git a/mayan/apps/authentication/settings.py b/mayan/apps/authentication/settings.py index 98a2980da5..56d7ee2410 100644 --- a/mayan/apps/authentication/settings.py +++ b/mayan/apps/authentication/settings.py @@ -8,6 +8,7 @@ namespace = Namespace(name='authentication', label=_('Authentication')) setting_login_method = namespace.add_setting( global_name='AUTHENTICATION_LOGIN_METHOD', default='username', help_text=_( - 'Controls the mechanism used to authenticated user. Options are: username, email' + 'Controls the mechanism used to authenticated user. Options are: ' + 'username, email' ) ) diff --git a/mayan/apps/checkouts/views.py b/mayan/apps/checkouts/views.py index 2d4f7e4c75..802be49a0b 100644 --- a/mayan/apps/checkouts/views.py +++ b/mayan/apps/checkouts/views.py @@ -140,12 +140,12 @@ def checkout_info(request, document_pk): return render_to_response( 'appearance/generic_template.html', { - 'paragraphs': paragraphs, - 'object': document, - 'title': _('Check out details for document: %s') % document - }, - context_instance=RequestContext(request) - ) + 'paragraphs': paragraphs, + 'object': document, + 'title': _('Check out details for document: %s') % document + }, + context_instance=RequestContext(request) + ) def checkin_document(request, document_pk): diff --git a/mayan/apps/converter/classes.py b/mayan/apps/converter/classes.py index ff2d2a7826..a8b2ecf29b 100644 --- a/mayan/apps/converter/classes.py +++ b/mayan/apps/converter/classes.py @@ -300,8 +300,10 @@ class TransformationZoom(BaseTransformation): decimal_value = float(self.percent) / 100 return self.image.resize( - (int(self.image.size[0] * decimal_value), - int(self.image.size[1] * decimal_value)), Image.ANTIALIAS + ( + int(self.image.size[0] * decimal_value), + int(self.image.size[1] * decimal_value) + ), Image.ANTIALIAS ) diff --git a/mayan/apps/converter/views.py b/mayan/apps/converter/views.py index 848dd30592..10bebf8366 100644 --- a/mayan/apps/converter/views.py +++ b/mayan/apps/converter/views.py @@ -69,10 +69,12 @@ class TransformationDeleteView(SingleObjectDeleteView): ] ), 'title': _( - 'Delete transformation "%(transformation)s" for: %(content_object)s?') % { - 'transformation': self.transformation, - 'content_object': self.transformation.content_object - }, + 'Delete transformation "%(transformation)s" for: ' + '%(content_object)s?' + ) % { + 'transformation': self.transformation, + 'content_object': self.transformation.content_object + }, 'transformation': self.transformation, } diff --git a/mayan/apps/document_comments/views.py b/mayan/apps/document_comments/views.py index c57b5576e5..09e0a781db 100644 --- a/mayan/apps/document_comments/views.py +++ b/mayan/apps/document_comments/views.py @@ -29,12 +29,19 @@ def comment_delete(request, comment_id=None, comment_id_list=None): if comment_id: comments = [get_object_or_404(Comment, pk=comment_id)] elif comment_id_list: - comments = [get_object_or_404(Comment, pk=comment_id) for comment_id in comment_id_list.split(',')] + comments = [ + get_object_or_404( + Comment, pk=comment_id + ) for comment_id in comment_id_list.split(',') + ] try: Permission.check_permissions(request.user, [permission_comment_delete]) except PermissionDenied: - comments = AccessControlList.objects.filter_by_access(permission_comment_delete, request.user, comments, related='content_object') + comments = AccessControlList.objects.filter_by_access( + permission_comment_delete, request.user, comments, + related='content_object' + ) if not comments: messages.error(request, _('Must provide at least one comment.')) @@ -47,11 +54,17 @@ def comment_delete(request, comment_id=None, comment_id_list=None): for comment in comments: try: comment.delete() - messages.success(request, _('Comment "%s" deleted successfully.') % comment) + messages.success( + request, _('Comment "%s" deleted successfully.') % comment + ) except Exception as exception: - messages.error(request, _('Error deleting comment "%(comment)s": %(error)s') % { - 'comment': comment, 'error': exception - }) + messages.error( + request, _( + 'Error deleting comment "%(comment)s": %(error)s' + ) % { + 'comment': comment, 'error': exception + } + ) return HttpResponseRedirect(next) diff --git a/mayan/apps/document_signatures/apps.py b/mayan/apps/document_signatures/apps.py index 5e2cb23b4f..a3178c2161 100644 --- a/mayan/apps/document_signatures/apps.py +++ b/mayan/apps/document_signatures/apps.py @@ -45,9 +45,13 @@ def document_version_post_save_hook(instance): logger.debug('instance: %s', instance) try: - document_signature = DocumentVersionSignature.objects.get(document_version=instance) + document_signature = DocumentVersionSignature.objects.get( + document_version=instance + ) except DocumentVersionSignature.DoesNotExist: - document_signature = DocumentVersionSignature.objects.create(document_version=instance) + document_signature = DocumentVersionSignature.objects.create( + document_version=instance + ) document_signature.check_for_embedded_signature() @@ -60,7 +64,9 @@ class DocumentSignaturesApp(MayanAppConfig): def ready(self): super(DocumentSignaturesApp, self).ready() - DocumentVersion.register_post_save_hook(1, document_version_post_save_hook) + DocumentVersion.register_post_save_hook( + 1, document_version_post_save_hook + ) DocumentVersion.register_pre_open_hook(1, document_pre_open_hook) ModelPermission.register( @@ -70,5 +76,18 @@ class DocumentSignaturesApp(MayanAppConfig): ) ) - menu_facet.bind_links(links=[link_document_verify], sources=[Document]) - menu_sidebar.bind_links(links=[link_document_signature_upload, link_document_signature_download, link_document_signature_delete], sources=['signatures:document_verify', 'signatures:document_signature_upload', 'signatures:document_signature_download', 'signatures:document_signature_delete']) + menu_facet.bind_links( + links=[link_document_verify], sources=[Document] + ) + menu_sidebar.bind_links( + links=[ + link_document_signature_upload, + link_document_signature_download, + link_document_signature_delete + ], sources=[ + 'signatures:document_verify', + 'signatures:document_signature_upload', + 'signatures:document_signature_download', + 'signatures:document_signature_delete' + ] + ) diff --git a/mayan/apps/document_signatures/links.py b/mayan/apps/document_signatures/links.py index 3298bc1043..ddee4211dc 100644 --- a/mayan/apps/document_signatures/links.py +++ b/mayan/apps/document_signatures/links.py @@ -12,14 +12,36 @@ from .permissions import ( def can_upload_detached_signature(context): - return not DocumentVersionSignature.objects.has_detached_signature(context['object'].latest_version) and not DocumentVersionSignature.objects.has_embedded_signature(context['object'].latest_version) + return not DocumentVersionSignature.objects.has_detached_signature( + context['object'].latest_version + ) and not DocumentVersionSignature.objects.has_embedded_signature( + context['object'].latest_version + ) def can_delete_detached_signature(context): - return DocumentVersionSignature.objects.has_detached_signature(context['object'].latest_version) + return DocumentVersionSignature.objects.has_detached_signature( + context['object'].latest_version + ) -link_document_signature_delete = Link(condition=can_delete_detached_signature, permissions=[permission_signature_delete], tags='dangerous', text=_('Delete signature'), view='signatures:document_signature_delete', args='object.pk') -link_document_signature_download = Link(condition=can_delete_detached_signature, text=_('Download signature'), view='signatures:document_signature_download', args='object.pk', permissions=[permission_signature_download]) -link_document_signature_upload = Link(condition=can_upload_detached_signature, permissions=[permission_signature_upload], text=_('Upload signature'), view='signatures:document_signature_upload', args='object.pk') -link_document_verify = Link(permissions=[permission_document_verify], text=_('Signatures'), view='signatures:document_verify', args='object.pk') +link_document_signature_delete = Link( + condition=can_delete_detached_signature, + permissions=[permission_signature_delete], tags='dangerous', + text=_('Delete signature'), view='signatures:document_signature_delete', + args='object.pk' +) +link_document_signature_download = Link( + condition=can_delete_detached_signature, text=_('Download signature'), + view='signatures:document_signature_download', args='object.pk', + permissions=[permission_signature_download] +) +link_document_signature_upload = Link( + condition=can_upload_detached_signature, + permissions=[permission_signature_upload], text=_('Upload signature'), + view='signatures:document_signature_upload', args='object.pk' +) +link_document_verify = Link( + permissions=[permission_document_verify], text=_('Signatures'), + view='signatures:document_verify', args='object.pk' +) diff --git a/mayan/apps/document_signatures/managers.py b/mayan/apps/document_signatures/managers.py index 68159046f2..c03e2b5699 100644 --- a/mayan/apps/document_signatures/managers.py +++ b/mayan/apps/document_signatures/managers.py @@ -24,7 +24,9 @@ class DocumentVersionSignatureManager(models.Manager): ) if document_signature.has_embedded_signature: - raise Exception('Document version already has an embedded signature') + raise Exception( + 'Document version already has an embedded signature' + ) else: if document_signature.signature_file: logger.debug('Existing detached signature') @@ -37,7 +39,9 @@ class DocumentVersionSignatureManager(models.Manager): def has_detached_signature(self, document_version): try: - document_signature = self.get_document_signature(document_version=document_version) + document_signature = self.get_document_signature( + document_version=document_version + ) except ValueError: return False else: @@ -50,23 +54,31 @@ class DocumentVersionSignatureManager(models.Manager): logger.debug('document_version: %s', document_version) try: - document_signature = self.get_document_signature(document_version=document_version) + document_signature = self.get_document_signature( + document_version=document_version + ) except ValueError: return False else: return document_signature.has_embedded_signature def detached_signature(self, document_version): - document_signature = self.get_document_signature(document_version=document_version) + document_signature = self.get_document_signature( + document_version=document_version + ) - return document_signature.signature_file.storage.open(document_signature.signature_file.name) + return document_signature.signature_file.storage.open( + document_signature.signature_file.name + ) def verify_signature(self, document_version): document_version_descriptor = document_version.open(raw=True) detached_signature = None if self.has_detached_signature(document_version=document_version): logger.debug('has detached signature') - detached_signature = self.detached_signature(document_version=document_version) + detached_signature = self.detached_signature( + document_version=document_version + ) args = (document_version_descriptor, detached_signature) else: args = (document_version_descriptor,) @@ -81,7 +93,9 @@ class DocumentVersionSignatureManager(models.Manager): detached_signature.close() def clear_detached_signature(self, document_version): - document_signature = self.get_document_signature(document_version=document_version) + document_signature = self.get_document_signature( + document_version=document_version + ) if not document_signature.signature_file: raise Exception('document doesn\'t have a detached signature') diff --git a/mayan/apps/document_signatures/models.py b/mayan/apps/document_signatures/models.py index a66fd55952..931bdde8eb 100644 --- a/mayan/apps/document_signatures/models.py +++ b/mayan/apps/document_signatures/models.py @@ -23,9 +23,16 @@ class DocumentVersionSignature(models.Model): """ Model that describes a document version signature properties """ - document_version = models.ForeignKey(DocumentVersion, editable=False, verbose_name=_('Document version')) - signature_file = models.FileField(blank=True, null=True, storage=storage_backend, upload_to=upload_to, verbose_name=_('Signature file')) - has_embedded_signature = models.BooleanField(default=False, verbose_name=_('Has embedded signature')) + document_version = models.ForeignKey( + DocumentVersion, editable=False, verbose_name=_('Document version') + ) + signature_file = models.FileField( + blank=True, null=True, storage=storage_backend, upload_to=upload_to, + verbose_name=_('Signature file') + ) + has_embedded_signature = models.BooleanField( + default=False, verbose_name=_('Has embedded signature') + ) objects = DocumentVersionSignatureManager() @@ -33,7 +40,9 @@ class DocumentVersionSignature(models.Model): logger.debug('checking for embedded signature') with self.document_version.open(raw=True) as file_object: - self.has_embedded_signature = gpg.has_embedded_signature(file_object) + self.has_embedded_signature = gpg.has_embedded_signature( + file_object + ) self.save() def delete_detached_signature_file(self): diff --git a/mayan/apps/document_signatures/permissions.py b/mayan/apps/document_signatures/permissions.py index d7ad294fd6..90f8270503 100644 --- a/mayan/apps/document_signatures/permissions.py +++ b/mayan/apps/document_signatures/permissions.py @@ -4,9 +4,19 @@ from django.utils.translation import ugettext_lazy as _ from permissions import PermissionNamespace -namespace = PermissionNamespace('document_signatures', _('Document signatures')) +namespace = PermissionNamespace( + 'document_signatures', _('Document signatures') +) -permission_document_verify = namespace.add_permission(name='document_verify', label=_('Verify document signatures')) -permission_signature_delete = namespace.add_permission(name='signature_delete', label=_('Delete detached signatures')) -permission_signature_download = namespace.add_permission(name='signature_download', label=_('Download detached signatures')) -permission_signature_upload = namespace.add_permission(name='signature_upload', label=_('Upload detached signatures')) +permission_document_verify = namespace.add_permission( + name='document_verify', label=_('Verify document signatures') +) +permission_signature_delete = namespace.add_permission( + name='signature_delete', label=_('Delete detached signatures') +) +permission_signature_download = namespace.add_permission( + name='signature_download', label=_('Download detached signatures') +) +permission_signature_upload = namespace.add_permission( + name='signature_upload', label=_('Upload detached signatures') +) diff --git a/mayan/apps/document_signatures/settings.py b/mayan/apps/document_signatures/settings.py index bff62ba559..012b357694 100644 --- a/mayan/apps/document_signatures/settings.py +++ b/mayan/apps/document_signatures/settings.py @@ -5,4 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from smart_settings import Namespace namespace = Namespace(name='signatures', label=_('Document signatures')) -setting_storage_backend = namespace.add_setting(global_name='SIGNATURES_STORAGE_BACKEND', default='storage.backends.filebasedstorage.FileBasedStorage') +setting_storage_backend = namespace.add_setting( + global_name='SIGNATURES_STORAGE_BACKEND', + default='storage.backends.filebasedstorage.FileBasedStorage' +) diff --git a/mayan/apps/document_signatures/test_models.py b/mayan/apps/document_signatures/test_models.py index 055c07f692..9646d4271d 100644 --- a/mayan/apps/document_signatures/test_models.py +++ b/mayan/apps/document_signatures/test_models.py @@ -13,21 +13,32 @@ from django_gpg.runtime import gpg from .models import DocumentVersionSignature -TEST_SIGNED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.gpg') -TEST_SIGNATURE_FILE_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.sig') -TEST_KEY_FILE = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'key0x5F3F7F75D210724D.asc') +TEST_SIGNED_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.gpg' +) +TEST_SIGNATURE_FILE_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf.sig' +) +TEST_KEY_FILE = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + 'key0x5F3F7F75D210724D.asc' +) class DocumentTestCase(TestCase): def setUp(self): - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) ocr_settings = self.document_type.ocr_settings ocr_settings.auto_ocr = False ocr_settings.save() with open(TEST_DOCUMENT_PATH) as file_object: - self.document = self.document_type.new_document(file_object=File(file_object), label='mayan_11_1.pdf') + self.document = self.document_type.new_document( + file_object=File(file_object), label='mayan_11_1.pdf' + ) with open(TEST_KEY_FILE) as file_object: gpg.import_key(file_object.read()) @@ -37,24 +48,52 @@ class DocumentTestCase(TestCase): self.document_type.delete() def test_document_no_signature(self): - self.assertEqual(DocumentVersionSignature.objects.has_detached_signature(self.document.latest_version), False) + self.assertEqual( + DocumentVersionSignature.objects.has_detached_signature( + self.document.latest_version + ), False + ) def test_new_document_version_signed(self): with open(TEST_SIGNED_DOCUMENT_PATH) as file_object: - self.document.new_version(file_object=File(file_object), comment='test comment 1') + self.document.new_version( + file_object=File(file_object), comment='test comment 1' + ) - self.assertEqual(DocumentVersionSignature.objects.has_detached_signature(self.document.latest_version), False) - self.assertEqual(DocumentVersionSignature.objects.verify_signature(self.document.latest_version).status, SIGNATURE_STATE_VALID) + self.assertEqual( + DocumentVersionSignature.objects.has_detached_signature( + self.document.latest_version + ), False + ) + self.assertEqual( + DocumentVersionSignature.objects.verify_signature( + self.document.latest_version + ).status, SIGNATURE_STATE_VALID + ) def test_detached_signatures(self): with open(TEST_DOCUMENT_PATH) as file_object: - self.document.new_version(file_object=File(file_object), comment='test comment 2') + self.document.new_version( + file_object=File(file_object), comment='test comment 2' + ) # GPGVerificationError - self.assertEqual(DocumentVersionSignature.objects.verify_signature(self.document.latest_version), None) + self.assertEqual(DocumentVersionSignature.objects.verify_signature( + self.document.latest_version), None + ) with open(TEST_SIGNATURE_FILE_PATH, 'rb') as file_object: - DocumentVersionSignature.objects.add_detached_signature(self.document.latest_version, File(file_object)) + DocumentVersionSignature.objects.add_detached_signature( + self.document.latest_version, File(file_object) + ) - self.assertEqual(DocumentVersionSignature.objects.has_detached_signature(self.document.latest_version), True) - self.assertEqual(DocumentVersionSignature.objects.verify_signature(self.document.latest_version).status, SIGNATURE_STATE_VALID) + self.assertEqual( + DocumentVersionSignature.objects.has_detached_signature( + self.document.latest_version + ), True + ) + self.assertEqual( + DocumentVersionSignature.objects.verify_signature( + self.document.latest_version + ).status, SIGNATURE_STATE_VALID + ) diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index 073c5fc03d..f677c3aeb2 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -37,32 +37,122 @@ class DocumentStatesApp(MayanAppConfig): def ready(self): super(DocumentStatesApp, self).ready() - SourceColumn(source=Workflow, label=_('Initial state'), attribute=encapsulate(lambda workflow: workflow.get_initial_state() or _('None'))) + SourceColumn( + source=Workflow, label=_('Initial state'), + attribute=encapsulate( + lambda workflow: workflow.get_initial_state() or _('None') + ) + ) - SourceColumn(source=WorkflowInstance, label=_('Current state'), attribute='get_current_state') - SourceColumn(source=WorkflowInstance, label=_('User'), attribute=encapsulate(lambda workflow: getattr(workflow.get_last_log_entry(), 'user', _('None')))) - SourceColumn(source=WorkflowInstance, label=_('Last transition'), attribute='get_last_transition') - SourceColumn(source=WorkflowInstance, label=_('Date and time'), attribute=encapsulate(lambda workflow: getattr(workflow.get_last_log_entry(), 'datetime', _('None')))) - SourceColumn(source=WorkflowInstance, label=_('Completion'), attribute=encapsulate(lambda workflow: getattr(workflow.get_current_state(), 'completion', _('None')))) + SourceColumn( + source=WorkflowInstance, label=_('Current state'), + attribute='get_current_state' + ) + SourceColumn( + source=WorkflowInstance, label=_('User'), + attribute=encapsulate( + lambda workflow: getattr( + workflow.get_last_log_entry(), 'user', _('None') + ) + ) + ) + SourceColumn( + source=WorkflowInstance, label=_('Last transition'), + attribute='get_last_transition' + ) + SourceColumn( + source=WorkflowInstance, label=_('Date and time'), + attribute=encapsulate( + lambda workflow: getattr( + workflow.get_last_log_entry(), 'datetime', _('None') + ) + ) + ) + SourceColumn( + source=WorkflowInstance, label=_('Completion'), + attribute=encapsulate(lambda workflow: getattr( + workflow.get_current_state(), 'completion', _('None')) + ) + ) - SourceColumn(source=WorkflowInstanceLogEntry, label=_('Date and time'), attribute='datetime') - SourceColumn(source=WorkflowInstanceLogEntry, label=_('User'), attribute='user') - SourceColumn(source=WorkflowInstanceLogEntry, label=_('Transition'), attribute='transition') - SourceColumn(source=WorkflowInstanceLogEntry, label=_('Comment'), attribute='comment') + SourceColumn( + source=WorkflowInstanceLogEntry, label=_('Date and time'), + attribute='datetime' + ) + SourceColumn( + source=WorkflowInstanceLogEntry, label=_('User'), attribute='user' + ) + SourceColumn( + source=WorkflowInstanceLogEntry, label=_('Transition'), + attribute='transition' + ) + SourceColumn( + source=WorkflowInstanceLogEntry, label=_('Comment'), + attribute='comment' + ) - SourceColumn(source=WorkflowState, label=_('Is initial state?'), attribute=encapsulate(lambda state: two_state_template(state.initial))) - SourceColumn(source=WorkflowState, label=_('Completion'), attribute='completion') + SourceColumn( + source=WorkflowState, label=_('Is initial state?'), + attribute=encapsulate( + lambda state: two_state_template(state.initial) + ) + ) + SourceColumn( + source=WorkflowState, label=_('Completion'), attribute='completion' + ) - SourceColumn(source=WorkflowTransition, label=_('Origin state'), attribute='origin_state') - SourceColumn(source=WorkflowTransition, label=_('Destination state'), attribute='destination_state') + SourceColumn( + source=WorkflowTransition, label=_('Origin state'), + attribute='origin_state' + ) + SourceColumn( + source=WorkflowTransition, label=_('Destination state'), + attribute='destination_state' + ) - menu_facet.bind_links(links=[link_document_workflow_instance_list], sources=[Document]) - menu_object.bind_links(links=[link_setup_workflow_states, link_setup_workflow_transitions, link_setup_workflow_document_types, link_setup_workflow_edit, link_setup_workflow_delete], sources=[Workflow]) - menu_object.bind_links(links=[link_setup_workflow_state_edit, link_setup_workflow_state_delete], sources=[WorkflowState]) - menu_object.bind_links(links=[link_setup_workflow_transition_edit, link_setup_workflow_transition_delete], sources=[WorkflowTransition]) - menu_object.bind_links(links=[link_workflow_instance_detail, link_workflow_instance_transition], sources=[WorkflowInstance]) - menu_secondary.bind_links(links=[link_setup_workflow_list, link_setup_workflow_create], sources=[Workflow, 'document_states:setup_workflow_create', 'document_states:setup_workflow_list']) + menu_facet.bind_links( + links=[link_document_workflow_instance_list], sources=[Document] + ) + menu_object.bind_links( + links=[ + link_setup_workflow_states, link_setup_workflow_transitions, + link_setup_workflow_document_types, link_setup_workflow_edit, + link_setup_workflow_delete + ], sources=[Workflow] + ) + menu_object.bind_links( + links=[ + link_setup_workflow_state_edit, + link_setup_workflow_state_delete + ], sources=[WorkflowState] + ) + menu_object.bind_links( + links=[ + link_setup_workflow_transition_edit, + link_setup_workflow_transition_delete + ], sources=[WorkflowTransition] + ) + menu_object.bind_links( + links=[ + link_workflow_instance_detail, + link_workflow_instance_transition + ], sources=[WorkflowInstance] + ) + menu_secondary.bind_links( + links=[link_setup_workflow_list, link_setup_workflow_create], + sources=[ + Workflow, 'document_states:setup_workflow_create', + 'document_states:setup_workflow_list' + ] + ) menu_setup.bind_links(links=[link_setup_workflow_list]) - menu_sidebar.bind_links(links=[link_setup_workflow_state_create, link_setup_workflow_transition_create], sources=[Workflow]) + menu_sidebar.bind_links( + links=[ + link_setup_workflow_state_create, + link_setup_workflow_transition_create + ], sources=[Workflow] + ) - post_save.connect(launch_workflow, dispatch_uid='launch_workflow', sender=Document) + post_save.connect( + launch_workflow, dispatch_uid='launch_workflow', sender=Document + ) diff --git a/mayan/apps/document_states/links.py b/mayan/apps/document_states/links.py index 1921654dfc..988224637c 100644 --- a/mayan/apps/document_states/links.py +++ b/mayan/apps/document_states/links.py @@ -4,19 +4,66 @@ from django.utils.translation import ugettext_lazy as _ from navigation import Link -link_document_workflow_instance_list = Link(text=_('Workflows'), view='document_states:document_workflow_instance_list', args='object.pk') -link_setup_workflow_create = Link(text=_('Create workflow'), view='document_states:setup_workflow_create') -link_setup_workflow_delete = Link(tags='dangerous', text=_('Delete'), view='document_states:setup_workflow_delete', args='object.pk') -link_setup_workflow_document_types = Link(text=_('Document types'), view='document_states:setup_workflow_document_types', args='object.pk') -link_setup_workflow_edit = Link(text=_('Edit'), view='document_states:setup_workflow_edit', args='object.pk') -link_setup_workflow_list = Link(icon='fa fa-sitemap', text=_('Workflows'), view='document_states:setup_workflow_list') -link_setup_workflow_state_create = Link(text=_('Create state'), view='document_states:setup_workflow_state_create', args='object.pk') -link_setup_workflow_state_delete = Link(tags='dangerous', text=_('Delete'), view='document_states:setup_workflow_state_delete', args='object.pk') -link_setup_workflow_state_edit = Link(text=_('Edit'), view='document_states:setup_workflow_state_edit', args='object.pk') -link_setup_workflow_states = Link(text=_('States'), view='document_states:setup_workflow_states', args='object.pk') -link_setup_workflow_transition_create = Link(text=_('Create transition'), view='document_states:setup_workflow_transition_create', args='object.pk') -link_setup_workflow_transition_delete = Link(tags='dangerous', text=_('Delete'), view='document_states:setup_workflow_transition_delete', args='object.pk') -link_setup_workflow_transition_edit = Link(text=_('Edit'), view='document_states:setup_workflow_transition_edit', args='object.pk') -link_setup_workflow_transitions = Link(text=_('Transitions'), view='document_states:setup_workflow_transitions', args='object.pk') -link_workflow_instance_detail = Link(text=_('Detail'), view='document_states:workflow_instance_detail', args='resolved_object.pk') -link_workflow_instance_transition = Link(text=_('Transition'), view='document_states:workflow_instance_transition', args='resolved_object.pk') +link_document_workflow_instance_list = Link( + text=_('Workflows'), + view='document_states:document_workflow_instance_list', args='object.pk' +) +link_setup_workflow_create = Link( + text=_('Create workflow'), view='document_states:setup_workflow_create' +) +link_setup_workflow_delete = Link( + tags='dangerous', text=_('Delete'), + view='document_states:setup_workflow_delete', args='object.pk' +) +link_setup_workflow_document_types = Link( + text=_('Document types'), + view='document_states:setup_workflow_document_types', args='object.pk' +) +link_setup_workflow_edit = Link( + text=_('Edit'), view='document_states:setup_workflow_edit', + args='object.pk' +) +link_setup_workflow_list = Link( + icon='fa fa-sitemap', text=_('Workflows'), + view='document_states:setup_workflow_list' +) +link_setup_workflow_state_create = Link( + text=_('Create state'), + view='document_states:setup_workflow_state_create', args='object.pk' +) +link_setup_workflow_state_delete = Link( + tags='dangerous', text=_('Delete'), + view='document_states:setup_workflow_state_delete', args='object.pk' +) +link_setup_workflow_state_edit = Link( + text=_('Edit'), view='document_states:setup_workflow_state_edit', + args='object.pk' +) +link_setup_workflow_states = Link( + text=_('States'), view='document_states:setup_workflow_states', + args='object.pk' +) +link_setup_workflow_transition_create = Link( + text=_('Create transition'), + view='document_states:setup_workflow_transition_create', args='object.pk' +) +link_setup_workflow_transition_delete = Link( + tags='dangerous', text=_('Delete'), + view='document_states:setup_workflow_transition_delete', args='object.pk' +) +link_setup_workflow_transition_edit = Link( + text=_('Edit'), view='document_states:setup_workflow_transition_edit', + args='object.pk' +) +link_setup_workflow_transitions = Link( + text=_('Transitions'), view='document_states:setup_workflow_transitions', + args='object.pk' +) +link_workflow_instance_detail = Link( + text=_('Detail'), view='document_states:workflow_instance_detail', + args='resolved_object.pk' +) +link_workflow_instance_transition = Link( + text=_('Transition'), + view='document_states:workflow_instance_transition', args='resolved_object.pk' +) diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index 9bea5af12d..49d84482b2 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -17,8 +17,13 @@ logger = logging.getLogger(__name__) @python_2_unicode_compatible class Workflow(models.Model): - label = models.CharField(max_length=255, unique=True, verbose_name=_('Label')) - document_types = models.ManyToManyField(DocumentType, related_name='workflows', verbose_name=_('Document types')) + label = models.CharField( + max_length=255, unique=True, verbose_name=_('Label') + ) + document_types = models.ManyToManyField( + DocumentType, related_name='workflows', + verbose_name=_('Document types') + ) objects = WorkflowManager() @@ -36,12 +41,18 @@ class Workflow(models.Model): def launch_for(self, document): try: - logger.info('Launching workflow %s for document %s', self, document) + logger.info( + 'Launching workflow %s for document %s', self, document + ) self.instances.create(document=document) except IntegrityError: - logger.info('Workflow %s already launched for document %s', self, document) + logger.info( + 'Workflow %s already launched for document %s', self, document + ) else: - logger.info('Workflow %s launched for document %s', self, document) + logger.info( + 'Workflow %s launched for document %s', self, document + ) class Meta: verbose_name = _('Workflow') @@ -50,10 +61,23 @@ class Workflow(models.Model): @python_2_unicode_compatible class WorkflowState(models.Model): - workflow = models.ForeignKey(Workflow, related_name='states', verbose_name=_('Workflow')) + workflow = models.ForeignKey( + Workflow, related_name='states', verbose_name=_('Workflow') + ) label = models.CharField(max_length=255, verbose_name=_('Label')) - initial = models.BooleanField(default=False, help_text=_('Select if this will be the state with which you want the workflow to start in. Only one state can be the initial state.'), verbose_name=_('Initial')) - completion = models.IntegerField(blank=True, default=0, help_text=_('Enter the percent of completion that this state represents in relation to the workflow. Use numbers without the percent sign.'), verbose_name=_('Completion')) + initial = models.BooleanField( + default=False, + help_text=_( + 'Select if this will be the state with which you want the ' + 'workflow to start in. Only one state can be the initial state.' + ), verbose_name=_('Initial') + ) + completion = models.IntegerField( + blank=True, default=0, help_text=_( + 'Enter the percent of completion that this state represents in ' + 'relation to the workflow. Use numbers without the percent sign.' + ), verbose_name=_('Completion') + ) def __str__(self): return self.label @@ -71,36 +95,54 @@ class WorkflowState(models.Model): @python_2_unicode_compatible class WorkflowTransition(models.Model): - workflow = models.ForeignKey(Workflow, related_name='transitions', verbose_name=_('Workflow')) + workflow = models.ForeignKey( + Workflow, related_name='transitions', verbose_name=_('Workflow') + ) label = models.CharField(max_length=255, verbose_name=_('Label')) - origin_state = models.ForeignKey(WorkflowState, related_name='origin_transitions', verbose_name=_('Origin state')) - destination_state = models.ForeignKey(WorkflowState, related_name='destination_transitions', verbose_name=_('Destination state')) + origin_state = models.ForeignKey( + WorkflowState, related_name='origin_transitions', + verbose_name=_('Origin state') + ) + destination_state = models.ForeignKey( + WorkflowState, related_name='destination_transitions', + verbose_name=_('Destination state') + ) def __str__(self): return self.label class Meta: - unique_together = ('workflow', 'label', 'origin_state', 'destination_state') + unique_together = ( + 'workflow', 'label', 'origin_state', 'destination_state' + ) verbose_name = _('Workflow transition') verbose_name_plural = _('Workflow transitions') @python_2_unicode_compatible class WorkflowInstance(models.Model): - workflow = models.ForeignKey(Workflow, related_name='instances', verbose_name=_('Workflow')) - document = models.ForeignKey(Document, related_name='workflows', verbose_name=_('Document')) + workflow = models.ForeignKey( + Workflow, related_name='instances', verbose_name=_('Workflow') + ) + document = models.ForeignKey( + Document, related_name='workflows', verbose_name=_('Document') + ) def __str__(self): return unicode(self.workflow) def get_absolute_url(self): - return reverse('document_states:workflow_instance_detail', args=[str(self.pk)]) + return reverse( + 'document_states:workflow_instance_detail', args=[str(self.pk)] + ) def do_transition(self, comment, transition, user): try: if transition in self.get_current_state().origin_transitions.all(): - self.log_entries.create(comment=comment, transition=transition, user=user) + self.log_entries.create( + comment=comment, transition=transition, user=user + ) except AttributeError: # No initial state has been set for this workflow pass @@ -134,9 +176,16 @@ class WorkflowInstance(models.Model): @python_2_unicode_compatible class WorkflowInstanceLogEntry(models.Model): - workflow_instance = models.ForeignKey(WorkflowInstance, related_name='log_entries', verbose_name=_('Workflow instance')) - datetime = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_('Datetime')) - transition = models.ForeignKey(WorkflowTransition, verbose_name=_('Transition')) + workflow_instance = models.ForeignKey( + WorkflowInstance, related_name='log_entries', + verbose_name=_('Workflow instance') + ) + datetime = models.DateTimeField( + auto_now_add=True, db_index=True, verbose_name=_('Datetime') + ) + transition = models.ForeignKey( + WorkflowTransition, verbose_name=_('Transition') + ) user = models.ForeignKey(User, verbose_name=_('User')) comment = models.TextField(blank=True, verbose_name=_('Comment')) diff --git a/mayan/apps/document_states/permissions.py b/mayan/apps/document_states/permissions.py index b0cd7dff39..d1e0a914c7 100644 --- a/mayan/apps/document_states/permissions.py +++ b/mayan/apps/document_states/permissions.py @@ -6,9 +6,22 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('document_states', _('Document workflows')) -permission_workflow_create = namespace.add_permission(name='workflow_create', label=_('Create workflows')) -permission_workflow_delete = namespace.add_permission(name='workflow_delte', label=_('Delete workflows')) -permission_workflow_edit = namespace.add_permission(name='workflow_edit', label=_('Edit workflows')) -permission_workflow_view = namespace.add_permission(name='workflow_view', label=_('View workflows')) -permission_document_workflow_view = namespace.add_permission(name='document_workflow_view', label=_('View document workflows')) -permission_document_workflow_transition = namespace.add_permission(name='document_workflow_transition', label=_('Transition document workflows')) +permission_workflow_create = namespace.add_permission( + name='workflow_create', label=_('Create workflows') +) +permission_workflow_delete = namespace.add_permission( + name='workflow_delte', label=_('Delete workflows') +) +permission_workflow_edit = namespace.add_permission( + name='workflow_edit', label=_('Edit workflows') +) +permission_workflow_view = namespace.add_permission( + name='workflow_view', label=_('View workflows') +) +permission_document_workflow_view = namespace.add_permission( + name='document_workflow_view', label=_('View document workflows') +) +permission_document_workflow_transition = namespace.add_permission( + name='document_workflow_transition', + label=_('Transition document workflows') +) diff --git a/mayan/apps/document_states/urls.py b/mayan/apps/document_states/urls.py index b56dfb2952..b4f91b1c91 100644 --- a/mayan/apps/document_states/urls.py +++ b/mayan/apps/document_states/urls.py @@ -16,22 +16,84 @@ from .views import ( urlpatterns = patterns( '', - url(r'^document/(?P\d+)/workflows/$', DocumentWorkflowInstanceListView.as_view(), name='document_workflow_instance_list'), - url(r'^document/workflows/(?P\d+)/$', WorkflowInstanceDetailView.as_view(), name='workflow_instance_detail'), - url(r'^document/workflows/(?P\d+)/transition/$', WorkflowInstanceTransitionView.as_view(), name='workflow_instance_transition'), + url( + r'^document/(?P\d+)/workflows/$', + DocumentWorkflowInstanceListView.as_view(), + name='document_workflow_instance_list' + ), + url( + r'^document/workflows/(?P\d+)/$', + WorkflowInstanceDetailView.as_view(), name='workflow_instance_detail' + ), + url( + r'^document/workflows/(?P\d+)/transition/$', + WorkflowInstanceTransitionView.as_view(), + name='workflow_instance_transition' + ), - url(r'^setup/all/$', SetupWorkflowListView.as_view(), name='setup_workflow_list'), - url(r'^setup/create/$', SetupWorkflowCreateView.as_view(), name='setup_workflow_create'), - url(r'^setup/(?P\d+)/edit/$', SetupWorkflowEditView.as_view(), name='setup_workflow_edit'), - url(r'^setup/(?P\d+)/delete/$', SetupWorkflowDeleteView.as_view(), name='setup_workflow_delete'), - url(r'^setup/(?P\d+)/documents/$', WorkflowDocumentListView.as_view(), name='setup_workflow_document_list'), - url(r'^setup/(?P\d+)/document_types/$', SetupWorkflowDocumentTypesView.as_view(), name='setup_workflow_document_types'), - url(r'^setup/(?P\d+)/states/$', SetupWorkflowStateListView.as_view(), name='setup_workflow_states'), - url(r'^setup/(?P\d+)/states/create/$', SetupWorkflowStateCreateView.as_view(), name='setup_workflow_state_create'), - url(r'^setup/(?P\d+)/transitions/$', SetupWorkflowTransitionListView.as_view(), name='setup_workflow_transitions'), - url(r'^setup/(?P\d+)/transitions/create/$', SetupWorkflowTransitionCreateView.as_view(), name='setup_workflow_transition_create'), - url(r'^setup/workflow/state/(?P\d+)/delete/$', SetupWorkflowStateDeleteView.as_view(), name='setup_workflow_state_delete'), - url(r'^setup/workflow/state/(?P\d+)/edit/$', SetupWorkflowStateEditView.as_view(), name='setup_workflow_state_edit'), - url(r'^setup/workflow/transitions/(?P\d+)/delete/$', SetupWorkflowTransitionDeleteView.as_view(), name='setup_workflow_transition_delete'), - url(r'^setup/workflow/transitions/(?P\d+)/edit/$', SetupWorkflowTransitionEditView.as_view(), name='setup_workflow_transition_edit'), + url( + r'^setup/all/$', SetupWorkflowListView.as_view(), + name='setup_workflow_list' + ), + url( + r'^setup/create/$', SetupWorkflowCreateView.as_view(), + name='setup_workflow_create' + ), + url( + r'^setup/(?P\d+)/edit/$', SetupWorkflowEditView.as_view(), + name='setup_workflow_edit' + ), + url( + r'^setup/(?P\d+)/delete/$', SetupWorkflowDeleteView.as_view(), + name='setup_workflow_delete' + ), + url( + r'^setup/(?P\d+)/documents/$', + WorkflowDocumentListView.as_view(), + name='setup_workflow_document_list' + ), + url( + r'^setup/(?P\d+)/document_types/$', + SetupWorkflowDocumentTypesView.as_view(), + name='setup_workflow_document_types' + ), + url( + r'^setup/(?P\d+)/states/$', SetupWorkflowStateListView.as_view(), + name='setup_workflow_states' + ), + url( + r'^setup/(?P\d+)/states/create/$', + SetupWorkflowStateCreateView.as_view(), + name='setup_workflow_state_create' + ), + url( + r'^setup/(?P\d+)/transitions/$', + SetupWorkflowTransitionListView.as_view(), + name='setup_workflow_transitions' + ), + url( + r'^setup/(?P\d+)/transitions/create/$', + SetupWorkflowTransitionCreateView.as_view(), + name='setup_workflow_transition_create' + ), + url( + r'^setup/workflow/state/(?P\d+)/delete/$', + SetupWorkflowStateDeleteView.as_view(), + name='setup_workflow_state_delete' + ), + url( + r'^setup/workflow/state/(?P\d+)/edit/$', + SetupWorkflowStateEditView.as_view(), + name='setup_workflow_state_edit' + ), + url( + r'^setup/workflow/transitions/(?P\d+)/delete/$', + SetupWorkflowTransitionDeleteView.as_view(), + name='setup_workflow_transition_delete' + ), + url( + r'^setup/workflow/transitions/(?P\d+)/edit/$', + SetupWorkflowTransitionEditView.as_view(), + name='setup_workflow_transition_edit' + ), ) diff --git a/mayan/apps/document_states/views.py b/mayan/apps/document_states/views.py index 5c62968c7d..71715b3559 100644 --- a/mayan/apps/document_states/views.py +++ b/mayan/apps/document_states/views.py @@ -33,11 +33,18 @@ from .permissions import ( class DocumentWorkflowInstanceListView(SingleObjectListView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_document_workflow_view]) + Permission.check_permissions( + request.user, [permission_document_workflow_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_workflow_view, request.user, self.get_document()) + AccessControlList.objects.check_access( + permission_document_workflow_view, request.user, + self.get_document() + ) - return super(DocumentWorkflowInstanceListView, self).dispatch(request, *args, **kwargs) + return super( + DocumentWorkflowInstanceListView, self + ).dispatch(request, *args, **kwargs) def get_document(self): return get_object_or_404(Document, pk=self.kwargs['pk']) @@ -46,12 +53,16 @@ class DocumentWorkflowInstanceListView(SingleObjectListView): return self.get_document().workflows.all() def get_context_data(self, **kwargs): - context = super(DocumentWorkflowInstanceListView, self).get_context_data(**kwargs) + context = super( + DocumentWorkflowInstanceListView, self + ).get_context_data(**kwargs) context.update( { 'hide_link': True, 'object': self.get_document(), - 'title': _('Workflows for document: %s') % self.get_document(), + 'title': _( + 'Workflows for document: %s' + ) % self.get_document(), } ) @@ -63,14 +74,22 @@ class WorkflowDocumentListView(DocumentListView): self.workflow = get_object_or_404(Workflow, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, [permission_workflow_view]) + Permission.check_permissions( + request.user, [permission_workflow_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_workflow_view, request.user, self.workflow) + AccessControlList.objects.check_access( + permission_workflow_view, request.user, self.workflow + ) - return super(WorkflowDocumentListView, self).dispatch(request, *args, **kwargs) + return super( + WorkflowDocumentListView, self + ).dispatch(request, *args, **kwargs) def get_document_queryset(self): - return Document.objects.filter(document_type__in=self.workflow.document_types.all()) + return Document.objects.filter( + document_type__in=self.workflow.document_types.all() + ) def get_extra_context(self): return { @@ -83,11 +102,18 @@ class WorkflowDocumentListView(DocumentListView): class WorkflowInstanceDetailView(SingleObjectListView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_document_workflow_view]) + Permission.check_permissions( + request.user, [permission_document_workflow_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_workflow_view, request.user, self.get_workflow_instance().document) + AccessControlList.objects.check_access( + permission_document_workflow_view, request.user, + self.get_workflow_instance().document + ) - return super(WorkflowInstanceDetailView, self).dispatch(request, *args, **kwargs) + return super( + WorkflowInstanceDetailView, self + ).dispatch(request, *args, **kwargs) def get_workflow_instance(self): return get_object_or_404(WorkflowInstance, pk=self.kwargs['pk']) @@ -116,15 +142,27 @@ class WorkflowInstanceTransitionView(FormView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_document_workflow_transition]) + Permission.check_permissions( + request.user, [permission_document_workflow_transition] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_workflow_transition, request.user, self.get_workflow_instance().document) + AccessControlList.objects.check_access( + permission_document_workflow_transition, request.user, + self.get_workflow_instance().document + ) - return super(WorkflowInstanceTransitionView, self).dispatch(request, *args, **kwargs) + return super( + WorkflowInstanceTransitionView, self + ).dispatch(request, *args, **kwargs) def form_valid(self, form): - transition = self.get_workflow_instance().workflow.transitions.get(pk=form.cleaned_data['transition']) - self.get_workflow_instance().do_transition(comment=form.cleaned_data['comment'], transition=transition, user=self.request.user) + transition = self.get_workflow_instance().workflow.transitions.get( + pk=form.cleaned_data['transition'] + ) + self.get_workflow_instance().do_transition( + comment=form.cleaned_data['comment'], transition=transition, + user=self.request.user + ) return HttpResponseRedirect(self.get_success_url()) def get_form_kwargs(self): @@ -136,14 +174,18 @@ class WorkflowInstanceTransitionView(FormView): return get_object_or_404(WorkflowInstance, pk=self.kwargs['pk']) def get_context_data(self, **kwargs): - context = super(WorkflowInstanceTransitionView, self).get_context_data(**kwargs) + context = super( + WorkflowInstanceTransitionView, self + ).get_context_data(**kwargs) context.update( { 'navigation_object_list': ['object', 'workflow_instance'], 'object': self.get_workflow_instance().document, 'submit_label': _('Submit'), - 'title': _('Do transition for workflow: %s') % self.get_workflow_instance(), + 'title': _( + 'Do transition for workflow: %s' + ) % self.get_workflow_instance(), 'workflow_instance': self.get_workflow_instance(), } ) @@ -193,17 +235,21 @@ class SetupWorkflowDocumentTypesView(AssignRemoveView): def add(self, item): self.get_object().document_types.add(item) - # TODO: add task launching this workflow for all the document types of - # item + # TODO: add task launching this workflow for all the document types + # of item def get_object(self): return get_object_or_404(Workflow, pk=self.kwargs['pk']) def left_list(self): - return AssignRemoveView.generate_choices(self.get_object().get_document_types_not_in_workflow()) + return AssignRemoveView.generate_choices( + self.get_object().get_document_types_not_in_workflow() + ) def right_list(self): - return AssignRemoveView.generate_choices(self.get_object().document_types.all()) + return AssignRemoveView.generate_choices( + self.get_object().document_types.all() + ) def remove(self, item): self.get_object().document_types.remove(item) @@ -211,9 +257,13 @@ class SetupWorkflowDocumentTypesView(AssignRemoveView): # item def get_context_data(self, **kwargs): - data = super(SetupWorkflowDocumentTypesView, self).get_context_data(**kwargs) + data = super( + SetupWorkflowDocumentTypesView, self + ).get_context_data(**kwargs) data.update({ - 'title': _('Document types assigned the workflow: %s') % self.get_object(), + 'title': _( + 'Document types assigned the workflow: %s' + ) % self.get_object(), 'object': self.get_object(), }) @@ -223,11 +273,17 @@ class SetupWorkflowDocumentTypesView(AssignRemoveView): class SetupWorkflowStateListView(SingleObjectListView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_workflow_edit]) + Permission.check_permissions( + request.user, [permission_workflow_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_workflow_edit, request.user, self.get_workflow()) + AccessControlList.objects.check_access( + permission_workflow_edit, request.user, self.get_workflow() + ) - return super(SetupWorkflowStateListView, self).dispatch(request, *args, **kwargs) + return super( + SetupWorkflowStateListView, self + ).dispatch(request, *args, **kwargs) def get_workflow(self): return get_object_or_404(Workflow, pk=self.kwargs['pk']) @@ -236,7 +292,9 @@ class SetupWorkflowStateListView(SingleObjectListView): return self.get_workflow().states.all() def get_context_data(self, **kwargs): - context = super(SetupWorkflowStateListView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowStateListView, self + ).get_context_data(**kwargs) context.update( { 'hide_link': True, @@ -253,18 +311,28 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_workflow_edit]) + Permission.check_permissions( + request.user, [permission_workflow_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_workflow_edit, request.user, self.get_workflow()) + AccessControlList.objects.check_access( + permission_workflow_edit, request.user, self.get_workflow() + ) - return super(SetupWorkflowStateCreateView, self).dispatch(request, *args, **kwargs) + return super( + SetupWorkflowStateCreateView, self + ).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): - context = super(SetupWorkflowStateCreateView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowStateCreateView, self + ).get_context_data(**kwargs) context.update( { 'object': self.get_workflow(), - 'title': _('Create states for workflow: %s') % self.get_workflow() + 'title': _( + 'Create states for workflow: %s' + ) % self.get_workflow() } ) return context @@ -276,7 +344,9 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView): return self.get_workflow().states.all() def get_success_url(self): - return reverse('document_states:setup_workflow_states', args=[self.kwargs['pk']]) + return reverse( + 'document_states:setup_workflow_states', args=[self.kwargs['pk']] + ) def form_valid(self, form): self.object = form.save(commit=False) @@ -290,7 +360,9 @@ class SetupWorkflowStateDeleteView(SingleObjectDeleteView): view_permission = permission_workflow_delete def get_context_data(self, **kwargs): - context = super(SetupWorkflowStateDeleteView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowStateDeleteView, self + ).get_context_data(**kwargs) context.update( { @@ -303,7 +375,10 @@ class SetupWorkflowStateDeleteView(SingleObjectDeleteView): return context def get_success_url(self): - return reverse('document_states:setup_workflow_States', args=[self.get_object().workflow.pk]) + return reverse( + 'document_states:setup_workflow_States', + args=[self.get_object().workflow.pk] + ) class SetupWorkflowStateEditView(SingleObjectEditView): @@ -312,7 +387,9 @@ class SetupWorkflowStateEditView(SingleObjectEditView): view_permission = permission_workflow_edit def get_context_data(self, **kwargs): - context = super(SetupWorkflowStateEditView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowStateEditView, self + ).get_context_data(**kwargs) context.update( { @@ -325,7 +402,10 @@ class SetupWorkflowStateEditView(SingleObjectEditView): return context def get_success_url(self): - return reverse('document_states:setup_workflow_states', args=[self.get_object().workflow.pk]) + return reverse( + 'document_states:setup_workflow_states', + args=[self.get_object().workflow.pk] + ) # Transitions @@ -334,11 +414,17 @@ class SetupWorkflowStateEditView(SingleObjectEditView): class SetupWorkflowTransitionListView(SingleObjectListView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_workflow_edit]) + Permission.check_permissions( + request.user, [permission_workflow_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_workflow_edit, request.user, self.get_workflow()) + AccessControlList.objects.check_access( + permission_workflow_edit, request.user, self.get_workflow() + ) - return super(SetupWorkflowTransitionListView, self).dispatch(request, *args, **kwargs) + return super( + SetupWorkflowTransitionListView, self + ).dispatch(request, *args, **kwargs) def get_workflow(self): return get_object_or_404(Workflow, pk=self.kwargs['pk']) @@ -347,12 +433,16 @@ class SetupWorkflowTransitionListView(SingleObjectListView): return self.get_workflow().transitions.all() def get_context_data(self, **kwargs): - context = super(SetupWorkflowTransitionListView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowTransitionListView, self + ).get_context_data(**kwargs) context.update( { 'hide_link': True, 'object': self.get_workflow(), - 'title': _('Transitions of workflow: %s') % self.get_workflow() + 'title': _( + 'Transitions of workflow: %s' + ) % self.get_workflow() } ) @@ -364,24 +454,36 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView): def dispatch(self, request, *args, **kwargs): try: - Permission.check_permissions(request.user, [permission_workflow_edit]) + Permission.check_permissions( + request.user, [permission_workflow_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_workflow_edit, request.user, self.get_workflow()) + AccessControlList.objects.check_access( + permission_workflow_edit, request.user, self.get_workflow() + ) - return super(SetupWorkflowTransitionCreateView, self).dispatch(request, *args, **kwargs) + return super( + SetupWorkflowTransitionCreateView, self + ).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): - context = super(SetupWorkflowTransitionCreateView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowTransitionCreateView, self + ).get_context_data(**kwargs) context.update( { 'object': self.get_workflow(), - 'title': _('Create transitions for workflow: %s') % self.get_workflow() + 'title': _( + 'Create transitions for workflow: %s' + ) % self.get_workflow() } ) return context def get_form_kwargs(self): - kwargs = super(SetupWorkflowTransitionCreateView, self).get_form_kwargs() + kwargs = super( + SetupWorkflowTransitionCreateView, self + ).get_form_kwargs() kwargs['workflow'] = self.get_workflow() return kwargs @@ -392,7 +494,10 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView): return self.get_workflow().transitions.all() def get_success_url(self): - return reverse('document_states:setup_workflow_transitions', args=[self.kwargs['pk']]) + return reverse( + 'document_states:setup_workflow_transitions', + args=[self.kwargs['pk']] + ) def form_valid(self, form): self.object = form.save(commit=False) @@ -400,8 +505,12 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView): try: self.object.save() except IntegrityError: - messages.error(self.request, _('Unable to save transition; integrity error.')) - return super(SetupWorkflowTransitionCreateView, self).form_invalid(form) + messages.error( + self.request, _('Unable to save transition; integrity error.') + ) + return super( + SetupWorkflowTransitionCreateView, self + ).form_invalid(form) else: return HttpResponseRedirect(self.get_success_url()) @@ -411,7 +520,9 @@ class SetupWorkflowTransitionDeleteView(SingleObjectDeleteView): view_permission = permission_workflow_delete def get_context_data(self, **kwargs): - context = super(SetupWorkflowTransitionDeleteView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowTransitionDeleteView, self + ).get_context_data(**kwargs) context.update( { @@ -424,7 +535,10 @@ class SetupWorkflowTransitionDeleteView(SingleObjectDeleteView): return context def get_success_url(self): - return reverse('document_states:setup_workflow_transitions', args=[self.get_object().workflow.pk]) + return reverse( + 'document_states:setup_workflow_transitions', + args=[self.get_object().workflow.pk] + ) class SetupWorkflowTransitionEditView(SingleObjectEditView): @@ -433,7 +547,9 @@ class SetupWorkflowTransitionEditView(SingleObjectEditView): view_permission = permission_workflow_edit def get_context_data(self, **kwargs): - context = super(SetupWorkflowTransitionEditView, self).get_context_data(**kwargs) + context = super( + SetupWorkflowTransitionEditView, self + ).get_context_data(**kwargs) context.update( { @@ -446,9 +562,14 @@ class SetupWorkflowTransitionEditView(SingleObjectEditView): return context def get_form_kwargs(self): - kwargs = super(SetupWorkflowTransitionEditView, self).get_form_kwargs() + kwargs = super( + SetupWorkflowTransitionEditView, self + ).get_form_kwargs() kwargs['workflow'] = self.get_object().workflow return kwargs def get_success_url(self): - return reverse('document_states:setup_workflow_transitions', args=[self.get_object().workflow.pk]) + return reverse( + 'document_states:setup_workflow_transitions', + args=[self.get_object().workflow.pk] + ) diff --git a/mayan/apps/documents/api_views.py b/mayan/apps/documents/api_views.py index 1f39864fe3..a9a2ed03e6 100644 --- a/mayan/apps/documents/api_views.py +++ b/mayan/apps/documents/api_views.py @@ -162,9 +162,9 @@ class APIDocumentVersionCreateView(generics.CreateAPIView): ) if serializer.is_valid(): - # Nested resource we take the document pk from the URL and insert it - # so that it needs not to be specified by the user, we mark it as - # a read only field in the serializer + # Nested resource we take the document pk from the URL and insert + # it so that it needs not to be specified by the user, we mark + # it as a read only field in the serializer document = get_object_or_404(Document, pk=kwargs['pk']) document.new_version( @@ -361,7 +361,8 @@ class APIDocumentTypeDocumentListView(generics.ListAPIView): ) except PermissionDenied: AccessControlList.objects.check_access( - permission_document_type_view, self.request.user, document_type + permission_document_type_view, self.request.user, + document_type ) return document_type.documents.all() diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py index 4c7ad12ade..c286220138 100644 --- a/mayan/apps/documents/apps.py +++ b/mayan/apps/documents/apps.py @@ -84,8 +84,10 @@ class DocumentsApp(MayanAppConfig): MissingItem( label=_('Create a document type'), - description=_('Every uploaded document must be assigned a document type, it is the basic way Mayan EDMS categorizes documents.'), - condition=lambda: not DocumentType.objects.exists(), + description=_( + 'Every uploaded document must be assigned a document type, ' + 'it is the basic way Mayan EDMS categorizes documents.' + ), condition=lambda: not DocumentType.objects.exists(), view='documents:document_type_list' ) diff --git a/mayan/apps/documents/forms.py b/mayan/apps/documents/forms.py index 4687cc3a85..a0997e908d 100644 --- a/mayan/apps/documents/forms.py +++ b/mayan/apps/documents/forms.py @@ -42,7 +42,9 @@ class DocumentPreviewForm(forms.Form): super(DocumentPreviewForm, self).__init__(*args, **kwargs) self.fields['preview'].initial = document try: - self.fields['preview'].label = _('Document pages (%d)') % document.page_count + self.fields['preview'].label = _( + 'Document pages (%d)' + ) % document.page_count except AttributeError: self.fields['preview'].label = _('Document pages (%d)') % 0 @@ -62,13 +64,16 @@ class DocumentForm(forms.ModelForm): super(DocumentForm, self).__init__(*args, **kwargs) - # Is a document (documents app edit) and has been saved (sources app upload)? + # Is a document (documents app edit) and has been saved (sources + # app upload)? if self.instance and self.instance.pk: document_type = self.instance.document_type filenames_qs = document_type.filenames.filter(enabled=True) if filenames_qs.count(): - self.fields['document_type_available_filenames'] = forms.ModelChoiceField( + self.fields[ + 'document_type_available_filenames' + ] = forms.ModelChoiceField( queryset=filenames_qs, required=False, label=_('Quick document rename') @@ -119,12 +124,20 @@ class DocumentTypeFilenameForm_create(forms.ModelForm): class DocumentDownloadForm(forms.Form): compressed = forms.BooleanField( label=_('Compress'), required=False, - help_text=_('Download the document in the original format or in a compressed manner. This option is selectable only when downloading one document, for multiple documents, the bundle will always be downloads as a compressed file.') + help_text=_( + 'Download the document in the original format or in a compressed ' + 'manner. This option is selectable only when downloading one ' + 'document, for multiple documents, the bundle will always be ' + 'downloads as a compressed file.' + ) ) zip_filename = forms.CharField( initial=DEFAULT_ZIP_FILENAME, label=_('Compressed filename'), required=False, - help_text=_('The filename of the compressed file that will contain the documents to be downloaded, if the previous option is selected.') + help_text=_( + 'The filename of the compressed file that will contain the ' + 'documents to be downloaded, if the previous option is selected.' + ) ) def __init__(self, *args, **kwargs): diff --git a/mayan/apps/documents/links.py b/mayan/apps/documents/links.py index bd626b3995..2c7652cfbe 100644 --- a/mayan/apps/documents/links.py +++ b/mayan/apps/documents/links.py @@ -38,67 +38,229 @@ def is_min_zoom(context): # Facet -link_document_preview = Link(permissions=[permission_document_view], text=_('Preview'), view='documents:document_preview', args='object.id') -link_document_properties = Link(permissions=[permission_document_view], text=_('Properties'), view='documents:document_properties', args='object.id') -link_document_version_list = Link(permissions=[permission_document_view], text=_('Versions'), view='documents:document_version_list', args='object.pk') -link_document_pages = Link(permissions=[permission_document_view], text=_('Pages'), view='documents:document_pages', args='resolved_object.pk') +link_document_preview = Link( + permissions=[permission_document_view], text=_('Preview'), + view='documents:document_preview', args='object.id' +) +link_document_properties = Link( + permissions=[permission_document_view], text=_('Properties'), + view='documents:document_properties', args='object.id' +) +link_document_version_list = Link( + permissions=[permission_document_view], text=_('Versions'), + view='documents:document_version_list', args='object.pk' +) +link_document_pages = Link( + permissions=[permission_document_view], text=_('Pages'), + view='documents:document_pages', args='resolved_object.pk' +) # Actions -link_document_clear_transformations = Link(permissions=[permission_transformation_delete], text=_('Clear transformations'), view='documents:document_clear_transformations', args='object.id') -link_document_delete = Link(permissions=[permission_document_delete], tags='dangerous', text=_('Delete'), view='documents:document_delete', args='object.id') -link_document_trash = Link(permissions=[permission_document_trash], tags='dangerous', text=_('Move to trash'), view='documents:document_trash', args='object.id') -link_document_edit = Link(permissions=[permission_document_properties_edit], text=_('Edit properties'), view='documents:document_edit', args='object.id') -link_document_document_type_edit = Link(permissions=[permission_document_properties_edit], text=_('Change type'), view='documents:document_document_type_edit', args='object.id') -link_document_download = Link(permissions=[permission_document_download], text=_('Download'), view='documents:document_download', args='object.id') -link_document_print = Link(permissions=[permission_document_print], text=_('Print'), view='documents:document_print', args='object.id') -link_document_update_page_count = Link(permissions=[permission_document_tools], text=_('Reset page count'), view='documents:document_update_page_count', args='object.pk') -link_document_restore = Link(permissions=[permission_document_restore], text=_('Restore'), view='documents:document_restore', args='object.pk') -link_document_multiple_clear_transformations = Link(permissions=[permission_transformation_delete], text=_('Clear transformations'), view='documents:document_multiple_clear_transformations') -link_document_multiple_trash = Link(permissions=[permission_document_trash], tags='dangerous', text=_('Move to trash'), view='documents:document_multiple_trash') -link_document_multiple_delete = Link(permissions=[permission_document_delete], tags='dangerous', text=_('Delete'), view='documents:document_multiple_delete') -link_document_multiple_document_type_edit = Link(permissions=[permission_document_properties_edit], text=_('Change type'), view='documents:document_multiple_document_type_edit') -link_document_multiple_download = Link(permissions=[permission_document_download], text=_('Download'), view='documents:document_multiple_download') -link_document_multiple_update_page_count = Link(permissions=[permission_document_tools], text=_('Reset page count'), view='documents:document_multiple_update_page_count') -link_document_multiple_restore = Link(permissions=[permission_document_restore], text=_('Restore'), view='documents:document_multiple_restore') -link_document_version_download = Link(args='object.pk', permissions=[permission_document_download], text=_('Download'), view='documents:document_version_download') +link_document_clear_transformations = Link( + permissions=[permission_transformation_delete], + text=_('Clear transformations'), + view='documents:document_clear_transformations', args='object.id' +) +link_document_delete = Link( + permissions=[permission_document_delete], tags='dangerous', + text=_('Delete'), view='documents:document_delete', args='object.id' +) +link_document_trash = Link( + permissions=[permission_document_trash], tags='dangerous', + text=_('Move to trash'), view='documents:document_trash', args='object.id' +) +link_document_edit = Link( + permissions=[permission_document_properties_edit], + text=_('Edit properties'), view='documents:document_edit', + args='object.id' +) +link_document_document_type_edit = Link( + permissions=[permission_document_properties_edit], text=_('Change type'), + view='documents:document_document_type_edit', args='object.id' +) +link_document_download = Link( + permissions=[permission_document_download], text=_('Download'), + view='documents:document_download', args='object.id' +) +link_document_print = Link( + permissions=[permission_document_print], text=_('Print'), + view='documents:document_print', args='object.id' +) +link_document_update_page_count = Link( + permissions=[permission_document_tools], text=_('Reset page count'), + view='documents:document_update_page_count', args='object.pk' +) +link_document_restore = Link( + permissions=[permission_document_restore], text=_('Restore'), + view='documents:document_restore', args='object.pk' +) +link_document_multiple_clear_transformations = Link( + permissions=[permission_transformation_delete], + text=_('Clear transformations'), + view='documents:document_multiple_clear_transformations' +) +link_document_multiple_trash = Link( + permissions=[permission_document_trash], tags='dangerous', + text=_('Move to trash'), view='documents:document_multiple_trash' +) +link_document_multiple_delete = Link( + permissions=[permission_document_delete], tags='dangerous', + text=_('Delete'), view='documents:document_multiple_delete' +) +link_document_multiple_document_type_edit = Link( + permissions=[permission_document_properties_edit], text=_('Change type'), + view='documents:document_multiple_document_type_edit' +) +link_document_multiple_download = Link( + permissions=[permission_document_download], text=_('Download'), + view='documents:document_multiple_download' +) +link_document_multiple_update_page_count = Link( + permissions=[permission_document_tools], text=_('Reset page count'), + view='documents:document_multiple_update_page_count' +) +link_document_multiple_restore = Link( + permissions=[permission_document_restore], text=_('Restore'), + view='documents:document_multiple_restore' +) +link_document_version_download = Link( + args='object.pk', permissions=[permission_document_download], + text=_('Download'), view='documents:document_version_download' +) # Views -link_document_list = Link(icon='fa fa-file', text=_('All documents'), view='documents:document_list') -link_document_list_recent = Link(icon='fa fa-clock-o', text=_('Recent documents'), view='documents:document_list_recent') -link_document_list_deleted = Link(icon='fa fa-trash', text=_('Trash'), view='documents:document_list_deleted') +link_document_list = Link( + icon='fa fa-file', text=_('All documents'), view='documents:document_list' +) +link_document_list_recent = Link( + icon='fa fa-clock-o', text=_('Recent documents'), + view='documents:document_list_recent' +) +link_document_list_deleted = Link( + icon='fa fa-trash', text=_('Trash'), + view='documents:document_list_deleted' +) # Tools link_clear_image_cache = Link( icon='fa fa-file-image-o', - description=_('Clear the graphics representations used to speed up the documents\' display and interactive transformations results.'), + description=_( + 'Clear the graphics representations used to speed up the documents\' ' + 'display and interactive transformations results.' + ), permissions=[permission_document_tools], text=_('Clear document cache'), view='documents:document_clear_image_cache' ) -link_trash_can_empty = Link(permissions=[permission_empty_trash], text=_('Empty trash'), view='documents:trash_can_empty') +link_trash_can_empty = Link( + permissions=[permission_empty_trash], text=_('Empty trash'), + view='documents:trash_can_empty' +) # Document pages -link_document_page_navigation_first = Link(conditional_disable=is_first_page, icon='fa fa-step-backward', keep_query=True, permissions=[permission_document_view], text=_('First page'), view='documents:document_page_navigation_first', args='resolved_object.pk') -link_document_page_navigation_last = Link(conditional_disable=is_last_page, icon='fa fa-step-forward', keep_query=True, text=_('Last page'), permissions=[permission_document_view], view='documents:document_page_navigation_last', args='resolved_object.pk') -link_document_page_navigation_previous = Link(conditional_disable=is_first_page, icon='fa fa-arrow-left', keep_query=True, permissions=[permission_document_view], text=_('Previous page'), view='documents:document_page_navigation_previous', args='resolved_object.pk') -link_document_page_navigation_next = Link(conditional_disable=is_last_page, icon='fa fa-arrow-right', keep_query=True, text=_('Next page'), permissions=[permission_document_view], view='documents:document_page_navigation_next', args='resolved_object.pk') -link_document_page_return = Link(icon='fa fa-file', permissions=[permission_document_view], text=_('Document'), view='documents:document_preview', args='resolved_object.document.pk') -link_document_page_rotate_left = Link(icon='fa fa-rotate-left', permissions=[permission_document_view], text=_('Rotate left'), view='documents:document_page_rotate_left', args='resolved_object.pk') -link_document_page_rotate_right = Link(icon='fa fa-rotate-right', permissions=[permission_document_view], text=_('Rotate right'), view='documents:document_page_rotate_right', args='resolved_object.pk') -link_document_page_view = Link(permissions=[permission_document_view], text=_('Page image'), view='documents:document_page_view', args='resolved_object.pk') -link_document_page_view_reset = Link(permissions=[permission_document_view], text=_('Reset view'), view='documents:document_page_view_reset', args='resolved_object.pk') -link_document_page_zoom_in = Link(conditional_disable=is_max_zoom, icon='fa fa-search-plus', permissions=[permission_document_view], text=_('Zoom in'), view='documents:document_page_zoom_in', args='resolved_object.pk') -link_document_page_zoom_out = Link(conditional_disable=is_min_zoom, icon='fa fa-search-minus', permissions=[permission_document_view], text=_('Zoom out'), view='documents:document_page_zoom_out', args='resolved_object.pk') +link_document_page_navigation_first = Link( + conditional_disable=is_first_page, icon='fa fa-step-backward', + keep_query=True, permissions=[permission_document_view], + text=_('First page'), view='documents:document_page_navigation_first', + args='resolved_object.pk' +) +link_document_page_navigation_last = Link( + conditional_disable=is_last_page, icon='fa fa-step-forward', + keep_query=True, text=_('Last page'), + permissions=[permission_document_view], + view='documents:document_page_navigation_last', args='resolved_object.pk' +) +link_document_page_navigation_previous = Link( + conditional_disable=is_first_page, icon='fa fa-arrow-left', + keep_query=True, permissions=[permission_document_view], + text=_('Previous page'), + view='documents:document_page_navigation_previous', + args='resolved_object.pk' +) +link_document_page_navigation_next = Link( + conditional_disable=is_last_page, icon='fa fa-arrow-right', + keep_query=True, text=_('Next page'), + permissions=[permission_document_view], + view='documents:document_page_navigation_next', args='resolved_object.pk' +) +link_document_page_return = Link( + icon='fa fa-file', permissions=[permission_document_view], + text=_('Document'), view='documents:document_preview', + args='resolved_object.document.pk' +) +link_document_page_rotate_left = Link( + icon='fa fa-rotate-left', permissions=[permission_document_view], + text=_('Rotate left'), view='documents:document_page_rotate_left', + args='resolved_object.pk' +) +link_document_page_rotate_right = Link( + icon='fa fa-rotate-right', permissions=[permission_document_view], + text=_('Rotate right'), view='documents:document_page_rotate_right', + args='resolved_object.pk' +) +link_document_page_view = Link( + permissions=[permission_document_view], text=_('Page image'), + view='documents:document_page_view', args='resolved_object.pk' +) +link_document_page_view_reset = Link( + permissions=[permission_document_view], text=_('Reset view'), + view='documents:document_page_view_reset', args='resolved_object.pk' +) +link_document_page_zoom_in = Link( + conditional_disable=is_max_zoom, icon='fa fa-search-plus', + permissions=[permission_document_view], text=_('Zoom in'), + view='documents:document_page_zoom_in', args='resolved_object.pk' +) +link_document_page_zoom_out = Link( + conditional_disable=is_min_zoom, icon='fa fa-search-minus', + permissions=[permission_document_view], text=_('Zoom out'), + view='documents:document_page_zoom_out', args='resolved_object.pk' +) # Document versions -link_document_version_revert = Link(condition=is_not_current_version, permissions=[permission_document_version_revert], tags='dangerous', text=_('Revert'), view='documents:document_version_revert', args='object.pk') +link_document_version_revert = Link( + condition=is_not_current_version, + permissions=[permission_document_version_revert], tags='dangerous', + text=_('Revert'), view='documents:document_version_revert', + args='object.pk' +) # Document type related links -link_document_type_create = Link(permissions=[permission_document_type_create], text=_('Create document type'), view='documents:document_type_create') -link_document_type_delete = Link(permissions=[permission_document_type_delete], tags='dangerous', text=_('Delete'), view='documents:document_type_delete', args='resolved_object.id') -link_document_type_edit = Link(permissions=[permission_document_type_edit], text=_('Edit'), view='documents:document_type_edit', args='resolved_object.id') -link_document_type_filename_create = Link(permissions=[permission_document_type_edit], text=_('Add filename to document type'), view='documents:document_type_filename_create', args='document_type.id') -link_document_type_filename_delete = Link(permissions=[permission_document_type_edit], tags='dangerous', text=_('Delete'), view='documents:document_type_filename_delete', args='resolved_object.id') -link_document_type_filename_edit = Link(permissions=[permission_document_type_edit], text=_('Edit'), view='documents:document_type_filename_edit', args='resolved_object.id') -link_document_type_filename_list = Link(permissions=[permission_document_type_view], text=_('Filenames'), view='documents:document_type_filename_list', args='resolved_object.id') -link_document_type_list = Link(permissions=[permission_document_type_view], text=_('Document types'), view='documents:document_type_list') -link_document_type_setup = Link(icon='fa fa-file', permissions=[permission_document_type_view], text=_('Document types'), view='documents:document_type_list') +link_document_type_create = Link( + permissions=[permission_document_type_create], + text=_('Create document type'), view='documents:document_type_create' +) +link_document_type_delete = Link( + permissions=[permission_document_type_delete], tags='dangerous', + text=_('Delete'), view='documents:document_type_delete', + args='resolved_object.id' +) +link_document_type_edit = Link( + permissions=[permission_document_type_edit], text=_('Edit'), + view='documents:document_type_edit', args='resolved_object.id' +) +link_document_type_filename_create = Link( + permissions=[permission_document_type_edit], + text=_('Add filename to document type'), + view='documents:document_type_filename_create', args='document_type.id' +) +link_document_type_filename_delete = Link( + permissions=[permission_document_type_edit], tags='dangerous', + text=_('Delete'), view='documents:document_type_filename_delete', + args='resolved_object.id' +) +link_document_type_filename_edit = Link( + permissions=[permission_document_type_edit], text=_('Edit'), + view='documents:document_type_filename_edit', args='resolved_object.id' +) +link_document_type_filename_list = Link( + permissions=[permission_document_type_view], text=_('Filenames'), + view='documents:document_type_filename_list', args='resolved_object.id' +) +link_document_type_list = Link( + permissions=[permission_document_type_view], text=_('Document types'), + view='documents:document_type_list' +) +link_document_type_setup = Link( + icon='fa fa-file', permissions=[permission_document_type_view], + text=_('Document types'), view='documents:document_type_list' +) diff --git a/mayan/apps/documents/managers.py b/mayan/apps/documents/managers.py index 97f23b8614..29a064a3b5 100644 --- a/mayan/apps/documents/managers.py +++ b/mayan/apps/documents/managers.py @@ -13,7 +13,9 @@ logger = logging.getLogger(__name__) class RecentDocumentManager(models.Manager): def add_document_for_user(self, user, document): if user.is_authenticated(): - new_recent, created = self.model.objects.get_or_create(user=user, document=document) + new_recent, created = self.model.objects.get_or_create( + user=user, document=document + ) if not created: # document already in the recent list, just save to force # accessed date and time update @@ -25,7 +27,9 @@ class RecentDocumentManager(models.Manager): document_model = apps.get_model('documents', 'document') if user.is_authenticated(): - return document_model.objects.filter(recentdocument__user=user).order_by('-recentdocument__datetime_accessed') + return document_model.objects.filter( + recentdocument__user=user + ).order_by('-recentdocument__datetime_accessed') else: return document_model.objects.none() @@ -37,7 +41,9 @@ class DocumentTypeManager(models.Manager): class DocumentManager(models.Manager): def get_queryset(self): - return TrashCanQuerySet(self.model, using=self._db).filter(in_trash=False) + return TrashCanQuerySet( + self.model, using=self._db + ).filter(in_trash=False) def invalidate_cache(self): for document in self.model.objects.all(): @@ -50,7 +56,9 @@ class PassthroughManager(models.Manager): class TrashCanManager(models.Manager): def get_queryset(self): - return super(TrashCanManager, self).get_queryset().filter(in_trash=True) + return super( + TrashCanManager, self + ).get_queryset().filter(in_trash=True) class TrashCanQuerySet(models.QuerySet): diff --git a/mayan/apps/documents/models.py b/mayan/apps/documents/models.py index f69fb11da4..13b9b6d60b 100644 --- a/mayan/apps/documents/models.py +++ b/mayan/apps/documents/models.py @@ -43,7 +43,8 @@ from .signals import ( post_document_created, post_document_type_change, post_version_upload ) -HASH_FUNCTION = lambda x: hashlib.sha256(x).hexdigest() # document image cache name hash function +# document image cache name hash function +HASH_FUNCTION = lambda x: hashlib.sha256(x).hexdigest() logger = logging.getLogger(__name__) @@ -57,11 +58,29 @@ class DocumentType(models.Model): Define document types or classes to which a specific set of properties can be attached """ - label = models.CharField(max_length=32, unique=True, verbose_name=_('Label')) - trash_time_period = models.PositiveIntegerField(blank=True, help_text=_('Amount of time after which documents of this type will be moved to the trash.'), null=True, verbose_name=_('Trash time period')) - trash_time_unit = models.CharField(blank=True, choices=TIME_DELTA_UNIT_CHOICES, null=True, max_length=8, verbose_name=_('Trash time unit')) - delete_time_period = models.PositiveIntegerField(default=DEFAULT_DELETE_PERIOD, help_text=_('Amount of time after which documents of this type in the trash will be deleted.'), verbose_name=_('Delete time period')) - delete_time_unit = models.CharField(choices=TIME_DELTA_UNIT_CHOICES, default=DEFAULT_DELETE_TIME_UNIT, max_length=8, verbose_name=_('Delete time unit')) + label = models.CharField( + max_length=32, unique=True, verbose_name=_('Label') + ) + trash_time_period = models.PositiveIntegerField( + blank=True, help_text=_( + 'Amount of time after which documents of this type will be ' + 'moved to the trash.' + ), null=True, verbose_name=_('Trash time period') + ) + trash_time_unit = models.CharField( + blank=True, choices=TIME_DELTA_UNIT_CHOICES, null=True, max_length=8, + verbose_name=_('Trash time unit') + ) + delete_time_period = models.PositiveIntegerField( + default=DEFAULT_DELETE_PERIOD, help_text=_( + 'Amount of time after which documents of this type in the trash ' + 'will be deleted.' + ), verbose_name=_('Delete time period') + ) + delete_time_unit = models.CharField( + choices=TIME_DELTA_UNIT_CHOICES, default=DEFAULT_DELETE_TIME_UNIT, + max_length=8, verbose_name=_('Delete time unit') + ) objects = DocumentTypeManager() @@ -77,13 +96,21 @@ class DocumentType(models.Model): def new_document(self, file_object, label=None, description=None, language=None, _user=None): try: with transaction.atomic(): - document = self.documents.create(description=description or '', label=label or unicode(file_object), language=language or setting_language.value) + document = self.documents.create( + description=description or '', + label=label or unicode(file_object), + language=language or setting_language.value + ) document.save(_user=_user) document.new_version(file_object=file_object, _user=_user) return document except Exception as exception: - logger.critical('Unexpected exception while trying to create new document "%s" from document type "%s"; %s', label or unicode(file_object), self, exception) + logger.critical( + 'Unexpected exception while trying to create new document ' + '"%s" from document type "%s"; %s', + label or unicode(file_object), self, exception + ) raise class Meta: @@ -98,15 +125,38 @@ class Document(models.Model): Defines a single document with it's fields and properties """ - uuid = models.CharField(default=UUID_FUNCTION, editable=False, max_length=48) - document_type = models.ForeignKey(DocumentType, related_name='documents', verbose_name=_('Document type')) - label = models.CharField(blank=True, db_index=True, default='', max_length=255, help_text=_('The name of the document'), verbose_name=_('Label')) - description = models.TextField(blank=True, default='', verbose_name=_('Description')) - date_added = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_('Added')) - language = models.CharField(blank=True, choices=setting_language_choices.value, default=setting_language.value, max_length=8, verbose_name=_('Language')) - in_trash = models.BooleanField(default=False, editable=False, verbose_name=_('In trash?')) - deleted_date_time = models.DateTimeField(blank=True, editable=True, null=True, verbose_name=_('Date and time trashed')) - is_stub = models.BooleanField(default=True, editable=False, verbose_name=_('Is stub?')) + uuid = models.CharField( + default=UUID_FUNCTION, editable=False, max_length=48 + ) + document_type = models.ForeignKey( + DocumentType, related_name='documents', + verbose_name=_('Document type') + ) + label = models.CharField( + blank=True, db_index=True, default='', max_length=255, + help_text=_('The name of the document'), verbose_name=_('Label') + ) + description = models.TextField( + blank=True, default='', verbose_name=_('Description') + ) + date_added = models.DateTimeField( + auto_now_add=True, db_index=True, verbose_name=_('Added') + ) + language = models.CharField( + blank=True, choices=setting_language_choices.value, + default=setting_language.value, max_length=8, + verbose_name=_('Language') + ) + in_trash = models.BooleanField( + default=False, editable=False, verbose_name=_('In trash?') + ) + deleted_date_time = models.DateTimeField( + blank=True, editable=True, null=True, + verbose_name=_('Date and time trashed') + ) + is_stub = models.BooleanField( + default=True, editable=False, verbose_name=_('Is stub?') + ) objects = DocumentManager() passthrough = PassthroughManager() @@ -123,7 +173,9 @@ class Document(models.Model): self.document_type = document_type self.save() if has_changed or force: - post_document_type_change.send(sender=self.__class__, instance=self) + post_document_type_change.send( + sender=self.__class__, instance=self + ) def invalidate_cache(self): for document_version in self.versions.all(): @@ -174,7 +226,9 @@ class Document(models.Model): def new_version(self, file_object, comment=None, _user=None): logger.info('Creating new document version for document: %s', self) - document_version = DocumentVersion(document=self, comment=comment or '', file=File(file_object)) + document_version = DocumentVersion( + document=self, comment=comment or '', file=File(file_object) + ) document_version.save(_user=_user) logger.info('New document version queued for document: %s', self) @@ -238,7 +292,9 @@ class Document(models.Model): # TODO: look to remove, only used by the OCR parser def document_save_to_temp_dir(self, filename, buffer_size=1024 * 1024): - temporary_path = os.path.join(setting_temporary_directory.value, filename) + temporary_path = os.path.join( + setting_temporary_directory.value, filename + ) return self.save_to_file(temporary_path, buffer_size) @@ -265,15 +321,30 @@ class DocumentVersion(models.Model): def register_post_save_hook(cls, order, func): cls._post_save_hooks[order] = func - document = models.ForeignKey(Document, related_name='versions', verbose_name=_('Document')) - timestamp = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_('Timestamp')) - comment = models.TextField(blank=True, default='', verbose_name=_('Comment')) + document = models.ForeignKey( + Document, related_name='versions', verbose_name=_('Document') + ) + timestamp = models.DateTimeField( + auto_now_add=True, db_index=True, verbose_name=_('Timestamp') + ) + comment = models.TextField( + blank=True, default='', verbose_name=_('Comment') + ) # File related fields - file = models.FileField(storage=storage_backend, upload_to=UUID_FUNCTION, verbose_name=_('File')) - mimetype = models.CharField(blank=True, editable=False, max_length=255, null=True) - encoding = models.CharField(blank=True, editable=False, max_length=64, null=True) - checksum = models.TextField(blank=True, editable=False, null=True, verbose_name=_('Checksum')) + file = models.FileField( + storage=storage_backend, upload_to=UUID_FUNCTION, + verbose_name=_('File') + ) + mimetype = models.CharField( + blank=True, editable=False, max_length=255, null=True + ) + encoding = models.CharField( + blank=True, editable=False, max_length=64, null=True + ) + checksum = models.TextField( + blank=True, editable=False, null=True, verbose_name=_('Checksum') + ) class Meta: verbose_name = _('Document version') @@ -308,7 +379,10 @@ class DocumentVersion(models.Model): self.save() self.update_page_count(save=False) - logger.info('New document version "%s" created for document: %s', self, self.document) + logger.info( + 'New document version "%s" created for document: %s', + self, self.document + ) self.document.is_stub = False if not self.document.label: @@ -316,15 +390,22 @@ class DocumentVersion(models.Model): self.document.save() except Exception as exception: - logger.error('Error creating new document version for document "%s"; %s', self.document, exception) + logger.error( + 'Error creating new document version for document "%s"; %s', + self.document, exception + ) raise else: if new_document_version: - event_document_new_version.commit(actor=user, target=self.document) + event_document_new_version.commit( + actor=user, target=self.document + ) post_version_upload.send(sender=self.__class__, instance=self) if tuple(self.document.versions.all()) == (self,): - post_document_created.send(sender=self.document.__class__, instance=self.document) + post_document_created.send( + sender=self.document.__class__, instance=self.document + ) def invalidate_cache(self): cache_storage_backend.delete(self.cache_filename) @@ -333,8 +414,8 @@ class DocumentVersion(models.Model): def update_checksum(self, save=True): """ - Open a document version's file and update the checksum field using the - user provided checksum function + Open a document version's file and update the checksum field using + the user provided checksum function """ if self.exists(): source = self.open() @@ -346,7 +427,9 @@ class DocumentVersion(models.Model): def update_page_count(self, save=True): try: with self.open() as file_object: - converter = converter_class(file_object=file_object, mime_type=self.mimetype) + converter = converter_class( + file_object=file_object, mime_type=self.mimetype + ) detected_pages = converter.get_page_count() except UnknownFileFormat: # If converter backend doesn't understand the format, @@ -372,7 +455,10 @@ class DocumentVersion(models.Model): """ Delete the subsequent versions after this one """ - logger.info('Reverting to document document: %s to version: %s', self.document, self) + logger.info( + 'Reverting to document document: %s to version: %s', + self.document, self + ) event_document_version_revert.commit(actor=user, target=self.document) @@ -381,13 +467,15 @@ class DocumentVersion(models.Model): def update_mimetype(self, save=True): """ - Read a document verions's file and determine the mimetype by calling the - get_mimetype wrapper + Read a document verions's file and determine the mimetype by calling + the get_mimetype wrapper """ if self.exists(): try: with self.open() as file_object: - self.mimetype, self.encoding = get_mimetype(file_object=file_object) + self.mimetype, self.encoding = get_mimetype( + file_object=file_object + ) except: self.mimetype = '' self.encoding = '' @@ -486,7 +574,10 @@ class DocumentVersion(models.Model): return self.open() except Exception as exception: # Cleanup in case of error - logger.error('Error creating intermediate file "%s"; %s.', cache_filename, exception) + logger.error( + 'Error creating intermediate file "%s"; %s.', + cache_filename, exception + ) cache_storage_backend.delete(cache_filename) raise @@ -497,8 +588,12 @@ class DocumentTypeFilename(models.Model): List of filenames available to a specific document type for the quick rename functionality """ - document_type = models.ForeignKey(DocumentType, related_name='filenames', verbose_name=_('Document type')) - filename = models.CharField(db_index=True, max_length=128, verbose_name=_('Filename')) + document_type = models.ForeignKey( + DocumentType, related_name='filenames', verbose_name=_('Document type') + ) + filename = models.CharField( + db_index=True, max_length=128, verbose_name=_('Filename') + ) enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) def __str__(self): @@ -516,11 +611,18 @@ class DocumentPage(models.Model): """ Model that describes a document version page """ - document_version = models.ForeignKey(DocumentVersion, related_name='pages', verbose_name=_('Document version')) - page_number = models.PositiveIntegerField(db_index=True, default=1, editable=False, verbose_name=_('Page number')) + document_version = models.ForeignKey( + DocumentVersion, related_name='pages', + verbose_name=_('Document version') + ) + page_number = models.PositiveIntegerField( + db_index=True, default=1, editable=False, verbose_name=_('Page number') + ) def __str__(self): - return _('Page %(page_num)d out of %(total_pages)d of %(document)s') % { + return _( + 'Page %(page_num)d out of %(total_pages)d of %(document)s' + ) % { 'document': unicode(self.document), 'page_num': self.page_number, 'total_pages': self.document_version.pages.count() @@ -540,7 +642,9 @@ class DocumentPage(models.Model): @property def siblings(self): - return DocumentPage.objects.filter(document_version=self.document_version) + return DocumentPage.objects.filter( + document_version=self.document_version + ) # Compatibility methods @property @@ -582,14 +686,18 @@ class DocumentPage(models.Model): if cache_storage_backend.exists(cache_filename): logger.debug('Page cache file "%s" found', cache_filename) - converter = converter_class(file_object=cache_storage_backend.open(cache_filename)) + converter = converter_class( + file_object=cache_storage_backend.open(cache_filename) + ) converter.seek(0) else: logger.debug('Page cache file "%s" not found', cache_filename) try: - converter = converter_class(file_object=self.document_version.get_intermidiate_file()) + converter = converter_class( + file_object=self.document_version.get_intermidiate_file() + ) converter.seek(page_number=self.page_number - 1) page_image = converter.get_page() @@ -598,7 +706,10 @@ class DocumentPage(models.Model): file_object.write(page_image.getvalue()) except Exception as exception: # Cleanup in case of error - logger.error('Error creating page cache file "%s"; %s', cache_filename, exception) + logger.error( + 'Error creating page cache file "%s"; %s', + cache_filename, exception + ) cache_storage_backend.delete(cache_filename) raise @@ -611,13 +722,19 @@ class DocumentPage(models.Model): converter.transform(transformation=transformation) if rotation: - converter.transform(transformation=TransformationRotate(degrees=rotation)) + converter.transform(transformation=TransformationRotate( + degrees=rotation) + ) if size: - converter.transform(transformation=TransformationResize(**dict(zip(('width', 'height'), (size.split('x')))))) + converter.transform(transformation=TransformationResize( + **dict(zip(('width', 'height'), (size.split('x'))))) + ) if zoom_level: - converter.transform(transformation=TransformationZoom(percent=zoom_level)) + converter.transform( + transformation=TransformationZoom(percent=zoom_level) + ) page_image = converter.get_page() @@ -635,8 +752,12 @@ class RecentDocument(models.Model): a given user """ user = models.ForeignKey(User, editable=False, verbose_name=_('User')) - document = models.ForeignKey(Document, editable=False, verbose_name=_('Document')) - datetime_accessed = models.DateTimeField(auto_now=True, db_index=True, verbose_name=_('Accessed')) + document = models.ForeignKey( + Document, editable=False, verbose_name=_('Document') + ) + datetime_accessed = models.DateTimeField( + auto_now=True, db_index=True, verbose_name=_('Accessed') + ) objects = RecentDocumentManager() diff --git a/mayan/apps/documents/permissions.py b/mayan/apps/documents/permissions.py index 6be6231871..9f0e42043a 100644 --- a/mayan/apps/documents/permissions.py +++ b/mayan/apps/documents/permissions.py @@ -6,24 +6,61 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('documents', _('Documents')) -permission_document_create = namespace.add_permission(name='document_create', label=_('Create documents')) -permission_document_delete = namespace.add_permission(name='document_delete', label=_('Delete documents')) -permission_document_trash = namespace.add_permission(name='document_trash', label=_('Trash documents')) -permission_document_download = namespace.add_permission(name='document_download', label=_('Download documents')) -permission_document_edit = namespace.add_permission(name='document_edit', label=_('Edit documents')) -permission_document_new_version = namespace.add_permission(name='document_new_version', label=_('Create new document versions')) -permission_document_properties_edit = namespace.add_permission(name='document_properties_edit', label=_('Edit document properties')) -permission_document_print = namespace.add_permission(name='document_print', label=_('Can print documents')) -permission_document_restore = namespace.add_permission(name='document_restore', label=_('Restore deleted document')) -permission_document_tools = namespace.add_permission(name='document_tools', label=_('Execute document modifying tools')) -permission_document_version_revert = namespace.add_permission(name='document_version_revert', label=_('Revert documents to a previous version')) -permission_document_view = namespace.add_permission(name='document_view', label=_('View documents')) +permission_document_create = namespace.add_permission( + name='document_create', label=_('Create documents') +) +permission_document_delete = namespace.add_permission( + name='document_delete', label=_('Delete documents') +) +permission_document_trash = namespace.add_permission( + name='document_trash', label=_('Trash documents') +) +permission_document_download = namespace.add_permission( + name='document_download', label=_('Download documents') +) +permission_document_edit = namespace.add_permission( + name='document_edit', label=_('Edit documents') +) +permission_document_new_version = namespace.add_permission( + name='document_new_version', label=_('Create new document versions') +) +permission_document_properties_edit = namespace.add_permission( + name='document_properties_edit', label=_('Edit document properties') +) +permission_document_print = namespace.add_permission( + name='document_print', label=_('Can print documents') +) +permission_document_restore = namespace.add_permission( + name='document_restore', label=_('Restore deleted document') +) +permission_document_tools = namespace.add_permission( + name='document_tools', label=_('Execute document modifying tools') +) +permission_document_version_revert = namespace.add_permission( + name='document_version_revert', + label=_('Revert documents to a previous version') +) +permission_document_view = namespace.add_permission( + name='document_view', label=_('View documents') +) -permission_empty_trash = namespace.add_permission(name='document_empty_trash', label=_('Empty trash')) +permission_empty_trash = namespace.add_permission( + name='document_empty_trash', label=_('Empty trash') +) -setup_namespace = PermissionNamespace('documents_setup', label=_('Documents setup')) +setup_namespace = PermissionNamespace( + 'documents_setup', label=_('Documents setup') +) -permission_document_type_create = setup_namespace.add_permission(name='document_type_create', label=_('Create document types')) -permission_document_type_delete = setup_namespace.add_permission(name='document_type_delete', label=_('Delete document types')) -permission_document_type_edit = setup_namespace.add_permission(name='document_type_edit', label=_('Edit document types')) -permission_document_type_view = setup_namespace.add_permission(name='document_type_view', label=_('View document types')) +permission_document_type_create = setup_namespace.add_permission( + name='document_type_create', label=_('Create document types') +) +permission_document_type_delete = setup_namespace.add_permission( + name='document_type_delete', label=_('Delete document types') +) +permission_document_type_edit = setup_namespace.add_permission( + name='document_type_edit', label=_('Edit document types') +) +permission_document_type_view = setup_namespace.add_permission( + name='document_type_view', label=_('View document types') +) diff --git a/mayan/apps/documents/search.py b/mayan/apps/documents/search.py index 7cf55a064e..341692565a 100644 --- a/mayan/apps/documents/search.py +++ b/mayan/apps/documents/search.py @@ -6,9 +6,16 @@ from dynamic_search.classes import SearchModel from .permissions import permission_document_view -document_search = SearchModel('documents', 'Document', permission=permission_document_view, serializer_string='documents.serializers.DocumentSerializer') +document_search = SearchModel( + 'documents', 'Document', permission=permission_document_view, + serializer_string='documents.serializers.DocumentSerializer' +) -document_search.add_model_field(field='document_type__label', label=_('Document type')) -document_search.add_model_field(field='versions__mimetype', label=_('MIME type')) +document_search.add_model_field( + field='document_type__label', label=_('Document type') +) +document_search.add_model_field( + field='versions__mimetype', label=_('MIME type') +) document_search.add_model_field(field='label', label=_('Label')) document_search.add_model_field(field='description', label=_('Description')) diff --git a/mayan/apps/documents/serializers.py b/mayan/apps/documents/serializers.py index a45e1b4e06..365330c9bf 100644 --- a/mayan/apps/documents/serializers.py +++ b/mayan/apps/documents/serializers.py @@ -40,11 +40,16 @@ class DocumentSerializer(serializers.ModelSerializer): versions = DocumentVersionSerializer(many=True, read_only=True) # TODO: Deprecate, move this as an entry point of DocumentVersion's pages image = serializers.HyperlinkedIdentityField(view_name='document-image') - new_version = serializers.HyperlinkedIdentityField(view_name='document-new-version') + new_version = serializers.HyperlinkedIdentityField( + view_name='document-new-version' + ) document_type = DocumentTypeSerializer() class Meta: - fields = ('id', 'label', 'image', 'new_version', 'uuid', 'document_type', 'description', 'date_added', 'versions') + fields = ( + 'id', 'label', 'image', 'new_version', 'uuid', 'document_type', + 'description', 'date_added', 'versions' + ) model = Document @@ -53,7 +58,10 @@ class NewDocumentSerializer(serializers.Serializer): document_type = serializers.IntegerField() file = serializers.FileField() label = serializers.CharField(required=False) - language = serializers.ChoiceField(blank_display_value=None, choices=setting_language_choices.value, default=setting_language.value, required=False) + language = serializers.ChoiceField( + blank_display_value=None, choices=setting_language_choices.value, + default=setting_language.value, required=False + ) class RecentDocumentSerializer(serializers.ModelSerializer): diff --git a/mayan/apps/documents/settings.py b/mayan/apps/documents/settings.py index e5bd1ce859..9680ef6cbb 100644 --- a/mayan/apps/documents/settings.py +++ b/mayan/apps/documents/settings.py @@ -7,21 +7,75 @@ from django.utils.translation import ugettext_lazy as _ from smart_settings import Namespace # TODO: Findout method to make languages names' translatable. -# YAML fails to serialize ugettext_lazy and ugettext is not allowed at this level -LANGUAGE_CHOICES = [(i.terminology, i.name) for i in list(pycountry.languages)] +# YAML fails to serialize ugettext_lazy and ugettext is not allowed at this +# level +LANGUAGE_CHOICES = [ + (i.terminology, i.name) for i in list(pycountry.languages) +] namespace = Namespace(name='documents', label=_('Documents')) -setting_storage_backend = namespace.add_setting(global_name='DOCUMENTS_STORAGE_BACKEND', default='storage.backends.filebasedstorage.FileBasedStorage') -setting_preview_size = namespace.add_setting(global_name='DOCUMENTS_PREVIEW_SIZE', default='640x480') -setting_print_size = namespace.add_setting(global_name='DOCUMENTS_PRINT_SIZE', default='3600') -setting_multipage_preview_size = namespace.add_setting(global_name='DOCUMENTS_MULTIPAGE_PREVIEW_SIZE', default='160x120') -setting_thumbnail_size = namespace.add_setting(global_name='DOCUMENTS_THUMBNAIL_SIZE', default='50x50') -setting_display_size = namespace.add_setting(global_name='DOCUMENTS_DISPLAY_SIZE', default='3600') -setting_recent_count = namespace.add_setting(global_name='DOCUMENTS_RECENT_COUNT', default=40, help_text=_('Maximum number of recent (created, edited, viewed) documents to remember per user.')) -setting_zoom_percent_step = namespace.add_setting(global_name='DOCUMENTS_ZOOM_PERCENT_STEP', default=25, help_text=_('Amount in percent zoom in or out a document page per user interaction.')) -setting_zoom_max_level = namespace.add_setting(global_name='DOCUMENTS_ZOOM_MAX_LEVEL', default=300, help_text=_('Maximum amount in percent (%) to allow user to zoom in a document page interactively.')) -setting_zoom_min_level = namespace.add_setting(global_name='DOCUMENTS_ZOOM_MIN_LEVEL', default=25, help_text=_('Minimum amount in percent (%) to allow user to zoom out a document page interactively.')) -setting_rotation_step = namespace.add_setting(global_name='DOCUMENTS_ROTATION_STEP', default=90, help_text=_('Amount in degrees to rotate a document page per user interaction.')) -setting_cache_storage_backend = namespace.add_setting(global_name='DOCUMENTS_CACHE_STORAGE_BACKEND', default='documents.storage.LocalCacheFileStorage') -setting_language = namespace.add_setting(global_name='DOCUMENTS_LANGUAGE', default='eng', help_text=_('Default documents language (in ISO639-2 format).')) -setting_language_choices = namespace.add_setting(global_name='DOCUMENTS_LANGUAGE_CHOICES', default=LANGUAGE_CHOICES, help_text=_('List of supported document languages.')) +setting_storage_backend = namespace.add_setting( + global_name='DOCUMENTS_STORAGE_BACKEND', + default='storage.backends.filebasedstorage.FileBasedStorage' +) +setting_preview_size = namespace.add_setting( + global_name='DOCUMENTS_PREVIEW_SIZE', default='640x480' +) +setting_print_size = namespace.add_setting( + global_name='DOCUMENTS_PRINT_SIZE', default='3600' +) +setting_multipage_preview_size = namespace.add_setting( + global_name='DOCUMENTS_MULTIPAGE_PREVIEW_SIZE', default='160x120' +) +setting_thumbnail_size = namespace.add_setting( + global_name='DOCUMENTS_THUMBNAIL_SIZE', default='50x50' +) +setting_display_size = namespace.add_setting( + global_name='DOCUMENTS_DISPLAY_SIZE', default='3600' +) +setting_recent_count = namespace.add_setting( + global_name='DOCUMENTS_RECENT_COUNT', default=40, + help_text=_( + 'Maximum number of recent (created, edited, viewed) documents to ' + 'remember per user.' + ) +) +setting_zoom_percent_step = namespace.add_setting( + global_name='DOCUMENTS_ZOOM_PERCENT_STEP', default=25, + help_text=_( + 'Amount in percent zoom in or out a document page per user ' + 'interaction.' + ) +) +setting_zoom_max_level = namespace.add_setting( + global_name='DOCUMENTS_ZOOM_MAX_LEVEL', default=300, + help_text=_( + 'Maximum amount in percent (%) to allow user to zoom in a document ' + 'page interactively.' + ) +) +setting_zoom_min_level = namespace.add_setting( + global_name='DOCUMENTS_ZOOM_MIN_LEVEL', default=25, + help_text=_( + 'Minimum amount in percent (%) to allow user to zoom out a document ' + 'page interactively.' + ) +) +setting_rotation_step = namespace.add_setting( + global_name='DOCUMENTS_ROTATION_STEP', default=90, + help_text=_( + 'Amount in degrees to rotate a document page per user interaction.' + ) +) +setting_cache_storage_backend = namespace.add_setting( + global_name='DOCUMENTS_CACHE_STORAGE_BACKEND', + default='documents.storage.LocalCacheFileStorage' +) +setting_language = namespace.add_setting( + global_name='DOCUMENTS_LANGUAGE', default='eng', + help_text=_('Default documents language (in ISO639-2 format).') +) +setting_language_choices = namespace.add_setting( + global_name='DOCUMENTS_LANGUAGE_CHOICES', default=LANGUAGE_CHOICES, + help_text=_('List of supported document languages.') +) diff --git a/mayan/apps/documents/signals.py b/mayan/apps/documents/signals.py index ecfe5426d4..b202749630 100644 --- a/mayan/apps/documents/signals.py +++ b/mayan/apps/documents/signals.py @@ -3,5 +3,7 @@ from __future__ import unicode_literals from django.dispatch import Signal post_version_upload = Signal(providing_args=['instance'], use_caching=True) -post_document_type_change = Signal(providing_args=['instance'], use_caching=True) +post_document_type_change = Signal( + providing_args=['instance'], use_caching=True +) post_document_created = Signal(providing_args=['instance'], use_caching=True) diff --git a/mayan/apps/documents/statistics.py b/mayan/apps/documents/statistics.py index c8697d82ec..3569ee5eab 100644 --- a/mayan/apps/documents/statistics.py +++ b/mayan/apps/documents/statistics.py @@ -14,7 +14,9 @@ def get_used_size(path, file_list): total_size = 0 for filename in file_list: try: - total_size += storage_backend.size(storage_backend.separator.join([path, filename])) + total_size += storage_backend.size( + storage_backend.separator.join([path, filename]) + ) except OSError: pass @@ -45,11 +47,19 @@ class DocumentStatistics(Statistic): results.extend([ _('Document types: %d') % DocumentType.objects.count(), ]) - document_stats = DocumentVersion.objects.annotate(page_count=Count('pages')).aggregate(Min('page_count'), Max('page_count'), Avg('page_count')) + document_stats = DocumentVersion.objects.annotate( + page_count=Count('pages') + ).aggregate(Min('page_count'), Max('page_count'), Avg('page_count')) results.extend([ - _('Minimum amount of pages per document: %d') % (document_stats['page_count__min'] or 0), - _('Maximum amount of pages per document: %d') % (document_stats['page_count__max'] or 0), - _('Average amount of pages per document: %f') % (document_stats['page_count__avg'] or 0), + _( + 'Minimum amount of pages per document: %d' + ) % (document_stats['page_count__min'] or 0), + _( + 'Maximum amount of pages per document: %d' + ) % (document_stats['page_count__max'] or 0), + _( + 'Average amount of pages per document: %f' + ) % (document_stats['page_count__avg'] or 0), ]) return results @@ -69,16 +79,25 @@ class DocumentUsageStatistics(Statistic): total_storage_documents, storage_used_space = storage_count() results.append(_('Documents in storage: %d') % total_storage_documents) - results.append(_('Space used in storage: %(base_2)s (base 2), %(base_10)s (base 10), %(bytes)d bytes') % { - 'base_2': pretty_size(storage_used_space), - 'base_10': pretty_size_10(storage_used_space), - 'bytes': storage_used_space - }) + results.append( + _( + 'Space used in storage: %(base_2)s (base 2), %(base_10)s ' + '(base 10), %(bytes)d bytes' + ) % { + 'base_2': pretty_size(storage_used_space), + 'base_10': pretty_size_10(storage_used_space), + 'bytes': storage_used_space + } + ) except NotImplementedError: pass - results.extend([ - _('Document pages in database: %d') % DocumentPage.objects.only('pk',).count(), - ]) + results.extend( + [ + _( + 'Document pages in database: %d' + ) % DocumentPage.objects.only('pk',).count(), + ] + ) return results diff --git a/mayan/apps/documents/tasks.py b/mayan/apps/documents/tasks.py index 778b21da1b..96014d8b0a 100644 --- a/mayan/apps/documents/tasks.py +++ b/mayan/apps/documents/tasks.py @@ -27,16 +27,31 @@ def task_check_delete_periods(): logger.info('Executing') for document_type in DocumentType.objects.all(): - logger.info('Checking deletion period of document type: %s', document_type) + logger.info( + 'Checking deletion period of document type: %s', document_type + ) if document_type.delete_time_period and document_type.delete_time_unit: - delta = timedelta(**{document_type.delete_time_unit: document_type.delete_time_period}) - logger.info('Document type: %s, has a deletion period delta of: %s', document_type, delta) + delta = timedelta( + **{ + document_type.delete_time_unit: document_type.delete_time_period + } + ) + logger.info( + 'Document type: %s, has a deletion period delta of: %s', + document_type, delta + ) for document in DeletedDocument.objects.filter(document_type=document_type): if now() > document.deleted_date_time + delta: - logger.info('Document "%s" with id: %d, trashed on: %s, exceded delete period', document, document.pk, document.deleted_date_time) + logger.info( + 'Document "%s" with id: %d, trashed on: %s, exceded ' + 'delete period', document, document.pk, + document.deleted_date_time + ) document.delete() else: - logger.info('Document type: %s, has a no retention delta', document_type) + logger.info( + 'Document type: %s, has a no retention delta', document_type + ) logger.info('Finshed') @@ -46,16 +61,31 @@ def task_check_trash_periods(): logger.info('Executing') for document_type in DocumentType.objects.all(): - logger.info('Checking trash period of document type: %s', document_type) + logger.info( + 'Checking trash period of document type: %s', document_type + ) if document_type.trash_time_period and document_type.trash_time_unit: - delta = timedelta(**{document_type.trash_time_unit: document_type.trash_time_period}) - logger.info('Document type: %s, has a trash period delta of: %s', document_type, delta) + delta = timedelta( + **{ + document_type.trash_time_unit: document_type.trash_time_period + } + ) + logger.info( + 'Document type: %s, has a trash period delta of: %s', + document_type, delta + ) for document in Document.objects.filter(document_type=document_type): if now() > document.date_added + delta: - logger.info('Document "%s" with id: %d, added on: %s, exceded trash period', document, document.pk, document.date_added) + logger.info( + 'Document "%s" with id: %d, added on: %s, exceded ' + 'trash period', document, document.pk, + document.date_added + ) document.delete() else: - logger.info('Document type: %s, has a no retention delta', document_type) + logger.info( + 'Document type: %s, has a no retention delta', document_type + ) logger.info('Finshed') @@ -80,7 +110,11 @@ def task_update_page_count(self, version_id): try: document_version.update_page_count() except OperationalError as exception: - logger.warning('Operational error during attempt to update page count for document version: %s; %s. Retrying.', document_version, exception) + logger.warning( + 'Operational error during attempt to update page count for ' + 'document version: %s; %s. Retrying.', document_version, + exception + ) raise self.retry(exc=exception) @@ -88,63 +122,100 @@ def task_update_page_count(self, version_id): def task_upload_new_document(self, document_type_id, shared_uploaded_file_id, description=None, label=None, language=None, user_id=None): try: document_type = DocumentType.objects.get(pk=document_type_id) - shared_file = SharedUploadedFile.objects.get(pk=shared_uploaded_file_id) + shared_file = SharedUploadedFile.objects.get( + pk=shared_uploaded_file_id + ) if user_id: user = User.objects.get(pk=user_id) else: user = None except OperationalError as exception: - logger.warning('Operational error during attempt to gather data for new document: %s; Retrying.', exception) + logger.warning( + 'Operational error during attempt to gather data for new ' + 'document: %s; Retrying.', exception + ) raise self.retry(exc=exception) try: with shared_file.open as file_object: - document_version = document_type.new_document(self, file_object=file_object, label=label, description=description, language=language, _user=user) + document_version = document_type.new_document( + self, file_object=file_object, label=label, + description=description, language=language, _user=user + ) except OperationalError as exception: - logger.warning('Operational error during attempt to gather data for new document: %s; Retrying.', exception) + logger.warning( + 'Operational error during attempt to gather data for new ' + 'document: %s; Retrying.', exception + ) raise self.retry(exc=exception) try: shared_file.delete() except OperationalError as exception: - logger.warning('Operational error while trying to delete shared file used to upload new document: %s; %s. Retrying.', document_version.document, exception) + logger.warning( + 'Operational error while trying to delete shared file used to ' + 'upload new document: %s; %s. Retrying.', + document_version.document, exception + ) @app.task(bind=True, default_retry_delay=UPLOAD_NEW_VERSION_RETRY_DELAY, ignore_result=True) def task_upload_new_version(self, document_id, shared_uploaded_file_id, user_id, comment=None): try: document = Document.objects.get(pk=document_id) - shared_file = SharedUploadedFile.objects.get(pk=shared_uploaded_file_id) + shared_file = SharedUploadedFile.objects.get( + pk=shared_uploaded_file_id + ) if user_id: user = User.objects.get(pk=user_id) else: user = None except OperationalError as exception: - logger.warning('Operational error during attempt to retrieve shared data for new document version for:%s; %s. Retrying.', document, exception) + logger.warning( + 'Operational error during attempt to retrieve shared data for ' + 'new document version for:%s; %s. Retrying.', document, exception + ) raise self.retry(exc=exception) with shared_file.open() as file_object: - document_version = DocumentVersion(document=document, comment=comment, file=file_object) + document_version = DocumentVersion( + document=document, comment=comment, file=file_object + ) try: document_version.save(_user=user) except Warning as warning: # New document version are blocked - logger.info('Warning during attempt to create new document version for document: %s; %s', document, warning) + logger.info( + 'Warning during attempt to create new document version for ' + 'document: %s; %s', document, warning + ) shared_file.delete() except OperationalError as exception: - logger.warning('Operational error during attempt to create new document version for document: %s; %s. Retrying.', document, exception) + logger.warning( + 'Operational error during attempt to create new document ' + 'version for document: %s; %s. Retrying.', document, exception + ) raise self.retry(exc=exception) except Exception as exception: # This except and else block emulate a finally: - logger.error('Unexpected error during attempt to create new document version for document: %s; %s', document, exception) + logger.error( + 'Unexpected error during attempt to create new document ' + 'version for document: %s; %s', document, exception + ) try: shared_file.delete() except OperationalError as exception: - logger.warning('Operational error during attempt to delete shared file: %s; %s.', shared_file, exception) + logger.warning( + 'Operational error during attempt to delete shared ' + 'file: %s; %s.', shared_file, exception + ) else: try: shared_file.delete() except OperationalError as exception: - logger.warning('Operational error during attempt to delete shared file: %s; %s.', shared_file, exception) + logger.warning( + 'Operational error during attempt to delete shared ' + 'file: %s; %s.', shared_file, exception + ) diff --git a/mayan/apps/documents/test_api.py b/mayan/apps/documents/test_api.py index edad44ef6b..726e221c52 100644 --- a/mayan/apps/documents/test_api.py +++ b/mayan/apps/documents/test_api.py @@ -22,13 +22,18 @@ from .test_models import ( class DocumentAPICreateDocumentTestCase(TestCase): """ - Functional test to make sure all the moving parts to create a document from - the API are working correctly + Functional test to make sure all the moving parts to create a document + from the API are working correctly """ def setUp(self): - self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD) - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.admin_user = User.objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) ocr_settings = self.document_type.ocr_settings ocr_settings.auto_ocr = False @@ -41,7 +46,12 @@ class DocumentAPICreateDocumentTestCase(TestCase): def test_uploading_a_document_using_token_auth(self): # Get the an user token token_client = APIClient() - response = token_client.post(reverse('auth_token_obtain'), {'username': TEST_ADMIN_USERNAME, 'password': TEST_ADMIN_PASSWORD}) + response = token_client.post( + reverse('auth_token_obtain'), { + 'username': TEST_ADMIN_USERNAME, + 'password': TEST_ADMIN_PASSWORD + } + ) # Be able to get authentication token self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -66,17 +76,26 @@ class DocumentAPICreateDocumentTestCase(TestCase): # Create a blank document with open(TEST_SMALL_DOCUMENT_PATH) as file_descriptor: - document_response = document_client.post(reverse('document-list'), {'document_type': self.document_type.pk, 'file': file_descriptor}) + document_response = document_client.post( + reverse('document-list'), { + 'document_type': self.document_type.pk, + 'file': file_descriptor + } + ) self.assertEqual(document_response.status_code, status.HTTP_201_CREATED) # The document was created in the DB? self.assertEqual(Document.objects.count(), 1) - new_version_url = reverse('document-new-version', args=[Document.objects.first().pk]) + new_version_url = reverse( + 'document-new-version', args=[Document.objects.first().pk] + ) with open(TEST_DOCUMENT_PATH) as file_descriptor: - response = document_client.post(new_version_url, {'file': file_descriptor}) + response = document_client.post( + new_version_url, {'file': file_descriptor} + ) # Make sure the document uploaded correctly document = Document.objects.first() @@ -86,13 +105,20 @@ class DocumentAPICreateDocumentTestCase(TestCase): self.assertEqual(document.file_mimetype, 'application/pdf') self.assertEqual(document.file_mime_encoding, 'binary') self.assertEqual(document.label, TEST_SMALL_DOCUMENT_FILENAME) - self.assertEqual(document.checksum, 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3') + self.assertEqual( + document.checksum, + 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3' + ) self.assertEqual(document.page_count, 47) # Make sure we can edit the document via the API - document_url = reverse('document-detail', args=[Document.objects.first().pk]) + document_url = reverse( + 'document-detail', args=[Document.objects.first().pk] + ) - response = document_client.post(document_url, {'description': 'edited test document'}) + response = document_client.post( + document_url, {'description': 'edited test document'} + ) # self.assertTrue(document.description, 'edited test document') diff --git a/mayan/apps/documents/test_models.py b/mayan/apps/documents/test_models.py index 53fff7806e..f5b27066b7 100644 --- a/mayan/apps/documents/test_models.py +++ b/mayan/apps/documents/test_models.py @@ -17,26 +17,47 @@ TEST_COMPRESSED_DOCUMENTS_FILENAME = 'compressed_documents.zip' TEST_SMALL_DOCUMENT_FILENAME = 'title_page.png' TEST_NON_ASCII_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png' TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png.zip' -TEST_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf') -TEST_SMALL_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_SMALL_DOCUMENT_FILENAME) -TEST_NON_ASCII_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_NON_ASCII_DOCUMENT_FILENAME) -TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME) -TEST_DEU_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_DEU_DOCUMENT_FILENAME) -TEST_COMPRESSED_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', TEST_COMPRESSED_DOCUMENTS_FILENAME) +TEST_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', 'mayan_11_1.pdf' +) +TEST_SMALL_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + TEST_SMALL_DOCUMENT_FILENAME +) +TEST_NON_ASCII_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + TEST_NON_ASCII_DOCUMENT_FILENAME +) +TEST_NON_ASCII_COMPRESSED_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME +) +TEST_DEU_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + TEST_DEU_DOCUMENT_FILENAME +) +TEST_COMPRESSED_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', + TEST_COMPRESSED_DOCUMENTS_FILENAME +) TEST_DOCUMENT_DESCRIPTION = 'test description' TEST_DOCUMENT_TYPE = 'test_document_type' class DocumentTestCase(TestCase): def setUp(self): - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) ocr_settings = self.document_type.ocr_settings ocr_settings.auto_ocr = False ocr_settings.save() with open(TEST_DOCUMENT_PATH) as file_object: - self.document = self.document_type.new_document(file_object=File(file_object), label='mayan_11_1.pdf') + self.document = self.document_type.new_document( + file_object=File(file_object), label='mayan_11_1.pdf' + ) def tearDown(self): self.document_type.delete() @@ -50,7 +71,10 @@ class DocumentTestCase(TestCase): self.assertEqual(self.document.file_mimetype, 'application/pdf') self.assertEqual(self.document.file_mime_encoding, 'binary') self.assertEqual(self.document.label, 'mayan_11_1.pdf') - self.assertEqual(self.document.checksum, 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3') + self.assertEqual( + self.document.checksum, + 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3' + ) self.assertEqual(self.document.page_count, 47) def test_version_creation(self): @@ -58,7 +82,9 @@ class DocumentTestCase(TestCase): self.document.new_version(file_object=File(file_object)) with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document.new_version(file_object=File(file_object), comment='test comment 1') + self.document.new_version( + file_object=File(file_object), comment='test comment 1' + ) self.assertEqual(self.document.versions.count(), 3) diff --git a/mayan/apps/documents/test_views.py b/mayan/apps/documents/test_views.py index e1290e1273..8e66b52a7d 100644 --- a/mayan/apps/documents/test_views.py +++ b/mayan/apps/documents/test_views.py @@ -22,20 +22,29 @@ class DocumentsViewsFunctionalTestCase(TestCase): """ def setUp(self): - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) ocr_settings = self.document_type.ocr_settings ocr_settings.auto_ocr = False ocr_settings.save() - self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD) + self.admin_user = User.objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) self.client = Client() # Login the admin user - logged_in = self.client.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD) + logged_in = self.client.login( + username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD + ) self.assertTrue(logged_in) self.assertTrue(self.admin_user.is_authenticated()) with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document = self.document_type.new_document(file_object=File(file_object), label='mayan_11_1.pdf') + self.document = self.document_type.new_document( + file_object=File(file_object), label='mayan_11_1.pdf' + ) def tearDown(self): self.document_type.delete() @@ -45,12 +54,16 @@ class DocumentsViewsFunctionalTestCase(TestCase): self.assertEqual(Document.objects.count(), 1) # Trash the document - self.client.post(reverse('documents:document_trash', args=[self.document.pk])) + self.client.post( + reverse('documents:document_trash', args=[self.document.pk]) + ) self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) # Restore the document - self.client.post(reverse('documents:document_restore', args=[self.document.pk])) + self.client.post( + reverse('documents:document_restore', args=[self.document.pk]) + ) self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 1) @@ -58,12 +71,16 @@ class DocumentsViewsFunctionalTestCase(TestCase): self.assertEqual(Document.objects.count(), 1) # Trash the document - self.client.post(reverse('documents:document_trash', args=[self.document.pk])) + self.client.post( + reverse('documents:document_trash', args=[self.document.pk]) + ) self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) # Delete the document - self.client.post(reverse('documents:document_delete', args=[self.document.pk])) + self.client.post( + reverse('documents:document_delete', args=[self.document.pk]) + ) self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 0) @@ -72,8 +89,12 @@ class DocumentsViewsFunctionalTestCase(TestCase): self.assertContains(response, 'Total: 1', status_code=200) # test document simple view - response = self.client.get(reverse('documents:document_properties', args=[self.document.pk])) - self.assertContains(response, 'roperties for document', status_code=200) + response = self.client.get( + reverse('documents:document_properties', args=[self.document.pk]) + ) + self.assertContains( + response, 'roperties for document', status_code=200 + ) def test_document_type_views(self): # Check that there are no document types @@ -81,8 +102,12 @@ class DocumentsViewsFunctionalTestCase(TestCase): self.assertContains(response, 'Total: 1', status_code=200) # Create a document type - response = self.client.post(reverse('documents:document_type_create'), {'name': 'test document type 2'}, follow=True) - #TODO: FIX self.assertContains(response, 'successfully', status_code=200) + response = self.client.post( + reverse('documents:document_type_create'), + {'name': 'test document type 2'}, follow=True + ) + #TODO: FIX + # self.assertContains(response, 'successfully', status_code=200) # Check that there are two document types response = self.client.get(reverse('documents:document_type_list')) @@ -91,18 +116,42 @@ class DocumentsViewsFunctionalTestCase(TestCase): self.assertEqual(self.document_type.label, TEST_DOCUMENT_TYPE) # Edit the document type - response = self.client.post(reverse('documents:document_type_edit', args=[self.document_type.pk]), data={'name': TEST_DOCUMENT_TYPE + 'partial'}, follow=True) - #TODO: FIX self.assertContains(response, 'Document type edited successfully', status_code=200) + response = self.client.post( + reverse( + 'documents:document_type_edit', args=[self.document_type.pk] + ), data={'name': TEST_DOCUMENT_TYPE + 'partial'}, follow=True + ) + #TODO: FIX + # self.assertContains( + # response, 'Document type edited successfully', status_code=200 + #) # Reload document type model data - #self.document_type = DocumentType.objects.get(pk=self.document_type.pk) - #TODO: FIX self.assertEqual(self.document_type.name, TEST_DOCUMENT_TYPE + 'partial') + #self.document_type = DocumentType.objects.get( + # pk=self.document_type.pk + #) + #TODO: FIX# + #self.assertEqual( + # self.document_type.name, TEST_DOCUMENT_TYPE + 'partial' + #) # Delete the document type - response = self.client.post(reverse('documents:document_type_delete', args=[self.document_type.pk]), follow=True) - #TODO: FIX self.assertContains(response, 'Document type: {0} deleted successfully'.format(self.document_type.name), status_code=200) + #response = self.client.post( + # reverse( + # 'documents:document_type_delete', args=[self.document_type.pk] + # ), follow=True + #) + #TODO: FIX# + # self.assertContains( + # response, 'Document type: {0} deleted successfully'.format( + # self.document_type.name + # ), status_code=200 + #) # Check that there are no document types - response = self.client.get(reverse('documents:document_type_list')) + #response = self.client.get(reverse('documents:document_type_list')) #TODO: FIX self.assertEqual(response.status_code, 200) - #TODO: FIX self.assertContains(response, 'ocument types (0)', status_code=200) + #TODO: FIX + #self.assertContains( + # response, 'ocument types (0)', status_code=200 + #) diff --git a/mayan/apps/documents/urls.py b/mayan/apps/documents/urls.py index 8a6c8a7661..964df5f47d 100644 --- a/mayan/apps/documents/urls.py +++ b/mayan/apps/documents/urls.py @@ -21,74 +21,250 @@ from .views import ( urlpatterns = patterns( 'documents.views', url(r'^list/$', DocumentListView.as_view(), name='document_list'), - url(r'^list/recent/$', RecentDocumentListView.as_view(), name='document_list_recent'), - url(r'^list/deleted/$', DeletedDocumentListView.as_view(), name='document_list_deleted'), + url( + r'^list/recent/$', RecentDocumentListView.as_view(), + name='document_list_recent' + ), + url( + r'^list/deleted/$', DeletedDocumentListView.as_view(), + name='document_list_deleted' + ), - url(r'^(?P\d+)/preview/$', 'document_preview', name='document_preview'), - url(r'^(?P\d+)/properties/$', 'document_properties', name='document_properties'), - url(r'^(?P\d+)/restore/$', DocumentRestoreView.as_view(), name='document_restore'), - url(r'^multiple/restore/$', DocumentManyRestoreView.as_view(), name='document_multiple_restore'), - url(r'^(?P\d+)/delete/$', DeletedDocumentDeleteView.as_view(), name='document_delete'), - url(r'^multiple/delete/$', DocumentManyDeleteView.as_view(), name='document_multiple_delete'), - url(r'^(?P\d+)/type/$', 'document_document_type_edit', name='document_document_type_edit'), - url(r'^multiple/type/$', 'document_multiple_document_type_edit', name='document_multiple_document_type_edit'), - url(r'^(?P\d+)/trash/$', 'document_trash', name='document_trash'), - url(r'^multiple/trash/$', 'document_multiple_trash', name='document_multiple_trash'), - url(r'^(?P\d+)/edit/$', 'document_edit', name='document_edit'), - url(r'^(?P\d+)/print/$', 'document_print', name='document_print'), - url(r'^(?P\d+)/reset_page_count/$', 'document_update_page_count', name='document_update_page_count'), - url(r'^multiple/reset_page_count/$', 'document_multiple_update_page_count', name='document_multiple_update_page_count'), + url( + r'^(?P\d+)/preview/$', 'document_preview', + name='document_preview' + ), + url( + r'^(?P\d+)/properties/$', 'document_properties', + name='document_properties' + ), + url( + r'^(?P\d+)/restore/$', DocumentRestoreView.as_view(), + name='document_restore' + ), + url( + r'^multiple/restore/$', DocumentManyRestoreView.as_view(), + name='document_multiple_restore' + ), + url( + r'^(?P\d+)/delete/$', DeletedDocumentDeleteView.as_view(), + name='document_delete' + ), + url( + r'^multiple/delete/$', DocumentManyDeleteView.as_view(), + name='document_multiple_delete' + ), + url( + r'^(?P\d+)/type/$', 'document_document_type_edit', + name='document_document_type_edit' + ), + url( + r'^multiple/type/$', 'document_multiple_document_type_edit', + name='document_multiple_document_type_edit' + ), + url( + r'^(?P\d+)/trash/$', 'document_trash', + name='document_trash' + ), + url( + r'^multiple/trash/$', 'document_multiple_trash', + name='document_multiple_trash' + ), + url( + r'^(?P\d+)/edit/$', 'document_edit', + name='document_edit' + ), + url( + r'^(?P\d+)/print/$', 'document_print', + name='document_print' + ), + url( + r'^(?P\d+)/reset_page_count/$', + 'document_update_page_count', name='document_update_page_count' + ), + url( + r'^multiple/reset_page_count/$', + 'document_multiple_update_page_count', + name='document_multiple_update_page_count' + ), - url(r'^(?P\d+)/display/$', 'get_document_image', {'size': setting_display_size.value}, 'document_display'), - url(r'^(?P\d+)/display/print/$', 'get_document_image', {'size': setting_print_size.value}, 'document_display_print'), + url( + r'^(?P\d+)/display/$', 'get_document_image', { + 'size': setting_display_size.value + }, 'document_display' + ), + url( + r'^(?P\d+)/display/print/$', 'get_document_image', { + 'size': setting_print_size.value + }, 'document_display_print' + ), - url(r'^(?P\d+)/download/$', 'document_download', name='document_download'), - url(r'^multiple/download/$', 'document_multiple_download', name='document_multiple_download'), - url(r'^(?P\d+)/clear_transformations/$', 'document_clear_transformations', name='document_clear_transformations'), + url( + r'^(?P\d+)/download/$', 'document_download', + name='document_download' + ), + url( + r'^multiple/download/$', 'document_multiple_download', + name='document_multiple_download' + ), + url( + r'^(?P\d+)/clear_transformations/$', + 'document_clear_transformations', + name='document_clear_transformations' + ), - url(r'^(?P\d+)/version/all/$', 'document_version_list', name='document_version_list'), - url(r'^document/version/(?P\d+)/download/$', 'document_download', name='document_version_download'), - url(r'^document/version/(?P\d+)/revert/$', 'document_version_revert', name='document_version_revert'), + url( + r'^(?P\d+)/version/all/$', 'document_version_list', + name='document_version_list' + ), + url( + r'^document/version/(?P\d+)/download/$', + 'document_download', name='document_version_download' + ), + url( + r'^document/version/(?P\d+)/revert/$', + 'document_version_revert', name='document_version_revert' + ), - url(r'^(?P\d+)/pages/all/$', DocumentPageListView.as_view(), name='document_pages'), + url( + r'^(?P\d+)/pages/all/$', DocumentPageListView.as_view(), + name='document_pages' + ), - url(r'^multiple/clear_transformations/$', 'document_multiple_clear_transformations', name='document_multiple_clear_transformations'), - url(r'^cache/clear/$', 'document_clear_image_cache', name='document_clear_image_cache'), - url(r'^trash_can/empty/$', EmptyTrashCanView.as_view(), name='trash_can_empty'), + url( + r'^multiple/clear_transformations/$', + 'document_multiple_clear_transformations', + name='document_multiple_clear_transformations' + ), + url( + r'^cache/clear/$', 'document_clear_image_cache', + name='document_clear_image_cache' + ), + url( + r'^trash_can/empty/$', EmptyTrashCanView.as_view(), + name='trash_can_empty' + ), - url(r'^page/(?P\d+)/$', 'document_page_view', name='document_page_view'), - url(r'^page/(?P\d+)/navigation/next/$', 'document_page_navigation_next', name='document_page_navigation_next'), - url(r'^page/(?P\d+)/navigation/previous/$', 'document_page_navigation_previous', name='document_page_navigation_previous'), - url(r'^page/(?P\d+)/navigation/first/$', 'document_page_navigation_first', name='document_page_navigation_first'), - url(r'^page/(?P\d+)/navigation/last/$', 'document_page_navigation_last', name='document_page_navigation_last'), - url(r'^page/(?P\d+)/zoom/in/$', 'document_page_zoom_in', name='document_page_zoom_in'), - url(r'^page/(?P\d+)/zoom/out/$', 'document_page_zoom_out', name='document_page_zoom_out'), - url(r'^page/(?P\d+)/rotate/right/$', 'document_page_rotate_right', name='document_page_rotate_right'), - url(r'^page/(?P\d+)/rotate/left/$', 'document_page_rotate_left', name='document_page_rotate_left'), - url(r'^page/(?P\d+)/reset/$', 'document_page_view_reset', name='document_page_view_reset'), + url( + r'^page/(?P\d+)/$', 'document_page_view', + name='document_page_view' + ), + url( + r'^page/(?P\d+)/navigation/next/$', + 'document_page_navigation_next', name='document_page_navigation_next' + ), + url( + r'^page/(?P\d+)/navigation/previous/$', + 'document_page_navigation_previous', + name='document_page_navigation_previous' + ), + url( + r'^page/(?P\d+)/navigation/first/$', + 'document_page_navigation_first', name='document_page_navigation_first' + ), + url( + r'^page/(?P\d+)/navigation/last/$', + 'document_page_navigation_last', name='document_page_navigation_last' + ), + url( + r'^page/(?P\d+)/zoom/in/$', + 'document_page_zoom_in', name='document_page_zoom_in' + ), + url( + r'^page/(?P\d+)/zoom/out/$', + 'document_page_zoom_out', name='document_page_zoom_out' + ), + url( + r'^page/(?P\d+)/rotate/right/$', + 'document_page_rotate_right', name='document_page_rotate_right' + ), + url( + r'^page/(?P\d+)/rotate/left/$', + 'document_page_rotate_left', name='document_page_rotate_left' + ), + url( + r'^page/(?P\d+)/reset/$', + 'document_page_view_reset', name='document_page_view_reset' + ), # Admin views - url(r'^type/list/$', DocumentTypeListView.as_view(), name='document_type_list'), - url(r'^type/create/$', DocumentTypeCreateView.as_view(), name='document_type_create'), - url(r'^type/(?P\d+)/edit/$', DocumentTypeEditView.as_view(), name='document_type_edit'), - url(r'^type/(?P\d+)/delete/$', DocumentTypeDeleteView.as_view(), name='document_type_delete'), - url(r'^type/(?P\d+)/documents/$', DocumentTypeDocumentListView.as_view(), name='document_type_document_list'), - url(r'^type/(?P\d+)/filename/list/$', 'document_type_filename_list', name='document_type_filename_list'), - url(r'^type/filename/(?P\d+)/edit/$', 'document_type_filename_edit', name='document_type_filename_edit'), - url(r'^type/filename/(?P\d+)/delete/$', 'document_type_filename_delete', name='document_type_filename_delete'), - url(r'^type/(?P\d+)/filename/create/$', 'document_type_filename_create', name='document_type_filename_create'), + url( + r'^type/list/$', DocumentTypeListView.as_view(), + name='document_type_list' + ), + url( + r'^type/create/$', DocumentTypeCreateView.as_view(), + name='document_type_create' + ), + url( + r'^type/(?P\d+)/edit/$', DocumentTypeEditView.as_view(), + name='document_type_edit' + ), + url( + r'^type/(?P\d+)/delete/$', DocumentTypeDeleteView.as_view(), + name='document_type_delete' + ), + url( + r'^type/(?P\d+)/documents/$', + DocumentTypeDocumentListView.as_view(), + name='document_type_document_list' + ), + url( + r'^type/(?P\d+)/filename/list/$', + 'document_type_filename_list', name='document_type_filename_list' + ), + url( + r'^type/filename/(?P\d+)/edit/$', + 'document_type_filename_edit', name='document_type_filename_edit' + ), + url( + r'^type/filename/(?P\d+)/delete/$', + 'document_type_filename_delete', name='document_type_filename_delete' + ), + url( + r'^type/(?P\d+)/filename/create/$', + 'document_type_filename_create', name='document_type_filename_create' + ), ) api_urls = patterns( '', url(r'^documents/$', APIDocumentListView.as_view(), name='document-list'), - url(r'^documents/recent/$', APIRecentDocumentListView.as_view(), name='document-recent-list'), - url(r'^documents/(?P[0-9]+)/$', APIDocumentView.as_view(), name='document-detail'), - url(r'^document_version/(?P[0-9]+)/$', APIDocumentVersionView.as_view(), name='documentversion-detail'), - url(r'^document_page/(?P[0-9]+)/$', APIDocumentPageView.as_view(), name='documentpage-detail'), - url(r'^documents/(?P[0-9]+)/image/$', APIDocumentImageView.as_view(), name='document-image'), - url(r'^documents/(?P[0-9]+)/new_version/$', APIDocumentVersionCreateView.as_view(), name='document-new-version'), - url(r'^documenttypes/(?P[0-9]+)/documents/$', APIDocumentTypeDocumentListView.as_view(), name='documenttype-document-list'), - url(r'^documenttypes/(?P[0-9]+)/$', APIDocumentTypeView.as_view(), name='documenttype-detail'), - url(r'^documenttypes/$', APIDocumentTypeListView.as_view(), name='documenttype-list'), + url( + r'^documents/recent/$', APIRecentDocumentListView.as_view(), + name='document-recent-list' + ), + url( + r'^documents/(?P[0-9]+)/$', APIDocumentView.as_view(), + name='document-detail' + ), + url( + r'^document_version/(?P[0-9]+)/$', + APIDocumentVersionView.as_view(), name='documentversion-detail' + ), + url( + r'^document_page/(?P[0-9]+)/$', APIDocumentPageView.as_view(), + name='documentpage-detail' + ), + url( + r'^documents/(?P[0-9]+)/image/$', APIDocumentImageView.as_view(), + name='document-image' + ), + url( + r'^documents/(?P[0-9]+)/new_version/$', + APIDocumentVersionCreateView.as_view(), name='document-new-version' + ), + url( + r'^documenttypes/(?P[0-9]+)/documents/$', + APIDocumentTypeDocumentListView.as_view(), + name='documenttype-document-list' + ), + url( + r'^documenttypes/(?P[0-9]+)/$', APIDocumentTypeView.as_view(), + name='documenttype-detail' + ), + url( + r'^documenttypes/$', APIDocumentTypeListView.as_view(), + name='documenttype-list' + ), ) diff --git a/mayan/apps/documents/utils.py b/mayan/apps/documents/utils.py index f64cec7534..e33b45e9de 100644 --- a/mayan/apps/documents/utils.py +++ b/mayan/apps/documents/utils.py @@ -2,7 +2,8 @@ from __future__ import unicode_literals def parse_range(astr): - # http://stackoverflow.com/questions/4248399/page-range-for-printing-algorithm + # http://stackoverflow.com/questions/4248399/ + # page-range-for-printing-algorithm result = set() for part in astr.split(','): x = part.split('-') diff --git a/mayan/apps/documents/views.py b/mayan/apps/documents/views.py index 6d48a85760..a34d6b4ae5 100644 --- a/mayan/apps/documents/views.py +++ b/mayan/apps/documents/views.py @@ -41,8 +41,8 @@ from .forms import ( ) from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT from .models import ( - DeletedDocument, Document, DocumentType, DocumentPage, DocumentTypeFilename, - DocumentVersion, RecentDocument + DeletedDocument, Document, DocumentType, DocumentPage, + DocumentTypeFilename, DocumentVersion, RecentDocument ) from .permissions import ( permission_document_delete, permission_document_download, @@ -94,11 +94,17 @@ class DeletedDocumentListView(DocumentListView): queryset = Document.trash.all() try: - Permission.check_permissions(self.request.user, [permission_document_view]) + Permission.check_permissions( + self.request.user, [permission_document_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, self.request.user, queryset) + AccessControlList.objects.check_access( + permission_document_view, self.request.user, queryset + ) - return DeletedDocument.objects.filter(pk__in=queryset.values_list('pk', flat=True)) + return DeletedDocument.objects.filter( + pk__in=queryset.values_list('pk', flat=True) + ) class DeletedDocumentDeleteView(ConfirmView): @@ -107,12 +113,18 @@ class DeletedDocumentDeleteView(ConfirmView): } def object_action(self, request, instance): - source_document = get_object_or_404(Document.passthrough, pk=instance.pk) + source_document = get_object_or_404( + Document.passthrough, pk=instance.pk + ) try: - Permission.check_permissions(request.user, [permission_document_delete]) + Permission.check_permissions( + request.user, [permission_document_delete] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_delete, request.user, source_document) + AccessControlList.objects.check_access( + permission_document_delete, request.user, source_document + ) instance.delete() messages.success(request, _('Document: %(document)s deleted.') % { @@ -132,16 +144,24 @@ class DocumentRestoreView(ConfirmView): } def object_action(self, request, instance): - source_document = get_object_or_404(Document.passthrough, pk=instance.pk) + source_document = get_object_or_404( + Document.passthrough, pk=instance.pk + ) try: - Permission.check_permissions(request.user, [permission_document_restore]) + Permission.check_permissions( + request.user, [permission_document_restore] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_restore, request.user, source_document) + AccessControlList.objects.check_access( + permission_document_restore, request.user, source_document + ) instance.restore() - messages.success(request, _('Document: %(document)s restored.') % { - 'document': instance} + messages.success( + request, _('Document: %(document)s restored.') % { + 'document': instance + } ) def post(self, request, *args, **kwargs): @@ -204,7 +224,9 @@ class EmptyTrashCanView(ConfirmView): 'title': _('Empty trash?') } view_permission = permission_empty_trash - action_cancel_redirect = post_action_redirect = reverse_lazy('documents:document_list_deleted') + action_cancel_redirect = post_action_redirect = reverse_lazy( + 'documents:document_list_deleted' + ) def post(self, request, *args, **kwargs): for deleted_document in DeletedDocument.objects.all(): @@ -231,27 +253,43 @@ def document_properties(request, document_id): try: Permission.check_permissions(request.user, [permission_document_view]) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, request.user, document) + AccessControlList.objects.check_access( + permission_document_view, request.user, document + ) document.add_as_recent_document_for_user(request.user) document_fields = [ {'label': _('Date added'), 'field': lambda x: x.date_added.date()}, - {'label': _('Time added'), 'field': lambda x: unicode(x.date_added.time()).split('.')[0]}, + { + 'label': _('Time added'), + 'field': lambda x: unicode(x.date_added.time()).split('.')[0] + }, {'label': _('UUID'), 'field': 'uuid'}, ] if document.latest_version: document_fields.extend([ - {'label': _('File mimetype'), 'field': lambda x: x.file_mimetype or _('None')}, - {'label': _('File encoding'), 'field': lambda x: x.file_mime_encoding or _('None')}, - {'label': _('File size'), 'field': lambda x: pretty_size(x.size) if x.size else '-'}, + { + 'label': _('File mimetype'), + 'field': lambda x: x.file_mimetype or _('None') + }, + { + 'label': _('File encoding'), + 'field': lambda x: x.file_mime_encoding or _('None') + }, + { + 'label': _('File size'), + 'field': lambda x: pretty_size(x.size) if x.size else '-' + }, {'label': _('Exists in storage'), 'field': 'exists'}, {'label': _('File path in storage'), 'field': 'file'}, {'label': _('Checksum'), 'field': 'checksum'}, {'label': _('Pages'), 'field': 'page_count'}, ]) - document_properties_form = DocumentPropertiesForm(instance=document, extra_fields=document_fields) + document_properties_form = DocumentPropertiesForm( + instance=document, extra_fields=document_fields + ) return render_to_response('appearance/generic_form.html', { 'form': document_properties_form, @@ -268,7 +306,9 @@ def document_preview(request, document_id): try: Permission.check_permissions(request.user, [permission_document_view]) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, request.user, document) + AccessControlList.objects.check_access( + permission_document_view, request.user, document + ) document.add_as_recent_document_for_user(request.user) @@ -291,15 +331,23 @@ def document_trash(request, document_id=None, document_id_list=None): documents = [get_object_or_404(Document, pk=document_id)] post_action_redirect = reverse('documents:document_list_recent') elif document_id_list: - documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] + documents = [ + get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',') + ] else: messages.error(request, _('Must provide at least one document.')) - return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) + return HttpResponseRedirect( + request.META.get( + 'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL) + ) + ) try: Permission.check_permissions(request.user, [permission_document_trash]) except PermissionDenied: - documents = AccessControlList.objects.filter_by_access(permission_document_trash, request.user, documents) + documents = AccessControlList.objects.filter_by_access( + permission_document_trash, request.user, documents + ) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) diff --git a/mayan/apps/documents/widgets.py b/mayan/apps/documents/widgets.py index 86c85fc5f4..a0d3dcbb9b 100644 --- a/mayan/apps/documents/widgets.py +++ b/mayan/apps/documents/widgets.py @@ -21,8 +21,14 @@ class DocumentPageImageWidget(forms.widgets.Widget): rotation = final_attrs.get('rotation', 0) if value: output = [] - output.append('
') - output.append(document_html_widget(value, zoom=zoom, rotation=rotation, image_class='lazy-load', nolazyload=False, size=setting_display_size.value)) + output.append( + '
' + ) + output.append(document_html_widget( + value, zoom=zoom, rotation=rotation, image_class='lazy-load', + nolazyload=False, size=setting_display_size.value) + ) output.append('
') return mark_safe(''.join(output)) else: @@ -35,7 +41,10 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget): """ def render(self, name, value, attrs=None): output = [] - output.append('') @@ -66,11 +82,16 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget): def document_thumbnail(document, **kwargs): - return document_html_widget(document.latest_version.pages.first(), click_view='documents:document_display', **kwargs) + return document_html_widget( + document.latest_version.pages.first(), + click_view='documents:document_display', **kwargs + ) def document_link(document): - return mark_safe('%s' % (document.get_absolute_url(), document)) + return mark_safe('%s' % ( + document.get_absolute_url(), document) + ) def document_html_widget(document_page, click_view=None, click_view_arguments=None, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', image_class='lazy-load', title=None, size=setting_thumbnail_size.value, nolazyload=False, post_load_class=None): @@ -95,9 +116,15 @@ def document_html_widget(document_page, click_view=None, click_view_arguments=No query_string = urlencode(query_dict) - preview_view = '%s?%s' % (reverse('document-image', args=[document.pk]), query_string) + preview_view = '%s?%s' % ( + reverse('document-image', args=[document.pk]), query_string + ) - result.append('
' % (document.pk, page if page else 1)) + result.append( + '
' % ( + document.pk, page if page else 1 + ) + ) if title: title_template = 'title="%s"' % strip_tags(title) @@ -105,17 +132,34 @@ def document_html_widget(document_page, click_view=None, click_view_arguments=No title_template = '' if click_view: - result.append(''.format( - gallery_template=gallery_template, - fancybox_class=fancybox_class, - image_data='%s?%s' % (reverse(click_view, args=click_view_arguments or [document.pk]), query_string), - title_template=title_template - )) + result.append( + ''.format( + gallery_template=gallery_template, + fancybox_class=fancybox_class, + image_data='%s?%s' % ( + reverse( + click_view, args=click_view_arguments or [document.pk] + ), query_string + ), + title_template=title_template + ) + ) if nolazyload: - result.append('%s' % (preview_view, alt_text)) + result.append( + '%s' % ( + preview_view, alt_text + ) + ) else: - result.append('%s' % (image_class, preview_view, post_load_class, static('appearance/images/loading.png'), alt_text)) + result.append( + '%s' % ( + image_class, preview_view, post_load_class, + static('appearance/images/loading.png'), alt_text + ) + ) if click_view: result.append('') diff --git a/mayan/apps/dynamic_search/apps.py b/mayan/apps/dynamic_search/apps.py index 41edab00c3..e730839bad 100644 --- a/mayan/apps/dynamic_search/apps.py +++ b/mayan/apps/dynamic_search/apps.py @@ -19,5 +19,12 @@ class DynamicSearchApp(MayanAppConfig): APIEndPoint('search', app_name='dynamic_search') - menu_facet.bind_links(links=[link_search, link_search_advanced], sources=['search:search', 'search:search_advanced', 'search:results']) - menu_sidebar.bind_links(links=[link_search_again], sources=['search:results']) + menu_facet.bind_links( + links=[link_search, link_search_advanced], + sources=[ + 'search:search', 'search:search_advanced', 'search:results' + ] + ) + menu_sidebar.bind_links( + links=[link_search_again], sources=['search:results'] + ) diff --git a/mayan/apps/dynamic_search/classes.py b/mayan/apps/dynamic_search/classes.py index 4922ed4e1e..77c6e3f112 100644 --- a/mayan/apps/dynamic_search/classes.py +++ b/mayan/apps/dynamic_search/classes.py @@ -72,13 +72,15 @@ class SearchModel(object): findterms=re.compile(r'"([^"]+)"|(\S+)').findall, normspace=re.compile(r'\s{2,}').sub): """ - Splits the query string in invidual keywords, getting rid of unecessary spaces - and grouping quoted words together. + Splits the query string in invidual keywords, getting rid of + unecessary spaces and grouping quoted words together. Example: >>> normalize_query(' some random words "with quotes " and spaces') ['some', 'random', 'words', 'with quotes', 'and', 'spaces'] """ - return [normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string)] + return [ + normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string) + ] def search(self, query_string, user, global_and_search=False): elapsed_time = 0 @@ -102,7 +104,9 @@ class SearchModel(object): search_dict[search_field.get_model()]['searches'].append( { 'field_name': [search_field.field], - 'terms': self.normalize_query(query_string.get('q', '').strip()) + 'terms': self.normalize_query( + query_string.get('q', '').strip() + ) } ) else: @@ -117,7 +121,9 @@ class SearchModel(object): search_dict[search_field.get_model()]['searches'].append( { 'field_name': [search_field.field], - 'terms': self.normalize_query(query_string[search_field.field]) + 'terms': self.normalize_query( + query_string[search_field.field] + ) } ) @@ -129,7 +135,9 @@ class SearchModel(object): for query_entry in data['searches']: # Fashion a list of queries for a field for each term - field_query_list = self.assemble_query(query_entry['terms'], query_entry['field_name']) + field_query_list = self.assemble_query( + query_entry['terms'], query_entry['field_name'] + ) logger.debug('field_query_list: %s', field_query_list) @@ -139,20 +147,26 @@ class SearchModel(object): # Get results per search field for query in field_query_list: logger.debug('query: %s', query) - term_query_result_set = set(model.objects.filter(query).values_list(data['return_value'], flat=True)) + term_query_result_set = set( + model.objects.filter(query).values_list( + data['return_value'], flat=True + ) + ) # Convert the QuerySet to a Python set and perform the # AND operation on the program and not as a query. # This operation ANDs all the field term results # belonging to a single model, making sure to only include - # results in the final field result variable if all the terms - # are found in a single field. + # results in the final field result variable if all the + # terms are found in a single field. if not field_result_set: field_result_set = term_query_result_set else: field_result_set &= term_query_result_set - logger.debug('term_query_result_set: %s', term_query_result_set) + logger.debug( + 'term_query_result_set: %s', term_query_result_set + ) logger.debug('field_result_set: %s', field_result_set) if global_and_search: @@ -165,24 +179,33 @@ class SearchModel(object): result_set = result_set | model_result_set - elapsed_time = unicode(datetime.datetime.now() - start_time).split(':')[2] + elapsed_time = unicode( + datetime.datetime.now() - start_time + ).split(':')[2] - queryset = self.model.objects.filter(pk__in=list(result_set)[:setting_limit.value]) + queryset = self.model.objects.filter( + pk__in=list(result_set)[:setting_limit.value] + ) if self.permission: try: Permission.check_permissions(user, [self.permission]) except PermissionDenied: - queryset = AccessControlList.objects.filter_by_access(self.permission, user, queryset) + queryset = AccessControlList.objects.filter_by_access( + self.permission, user, queryset + ) - RecentSearch.objects.add_query_for_user(user, query_string, len(result_set)) + RecentSearch.objects.add_query_for_user( + user, query_string, len(result_set) + ) return queryset, result_set, elapsed_time def assemble_query(self, terms, search_fields): """ Returns a query, that is a combination of Q objects. That combination - aims to search keywords within a model by testing the given search fields. + aims to search keywords within a model by testing the given search + fields. """ queries = [] for term in terms: diff --git a/mayan/apps/dynamic_search/links.py b/mayan/apps/dynamic_search/links.py index 3dc3c16d4a..8ebe2cf8ef 100644 --- a/mayan/apps/dynamic_search/links.py +++ b/mayan/apps/dynamic_search/links.py @@ -5,5 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from navigation import Link link_search = Link(text=_('Search'), view='search:search') -link_search_advanced = Link(text=_('Advanced search'), view='search:search_advanced') +link_search_advanced = Link( + text=_('Advanced search'), view='search:search_advanced' +) link_search_again = Link(text=_('Search again'), view='search:search_again') diff --git a/mayan/apps/dynamic_search/managers.py b/mayan/apps/dynamic_search/managers.py index 46c68b1468..456d53c819 100644 --- a/mayan/apps/dynamic_search/managers.py +++ b/mayan/apps/dynamic_search/managers.py @@ -9,7 +9,9 @@ from .settings import setting_recent_count class RecentSearchManager(models.Manager): def add_query_for_user(self, user, query_string, hits): - parsed_query = urlparse.parse_qs(urlencode(dict(query_string.items()))) + parsed_query = urlparse.parse_qs( + urlencode(dict(query_string.items())) + ) for key, value in parsed_query.items(): parsed_query[key] = ' '.join(value) @@ -26,7 +28,10 @@ class RecentSearchManager(models.Manager): if parsed_query and not isinstance(user, AnonymousUser): # If the URL query has at least one variable with a value - new_recent, created = self.model.objects.get_or_create(user=user, query=urlencode(parsed_query), defaults={'hits': hits}) + new_recent, created = self.model.objects.get_or_create( + user=user, query=urlencode(parsed_query), + defaults={'hits': hits} + ) if not created: new_recent.hits = hits new_recent.save() diff --git a/mayan/apps/dynamic_search/models.py b/mayan/apps/dynamic_search/models.py index a5b13810dd..ab7a68206b 100644 --- a/mayan/apps/dynamic_search/models.py +++ b/mayan/apps/dynamic_search/models.py @@ -6,7 +6,9 @@ import urlparse from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.db import models -from django.utils.encoding import python_2_unicode_compatible, smart_str, smart_unicode +from django.utils.encoding import ( + python_2_unicode_compatible, smart_str, smart_unicode +) from django.utils.translation import ugettext_lazy as _ from .managers import RecentSearchManager @@ -24,13 +26,16 @@ class RecentSearch(models.Model): # TODO: Fix after upgrade to DRF v2.4.4 query = models.TextField(editable=False, verbose_name=_('Query')) - datetime_created = models.DateTimeField(auto_now=True, db_index=True, verbose_name=_('Datetime created')) + datetime_created = models.DateTimeField( + auto_now=True, db_index=True, verbose_name=_('Datetime created') + ) hits = models.IntegerField(editable=False, verbose_name=_('Hits')) objects = RecentSearchManager() def __str__(self): - # TODO: Fix this hack, store the search model name in the recent search entry + # TODO: Fix this hack, store the search model name in the recent + # search entry from .classes import SearchModel document_search = SearchModel.get('documents.Document') @@ -41,7 +46,11 @@ class RecentSearch(models.Model): advanced_string = [] for key, value in query_dict.items(): search_field = document_search.get_search_field(key) - advanced_string.append('%s: %s' % (search_field.label, smart_unicode(' '.join(value)))) + advanced_string.append( + '%s: %s' % ( + search_field.label, smart_unicode(' '.join(value)) + ) + ) display_string = ', '.join(advanced_string) else: diff --git a/mayan/apps/dynamic_search/settings.py b/mayan/apps/dynamic_search/settings.py index 1fff2aa292..0d19b2a244 100644 --- a/mayan/apps/dynamic_search/settings.py +++ b/mayan/apps/dynamic_search/settings.py @@ -6,6 +6,14 @@ from smart_settings import Namespace namespace = Namespace(name='dynamic_search', label=_('Search')) -setting_show_object_type = namespace.add_setting(global_name='SEARCH_SHOW_OBJECT_TYPE', default=False) -setting_limit = namespace.add_setting(global_name='SEARCH_LIMIT', default=100, help_text=_('Maximum amount search hits to fetch and display.')) -setting_recent_count = namespace.add_setting(global_name='SEARCH_RECENT_COUNT', default=5, help_text=_('Maximum number of search queries to remember per user.')) +setting_show_object_type = namespace.add_setting( + global_name='SEARCH_SHOW_OBJECT_TYPE', default=False +) +setting_limit = namespace.add_setting( + global_name='SEARCH_LIMIT', default=100, + help_text=_('Maximum amount search hits to fetch and display.') +) +setting_recent_count = namespace.add_setting( + global_name='SEARCH_RECENT_COUNT', default=5, + help_text=_('Maximum number of search queries to remember per user.') +) diff --git a/mayan/apps/dynamic_search/test_models.py b/mayan/apps/dynamic_search/test_models.py index 52f11d0eb2..7ffe963d93 100644 --- a/mayan/apps/dynamic_search/test_models.py +++ b/mayan/apps/dynamic_search/test_models.py @@ -14,11 +14,18 @@ from documents.test_models import ( class DocumentSearchTestCase(TestCase): def setUp(self): - self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD) - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.admin_user = User.objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document = self.document_type.new_document(file_object=File(file_object), label='mayan_11_1.pdf') + self.document = self.document_type.new_document( + file_object=File(file_object), label='mayan_11_1.pdf' + ) def tearDown(self): self.document.delete() @@ -30,17 +37,24 @@ class DocumentSearchTestCase(TestCase): document versions and document version pages """ - model_list, result_set, elapsed_time = document_search.search({'q': 'Mayan'}, user=self.admin_user) + model_list, result_set, elapsed_time = document_search.search( + {'q': 'Mayan'}, user=self.admin_user + ) self.assertEqual(len(result_set), 1) self.assertEqual(list(model_list), [self.document]) def test_advanced_search_after_related_name_change(self): # Test versions__filename - model_list, result_set, elapsed_time = document_search.search({'label': self.document.label}, user=self.admin_user) + model_list, result_set, elapsed_time = document_search.search( + {'label': self.document.label}, user=self.admin_user + ) self.assertEqual(len(result_set), 1) self.assertEqual(list(model_list), [self.document]) # Test versions__mimetype - model_list, result_set, elapsed_time = document_search.search({'versions__mimetype': self.document.file_mimetype}, user=self.admin_user) + model_list, result_set, elapsed_time = document_search.search( + {'versions__mimetype': self.document.file_mimetype}, + user=self.admin_user + ) self.assertEqual(len(result_set), 1) self.assertEqual(list(model_list), [self.document]) diff --git a/mayan/apps/dynamic_search/test_views.py b/mayan/apps/dynamic_search/test_views.py index 3a5767c864..a97e998060 100644 --- a/mayan/apps/dynamic_search/test_views.py +++ b/mayan/apps/dynamic_search/test_views.py @@ -22,16 +22,23 @@ class Issue46TestCase(TestCase): """ def setUp(self): - self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD) + self.admin_user = User.objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) self.client = Client() # Login the admin user - logged_in = self.client.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD) + logged_in = self.client.login( + username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD + ) self.assertTrue(logged_in) self.assertTrue(self.admin_user.is_authenticated()) self.document_count = 4 - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) # Upload many instances of the same test document for i in range(self.document_count): @@ -49,16 +56,28 @@ class Issue46TestCase(TestCase): def test_advanced_search_past_first_page(self): # Make sure all documents are returned by the search - model_list, result_set, elapsed_time = document_search.search({'label': 'test document'}, user=self.admin_user) + model_list, result_set, elapsed_time = document_search.search( + {'label': 'test document'}, user=self.admin_user + ) self.assertEqual(len(result_set), self.document_count) with self.settings(PAGINATION_DEFAULT_PAGINATION=2): reload(pagination_tags) # Funcitonal test for the first page of advanced results - response = self.client.get(reverse('search:results'), {'label': 'test'}) - self.assertContains(response, 'Total (1 - 2 out of 4) (Page 1 of 2)', status_code=200) + response = self.client.get( + reverse('search:results'), {'label': 'test'} + ) + self.assertContains( + response, 'Total (1 - 2 out of 4) (Page 1 of 2)', + status_code=200 + ) # Functional test for the second page of advanced results - response = self.client.get(reverse('search:results'), {'label': 'test', 'page': 2}) - self.assertContains(response, 'Total (3 - 4 out of 4) (Page 2 of 2)', status_code=200) + response = self.client.get( + reverse('search:results'), {'label': 'test', 'page': 2} + ) + self.assertContains( + response, 'Total (3 - 4 out of 4) (Page 2 of 2)', + status_code=200 + ) diff --git a/mayan/apps/dynamic_search/urls.py b/mayan/apps/dynamic_search/urls.py index d0a7365294..2565e396b5 100644 --- a/mayan/apps/dynamic_search/urls.py +++ b/mayan/apps/dynamic_search/urls.py @@ -16,7 +16,13 @@ urlpatterns = patterns( api_urls = patterns( '', - url(r'^recent_searches/$', APIRecentSearchListView.as_view(), name='recentsearch-list'), - url(r'^recent_searches/(?P[0-9]+)/$', APIRecentSearchView.as_view(), name='recentsearch-detail'), + url( + r'^recent_searches/$', APIRecentSearchListView.as_view(), + name='recentsearch-list' + ), + url( + r'^recent_searches/(?P[0-9]+)/$', APIRecentSearchView.as_view(), + name='recentsearch-detail' + ), url(r'^search/$', APISearchView.as_view(), name='search-view'), ) diff --git a/mayan/apps/dynamic_search/views.py b/mayan/apps/dynamic_search/views.py index c7c2232c2e..779f922917 100644 --- a/mayan/apps/dynamic_search/views.py +++ b/mayan/apps/dynamic_search/views.py @@ -44,18 +44,27 @@ def results(request, extra_context=None): if setting_show_object_type.value: context.update({ - 'extra_columns': [{'name': _('Type'), 'attribute': lambda x: x._meta.verbose_name[0].upper() + x._meta.verbose_name[1:]}] + 'extra_columns': [ + { + 'name': _('Type'), + 'attribute': lambda x: x._meta.verbose_name[0].upper() + x._meta.verbose_name[1:] + } + ] }) - return render_to_response('dynamic_search/search_results.html', context, - context_instance=RequestContext(request)) + return render_to_response( + 'dynamic_search/search_results.html', context, + context_instance=RequestContext(request) + ) def search(request, advanced=False): document_search = SearchModel.get('documents.Document') if advanced: - form = AdvancedSearchForm(data=request.GET, search_model=document_search) + form = AdvancedSearchForm( + data=request.GET, search_model=document_search + ) return render_to_response( 'appearance/generic_form.html', { @@ -87,5 +96,9 @@ def search(request, advanced=False): def search_again(request): - query = urlparse.urlparse(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))).query - return HttpResponseRedirect('%s?%s' % (reverse('search:search_advanced'), query)) + query = urlparse.urlparse( + request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)) + ).query + return HttpResponseRedirect( + '%s?%s' % (reverse('search:search_advanced'), query) + ) diff --git a/mayan/apps/events/apps.py b/mayan/apps/events/apps.py index d3d25106d0..b527aaf87b 100644 --- a/mayan/apps/events/apps.py +++ b/mayan/apps/events/apps.py @@ -21,6 +21,8 @@ class EventsApp(MayanAppConfig): SourceColumn(source=Action, label=_('Timestamp'), attribute='timestamp') SourceColumn(source=Action, label=_('Actor'), attribute='actor') - SourceColumn(source=Action, label=_('Verb'), attribute=encapsulate(lambda entry: event_type_link(entry))) + SourceColumn(source=Action, label=_('Verb'), attribute=encapsulate( + lambda entry: event_type_link(entry)) + ) menu_tools.bind_links(links=[link_events_list]) diff --git a/mayan/apps/events/classes.py b/mayan/apps/events/classes.py index c61a4f4249..02076599bf 100644 --- a/mayan/apps/events/classes.py +++ b/mayan/apps/events/classes.py @@ -28,4 +28,7 @@ class Event(object): if not self.event_type: self.event_type, created = model.objects.get_or_create(name=self.name) - action.send(actor or target, actor=actor, verb=self.name, action_object=action_object, target=target) + action.send( + actor or target, actor=actor, verb=self.name, + action_object=action_object, target=target + ) diff --git a/mayan/apps/events/links.py b/mayan/apps/events/links.py index 412ebbd947..64cb95b76b 100644 --- a/mayan/apps/events/links.py +++ b/mayan/apps/events/links.py @@ -11,10 +11,21 @@ from .permissions import permission_events_view def get_kwargs_factory(variable_name): def get_kwargs(context): content_type = ContentType.objects.get_for_model(context[variable_name]) - return {'app_label': '"{}"'.format(content_type.app_label), 'model': '"{}"'.format(content_type.model), 'object_id': '{}.pk'.format(variable_name)} + return { + 'app_label': '"{}"'.format(content_type.app_label), + 'model': '"{}"'.format(content_type.model), + 'object_id': '{}.pk'.format(variable_name) + } return get_kwargs -link_events_list = Link(icon='fa fa-list-ol', permissions=[permission_events_view], text=_('Events'), view='events:events_list') -link_events_for_object = Link(permissions=[permission_events_view], text=_('Events'), view='events:events_for_object', kwargs=get_kwargs_factory('resolved_object')) +link_events_list = Link( + icon='fa fa-list-ol', permissions=[permission_events_view], + text=_('Events'), view='events:events_list' +) +link_events_for_object = Link( + permissions=[permission_events_view], text=_('Events'), + view='events:events_for_object', + kwargs=get_kwargs_factory('resolved_object') +) diff --git a/mayan/apps/events/models.py b/mayan/apps/events/models.py index 19f187d2e2..bae47fe833 100644 --- a/mayan/apps/events/models.py +++ b/mayan/apps/events/models.py @@ -9,7 +9,9 @@ from .classes import Event @python_2_unicode_compatible class EventType(models.Model): - name = models.CharField(max_length=64, unique=True, verbose_name=_('Name')) + name = models.CharField( + max_length=64, unique=True, verbose_name=_('Name') + ) def __str__(self): return unicode(Event.get_label(self.name)) diff --git a/mayan/apps/events/permissions.py b/mayan/apps/events/permissions.py index c045fe0ac2..f0f7a01eff 100644 --- a/mayan/apps/events/permissions.py +++ b/mayan/apps/events/permissions.py @@ -5,4 +5,6 @@ from django.utils.translation import ugettext_lazy as _ from permissions import PermissionNamespace namespace = PermissionNamespace('events', _('Events')) -permission_events_view = namespace.add_permission(name='events_view', label=_('Access the events of an object')) +permission_events_view = namespace.add_permission( + name='events_view', label=_('Access the events of an object') +) diff --git a/mayan/apps/events/urls.py b/mayan/apps/events/urls.py index b45abfc062..3f87a57e58 100644 --- a/mayan/apps/events/urls.py +++ b/mayan/apps/events/urls.py @@ -7,6 +7,12 @@ from .views import EventListView, ObjectEventListView, VerbEventListView urlpatterns = patterns( 'events.views', url(r'^all/$', EventListView.as_view(), name='events_list'), - url(r'^for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', ObjectEventListView.as_view(), name='events_for_object'), - url(r'^by_verb/(?P[\w\-]+)/$', VerbEventListView.as_view(), name='events_by_verb'), + url( + r'^for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', + ObjectEventListView.as_view(), name='events_for_object' + ), + url( + r'^by_verb/(?P[\w\-]+)/$', VerbEventListView.as_view(), + name='events_by_verb' + ), ) diff --git a/mayan/apps/events/views.py b/mayan/apps/events/views.py index fd25cb1074..86574fa494 100644 --- a/mayan/apps/events/views.py +++ b/mayan/apps/events/views.py @@ -29,7 +29,9 @@ class EventListView(SingleObjectListView): 'extra_columns': [ { 'name': _('Target'), - 'attribute': encapsulate(lambda entry: event_object_link(entry)) + 'attribute': encapsulate( + lambda entry: event_object_link(entry) + ) } ], 'hide_object': True, @@ -41,19 +43,30 @@ class ObjectEventListView(EventListView): view_permissions = None def dispatch(self, request, *args, **kwargs): - self.content_type = get_object_or_404(ContentType, app_label=self.kwargs['app_label'], model=self.kwargs['model']) + self.content_type = get_object_or_404( + ContentType, app_label=self.kwargs['app_label'], + model=self.kwargs['model'] + ) try: - self.content_object = self.content_type.get_object_for_this_type(pk=self.kwargs['object_id']) + self.content_object = self.content_type.get_object_for_this_type( + pk=self.kwargs['object_id'] + ) except self.content_type.model_class().DoesNotExist: raise Http404 try: - Permission.check_permissions(request.user, permissions=(permission_events_view,)) + Permission.check_permissions( + request.user, permissions=(permission_events_view,) + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_events_view, request.user, self.content_object) + AccessControlList.objects.check_access( + permission_events_view, request.user, self.content_object + ) - return super(ObjectEventListView, self).dispatch(request, *args, **kwargs) + return super( + ObjectEventListView, self + ).dispatch(request, *args, **kwargs) def get_queryset(self): return any_stream(self.content_object) @@ -75,9 +88,13 @@ class VerbEventListView(SingleObjectListView): 'extra_columns': [ { 'name': _('Target'), - 'attribute': encapsulate(lambda entry: event_object_link(entry)) + 'attribute': encapsulate( + lambda entry: event_object_link(entry) + ) } ], 'hide_object': True, - 'title': _('Events of type: %s') % Event.get_label(self.kwargs['verb']), + 'title': _( + 'Events of type: %s' + ) % Event.get_label(self.kwargs['verb']), } diff --git a/mayan/apps/folders/api_views.py b/mayan/apps/folders/api_views.py index e3a588c069..f7a2e4026d 100644 --- a/mayan/apps/folders/api_views.py +++ b/mayan/apps/folders/api_views.py @@ -32,15 +32,21 @@ class APIFolderListView(generics.ListCreateAPIView): mayan_view_permissions = {'POST': [permission_folder_create]} def get(self, *args, **kwargs): - """Returns a list of all the folders.""" + """ + Returns a list of all the folders. + """ return super(APIFolderListView, self).get(*args, **kwargs) def post(self, *args, **kwargs): - """Create a new folder.""" + """ + Create a new folder. + """ return super(APIFolderListView, self).post(*args, **kwargs) def create(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.DATA, files=request.FILES) + serializer = self.get_serializer( + data=request.DATA, files=request.FILES + ) if serializer.is_valid(): serializer.object.user = request.user @@ -48,8 +54,10 @@ class APIFolderListView(generics.ListCreateAPIView): self.object = serializer.save(force_insert=True) self.post_save(self.object, created=True) headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, - headers=headers) + return Response( + serializer.data, status=status.HTTP_201_CREATED, + headers=headers + ) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -67,24 +75,34 @@ class APIFolderView(generics.RetrieveUpdateDestroyAPIView): } def delete(self, *args, **kwargs): - """Delete the selected folder.""" + """ + Delete the selected folder. + """ return super(APIFolderView, self).delete(*args, **kwargs) def get(self, *args, **kwargs): - """Returns the details of the selected folder.""" + """ + Returns the details of the selected folder. + """ return super(APIFolderView, self).get(*args, **kwargs) def patch(self, *args, **kwargs): - """Edit the selected folder.""" + """ + Edit the selected folder. + """ return super(APIFolderView, self).patch(*args, **kwargs) def put(self, *args, **kwargs): - """Edit the selected folder.""" + """ + Edit the selected folder. + """ return super(APIFolderView, self).put(*args, **kwargs) class APIFolderDocumentListView(generics.ListAPIView): - """Returns a list of all the documents contained in a particular folder.""" + """ + Returns a list of all the documents contained in a particular folder. + """ filter_backends = (MayanObjectPermissionsFilter,) mayan_object_permissions = {'GET': [permission_document_view]} @@ -96,15 +114,21 @@ class APIFolderDocumentListView(generics.ListAPIView): def get_queryset(self): folder = get_object_or_404(Folder, pk=self.kwargs['pk']) try: - Permission.check_permissions(self.request.user, [permission_folder_view]) + Permission.check_permissions( + self.request.user, [permission_folder_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_folder_view, self.request.user, folder) + AccessControlList.objects.check_access( + permission_folder_view, self.request.user, folder + ) return folder.documents.all() class APIDocumentFolderListView(generics.ListAPIView): - """Returns a list of all the folders to which a document belongs.""" + """ + Returns a list of all the folders to which a document belongs. + """ serializer_class = FolderSerializer @@ -114,9 +138,13 @@ class APIDocumentFolderListView(generics.ListAPIView): def get_queryset(self): document = get_object_or_404(Document, pk=self.kwargs['pk']) try: - Permission.check_permissions(self.request.user, [permission_document_view]) + Permission.check_permissions( + self.request.user, [permission_document_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, self.request.user, document) + AccessControlList.objects.check_access( + permission_document_view, self.request.user, document + ) queryset = document.folders.all() return queryset @@ -125,13 +153,19 @@ class APIDocumentFolderListView(generics.ListAPIView): class APIFolderDocumentView(views.APIView): def delete(self, request, *args, **kwargs): - """Remove a document from the selected folder.""" + """ + Remove a document from the selected folder. + """ folder = get_object_or_404(Folder, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, [permission_folder_remove_document]) + Permission.check_permissions( + request.user, [permission_folder_remove_document] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_folder_remove_document, request.user, folder) + AccessControlList.objects.check_access( + permission_folder_remove_document, request.user, folder + ) document = get_object_or_404(Document, pk=self.kwargs['document_pk']) folder.documents.remove(document) @@ -139,13 +173,19 @@ class APIFolderDocumentView(views.APIView): # TODO: move this method as post of APIFolderDocumentListView def post(self, request, *args, **kwargs): - """Add a document to the selected folder.""" + """ + Add a document to the selected folder. + """ folder = get_object_or_404(Folder, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, [permission_folder_add_document]) + Permission.check_permissions( + request.user, [permission_folder_add_document] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_folder_add_document, request.user, folder) + AccessControlList.objects.check_access( + permission_folder_add_document, request.user, folder + ) document = get_object_or_404(Document, pk=self.kwargs['document_pk']) folder.documents.add(document) diff --git a/mayan/apps/folders/apps.py b/mayan/apps/folders/apps.py index 01d5b2d34e..4414caa095 100644 --- a/mayan/apps/folders/apps.py +++ b/mayan/apps/folders/apps.py @@ -39,24 +39,48 @@ class FoldersApp(MayanAppConfig): ModelPermission.register( model=Document, permissions=( - permission_folder_add_document, permission_folder_remove_document + permission_folder_add_document, + permission_folder_remove_document ) ) ModelPermission.register( model=Folder, permissions=( - permission_acl_edit, permission_acl_view, permission_folder_delete, - permission_folder_edit, permission_folder_view + permission_acl_edit, permission_acl_view, + permission_folder_delete, permission_folder_edit, + permission_folder_view ) ) - menu_facet.bind_links(links=[link_document_folder_list], sources=[Document]) + menu_facet.bind_links( + links=[link_document_folder_list], sources=[Document] + ) menu_main.bind_links(links=[link_folder_list]) - menu_multi_item.bind_links(links=[link_folder_add_multiple_documents], sources=[Document]) - menu_multi_item.bind_links(links=[link_folder_document_multiple_remove], sources=[CombinedSource(obj=Document, view='folders:folder_view')]) - menu_object.bind_links(links=[link_folder_view, link_folder_edit, link_acl_list, link_folder_delete], sources=[Folder]) - menu_secondary.bind_links(links=[link_folder_list, link_folder_create], sources=[Folder, 'folders:folder_list', 'folders:folder_create']) - menu_sidebar.bind_links(links=[link_folder_add_document], sources=['folders:document_folder_list', 'folders:folder_add_document']) + menu_multi_item.bind_links( + links=[link_folder_add_multiple_documents], sources=[Document] + ) + menu_multi_item.bind_links( + links=[link_folder_document_multiple_remove], + sources=[CombinedSource(obj=Document, view='folders:folder_view')] + ) + menu_object.bind_links( + links=[ + link_folder_view, link_folder_edit, link_acl_list, + link_folder_delete + ], sources=[Folder] + ) + menu_secondary.bind_links( + links=[link_folder_list, link_folder_create], + sources=[Folder, 'folders:folder_list', 'folders:folder_create'] + ) + menu_sidebar.bind_links( + links=[link_folder_add_document], + sources=[ + 'folders:document_folder_list', 'folders:folder_add_document' + ] + ) - SourceColumn(source=Folder, label=_('Created'), attribute='datetime_created') + SourceColumn( + source=Folder, label=_('Created'), attribute='datetime_created' + ) SourceColumn(source=Folder, label=_('User'), attribute='user') diff --git a/mayan/apps/folders/forms.py b/mayan/apps/folders/forms.py index 9b9a3fedb3..4dfac65b43 100644 --- a/mayan/apps/folders/forms.py +++ b/mayan/apps/folders/forms.py @@ -25,7 +25,9 @@ class FolderListForm(forms.Form): try: Permission.check_permissions(user, [permission_folder_view]) except PermissionDenied: - queryset = AccessControlList.objects.filter_by_access(permission_folder_view, user, queryset) + queryset = AccessControlList.objects.filter_by_access( + permission_folder_view, user, queryset + ) self.fields['folder'] = forms.ModelChoiceField( queryset=queryset, diff --git a/mayan/apps/folders/links.py b/mayan/apps/folders/links.py index 879b9ecc43..59911bbe1c 100644 --- a/mayan/apps/folders/links.py +++ b/mayan/apps/folders/links.py @@ -11,12 +11,38 @@ from .permissions import ( permission_folder_remove_document ) -link_document_folder_list = Link(permissions=[permission_document_view], text=_('Folders'), view='folders:document_folder_list', args='object.pk') -link_folder_add_document = Link(permissions=[permission_folder_add_document], text=_('Add to a folder'), view='folders:folder_add_document', args='object.pk') -link_folder_add_multiple_documents = Link(text=_('Add to folder'), view='folders:folder_add_multiple_documents') -link_folder_create = Link(permissions=[permission_folder_create], text=_('Create folder'), view='folders:folder_create') -link_folder_delete = Link(permissions=[permission_folder_delete], tags='dangerous', text=_('Delete'), view='folders:folder_delete', args='object.pk') -link_folder_document_multiple_remove = Link(permissions=[permission_folder_remove_document], text=_('Remove from folder'), view='folders:folder_document_multiple_remove', args='object.pk') -link_folder_edit = Link(permissions=[permission_folder_edit], text=_('Edit'), view='folders:folder_edit', args='object.pk') -link_folder_list = Link(icon='fa fa-folder', text=_('Folders'), view='folders:folder_list') -link_folder_view = Link(permissions=[permission_folder_view], text=_('Documents'), view='folders:folder_view', args='object.pk') +link_document_folder_list = Link( + permissions=[permission_document_view], text=_('Folders'), + view='folders:document_folder_list', args='object.pk' +) +link_folder_add_document = Link( + permissions=[permission_folder_add_document], text=_('Add to a folder'), + view='folders:folder_add_document', args='object.pk' +) +link_folder_add_multiple_documents = Link( + text=_('Add to folder'), view='folders:folder_add_multiple_documents' +) +link_folder_create = Link( + permissions=[permission_folder_create], text=_('Create folder'), + view='folders:folder_create' +) +link_folder_delete = Link( + permissions=[permission_folder_delete], tags='dangerous', + text=_('Delete'), view='folders:folder_delete', args='object.pk' +) +link_folder_document_multiple_remove = Link( + permissions=[permission_folder_remove_document], + text=_('Remove from folder'), + view='folders:folder_document_multiple_remove', args='object.pk' +) +link_folder_edit = Link( + permissions=[permission_folder_edit], text=_('Edit'), + view='folders:folder_edit', args='object.pk' +) +link_folder_list = Link( + icon='fa fa-folder', text=_('Folders'), view='folders:folder_list' +) +link_folder_view = Link( + permissions=[permission_folder_view], text=_('Documents'), + view='folders:folder_view', args='object.pk' +) diff --git a/mayan/apps/folders/models.py b/mayan/apps/folders/models.py index d0f98663cd..a2127c573a 100644 --- a/mayan/apps/folders/models.py +++ b/mayan/apps/folders/models.py @@ -11,10 +11,16 @@ from documents.models import Document @python_2_unicode_compatible class Folder(models.Model): - label = models.CharField(db_index=True, max_length=128, verbose_name=_('Label')) + label = models.CharField( + db_index=True, max_length=128, verbose_name=_('Label') + ) user = models.ForeignKey(User, verbose_name=_('User')) - datetime_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Datetime created')) - documents = models.ManyToManyField(Document, related_name='folders', verbose_name=_('Documents')) + datetime_created = models.DateTimeField( + auto_now_add=True, verbose_name=_('Datetime created') + ) + documents = models.ManyToManyField( + Document, related_name='folders', verbose_name=_('Documents') + ) def __str__(self): return self.label diff --git a/mayan/apps/folders/permissions.py b/mayan/apps/folders/permissions.py index 36d4c57f5f..694de87367 100644 --- a/mayan/apps/folders/permissions.py +++ b/mayan/apps/folders/permissions.py @@ -6,9 +6,21 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('folders', _('Folders')) -permission_folder_create = namespace.add_permission(name='folder_create', label=_('Create new folders')) -permission_folder_edit = namespace.add_permission(name='folder_edit', label=_('Edit new folders')) -permission_folder_delete = namespace.add_permission(name='folder_delete', label=_('Delete new folders')) -permission_folder_remove_document = namespace.add_permission(name='folder_remove_document', label=_('Remove documents from folders')) -permission_folder_view = namespace.add_permission(name='folder_view', label=_('View existing folders')) -permission_folder_add_document = namespace.add_permission(name='folder_add_document', label=_('Add documents to existing folders')) +permission_folder_create = namespace.add_permission( + name='folder_create', label=_('Create new folders') +) +permission_folder_edit = namespace.add_permission( + name='folder_edit', label=_('Edit new folders') +) +permission_folder_delete = namespace.add_permission( + name='folder_delete', label=_('Delete new folders') +) +permission_folder_remove_document = namespace.add_permission( + name='folder_remove_document', label=_('Remove documents from folders') +) +permission_folder_view = namespace.add_permission( + name='folder_view', label=_('View existing folders') +) +permission_folder_add_document = namespace.add_permission( + name='folder_add_document', label=_('Add documents to existing folders') +) diff --git a/mayan/apps/folders/test_models.py b/mayan/apps/folders/test_models.py index 90525290c8..eec0ebbd0c 100644 --- a/mayan/apps/folders/test_models.py +++ b/mayan/apps/folders/test_models.py @@ -15,17 +15,26 @@ from documents.test_models import TEST_DOCUMENT_TYPE from .models import Folder -TEST_DOCUMENT_PATH = os.path.join(settings.BASE_DIR, 'contrib', 'sample_documents', 'title_page.png') +TEST_DOCUMENT_PATH = os.path.join( + settings.BASE_DIR, 'contrib', 'sample_documents', 'title_page.png' +) class FolderTestCase(TestCase): def setUp(self): - self.document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE) + self.document_type = DocumentType.objects.create( + label=TEST_DOCUMENT_TYPE + ) with open(TEST_DOCUMENT_PATH) as file_object: - self.document = self.document_type.new_document(file_object=File(file_object)) + self.document = self.document_type.new_document( + file_object=File(file_object) + ) - self.user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD) + self.user = User.objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) def tearDown(self): self.document.delete() diff --git a/mayan/apps/folders/urls.py b/mayan/apps/folders/urls.py index 0fe78b4f5e..c76d168e69 100644 --- a/mayan/apps/folders/urls.py +++ b/mayan/apps/folders/urls.py @@ -16,20 +16,47 @@ urlpatterns = patterns( url(r'^list/$', FolderListView.as_view(), name='folder_list'), url(r'^create/$', FolderCreateView.as_view(), name='folder_create'), url(r'^(?P\d+)/edit/$', FolderEditView.as_view(), name='folder_edit'), - url(r'^(?P\d+)/delete/$', 'folder_delete', name='folder_delete'), + url( + r'^(?P\d+)/delete/$', 'folder_delete', name='folder_delete' + ), url(r'^(?P\d+)/$', FolderDetailView.as_view(), name='folder_view'), - url(r'^(?P\d+)/remove/document/multiple/$', 'folder_document_multiple_remove', name='folder_document_multiple_remove'), + url( + r'^(?P\d+)/remove/document/multiple/$', + 'folder_document_multiple_remove', + name='folder_document_multiple_remove' + ), - url(r'^document/(?P\d+)/folder/add/$', 'folder_add_document', name='folder_add_document'), - url(r'^document/multiple/folder/add/$', 'folder_add_multiple_documents', name='folder_add_multiple_documents'), - url(r'^document/(?P\d+)/folder/list/$', DocumentFolderListView.as_view(), name='document_folder_list'), + url( + r'^document/(?P\d+)/folder/add/$', + 'folder_add_document', name='folder_add_document' + ), + url( + r'^document/multiple/folder/add/$', 'folder_add_multiple_documents', + name='folder_add_multiple_documents' + ), + url( + r'^document/(?P\d+)/folder/list/$', + DocumentFolderListView.as_view(), name='document_folder_list' + ), ) api_urls = patterns( '', - url(r'^folders/(?P[0-9]+)/documents/(?P[0-9]+)/$', APIFolderDocumentView.as_view(), name='folder-document'), - url(r'^folders/(?P[0-9]+)/documents/$', APIFolderDocumentListView.as_view(), name='folder-document-list'), - url(r'^folders/(?P[0-9]+)/$', APIFolderView.as_view(), name='folder-detail'), + url( + r'^folders/(?P[0-9]+)/documents/(?P[0-9]+)/$', + APIFolderDocumentView.as_view(), name='folder-document' + ), + url( + r'^folders/(?P[0-9]+)/documents/$', + APIFolderDocumentListView.as_view(), name='folder-document-list' + ), + url( + r'^folders/(?P[0-9]+)/$', APIFolderView.as_view(), + name='folder-detail' + ), url(r'^folders/$', APIFolderListView.as_view(), name='folder-list'), - url(r'^document/(?P[0-9]+)/folders/$', APIDocumentFolderListView.as_view(), name='document-folder-list'), + url( + r'^document/(?P[0-9]+)/folders/$', + APIDocumentFolderListView.as_view(), name='document-folder-list' + ), ) diff --git a/mayan/apps/folders/views.py b/mayan/apps/folders/views.py index 0f3af173ea..1d69c18a8f 100644 --- a/mayan/apps/folders/views.py +++ b/mayan/apps/folders/views.py @@ -53,7 +53,9 @@ class FolderListView(SingleObjectListView): try: Permission.check_permissions(user, [permission_document_view]) except PermissionDenied: - queryset = AccessControlList.objects.filter_by_access(permission_document_view, user, queryset) + queryset = AccessControlList.objects.filter_by_access( + permission_document_view, user, queryset + ) return queryset.count() @@ -69,7 +71,14 @@ class FolderListView(SingleObjectListView): def get_extra_context(self): return { 'extra_columns': [ - {'name': _('Documents'), 'attribute': encapsulate(lambda instance: FolderListView.get_document_count(instance=instance, user=self.request.user))}, + { + 'name': _('Documents'), + 'attribute': encapsulate( + lambda instance: FolderListView.get_document_count( + instance=instance, user=self.request.user + ) + ) + }, ], 'title': _('Folders'), 'hide_link': True, @@ -83,14 +92,21 @@ class FolderCreateView(SingleObjectCreateView): def form_valid(self, form): try: - Folder.objects.get(label=form.cleaned_data['label'], user=self.request.user) + Folder.objects.get( + label=form.cleaned_data['label'], user=self.request.user + ) except Folder.DoesNotExist: instance = form.save(commit=False) instance.user = self.request.user instance.save() return super(FolderCreateView, self).form_valid(form) else: - messages.error(self.request, _('A folder named: %s, already exists.') % form.cleaned_data['label']) + messages.error( + self.request, + _( + 'A folder named: %s, already exists.' + ) % form.cleaned_data['label'] + ) return super(FolderCreateView, self).form_invalid(form) def get_extra_context(self): @@ -105,7 +121,9 @@ def folder_delete(request, folder_id): try: Permission.check_permissions(request.user, [permission_folder_delete]) except PermissionDenied: - AccessControlList.objects.check_access(permission_folder_delete, request.user, folder) + AccessControlList.objects.check_access( + permission_folder_delete, request.user, folder + ) post_action_redirect = reverse('folders:folder_list') @@ -139,9 +157,13 @@ class FolderDetailView(DocumentListView): folder = get_object_or_404(Folder, pk=self.kwargs['pk']) try: - Permission.check_permissions(self.request.user, [permission_folder_view]) + Permission.check_permissions( + self.request.user, [permission_folder_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_folder_view, self.request.user, folder) + AccessControlList.objects.check_access( + permission_folder_view, self.request.user, folder + ) return folder @@ -216,9 +238,13 @@ class DocumentFolderListView(FolderListView): self.document = get_object_or_404(Document, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, [permission_document_view]) + Permission.check_permissions( + request.user, [permission_document_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, request.user, self.document) + AccessControlList.objects.check_access( + permission_document_view, request.user, self.document + ) return super(DocumentFolderListView, self).dispatch(request, *args, **kwargs) @@ -228,7 +254,14 @@ class DocumentFolderListView(FolderListView): def get_extra_context(self): return { 'extra_columns': [ - {'name': _('Documents'), 'attribute': encapsulate(lambda instance: FolderListView.get_document_count(instance=instance, user=self.request.user))}, + { + 'name': _('Documents'), + 'attribute': encapsulate( + lambda instance: FolderListView.get_document_count( + instance=instance, user=self.request.user + ) + ) + }, ], 'hide_link': True, 'object': self.document, diff --git a/mayan/apps/installation/apps.py b/mayan/apps/installation/apps.py index f22b320e7a..9429f92213 100644 --- a/mayan/apps/installation/apps.py +++ b/mayan/apps/installation/apps.py @@ -7,7 +7,9 @@ from common.utils import encapsulate from navigation import SourceColumn from .classes import Property, PropertyNamespace, PIPNotFound, VirtualEnv -from .links import link_menu_link, link_namespace_details, link_namespace_list +from .links import ( + link_menu_link, link_namespace_details, link_namespace_list +) class InstallationApp(MayanAppConfig): @@ -17,21 +19,35 @@ class InstallationApp(MayanAppConfig): def ready(self): super(InstallationApp, self).ready() - SourceColumn(source=PropertyNamespace, label=_('Label'), attribute='label') - SourceColumn(source=PropertyNamespace, label=_('Items'), attribute=encapsulate(lambda entry: len(entry.get_properties()))) + SourceColumn( + source=PropertyNamespace, label=_('Label'), attribute='label' + ) + SourceColumn( + source=PropertyNamespace, label=_('Items'), + attribute=encapsulate(lambda entry: len(entry.get_properties())) + ) SourceColumn(source=Property, label=_('Label'), attribute='label') SourceColumn(source=Property, label=_('Value'), attribute='value') - menu_object.bind_links(links=[link_namespace_details], sources=[PropertyNamespace]) - menu_secondary.bind_links(links=[link_namespace_list], sources=['installation:namespace_list', PropertyNamespace]) + menu_object.bind_links( + links=[link_namespace_details], sources=[PropertyNamespace] + ) + menu_secondary.bind_links( + links=[link_namespace_list], + sources=['installation:namespace_list', PropertyNamespace] + ) menu_tools.bind_links(links=[link_menu_link]) namespace = PropertyNamespace('venv', _('VirtualEnv')) try: venv = VirtualEnv() except PIPNotFound: - namespace.add_property('pip', 'pip', _('pip not found.'), report=True) + namespace.add_property( + 'pip', 'pip', _('pip not found.'), report=True + ) else: for item, version, result in venv.get_results(): - namespace.add_property(item, '%s (%s)' % (item, version), result, report=True) + namespace.add_property( + item, '%s (%s)' % (item, version), result, report=True + ) diff --git a/mayan/apps/installation/classes.py b/mayan/apps/installation/classes.py index 4206718f4d..1efdc370ef 100644 --- a/mayan/apps/installation/classes.py +++ b/mayan/apps/installation/classes.py @@ -108,7 +108,8 @@ class VirtualEnv(object): # has no version number return Dependency(string, version=None, standard=True) else: - version = version.split('#')[0].split(' ')[1] # Get rid of '#egg' and '-e' + # Get rid of '#egg' and '-e' + version = version.split('#')[0].split(' ')[1] return Dependency(package, version, standard=False) else: return Dependency(package, version, standard=True) @@ -146,9 +147,12 @@ class VirtualEnv(object): if item.version == installed_packages['%s-dev' % name.replace('-', '_')].version: status = item.version else: - status = installed_packages['%s-dev' % name.replace('-', '_')].version + status = installed_packages[ + '%s-dev' % name.replace('-', '_') + ].version except KeyError: - # Not installed package found matching with name matchin requirement + # Not installed package found matching with name matching + # requirement status = False yield name, item.version, status diff --git a/mayan/apps/installation/links.py b/mayan/apps/installation/links.py index 68657dd334..348b5d0df2 100644 --- a/mayan/apps/installation/links.py +++ b/mayan/apps/installation/links.py @@ -6,6 +6,17 @@ from navigation import Link from .permissions import permission_installation_details -link_menu_link = Link(icon='fa fa-check-square-o', permissions=[permission_installation_details], text=_('Installation details'), view='installation:namespace_list') -link_namespace_details = Link(permissions=[permission_installation_details], text=_('Details'), view='installation:namespace_details', args='object.id') -link_namespace_list = Link(permissions=[permission_installation_details], text=_('Installation property namespaces'), view='installation:namespace_list') +link_menu_link = Link( + icon='fa fa-check-square-o', + permissions=[permission_installation_details], + text=_('Installation details'), view='installation:namespace_list' +) +link_namespace_details = Link( + permissions=[permission_installation_details], text=_('Details'), + view='installation:namespace_details', args='object.id' +) +link_namespace_list = Link( + permissions=[permission_installation_details], + text=_('Installation property namespaces'), + view='installation:namespace_list' +) diff --git a/mayan/apps/installation/permissions.py b/mayan/apps/installation/permissions.py index 40650bfddd..14dcf66821 100644 --- a/mayan/apps/installation/permissions.py +++ b/mayan/apps/installation/permissions.py @@ -5,4 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from permissions import PermissionNamespace namespace = PermissionNamespace('installation', _('Installation')) -permission_installation_details = namespace.add_permission(name='installation_details', label=_('View installation environment details')) +permission_installation_details = namespace.add_permission( + name='installation_details', + label=_('View installation environment details') +) diff --git a/mayan/apps/installation/urls.py b/mayan/apps/installation/urls.py index a734970a5c..483e868d6d 100644 --- a/mayan/apps/installation/urls.py +++ b/mayan/apps/installation/urls.py @@ -5,5 +5,8 @@ from django.conf.urls import patterns, url urlpatterns = patterns( 'installation.views', url(r'^$', 'namespace_list', name='namespace_list'), - url(r'^(?P\w+)/details/$', 'namespace_details', name='namespace_details'), + url( + r'^(?P\w+)/details/$', 'namespace_details', + name='namespace_details' + ), ) diff --git a/mayan/apps/installation/views.py b/mayan/apps/installation/views.py index f04f2577e3..a430a13973 100644 --- a/mayan/apps/installation/views.py +++ b/mayan/apps/installation/views.py @@ -11,7 +11,9 @@ from .permissions import permission_installation_details def namespace_list(request): - Permission.check_permissions(request.user, [permission_installation_details]) + Permission.check_permissions( + request.user, [permission_installation_details] + ) return render_to_response('appearance/generic_list.html', { 'object_list': PropertyNamespace.get_all(), @@ -21,7 +23,9 @@ def namespace_list(request): def namespace_details(request, namespace_id): - Permission.check_permissions(request.user, [permission_installation_details]) + Permission.check_permissions( + request.user, [permission_installation_details] + ) namespace = PropertyNamespace.get(namespace_id) object_list = namespace.get_properties() diff --git a/mayan/apps/linking/apps.py b/mayan/apps/linking/apps.py index 926f9035db..c614940024 100644 --- a/mayan/apps/linking/apps.py +++ b/mayan/apps/linking/apps.py @@ -41,10 +41,41 @@ class LinkingApp(MayanAppConfig): ) ) - menu_facet.bind_links(links=[link_smart_link_instances_for_document], sources=[Document]) - menu_object.bind_links(links=[link_smart_link_condition_edit, link_smart_link_condition_delete], sources=[SmartLinkCondition]) - menu_object.bind_links(links=[link_smart_link_edit, link_smart_link_document_types, link_smart_link_condition_list, link_acl_list, link_smart_link_delete], sources=[SmartLink]) - menu_object.bind_links(links=[link_smart_link_instance_view], sources=[ResolvedSmartLink]) - menu_secondary.bind_links(links=[link_smart_link_list, link_smart_link_create], sources=[SmartLink, 'linking:smart_link_list', 'linking:smart_link_create']) + menu_facet.bind_links( + links=[link_smart_link_instances_for_document], + sources=[Document] + ) + menu_object.bind_links( + links=[ + link_smart_link_condition_edit, + link_smart_link_condition_delete + ], sources=[SmartLinkCondition] + ) + menu_object.bind_links( + links=[ + link_smart_link_edit, link_smart_link_document_types, + link_smart_link_condition_list, link_acl_list, + link_smart_link_delete + ], sources=[SmartLink] + ) + menu_object.bind_links( + links=[link_smart_link_instance_view], + sources=[ResolvedSmartLink] + ) + menu_secondary.bind_links( + links=[link_smart_link_list, link_smart_link_create], + sources=[ + SmartLink, 'linking:smart_link_list', + 'linking:smart_link_create' + ] + ) menu_setup.bind_links(links=[link_smart_link_setup]) - menu_sidebar.bind_links(links=[link_smart_link_condition_create], sources=['linking:smart_link_condition_list', 'linking:smart_link_condition_create', 'linking:smart_link_condition_edit', 'linking:smart_link_condition_delete']) + menu_sidebar.bind_links( + links=[link_smart_link_condition_create], + sources=[ + 'linking:smart_link_condition_list', + 'linking:smart_link_condition_create', + 'linking:smart_link_condition_edit', + 'linking:smart_link_condition_delete' + ] + ) diff --git a/mayan/apps/linking/forms.py b/mayan/apps/linking/forms.py index 6f719edc43..9fe71956bc 100644 --- a/mayan/apps/linking/forms.py +++ b/mayan/apps/linking/forms.py @@ -12,7 +12,14 @@ from .models import SmartLink, SmartLinkCondition class SmartLinkForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(SmartLinkForm, self).__init__(*args, **kwargs) - self.fields['dynamic_label'].help_text = ' '.join([unicode(self.fields['dynamic_label'].help_text), ModelAttribute.help_text_for(Document, type_names=['field', 'related', 'property'])]) + self.fields['dynamic_label'].help_text = ' '.join( + [ + unicode(self.fields['dynamic_label'].help_text), + ModelAttribute.help_text_for( + Document, type_names=['field', 'related', 'property'] + ) + ] + ) class Meta: fields = ('label', 'dynamic_label', 'enabled') @@ -22,8 +29,19 @@ class SmartLinkForm(forms.ModelForm): class SmartLinkConditionForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(SmartLinkConditionForm, self).__init__(*args, **kwargs) - self.fields['foreign_document_data'] = forms.ChoiceField(choices=ModelAttribute.get_choices_for(Document, type_names=['field', 'query']), label=_('Foreign document attribute')) - self.fields['expression'].help_text = ' '.join([unicode(self.fields['expression'].help_text), ModelAttribute.help_text_for(Document, type_names=['field', 'related', 'property'])]) + self.fields['foreign_document_data'] = forms.ChoiceField( + choices=ModelAttribute.get_choices_for( + Document, type_names=['field', 'query'] + ), label=_('Foreign document attribute') + ) + self.fields['expression'].help_text = ' '.join( + [ + unicode(self.fields['expression'].help_text), + ModelAttribute.help_text_for( + Document, type_names=['field', 'related', 'property'] + ) + ] + ) class Meta: model = SmartLinkCondition diff --git a/mayan/apps/linking/links.py b/mayan/apps/linking/links.py index 6747d0dc03..c742cc5557 100644 --- a/mayan/apps/linking/links.py +++ b/mayan/apps/linking/links.py @@ -10,15 +10,54 @@ from .permissions import ( permission_smart_link_edit, permission_smart_link_view ) -link_smart_link_condition_create = Link(permissions=[permission_smart_link_edit], text=_('Create condition'), view='linking:smart_link_condition_create', args='object.pk') -link_smart_link_condition_delete = Link(permissions=[permission_smart_link_edit], tags='dangerous', text=_('Delete'), view='linking:smart_link_condition_delete', args='resolved_object.pk') -link_smart_link_condition_edit = Link(permissions=[permission_smart_link_edit], text=_('Edit'), view='linking:smart_link_condition_edit', args='resolved_object.pk') -link_smart_link_condition_list = Link(permissions=[permission_smart_link_edit], text=_('Conditions'), view='linking:smart_link_condition_list', args='object.pk') -link_smart_link_create = Link(permissions=[permission_smart_link_create], text=_('Create new smart link'), view='linking:smart_link_create') -link_smart_link_delete = Link(permissions=[permission_smart_link_delete], tags='dangerous', text=_('Delete'), view='linking:smart_link_delete', args='object.pk') -link_smart_link_document_types = Link(permissions=[permission_smart_link_edit], text=_('Document types'), view='linking:smart_link_document_types', args='object.pk') -link_smart_link_edit = Link(permissions=[permission_smart_link_edit], text=_('Edit'), view='linking:smart_link_edit', args='object.pk') -link_smart_link_instance_view = Link(permissions=[permission_smart_link_view], text=_('Documents'), view='linking:smart_link_instance_view', args=['document.pk', 'object.pk']) -link_smart_link_instances_for_document = Link(permissions=[permission_document_view], text=_('Smart links'), view='linking:smart_link_instances_for_document', args='object.pk') -link_smart_link_list = Link(permissions=[permission_smart_link_create], text=_('Smart links'), view='linking:smart_link_list') -link_smart_link_setup = Link(icon='fa fa-link', permissions=[permission_smart_link_create], text=_('Smart links'), view='linking:smart_link_list') +link_smart_link_condition_create = Link( + permissions=[permission_smart_link_edit], text=_('Create condition'), + view='linking:smart_link_condition_create', args='object.pk' +) +link_smart_link_condition_delete = Link( + permissions=[permission_smart_link_edit], tags='dangerous', + text=_('Delete'), view='linking:smart_link_condition_delete', + args='resolved_object.pk' +) +link_smart_link_condition_edit = Link( + permissions=[permission_smart_link_edit], text=_('Edit'), + view='linking:smart_link_condition_edit', args='resolved_object.pk' +) +link_smart_link_condition_list = Link( + permissions=[permission_smart_link_edit], text=_('Conditions'), + view='linking:smart_link_condition_list', args='object.pk' +) +link_smart_link_create = Link( + permissions=[permission_smart_link_create], + text=_('Create new smart link'), view='linking:smart_link_create' +) +link_smart_link_delete = Link( + permissions=[permission_smart_link_delete], tags='dangerous', + text=_('Delete'), view='linking:smart_link_delete', args='object.pk' +) +link_smart_link_document_types = Link( + permissions=[permission_smart_link_edit], text=_('Document types'), + view='linking:smart_link_document_types', args='object.pk' +) +link_smart_link_edit = Link( + permissions=[permission_smart_link_edit], text=_('Edit'), + view='linking:smart_link_edit', args='object.pk' +) +link_smart_link_instance_view = Link( + permissions=[permission_smart_link_view], text=_('Documents'), + view='linking:smart_link_instance_view', args=[ + 'document.pk', 'object.pk' + ] +) +link_smart_link_instances_for_document = Link( + permissions=[permission_document_view], text=_('Smart links'), + view='linking:smart_link_instances_for_document', args='object.pk' +) +link_smart_link_list = Link( + permissions=[permission_smart_link_create], text=_('Smart links'), + view='linking:smart_link_list' +) +link_smart_link_setup = Link( + icon='fa fa-link', permissions=[permission_smart_link_create], + text=_('Smart links'), view='linking:smart_link_list' +) diff --git a/mayan/apps/linking/models.py b/mayan/apps/linking/models.py index 4d1a8b9e04..76dfd52ae2 100644 --- a/mayan/apps/linking/models.py +++ b/mayan/apps/linking/models.py @@ -15,9 +15,16 @@ from .literals import ( @python_2_unicode_compatible class SmartLink(models.Model): label = models.CharField(max_length=96, verbose_name=_('Label')) - dynamic_label = models.CharField(blank=True, max_length=96, help_text=_('This expression will be evaluated against the current selected document.'), verbose_name=_('Dynamic label')) + dynamic_label = models.CharField( + blank=True, max_length=96, help_text=_( + 'This expression will be evaluated against the current selected ' + 'document.' + ), verbose_name=_('Dynamic label') + ) enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) - document_types = models.ManyToManyField(DocumentType, verbose_name=_('Document types')) + document_types = models.ManyToManyField( + DocumentType, verbose_name=_('Document types') + ) def __str__(self): return self.label @@ -27,19 +34,30 @@ class SmartLink(models.Model): try: return eval(self.dynamic_label, {'document': document}) except Exception as exception: - return Exception(_('Error generating dynamic label; %s' % unicode(exception))) + return Exception( + _( + 'Error generating dynamic label; %s' % unicode(exception) + ) + ) else: return self.label def get_linked_document_for(self, document): if document.document_type.pk not in self.document_types.values_list('pk', flat=True): - raise Exception(_('This smart link is not allowed for the selected document\'s type.')) + raise Exception( + _( + 'This smart link is not allowed for the selected ' + 'document\'s type.' + ) + ) smart_link_query = Q() for condition in self.conditions.filter(enabled=True): condition_query = Q(**{ - '%s__%s' % (condition.foreign_document_data, condition.operator): eval(condition.expression, {'document': document}) + '%s__%s' % ( + condition.foreign_document_data, condition.operator + ): eval(condition.expression, {'document': document}) }) if condition.negated: condition_query = ~condition_query @@ -55,7 +73,9 @@ class SmartLink(models.Model): return Document.objects.none() def resolve_for(self, document): - return ResolvedSmartLink(smart_link=self, queryset=self.get_linked_document_for(document)) + return ResolvedSmartLink( + smart_link=self, queryset=self.get_linked_document_for(document) + ) class Meta: verbose_name = _('Smart link') @@ -69,16 +89,36 @@ class ResolvedSmartLink(SmartLink): @python_2_unicode_compatible class SmartLinkCondition(models.Model): - smart_link = models.ForeignKey(SmartLink, related_name='conditions', verbose_name=_('Smart link')) - inclusion = models.CharField(choices=INCLUSION_CHOICES, default=INCLUSION_AND, help_text=_('The inclusion is ignored for the first item.'), max_length=16) - foreign_document_data = models.CharField(help_text=_('This represents the metadata of all other documents.'), max_length=128, verbose_name=_('Foreign document attribute')) + smart_link = models.ForeignKey( + SmartLink, related_name='conditions', verbose_name=_('Smart link') + ) + inclusion = models.CharField( + choices=INCLUSION_CHOICES, default=INCLUSION_AND, + help_text=_('The inclusion is ignored for the first item.'), + max_length=16 + ) + foreign_document_data = models.CharField( + help_text=_('This represents the metadata of all other documents.'), + max_length=128, verbose_name=_('Foreign document attribute') + ) operator = models.CharField(choices=OPERATOR_CHOICES, max_length=16) - expression = models.TextField(help_text=_('This expression will be evaluated against the current document.'), verbose_name=_('Expression')) - negated = models.BooleanField(default=False, help_text=_('Inverts the logic of the operator.'), verbose_name=_('Negated')) + expression = models.TextField( + help_text=_( + 'This expression will be evaluated against the current document.' + ), verbose_name=_('Expression') + ) + negated = models.BooleanField( + default=False, help_text=_('Inverts the logic of the operator.'), + verbose_name=_('Negated') + ) enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) def __str__(self): - return '%s foreign %s %s %s %s' % (self.get_inclusion_display(), self.foreign_document_data, _('not') if self.negated else '', self.get_operator_display(), self.expression) + return '%s foreign %s %s %s %s' % ( + self.get_inclusion_display(), + self.foreign_document_data, _('not') if self.negated else '', + self.get_operator_display(), self.expression + ) class Meta: verbose_name = _('Link condition') diff --git a/mayan/apps/linking/permissions.py b/mayan/apps/linking/permissions.py index 15020ca0fd..6c9882dfc5 100644 --- a/mayan/apps/linking/permissions.py +++ b/mayan/apps/linking/permissions.py @@ -6,7 +6,15 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('linking', _('Smart links')) -permission_smart_link_view = namespace.add_permission(name='smart_link_view', label=_('View existing smart links')) -permission_smart_link_create = namespace.add_permission(name='smart_link_create', label=_('Create new smart links')) -permission_smart_link_delete = namespace.add_permission(name='smart_link_delete', label=_('Delete smart links')) -permission_smart_link_edit = namespace.add_permission(name='smart_link_edit', label=_('Edit smart links')) +permission_smart_link_view = namespace.add_permission( + name='smart_link_view', label=_('View existing smart links') +) +permission_smart_link_create = namespace.add_permission( + name='smart_link_create', label=_('Create new smart links') +) +permission_smart_link_delete = namespace.add_permission( + name='smart_link_delete', label=_('Delete smart links') +) +permission_smart_link_edit = namespace.add_permission( + name='smart_link_edit', label=_('Edit smart links') +) diff --git a/mayan/apps/linking/urls.py b/mayan/apps/linking/urls.py index fba66139c3..cf07ba29df 100644 --- a/mayan/apps/linking/urls.py +++ b/mayan/apps/linking/urls.py @@ -9,17 +9,47 @@ from .views import ( urlpatterns = patterns( 'linking.views', - url(r'^document/(?P\d+)/list/$', DocumentSmartLinkListView.as_view(), name='smart_link_instances_for_document'), - url(r'^document/(?P\d+)/(?P\d+)/$', ResolvedSmartLinkView.as_view(), name='smart_link_instance_view'), + url( + r'^document/(?P\d+)/list/$', DocumentSmartLinkListView.as_view(), + name='smart_link_instances_for_document' + ), + url( + r'^document/(?P\d+)/(?P\d+)/$', + ResolvedSmartLinkView.as_view(), name='smart_link_instance_view' + ), - url(r'^setup/list/$', SmartLinkListView.as_view(), name='smart_link_list'), + url( + r'^setup/list/$', SmartLinkListView.as_view(), name='smart_link_list' + ), url(r'^setup/create/$', 'smart_link_create', name='smart_link_create'), - url(r'^setup/(?P\d+)/delete/$', 'smart_link_delete', name='smart_link_delete'), - url(r'^setup/(?P\d+)/edit/$', 'smart_link_edit', name='smart_link_edit'), - url(r'^setup/(?P\d+)/document_types/$', SetupSmartLinkDocumentTypesView.as_view(), name='smart_link_document_types'), + url( + r'^setup/(?P\d+)/delete/$', 'smart_link_delete', + name='smart_link_delete' + ), + url( + r'^setup/(?P\d+)/edit/$', 'smart_link_edit', + name='smart_link_edit' + ), + url( + r'^setup/(?P\d+)/document_types/$', + SetupSmartLinkDocumentTypesView.as_view(), + name='smart_link_document_types' + ), - url(r'^setup/(?P\d+)/condition/list/$', 'smart_link_condition_list', name='smart_link_condition_list'), - url(r'^setup/(?P\d+)/condition/create/$', 'smart_link_condition_create', name='smart_link_condition_create'), - url(r'^setup/smart_link/condition/(?P\d+)/edit/$', 'smart_link_condition_edit', name='smart_link_condition_edit'), - url(r'^setup/smart_link/condition/(?P\d+)/delete/$', 'smart_link_condition_delete', name='smart_link_condition_delete'), + url( + r'^setup/(?P\d+)/condition/list/$', + 'smart_link_condition_list', name='smart_link_condition_list' + ), + url( + r'^setup/(?P\d+)/condition/create/$', + 'smart_link_condition_create', name='smart_link_condition_create' + ), + url( + r'^setup/smart_link/condition/(?P\d+)/edit/$', + 'smart_link_condition_edit', name='smart_link_condition_edit' + ), + url( + r'^setup/smart_link/condition/(?P\d+)/delete/$', + 'smart_link_condition_delete', name='smart_link_condition_delete' + ), ) diff --git a/mayan/apps/linking/views.py b/mayan/apps/linking/views.py index 169d956675..74c20994dd 100644 --- a/mayan/apps/linking/views.py +++ b/mayan/apps/linking/views.py @@ -43,19 +43,29 @@ class SetupSmartLinkDocumentTypesView(AssignRemoveView): return get_object_or_404(SmartLink, pk=self.kwargs['pk']) def left_list(self): - return AssignRemoveView.generate_choices(DocumentType.objects.exclude(pk__in=self.get_object().document_types.all())) + return AssignRemoveView.generate_choices( + DocumentType.objects.exclude( + pk__in=self.get_object().document_types.all() + ) + ) def right_list(self): - return AssignRemoveView.generate_choices(self.get_object().document_types.all()) + return AssignRemoveView.generate_choices( + self.get_object().document_types.all() + ) def remove(self, item): self.get_object().document_types.remove(item) def get_context_data(self, **kwargs): - data = super(SetupSmartLinkDocumentTypesView, self).get_context_data(**kwargs) + data = super( + SetupSmartLinkDocumentTypesView, self + ).get_context_data(**kwargs) data.update({ 'object': self.get_object(), - 'title': _('Document type for which to enable smart link: %s') % self.get_object(), + 'title': _( + 'Document type for which to enable smart link: %s' + ) % self.get_object(), }) return data @@ -63,20 +73,34 @@ class SetupSmartLinkDocumentTypesView(AssignRemoveView): class ResolvedSmartLinkView(DocumentListView): def dispatch(self, request, *args, **kwargs): - self.document = get_object_or_404(Document, pk=self.kwargs['document_pk']) - self.smart_link = get_object_or_404(SmartLink, pk=self.kwargs['smart_link_pk']) + self.document = get_object_or_404( + Document, pk=self.kwargs['document_pk'] + ) + self.smart_link = get_object_or_404( + SmartLink, pk=self.kwargs['smart_link_pk'] + ) try: - Permission.check_permissions(request.user, [permission_document_view]) + Permission.check_permissions( + request.user, [permission_document_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_document_view, request.user, self.document) + AccessControlList.objects.check_access( + permission_document_view, request.user, self.document + ) try: - Permission.check_permissions(request.user, [permission_smart_link_view]) + Permission.check_permissions( + request.user, [permission_smart_link_view] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_smart_link_view, request.user, self.smart_link) + AccessControlList.objects.check_access( + permission_smart_link_view, request.user, self.smart_link + ) - return super(ResolvedSmartLinkView, self).dispatch(request, *args, **kwargs) + return super( + ResolvedSmartLinkView, self + ).dispatch(request, *args, **kwargs) def get_document_queryset(self): try: @@ -85,7 +109,9 @@ class ResolvedSmartLinkView(DocumentListView): queryset = Document.objects.none() if self.request.user.is_staff or self.request.user.is_superuser: - messages.error(self.request, _('Smart link query error: %s' % exception)) + messages.error( + self.request, _('Smart link query error: %s' % exception) + ) return queryset @@ -93,7 +119,10 @@ class ResolvedSmartLinkView(DocumentListView): return { 'hide_links': True, 'object': self.document, - 'title': _('Documents in smart link "%(smart_link)s" as relation to "%(document)s"') % { + 'title': _( + 'Documents in smart link "%(smart_link)s" as relation to ' + '"%(document)s"' + ) % { 'document': self.document, 'smart_link': self.smart_link.get_dynamic_label(self.document), } @@ -114,7 +143,11 @@ class SmartLinkListView(SingleObjectListView): return { 'extra_columns': [ {'name': _('Dynamic label'), 'attribute': 'dynamic_label'}, - {'name': _('Enabled'), 'attribute': encapsulate(lambda instance: two_state_template(instance.enabled))}, + { + 'name': _('Enabled'), 'attribute': encapsulate( + lambda instance: two_state_template(instance.enabled) + ) + }, ], 'hide_link': True, 'title': _('Smart links'), @@ -126,20 +159,34 @@ class DocumentSmartLinkListView(SmartLinkListView): self.document = get_object_or_404(Document, pk=self.kwargs['pk']) try: - Permission.check_permissions(request.user, (permission_document_view,)) + Permission.check_permissions( + request.user, (permission_document_view,) + ) except PermissionDenied: - AccessControlList.objects.check_permissions(permission_document_view, request.user, self.document) + AccessControlList.objects.check_permissions( + permission_document_view, request.user, self.document + ) - return super(DocumentSmartLinkListView, self).dispatch(request, *args, **kwargs) + return super( + DocumentSmartLinkListView, self + ).dispatch(request, *args, **kwargs) def get_smart_link_queryset(self): - return ResolvedSmartLink.objects.filter(document_types=self.document.document_type, enabled=True) + return ResolvedSmartLink.objects.filter( + document_types=self.document.document_type, enabled=True + ) def get_extra_context(self): return { 'document': self.document, 'extra_columns': ( - {'name': _('Label'), 'attribute': encapsulate(lambda smart_link: smart_link.get_dynamic_label(self.document))}, + { + 'name': _('Label'), 'attribute': encapsulate( + lambda smart_link: smart_link.get_dynamic_label( + self.document + ) + ) + }, ), 'hide_object': True, 'hide_link': True, @@ -149,13 +196,19 @@ class DocumentSmartLinkListView(SmartLinkListView): def smart_link_create(request): - Permission.check_permissions(request.user, [permission_smart_link_create]) + Permission.check_permissions( + request.user, [permission_smart_link_create] + ) if request.method == 'POST': form = SmartLinkForm(request.POST) if form.is_valid(): document_group = form.save() - messages.success(request, _('Smart link: %s created successfully.') % document_group) + messages.success( + request, _( + 'Smart link: %s created successfully.' + ) % document_group + ) return HttpResponseRedirect(reverse('linking:smart_link_list')) else: form = SmartLinkForm() @@ -170,15 +223,23 @@ def smart_link_edit(request, smart_link_pk): smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) try: - Permission.check_permissions(request.user, [permission_smart_link_edit]) + Permission.check_permissions( + request.user, [permission_smart_link_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_smart_link_edit, request.user, smart_link) + AccessControlList.objects.check_access( + permission_smart_link_edit, request.user, smart_link + ) if request.method == 'POST': form = SmartLinkForm(request.POST, instance=smart_link) if form.is_valid(): smart_link = form.save() - messages.success(request, _('Smart link: %s edited successfully.') % smart_link) + messages.success( + request, _( + 'Smart link: %s edited successfully.' + ) % smart_link + ) return HttpResponseRedirect(reverse('linking:smart_link_list')) else: form = SmartLinkForm(instance=smart_link) @@ -194,22 +255,39 @@ def smart_link_delete(request, smart_link_pk): smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) try: - Permission.check_permissions(request.user, [permission_smart_link_delete]) + Permission.check_permissions( + request.user, [permission_smart_link_delete] + ) except PermissionDenied: - AccessControlList.objects.check_access(permission_smart_link_delete, request.user, smart_link) + AccessControlList.objects.check_access( + permission_smart_link_delete, request.user, smart_link + ) - next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) - previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) + next = request.POST.get( + 'next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) + ) + previous = request.POST.get( + 'previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) + ) if request.method == 'POST': try: smart_link.delete() - messages.success(request, _('Smart link: %s deleted successfully.') % smart_link) + messages.success( + request, _( + 'Smart link: %s deleted successfully.' + ) % smart_link + ) except Exception as exception: - messages.error(request, _('Error deleting smart link: %(smart_link)s; %(exception)s.') % { - 'smart_link': smart_link, - 'exception': exception - }) + messages.error( + request, _( + 'Error deleting smart link: %(smart_link)s; ' + '%(exception)s.' + ) % { + 'smart_link': smart_link, + 'exception': exception + } + ) return HttpResponseRedirect(next) return render_to_response('appearance/generic_confirm.html', { @@ -225,15 +303,24 @@ def smart_link_condition_list(request, smart_link_pk): smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) try: - Permission.check_permissions(request.user, [permission_smart_link_edit]) + Permission.check_permissions( + request.user, [permission_smart_link_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access([permission_smart_link_edit], request.user, smart_link) + AccessControlList.objects.check_access( + [permission_smart_link_edit], request.user, smart_link + ) return render_to_response('appearance/generic_list.html', { 'title': _('Conditions for smart link: %s') % smart_link, 'object_list': smart_link.conditions.all(), 'extra_columns': [ - {'name': _('Enabled'), 'attribute': encapsulate(lambda x: two_state_template(x.enabled))}, + { + 'name': _('Enabled'), + 'attribute': encapsulate( + lambda x: two_state_template(x.enabled) + ) + }, ], 'hide_link': True, 'object': smart_link, @@ -244,9 +331,13 @@ def smart_link_condition_create(request, smart_link_pk): smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) try: - Permission.check_permissions(request.user, [permission_smart_link_edit]) + Permission.check_permissions( + request.user, [permission_smart_link_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access([permission_smart_link_edit], request.user, smart_link) + AccessControlList.objects.check_access( + [permission_smart_link_edit], request.user, smart_link + ) if request.method == 'POST': form = SmartLinkConditionForm(data=request.POST) @@ -254,8 +345,16 @@ def smart_link_condition_create(request, smart_link_pk): new_smart_link_condition = form.save(commit=False) new_smart_link_condition.smart_link = smart_link new_smart_link_condition.save() - messages.success(request, _('Smart link condition: "%s" created successfully.') % new_smart_link_condition) - return HttpResponseRedirect(reverse('linking:smart_link_condition_list', args=[smart_link.pk])) + messages.success( + request, _( + 'Smart link condition: "%s" created successfully.' + ) % new_smart_link_condition + ) + return HttpResponseRedirect( + reverse( + 'linking:smart_link_condition_list', args=[smart_link.pk] + ) + ) else: form = SmartLinkConditionForm() @@ -267,21 +366,34 @@ def smart_link_condition_create(request, smart_link_pk): def smart_link_condition_edit(request, smart_link_condition_pk): - smart_link_condition = get_object_or_404(SmartLinkCondition, pk=smart_link_condition_pk) + smart_link_condition = get_object_or_404( + SmartLinkCondition, pk=smart_link_condition_pk + ) try: - Permission.check_permissions(request.user, [permission_smart_link_edit]) + Permission.check_permissions( + request.user, [permission_smart_link_edit] + ) except PermissionDenied: - AccessControlList.objects.check_access([permission_smart_link_edit], request.user, smart_link_condition.smart_link) + AccessControlList.objects.check_access( + [permission_smart_link_edit], request.user, + smart_link_condition.smart_link + ) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) if request.method == 'POST': - form = SmartLinkConditionForm(request.POST, instance=smart_link_condition) + form = SmartLinkConditionForm( + request.POST, instance=smart_link_condition + ) if form.is_valid(): smart_link_condition = form.save() - messages.success(request, _('Smart link condition: "%s" edited successfully.') % smart_link_condition) + messages.success( + request, _( + 'Smart link condition: "%s" edited successfully.' + ) % smart_link_condition + ) return HttpResponseRedirect(next) else: form = SmartLinkConditionForm(instance=smart_link_condition) @@ -298,12 +410,17 @@ def smart_link_condition_edit(request, smart_link_condition_pk): def smart_link_condition_delete(request, smart_link_condition_pk): - smart_link_condition = get_object_or_404(SmartLinkCondition, pk=smart_link_condition_pk) + smart_link_condition = get_object_or_404( + SmartLinkCondition, pk=smart_link_condition_pk + ) try: Permission.check_permissions(request.user, [permission_smart_link_edit]) except PermissionDenied: - AccessControlList.objects.check_access([permission_smart_link_edit], request.user, smart_link_condition.smart_link) + AccessControlList.objects.check_access( + [permission_smart_link_edit], request.user, + smart_link_condition.smart_link + ) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) @@ -311,12 +428,21 @@ def smart_link_condition_delete(request, smart_link_condition_pk): if request.method == 'POST': try: smart_link_condition.delete() - messages.success(request, _('Smart link condition: "%s" deleted successfully.') % smart_link_condition) + messages.success( + request, _( + 'Smart link condition: "%s" deleted successfully.' + ) % smart_link_condition + ) except Exception as exception: - messages.error(request, _('Error deleting smart link condition: %(smart_link_condition)s; %(exception)s.') % { - 'smart_link_condition': smart_link_condition, - 'exception': exception - }) + messages.error( + request, _( + 'Error deleting smart link condition: ' + '%(smart_link_condition)s; %(exception)s.' + ) % { + 'smart_link_condition': smart_link_condition, + 'exception': exception + } + ) return HttpResponseRedirect(next) return render_to_response('appearance/generic_confirm.html', { diff --git a/mayan/apps/lock_manager/managers.py b/mayan/apps/lock_manager/managers.py index 629ef5f760..01a7405501 100644 --- a/mayan/apps/lock_manager/managers.py +++ b/mayan/apps/lock_manager/managers.py @@ -42,7 +42,10 @@ class LockManager(models.Manager): logger.debug('unable to acquire lock: %s', name) raise LockError('Unable to acquire lock') except OperationalError as exception: - raise LockError('Operational error while trying to acquire lock: %s; %s', name, exception) + raise LockError( + 'Operational error while trying to acquire lock: %s; %s', + name, exception + ) else: logger.debug('acquired lock: %s', name) return lock diff --git a/mayan/apps/lock_manager/models.py b/mayan/apps/lock_manager/models.py index e112a1ee04..7d66c38f98 100644 --- a/mayan/apps/lock_manager/models.py +++ b/mayan/apps/lock_manager/models.py @@ -10,9 +10,15 @@ from .settings import DEFAULT_LOCK_TIMEOUT @python_2_unicode_compatible class Lock(models.Model): - creation_datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Creation datetime')) - timeout = models.IntegerField(default=DEFAULT_LOCK_TIMEOUT, verbose_name=_('Timeout')) - name = models.CharField(max_length=64, unique=True, verbose_name=_('Name')) + creation_datetime = models.DateTimeField( + auto_now_add=True, verbose_name=_('Creation datetime') + ) + timeout = models.IntegerField( + default=DEFAULT_LOCK_TIMEOUT, verbose_name=_('Timeout') + ) + name = models.CharField( + max_length=64, unique=True, verbose_name=_('Name') + ) objects = LockManager() @@ -27,7 +33,9 @@ class Lock(models.Model): def release(self): try: - lock = Lock.objects.get(name=self.name, creation_datetime=self.creation_datetime) + lock = Lock.objects.get( + name=self.name, creation_datetime=self.creation_datetime + ) except Lock.DoesNotExist: # Our lock has expired and was reassigned pass diff --git a/mayan/apps/lock_manager/settings.py b/mayan/apps/lock_manager/settings.py index bbbbd3bbcc..6a152657d3 100644 --- a/mayan/apps/lock_manager/settings.py +++ b/mayan/apps/lock_manager/settings.py @@ -4,4 +4,6 @@ from django.conf import settings DEFAULT_LOCK_TIMEOUT_VALUE = 30 -DEFAULT_LOCK_TIMEOUT = getattr(settings, 'LOCK_MANAGER_DEFAULT_LOCK_TIMEOUT', DEFAULT_LOCK_TIMEOUT_VALUE) +DEFAULT_LOCK_TIMEOUT = getattr( + settings, 'LOCK_MANAGER_DEFAULT_LOCK_TIMEOUT', DEFAULT_LOCK_TIMEOUT_VALUE +) diff --git a/mayan/apps/metadata/migrations/0001_initial.py b/mayan/apps/metadata/migrations/0001_initial.py index 2641a64828..c6afb45542 100644 --- a/mayan/apps/metadata/migrations/0001_initial.py +++ b/mayan/apps/metadata/migrations/0001_initial.py @@ -14,9 +14,24 @@ class Migration(migrations.Migration): migrations.CreateModel( name='DocumentMetadata', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('value', models.CharField(db_index=True, max_length=255, null=True, verbose_name='Value', blank=True)), - ('document', models.ForeignKey(related_name='metadata', verbose_name='Document', to='documents.Document')), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), + ( + 'value', models.CharField( + db_index=True, max_length=255, null=True, + verbose_name='Value', blank=True + ) + ), + ( + 'document', models.ForeignKey( + related_name='metadata', verbose_name='Document', + to='documents.Document' + ) + ), ], options={ 'verbose_name': 'Document metadata', @@ -27,9 +42,23 @@ class Migration(migrations.Migration): migrations.CreateModel( name='DocumentTypeMetadataType', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('required', models.BooleanField(default=False, verbose_name='Required')), - ('document_type', models.ForeignKey(related_name='metadata', verbose_name='Document type', to='documents.DocumentType')), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), + ( + 'required', models.BooleanField( + default=False, verbose_name='Required' + ) + ), + ( + 'document_type', models.ForeignKey( + related_name='metadata', + verbose_name='Document type', to='documents.DocumentType' + ) + ), ], options={ 'verbose_name': 'Document type metadata type options', @@ -40,12 +69,47 @@ class Migration(migrations.Migration): migrations.CreateModel( name='MetadataType', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(help_text='Do not use python reserved words, or spaces.', unique=True, max_length=48, verbose_name='Name')), - ('title', models.CharField(max_length=48, verbose_name='Title')), - ('default', models.CharField(help_text='Enter a string to be evaluated.', max_length=128, null=True, verbose_name='Default', blank=True)), - ('lookup', models.TextField(help_text='Enter a string to be evaluated that returns an iterable.', null=True, verbose_name='Lookup', blank=True)), - ('validation', models.CharField(blank=True, max_length=64, verbose_name='Validation function name', choices=[('Parse date', 'Parse date'), ('Parse date and time', 'Parse date and time'), ('Parse time', 'Parse time')])), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), + ( + 'name', models.CharField( + help_text='Do not use python reserved words, ' + 'or spaces.', unique=True, max_length=48, + verbose_name='Name' + ) + ), + ( + 'title', models.CharField( + max_length=48, verbose_name='Title' + ) + ), + ( + 'default', models.CharField( + help_text='Enter a string to be evaluated.', + max_length=128, null=True, verbose_name='Default', blank=True + ) + ), + ( + 'lookup', models.TextField( + help_text='Enter a string to be evaluated that ' + 'returns an iterable.', null=True, + verbose_name='Lookup', blank=True) + ), + ( + 'validation', models.CharField( + blank=True, max_length=64, + verbose_name='Validation function name', + choices=[ + ('Parse date', 'Parse date'), + ('Parse date and time', 'Parse date and time'), + ('Parse time', 'Parse time') + ] + ) + ), ], options={ 'ordering': ('title',), @@ -57,7 +121,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='documenttypemetadatatype', name='metadata_type', - field=models.ForeignKey(verbose_name='Metadata type', to='metadata.MetadataType'), + field=models.ForeignKey( + verbose_name='Metadata type', to='metadata.MetadataType' + ), preserve_default=True, ), migrations.AlterUniqueTogether( @@ -67,7 +133,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='documentmetadata', name='metadata_type', - field=models.ForeignKey(verbose_name='Type', to='metadata.MetadataType'), + field=models.ForeignKey( + verbose_name='Type', to='metadata.MetadataType' + ), preserve_default=True, ), migrations.AlterUniqueTogether( diff --git a/mayan/apps/metadata/migrations/0003_auto_20150708_0323.py b/mayan/apps/metadata/migrations/0003_auto_20150708_0323.py index 3e9aaa6674..856562cf30 100644 --- a/mayan/apps/metadata/migrations/0003_auto_20150708_0323.py +++ b/mayan/apps/metadata/migrations/0003_auto_20150708_0323.py @@ -13,7 +13,10 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='metadatatype', - options={'ordering': ('label',), 'verbose_name': 'Metadata type', 'verbose_name_plural': 'Metadata types'}, + options={ + 'ordering': ('label',), 'verbose_name': 'Metadata type', + 'verbose_name_plural': 'Metadata types' + }, ), migrations.RenameField( model_name='metadatatype', diff --git a/mayan/apps/rest_api/filters.py b/mayan/apps/rest_api/filters.py index aee7c7948f..e390fb8bb5 100644 --- a/mayan/apps/rest_api/filters.py +++ b/mayan/apps/rest_api/filters.py @@ -11,8 +11,8 @@ from permissions import Permission class MayanObjectPermissionsFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): required_permission = getattr( - view, 'mayan_object_permissions', {}).get(request.method, None - ) + view, 'mayan_object_permissions', {} + ).get(request.method, None) if required_permission: try: diff --git a/mayan/apps/rest_api/permissions.py b/mayan/apps/rest_api/permissions.py index 0e13979dab..b69c42a8d0 100644 --- a/mayan/apps/rest_api/permissions.py +++ b/mayan/apps/rest_api/permissions.py @@ -13,8 +13,8 @@ from permissions import Permission class MayanPermission(BasePermission): def has_permission(self, request, view): required_permission = getattr( - view, 'mayan_view_permissions', {}).get(request.method, None - ) + view, 'mayan_view_permissions', {} + ).get(request.method, None) if required_permission: try: @@ -28,8 +28,8 @@ class MayanPermission(BasePermission): def has_object_permission(self, request, view, obj): required_permission = getattr( - view, 'mayan_object_permissions', {}).get(request.method, None - ) + view, 'mayan_object_permissions', {} + ).get(request.method, None) if required_permission: try: diff --git a/mayan/apps/rest_api/urls.py b/mayan/apps/rest_api/urls.py index 82ab9fef65..0868e7fb68 100644 --- a/mayan/apps/rest_api/urls.py +++ b/mayan/apps/rest_api/urls.py @@ -8,7 +8,8 @@ version_0_urlpatterns = patterns( '', url(r'^$', Version_0.as_view(), name='api-version-0'), url( - r'^(?P\w+)/$', APIAppView.as_view(), name='api-version-0-app' + r'^(?P\w+)/$', APIAppView.as_view(), + name='api-version-0-app' ), ) diff --git a/mayan/apps/rest_api/views.py b/mayan/apps/rest_api/views.py index 22a18b5994..4a193b2be4 100644 --- a/mayan/apps/rest_api/views.py +++ b/mayan/apps/rest_api/views.py @@ -46,7 +46,9 @@ class Version_0(generics.GenericAPIView): { 'name': unicode(endpoint), 'url': reverse('api-version-0-app', - args=[unicode(endpoint)], request=request, format=format) + args=[unicode(endpoint)], request=request, + format=format + ) } for endpoint in APIEndPoint.get_all() ], }) diff --git a/mayan/apps/smart_settings/apps.py b/mayan/apps/smart_settings/apps.py index d574a8c2e4..e16639d2db 100644 --- a/mayan/apps/smart_settings/apps.py +++ b/mayan/apps/smart_settings/apps.py @@ -40,7 +40,9 @@ class SmartSettingsApp(MayanAppConfig): ) SourceColumn( source=Setting, label=_('Found in path'), - attribute=encapsulate(lambda instance: exists_widget(instance.value) if instance.is_path else _('n/a')) + attribute=encapsulate( + lambda instance: exists_widget(instance.value) if instance.is_path else _('n/a') + ) ) menu_object.bind_links( @@ -54,4 +56,6 @@ class SmartSettingsApp(MayanAppConfig): except ImportError: logger.debug('App %s has not settings.py file', app.name) else: - logger.debug('Imported settings.py file for app %s', app.name) + logger.debug( + 'Imported settings.py file for app %s', app.name + ) diff --git a/mayan/apps/smart_settings/urls.py b/mayan/apps/smart_settings/urls.py index 9899863360..2127456fa4 100644 --- a/mayan/apps/smart_settings/urls.py +++ b/mayan/apps/smart_settings/urls.py @@ -7,10 +7,11 @@ from .views import NamespaceDetailView, NamespaceListView urlpatterns = patterns( '', url( - r'^namespace/all/$', NamespaceListView.as_view(), name='namespace_list' + r'^namespace/all/$', NamespaceListView.as_view(), + name='namespace_list' ), url( - r'^namespace/(?P\w+)/$', NamespaceDetailView.as_view(), - name='namespace_detail' + r'^namespace/(?P\w+)/$', + NamespaceDetailView.as_view(), name='namespace_detail' ), ) diff --git a/mayan/apps/sources/apps.py b/mayan/apps/sources/apps.py index 8a60c4c892..b945da74d3 100644 --- a/mayan/apps/sources/apps.py +++ b/mayan/apps/sources/apps.py @@ -49,7 +49,11 @@ class SourcesApp(MayanAppConfig): MissingItem( label=_('Create a document source'), - description=_('Document sources are the way in which new documents are feed to Mayan EDMS, create at least a web form source to be able to upload documents from a browser.'), + description=_( + 'Document sources are the way in which new documents are ' + 'feed to Mayan EDMS, create at least a web form source to ' + 'be able to upload documents from a browser.' + ), condition=lambda: not Source.objects.exists(), view='sources:setup_source_list' ) @@ -131,7 +135,8 @@ class SourcesApp(MayanAppConfig): ) post_upgrade.connect( - initialize_periodic_tasks, dispatch_uid='initialize_periodic_tasks' + initialize_periodic_tasks, + dispatch_uid='initialize_periodic_tasks' ) post_initial_setup.connect( create_default_document_source, diff --git a/mayan/apps/sources/test_views.py b/mayan/apps/sources/test_views.py index 0c5825d3f4..f828e7fd7d 100644 --- a/mayan/apps/sources/test_views.py +++ b/mayan/apps/sources/test_views.py @@ -51,7 +51,7 @@ class UploadDocumentTestCase(TestCase): self.client.post( reverse( 'sources:setup_source_create', args=[SOURCE_CHOICE_WEB_FORM] - ), {'label': 'test', 'uncompress': 'n', 'enabled': True} + ), {'label': 'test', 'uncompress': 'n', 'enabled': True} ) self.assertEqual(WebFormSource.objects.count(), 1) diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index 0215e92e60..a4be4165a2 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -418,7 +418,7 @@ def tag_remove(request, document_id=None, document_id_list=None, tag_id=None, ta request, _( 'Tag "%(tag)s" removed successfully from document "%(document)s".' ) % { - 'document': document, 'tag': tag + 'document': document, 'tag': tag } )