Note: we'll be using our home-built API connector Create an API connector. We could simplify this by using the 'official' Unicat client lib, but that wouldn't give the API insights because it is more high-level.
Note: at the end of the page, we show the same example while using the Unicat client lib.
We'll start by fetching the root record, and then walk through the entire records tree depth-first. For every product, we emit the record so we can use the data.
We set the channel to 'Main channel' and the ordering to 'Main ordering'. We need to set the keys for these, they are found in the project options.
To keep thing simple, we assume every call is successful.
from ccapi import UnicatApi
from .config import PROJECT_GID, SECRET_API_KEY
ccapi = UnicatApi("https://unicat.app", PROJECT_GID)
success, result = ccapi.connect(SECRET_API_KEY)
project = ccapi.data["cc.projects"][PROJECT_GID]
channels_by_name = {v: k for k, v in project["options"]["channels"].items()}
orderings_by_name = {v: k for k, v in project["options"]["orderings"].items()}
language = en
channel = channels_by_name["Main channel"]
ordering = orderings_by_name["Main ordering"]
def walk_tree_depth_first(ccapi, parent_gid, language, channel, ordering):
top = 0
size = 100
while True:
data = {
"record": parent_gid,
"language": language,
"channel": channel,
"ordering": ordering,
"page.top": top,
"page.size": size,
}
succes, result = ccapi.call("/records/children", data)
if not result["children"]:
return
for record_gid in result["children"]:
record = ccapi.data["records"][record_gid]
yield record
if record["childcount"] > 0:
yield from walk_tree_depth_first(ccapi, record_gid, language, channel, ordering)
top += size
if top >= result["children.size"]: # know when to stop
break
succes, result = ccapi.call("/records/root", { "language": "en" })
root_record = ccapi.data["records"][result["root"]]
This sets up the tools we need. We use pagination in case we have more than one hundred children anywhere in the tree, and we pre-check the childcount so we don't perform calls we know won't return anything (they can still return an empty list, since childcount doesn't take the channel into account).
When we walk the tree, we only want to process records that have the article class, so we know that the fields artnr
, price
, and stock
are available (your definitions and classes may differ, but you can extrapolate from here).
article_class_gid = None
for class_gid, class_ in ccapi.data["classes"].items():
if class_["name"] == "article":
article_class_gid = class_gid
with open("product-feed.xml", "w") as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
f.write('<products>\n')
for record in walk_tree_depth_first(ccapi, root_record["gid"], language, channel, ordering):
definition = ccapi.data["definitions"][record["definition"]]
if article_class_gid not in definition["classes"]:
continue
recordfields = record["fields"][language]
artnr = recordfields["artnr"]
price = (
str(recordfields["price"]) if recordfields["price"] else "0.00"
)
stock = str(recordfields["stock"])
f.write(f' <product artnr="{artnr}" price="{price}" stock="{stock}"/>\n')
f.write('</products>\n')
In the inner loop, we should use the image field to fetch a public url for a thumbnail.
succes, result = ccapi.call(
"/assets/get", {"asset": recordfields["image"]}
)
if success:
image = ccapi.data["assets"][result["asset"]]
options = {
"fill": "200,200",
"type": "jpg",
"optimize": True,
}
succes, result = ccapi.transform(image, options)
download_url = result["public_url"]
We can add that to the written XML file.
pip install unicat
Ok, here it goes:
from unicat import Unicat, UnicatTransform
from .config import PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER
unicat = Unicat("https://unicat.app", PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER)
if not unicat.connect():
raise Exception("Invalid connection settings")
language = unicat.project.default_language
channel = unicat.project.channels["Main channel"]
ordering = unicat.project.orderings["Main ordering"]
transform = UnicatTransform(resize="fill", width=200, height=200, type="jpg", optimize=True)
with open("product-feed.xml", "w") as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
f.write('<products>\n')
for record in unicat.walk_record_tree(channel=channel, ordering=ordering):
if "article" not in [class_.name for class_ in record.definition.classes]:
continue
fields = record.fields[language]
artnr = fields["artnr"].value
price = (
str(fields["price"].value) if fields["price"].value else "0.00"
)
stock = str(fields["stock"].value)
image_url = fields["image"].publish_transformed(transform)
f.write(f' <product artnr="{artnr}" price="{price}" stock="{stock}" image="{image_url}"/>\n')
f.write('</products>\n')