commit 455027c5d0ceeca9a0cf878f734cc2bdab6c1560 Author: Lance Edgar Date: Sat Jan 29 14:51:59 2022 -0600 Initial commit; w/ views for cache tables diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ee5074 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tailbone_harvest.egg-info/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..309a36c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ + +# Changelog +All notable changes to tailbone-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). + +## [0.1.0] - ?? +### Added +- Initial version. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..1814f10 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include *.md +include *.rst +recursive-include tailbone_harvest/templates *.mako diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..4866f29 --- /dev/null +++ b/README.rst @@ -0,0 +1,14 @@ + +tailbone-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/setup.py b/setup.py new file mode 100644 index 0000000..d333d3f --- /dev/null +++ b/setup.py @@ -0,0 +1,96 @@ +# -*- 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 . +# +################################################################################ +""" +tailbone-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, 'tailbone_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-harvest', # 0.1.0 + 'Tailbone', # 0.8.199 +] + + +setup( + name = "tailbone-harvest", + version = __version__, + author = "Lance Edgar", + author_email = "lance@edbob.org", + url = "https://rattailproject.org/", + description = "Tailbone integration package for Harvest", + long_description = README, + + classifiers = [ + 'Development Status :: 3 - Alpha', + '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, +) diff --git a/tailbone_harvest/__init__.py b/tailbone_harvest/__init__.py new file mode 100644 index 0000000..c26efeb --- /dev/null +++ b/tailbone_harvest/__init__.py @@ -0,0 +1,27 @@ +# -*- 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 . +# +################################################################################ +""" +tailbone-harvest package root +""" + +from ._version import __version__ diff --git a/tailbone_harvest/_version.py b/tailbone_harvest/_version.py new file mode 100644 index 0000000..e41b669 --- /dev/null +++ b/tailbone_harvest/_version.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8; -*- + +__version__ = '0.1.0' diff --git a/tailbone_harvest/menus.py b/tailbone_harvest/menus.py new file mode 100644 index 0000000..2797eea --- /dev/null +++ b/tailbone_harvest/menus.py @@ -0,0 +1,74 @@ +# -*- 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 . +# +################################################################################ +""" +Common menus for Harvest +""" + + +def make_harvest_menu(request): + url = request.route_url + + harvest_menu = { + 'title': "Harvest", + 'type': 'menu', + 'items': [ + { + 'title': "Users", + 'url': url('harvest.users'), + 'perm': 'harvest.users.list', + }, + { + 'title': "Clients", + 'url': url('harvest.clients'), + 'perm': 'harvest.clients.list', + }, + { + 'title': "Projects", + 'url': url('harvest.projects'), + 'perm': 'harvest.projects.list', + }, + { + 'title': "Tasks", + 'url': url('harvest.tasks'), + 'perm': 'harvest.tasks.list', + }, + { + 'title': "Time Entries", + 'url': url('harvest.time_entries'), + 'perm': 'harvest.time_entries.list', + }, + ], + } + + harvest_url = request.rattail_config.get('harvest', 'url') + if harvest_url: + harvest_menu['items'].insert( + 0, { + 'title': "Go to Harvest", + 'url': harvest_url, + 'target': '_blank', + }) + harvest_menu['items'].insert( + 1, {'type': 'sep'}) + + return harvest_menu diff --git a/tailbone_harvest/static/__init__.py b/tailbone_harvest/static/__init__.py new file mode 100644 index 0000000..8435a03 --- /dev/null +++ b/tailbone_harvest/static/__init__.py @@ -0,0 +1,30 @@ +# -*- 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 . +# +################################################################################ +""" +Static assets for tailbone-harvest +""" + + +def includeme(config): + config.add_static_view('tailbone_harvest', 'tailbone_harvest:static', + cache_max_age=3600) diff --git a/tailbone_harvest/templates/.keepme b/tailbone_harvest/templates/.keepme new file mode 100644 index 0000000..e69de29 diff --git a/tailbone_harvest/views/__init__.py b/tailbone_harvest/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tailbone_harvest/views/harvest/__init__.py b/tailbone_harvest/views/harvest/__init__.py new file mode 100644 index 0000000..0101c7c --- /dev/null +++ b/tailbone_harvest/views/harvest/__init__.py @@ -0,0 +1,35 @@ +# -*- 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 Views +""" + +from .master import HarvestMasterView + + +def includeme(config): + config.include('tailbone_harvest.views.harvest.users') + config.include('tailbone_harvest.views.harvest.clients') + config.include('tailbone_harvest.views.harvest.projects') + config.include('tailbone_harvest.views.harvest.tasks') + config.include('tailbone_harvest.views.harvest.time_entries') diff --git a/tailbone_harvest/views/harvest/clients.py b/tailbone_harvest/views/harvest/clients.py new file mode 100644 index 0000000..1f7be2b --- /dev/null +++ b/tailbone_harvest/views/harvest/clients.py @@ -0,0 +1,82 @@ +# -*- 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 Client views +""" + +from rattail_harvest.db.model import HarvestClient + +from webhelpers2.html import HTML, tags + +from .master import HarvestMasterView + + +class HarvestClientView(HarvestMasterView): + """ + Master view for Harvest Clients + """ + model_class = HarvestClient + url_prefix = '/harvest/clients' + route_prefix = 'harvest.clients' + + grid_columns = [ + 'id', + 'name', + 'currency', + ] + + def configure_grid(self, g): + super(HarvestClientView, self).configure_grid(g) + + g.filters['name'].default_active = True + g.filters['name'].default_verb = 'contains' + + g.set_sort_defaults('name') + + g.set_link('id') + g.set_link('name') + + def configure_form(self, f): + super(HarvestClientView, self).configure_form(f) + + # projects + f.set_renderer('projects', self.render_projects) + + # time_entries + f.remove_field('time_entries') + + def render_projects(self, client, field): + projects = client.projects + if not projects: + return + + items = [] + for project in projects: + text = "({}) {}".format(project.code, project.name) + url = self.request.route_url('harvest.projects.view', uuid=project.uuid) + items.append(HTML.tag('li', c=[tags.link_to(text, url)])) + return HTML.tag('ul', c=items) + + +def includeme(config): + HarvestClientView.defaults(config) diff --git a/tailbone_harvest/views/harvest/master.py b/tailbone_harvest/views/harvest/master.py new file mode 100644 index 0000000..5d206ac --- /dev/null +++ b/tailbone_harvest/views/harvest/master.py @@ -0,0 +1,41 @@ +# -*- 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 master view +""" + +from tailbone.views import MasterView + + +class HarvestMasterView(MasterView): + """ + Base class for Harvest master views + """ + creatable = False + editable = False + deletable = False + has_versions = True + + labels = { + 'id': "ID", + } diff --git a/tailbone_harvest/views/harvest/projects.py b/tailbone_harvest/views/harvest/projects.py new file mode 100644 index 0000000..30b8a9d --- /dev/null +++ b/tailbone_harvest/views/harvest/projects.py @@ -0,0 +1,74 @@ +# -*- 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 Project views +""" + +from rattail_harvest.db.model import HarvestProject + +from .master import HarvestMasterView + + +class HarvestProjectView(HarvestMasterView): + """ + Master view for Harvest Projects + """ + model_class = HarvestProject + url_prefix = '/harvest/projects' + route_prefix = 'harvest.projects' + + grid_columns = [ + 'id', + 'client', + 'name', + 'code', + 'is_active', + 'is_billable', + 'bill_by', + 'hourly_rate', + 'fee', + ] + + def configure_grid(self, g): + super(HarvestProjectView, self).configure_grid(g) + model = self.model + + g.set_joiner('client', lambda q: q.outerjoin(model.HarvestClient)) + g.set_sorter('client', model.HarvestClient.name) + g.set_filter('client', model.HarvestClient.name, label="Client Name") + g.filters['client'].default_active = True + g.filters['client'].default_verb = 'contains' + + g.set_type('hourly_rate', 'currency') + g.set_type('fee', 'currency') + + g.set_sort_defaults('client') + + g.set_link('id') + g.set_link('client') + g.set_link('name') + g.set_link('code') + + +def includeme(config): + HarvestProjectView.defaults(config) diff --git a/tailbone_harvest/views/harvest/tasks.py b/tailbone_harvest/views/harvest/tasks.py new file mode 100644 index 0000000..a3b61b4 --- /dev/null +++ b/tailbone_harvest/views/harvest/tasks.py @@ -0,0 +1,65 @@ +# -*- 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 Task views +""" + +from rattail_harvest.db.model import HarvestTask + +from .master import HarvestMasterView + + +class HarvestTaskView(HarvestMasterView): + """ + Master view for Harvest Tasks + """ + model_class = HarvestTask + url_prefix = '/harvest/tasks' + route_prefix = 'harvest.tasks' + + grid_columns = [ + 'id', + 'name', + 'billable_by_default', + 'default_hourly_rate', + 'is_default', + 'is_active', + ] + + def configure_grid(self, g): + super(HarvestTaskView, self).configure_grid(g) + + g.set_sort_defaults('name') + + g.set_link('id') + g.set_link('name') + + def configure_form(self, f): + super(HarvestTaskView, self).configure_form(f) + + # time_entries + f.remove_field('time_entries') + + +def includeme(config): + HarvestTaskView.defaults(config) diff --git a/tailbone_harvest/views/harvest/time_entries.py b/tailbone_harvest/views/harvest/time_entries.py new file mode 100644 index 0000000..3ddfdab --- /dev/null +++ b/tailbone_harvest/views/harvest/time_entries.py @@ -0,0 +1,73 @@ +# -*- 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 Time Entry views +""" + +from rattail_harvest.db.model import HarvestTimeEntry + +from .master import HarvestMasterView + + +class HarvestTimeEntryView(HarvestMasterView): + """ + Master view for Harvest Time Entries + """ + model_class = HarvestTimeEntry + url_prefix = '/harvest/time-entries' + route_prefix = 'harvest.time_entries' + + labels = { + 'user_id': "User ID", + 'client_id': "Client ID", + 'project_id': "Project ID", + 'task_id': "Task ID", + 'invoice_id': "Invoice ID", + } + + grid_columns = [ + 'id', + 'spent_date', + 'user', + 'client', + 'project', + 'task', + 'hours', + 'notes', + ] + + def configure_grid(self, g): + super(HarvestTimeEntryView, self).configure_grid(g) + + g.set_type('hours', 'duration_hours') + + g.set_sort_defaults('spent_date', 'desc') + + g.set_link('id') + g.set_link('user') + g.set_link('client') + g.set_link('notes') + + +def includeme(config): + HarvestTimeEntryView.defaults(config) diff --git a/tailbone_harvest/views/harvest/users.py b/tailbone_harvest/views/harvest/users.py new file mode 100644 index 0000000..8f87fb0 --- /dev/null +++ b/tailbone_harvest/views/harvest/users.py @@ -0,0 +1,68 @@ +# -*- 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 User views +""" + +from rattail_harvest.db.model import HarvestUser + +from .master import HarvestMasterView + + +class HarvestUserView(HarvestMasterView): + """ + Master view for Harvest Users + """ + model_class = HarvestUser + url_prefix = '/harvest/users' + route_prefix = 'harvest.users' + + grid_columns = [ + 'id', + 'first_name', + 'last_name', + 'email', + 'telephone', + 'timezone', + 'is_admin', + ] + + def configure_grid(self, g): + super(HarvestUserView, self).configure_grid(g) + + g.set_sort_defaults('first_name') + + g.set_link('id') + g.set_link('first_name') + g.set_link('last_name') + g.set_link('email') + + def configure_form(self, f): + super(HarvestUserView, self).configure_form(f) + + # TODO: should add this as child rows/grid instead + f.remove('time_entries') + + +def includeme(config): + HarvestUserView.defaults(config) diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..88b264a --- /dev/null +++ b/tasks.py @@ -0,0 +1,48 @@ +# -*- 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 . +# +################################################################################ +""" +Tasks for tailbone-harvest +""" + +import os +import shutil + +from invoke import task + + +here = os.path.abspath(os.path.dirname(__file__)) +exec(open(os.path.join(here, 'tailbone_harvest', '_version.py')).read()) + + +@task +def release(ctx): + """ + Release a new version of tailbone-harvest + """ + # rebuild local tar.gz file for distribution + shutil.rmtree('tailbone_harvest.egg-info') + ctx.run('python setup.py sdist --formats=gztar') + + # upload to public PyPI + filename = 'tailbone-harvest-{}.tar.gz'.format(__version__) + ctx.run('twine upload dist/{}'.format(filename))