Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat/init_marketplace'
Browse files Browse the repository at this point in the history
  • Loading branch information
SukiCZ committed Jan 28, 2024
2 parents 52f95e2 + 765ad21 commit 5fde02e
Show file tree
Hide file tree
Showing 8 changed files with 3,382 additions and 1,592 deletions.
15 changes: 15 additions & 0 deletions boardgamegeek/loaders/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ..objects.games import BoardGame
from ..utils import (
get_board_game_version_from_element,
get_marketplace_listing_from_element,
html_unescape,
xml_subelement_attr,
xml_subelement_attr_list,
Expand Down Expand Up @@ -117,6 +118,20 @@ def create_game_from_xml(xml_root, game_id):

data["versions"] = ver_list

# look for marketplace
marketplace = xml_root.find("marketplacelistings")
if marketplace is not None:
listings = []

for listing in marketplace.findall("listing"):
try:
ld = get_marketplace_listing_from_element(listing)
listings.append(ld)
except KeyError:
raise BGGApiError("malformed XML element ('marketplacelistings')")

data["marketplace"] = listings

# look for the statistics
stats = xml_root.find("statistics/ratings")
if stats is not None:
Expand Down
85 changes: 85 additions & 0 deletions boardgamegeek/objects/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,75 @@ def year(self):
return self._data.get("yearpublished")


class MarketplaceListing(DictObject):
"""
Object containing information about a marketplace listing
"""

def __init__(self, data):
kw = copy(data)
super().__init__(kw)

def _format(self, log):
log.info(f"listing date : {self.list_date}")
log.info(f"listing price : {self.price}")
log.info(f"listing currency : {self.currency}")
log.info(f"listing condition: {self.condition}")
log.info(f"listing notes : {self.notes}")
log.info(f"listing link : {self.link}")

@property
def list_date(self):
"""
:return: date when this listing was created
:rtype: datetime.datetime
:return: ``None`` on parse error
"""
return self._data.get("list_date")

@property
def price(self):
"""
:return: price of the item
:rtype: float
"""
return self._data.get("price")

@property
def currency(self):
"""
:return: ISO code of the currency (EUR, USD, etc.)
:rtype: string
"""
return self._data.get("currency")

@property
def condition(self):
"""
:return: condition of the item ((like)new, (very)good, acceptable, etc.)
:rtype: string
"""
return self._data.get("condition")

@property
def notes(self):
"""
:return: notes about the item
Example: "Game is in great shape, but the box has shelf-wear."
:rtype: string
"""
return self._data.get("notes")

@property
def link(self):
"""
:return: link to the item
Example: https://boardgamegeek.com/market/product/633634
:rtype: string
"""
return self._data.get("link")


class BaseGame(Thing):
def __init__(self, data):
self._thumbnail = fix_url(data["thumbnail"]) if "thumbnail" in data else None
Expand All @@ -443,6 +512,14 @@ def __init__(self, data):
except KeyError:
raise BGGError("invalid version data")

self._marketplace = []

for listing in data.get("marketplace", []):
try:
self._marketplace.append(MarketplaceListing(listing))
except KeyError:
raise BGGError("invalid marketplace data")

super().__init__(data)

@property
Expand Down Expand Up @@ -1117,6 +1194,14 @@ def versions(self):
"""
return self._versions

@property
def marketplace(self):
"""
:return: marketplace listings of this game
:rtype: list of :py:class:`boardgamegeek.game.MarketplaceListing`
"""
return self._marketplace

@property
def player_suggestions(self):
"""
Expand Down
23 changes: 22 additions & 1 deletion boardgamegeek/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
.. moduleauthor:: Cosmin Luță <[email protected]>
"""

import datetime
import html
import logging
import threading
Expand Down Expand Up @@ -443,3 +443,24 @@ def get_board_game_version_from_element(xml_elem):
)

return data


def get_marketplace_listing_from_element(xml_elem):
try:
list_date_string = xml_subelement_attr(xml_elem, "listdate")
list_date = datetime.datetime.strptime(
list_date_string, "%a, %d %b %Y %H:%M:%S %z"
)
except ValueError:
list_date = None

data = {
"list_date": list_date,
"price": xml_subelement_attr(xml_elem, "price", convert=float, default=0.0),
"currency": xml_subelement_attr(xml_elem, "price", attribute="currency"),
"condition": xml_subelement_attr(xml_elem, "condition"),
"notes": xml_subelement_attr(xml_elem, "notes"),
"link": xml_subelement_attr(xml_elem, "link", attribute="href"),
}

return data
21 changes: 19 additions & 2 deletions test/test_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
BoardGameRank,
BoardGameVersion,
BoardGameVideo,
MarketplaceListing,
PlayerSuggestion,
)

Expand Down Expand Up @@ -146,6 +147,17 @@ def check_game(game):
# should have found suggestions for all number of players
assert not len(suggestions_not_found)

# check marketplace listings were retrieved
assert isinstance(game.marketplace, list)
for listing in game.marketplace:
assert isinstance(listing, MarketplaceListing)
assert isinstance(listing.price, float)
assert isinstance(listing.currency, str)
assert isinstance(listing.condition, str)
assert isinstance(listing.link, str)
assert isinstance(listing.list_date, datetime.datetime)
assert isinstance(listing.notes, str)

# make sure no exception gets thrown
repr(game)

Expand All @@ -155,7 +167,9 @@ def test_get_known_game_info(bgg, mocker, null_logger):
mock_get.side_effect = _common.simulate_bgg

# use an older game that's not so likely to change
game = bgg.game(_common.TEST_GAME_NAME, videos=True, versions=True)
game = bgg.game(
_common.TEST_GAME_NAME, videos=True, versions=True, marketplace=True
)

check_game(game)

Expand All @@ -169,7 +183,9 @@ def test_get_known_game_info_by_id(bgg, mocker):
mock_get = mocker.patch("requests.sessions.Session.get")
mock_get.side_effect = _common.simulate_bgg

game = bgg.game(None, game_id=_common.TEST_GAME_ID, videos=True, versions=True)
game = bgg.game(
None, game_id=_common.TEST_GAME_ID, videos=True, versions=True, marketplace=True
)
check_game(game)


Expand All @@ -181,6 +197,7 @@ def test_get_known_game_info_by_id_list(bgg, mocker):
game_id_list=[_common.TEST_GAME_ID, _common.TEST_GAME_ID_2],
videos=True,
versions=True,
marketplace=True,
)
check_game(game_list[0])

Expand Down
Loading

0 comments on commit 5fde02e

Please sign in to comment.