diff --git a/.gitignore b/.gitignore
index 4c842ae..33ccf84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
+*~
+*.pyc
+dist/
rattail_woocommerce.egg-info/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73b40f5..088d546 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to rattail-woocommerce will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## v0.2.0 (2024-06-11)
+
+### Feat
+
+- switch from setup.cfg to pyproject.toml + hatchling
+
## [0.1.0] - 2021-01-21
### Added
- Initial version.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f4786ea
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+
+# rattail-woocommerce
+
+Rattail is a retail software framework, released under the GNU General Public
+License.
+
+This package contains software interfaces for the
+[WooCommerce](https://woocommerce.com/) system.
+
+Please see the [Rattail Project](https://rattailproject.org/) for more
+information.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 4c84e9e..0000000
--- a/README.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-
-rattail-woocommerce
-===================
-
-Rattail is a retail software framework, released under the GNU General Public
-License.
-
-This package contains software interfaces for the `WooCommerce`_ system.
-
-.. _WooCommerce: https://woocommerce.com/
-
-Please see the `Rattail Project`_ for more information.
-
-.. _`Rattail Project`: https://rattailproject.org/
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..17a034b
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,46 @@
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+
+[project]
+name = "rattail-woocommerce"
+version = "0.2.0"
+description = "Rattail Software Interfaces for WooCommerce"
+readme = "README.md"
+authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
+license = {text = "GNU GPL v3+"}
+classifiers = [
+ "Development Status :: 3 - Alpha",
+ "Environment :: Console",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Topic :: Office/Business",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+dependencies = [
+ "rattail",
+ "woocommerce",
+]
+
+
+[project.entry-points."rattail.typer_imports"]
+rattail_woocommerce = "rattail_woocommerce.commands"
+
+
+[project.urls]
+Homepage = "https://rattailproject.org"
+Repository = "https://kallithea.rattailproject.org/rattail-project/rattail-woocommerce"
+Changelog = "https://kallithea.rattailproject.org/rattail-project/rattail-woocommerce/files/master/CHANGELOG.md"
+
+
+[tool.commitizen]
+version_provider = "pep621"
+tag_format = "v$version"
+update_changelog_on_bump = true
diff --git a/rattail_woocommerce/_version.py b/rattail_woocommerce/_version.py
index e41b669..b78065c 100644
--- a/rattail_woocommerce/_version.py
+++ b/rattail_woocommerce/_version.py
@@ -1,3 +1,6 @@
# -*- coding: utf-8; -*-
-__version__ = '0.1.0'
+from importlib.metadata import version
+
+
+__version__ = version('rattail-woocommerce')
diff --git a/rattail_woocommerce/commands.py b/rattail_woocommerce/commands.py
index 565ef34..816af6c 100644
--- a/rattail_woocommerce/commands.py
+++ b/rattail_woocommerce/commands.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,22 +24,44 @@
Rattail/WooCommerce Commands
"""
-from rattail import commands
+import typer
+
+from rattail.commands import rattail_typer
+from rattail.commands.typer import importer_command, typer_get_runas_user
+from rattail.commands.importing import ImportCommandHandler
-class ExportWooCommerce(commands.ImportSubcommand):
+@rattail_typer.command()
+@importer_command
+def export_woocommerce(
+ ctx: typer.Context,
+ **kwargs
+):
"""
Export data to WooCommerce
"""
- name = 'export-woocommerce'
- description = __doc__.strip()
- handler_spec = 'rattail_woocommerce.woocommerce.importing.rattail:FromRattailToWooCommerce'
+ config = ctx.parent.rattail_config
+ progress = ctx.parent.rattail_progress
+ handler = ImportCommandHandler(
+ config,
+ import_handler_spec='rattail_woocommerce.woocommerce.importing.rattail:FromRattailToWooCommerce')
+ kwargs['user'] = typer_get_runas_user(ctx)
+ handler.run(kwargs, progress=progress)
-class ImportWooCommerce(commands.ImportSubcommand):
+@rattail_typer.command()
+@importer_command
+def import_woocommerce(
+ ctx: typer.Context,
+ **kwargs
+):
"""
Import data from WooCommerce
"""
- name = 'import-woocommerce'
- description = __doc__.strip()
- handler_spec = 'rattail_woocommerce.importing.woocommerce:FromWooCommerceToRattail'
+ config = ctx.parent.rattail_config
+ progress = ctx.parent.rattail_progress
+ handler = ImportCommandHandler(
+ config,
+ import_handler_spec='rattail_woocommerce.importing.woocommerce:FromWooCommerceToRattail')
+ kwargs['user'] = typer_get_runas_user(ctx)
+ handler.run(kwargs, progress=progress)
diff --git a/rattail_woocommerce/datasync/woocommerce.py b/rattail_woocommerce/datasync/woocommerce.py
index a7738ee..c39fc86 100644
--- a/rattail_woocommerce/datasync/woocommerce.py
+++ b/rattail_woocommerce/datasync/woocommerce.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
@@ -61,9 +61,9 @@ class FromRattailToWooCommerce(FromRattailConsumer):
model = self.model
if change.payload_type == 'Product':
- return session.query(model.Product).get(change.payload_key)
+ return session.get(model.Product, change.payload_key)
if change.payload_type == 'ProductPrice':
- price = session.query(model.ProductPrice).get(change.payload_key)
+ price = session.get(model.ProductPrice, change.payload_key)
if price:
return price.product
diff --git a/rattail_woocommerce/importing/woocommerce.py b/rattail_woocommerce/importing/woocommerce.py
index b5bdd62..c6e6a2c 100644
--- a/rattail_woocommerce/importing/woocommerce.py
+++ b/rattail_woocommerce/importing/woocommerce.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
@@ -25,6 +25,7 @@ WooCommerce -> Rattail data import
"""
import datetime
+from collections import OrderedDict
from sqlalchemy import orm
@@ -32,9 +33,9 @@ from woocommerce import API as WooAPI
from rattail import importing
from rattail.core import get_uuid
-from rattail.util import OrderedDict
from rattail.time import localtime, make_utc
from rattail_woocommerce import importing as rattail_woocommerce_importing
+from rattail_woocommerce.woocommerce.util import get_woocommerce_products
class FromWooCommerceToRattail(importing.ToRattailHandler):
@@ -61,23 +62,13 @@ class FromWooCommerce(importing.Importer):
'consumer_secret': self.config.require('woocommerce', 'api_consumer_secret'),
'version': 'wc/v3',
}
+
+ timeout = self.config.getint('woocommerce', 'api_timeout')
+ if timeout:
+ kwargs['timeout'] = timeout
+
self.api = WooAPI(**kwargs)
- def get_woocommerce_products(self):
- products = []
- page = 1
- while True:
- block = self.api.get('products', params={'per_page': 100,
- 'page': page})
- products.extend(block.json())
- link = block.headers.get('Link')
- if link and 'rel="next"' in link:
- page += 1
- else:
- break
-
- return products
-
class ProductImporter(FromWooCommerce, rattail_woocommerce_importing.model.ProductImporter):
"""
@@ -109,7 +100,7 @@ class ProductImporter(FromWooCommerce, rattail_woocommerce_importing.model.Produ
query=query)
def get_host_objects(self):
- return self.get_woocommerce_products()
+ return get_woocommerce_products(self.api)
def find_rattail_product(self, api_product):
product = self.get_product_by_woo_id(api_product['id'])
@@ -266,7 +257,7 @@ class WooCacheProductImporter(FromWooCommerce, rattail_woocommerce_importing.mod
pass
def get_host_objects(self):
- return self.get_woocommerce_products()
+ return get_woocommerce_products(self.api)
def normalize_host_object(self, api_product):
data = dict(api_product)
diff --git a/rattail_woocommerce/woocommerce/importing/model.py b/rattail_woocommerce/woocommerce/importing/model.py
index 8628c82..4a8ccb4 100644
--- a/rattail_woocommerce/woocommerce/importing/model.py
+++ b/rattail_woocommerce/woocommerce/importing/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -27,6 +27,7 @@ WooCommerce model importers
from woocommerce import API as WooAPI
from rattail import importing
+from rattail_woocommerce.woocommerce.util import get_woocommerce_products
class ToWooCommerce(importing.Importer):
@@ -114,23 +115,11 @@ class ProductImporter(ToWooCommerce):
Fetch existing products from WooCommerce.
"""
cache = {}
- page = 1
- while True:
- response = self.api.get('products', params={'per_page': 100,
- 'page': page})
- for product in response.json():
- data = self.normalize_local_object(product)
- normal = self.normalize_cache_object(product, data)
- key = self.get_cache_key(product, normal)
- cache[key] = normal
-
- # TODO: this seems a bit hacky, is there a better way?
- link = response.headers.get('Link')
- if link and 'rel="next"' in link:
- page += 1
- else:
- break
-
+ for product in get_woocommerce_products(self.api):
+ data = self.normalize_local_object(product)
+ normal = self.normalize_cache_object(product, data)
+ key = self.get_cache_key(product, normal)
+ cache[key] = normal
return cache
def get_single_local_object(self, key):
diff --git a/rattail_woocommerce/woocommerce/importing/rattail.py b/rattail_woocommerce/woocommerce/importing/rattail.py
index 8e59192..0619b50 100644
--- a/rattail_woocommerce/woocommerce/importing/rattail.py
+++ b/rattail_woocommerce/woocommerce/importing/rattail.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,9 +24,10 @@
Rattail -> WooCommerce importing
"""
+from collections import OrderedDict
+
from rattail import importing
from rattail.db import model
-from rattail.util import OrderedDict
from rattail_woocommerce.db.model import WooCacheProduct
from rattail_woocommerce.woocommerce import importing as woocommerce_importing
diff --git a/rattail_woocommerce/woocommerce/util.py b/rattail_woocommerce/woocommerce/util.py
new file mode 100644
index 0000000..dc5ca04
--- /dev/null
+++ b/rattail_woocommerce/woocommerce/util.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2022 Lance Edgar
+#
+# This file is part of Rattail.
+#
+# Rattail is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Rattail. If not, see .
+#
+################################################################################
+"""
+WooCommerce utilities
+"""
+
+
+def get_woocommerce_products(api):
+ """
+ Fetch and return all products from Woo API.
+ """
+ products = []
+ page = 1
+ while True:
+
+ # TODO: 100 seems to be the max allowed per page?
+ # although docs do not seem to mention a limit..
+ # https://woocommerce.github.io/woocommerce-rest-api-docs/?python#list-all-products
+ response = api.get('products', params={'per_page': 100,
+ 'page': page,
+ 'orderby': 'id',
+ 'order': 'asc'})
+
+ products.extend(response.json())
+
+ # TODO: this seems a bit hacky, is there a better way?
+ link = response.headers.get('Link')
+ if link and 'rel="next"' in link:
+ page += 1
+ else:
+ break
+
+ return products
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 975d524..0000000
--- a/setup.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-
-import os
-from setuptools import setup, find_packages
-
-
-here = os.path.abspath(os.path.dirname(__file__))
-exec(open(os.path.join(here, 'rattail_woocommerce', '_version.py')).read())
-README = open(os.path.join(here, 'README.rst')).read()
-
-
-requires = [
- #
- # Version numbers within comments below have specific meanings.
- # Basically the 'low' value is a "soft low," and 'high' a "soft high."
- # In other words:
- #
- # If either a 'low' or 'high' value exists, the primary point to be
- # made about the value is that it represents the most current (stable)
- # version available for the package (assuming typical public access
- # methods) whenever this project was started and/or documented.
- # Therefore:
- #
- # If a 'low' version is present, you should know that attempts to use
- # versions of the package significantly older than the 'low' version
- # may not yield happy results. (A "hard" high limit may or may not be
- # indicated by a true version requirement.)
- #
- # Similarly, if a 'high' version is present, and especially if this
- # project has laid dormant for a while, you may need to refactor a bit
- # when attempting to support a more recent version of the package. (A
- # "hard" low limit should be indicated by a true version requirement
- # when a 'high' version is present.)
- #
- # In any case, developers and other users are encouraged to play
- # outside the lines with regard to these soft limits. If bugs are
- # encountered then they should be filed as such.
- #
- # package # low high
-
- 'rattail', # 0.9.153
- 'woocommerce', # 2.1.1
-]
-
-
-setup(
- name = "rattail-woocommerce",
- version = __version__,
- author = "Lance Edgar",
- author_email = "lance@edbob.org",
- url = "https://rattailproject.org/",
- license = "GNU GPL v3",
- description = "Rattail Software Interfaces for WooCommerce",
- long_description = README,
-
- classifiers = [
- 'Development Status :: 3 - Alpha',
- 'Environment :: Console',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3',
- 'Topic :: Office/Business',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- ],
-
- install_requires = requires,
- packages = find_packages(),
- include_package_data = True,
- zip_safe = False,
-
- entry_points = {
-
- 'rattail.commands': [
- 'export-woocommerce = rattail_woocommerce.commands:ExportWooCommerce',
- 'import-woocommerce = rattail_woocommerce.commands:ImportWooCommerce',
- ],
- },
-)
diff --git a/tasks.py b/tasks.py
index 28e11c4..be2931a 100644
--- a/tasks.py
+++ b/tasks.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@@ -30,15 +30,14 @@ import shutil
from invoke import task
-here = os.path.abspath(os.path.dirname(__file__))
-exec(open(os.path.join(here, 'rattail_woocommerce', '_version.py')).read())
-
-
@task
-def release(ctx):
+def release(c):
"""
Release a new version of 'rattail-woocommerce'.
"""
- shutil.rmtree('rattail_woocommerce.egg-info')
- ctx.run('python setup.py sdist --formats=gztar')
- ctx.run('twine upload dist/rattail-woocommerce-{}.tar.gz'.format(__version__))
+ if os.path.exists('dist'):
+ shutil.rmtree('dist')
+ if os.path.exists('rattail_woocommerce.egg-info'):
+ shutil.rmtree('rattail_woocommerce.egg-info')
+ c.run('python -m build --sdist')
+ c.run('twine upload dist/*')