amazon-sp-cli 0.2.3__tar.gz → 0.2.5__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.
- {amazon_sp_cli-0.2.3/amazon_sp_cli.egg-info → amazon_sp_cli-0.2.5}/PKG-INFO +1 -1
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/README.md +2 -3
- amazon_sp_cli-0.2.5/amazon_sp_cli/__init__.py +2 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/models/a_plus.py +72 -19
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5/amazon_sp_cli.egg-info}/PKG-INFO +1 -1
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_a_plus.py +107 -4
- amazon_sp_cli-0.2.3/amazon_sp_cli/__init__.py +0 -2
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/LICENSE +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/MANIFEST.in +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/__main__.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/auth.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/cli.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/client.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/__init__.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/a_plus.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/auth.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/commands/pricing.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/main.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli/models/__init__.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/SOURCES.txt +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/dependency_links.txt +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/entry_points.txt +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/requires.txt +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/amazon_sp_cli.egg-info/top_level.txt +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/pyproject.toml +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/setup.cfg +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/setup.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/__init__.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_auth.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_client.py +0 -0
- {amazon_sp_cli-0.2.3 → amazon_sp_cli-0.2.5}/tests/test_pricing.py +0 -0
|
@@ -120,10 +120,9 @@ python3 -m amazon_sp_cli get-price PAW2603190101-BLU
|
|
|
120
120
|
|
|
121
121
|
This repository uses automated releases. To publish a new version:
|
|
122
122
|
|
|
123
|
-
1. Open a **standalone PR** that only bumps `
|
|
123
|
+
1. Open a **standalone PR** that only bumps `__version__` in `amazon_sp_cli/__init__.py`.
|
|
124
124
|
2. The PR must not include code, test, or documentation changes — a CI check enforces this.
|
|
125
|
-
3. Once the PR is merged to `main`, the `Auto Release` workflow
|
|
126
|
-
4. The tag push triggers the `Publish to PyPI` workflow, which runs the full test suite and publishes the package.
|
|
125
|
+
3. Once the PR is merged to `main`, the `Auto Release` workflow runs the full test suite, creates a tag (e.g. `v0.2.1`), creates a GitHub release, and publishes to PyPI.
|
|
127
126
|
|
|
128
127
|
Do not create tags manually.
|
|
129
128
|
|
|
@@ -18,14 +18,26 @@ class TextComponent:
|
|
|
18
18
|
class ImageComponent:
|
|
19
19
|
"""Image component for A+ Content modules."""
|
|
20
20
|
|
|
21
|
-
def __init__(
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
upload_destination_id: str,
|
|
24
|
+
image_crop: dict = None,
|
|
25
|
+
alt_text: str = None,
|
|
26
|
+
image_crop_specification: dict = None,
|
|
27
|
+
):
|
|
22
28
|
self.upload_destination_id = upload_destination_id
|
|
23
29
|
self.image_crop = image_crop
|
|
30
|
+
self.alt_text = alt_text
|
|
31
|
+
self.image_crop_specification = image_crop_specification
|
|
24
32
|
|
|
25
33
|
def to_dict(self) -> dict:
|
|
26
34
|
result = {"uploadDestinationId": self.upload_destination_id}
|
|
27
35
|
if self.image_crop:
|
|
28
36
|
result["imageCrop"] = self.image_crop
|
|
37
|
+
if self.alt_text:
|
|
38
|
+
result["altText"] = self.alt_text
|
|
39
|
+
if self.image_crop_specification:
|
|
40
|
+
result["imageCropSpecification"] = self.image_crop_specification
|
|
29
41
|
return result
|
|
30
42
|
|
|
31
43
|
|
|
@@ -141,19 +153,23 @@ class StandardTextModule:
|
|
|
141
153
|
class StandardImageTextOverlayModule:
|
|
142
154
|
"""Standard Image with Text Overlay module."""
|
|
143
155
|
|
|
144
|
-
def __init__(self, headline=None, image=None, body=None):
|
|
156
|
+
def __init__(self, headline=None, image=None, body=None, overlay_color_type="DARK"):
|
|
145
157
|
self.headline = headline
|
|
146
158
|
self.image = image
|
|
147
159
|
self.body = body
|
|
160
|
+
self.overlay_color_type = overlay_color_type
|
|
148
161
|
|
|
149
162
|
def to_dict(self) -> dict:
|
|
150
|
-
result = {}
|
|
163
|
+
result = {"overlayColorType": self.overlay_color_type}
|
|
164
|
+
block = {}
|
|
151
165
|
if self.headline:
|
|
152
|
-
|
|
166
|
+
block["headline"] = self.headline.to_dict()
|
|
153
167
|
if self.image:
|
|
154
|
-
|
|
168
|
+
block["image"] = self.image.to_dict()
|
|
155
169
|
if self.body:
|
|
156
|
-
|
|
170
|
+
block["body"] = self.body.to_dict()
|
|
171
|
+
if block:
|
|
172
|
+
result["block"] = block
|
|
157
173
|
return result
|
|
158
174
|
|
|
159
175
|
|
|
@@ -166,7 +182,7 @@ class StandardCompanyLogoModule:
|
|
|
166
182
|
def to_dict(self) -> dict:
|
|
167
183
|
result = {}
|
|
168
184
|
if self.company_logo:
|
|
169
|
-
result["companyLogo"] =
|
|
185
|
+
result["companyLogo"] = self.company_logo.to_dict()
|
|
170
186
|
return result
|
|
171
187
|
|
|
172
188
|
|
|
@@ -174,7 +190,7 @@ class ContentModule:
|
|
|
174
190
|
"""A+ Content module wrapper."""
|
|
175
191
|
|
|
176
192
|
MODULE_TYPES = {
|
|
177
|
-
"STANDARD_IMAGE_TEXT": "
|
|
193
|
+
"STANDARD_IMAGE_TEXT": "standardImageTextOverlay",
|
|
178
194
|
"STANDARD_SINGLE_IMAGE": "standardSingleImage",
|
|
179
195
|
"STANDARD_MULTIPLE_IMAGE_TEXT": "standardMultipleImageText",
|
|
180
196
|
"STANDARD_FOUR_IMAGE_TEXT": "standardFourImageText",
|
|
@@ -210,8 +226,8 @@ class ContentModule:
|
|
|
210
226
|
result = {"contentModuleType": self.module_type}
|
|
211
227
|
field_name = self.MODULE_TYPES.get(self.module_type)
|
|
212
228
|
|
|
213
|
-
if field_name == "
|
|
214
|
-
result["
|
|
229
|
+
if field_name == "standardImageTextOverlay" and self.standard_image_text_overlay:
|
|
230
|
+
result["standardImageTextOverlay"] = self.standard_image_text_overlay.to_dict()
|
|
215
231
|
elif field_name == "standardSingleImage" and self.standard_single_image:
|
|
216
232
|
result["standardSingleImage"] = self.standard_single_image.to_dict()
|
|
217
233
|
elif field_name == "standardMultipleImageText" and self.standard_multiple_image_text:
|
|
@@ -222,8 +238,6 @@ class ContentModule:
|
|
|
222
238
|
result["standardComparisonTable"] = self.standard_comparison_table.to_dict()
|
|
223
239
|
elif field_name == "standardText" and self.standard_text:
|
|
224
240
|
result["standardText"] = self.standard_text.to_dict()
|
|
225
|
-
elif field_name == "standardImageTextOverlay" and self.standard_image_text_overlay:
|
|
226
|
-
result["standardImageTextOverlay"] = self.standard_image_text_overlay.to_dict()
|
|
227
241
|
elif field_name == "standardCompanyLogo" and self.standard_company_logo:
|
|
228
242
|
result["standardCompanyLogo"] = self.standard_company_logo.to_dict()
|
|
229
243
|
|
|
@@ -308,19 +322,38 @@ def build_module_from_json(data: dict) -> ContentModule:
|
|
|
308
322
|
module_type = data.get("contentModuleType") or data.get("moduleType", "STANDARD_TEXT")
|
|
309
323
|
|
|
310
324
|
if module_type == "STANDARD_IMAGE_TEXT":
|
|
325
|
+
body = None
|
|
326
|
+
if data.get("body"):
|
|
327
|
+
body = ParagraphComponent(text_list=[TextComponent(data["body"])])
|
|
311
328
|
return ContentModule(
|
|
312
329
|
module_type=module_type,
|
|
313
|
-
|
|
330
|
+
standard_image_text_overlay=StandardImageTextOverlayModule(
|
|
314
331
|
headline=TextComponent(data["headline"]) if data.get("headline") else None,
|
|
315
|
-
body=
|
|
316
|
-
image=
|
|
332
|
+
body=body,
|
|
333
|
+
image=(
|
|
334
|
+
ImageComponent(
|
|
335
|
+
data["imageId"],
|
|
336
|
+
alt_text=data.get("altText"),
|
|
337
|
+
image_crop_specification=data.get("imageCropSpecification"),
|
|
338
|
+
)
|
|
339
|
+
if data.get("imageId")
|
|
340
|
+
else None
|
|
341
|
+
),
|
|
317
342
|
),
|
|
318
343
|
)
|
|
319
344
|
elif module_type == "STANDARD_SINGLE_IMAGE":
|
|
320
345
|
return ContentModule(
|
|
321
346
|
module_type=module_type,
|
|
322
347
|
standard_single_image=StandardSingleImageModule(
|
|
323
|
-
image=
|
|
348
|
+
image=(
|
|
349
|
+
ImageComponent(
|
|
350
|
+
data["imageId"],
|
|
351
|
+
alt_text=data.get("altText"),
|
|
352
|
+
image_crop_specification=data.get("imageCropSpecification"),
|
|
353
|
+
)
|
|
354
|
+
if data.get("imageId")
|
|
355
|
+
else None
|
|
356
|
+
),
|
|
324
357
|
image_caption=TextComponent(data["caption"]) if data.get("caption") else None,
|
|
325
358
|
),
|
|
326
359
|
)
|
|
@@ -360,19 +393,39 @@ def build_module_from_json(data: dict) -> ContentModule:
|
|
|
360
393
|
),
|
|
361
394
|
)
|
|
362
395
|
elif module_type == "STANDARD_IMAGE_TEXT_OVERLAY":
|
|
396
|
+
body = None
|
|
397
|
+
if data.get("body"):
|
|
398
|
+
body = ParagraphComponent(text_list=[TextComponent(data["body"])])
|
|
363
399
|
return ContentModule(
|
|
364
400
|
module_type=module_type,
|
|
365
401
|
standard_image_text_overlay=StandardImageTextOverlayModule(
|
|
366
402
|
headline=TextComponent(data["headline"]) if data.get("headline") else None,
|
|
367
|
-
body=
|
|
368
|
-
image=
|
|
403
|
+
body=body,
|
|
404
|
+
image=(
|
|
405
|
+
ImageComponent(
|
|
406
|
+
data["imageId"],
|
|
407
|
+
alt_text=data.get("altText"),
|
|
408
|
+
image_crop_specification=data.get("imageCropSpecification"),
|
|
409
|
+
)
|
|
410
|
+
if data.get("imageId")
|
|
411
|
+
else None
|
|
412
|
+
),
|
|
413
|
+
overlay_color_type=data.get("overlayColorType", "DARK"),
|
|
369
414
|
),
|
|
370
415
|
)
|
|
371
416
|
elif module_type == "STANDARD_COMPANY_LOGO":
|
|
372
417
|
return ContentModule(
|
|
373
418
|
module_type=module_type,
|
|
374
419
|
standard_company_logo=StandardCompanyLogoModule(
|
|
375
|
-
company_logo=
|
|
420
|
+
company_logo=(
|
|
421
|
+
ImageComponent(
|
|
422
|
+
data["imageId"],
|
|
423
|
+
alt_text=data.get("altText"),
|
|
424
|
+
image_crop_specification=data.get("imageCropSpecification"),
|
|
425
|
+
)
|
|
426
|
+
if data.get("imageId")
|
|
427
|
+
else None
|
|
428
|
+
),
|
|
376
429
|
),
|
|
377
430
|
)
|
|
378
431
|
else:
|
|
@@ -75,7 +75,7 @@ class TestDataModels:
|
|
|
75
75
|
assert "has no content" in doc.validate()[0]
|
|
76
76
|
|
|
77
77
|
def test_module_validate_empty_content(self):
|
|
78
|
-
mod = ContentModule("
|
|
78
|
+
mod = ContentModule("STANDARD_TEXT")
|
|
79
79
|
issues = mod.validate(0)
|
|
80
80
|
assert len(issues) == 1
|
|
81
81
|
assert "has no content" in issues[0]
|
|
@@ -113,11 +113,24 @@ class TestBuildFromJson:
|
|
|
113
113
|
data = {
|
|
114
114
|
"contentModuleType": "STANDARD_COMPANY_LOGO",
|
|
115
115
|
"imageId": "logo-123",
|
|
116
|
+
"altText": "Company Logo",
|
|
117
|
+
"imageCropSpecification": {
|
|
118
|
+
"size": {
|
|
119
|
+
"width": {"value": 600, "units": "pixels"},
|
|
120
|
+
"height": {"value": 180, "units": "pixels"},
|
|
121
|
+
},
|
|
122
|
+
"offset": {
|
|
123
|
+
"x": {"value": 0, "units": "pixels"},
|
|
124
|
+
"y": {"value": 0, "units": "pixels"},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
116
127
|
}
|
|
117
128
|
mod = build_module_from_json(data)
|
|
118
129
|
assert mod.module_type == "STANDARD_COMPANY_LOGO"
|
|
119
130
|
result = mod.to_dict()
|
|
120
|
-
assert result["standardCompanyLogo"]["companyLogo"]["
|
|
131
|
+
assert result["standardCompanyLogo"]["companyLogo"]["uploadDestinationId"] == "logo-123"
|
|
132
|
+
assert result["standardCompanyLogo"]["companyLogo"]["altText"] == "Company Logo"
|
|
133
|
+
assert result["standardCompanyLogo"]["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
|
|
121
134
|
|
|
122
135
|
def test_standard_company_logo_module_to_dict_empty(self):
|
|
123
136
|
mod = StandardCompanyLogoModule()
|
|
@@ -133,8 +146,25 @@ class TestBuildFromJson:
|
|
|
133
146
|
mod = build_module_from_json(data)
|
|
134
147
|
assert mod.module_type == "STANDARD_IMAGE_TEXT"
|
|
135
148
|
result = mod.to_dict()
|
|
136
|
-
assert result["
|
|
137
|
-
assert result["
|
|
149
|
+
assert result["standardImageTextOverlay"]["overlayColorType"] == "DARK"
|
|
150
|
+
assert result["standardImageTextOverlay"]["block"]["headline"]["value"] == "H"
|
|
151
|
+
assert result["standardImageTextOverlay"]["block"]["body"]["textList"][0]["value"] == "B"
|
|
152
|
+
assert result["standardImageTextOverlay"]["block"]["image"]["uploadDestinationId"] == "img-1"
|
|
153
|
+
|
|
154
|
+
def test_build_module_image_text_overlay(self):
|
|
155
|
+
data = {
|
|
156
|
+
"moduleType": "STANDARD_IMAGE_TEXT_OVERLAY",
|
|
157
|
+
"headline": "H",
|
|
158
|
+
"body": "B",
|
|
159
|
+
"imageId": "img-1",
|
|
160
|
+
"overlayColorType": "LIGHT",
|
|
161
|
+
}
|
|
162
|
+
mod = build_module_from_json(data)
|
|
163
|
+
assert mod.module_type == "STANDARD_IMAGE_TEXT_OVERLAY"
|
|
164
|
+
result = mod.to_dict()
|
|
165
|
+
assert result["standardImageTextOverlay"]["overlayColorType"] == "LIGHT"
|
|
166
|
+
assert result["standardImageTextOverlay"]["block"]["headline"]["value"] == "H"
|
|
167
|
+
assert result["standardImageTextOverlay"]["block"]["body"]["textList"][0]["value"] == "B"
|
|
138
168
|
|
|
139
169
|
def test_build_module_comparison_table(self):
|
|
140
170
|
data = {
|
|
@@ -148,6 +178,79 @@ class TestBuildFromJson:
|
|
|
148
178
|
assert result["standardComparisonTable"]["headline"]["value"] == "Compare"
|
|
149
179
|
assert result["standardComparisonTable"]["comparisonTableRows"][0]["name"] == "Feature"
|
|
150
180
|
|
|
181
|
+
def test_integration_full_document_from_json(self):
|
|
182
|
+
data = {
|
|
183
|
+
"modules": [
|
|
184
|
+
{
|
|
185
|
+
"moduleType": "STANDARD_COMPANY_LOGO",
|
|
186
|
+
"imageId": "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg",
|
|
187
|
+
"altText": "Pawified Logo",
|
|
188
|
+
"imageCropSpecification": {
|
|
189
|
+
"size": {
|
|
190
|
+
"width": {"value": 600, "units": "pixels"},
|
|
191
|
+
"height": {"value": 180, "units": "pixels"},
|
|
192
|
+
},
|
|
193
|
+
"offset": {
|
|
194
|
+
"x": {"value": 0, "units": "pixels"},
|
|
195
|
+
"y": {"value": 0, "units": "pixels"},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"moduleType": "STANDARD_IMAGE_TEXT",
|
|
201
|
+
"headline": "Pure Joy in Every Bite",
|
|
202
|
+
"body": "Treat your feline friend to the finest premium catnip...",
|
|
203
|
+
"imageId": "aplus-media/sc/77967e76-a03c-4514-85e1-371e7f2395d1.jpg",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"moduleType": "STANDARD_IMAGE_TEXT",
|
|
207
|
+
"headline": "100% Natural & Safe",
|
|
208
|
+
"body": "Made in the USA with all-natural...",
|
|
209
|
+
"imageId": "aplus-media/sc/215948ec-b306-4a0b-8491-f1342dc83f91.jpg",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"moduleType": "STANDARD_IMAGE_TEXT",
|
|
213
|
+
"headline": "Perfect for Playtime",
|
|
214
|
+
"body": "Each 2-pack includes convenient 5g sachets...",
|
|
215
|
+
"imageId": "aplus-media/sc/2e6493d5-e7a4-4c3a-8e47-a78e5f5b4802.jpg",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"moduleType": "STANDARD_IMAGE_TEXT",
|
|
219
|
+
"headline": "Premium Quality You Can Trust",
|
|
220
|
+
"body": "Pawified is committed to delivering premium pet products...",
|
|
221
|
+
"imageId": "aplus-media/sc/ef864286-0fec-4472-ad20-8b7dfee4922e.jpg",
|
|
222
|
+
},
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
doc = build_content_from_json("pawified-catnip", data)
|
|
226
|
+
assert doc.name == "pawified-catnip"
|
|
227
|
+
assert len(doc.content_module_list) == 5
|
|
228
|
+
assert doc.content_module_list[0].module_type == "STANDARD_COMPANY_LOGO"
|
|
229
|
+
assert doc.content_module_list[1].module_type == "STANDARD_IMAGE_TEXT"
|
|
230
|
+
|
|
231
|
+
result = doc.to_dict()
|
|
232
|
+
modules = result["contentModuleList"]
|
|
233
|
+
assert len(modules) == 5
|
|
234
|
+
|
|
235
|
+
logo_module = modules[0]["standardCompanyLogo"]
|
|
236
|
+
assert "image" not in logo_module["companyLogo"]
|
|
237
|
+
assert (
|
|
238
|
+
logo_module["companyLogo"]["uploadDestinationId"]
|
|
239
|
+
== "aplus-media/sc/76a7fb65-607b-4e96-a3fa-a145d1397725.jpg"
|
|
240
|
+
)
|
|
241
|
+
assert logo_module["companyLogo"]["altText"] == "Pawified Logo"
|
|
242
|
+
assert logo_module["companyLogo"]["imageCropSpecification"]["size"]["width"]["value"] == 600
|
|
243
|
+
|
|
244
|
+
for i in range(1, 5):
|
|
245
|
+
mod = modules[i]["standardImageTextOverlay"]
|
|
246
|
+
assert mod["overlayColorType"] == "DARK"
|
|
247
|
+
assert "block" in mod
|
|
248
|
+
assert "headline" in mod["block"]
|
|
249
|
+
assert "body" in mod["block"]
|
|
250
|
+
assert "textList" in mod["block"]["body"]
|
|
251
|
+
assert "image" in mod["block"]
|
|
252
|
+
assert mod["block"]["image"]["uploadDestinationId"].startswith("aplus-media/sc/")
|
|
253
|
+
|
|
151
254
|
|
|
152
255
|
class TestAPlusCLI:
|
|
153
256
|
@pytest.fixture
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|