Completed
Push — master ( ca176f...daddf9 )
by Johannes
27s
created

Command.render()   B

Complexity

Conditions 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 8.3411
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
    def handle(self, *args, **options):
28
        replace = options.get('replace')
29
        if len(options['field_path']):
30
            routes = options['field_path']
31
        else:
32
            routes = [options['field_path']]
33
        for route in routes:
34
            try:
35
                app_label, model_name, field_name = route.rsplit('.')
36
            except ValueError:
37
                raise CommandError("Error parsing field_path '{}'. Use format "
38
                                   "<app.model.field app.model.field>."
39
                                   .format(route))
40
            model_class = apps.get_model(app_label, model_name)
41
            field = model_class._meta.get_field(field_name)
42
43
            queryset = model_class._default_manager \
44
                .exclude(**{'%s__isnull' % field_name: True}) \
45
                .exclude(**{field_name: ''})
46
            obj = queryset.first()
47
            do_render = True
48
            if obj:
49
                f = getattr(obj, field_name)
50
                do_render = f.field.render_variations
51
            images = queryset.values_list(field_name, flat=True).iterator()
52
            count = queryset.count()
53
54
            self.render(field, images, count, replace, do_render)
55
56
    @staticmethod
57
    def render(field, images, count, replace, do_render):
58
        kwargs_list = (
59
            dict(
60
                file_name=file_name,
61
                do_render=do_render,
62
                variations=field.variations,
63
                replace=replace,
64
                storage=field.storage.deconstruct()[0],
65
            )
66
            for file_name in images
67
        )
68
        with progressbar.ProgressBar(maxval=count, widgets=(
69
            progressbar.RotatingMarker(),
70
            ' | CPUs: {}'.format(cpu_count()),
71
            ' | ', progressbar.AdaptiveETA(),
72
            ' | ', progressbar.Percentage(),
73
            ' ', progressbar.Bar(),
74
        )) as bar:
75
            with ProcessPoolExecutor() as executor:
76
                while next(executor.map(render_field_variations, kwargs_list)):
77
                    bar += 1
78
79
80
def render_field_variations(kwargs):
81
    kwargs['storage'] = get_storage_class(kwargs['storage'])()
82
    do_render = kwargs.pop('do_render')
83
    if callable(do_render):
84
        do_render = do_render(**kwargs)
85
    if do_render:
86
        render_variations(**kwargs)
87