diff --git a/.gitignore b/.gitignore
index 129bcb9..21277ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1 @@
-*~
-*.pyc
-dist/
rattail_harvest.egg-info/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1c7891..0cc2c69 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,36 +5,6 @@ All notable changes to rattail-harvest 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.3.2 (2024-08-18)
-
-### Fix
-
-- avoid deprecated base class for config extension
-
-## v0.3.1 (2024-07-01)
-
-### Fix
-
-- remove legacy command definitions
-
-## v0.3.0 (2024-06-10)
-
-### Feat
-
-- switch from setup.cfg to pyproject.toml + hatchling
-
-## [0.2.1] - 2024-06-06
-### Changed
-- Add alembic scripts to project manifest.
-
-## [0.2.0] - 2024-06-06
-### Changed
-- Add typer equivalents for `rattail` commands.
-
-## [0.1.2] - 2023-11-18
-### Changed
-- Catch-up release, with various schema changes etc.
-
## [0.1.1] - 2022-01-29
### Added
- Initial version.
diff --git a/MANIFEST.in b/MANIFEST.in
index d765c69..cf92731 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,3 @@
include *.md
include *.rst
-
recursive-include rattail_harvest/db/alembic *.mako
-recursive-include rattail_harvest/db/alembic *.py
diff --git a/README.md b/README.md
deleted file mode 100644
index 3a6e08e..0000000
--- a/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-# rattail-harvest
-
-Rattail is a retail software framework, released under the GNU General
-Public License.
-
-This package contains software interfaces for
-[Harvest](https://www.getharvest.com/).
-
-Please see the [Rattail Project](https://rattailproject.org/) for more
-information.
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..ab02203
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,14 @@
+
+rattail-harvest
+===============
+
+Rattail is a retail software framework, released under the GNU General
+Public License.
+
+This package contains software interfaces for `Harvest`_.
+
+.. _`Harvest`: https://www.getharvest.com/
+
+Please see the `Rattail Project`_ for more information.
+
+.. _`Rattail Project`: https://rattailproject.org/
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 8002dd2..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-[build-system]
-requires = ["hatchling"]
-build-backend = "hatchling.build"
-
-
-[project]
-name = "rattail-harvest"
-version = "0.3.2"
-description = "Rattail integration package for Harvest"
-readme = "README.md"
-authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
-license = {text = "GNU GPL v3+"}
-classifiers = [
- "Development Status :: 3 - Alpha",
- "Environment :: Console",
- "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 = [
- "invoke",
- "rattail[db]",
-]
-
-
-[project.urls]
-Homepage = "https://rattailproject.org"
-Repository = "https://forgejo.wuttaproject.org/rattail/rattail-harvest"
-Changelog = "https://forgejo.wuttaproject.org/rattail/rattail-harvest/src/branch/master/CHANGELOG.md"
-
-
-[project.entry-points."rattail.typer_imports"]
-rattail_harvest = "rattail_harvest.commands"
-
-
-[project.entry-points."rattail.config.extensions"]
-rattail_harvest = "rattail_harvest.config:RattailHarvestExtension"
-
-
-[project.entry-points."rattail.importing"]
-"to_rattail.from_harvest.import" = "rattail_harvest.importing.harvest:FromHarvestToRattail"
-
-
-[tool.commitizen]
-version_provider = "pep621"
-tag_format = "v$version"
-update_changelog_on_bump = true
diff --git a/rattail_harvest/_version.py b/rattail_harvest/_version.py
index 885cd51..4984097 100644
--- a/rattail_harvest/_version.py
+++ b/rattail_harvest/_version.py
@@ -1,6 +1,3 @@
# -*- coding: utf-8; -*-
-from importlib.metadata import version
-
-
-__version__ = version('rattail-harvest')
+__version__ = '0.1.1'
diff --git a/rattail_harvest/commands.py b/rattail_harvest/commands.py
index c54848c..80d5be1 100644
--- a/rattail_harvest/commands.py
+++ b/rattail_harvest/commands.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2024 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,25 +24,13 @@
rattail-harvest 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
+from rattail import commands
-@rattail_typer.command()
-@importer_command
-def import_harvest(
- ctx: typer.Context,
- **kwargs
-):
+class ImportHarvest(commands.ImportSubcommand):
"""
Import data to Rattail, from Harvest API
"""
- config = ctx.parent.rattail_config
- progress = ctx.parent.rattail_progress
- handler = ImportCommandHandler(
- config, import_handler_key='to_rattail.from_harvest.import')
- kwargs['user'] = typer_get_runas_user(ctx)
- handler.run(kwargs, progress=progress)
+ name = 'import-harvest'
+ description = __doc__.strip()
+ handler_key = 'to_rattail.from_harvest.import'
diff --git a/rattail_harvest/config.py b/rattail_harvest/config.py
deleted file mode 100644
index 93cc4d1..0000000
--- a/rattail_harvest/config.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2024 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 .
-#
-################################################################################
-"""
-Config Extension
-"""
-
-from wuttjamaican.conf import WuttaConfigExtension
-
-
-class RattailHarvestExtension(WuttaConfigExtension):
- """
- Config extension for rattail-harvest.
- """
- key = 'rattail_harvest'
-
- def configure(self, config):
-
- # rattail import-harvest
- config.setdefault('rattail.importing', 'to_rattail.from_harvest.import.default_handler',
- 'rattail_harvest.importing.harvest:FromHarvestToRattail')
- config.setdefault('rattail.importing', 'to_rattail.from_harvest.import.default_cmd',
- 'rattail import-harvest')
diff --git a/rattail_harvest/db/alembic/versions/53c066772ad5_rename_cache_tables.py b/rattail_harvest/db/alembic/versions/53c066772ad5_rename_cache_tables.py
deleted file mode 100644
index b185837..0000000
--- a/rattail_harvest/db/alembic/versions/53c066772ad5_rename_cache_tables.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# -*- coding: utf-8; -*-
-"""rename cache tables
-
-Revision ID: 53c066772ad5
-Revises: f2a1650e7fbc
-Create Date: 2023-10-04 15:19:03.857323
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = '53c066772ad5'
-down_revision = 'f2a1650e7fbc'
-branch_labels = None
-depends_on = None
-
-from alembic import op
-import sqlalchemy as sa
-import rattail.db.types
-
-
-
-def upgrade():
-
- ##############################
- # drop all constraints
- ##############################
-
- # harvest_time_entry
- op.drop_constraint('harvest_time_entry_fk_user', 'harvest_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_time_entry_fk_client', 'harvest_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_time_entry_fk_project', 'harvest_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_time_entry_fk_task', 'harvest_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_time_entry_uq_id', 'harvest_time_entry', type_='unique')
-
- # harvest_task
- op.drop_constraint('harvest_task_uq_id', 'harvest_task', type_='unique')
-
- # harvest_project
- op.drop_constraint('harvest_project_fk_client', 'harvest_project', type_='foreignkey')
- op.drop_constraint('harvest_project_uq_id', 'harvest_project', type_='unique')
-
- # harvest_client
- op.drop_constraint('harvest_client_uq_id', 'harvest_client', type_='unique')
-
- # harvest_user
- op.drop_constraint('harvest_user_fk_person', 'harvest_user', type_='foreignkey')
- op.drop_constraint('harvest_user_uq_id', 'harvest_user', type_='unique')
-
- ##############################
- # rename all tables
- ##############################
-
- op.rename_table('harvest_user', 'harvest_cache_user')
- op.rename_table('harvest_user_version', 'harvest_cache_user_version')
- op.rename_table('harvest_client', 'harvest_cache_client')
- op.rename_table('harvest_client_version', 'harvest_cache_client_version')
- op.rename_table('harvest_project', 'harvest_cache_project')
- op.rename_table('harvest_project_version', 'harvest_cache_project_version')
- op.rename_table('harvest_task', 'harvest_cache_task')
- op.rename_table('harvest_task_version', 'harvest_cache_task_version')
- op.rename_table('harvest_time_entry', 'harvest_cache_time_entry')
- op.rename_table('harvest_time_entry_version', 'harvest_cache_time_entry_version')
-
- ##############################
- # re-create all constraints
- ##############################
-
- # harvest_cache_user
- op.create_foreign_key('harvest_cache_user_fk_person',
- 'harvest_cache_user', 'person',
- ['person_uuid'], ['uuid'])
- op.create_unique_constraint('harvest_cache_user_uq_id', 'harvest_cache_user', ['id'])
-
- # harvest_cache_client
- op.create_unique_constraint('harvest_cache_client_uq_id', 'harvest_cache_client', ['id'])
-
- # harvest_cache_project
- op.create_foreign_key('harvest_cache_project_fk_client',
- 'harvest_cache_project', 'harvest_cache_client',
- ['client_id'], ['id'])
- op.create_unique_constraint('harvest_cache_project_uq_id', 'harvest_cache_project', ['id'])
-
- # harvest_cache_task
- op.create_unique_constraint('harvest_cache_task_uq_id', 'harvest_cache_task', ['id'])
-
- # harvest_cache_time_entry
- op.create_foreign_key('harvest_cache_time_entry_fk_user',
- 'harvest_cache_time_entry', 'harvest_cache_user',
- ['user_id'], ['id'])
- op.create_foreign_key('harvest_cache_time_entry_fk_client',
- 'harvest_cache_time_entry', 'harvest_cache_client',
- ['client_id'], ['id'])
- op.create_foreign_key('harvest_cache_time_entry_fk_project',
- 'harvest_cache_time_entry', 'harvest_cache_project',
- ['project_id'], ['id'])
- op.create_foreign_key('harvest_cache_time_entry_fk_task',
- 'harvest_cache_time_entry', 'harvest_cache_task',
- ['task_id'], ['id'])
- op.create_unique_constraint('harvest_cache_time_entry_uq_id', 'harvest_cache_time_entry', ['id'])
-
-
-def downgrade():
-
- ##############################
- # drop all constraints
- ##############################
-
- # harvest_cache_time_entry
- op.drop_constraint('harvest_cache_time_entry_fk_user', 'harvest_cache_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_cache_time_entry_fk_client', 'harvest_cache_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_cache_time_entry_fk_project', 'harvest_cache_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_cache_time_entry_fk_task', 'harvest_cache_time_entry', type_='foreignkey')
- op.drop_constraint('harvest_cache_time_entry_uq_id', 'harvest_cache_time_entry', type_='unique')
-
- # harvest_cache_task
- op.drop_constraint('harvest_cache_task_uq_id', 'harvest_cache_task', type_='unique')
-
- # harvest_cache_project
- op.drop_constraint('harvest_cache_project_fk_client', 'harvest_cache_project', type_='foreignkey')
- op.drop_constraint('harvest_cache_project_uq_id', 'harvest_cache_project', type_='unique')
-
- # harvest_cache_client
- op.drop_constraint('harvest_cache_client_uq_id', 'harvest_cache_client', type_='unique')
-
- # harvest_cache_user
- op.drop_constraint('harvest_cache_user_fk_person', 'harvest_cache_user', type_='foreignkey')
- op.drop_constraint('harvest_cache_user_uq_id', 'harvest_cache_user', type_='unique')
-
- ##############################
- # rename all tables
- ##############################
-
- op.rename_table('harvest_cache_user', 'harvest_user')
- op.rename_table('harvest_cache_user_version', 'harvest_user_version')
- op.rename_table('harvest_cache_client', 'harvest_client')
- op.rename_table('harvest_cache_client_version', 'harvest_client_version')
- op.rename_table('harvest_cache_project', 'harvest_project')
- op.rename_table('harvest_cache_project_version', 'harvest_project_version')
- op.rename_table('harvest_cache_task', 'harvest_task')
- op.rename_table('harvest_cache_task_version', 'harvest_task_version')
- op.rename_table('harvest_cache_time_entry', 'harvest_time_entry')
- op.rename_table('harvest_cache_time_entry_version', 'harvest_time_entry_version')
-
- ##############################
- # re-create all constraints
- ##############################
-
- # harvest_user
- op.create_foreign_key('harvest_user_fk_person',
- 'harvest_user', 'person',
- ['person_uuid'], ['uuid'])
- op.create_unique_constraint('harvest_user_uq_id', 'harvest_user', ['id'])
-
- # harvest_client
- op.create_unique_constraint('harvest_client_uq_id', 'harvest_client', ['id'])
-
- # harvest_project
- op.create_foreign_key('harvest_project_fk_client',
- 'harvest_project', 'harvest_client',
- ['client_id'], ['id'])
- op.create_unique_constraint('harvest_project_uq_id', 'harvest_project', ['id'])
-
- # harvest_cache_task
- op.create_unique_constraint('harvest_task_uq_id', 'harvest_task', ['id'])
-
- # harvest_time_entry
- op.create_foreign_key('harvest_time_entry_fk_user',
- 'harvest_time_entry', 'harvest_user',
- ['user_id'], ['id'])
- op.create_foreign_key('harvest_time_entry_fk_client',
- 'harvest_time_entry', 'harvest_client',
- ['client_id'], ['id'])
- op.create_foreign_key('harvest_time_entry_fk_project',
- 'harvest_time_entry', 'harvest_project',
- ['project_id'], ['id'])
- op.create_foreign_key('harvest_time_entry_fk_task',
- 'harvest_time_entry', 'harvest_task',
- ['task_id'], ['id'])
- op.create_unique_constraint('harvest_time_entry_uq_id', 'harvest_time_entry', ['id'])
diff --git a/rattail_harvest/db/alembic/versions/5505c0e60d28_add_project_deleted.py b/rattail_harvest/db/alembic/versions/5505c0e60d28_add_project_deleted.py
deleted file mode 100644
index 042663d..0000000
--- a/rattail_harvest/db/alembic/versions/5505c0e60d28_add_project_deleted.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8; -*-
-"""add project.deleted
-
-Revision ID: 5505c0e60d28
-Revises: d59ce24c2f9f
-Create Date: 2022-01-30 12:08:04.338229
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = '5505c0e60d28'
-down_revision = 'd59ce24c2f9f'
-branch_labels = None
-depends_on = None
-
-from alembic import op
-import sqlalchemy as sa
-import rattail.db.types
-
-
-
-def upgrade():
-
- # harvest_project
- op.add_column('harvest_project', sa.Column('deleted', sa.Boolean(), nullable=True))
- op.add_column('harvest_project_version', sa.Column('deleted', sa.Boolean(), autoincrement=False, nullable=True))
-
-
-def downgrade():
-
- # harvest_project
- op.drop_column('harvest_project_version', 'deleted')
- op.drop_column('harvest_project', 'deleted')
diff --git a/rattail_harvest/db/alembic/versions/6bc1cb21d920_add_harvest_user_person.py b/rattail_harvest/db/alembic/versions/6bc1cb21d920_add_harvest_user_person.py
deleted file mode 100644
index 31cca85..0000000
--- a/rattail_harvest/db/alembic/versions/6bc1cb21d920_add_harvest_user_person.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8; -*-
-"""add harvest_user.person
-
-Revision ID: 6bc1cb21d920
-Revises: 5505c0e60d28
-Create Date: 2022-01-30 16:49:32.271745
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = '6bc1cb21d920'
-down_revision = '5505c0e60d28'
-branch_labels = None
-depends_on = None
-
-from alembic import op
-import sqlalchemy as sa
-import rattail.db.types
-
-
-
-def upgrade():
-
- # harvest_user
- op.add_column('harvest_user', sa.Column('person_uuid', sa.String(length=32), nullable=True))
- op.create_foreign_key('harvest_user_fk_person', 'harvest_user', 'person', ['person_uuid'], ['uuid'])
- op.add_column('harvest_user_version', sa.Column('person_uuid', sa.String(length=32), autoincrement=False, nullable=True))
-
-
-def downgrade():
-
- # harvest_user
- op.drop_column('harvest_user_version', 'person_uuid')
- op.drop_constraint('harvest_user_fk_person', 'harvest_user', type_='foreignkey')
- op.drop_column('harvest_user', 'person_uuid')
diff --git a/rattail_harvest/db/alembic/versions/a1cf300fb371_fix_indeces.py b/rattail_harvest/db/alembic/versions/a1cf300fb371_fix_indeces.py
deleted file mode 100644
index fe059e0..0000000
--- a/rattail_harvest/db/alembic/versions/a1cf300fb371_fix_indeces.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# -*- coding: utf-8; -*-
-"""fix indeces
-
-Revision ID: a1cf300fb371
-Revises: 53c066772ad5
-Create Date: 2023-10-23 17:35:15.527740
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = 'a1cf300fb371'
-down_revision = '53c066772ad5'
-branch_labels = None
-depends_on = None
-
-from alembic import op
-import sqlalchemy as sa
-import rattail.db.types
-
-
-
-def upgrade():
-
- # harvest_cache_user
- op.drop_index('ix_harvest_user_version_end_transaction_id', table_name='harvest_cache_user_version')
- op.drop_index('ix_harvest_user_version_operation_type', table_name='harvest_cache_user_version')
- op.drop_index('ix_harvest_user_version_transaction_id', table_name='harvest_cache_user_version')
- op.create_index(op.f('ix_harvest_cache_user_version_end_transaction_id'), 'harvest_cache_user_version', ['end_transaction_id'], unique=False)
- op.create_index(op.f('ix_harvest_cache_user_version_operation_type'), 'harvest_cache_user_version', ['operation_type'], unique=False)
- op.create_index(op.f('ix_harvest_cache_user_version_transaction_id'), 'harvest_cache_user_version', ['transaction_id'], unique=False)
-
- # harvest_cache_client
- op.drop_index('ix_harvest_client_version_end_transaction_id', table_name='harvest_cache_client_version')
- op.drop_index('ix_harvest_client_version_operation_type', table_name='harvest_cache_client_version')
- op.drop_index('ix_harvest_client_version_transaction_id', table_name='harvest_cache_client_version')
- op.create_index(op.f('ix_harvest_cache_client_version_end_transaction_id'), 'harvest_cache_client_version', ['end_transaction_id'], unique=False)
- op.create_index(op.f('ix_harvest_cache_client_version_operation_type'), 'harvest_cache_client_version', ['operation_type'], unique=False)
- op.create_index(op.f('ix_harvest_cache_client_version_transaction_id'), 'harvest_cache_client_version', ['transaction_id'], unique=False)
-
- # harvest_cache_project
- op.drop_index('ix_harvest_project_version_end_transaction_id', table_name='harvest_cache_project_version')
- op.drop_index('ix_harvest_project_version_operation_type', table_name='harvest_cache_project_version')
- op.drop_index('ix_harvest_project_version_transaction_id', table_name='harvest_cache_project_version')
- op.create_index(op.f('ix_harvest_cache_project_version_end_transaction_id'), 'harvest_cache_project_version', ['end_transaction_id'], unique=False)
- op.create_index(op.f('ix_harvest_cache_project_version_operation_type'), 'harvest_cache_project_version', ['operation_type'], unique=False)
- op.create_index(op.f('ix_harvest_cache_project_version_transaction_id'), 'harvest_cache_project_version', ['transaction_id'], unique=False)
-
- # harvest_cache_task
- op.drop_index('ix_harvest_task_version_end_transaction_id', table_name='harvest_cache_task_version')
- op.drop_index('ix_harvest_task_version_operation_type', table_name='harvest_cache_task_version')
- op.drop_index('ix_harvest_task_version_transaction_id', table_name='harvest_cache_task_version')
- op.create_index(op.f('ix_harvest_cache_task_version_end_transaction_id'), 'harvest_cache_task_version', ['end_transaction_id'], unique=False)
- op.create_index(op.f('ix_harvest_cache_task_version_operation_type'), 'harvest_cache_task_version', ['operation_type'], unique=False)
- op.create_index(op.f('ix_harvest_cache_task_version_transaction_id'), 'harvest_cache_task_version', ['transaction_id'], unique=False)
-
- # harvest_cache_time_entry
- op.drop_index('ix_harvest_time_entry_version_end_transaction_id', table_name='harvest_cache_time_entry_version')
- op.drop_index('ix_harvest_time_entry_version_operation_type', table_name='harvest_cache_time_entry_version')
- op.drop_index('ix_harvest_time_entry_version_transaction_id', table_name='harvest_cache_time_entry_version')
- op.create_index(op.f('ix_harvest_cache_time_entry_version_end_transaction_id'), 'harvest_cache_time_entry_version', ['end_transaction_id'], unique=False)
- op.create_index(op.f('ix_harvest_cache_time_entry_version_operation_type'), 'harvest_cache_time_entry_version', ['operation_type'], unique=False)
- op.create_index(op.f('ix_harvest_cache_time_entry_version_transaction_id'), 'harvest_cache_time_entry_version', ['transaction_id'], unique=False)
-
-
-def downgrade():
-
- # harvest_cache_time_entry
- op.drop_index(op.f('ix_harvest_cache_time_entry_version_transaction_id'), table_name='harvest_cache_time_entry_version')
- op.drop_index(op.f('ix_harvest_cache_time_entry_version_operation_type'), table_name='harvest_cache_time_entry_version')
- op.drop_index(op.f('ix_harvest_cache_time_entry_version_end_transaction_id'), table_name='harvest_cache_time_entry_version')
- op.create_index('ix_harvest_time_entry_version_transaction_id', 'harvest_cache_time_entry_version', ['transaction_id'], unique=False)
- op.create_index('ix_harvest_time_entry_version_operation_type', 'harvest_cache_time_entry_version', ['operation_type'], unique=False)
- op.create_index('ix_harvest_time_entry_version_end_transaction_id', 'harvest_cache_time_entry_version', ['end_transaction_id'], unique=False)
-
- # harvest_cache_task
- op.drop_index(op.f('ix_harvest_cache_task_version_transaction_id'), table_name='harvest_cache_task_version')
- op.drop_index(op.f('ix_harvest_cache_task_version_operation_type'), table_name='harvest_cache_task_version')
- op.drop_index(op.f('ix_harvest_cache_task_version_end_transaction_id'), table_name='harvest_cache_task_version')
- op.create_index('ix_harvest_task_version_transaction_id', 'harvest_cache_task_version', ['transaction_id'], unique=False)
- op.create_index('ix_harvest_task_version_operation_type', 'harvest_cache_task_version', ['operation_type'], unique=False)
- op.create_index('ix_harvest_task_version_end_transaction_id', 'harvest_cache_task_version', ['end_transaction_id'], unique=False)
-
- # harvest_cache_project
- op.drop_index(op.f('ix_harvest_cache_project_version_transaction_id'), table_name='harvest_cache_project_version')
- op.drop_index(op.f('ix_harvest_cache_project_version_operation_type'), table_name='harvest_cache_project_version')
- op.drop_index(op.f('ix_harvest_cache_project_version_end_transaction_id'), table_name='harvest_cache_project_version')
- op.create_index('ix_harvest_project_version_transaction_id', 'harvest_cache_project_version', ['transaction_id'], unique=False)
- op.create_index('ix_harvest_project_version_operation_type', 'harvest_cache_project_version', ['operation_type'], unique=False)
- op.create_index('ix_harvest_project_version_end_transaction_id', 'harvest_cache_project_version', ['end_transaction_id'], unique=False)
-
- # harvest_cache_client
- op.drop_index(op.f('ix_harvest_cache_client_version_transaction_id'), table_name='harvest_cache_client_version')
- op.drop_index(op.f('ix_harvest_cache_client_version_operation_type'), table_name='harvest_cache_client_version')
- op.drop_index(op.f('ix_harvest_cache_client_version_end_transaction_id'), table_name='harvest_cache_client_version')
- op.create_index('ix_harvest_client_version_transaction_id', 'harvest_cache_client_version', ['transaction_id'], unique=False)
- op.create_index('ix_harvest_client_version_operation_type', 'harvest_cache_client_version', ['operation_type'], unique=False)
- op.create_index('ix_harvest_client_version_end_transaction_id', 'harvest_cache_client_version', ['end_transaction_id'], unique=False)
-
- # harvest_cache_user
- op.drop_index(op.f('ix_harvest_cache_user_version_transaction_id'), table_name='harvest_cache_user_version')
- op.drop_index(op.f('ix_harvest_cache_user_version_operation_type'), table_name='harvest_cache_user_version')
- op.drop_index(op.f('ix_harvest_cache_user_version_end_transaction_id'), table_name='harvest_cache_user_version')
- op.create_index('ix_harvest_user_version_transaction_id', 'harvest_cache_user_version', ['transaction_id'], unique=False)
- op.create_index('ix_harvest_user_version_operation_type', 'harvest_cache_user_version', ['operation_type'], unique=False)
- op.create_index('ix_harvest_user_version_end_transaction_id', 'harvest_cache_user_version', ['end_transaction_id'], unique=False)
diff --git a/rattail_harvest/db/alembic/versions/f2a1650e7fbc_grow_id_fields.py b/rattail_harvest/db/alembic/versions/f2a1650e7fbc_grow_id_fields.py
deleted file mode 100644
index 0c24bb5..0000000
--- a/rattail_harvest/db/alembic/versions/f2a1650e7fbc_grow_id_fields.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8; -*-
-"""grow id fields
-
-Revision ID: f2a1650e7fbc
-Revises: 6bc1cb21d920
-Create Date: 2023-08-08 10:53:56.013211
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = 'f2a1650e7fbc'
-down_revision = '6bc1cb21d920'
-branch_labels = None
-depends_on = None
-
-from alembic import op
-import sqlalchemy as sa
-import rattail.db.types
-from sqlalchemy.dialects import postgresql
-
-
-def upgrade():
-
- # harvest_user
- op.alter_column('harvest_user', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_user_version', 'id', type_=sa.BigInteger())
-
- # harvest_client
- op.alter_column('harvest_client', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_client_version', 'id', type_=sa.BigInteger())
-
- # harvest_project
- op.alter_column('harvest_project', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_project', 'client_id', type_=sa.BigInteger())
- op.alter_column('harvest_project_version', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_project_version', 'client_id', type_=sa.BigInteger())
-
- # harvest_task
- op.alter_column('harvest_task', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_task_version', 'id', type_=sa.BigInteger())
-
- # harvest_time_entry
- op.alter_column('harvest_time_entry', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry', 'user_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry', 'client_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry', 'project_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry', 'task_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry', 'invoice_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'user_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'client_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'project_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'task_id', type_=sa.BigInteger())
- op.alter_column('harvest_time_entry_version', 'invoice_id', type_=sa.BigInteger())
-
-
-def downgrade():
-
- # harvest_time_entry
- op.alter_column('harvest_time_entry_version', 'id', type_=sa.Integer())
- op.alter_column('harvest_time_entry_version', 'user_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry_version', 'client_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry_version', 'project_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry_version', 'task_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry_version', 'invoice_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'user_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'client_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'project_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'task_id', type_=sa.Integer())
- op.alter_column('harvest_time_entry', 'invoice_id', type_=sa.Integer())
-
- # harvest_task
- op.alter_column('harvest_task_version', 'id', type_=sa.Integer())
- op.alter_column('harvest_task', 'id', type_=sa.Integer())
-
- # harvest_project
- op.alter_column('harvest_project_version', 'id', type_=sa.Integer())
- op.alter_column('harvest_project_version', 'client_id', type_=sa.Integer())
- op.alter_column('harvest_project', 'id', type_=sa.Integer())
- op.alter_column('harvest_project', 'client_id', type_=sa.Integer())
-
- # harvest_client
- op.alter_column('harvest_client_version', 'id', type_=sa.Integer())
- op.alter_column('harvest_client', 'id', type_=sa.Integer())
-
- # harvest_user
- op.alter_column('harvest_user_version', 'id', type_=sa.Integer())
- op.alter_column('harvest_user', 'id', type_=sa.Integer())
diff --git a/rattail_harvest/db/model/__init__.py b/rattail_harvest/db/model/__init__.py
index 86a099f..e55b2a1 100644
--- a/rattail_harvest/db/model/__init__.py
+++ b/rattail_harvest/db/model/__init__.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,6 +24,5 @@
Harvest integration data models
"""
-from .harvest import (HarvestCacheUser, HarvestCacheClient,
- HarvestCacheProject, HarvestCacheTask,
- HarvestCacheTimeEntry)
+from .harvest import (HarvestUser, HarvestClient, HarvestProject,
+ HarvestTask, HarvestTimeEntry)
diff --git a/rattail_harvest/db/model/harvest.py b/rattail_harvest/db/model/harvest.py
index 4b015d2..d7a098c 100644
--- a/rattail_harvest/db/model/harvest.py
+++ b/rattail_harvest/db/model/harvest.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,8 +24,6 @@
Harvest "cache" data models
"""
-import warnings
-
import sqlalchemy as sa
from sqlalchemy import orm
@@ -33,23 +31,21 @@ from rattail.db import model
from rattail.db.util import normalize_full_name
-class HarvestCacheUser(model.Base):
+class HarvestUser(model.Base):
"""
Represents a user record in Harvest.
https://help.getharvest.com/api-v2/users-api/users/users/#the-user-object
"""
- __tablename__ = 'harvest_cache_user'
+ __tablename__ = 'harvest_user'
__table_args__ = (
- sa.ForeignKeyConstraint(['person_uuid'], ['person.uuid'],
- name='harvest_cache_user_fk_person'),
- sa.UniqueConstraint('id', name='harvest_cache_user_uq_id'),
+ sa.UniqueConstraint('id', name='harvest_user_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
- id = sa.Column(sa.BigInteger(), nullable=False)
+ id = sa.Column(sa.Integer(), nullable=False)
first_name = sa.Column(sa.String(length=255), nullable=True)
@@ -94,38 +90,25 @@ class HarvestCacheUser(model.Base):
updated_at = sa.Column(sa.DateTime(), nullable=True)
- person_uuid = sa.Column(sa.String(length=32), nullable=True)
- person = orm.relationship(
- model.Person,
- doc="""
- Reference to the person associated with this Harvest user.
- """,
- backref=orm.backref(
- 'harvest_users',
- doc="""
- List of all Harvest user accounts for the person.
- """)
- )
-
def __str__(self):
return normalize_full_name(self.first_name, self.last_name)
-class HarvestCacheClient(model.Base):
+class HarvestClient(model.Base):
"""
Represents a client record in Harvest.
https://help.getharvest.com/api-v2/clients-api/clients/clients/#the-client-object
"""
- __tablename__ = 'harvest_cache_client'
+ __tablename__ = 'harvest_client'
__table_args__ = (
- sa.UniqueConstraint('id', name='harvest_cache_client_uq_id'),
+ sa.UniqueConstraint('id', name='harvest_client_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
- id = sa.Column(sa.BigInteger(), nullable=False)
+ id = sa.Column(sa.Integer(), nullable=False)
name = sa.Column(sa.String(length=255), nullable=True)
@@ -143,26 +126,25 @@ class HarvestCacheClient(model.Base):
return self.name or ''
-class HarvestCacheProject(model.Base):
+class HarvestProject(model.Base):
"""
Represents a project record in Harvest.
https://help.getharvest.com/api-v2/projects-api/projects/projects/#the-project-object
"""
- __tablename__ = 'harvest_cache_project'
+ __tablename__ = 'harvest_project'
__table_args__ = (
- sa.UniqueConstraint('id', name='harvest_cache_project_uq_id'),
- sa.ForeignKeyConstraint(['client_id'], ['harvest_cache_client.id'],
- name='harvest_cache_project_fk_client'),
+ sa.UniqueConstraint('id', name='harvest_project_uq_id'),
+ sa.ForeignKeyConstraint(['client_id'], ['harvest_client.id'], name='harvest_project_fk_client'),
)
__versioned__ = {'exclude': ['over_budget_notification_date']}
uuid = model.uuid_column()
- id = sa.Column(sa.BigInteger(), nullable=False)
+ id = sa.Column(sa.Integer(), nullable=False)
- client_id = sa.Column(sa.BigInteger(), nullable=True) # TODO: should not allow null?
- client = orm.relationship(HarvestCacheClient, backref=orm.backref('projects'))
+ client_id = sa.Column(sa.Integer(), nullable=True) # TODO: should not allow null?
+ client = orm.relationship(HarvestClient, backref=orm.backref('projects'))
name = sa.Column(sa.String(length=255), nullable=True)
@@ -208,29 +190,25 @@ class HarvestCacheProject(model.Base):
updated_at = sa.Column(sa.DateTime(), nullable=True)
- deleted = sa.Column(sa.Boolean(), nullable=True, doc="""
- Flag indicating the record has been deleted in Harvest.
- """)
-
def __str__(self):
return self.name or ''
-class HarvestCacheTask(model.Base):
+class HarvestTask(model.Base):
"""
Represents a task record in Harvest.
https://help.getharvest.com/api-v2/tasks-api/tasks/tasks/#the-task-object
"""
- __tablename__ = 'harvest_cache_task'
+ __tablename__ = 'harvest_task'
__table_args__ = (
- sa.UniqueConstraint('id', name='harvest_cache_task_uq_id'),
+ sa.UniqueConstraint('id', name='harvest_task_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
- id = sa.Column(sa.BigInteger(), nullable=False)
+ id = sa.Column(sa.Integer(), nullable=False)
name = sa.Column(sa.String(length=255), nullable=True)
@@ -250,46 +228,42 @@ class HarvestCacheTask(model.Base):
return self.name or ''
-class HarvestCacheTimeEntry(model.Base):
+class HarvestTimeEntry(model.Base):
"""
Represents a time entry record in Harvest.
https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#the-time-entry-object
"""
- __tablename__ = 'harvest_cache_time_entry'
+ __tablename__ = 'harvest_time_entry'
__table_args__ = (
- sa.UniqueConstraint('id', name='harvest_cache_time_entry_uq_id'),
- sa.ForeignKeyConstraint(['user_id'], ['harvest_cache_user.id'],
- name='harvest_cache_time_entry_fk_user'),
- sa.ForeignKeyConstraint(['client_id'], ['harvest_cache_client.id'],
- name='harvest_cache_time_entry_fk_client'),
- sa.ForeignKeyConstraint(['project_id'], ['harvest_cache_project.id'],
- name='harvest_cache_time_entry_fk_project'),
- sa.ForeignKeyConstraint(['task_id'], ['harvest_cache_task.id'],
- name='harvest_cache_time_entry_fk_task'),
+ sa.UniqueConstraint('id', name='harvest_time_entry_uq_id'),
+ sa.ForeignKeyConstraint(['user_id'], ['harvest_user.id'], name='harvest_time_entry_fk_user'),
+ sa.ForeignKeyConstraint(['client_id'], ['harvest_client.id'], name='harvest_time_entry_fk_client'),
+ sa.ForeignKeyConstraint(['project_id'], ['harvest_project.id'], name='harvest_time_entry_fk_project'),
+ sa.ForeignKeyConstraint(['task_id'], ['harvest_task.id'], name='harvest_time_entry_fk_task'),
)
__versioned__ = {}
model_title_plural = "Harvest Time Entries"
uuid = model.uuid_column()
- id = sa.Column(sa.BigInteger(), nullable=False)
+ id = sa.Column(sa.Integer(), nullable=False)
spent_date = sa.Column(sa.Date(), nullable=True)
- user_id = sa.Column(sa.BigInteger(), nullable=True)
- user = orm.relationship(HarvestCacheUser, backref=orm.backref('time_entries'))
+ user_id = sa.Column(sa.Integer(), nullable=True)
+ user = orm.relationship(HarvestUser, backref=orm.backref('time_entries'))
- client_id = sa.Column(sa.BigInteger(), nullable=True)
- client = orm.relationship(HarvestCacheClient, backref=orm.backref('time_entries'))
+ client_id = sa.Column(sa.Integer(), nullable=True)
+ client = orm.relationship(HarvestClient, backref=orm.backref('time_entries'))
- project_id = sa.Column(sa.BigInteger(), nullable=True)
- project = orm.relationship(HarvestCacheProject, backref=orm.backref('time_entries'))
+ project_id = sa.Column(sa.Integer(), nullable=True)
+ project = orm.relationship(HarvestProject, backref=orm.backref('time_entries'))
- task_id = sa.Column(sa.BigInteger(), nullable=True)
- task = orm.relationship(HarvestCacheTask, backref=orm.backref('time_entries'))
+ task_id = sa.Column(sa.Integer(), nullable=True)
+ task = orm.relationship(HarvestTask, backref=orm.backref('time_entries'))
- invoice_id = sa.Column(sa.BigInteger(), nullable=True)
+ invoice_id = sa.Column(sa.Integer(), nullable=True)
hours = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
diff --git a/rattail_harvest/harvest/config.py b/rattail_harvest/harvest/config.py
deleted file mode 100644
index 3a2cbd6..0000000
--- a/rattail_harvest/harvest/config.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Harvest config
-"""
-
-
-def get_harvest_url(config):
- url = config.get('harvest', 'url')
- if url:
- return url.rstrip('/')
diff --git a/rattail_harvest/harvest/importing/__init__.py b/rattail_harvest/harvest/importing/__init__.py
deleted file mode 100644
index e0b3e09..0000000
--- a/rattail_harvest/harvest/importing/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Exporting data to Harvest
-"""
-
-from . import model
diff --git a/rattail_harvest/harvest/importing/model.py b/rattail_harvest/harvest/importing/model.py
deleted file mode 100644
index eacbeb0..0000000
--- a/rattail_harvest/harvest/importing/model.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 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 .
-#
-################################################################################
-"""
-Harvest model importers
-"""
-
-from rattail import importing
-from rattail_harvest.harvest.webapi import make_harvest_webapi
-
-
-class ToHarvest(importing.Importer):
-
- def setup(self):
- super().setup()
- self.setup_webapi()
-
- def datasync_setup(self):
- super().datasync_setup()
- self.setup_webapi()
-
- def setup_webapi(self):
- self.webapi = make_harvest_webapi(self.config)
-
-
-class TimeEntryImporter(ToHarvest):
- """
- Harvest time entry data importer.
- """
- model_name = 'TimeEntry'
- key = 'id'
- supported_fields = [
- 'id',
- 'user_id',
- 'client_id',
- 'project_id',
- 'task_id',
- 'spent_date',
- # 'started_time',
- # 'ended_time',
- 'hours',
- 'notes',
- ]
- caches_local_data = True
-
- def cache_local_data(self, host_data=None):
- """
- Fetch existing time entries from Harvest.
- """
- cache = {}
-
- # TODO: we try to avoid entries w/ timer still running here,
- # but for some reason they still come back, so double-check
- kw = {'is_running': False}
- if self.start_date:
- kw['from'] = self.start_date
- if self.end_date:
- kw['to'] = self.end_date
- entries = self.webapi.get_time_entries(**kw)
- for entry in entries:
- # double-check here
- if not entry['is_running']:
- data = self.normalize_local_object(entry)
- if data:
- normal = self.normalize_cache_object(entry, data)
- key = self.get_cache_key(entry, normal)
- cache[key] = normal
- return cache
-
- def get_single_local_object(self, key):
- assert len(self.key) == 1 and self.key[0] == 'id'
- entry_id = key[0]
- if entry_id > 0:
- return self.webapi.get_time_entry(entry_id)
-
- def normalize_local_object(self, entry):
- data = {
- 'id': entry['id'],
- 'client_id': entry['client']['id'],
- 'project_id': entry['project']['id'],
- 'task_id': entry['task']['id'],
- 'spent_date': entry['spent_date'],
- # 'started_time': entry['started_time'],
- # 'ended_time': entry['ended_time'],
- 'hours': entry['hours'],
- 'notes': entry['notes'],
- }
-
- if 'user_id' in self.fields:
- data['user_id'] = entry['user']['id']
-
- return data
-
- def get_next_harvest_id(self):
- if hasattr(self, 'next_harvest_id'):
- next_id = self.next_harvest_id
- else:
- next_id = 1
- self.next_harvest_id = next_id + 1
- return -next_id
-
- def create_object(self, key, host_data):
- if self.dry_run:
- # mock out return value
- result = dict(host_data)
- if 'user_id' in self.fields:
- result['user'] = {'id': result['user_id']}
- if 'client_id' in self.fields:
- result['client'] = {'id': result['client_id']}
- result['project'] = {'id': result['project_id']}
- result['task'] = {'id': result['task_id']}
- return result
-
- kwargs = {
- 'client_id': host_data['client_id'],
- 'project_id': host_data['project_id'],
- 'task_id': host_data['task_id'],
- 'spent_date': host_data['spent_date'],
- # 'started_time': host_data['started_time'],
- # 'ended_time': host_data['ended_time'],
- 'hours': host_data['hours'],
- 'notes': host_data['notes'],
- }
- if 'user_id' in self.fields:
- kwargs['user_id'] = host_data['user_id']
- entry = self.webapi.put_time_entry(**kwargs)
- return entry
-
- def update_object(self, entry, host_data, local_data=None, all_fields=False):
- if self.dry_run:
- return entry
-
- kwargs = {
- 'project_id': host_data['project_id'],
- 'task_id': host_data['task_id'],
- 'spent_date': host_data['spent_date'],
- # 'started_time': host_data['started_time'],
- # 'ended_time': host_data['ended_time'],
- 'hours': host_data['hours'],
- 'notes': host_data['notes'],
- }
-
- return self.webapi.update_time_entry(entry['id'], **kwargs)
-
- def delete_object(self, entry):
- if self.dry_run:
- return True
-
- self.webapi.delete_time_entry(entry['id'])
- return True
diff --git a/rattail_harvest/harvest/webapi.py b/rattail_harvest/harvest/webapi.py
index fafdcb8..6107b19 100644
--- a/rattail_harvest/harvest/webapi.py
+++ b/rattail_harvest/harvest/webapi.py
@@ -58,12 +58,6 @@ class HarvestWebAPI(object):
elif request_method == 'POST':
response = requests.post('{}/{}'.format(self.base_url, api_method),
headers=headers, params=params)
- elif request_method == 'PATCH':
- response = requests.patch('{}/{}'.format(self.base_url, api_method),
- headers=headers, params=params)
- elif request_method == 'DELETE':
- response = requests.delete('{}/{}'.format(self.base_url, api_method),
- headers=headers, params=params)
else:
raise NotImplementedError("unknown request method: {}".format(
request_method))
@@ -82,18 +76,6 @@ class HarvestWebAPI(object):
"""
return self._request('POST', api_method, params=params)
- def patch(self, api_method, params=None):
- """
- Perform a PATCH request for the given API method, and return the response.
- """
- return self._request('PATCH', api_method, params=params)
-
- def delete(self, api_method, params=None):
- """
- Perform a DELETE request for the given API method, and return the response.
- """
- return self._request('DELETE', api_method, params=params)
-
def get_company(self):
"""
Retrieves the company for the currently authenticated user.
@@ -131,17 +113,7 @@ class HarvestWebAPI(object):
https://help.getharvest.com/api-v2/projects-api/projects/projects/#list-all-projects
"""
response = self.get('/projects', params=kwargs)
- data = response.json()
- projects = data['projects']
- while data['next_page']:
-
- kw = dict(kwargs)
- kw['page'] = data['next_page']
- response = self.get('/projects', params=kw)
- data = response.json()
- projects.extend(data['projects'])
-
- return projects
+ return response.json()
def get_tasks(self, **kwargs):
"""
@@ -179,15 +151,10 @@ class HarvestWebAPI(object):
https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#retrieve-a-time-entry
"""
- try:
- response = self.get('/time_entries/{}'.format(time_entry_id))
- except requests.exceptions.HTTPError as error:
- if error.response.status_code != 404:
- raise
- else:
- return response.json()
+ response = self.get('/time_entries/{}'.format(time_entry_id))
+ return response.json()
- def create_time_entry(self, **kwargs):
+ def put_time_entry(self, **kwargs):
"""
Create a new time entry. All kwargs are passed on as POST parameters.
@@ -200,41 +167,3 @@ class HarvestWebAPI(object):
raise ValueError("must provide all of: {}".format(', '.join(required)))
response = self.post('/time_entries', params=kwargs)
return response.json()
-
- # TODO: deprecate / remove this
- put_time_entry = create_time_entry
-
- def stop_time_entry(self, time_entry_id):
- """
- Stop a running time entry.
-
- https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#stop-a-running-time-entry
- """
- response = self.patch('/time_entries/{}/stop'.format(time_entry_id))
- return response.json()
-
- def update_time_entry(self, time_entry_id, **kwargs):
- """
- Update a time entry.
-
- https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#update-a-time-entry
- """
- response = self.patch('/time_entries/{}'.format(time_entry_id), params=kwargs)
- return response.json()
-
- def delete_time_entry(self, time_entry_id, **kwargs):
- """
- Delete a time entry.
-
- https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#delete-a-time-entry
- """
- self.delete('/time_entries/{}'.format(time_entry_id), params=kwargs)
-
-
-def make_harvest_webapi(config):
- access_token = config.require('harvest', 'api.access_token')
- account_id = config.require('harvest', 'api.account_id')
- user_agent = config.require('harvest', 'api.user_agent')
- return HarvestWebAPI(access_token=access_token,
- account_id=account_id,
- user_agent=user_agent)
diff --git a/rattail_harvest/importing/harvest.py b/rattail_harvest/importing/harvest.py
index 4f22ef1..8cbd9dd 100644
--- a/rattail_harvest/importing/harvest.py
+++ b/rattail_harvest/importing/harvest.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -27,13 +27,11 @@ Harvest -> Rattail "cache" data import
import datetime
import decimal
import logging
-from collections import OrderedDict
-
-import sqlalchemy as sa
from rattail import importing
+from rattail.util import OrderedDict
from rattail_harvest import importing as rattail_harvest_importing
-from rattail_harvest.harvest.webapi import make_harvest_webapi
+from rattail_harvest.harvest.webapi import HarvestWebAPI
log = logging.getLogger(__name__)
@@ -49,11 +47,11 @@ class FromHarvestToRattail(importing.ToRattailHandler):
def get_importers(self):
importers = OrderedDict()
- importers['HarvestCacheUser'] = HarvestCacheUserImporter
- importers['HarvestCacheClient'] = HarvestCacheClientImporter
- importers['HarvestCacheProject'] = HarvestCacheProjectImporter
- importers['HarvestCacheTask'] = HarvestCacheTaskImporter
- importers['HarvestCacheTimeEntry'] = HarvestCacheTimeEntryImporter
+ importers['HarvestUser'] = HarvestUserImporter
+ importers['HarvestClient'] = HarvestClientImporter
+ importers['HarvestProject'] = HarvestProjectImporter
+ importers['HarvestTask'] = HarvestTaskImporter
+ importers['HarvestTimeEntry'] = HarvestTimeEntryImporter
return importers
@@ -71,7 +69,13 @@ class FromHarvest(importing.Importer):
def setup(self):
super(FromHarvest, self).setup()
- self.webapi = make_harvest_webapi(self.config)
+
+ access_token = self.config.require('harvest', 'api.access_token')
+ account_id = self.config.require('harvest', 'api.account_id')
+ user_agent = self.config.require('harvest', 'api.user_agent')
+ self.webapi = HarvestWebAPI(access_token=access_token,
+ account_id=account_id,
+ user_agent=user_agent)
def time_from_harvest(self, value):
# all harvest times appear to come as UTC, so no conversion needed
@@ -90,17 +94,14 @@ class FromHarvest(importing.Importer):
return data
-class HarvestCacheUserImporter(FromHarvest, rattail_harvest_importing.model.HarvestCacheUserImporter):
+class HarvestUserImporter(FromHarvest, rattail_harvest_importing.model.HarvestUserImporter):
"""
Import user data from Harvest
"""
@property
def supported_fields(self):
- fields = list(super().supported_fields)
-
- # this is for local tracking only; is not in harvest
- fields.remove('person_uuid')
+ fields = list(super(HarvestUserImporter, self).supported_fields)
# this used to be in harvest i thought, but is no longer?
fields.remove('name')
@@ -110,28 +111,8 @@ class HarvestCacheUserImporter(FromHarvest, rattail_harvest_importing.model.Harv
def get_host_objects(self):
return self.webapi.get_users()['users']
- def normalize_host_object(self, user):
- data = super().normalize_host_object(user)
- if data:
- # TODO: for some reason the API used to include the these
- # fields, but no longer does as of 2022-11-11, so null is
- # kinda the only thing that makes sense now. if possible,
- # should figure out "what changed" at Harvest, but maybe
- # these fields should just be removed from our cache
- # schema?
- data.setdefault('is_admin', None)
- data.setdefault('is_project_manager', None)
- data.setdefault('can_see_rates', None)
- data.setdefault('can_create_invoices', None)
-
- if data['telephone'] == '':
- data['telephone'] = None
-
- return data
-
-
-class HarvestCacheClientImporter(FromHarvest, rattail_harvest_importing.model.HarvestCacheClientImporter):
+class HarvestClientImporter(FromHarvest, rattail_harvest_importing.model.HarvestClientImporter):
"""
Import client data from Harvest
"""
@@ -140,32 +121,16 @@ class HarvestCacheClientImporter(FromHarvest, rattail_harvest_importing.model.Ha
return self.webapi.get_clients()['clients']
-class HarvestCacheProjectImporter(FromHarvest, rattail_harvest_importing.model.HarvestCacheProjectImporter):
+class HarvestProjectImporter(FromHarvest, rattail_harvest_importing.model.HarvestProjectImporter):
"""
Import project data from Harvest
"""
- @property
- def supported_fields(self):
- fields = list(super().supported_fields)
-
- # this is for local tracking only; is not in harvest
- fields.remove('deleted')
-
- return fields
-
- def cache_query(self):
- model = self.model
- return self.session.query(model.HarvestCacheProject)\
- .filter(sa.or_(
- model.HarvestCacheProject.deleted == False,
- model.HarvestCacheProject.deleted == None))
-
def get_host_objects(self):
- return self.webapi.get_projects()
+ return self.webapi.get_projects()['projects']
def normalize_host_object(self, project):
- data = super().normalize_host_object(project)
+ data = super(HarvestProjectImporter, self).normalize_host_object(project)
if not data:
return
@@ -203,15 +168,8 @@ class HarvestCacheProjectImporter(FromHarvest, rattail_harvest_importing.model.H
return data
- def can_delete_object(self, project, data):
- return not project.deleted
- def delete_object(self, project):
- project.deleted = True
- return True
-
-
-class HarvestCacheTaskImporter(FromHarvest, rattail_harvest_importing.model.HarvestCacheTaskImporter):
+class HarvestTaskImporter(FromHarvest, rattail_harvest_importing.model.HarvestTaskImporter):
"""
Import task data from Harvest
"""
@@ -220,49 +178,40 @@ class HarvestCacheTaskImporter(FromHarvest, rattail_harvest_importing.model.Harv
return self.webapi.get_tasks()['tasks']
-class HarvestCacheTimeEntryImporter(FromHarvest, rattail_harvest_importing.model.HarvestCacheTimeEntryImporter):
+class HarvestTimeEntryImporter(FromHarvest, rattail_harvest_importing.model.HarvestTimeEntryImporter):
"""
Import time entry data from Harvest
"""
- def get_host_objects(self):
- kw = {}
- if self.start_date:
- kw['from'] = self.start_date
- if self.end_date:
- kw['to'] = self.end_date
- return self.webapi.get_time_entries(**kw)
+ def setup(self):
+ super(HarvestTimeEntryImporter, self).setup()
+ model = self.model
- def get_single_host_object(self, key):
- assert len(self.key) == 1 and self.key[0] == 'id'
- entry_id = key[0]
- return self.webapi.get_time_entry(entry_id)
+ self.harvest_projects_by_id = self.app.cache_model(self.session,
+ model.HarvestProject,
+ key='id')
+
+ def get_host_objects(self):
+ return self.webapi.get_time_entries(**{'from': self.start_date,
+ 'to': self.end_date})
def normalize_host_object(self, entry):
- data = super().normalize_host_object(entry)
+ data = super(HarvestTimeEntryImporter, self).normalize_host_object(entry)
if not data:
return
- if entry['is_running']:
- log.debug("Harvest time entry is still running: %s", entry)
- return
-
data['user_id'] = entry['user']['id']
data['client_id'] = entry['client']['id']
+
+ data['project_id'] = entry['project']['id']
+ if data['project_id'] not in self.harvest_projects_by_id:
+ log.warning("time entry references non-existent project id %s: %s",
+ data['project_id'], entry)
+ data['project_id'] = None
+
data['task_id'] = entry['task']['id']
data['invoice_id'] = entry['invoice']['id'] if entry['invoice'] else None
- # project_id
- if 'project_id' in self.fields:
- data['project_id'] = entry['project']['id']
- project = self.get_harvest_project(data['project_id'])
- if not project:
- logger = log.warning if self.warn_for_unknown_project else log.debug
- logger("time entry references non-existent project id %s: %s",
- data['project_id'], entry)
- if not self.auto_create_unknown_project:
- data['project_id'] = None
-
# spent_date
spent_date = data['spent_date']
if spent_date:
diff --git a/rattail_harvest/importing/model.py b/rattail_harvest/importing/model.py
index 5e35839..6fbcfdb 100644
--- a/rattail_harvest/importing/model.py
+++ b/rattail_harvest/importing/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -24,86 +24,30 @@
rattail-harvest model importers
"""
-import logging
-
from rattail.importing.model import ToRattail
from rattail_harvest.db import model
-log = logging.getLogger(__name__)
-
-
##############################
# harvest cache models
##############################
-class HarvestCacheUserImporter(ToRattail):
- model_class = model.HarvestCacheUser
+class HarvestUserImporter(ToRattail):
+ model_class = model.HarvestUser
-class HarvestCacheClientImporter(ToRattail):
- model_class = model.HarvestCacheClient
+class HarvestClientImporter(ToRattail):
+ model_class = model.HarvestClient
-class HarvestCacheProjectImporter(ToRattail):
- model_class = model.HarvestCacheProject
+class HarvestProjectImporter(ToRattail):
+ model_class = model.HarvestProject
-class HarvestCacheTaskImporter(ToRattail):
- model_class = model.HarvestCacheTask
+class HarvestTaskImporter(ToRattail):
+ model_class = model.HarvestTask
-class HarvestCacheTimeEntryImporter(ToRattail):
- model_class = model.HarvestCacheTimeEntry
-
- # flags to auto-create records for "unknown" references
- auto_create_unknown_project = True
-
- # flags to log warning vs. debug for "unknown" references
- warn_for_unknown_project = True
-
- def setup(self):
- super().setup()
- model = self.model
-
- if 'project_id' in self.fields:
- self.harvest_projects_by_id = self.app.cache_model(
- self.session, model.HarvestCacheProject, key='id')
+class HarvestTimeEntryImporter(ToRattail):
+ model_class = model.HarvestTimeEntry
def cache_query(self):
- query = super().cache_query()
-
- if self.start_date:
- query = query.filter(self.model_class.spent_date >= self.start_date)
- if self.end_date:
- query = query.filter(self.model_class.spent_date <= self.end_date)
-
- return query
-
- def get_harvest_project(self, project_id):
- if hasattr(self, 'harvest_projects_by_id'):
- return self.harvest_projects_by_id.get(project_id)
-
- model = self.model
- return self.session.query(model.HarvestCacheProject)\
- .filter(model.HarvestCacheProject.id == project_id)\
- .first()
-
- def update_object(self, entry, data, local_data=None):
- entry = super().update_object(entry, data, local_data)
- model = self.model
-
- if 'project_id' in self.fields:
- project_id = data['project_id']
- project = self.get_harvest_project(project_id)
- if not project:
- logger = log.warning if self.warn_for_unknown_project else log.debug
- logger("unknown project id %s for time entry id %s: %s",
- project_id, entry.id, entry)
- if self.auto_create_unknown_project:
- project = model.HarvestCacheProject()
- project.id = project_id
- project.name = "(unknown)"
- self.session.add(project)
- if hasattr(self, 'harvest_projects_by_id'):
- self.harvest_projects_by_id[project_id] = project
- elif entry.project_id:
- entry.project_id = None
-
- return entry
+ query = super(HarvestTimeEntryImporter, self).cache_query()
+ return query.filter(self.model_class.spent_date >= self.start_date)\
+ .filter(self.model_class.spent_date <= self.end_date)
diff --git a/rattail_harvest/importing/rattail.py b/rattail_harvest/importing/rattail.py
index ab67489..da1e9c6 100644
--- a/rattail_harvest/importing/rattail.py
+++ b/rattail_harvest/importing/rattail.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -34,11 +34,11 @@ class FromRattailToRattailHarvestMixin(object):
"""
def add_harvest_importers(self, importers):
- importers['HarvestCacheUser'] = HarvestCacheUserImporter
- importers['HarvestCacheClient'] = HarvestCacheClientImporter
- importers['HarvestCacheProject'] = HarvestCacheProjectImporter
- importers['HarvestCacheTask'] = HarvestCacheTaskImporter
- importers['HarvestCacheTimeEntry'] = HarvestCacheTimeEntryImporter
+ importers['HarvestUser'] = HarvestUserImporter
+ importers['HarvestClient'] = HarvestClientImporter
+ importers['HarvestProject'] = HarvestProjectImporter
+ importers['HarvestTask'] = HarvestTaskImporter
+ importers['HarvestTimeEntry'] = HarvestTimeEntryImporter
return importers
@@ -46,26 +46,17 @@ class FromRattailToRattailHarvestMixin(object):
# harvest cache models
##############################
-class HarvestCacheUserImporter(base.FromRattail, rattail_harvest_importing.model.HarvestCacheUserImporter):
+class HarvestUserImporter(base.FromRattail, rattail_harvest_importing.model.HarvestUserImporter):
pass
-class HarvestCacheClientImporter(base.FromRattail, rattail_harvest_importing.model.HarvestCacheClientImporter):
+class HarvestClientImporter(base.FromRattail, rattail_harvest_importing.model.HarvestClientImporter):
pass
-class HarvestCacheProjectImporter(base.FromRattail, rattail_harvest_importing.model.HarvestCacheProjectImporter):
+class HarvestProjectImporter(base.FromRattail, rattail_harvest_importing.model.HarvestProjectImporter):
pass
-class HarvestCacheTaskImporter(base.FromRattail, rattail_harvest_importing.model.HarvestCacheTaskImporter):
+class HarvestTaskImporter(base.FromRattail, rattail_harvest_importing.model.HarvestTaskImporter):
pass
-class HarvestCacheTimeEntryImporter(base.FromRattail, rattail_harvest_importing.model.HarvestCacheTimeEntryImporter):
-
- def query(self):
- query = super().query()
-
- if self.start_date:
- query = query.filter(self.model_class.spent_date >= self.start_date)
- if self.end_date:
- query = query.filter(self.model_class.spent_date <= self.end_date)
-
- return query
+class HarvestTimeEntryImporter(base.FromRattail, rattail_harvest_importing.model.HarvestTimeEntryImporter):
+ pass
diff --git a/rattail_harvest/importing/versions.py b/rattail_harvest/importing/versions.py
deleted file mode 100644
index e82d861..0000000
--- a/rattail_harvest/importing/versions.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 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 .
-#
-################################################################################
-"""
-Rattail -> Rattail "versions" data import
-"""
-
-from rattail.importing import versions as base
-
-
-class HarvestVersionMixin(object):
-
- def add_harvest_importers(self, importers):
- importers['HarvestCacheUser'] = HarvestCacheUserImporter
- importers['HarvestCacheClient'] = HarvestCacheClientImporter
- importers['HarvestCacheProject'] = HarvestCacheProjectImporter
- importers['HarvestCacheTask'] = HarvestCacheTaskImporter
- importers['HarvestCacheTimeEntry'] = HarvestCacheTimeEntryImporter
- return importers
-
-
-class HarvestCacheUserImporter(base.VersionImporter):
-
- @property
- def host_model_class(self):
- return self.model.HarvestCacheUser
-
-
-class HarvestCacheClientImporter(base.VersionImporter):
-
- @property
- def host_model_class(self):
- return self.model.HarvestCacheClient
-
-
-class HarvestCacheProjectImporter(base.VersionImporter):
-
- @property
- def host_model_class(self):
- return self.model.HarvestCacheProject
-
-
-class HarvestCacheTaskImporter(base.VersionImporter):
-
- @property
- def host_model_class(self):
- return self.model.HarvestCacheTask
-
-
-class HarvestCacheTimeEntryImporter(base.VersionImporter):
-
- @property
- def host_model_class(self):
- return self.model.HarvestCacheTimeEntry
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..c11e7a1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,106 @@
+# -*- 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 .
+#
+################################################################################
+"""
+rattail-harvest setup script
+"""
+
+import os
+from setuptools import setup, find_packages
+
+
+here = os.path.abspath(os.path.dirname(__file__))
+exec(open(os.path.join(here, 'rattail_harvest', '_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
+
+ 'invoke', # 1.5.0
+ 'rattail[db]', # 0.9.246
+]
+
+
+setup(
+ name = "rattail-harvest",
+ version = __version__,
+ author = "Lance Edgar",
+ author_email = "lance@edbob.org",
+ url = "https://rattailproject.org/",
+ description = "Rattail integration package for Harvest",
+ long_description = README,
+
+ classifiers = [
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Console',
+ '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,
+
+ entry_points = {
+
+ 'rattail.commands': [
+ 'import-harvest = rattail_harvest.commands:ImportHarvest',
+ ],
+
+ 'rattail.importing': [
+ 'to_rattail.from_harvest.import = rattail_harvest.importing.harvest:FromHarvestToRattail',
+ ],
+ },
+)
diff --git a/tasks.py b/tasks.py
index 3ed6102..6454e3f 100644
--- a/tasks.py
+++ b/tasks.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2024 Lance Edgar
+# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
@@ -25,36 +25,24 @@ Tasks for rattail-harvest
"""
import os
-import re
import shutil
from invoke import task
here = os.path.abspath(os.path.dirname(__file__))
-__version__ = None
-pattern = re.compile(r'^version = "(\d+\.\d+\.\d+)"$')
-with open(os.path.join(here, 'pyproject.toml'), 'rt') as f:
- for line in f:
- line = line.rstrip('\n')
- match = pattern.match(line)
- if match:
- __version__ = match.group(1)
- break
-if not __version__:
- raise RuntimeError("could not parse version!")
+exec(open(os.path.join(here, 'rattail_harvest', '_version.py')).read())
@task
-def release(c):
+def release(ctx):
"""
Release a new version of rattail-harvest
"""
# rebuild local tar.gz file for distribution
- if os.path.exists('rattail_harvest.egg-info'):
- shutil.rmtree('rattail_harvest.egg-info')
- c.run('python -m build --sdist')
+ shutil.rmtree('rattail_harvest.egg-info')
+ ctx.run('python setup.py sdist --formats=gztar')
# upload to public PyPI
- filename = f'rattail_harvest-{__version__}.tar.gz'
- c.run(f'twine upload dist/{filename}')
+ filename = 'rattail-harvest-{}.tar.gz'.format(__version__)
+ ctx.run('twine upload dist/{}'.format(filename))