diff --git a/chcemvediet/apps/anonymization/management/commands/attachment_anonymization.py b/chcemvediet/apps/anonymization/management/commands/attachment_anonymization.py index ae2f7c921..e83f7f4e0 100644 --- a/chcemvediet/apps/anonymization/management/commands/attachment_anonymization.py +++ b/chcemvediet/apps/anonymization/management/commands/attachment_anonymization.py @@ -5,51 +5,59 @@ from django.core.management.base import BaseCommand, CommandError from poleno.attachments.models import Attachment +from poleno.utils.misc import squeeze from chcemvediet.apps.anonymization.models import AttachmentFinalization from chcemvediet.apps.inforequests.models import Action class Command(BaseCommand): - help = u'Creates AttachmentFinalization for the specified Attachment.' + args = u'attachment [file]' + help = squeeze(u""" + Creates anonymization for the specified Attachment. By default, the file path is read + from stdin. File path can be explicitly passed as an command argument. Anonymization + created this way will be marked as successful. Only one successful anonymization can be + assigned to the Attachment. + """) option_list = BaseCommand.option_list + ( - make_option(u'-f', u'--file', - dest=u'filename', - help=u'define file path'), make_option(u'--content_type', - help=u'define content type of file', + help=squeeze(u""" + Content type of file, e.g. "application/pdf". Automatically computed if + not specified. + """) ), make_option(u'--debug', default=u'', - help=u'add debug message' + help=u'Debug message to the newly created anonymization. Empty by default.' ), make_option(u'--force', action=u'store_true', - help=u'overwrite an existing successful AttachmentFinalization' + help=squeeze(u""" + The command refuses to anonymize attachment if a successful + anonymization already exists. This flag disables this check. Deletes all + existing successful anonymizations and creates new one. Unsuccessful + anonymizations will stay unaffected. + """) ), ) - def usage(self, subcommand=None): - return u'manage.py attachment_anonymization attachment [options] [file]' - def handle(self, *args, **options): - try: - filename = options[u'filename'] or args[1] - except IndexError: - self.stdout.write(u'Usage: {}'.format(self.usage())) - self.stdout.write( - u'Try "manage.py attachment_anonymization --help" for more information.') - return 1 - + if not args: + raise CommandError(u'attachment_anonymization takes at least 1 argument (0 given).') + if len(args) > 2: + raise CommandError( + u'attachment_anonymization takes at most 2 arguments ({} given).'.format(len(args)) + ) + pk = args[0] + filename = args[1] if len(args) == 2 else raw_input(u'File:') + if not filename: + raise CommandError(u'Missing source file name.') with open(filename, u'rb') as file: try: - attachment = (Attachment.objects - .attached_to(Action) - .not_normalized() - .get(pk=int(args[0]))) + attachment = Attachment.objects.attached_to(Action).get(pk=pk) attachments_finalization = (AttachmentFinalization.objects - .filter(attachment=attachment) - .successful()) + .filter(attachment=attachment) + .successful()) if options[u'force'] or not attachments_finalization: attachments_finalization.delete() content = file.read() @@ -61,5 +69,10 @@ def handle(self, *args, **options): content_type=options[u'content_type'] or content_type, debug=options[u'debug'], ) + else: + raise CommandError(squeeze(u""" + Anonymization files already exist. Use the --force option to overwrite + them. + """)) except Attachment.DoesNotExist: - raise CommandError(u'Attachment {} does not exist'.format(args[0])) + raise CommandError(u'Attachment instance with pk {} does not exist.'.format(pk)) diff --git a/chcemvediet/apps/anonymization/tests/test_management_commands.py b/chcemvediet/apps/anonymization/tests/test_management_commands.py index 6440b79bf..e8b63bf6e 100644 --- a/chcemvediet/apps/anonymization/tests/test_management_commands.py +++ b/chcemvediet/apps/anonymization/tests/test_management_commands.py @@ -1,9 +1,11 @@ import os +import sys +from StringIO import StringIO from testfixtures import TempDirectory from django.core.management import call_command +from django.core.management.base import CommandError from django.test import TestCase -from django.test.utils import override_settings from chcemvediet.apps.anonymization.models import AttachmentFinalization from chcemvediet.tests import ChcemvedietTestCaseMixin @@ -14,14 +16,11 @@ class AttachmentAnonymizationManagementCommandTest(ChcemvedietTestCaseMixin, Tes def _pre_setup(self): super(AttachmentAnonymizationManagementCommandTest, self)._pre_setup() self.tempdir = TempDirectory() - self.settings_override = override_settings( - MEDIA_ROOT=self.tempdir.path, - ) - self.settings_override.enable() + self.tempdir.write(u'testfile.txt', u'Default testing content') + self.filename = os.path.join(self.tempdir.path, u'testfile.txt') self.attachment = self._create_attachment() def _post_teardown(self): - self.settings_override.disable() self.tempdir.cleanup() super(AttachmentAnonymizationManagementCommandTest, self)._post_teardown() @@ -32,40 +31,66 @@ def _create_attachment(self, **kwargs): ) - def test_attachment_anonymization_command_with_valid_arguments_creates_attachment_finalization(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, file.name) - AttachmentFinalization.objects.get(attachment=self.attachment) - - def test_attachment_anonymization_command_file_option(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, filename=file.name) - AttachmentFinalization.objects.get(attachment=self.attachment) - - def test_attachment_anonymization_command_content_type_option(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, file.name, content_type=u'type') - attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) - self.assertEqual(attachment_finalization.content_type, u'type') - - def test_attachment_anonymization_command_debug_option(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, file.name, debug=u'debug') - attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) - self.assertEqual(attachment_finalization.debug, u'debug') - - def test_attachment_anonymization_command_with_existing_attachment_finalization(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, file.name) - attachment_finalization1 = AttachmentFinalization.objects.get(attachment=self.attachment) - call_command(u'attachment_anonymization', self.attachment.pk, file.name) - attachment_finalization2 = AttachmentFinalization.objects.get(attachment=self.attachment) - self.assertEqual(attachment_finalization1.pk, attachment_finalization2.pk) - - def test_attachment_anonymization_command_force_option(self): - with open(os.path.join(self.tempdir.path, u'myfile.txt'), u'w') as file: - call_command(u'attachment_anonymization', self.attachment.pk, file.name) - attachment_finalization1 = AttachmentFinalization.objects.get(attachment=self.attachment) - call_command(u'attachment_anonymization', self.attachment.pk, file.name, force=True) - attachment_finalization2 = AttachmentFinalization.objects.get(attachment=self.attachment) - self.assertNotEqual(attachment_finalization1.pk, attachment_finalization2.pk) + def test_attachment_and_file_arguments(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + self.assertEqual(attachment_finalization.attachment.pk, self.attachment.pk) + self.assertEqual(attachment_finalization.file.read(), u'Default testing content') + self.assertEqual(attachment_finalization.successful, True) + + def test_attachment_argument_may_not_be_omitted(self): + with self.assertRaisesMessage(CommandError, u'attachment_anonymization takes at least 1 argument (0 given).'): + call_command(u'attachment_anonymization') + + def test_non_existent_attachment_raises_exception(self): + with self.assertRaisesMessage(CommandError, u'Attachment instance with pk -1 does not exist.'): + call_command(u'attachment_anonymization', u'-1', self.filename) + + def test_command_with_too_many_arguments(self): + with self.assertRaisesMessage(CommandError, u'attachment_anonymization takes at most 2 arguments (3 given).'): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename, u'filename2') + + def test_file_argument_is_read_from_stdin_if_omitted(self): + self.addCleanup(setattr, sys, u'stdin', sys.stdin) + sys.stdin = StringIO(self.filename) + call_command(u'attachment_anonymization', self.attachment.pk) + + def test_file_argument_and_stdin_together_may_not_be_omitted(self): + self.addCleanup(setattr, sys, u'stdin', sys.stdin) + sys.stdin = StringIO(u'\n') + with self.assertRaisesMessage(CommandError, u'Missing source file name.'): + call_command(u'attachment_anonymization', self.attachment.pk) + + def test_content_type_option(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename, content_type=u'application/pdf') + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + self.assertEqual(attachment_finalization.content_type, u'application/pdf') + + def test_content_type_option_default_value_if_omitted(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + self.assertEqual(attachment_finalization.content_type, u'text/plain') + + def test_debug_option(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename, debug=u'debug') + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + self.assertEqual(attachment_finalization.debug, u'debug') + + def test_debug_option_default_value_if_omitted(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + self.assertEqual(attachment_finalization.debug, u'') + + def test_force_option(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) + attachment_finalization1 = AttachmentFinalization.objects.get(attachment=self.attachment) + call_command(u'attachment_anonymization', self.attachment.pk, self.filename, force=True) + attachment_finalization2 = AttachmentFinalization.objects.get(attachment=self.attachment) + with self.assertRaisesMessage(AttachmentFinalization.DoesNotExist, u'AttachmentFinalization matching query does not exist'): + AttachmentFinalization.objects.get(pk=attachment_finalization1.pk) + + def test_existent_attachment_finalization_raises_exception_if_force_option_is_omitted(self): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) + attachment_finalization = AttachmentFinalization.objects.get(attachment=self.attachment) + with self.assertRaisesMessage(CommandError, u'Anonymization files already exist. Use the --force option to overwrite them.'): + call_command(u'attachment_anonymization', self.attachment.pk, self.filename) diff --git a/misc/anonymization.md b/misc/anonymization.md index 8f110ec4b..174eb894f 100644 --- a/misc/anonymization.md +++ b/misc/anonymization.md @@ -91,35 +91,24 @@ Computed Properties: * `content`: String; May be NULL; May be empty; Read-only. -*\* Features that are marked ~~strikethrough~~ are not implemented yet.* +## `attachment_anonymization` + + $ env/bin/python manage.py attachment_anonymization [options] attachment_id [file] + + +Creates AttachmentFinalization instance for the specified Attachment. By default, the file path is +read from stdin. You can pass file path explicitly as an argument. +AttachmentFinalization created this way will be marked as successful. Only one successful +AttachmentFinalization can be assigned to the Attachment. -## Anonymization -You can manually anonymize `Attachment` using management command: - -``` -manage.py attachment_anonymization attachment [options] [file] - -Options: - -v VERBOSITY, --verbosity=VERBOSITY - Verbosity level; 0=minimal output, 1=normal output, - 2=verbose output, 3=very verbose output - --settings=SETTINGS The Python path to a settings module, e.g. - "myproject.settings.main". If this isn't provided, the - DJANGO_SETTINGS_MODULE environment variable will be - used. - --pythonpath=PYTHONPATH - A directory to add to the Python path, e.g. - "/home/djangoprojects/myproject". - --traceback Raise on exception - --no-color Don't colorize the command output. - -f FILENAME, --file=FILENAME - define file path - --content_type=CONTENT_TYPE - define content type of file - --debug=DEBUG add debug message - --force overwrite an existing successful - AttachmentFinalization - --version show program's version number and exit - -h, --help show this help message and exit -``` +* `--content_type=CONTENT_TYPE`: Content type of file, e.g. "application/pdf". Automatically + computed if not specified. +* `--debug=DEBUG`: Debug message to the newly created instance. Empty by default. +* `--force`: The command refuses to anonymize attachment if a successful anonymization already + exists. This flag disables this check. Deletes all existing successful + AttachmentFinalizations and creates new one. Unsuccessful AttachmentFinalizations will + stay unaffected. + + +*\* Features that are marked ~~strikethrough~~ are not implemented yet.*