Skip to content
This repository has been archived by the owner on Sep 10, 2021. It is now read-only.

Commit

Permalink
Merge pull request #2 from stefanitsky/xml-parse
Browse files Browse the repository at this point in the history
🔀 0.4.0 release
  • Loading branch information
stefanitsky committed Apr 1, 2020
2 parents 46150f5 + db84701 commit 2934df2
Show file tree
Hide file tree
Showing 34 changed files with 606 additions and 89 deletions.
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
History
=======

0.4.0 (2020-04-01)
------------------
* Added xml parsing for all models, except: Gitfts, Promos and another types of offers like audiobooks, medicine etc.
* Fixed fields parsing for datetime fields & fields that can be None.
* Added new field for offer: supplier.

0.3.0 (2020-03-30)
------------------

Expand Down
13 changes: 13 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ Or XML element::
>>> from xml.etree import ElementTree as ET
>>> ET.tostring(el)
b'<category id="1">Shoes</category>'


Parser
--------

You can parse XML files into ready-to-use Feed model instance with parser::

>>> from yandex_market_language import parser
>>> p = parser.YMLParser("./tests/fixtures/valid_feed.xml")
>>> feed = p.parse()
>>> feed
<yandex_market_language.models.feed.Feed object at 0x107724370>
>>> feed.to_dict()
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.0
current_version = 0.4.0
commit = True
tag = True
message = 🔖 Bump version: {current_version} → {new_version}
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@
test_suite='tests',
tests_require=test_requirements,
url='https://github.com/stefanitsky/yandex_market_language',
version='0.3.0',
version='0.4.0',
zip_safe=False,
)
3 changes: 3 additions & 0 deletions tests/factories/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def __init__(
bid=str(fake.pyint()),
url=fake.url(),
price=PriceFactory(),
supplier=str(fake.pyint()),
old_price=str(fake.pyint()),
enable_auto_discounts=fake.pybool(),
currency=fake.random_element(CURRENCY_CHOICES),
Expand Down Expand Up @@ -83,6 +84,7 @@ def __init__(
self.currency = currency
self.category_id = category_id
self.pictures = pictures
self.supplier = supplier
self.delivery = delivery
self.pickup = pickup
self.delivery_options = delivery_options
Expand Down Expand Up @@ -119,6 +121,7 @@ def get_values(self, **kwargs) -> dict:
currency=self.currency,
category_id=self.category_id,
pictures=self.pictures,
supplier=self.supplier,
delivery=self.delivery,
pickup=self.pickup,
delivery_options=self.delivery_options,
Expand Down
15 changes: 14 additions & 1 deletion tests/factories/shop.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
from .currency import CurrencyFactory
from .category import CategoryFactory
from .option import OptionFactory
from .offers import (
SimplifiedOfferFactory,
ArbitraryOfferFactory,
BookOfferFactory
)

fake = Faker()

Expand All @@ -20,7 +25,8 @@ def generate_random_shop(
categories=None,
delivery_options=None,
pickup_options=None,
enable_auto_discounts=fake.pybool()
enable_auto_discounts=fake.pybool(),
offers=None,
):
if currencies is None:
currencies = [CurrencyFactory() for _ in range(3)]
Expand All @@ -30,6 +36,12 @@ def generate_random_shop(
delivery_options = [OptionFactory() for _ in range(3)]
if pickup_options is None:
pickup_options = [OptionFactory() for _ in range(3)]
if offers is None:
offers = [
SimplifiedOfferFactory().create(),
ArbitraryOfferFactory().create(),
BookOfferFactory().create(),
]

return Shop(
name=name,
Expand All @@ -44,6 +56,7 @@ def generate_random_shop(
delivery_options=delivery_options,
pickup_options=pickup_options,
enable_auto_discounts=enable_auto_discounts,
offers=offers,
)


Expand Down
73 changes: 53 additions & 20 deletions tests/fixtures/valid_feed.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,38 @@
<weight>3.6</weight>
<dimensions>20.1/20.551/22.5</dimensions>
</offer>
<offer id="A1VV" type="vendor.model" bid="30">
<typePrefix>Сэндвичница</typePrefix>
<vendor>Brand</vendor>
<model>K220Y9</model>
<vendorCode>A1234567B</vendorCode>
<url>http://best.seller.ru/product_page.asp?pid=12345</url>
<price>1099</price>
<oldprice>1399</oldprice>
<enable_auto_discounts>false</enable_auto_discounts>
<currencyId>RUR</currencyId>
<categoryId>101</categoryId>
<picture>http://best.seller.ru/img/device56789.jpg</picture>
<supplier ogrn="5634567890123"/>
<delivery>true</delivery>
<pickup>true</pickup>
<delivery-options>
<option cost="300" days="1-3"/>
</delivery-options>
<pickup-options>
<option cost="350" days="1" order-before="12"/>
</pickup-options>
<param name="Мощность">750 Вт</param>
<description>Сэндвичница 2 в 1: можно приготовить как сэндвичи, так и вафли.</description>
<sales_notes>Наличные, Visa/Mastercard, б/н расчет</sales_notes>
<store>false</store>
<manufacturer_warranty>true</manufacturer_warranty>
<min-quantity>1</min-quantity>
<country_of_origin>Россия</country_of_origin>
<barcode>9876543210</barcode>
<weight>1.03</weight>
<dimensions>20.800/23.500/9.000</dimensions>
</offer>
<offer id="755B" type="book" bid="60">
<url>https://example.shop/product_page.asp?pid=2</url>
<price>80</price>
Expand All @@ -97,6 +129,7 @@
<name>Все не так. В 2 томах. Том 1</name>
<publisher>Эксмо</publisher>
<series>А. Маринина — королева детектива</series>
<min-quantity>1</min-quantity>
<year>2007</year>
<ISBN>978-5-699-23647-3</ISBN>
<volume>2</volume>
Expand Down Expand Up @@ -200,25 +233,25 @@
<age>6</age>
</offer>
</offers>
<gifts>
<gift id="33">
<name>Кружка 300 мл Brand 16</name>
<picture>https://example.shop/promos/33.jpg</picture>
</gift>
</gifts>
<promos>
<promo id="PromoGift" type="gift with purchase">
<start-date>2020-02-01 09:00:00</start-date>
<end-date>2020-03-01 22:00:00</end-date>
<description>Купите телефон марки Xiaomi и получите кружку в подарок.</description>
<url>http://example.com/promos/gift</url>
<purchase>
<product offer-id="1511AB"/>
</purchase>
<promo-gifts>
<promo-gift gift-id="33"/>
</promo-gifts>
</promo>
</promos>
<!-- <gifts>-->
<!-- <gift id="33">-->
<!-- <name>Кружка 300 мл Brand 16</name>-->
<!-- <picture>https://example.shop/promos/33.jpg</picture>-->
<!-- </gift>-->
<!-- </gifts>-->
<!-- <promos>-->
<!-- <promo id="PromoGift" type="gift with purchase">-->
<!-- <start-date>2020-02-01 09:00:00</start-date>-->
<!-- <end-date>2020-03-01 22:00:00</end-date>-->
<!-- <description>Купите телефон марки Xiaomi и получите кружку в подарок.</description>-->
<!-- <url>http://example.com/promos/gift</url>-->
<!-- <purchase>-->
<!-- <product offer-id="1511AB"/>-->
<!-- </purchase>-->
<!-- <promo-gifts>-->
<!-- <promo-gift gift-id="33"/>-->
<!-- </promo-gifts>-->
<!-- </promo>-->
<!-- </promos>-->
</shop>
</yml_catalog>
7 changes: 7 additions & 0 deletions tests/models/test_age.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from tests.factories import AgeFactory
from yandex_market_language import models
from yandex_market_language.exceptions import ValidationError
from yandex_market_language.models import Age


class AgeModelTestCase(ModelTestCase):
Expand Down Expand Up @@ -51,3 +52,9 @@ def test_value_wrong_month_choice(self):
with self.assertRaises(ValidationError) as e:
AgeFactory(unit="month", value=13)
self.assertEqual(str(e), expected_error)

def test_from_xml(self):
a = AgeFactory()
el = a.to_xml()
parsed_a = Age.from_xml(el)
self.assertEqual(a.to_dict(), parsed_a.to_dict())
13 changes: 13 additions & 0 deletions tests/models/test_base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from unittest import mock
from tests.cases import ModelTestCase, ET, fake
from yandex_market_language import models
Expand Down Expand Up @@ -84,3 +85,15 @@ def test_str_to_bool(self):
self.assertEqual(m("true"), True)
self.assertEqual(m("false"), False)
self.assertEqual(m("none"), None)

def test_is_valid_datetime(self):
dt_format = "%Y-%m-%d %H:%M"
m = models.BaseModel._is_valid_datetime
dt = datetime.now()
str_dt = dt.strftime(dt_format)
self.assertEqual(m(dt, dt_format, "test"), dt.strftime(dt_format))
self.assertEqual(m(str_dt, dt_format, "test"), str_dt)
self.assertEqual(m(None, dt_format, "test", allow_none=True), None)
with self.assertRaises(ValidationError) as e:
m(None, dt_format, "test")
self.assertEqual(str(e), "test must be a valid datetime")
7 changes: 7 additions & 0 deletions tests/models/test_category.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tests.cases import ModelTestCase, ET
from tests.factories import CategoryFactory
from yandex_market_language.models import Category


class CategoryModelTestCase(ModelTestCase):
Expand All @@ -17,3 +18,9 @@ def test_to_xml(self):
expected_el = ET.Element("category", {"id": c.category_id})
expected_el.text = c.name
self.assertElementsEquals(el, expected_el)

def test_from_xml(self):
c = CategoryFactory()
el = c.to_xml()
parsed_c = Category.from_xml(el)
self.assertEqual(c.to_dict(), parsed_c.to_dict())
7 changes: 7 additions & 0 deletions tests/models/test_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from tests.factories import ConditionFactory
from yandex_market_language import models
from yandex_market_language.exceptions import ValidationError
from yandex_market_language.models import Condition


class ConditionModelTestCase(ModelTestCase):
Expand Down Expand Up @@ -31,3 +32,9 @@ def test_condition_type_property_raises_validation_error(self):
with self.assertRaises(ValidationError) as e:
ConditionFactory(condition_type="err")
self.assertEqual(str(e), expected_message)

def test_from_xml(self):
c = ConditionFactory()
el = c.to_xml()
parsed_c = Condition.from_xml(el)
self.assertEqual(c.to_dict(), parsed_c.to_dict())
10 changes: 6 additions & 4 deletions tests/models/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from tests.factories import CurrencyFactory
from yandex_market_language import models
from yandex_market_language.exceptions import ValidationError
from yandex_market_language.models import Currency


class CurrencyModelTestCase(ModelTestCase):
Expand Down Expand Up @@ -47,7 +48,8 @@ def test_rate_validation_error(self):
CurrencyFactory(rate="err")
self.assertEqual(str(e), msg)

def test_plus_validation_error(self):
with self.assertRaises(ValidationError) as e:
CurrencyFactory(plus="err")
self.assertEqual(str(e), "The plus parameter only can be int.")
def test_from_xml(self):
c = CurrencyFactory()
el = c.to_xml()
parsed_c = Currency.from_xml(el)
self.assertEqual(c.to_dict(), parsed_c.to_dict())
7 changes: 7 additions & 0 deletions tests/models/test_dimensions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from tests.cases import ModelTestCase, ET
from tests.factories import DimensionsFactory
from yandex_market_language.exceptions import ValidationError
from yandex_market_language.models import Dimensions


class DimensionsModelTestCase(ModelTestCase):
Expand All @@ -25,3 +26,9 @@ def test_value_is_float_raises_validation_error(self):
with self.assertRaises(ValidationError) as e:
DimensionsFactory(length="err")
self.assertEqual(str(e), "length must be a valid float")

def test_from_xml(self):
d = DimensionsFactory()
el = d.to_xml()
parsed_d = Dimensions.from_xml(el)
self.assertEqual(d.to_dict(), parsed_d.to_dict())
14 changes: 13 additions & 1 deletion tests/models/test_feed.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest import mock

from tests.cases import ModelTestCase
from tests.factories import ShopFactory
from yandex_market_language import models
Expand All @@ -19,4 +21,14 @@ def test_to_xml(self):
self.assertEqual(list(el.tag for el in feed_el), ["shop"])
self.assertElementsEquals(feed_el[0], shop.to_xml())
self.assertEqual(feed_el.tag, "yml_catalog")
self.assertEqual(feed_el.get("date"), feed.date)
self.assertEqual(feed_el.get("date"), feed._date)

@mock.patch("yandex_market_language.models.Shop.from_xml")
def test_from_xml(self, p):
shop = ShopFactory()
feed = models.Feed(shop)
feed_el = feed.to_xml()
p.return_value = shop
parsed_feed = models.Feed.from_xml(feed_el)
self.assertEqual(p.call_count, 1)
self.assertEqual(feed.to_dict(), parsed_feed.to_dict())
Loading

0 comments on commit 2934df2

Please sign in to comment.