Completed
Push — master ( c898b8...217985 )
by Johannes
50s
created

render_field_variations()   B

Complexity

Conditions 5

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 15
rs 8.5454
cc 5
1
from concurrent.futures import ProcessPoolExecutor
2
from multiprocessing import cpu_count
3
4
import progressbar
5
from django.apps import apps
6
from django.core.files.storage import get_storage_class
7
from django.core.management import BaseCommand, CommandError
8
9
from stdimage.utils import render_variations
10
11
12
class Command(BaseCommand):
13
    help = 'Renders all variations of a StdImageField.'
14
    args = '<app.model.field app.model.field>'
15
16
    def add_arguments(self, parser):
17
        parser.add_argument('field_path',
18
                            nargs='+',
19
                            type=str,
20
                            help='<app.model.field app.model.field>')
21
        parser.add_argument('--replace',
22
                            action='store_true',
23
                            dest='replace',
24
                            default=False,
25
                            help='Replace existing files.')
26
27
        parser.add_argument('-i', '--ignore-missing',
28
                            action='store_true',
29
                            dest='ignore_missing',
30
                            default=False,
31
                            help='Ignore missing source file error and '
32
                                 'skip render for that file')
33
34
    def handle(self, *args, **options):
35
        replace = options.get('replace', False)
36
        ignore_missing = options.get('ignore_missing', False)
37
        routes = options.get('field_path', [])
38
        for route in routes:
39
            try:
40
                app_label, model_name, field_name = route.rsplit('.')
41
            except ValueError:
42
                raise CommandError("Error parsing field_path '{}'. Use format "
43
                                   "<app.model.field app.model.field>."
44
                                   .format(route))
45
            model_class = apps.get_model(app_label, model_name)
46
            field = model_class._meta.get_field(field_name)
47
48
            queryset = model_class._default_manager \
49
                .exclude(**{'%s__isnull' % field_name: True}) \
50
                .exclude(**{field_name: ''})
51
            obj = queryset.first()
52
            do_render = True
53
            if obj:
54
                f = getattr(obj, field_name)
55
                do_render = f.field.render_variations
56
            images = queryset.values_list(field_name, flat=True).iterator()
57
            count = queryset.count()
58
59
            self.render(field, images, count, replace, ignore_missing,
60
                        do_render)
61
62
    @staticmethod
63
    def render(field, images, count, replace, ignore_missing, do_render):
64
        kwargs_list = (
65
            dict(
66
                file_name=file_name,
67
                do_render=do_render,
68
                variations=field.variations,
69
                replace=replace,
70
                storage=field.storage.deconstruct()[0],
71
                field_class=field.attr_class,
72
                ignore_missing=ignore_missing,
73
            )
74
            for file_name in images
75
        )
76
        with progressbar.ProgressBar(maxval=count, widgets=(
77
            progressbar.RotatingMarker(),
78
            ' | CPUs: {}'.format(cpu_count()),
79
            ' | ', progressbar.AdaptiveETA(),
80
            ' | ', progressbar.Percentage(),
81
            ' ', progressbar.Bar(),
82
        )) as bar:
83
            with ProcessPoolExecutor() as executor:
84
                for _ in executor.map(render_field_variations, kwargs_list):
85
                    bar += 1
86
87
88
def render_field_variations(kwargs):
89
    kwargs['storage'] = get_storage_class(kwargs['storage'])()
90
    ignore_missing = kwargs.pop('ignore_missing')
91
    do_render = kwargs.pop('do_render')
92
    try:
93
        if callable(do_render):
94
            kwargs.pop('field_class')
95
            do_render = do_render(**kwargs)
96
        if do_render:
97
            render_variations(**kwargs)
98
    except FileNotFoundError as e:
99
        if not ignore_missing:
100
            raise CommandError(
101
                 'Source file was not found, terminating. '
102
                 'Use -i/--ignore-missing to skip this error.') from e
103