Skip to content

Commit

Permalink
#342 Remade attachment_anonymization management command
Browse files Browse the repository at this point in the history
  • Loading branch information
viliambalaz committed Jan 12, 2021
1 parent 3164f13 commit 6b82f3f
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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))
111 changes: 68 additions & 43 deletions chcemvediet/apps/anonymization/tests/test_management_commands.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()

Expand All @@ -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)
49 changes: 19 additions & 30 deletions misc/anonymization.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,35 +91,24 @@ Computed Properties:
* `content`: String; May be NULL; May be empty; Read-only.


<sub>*\* Features that are marked ~~strikethrough~~ are not implemented yet.*</sub>
## `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.


<sub>*\* Features that are marked ~~strikethrough~~ are not implemented yet.*</sub>

0 comments on commit 6b82f3f

Please sign in to comment.