papyru 2.10.4.dev77__tar.gz → 2.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {papyru-2.10.4.dev77/papyru.egg-info → papyru-2.11.0}/PKG-INFO +1 -1
  2. papyru-2.11.0/papyru/create_variant/component.py +35 -0
  3. papyru-2.11.0/papyru/create_variant/constants.py +42 -0
  4. papyru-2.11.0/papyru/create_variant/edit_template/__init__.py +0 -0
  5. papyru-2.11.0/papyru/create_variant/edit_template/ean.py +64 -0
  6. papyru-2.11.0/papyru/create_variant/edit_template/manufacturer_address.py +67 -0
  7. papyru-2.11.0/papyru/create_variant/edit_template/motive.py +14 -0
  8. papyru-2.11.0/papyru/create_variant/edit_template/render_text.py +107 -0
  9. papyru-2.11.0/papyru/create_variant/edit_template/title.py +59 -0
  10. papyru-2.11.0/papyru/create_variant/edit_template/xml.py +126 -0
  11. papyru-2.11.0/papyru/create_variant/resi.py +25 -0
  12. papyru-2.11.0/papyru/static/__init__.py +0 -0
  13. {papyru-2.10.4.dev77 → papyru-2.11.0/papyru.egg-info}/PKG-INFO +1 -1
  14. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru.egg-info/SOURCES.txt +11 -0
  15. {papyru-2.10.4.dev77 → papyru-2.11.0}/setup.py +3 -1
  16. {papyru-2.10.4.dev77 → papyru-2.11.0}/LICENSE.txt +0 -0
  17. {papyru-2.10.4.dev77 → papyru-2.11.0}/README.md +0 -0
  18. {papyru-2.10.4.dev77 → papyru-2.11.0}/bin/generate_jsonschema.py +0 -0
  19. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/__init__.py +0 -0
  20. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/auth.py +0 -0
  21. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/context.py +0 -0
  22. {papyru-2.10.4.dev77/papyru/static → papyru-2.11.0/papyru/create_variant}/__init__.py +0 -0
  23. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/logger.py +0 -0
  24. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/logger_stdout.py +0 -0
  25. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/logger_types.py +0 -0
  26. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/middleware.py +0 -0
  27. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/problem.py +0 -0
  28. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/profiler.py +0 -0
  29. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/resource.py +0 -0
  30. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/serializer.py +0 -0
  31. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/stack_context.py +0 -0
  32. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/static/apps.py +0 -0
  33. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/static/urls.py +0 -0
  34. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/static/views.py +0 -0
  35. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/utils.py +0 -0
  36. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/validation.py +0 -0
  37. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/__init__.py +0 -0
  38. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/assets/Job.json +0 -0
  39. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/assets/JobStatusHistory.json +0 -0
  40. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/command/__init__.py +0 -0
  41. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/command/config.py +0 -0
  42. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/command/types.py +0 -0
  43. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/serializers.py +0 -0
  44. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/varspool/types.py +0 -0
  45. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru/xml_helper.py +0 -0
  46. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru.egg-info/dependency_links.txt +0 -0
  47. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru.egg-info/requires.txt +0 -0
  48. {papyru-2.10.4.dev77 → papyru-2.11.0}/papyru.egg-info/top_level.txt +0 -0
  49. {papyru-2.10.4.dev77 → papyru-2.11.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: papyru
3
- Version: 2.10.4.dev77
3
+ Version: 2.11.0
4
4
  Summary: minimal REST library with OpenAPI-based validation for django
5
5
  Home-page: http://www.puzzleyou.net/
6
6
  Author: puzzleYOU GmbH
@@ -0,0 +1,35 @@
1
+ from papyru import XMLHelper
2
+
3
+ from .edit_template.ean import insert_ean
4
+ from .edit_template.manufacturer_address import insert_manufacturer_address
5
+ from .edit_template.motive import insert_motive
6
+ from .edit_template.title import insert_title
7
+ from .edit_template.xml import ImageInfo
8
+ from .resi import import_into_resi
9
+
10
+
11
+ def create_component(template: str,
12
+ motive_image_info: ImageInfo,
13
+ resi_instance: object,
14
+ resi_url: str,
15
+ disposition_component: str,
16
+ disposition_manufacturer_address: str,
17
+ disposition_title=None,
18
+ motive_title=None,
19
+ disposition_ean=None,
20
+ ean=None,
21
+ properties=None,
22
+ ) -> str:
23
+
24
+ doc = XMLHelper(template)
25
+ insert_motive(doc, motive_image_info)
26
+ insert_title(doc, motive_title, disposition_title, resi_instance, resi_url)
27
+ insert_ean(doc, ean, disposition_ean, resi_instance, resi_url)
28
+ insert_manufacturer_address(doc, disposition_manufacturer_address,
29
+ resi_instance, resi_url)
30
+
31
+ return import_into_resi(doc.serialize(doc.root),
32
+ resi_instance,
33
+ resi_url,
34
+ properties,
35
+ disposition_component)
@@ -0,0 +1,42 @@
1
+ """
2
+ Constants for specific templates
3
+
4
+ These constants are used in specific templates to ensure that SVG
5
+ components are generated correctly, e.g.:
6
+
7
+ - https://masa.productdesignerd.puzzleandplay.de/
8
+ templates/single-image-4-3-landscape/structure/original
9
+ - https://masa.productdesignerd.puzzleandplay.de/
10
+ templates/box_small_500_landscape_grey/structure/original
11
+ """
12
+
13
+ from .edit_template.render_text import (Alignment, HorizontalAlignment,
14
+ RGBAColor, VerticalAlignment)
15
+
16
+ # Preview types can be overwritten by ShopAttributes in Masa.
17
+ PREVIEW_TYPE_BOX = 'box'
18
+ PREVIEW_TYPE_PARTS = 'pieces'
19
+
20
+ PREVIEW_IMAGE_FORMAT = 'jpeg'
21
+ PREVIEW_MIME_TYPE = 'image/%s' % PREVIEW_IMAGE_FORMAT
22
+ PREVIEW_QUALITY_LEVEL = 90
23
+ DEFAULT_PREVIEW_SIZE = 1500
24
+
25
+ MOTIVE_PLACEHOLDER_ID = 'img-main'
26
+ MOTIVE_USE_ID = 'img-main-use'
27
+
28
+ TITLE_PLACEHOLDER_ID = 'txt-main'
29
+ TITLE_ALIGNMENT = Alignment(
30
+ vertical=VerticalAlignment.Top, horizontal=HorizontalAlignment.Center)
31
+ TITLE_FONT = 'FreeSerif'
32
+ TITLE_COLOR_FOREGROUND = RGBAColor(255, 255, 255, 1.0)
33
+ TITLE_COLOR_BACKGROUND = RGBAColor(0, 0, 0, 0.0)
34
+
35
+ EAN_PLACEHOLDER_ID = 'img-ean'
36
+
37
+ MANUFACTURER_ADDRESS_PLACEHOLDER_ID = 'manufacturer-address'
38
+ MANUFACTURER_ADDRESS_ALIGNMENT = Alignment(
39
+ vertical=VerticalAlignment.Center, horizontal=HorizontalAlignment.Center)
40
+ MANUFACTURER_ADDRESS_FONT = 'Open Sans'
41
+ MANUFACTURER_ADDRESS_COLOR_FOREGROUND = RGBAColor(0, 0, 0, 1.0)
42
+ MANUFACTURER_ADDRESS_COLOR_BACKGROUND = RGBAColor(0, 0, 0, 0.0)
@@ -0,0 +1,64 @@
1
+ from os import getpid
2
+ from subprocess import PIPE, Popen
3
+
4
+ from papyru import XMLHelper
5
+ from papyru.create_variant.constants import EAN_PLACEHOLDER_ID
6
+ from papyru.create_variant.resi import import_into_resi
7
+
8
+ from .xml import ImageInfo, find_placeholder, fit_image
9
+
10
+
11
+ def insert_ean(doc: XMLHelper,
12
+ ean: str,
13
+ disposition: str,
14
+ resi_instance: object,
15
+ resi_url: str,
16
+ ) -> XMLHelper:
17
+ ph = find_placeholder(doc, 'image', EAN_PLACEHOLDER_ID)
18
+
19
+ if ph is None:
20
+ return doc
21
+
22
+ width = float(doc.get_attribute(ph.wrapper, 'width'))
23
+ height = float(doc.get_attribute(ph.wrapper, 'height'))
24
+
25
+ ean_location = _create_ean(ean, width, height, disposition,
26
+ resi_instance, resi_url)
27
+
28
+ return fit_image(doc,
29
+ EAN_PLACEHOLDER_ID,
30
+ ImageInfo(location=ean_location,
31
+ width=width,
32
+ height=height))
33
+
34
+
35
+ def _create_ean(ean: str,
36
+ width: int,
37
+ height: int,
38
+ disposition: str,
39
+ resi_instance: any,
40
+ resi_url: str) -> str:
41
+ _, ean_bytes = resi_instance.provide_version_bytes(
42
+ generate_barcode_svg(ean).encode(),
43
+ {
44
+ "convert": "image/png",
45
+ "stretch": {
46
+ "width": int(width),
47
+ "height": int(height),
48
+ }
49
+ },
50
+ track=str(getpid()))
51
+
52
+ return import_into_resi(
53
+ ean_bytes, resi_instance, resi_url, None, disposition)
54
+
55
+
56
+ def generate_barcode_svg(text: str) -> str:
57
+ call = ['barcode', '-e', 'EAN', '-S']
58
+
59
+ with Popen(call, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) as proc:
60
+ out, err = proc.communicate(input=text)
61
+ if err != '':
62
+ raise Exception("call barcode failed: %s" % err)
63
+
64
+ return out.replace('Helvetica', 'Open Sans')
@@ -0,0 +1,67 @@
1
+ from logging import getLogger
2
+
3
+ from papyru import XMLHelper
4
+ from papyru.create_variant.constants import (
5
+ MANUFACTURER_ADDRESS_ALIGNMENT, MANUFACTURER_ADDRESS_COLOR_BACKGROUND,
6
+ MANUFACTURER_ADDRESS_COLOR_FOREGROUND, MANUFACTURER_ADDRESS_FONT,
7
+ MANUFACTURER_ADDRESS_PLACEHOLDER_ID)
8
+ from papyru.create_variant.resi import import_into_resi
9
+
10
+ from .render_text import TextConfig, render_text
11
+ from .xml import (ImageInfo, find_placeholder, fit_image,
12
+ replace_text_with_image)
13
+
14
+ log = getLogger(__name__)
15
+
16
+
17
+ def insert_manufacturer_address(doc: XMLHelper,
18
+ disposition: str,
19
+ resi_instance: object,
20
+ resi_url: str,
21
+ ) -> XMLHelper:
22
+ ph = find_placeholder(doc, 'text', MANUFACTURER_ADDRESS_PLACEHOLDER_ID)
23
+ if ph is None:
24
+ log.warning('no manufacturer address found')
25
+ return doc
26
+
27
+ width = float(doc.get_attribute(ph.wrapper, 'width'))
28
+ height = float(doc.get_attribute(ph.wrapper, 'height'))
29
+
30
+ address = '\n'.join(map(
31
+ lambda tspan: tspan.text,
32
+ ph.placeholder.findall(
33
+ './/{%s}tspan' % doc.default_namespace)))
34
+ location = _create_text_manufacturer_address(
35
+ address, width, height, disposition, resi_instance, resi_url)
36
+
37
+ return fit_image(
38
+ replace_text_with_image(doc, MANUFACTURER_ADDRESS_PLACEHOLDER_ID),
39
+ MANUFACTURER_ADDRESS_PLACEHOLDER_ID,
40
+ ImageInfo(location=location,
41
+ width=width,
42
+ height=height))
43
+
44
+
45
+ def _create_text_manufacturer_address(address: str,
46
+ width: int,
47
+ height: int,
48
+ disposition: str,
49
+ resi_instance: any,
50
+ resi_url: str) -> str:
51
+ text_bytes = render_text(
52
+ TextConfig(
53
+ text=address,
54
+ width=int(width),
55
+ height=int(height),
56
+ alignment=MANUFACTURER_ADDRESS_ALIGNMENT,
57
+ font_name=MANUFACTURER_ADDRESS_FONT,
58
+ fg_color=MANUFACTURER_ADDRESS_COLOR_FOREGROUND,
59
+ bg_color=MANUFACTURER_ADDRESS_COLOR_BACKGROUND),
60
+ resi_instance)
61
+
62
+ return import_into_resi(
63
+ text_bytes,
64
+ resi_instance,
65
+ resi_url,
66
+ None,
67
+ disposition)
@@ -0,0 +1,14 @@
1
+ from functools import reduce
2
+
3
+ from papyru import XMLHelper
4
+ from papyru.create_variant.constants import (MOTIVE_PLACEHOLDER_ID,
5
+ MOTIVE_USE_ID)
6
+
7
+ from .xml import ImageInfo, fit_image
8
+
9
+
10
+ def insert_motive(doc: XMLHelper, motive_info: ImageInfo) -> XMLHelper:
11
+ return reduce(lambda acc, el: fit_image(acc, el, motive_info),
12
+ [MOTIVE_PLACEHOLDER_ID,
13
+ MOTIVE_USE_ID],
14
+ doc)
@@ -0,0 +1,107 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from os import path
4
+ from subprocess import run
5
+ from tempfile import TemporaryDirectory
6
+ from uuid import uuid4
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class RGBAColor:
11
+ red: int
12
+ green: int
13
+ blue: int
14
+ alpha: float
15
+
16
+
17
+ class VerticalAlignment(Enum):
18
+ Center = 0x01
19
+ Top = 0x02
20
+ Bottom = 0x03
21
+
22
+
23
+ class HorizontalAlignment(Enum):
24
+ Center = 0x01
25
+ Left = 0x02
26
+ Right = 0x03
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class Alignment:
31
+ vertical: VerticalAlignment
32
+ horizontal: HorizontalAlignment
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class TextConfig:
37
+ text: str
38
+ fg_color: RGBAColor
39
+ bg_color: RGBAColor
40
+ font_name: str
41
+ alignment: Alignment
42
+ width: int
43
+ height: int
44
+
45
+
46
+ def render_text(config: TextConfig, resi_instance: object) -> bytes:
47
+ # see `convert -list gravity`
48
+ def _get_gravity(alignment: Alignment) -> str:
49
+ h = ''
50
+ v = ''
51
+
52
+ if alignment.horizontal == HorizontalAlignment.Left:
53
+ h = 'West'
54
+ elif alignment.horizontal == HorizontalAlignment.Right:
55
+ h = 'East'
56
+
57
+ if alignment.vertical == VerticalAlignment.Top:
58
+ v = 'North'
59
+ elif alignment.vertical == VerticalAlignment.Bottom:
60
+ v = 'South'
61
+
62
+ if h == '' and v == '':
63
+ return 'Center'
64
+
65
+ return '%s%s' % (v, h)
66
+
67
+ def _get_font(font_name, resi_instance):
68
+ fonts_dir = resi_instance.fonts_directory()
69
+
70
+ if fonts_dir is None or not path.exists(fonts_dir):
71
+ raise Exception('missing fonts directory')
72
+
73
+ return path.join(fonts_dir, font_name)
74
+
75
+ with TemporaryDirectory() as temp_dir:
76
+ out_filename = path.join(temp_dir, '%s.png' % uuid4())
77
+
78
+ text = config.text
79
+ draw_operators = (
80
+ ('-fill', _imagemagick_color_string(config.fg_color),
81
+ '-font', '%s' % _get_font(config.font_name, resi_instance),
82
+ '-gravity', _get_gravity(config.alignment),
83
+ 'label:%s' % text)
84
+ if text != ''
85
+ else ('xc:none',))
86
+
87
+ resp = run([
88
+ 'magick',
89
+ '-colorspace', 'RGB',
90
+ '-size', '%sx%s' % (config.width, config.height),
91
+ '-background', _imagemagick_color_string(config.bg_color),
92
+ *draw_operators,
93
+ 'PNG32:%s' % out_filename
94
+ ])
95
+
96
+ if resp.returncode != 0:
97
+ raise Exception('could not render text "%s"' % config.text)
98
+
99
+ with open(out_filename, 'rb') as f:
100
+ return f.read()
101
+
102
+
103
+ def _imagemagick_color_string(color: RGBAColor) -> str:
104
+ return 'rgba(%d, %d, %d, %0.2f)' % (color.red,
105
+ color.green,
106
+ color.blue,
107
+ color.alpha)
@@ -0,0 +1,59 @@
1
+ from papyru import XMLHelper
2
+ from papyru.create_variant.constants import (TITLE_ALIGNMENT,
3
+ TITLE_COLOR_BACKGROUND,
4
+ TITLE_COLOR_FOREGROUND,
5
+ TITLE_FONT, TITLE_PLACEHOLDER_ID)
6
+ from papyru.create_variant.resi import import_into_resi
7
+
8
+ from .render_text import TextConfig, render_text
9
+ from .xml import (ImageInfo, find_placeholder, fit_image,
10
+ replace_text_with_image)
11
+
12
+
13
+ def insert_title(doc: XMLHelper,
14
+ title: str,
15
+ disposition: str,
16
+ resi_instance: object,
17
+ resi_url: any
18
+ ) -> XMLHelper:
19
+ if title is None:
20
+ return doc
21
+
22
+ ph = find_placeholder(doc, 'text', TITLE_PLACEHOLDER_ID)
23
+
24
+ if ph is None:
25
+ return doc
26
+
27
+ width = float(doc.get_attribute(ph.wrapper, 'width'))
28
+ height = float(doc.get_attribute(ph.wrapper, 'height'))
29
+
30
+ title_location = _create_title_image(title, width, height, disposition,
31
+ resi_instance, resi_url)
32
+
33
+ return fit_image(
34
+ replace_text_with_image(doc, TITLE_PLACEHOLDER_ID),
35
+ TITLE_PLACEHOLDER_ID,
36
+ ImageInfo(location=title_location,
37
+ width=width,
38
+ height=height))
39
+
40
+
41
+ def _create_title_image(title: str,
42
+ width: int,
43
+ height: int,
44
+ disposition: str,
45
+ resi_instance: any,
46
+ resi_url: str
47
+ ) -> str:
48
+ text_bytes = render_text(
49
+ TextConfig(text=title,
50
+ width=int(width),
51
+ height=int(height),
52
+ alignment=TITLE_ALIGNMENT,
53
+ font_name=TITLE_FONT,
54
+ fg_color=TITLE_COLOR_FOREGROUND,
55
+ bg_color=TITLE_COLOR_BACKGROUND),
56
+ resi_instance)
57
+
58
+ return import_into_resi(
59
+ text_bytes, resi_instance, resi_url, None, disposition)
@@ -0,0 +1,126 @@
1
+ from dataclasses import dataclass
2
+ from xml.etree.ElementTree import Element, SubElement
3
+
4
+ from papyru import XMLHelper
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class PlaceholderInfo:
9
+ wrapper: Element
10
+ placeholder: Element
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class ImageInfo:
15
+ location: str
16
+ width: int
17
+ height: int
18
+
19
+
20
+ def find_placeholder(doc: XMLHelper,
21
+ placeholder_tag: str,
22
+ placeholder_id: str) -> PlaceholderInfo:
23
+
24
+ wrapper = doc.root.find('.//{%s}svg/{%s}%s[@id="%s"]/..'
25
+ % (doc.default_namespace,
26
+ doc.default_namespace,
27
+ placeholder_tag,
28
+ placeholder_id))
29
+
30
+ if wrapper is None:
31
+ return None
32
+
33
+ placeholder = wrapper.find('./{%s}%s[@id="%s"]'
34
+ % (doc.default_namespace,
35
+ placeholder_tag,
36
+ placeholder_id))
37
+
38
+ if placeholder is None:
39
+ return None
40
+
41
+ return PlaceholderInfo(wrapper=wrapper,
42
+ placeholder=placeholder)
43
+
44
+
45
+ def replace_text_with_image(doc: XMLHelper,
46
+ placeholder_id: str) -> XMLHelper:
47
+ ph = find_placeholder(doc, 'text', placeholder_id)
48
+
49
+ if ph is None:
50
+ return doc
51
+
52
+ ph.wrapper.remove(ph.placeholder)
53
+
54
+ replacement = SubElement(ph.wrapper,
55
+ '{%s}image' % doc.default_namespace)
56
+
57
+ doc.set_attribute(
58
+ replacement, 'id', doc.get_attribute(ph.placeholder, 'id'))
59
+
60
+ doc.set_attribute(replacement, 'x', '0')
61
+ doc.set_attribute(replacement, 'y', '0')
62
+
63
+ doc.set_attribute(
64
+ replacement, 'width', doc.get_attribute(ph.wrapper, 'width'))
65
+
66
+ doc.set_attribute(
67
+ replacement, 'height', doc.get_attribute(ph.wrapper, 'height'))
68
+
69
+ return doc
70
+
71
+
72
+ def fit_image(doc: XMLHelper,
73
+ placeholder_id: str,
74
+ image_info: ImageInfo) -> XMLHelper:
75
+ ph = find_placeholder(doc, 'image', placeholder_id)
76
+
77
+ if ph is None:
78
+ return doc
79
+
80
+ wrapper_dims = (float(doc.get_attribute(ph.wrapper, 'width')),
81
+ float(doc.get_attribute(ph.wrapper, 'height')))
82
+
83
+ image_dims = (float(image_info.width),
84
+ float(image_info.height))
85
+
86
+ viewbox = _fit_dimensions(wrapper_dims, image_dims)
87
+
88
+ doc.set_attribute(ph.wrapper,
89
+ 'viewBox',
90
+ '%s %s %s %s' % viewbox)
91
+
92
+ has_xlink = doc.namespace('xlink') is not None
93
+ doc.set_attribute(ph.placeholder,
94
+ 'href', image_info.location,
95
+ 'xlink' if has_xlink else None)
96
+
97
+ doc.set_attribute(ph.placeholder, 'x', '0.0')
98
+ doc.set_attribute(ph.placeholder, 'y', '0.0')
99
+ doc.set_attribute(ph.placeholder, 'width', '%s' % image_dims[0])
100
+ doc.set_attribute(ph.placeholder, 'height', '%s' % image_dims[1])
101
+
102
+ _set_changed(doc, ph.placeholder)
103
+
104
+ return doc
105
+
106
+
107
+ def _set_changed(document: XMLHelper, element: Element):
108
+ if document.has_attribute(element, 'is-unchanged', 'pap'):
109
+ document.set_attribute(element, 'is-unchanged', 'false', 'pap')
110
+
111
+
112
+ def _fit_dimensions(container_dims: (float, float),
113
+ element_dims: (float, float)
114
+ ) -> (float, float, float, float):
115
+ aspect_container = container_dims[0] / container_dims[1]
116
+ aspect_element = element_dims[0] / element_dims[1]
117
+
118
+ e_w = element_dims[0]
119
+ e_h = element_dims[1]
120
+
121
+ if aspect_container > aspect_element:
122
+ h = element_dims[0] / aspect_container
123
+ return (0.0, (e_h - h) / 2, e_w, h)
124
+ else:
125
+ w = element_dims[1] * aspect_container
126
+ return ((e_w - w) / 2, 0.0, w, e_h)
@@ -0,0 +1,25 @@
1
+ from os import getpid
2
+ from typing import Optional
3
+
4
+ import resi
5
+
6
+
7
+ def import_into_resi(data: bytes,
8
+ resi_instance: resi.Instance,
9
+ resi_url: str,
10
+ properties: Optional[resi.VersionProperties] = None,
11
+ disposition: Optional[str] = None
12
+ ) -> str:
13
+
14
+ if properties is not None:
15
+ properties.is_derived = False
16
+
17
+ with resi_instance.import_image_binary(data,
18
+ properties=properties,
19
+ track=str(getpid())) as img:
20
+ if disposition is not None:
21
+ img.set_properties(disposition=disposition)
22
+
23
+ location = '%s/images/%s' % (resi_url, img.ref_number)
24
+
25
+ return location
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: papyru
3
- Version: 2.10.4.dev77
3
+ Version: 2.11.0
4
4
  Summary: minimal REST library with OpenAPI-based validation for django
5
5
  Home-page: http://www.puzzleyou.net/
6
6
  Author: puzzleYOU GmbH
@@ -22,6 +22,17 @@ papyru.egg-info/SOURCES.txt
22
22
  papyru.egg-info/dependency_links.txt
23
23
  papyru.egg-info/requires.txt
24
24
  papyru.egg-info/top_level.txt
25
+ papyru/create_variant/__init__.py
26
+ papyru/create_variant/component.py
27
+ papyru/create_variant/constants.py
28
+ papyru/create_variant/resi.py
29
+ papyru/create_variant/edit_template/__init__.py
30
+ papyru/create_variant/edit_template/ean.py
31
+ papyru/create_variant/edit_template/manufacturer_address.py
32
+ papyru/create_variant/edit_template/motive.py
33
+ papyru/create_variant/edit_template/render_text.py
34
+ papyru/create_variant/edit_template/title.py
35
+ papyru/create_variant/edit_template/xml.py
25
36
  papyru/static/__init__.py
26
37
  papyru/static/apps.py
27
38
  papyru/static/urls.py
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name='papyru',
5
- version='2.10.4.dev77',
5
+ version='2.11.0',
6
6
  description=(
7
7
  'minimal REST library with OpenAPI-based validation for django'),
8
8
  author='puzzleYOU GmbH',
@@ -12,6 +12,8 @@ setup(
12
12
  platforms=['any'],
13
13
  packages=[
14
14
  'papyru',
15
+ 'papyru.create_variant',
16
+ 'papyru.create_variant.edit_template',
15
17
  'papyru.static',
16
18
  'papyru.varspool',
17
19
  'papyru.varspool.command'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes