Compare commits
No commits in common. "master" and "v0.1.1" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1 @@
|
||||||
*~
|
|
||||||
*.pyc
|
|
||||||
dist/
|
|
||||||
rattail_nationbuilder.egg-info/
|
rattail_nationbuilder.egg-info/
|
||||||
|
|
87
CHANGELOG.md
87
CHANGELOG.md
|
@ -5,93 +5,6 @@ All notable changes to rattail-nationbuilder will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
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).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## v0.3.4 (2024-08-19)
|
|
||||||
|
|
||||||
### Fix
|
|
||||||
|
|
||||||
- avoid deprecated method in app provider
|
|
||||||
- avoid deprecated base class for config extension
|
|
||||||
|
|
||||||
## v0.3.3 (2024-08-19)
|
|
||||||
|
|
||||||
### Fix
|
|
||||||
|
|
||||||
- avoid deprecated import for `parse_list()`
|
|
||||||
|
|
||||||
## v0.3.2 (2024-08-13)
|
|
||||||
|
|
||||||
### Fix
|
|
||||||
|
|
||||||
- update app provider entry point, per wuttjamaican
|
|
||||||
|
|
||||||
## 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.0] - 2024-05-29
|
|
||||||
### Changed
|
|
||||||
- Migrate all commands to use `typer`.
|
|
||||||
|
|
||||||
## [0.1.14] - 2023-12-01
|
|
||||||
### Changed
|
|
||||||
- Update subcommand entry point group names, per wuttjamaican.
|
|
||||||
|
|
||||||
## [0.1.13] - 2023-09-16
|
|
||||||
### Changed
|
|
||||||
- Limit page size to 100, for fetching Person records from NB API.
|
|
||||||
|
|
||||||
## [0.1.12] - 2023-09-15
|
|
||||||
### Changed
|
|
||||||
- Add rattail provider for NationBuilder integration.
|
|
||||||
|
|
||||||
## [0.1.11] - 2023-09-13
|
|
||||||
### Changed
|
|
||||||
- Fix schema inconsistencies.
|
|
||||||
|
|
||||||
## [0.1.10] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Assume null if NB person tags are empty.
|
|
||||||
|
|
||||||
## [0.1.9] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Add cache table, importer for NationBuilder donations.
|
|
||||||
|
|
||||||
## [0.1.8] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Fix manifest again..omg.
|
|
||||||
|
|
||||||
## [0.1.7] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Fix manifest...omg.
|
|
||||||
|
|
||||||
## [0.1.6] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Add alembic scripts to manifest.
|
|
||||||
|
|
||||||
## [0.1.5] - 2023-09-12
|
|
||||||
### Changed
|
|
||||||
- Add cache table, importer for NationBuilder People.
|
|
||||||
|
|
||||||
## [0.1.4] - 2023-09-07
|
|
||||||
### Changed
|
|
||||||
- Add web API methods for fetching donations from NationBuilder.
|
|
||||||
|
|
||||||
## [0.1.3] - 2023-05-25
|
|
||||||
### Changed
|
|
||||||
- Should actually use requests session for web api.
|
|
||||||
|
|
||||||
## [0.1.2] - 2023-05-17
|
|
||||||
### Changed
|
|
||||||
- Replace `setup.py` contents with `setup.cfg`.
|
|
||||||
|
|
||||||
## [0.1.1] - 2023-05-11
|
## [0.1.1] - 2023-05-11
|
||||||
### Changed
|
### Changed
|
||||||
- Add `max_retries` config for NationBuilder API.
|
- Add `max_retries` config for NationBuilder API.
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
include *.md
|
include *.md
|
||||||
include *.rst
|
include *.rst
|
||||||
recursive-include rattail_nationbuilder/db/alembic *.py
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["hatchling"]
|
|
||||||
build-backend = "hatchling.build"
|
|
||||||
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "rattail-nationbuilder"
|
|
||||||
version = "0.3.4"
|
|
||||||
description = "Rattail integration package for NationBuilder"
|
|
||||||
readme = "README.md"
|
|
||||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
|
||||||
license = {text = "GNU GPL v3+"}
|
|
||||||
classifiers = [
|
|
||||||
"Development Status :: 4 - Beta",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
||||||
"Natural Language :: English",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Topic :: Office/Business",
|
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
||||||
]
|
|
||||||
dependencies = [
|
|
||||||
"rattail",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
[project.entry-points."rattail.typer_imports"]
|
|
||||||
rattail_nationbuilder = "rattail_nationbuilder.commands"
|
|
||||||
|
|
||||||
|
|
||||||
[project.entry-points."rattail.config.extensions"]
|
|
||||||
rattail_nationbuilder = "rattail_nationbuilder.config:RattailNationBuilderExtension"
|
|
||||||
|
|
||||||
|
|
||||||
[project.entry-points."rattail.importing"]
|
|
||||||
"to_rattail.from_nationbuilder.import" = "rattail_nationbuilder.importing.nationbuilder:FromNationBuilderToRattail"
|
|
||||||
|
|
||||||
|
|
||||||
[project.entry-points."wutta.app.providers"]
|
|
||||||
rattail_nationbuilder = "rattail_nationbuilder.app:NationBuilderProvider"
|
|
||||||
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
Homepage = "https://rattailproject.org"
|
|
||||||
Repository = "https://forgejo.wuttaproject.org/rattail/rattail-nationbuilder"
|
|
||||||
Changelog = "https://forgejo.wuttaproject.org/rattail/rattail-nationbuilder/src/branch/master/CHANGELOG.md"
|
|
||||||
|
|
||||||
|
|
||||||
[tool.commitizen]
|
|
||||||
version_provider = "pep621"
|
|
||||||
tag_format = "v$version"
|
|
||||||
update_changelog_on_bump = true
|
|
|
@ -1,6 +1,3 @@
|
||||||
# -*- coding: utf-8; -*-
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
from importlib.metadata import version
|
__version__ = '0.1.1'
|
||||||
|
|
||||||
|
|
||||||
__version__ = version('rattail-nationbuilder')
|
|
||||||
|
|
|
@ -1,56 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
App Handler supplement
|
|
||||||
"""
|
|
||||||
|
|
||||||
from rattail.app import RattailProvider, GenericHandler
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderProvider(RattailProvider):
|
|
||||||
"""
|
|
||||||
App provider for NationBuilder integration.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_nationbuilder_handler(self, **kwargs):
|
|
||||||
if 'nationbuilder' not in self.handlers:
|
|
||||||
spec = self.config.get('rattail', 'nationbuilder.handler',
|
|
||||||
default='rattail_nationbuilder.app:NationBuilderHandler')
|
|
||||||
factory = self.app.load_object(spec)
|
|
||||||
self.handlers['nationbuilder'] = factory(self.config, **kwargs)
|
|
||||||
return self.handlers['nationbuilder']
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderHandler(GenericHandler):
|
|
||||||
"""
|
|
||||||
Handler for NationBuilder integration.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_url(self, require=False, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the base URL for the NationBuilder web app.
|
|
||||||
"""
|
|
||||||
getter = self.config.require if require else self.config.get
|
|
||||||
url = getter('nationbuilder', 'url')
|
|
||||||
if url:
|
|
||||||
return url.rstrip('/')
|
|
|
@ -1,48 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
rattail-nationbuilder 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
|
|
||||||
|
|
||||||
|
|
||||||
@rattail_typer.command()
|
|
||||||
@importer_command
|
|
||||||
def import_nationbuilder(
|
|
||||||
ctx: typer.Context,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Import data for NationBuilder => Rattail
|
|
||||||
"""
|
|
||||||
config = ctx.parent.rattail_config
|
|
||||||
progress = ctx.parent.rattail_progress
|
|
||||||
handler = ImportCommandHandler(
|
|
||||||
config, import_handler_key='to_rattail.from_nationbuilder.import')
|
|
||||||
kwargs['user'] = typer_get_runas_user(ctx)
|
|
||||||
handler.run(kwargs, progress=progress)
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
Config Extension
|
|
||||||
"""
|
|
||||||
|
|
||||||
from wuttjamaican.conf import WuttaConfigExtension
|
|
||||||
|
|
||||||
|
|
||||||
class RattailNationBuilderExtension(WuttaConfigExtension):
|
|
||||||
"""
|
|
||||||
Config extension for rattail-nationbuilder
|
|
||||||
"""
|
|
||||||
key = 'rattail_nationbuilder'
|
|
||||||
|
|
||||||
def configure(self, config):
|
|
||||||
|
|
||||||
# rattail import-nationbuilder
|
|
||||||
config.setdefault('rattail.importing.to_rattail.from_nationbulder.import.default_handler',
|
|
||||||
'rattail_nationbuilder.importing.nationbuilder:FromNationBuilderToRattail')
|
|
||||||
config.setdefault('rattail.importing.to_rattail.from_nationbuilder.import.default_cmd',
|
|
||||||
'rattail import-nationbuilder')
|
|
|
@ -1,91 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
"""first cache tables
|
|
||||||
|
|
||||||
Revision ID: 1e17031c4b3e
|
|
||||||
Revises: fa3aec1556bc
|
|
||||||
Create Date: 2023-09-12 15:05:08.853989
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '1e17031c4b3e'
|
|
||||||
down_revision = None
|
|
||||||
branch_labels = ('rattail_nationbuilder',)
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
import rattail.db.types
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_person
|
|
||||||
op.create_table('nationbuilder_cache_person',
|
|
||||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('email', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('email_opt_in', sa.Boolean(), nullable=True),
|
|
||||||
sa.Column('external_id', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('first_name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('middle_name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('last_name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('mobile', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('mobile_opt_in', sa.Boolean(), nullable=True),
|
|
||||||
sa.Column('note', sa.Text(), nullable=True),
|
|
||||||
sa.Column('phone', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('primary_image_url_ssl', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('signup_type', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('primary_address_address1', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('primary_address_address2', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('primary_address_city', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('primary_address_state', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('primary_address_zip', sa.String(length=10), nullable=True),
|
|
||||||
sa.Column('tags', sa.Text(), nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('uuid'),
|
|
||||||
sa.UniqueConstraint('id', name='nationbuilder_cache_person_uq_id')
|
|
||||||
)
|
|
||||||
op.create_table('nationbuilder_cache_person_version',
|
|
||||||
sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('email', sa.String(length=255), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('email_opt_in', sa.Boolean(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('external_id', sa.String(length=50), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('first_name', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('middle_name', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('last_name', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('mobile', sa.String(length=50), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('mobile_opt_in', sa.Boolean(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('note', sa.Text(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('phone', sa.String(length=50), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_image_url_ssl', sa.String(length=255), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('signup_type', sa.Integer(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_address_address1', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_address_address2', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_address_city', sa.String(length=100), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_address_state', sa.String(length=50), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('primary_address_zip', sa.String(length=10), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('tags', sa.Text(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True),
|
|
||||||
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
|
|
||||||
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
|
|
||||||
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint('uuid', 'transaction_id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_person_version_end_transaction_id'), 'nationbuilder_cache_person_version', ['end_transaction_id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_person_version_operation_type'), 'nationbuilder_cache_person_version', ['operation_type'], unique=False)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_person_version_transaction_id'), 'nationbuilder_cache_person_version', ['transaction_id'], unique=False)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_person
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_person_version_transaction_id'), table_name='nationbuilder_cache_person_version')
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_person_version_operation_type'), table_name='nationbuilder_cache_person_version')
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_person_version_end_transaction_id'), table_name='nationbuilder_cache_person_version')
|
|
||||||
op.drop_table('nationbuilder_cache_person_version')
|
|
||||||
op.drop_table('nationbuilder_cache_person')
|
|
|
@ -1,37 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
"""fix nullable
|
|
||||||
|
|
||||||
Revision ID: 7fc3dee0e9c5
|
|
||||||
Revises: c3cb75afcae2
|
|
||||||
Create Date: 2023-09-13 09:12:33.740638
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '7fc3dee0e9c5'
|
|
||||||
down_revision = 'c3cb75afcae2'
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
import rattail.db.types
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_donation
|
|
||||||
op.alter_column('nationbuilder_cache_donation_version', 'id',
|
|
||||||
existing_type=sa.INTEGER(),
|
|
||||||
nullable=True,
|
|
||||||
autoincrement=False)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_donation
|
|
||||||
op.alter_column('nationbuilder_cache_donation_version', 'id',
|
|
||||||
existing_type=sa.INTEGER(),
|
|
||||||
nullable=False,
|
|
||||||
autoincrement=False)
|
|
|
@ -1,81 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
"""add donations cache table
|
|
||||||
|
|
||||||
Revision ID: c3cb75afcae2
|
|
||||||
Revises: 1e17031c4b3e
|
|
||||||
Create Date: 2023-09-12 19:30:47.583505
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = 'c3cb75afcae2'
|
|
||||||
down_revision = '1e17031c4b3e'
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
import rattail.db.types
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_donation
|
|
||||||
op.create_table('nationbuilder_cache_donation',
|
|
||||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('author_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('membership_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('donor_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('donor_external_id', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('email', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('amount', sa.Numeric(precision=10, scale=2), nullable=True),
|
|
||||||
sa.Column('payment_type_name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('check_number', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('tracking_code_slug', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('note', sa.Text(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('succeeded_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('failed_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('canceled_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.PrimaryKeyConstraint('uuid'),
|
|
||||||
sa.UniqueConstraint('id', name='nationbuilder_cache_donation_uq_id')
|
|
||||||
)
|
|
||||||
op.create_table('nationbuilder_cache_donation_version',
|
|
||||||
sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), autoincrement=False, nullable=False),
|
|
||||||
sa.Column('author_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('membership_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('donor_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('donor_external_id', sa.String(length=50), nullable=True),
|
|
||||||
sa.Column('email', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('amount', sa.Numeric(precision=10, scale=2), nullable=True),
|
|
||||||
sa.Column('payment_type_name', sa.String(length=100), nullable=True),
|
|
||||||
sa.Column('check_number', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('tracking_code_slug', sa.String(length=255), nullable=True),
|
|
||||||
sa.Column('note', sa.Text(), nullable=True),
|
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('succeeded_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('failed_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('canceled_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
|
||||||
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
|
|
||||||
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
|
|
||||||
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint('uuid', 'transaction_id')
|
|
||||||
)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_donation_version_end_transaction_id'), 'nationbuilder_cache_donation_version', ['end_transaction_id'], unique=False)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_donation_version_operation_type'), 'nationbuilder_cache_donation_version', ['operation_type'], unique=False)
|
|
||||||
op.create_index(op.f('ix_nationbuilder_cache_donation_version_transaction_id'), 'nationbuilder_cache_donation_version', ['transaction_id'], unique=False)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
|
|
||||||
# nationbuilder_cache_donation
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_donation_version_transaction_id'), table_name='nationbuilder_cache_donation_version')
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_donation_version_operation_type'), table_name='nationbuilder_cache_donation_version')
|
|
||||||
op.drop_index(op.f('ix_nationbuilder_cache_donation_version_end_transaction_id'), table_name='nationbuilder_cache_donation_version')
|
|
||||||
op.drop_table('nationbuilder_cache_donation_version')
|
|
||||||
op.drop_table('nationbuilder_cache_donation')
|
|
|
@ -1,27 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
DB schema for NationBuilder integration
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .nationbuilder import NationBuilderCachePerson, NationBuilderCacheDonation
|
|
|
@ -1,116 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
NationBuilder cache tables
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
|
||||||
from sqlalchemy import orm
|
|
||||||
|
|
||||||
from wuttjamaican.util import parse_list
|
|
||||||
|
|
||||||
from rattail.db import model
|
|
||||||
from rattail.db.util import normalize_full_name
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCachePerson(model.Base):
|
|
||||||
"""
|
|
||||||
Represents a Person record in NationBuilder.
|
|
||||||
|
|
||||||
https://apiexplorer.nationbuilder.com/nationbuilder#People
|
|
||||||
"""
|
|
||||||
__tablename__ = 'nationbuilder_cache_person'
|
|
||||||
__table_args__ = (
|
|
||||||
sa.UniqueConstraint('id', name='nationbuilder_cache_person_uq_id'),
|
|
||||||
)
|
|
||||||
__versioned__ = {}
|
|
||||||
model_title = "NationBuilder Person"
|
|
||||||
model_title_plural = "NationBuilder People"
|
|
||||||
|
|
||||||
uuid = model.uuid_column()
|
|
||||||
|
|
||||||
id = sa.Column(sa.Integer(), nullable=False)
|
|
||||||
signup_type = sa.Column(sa.Integer(), nullable=True)
|
|
||||||
external_id = sa.Column(sa.String(length=50), nullable=True)
|
|
||||||
tags = sa.Column(sa.Text(), nullable=True)
|
|
||||||
first_name = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
middle_name = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
last_name = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
email = sa.Column(sa.String(length=255), nullable=True)
|
|
||||||
email_opt_in = sa.Column(sa.Boolean(), nullable=True)
|
|
||||||
mobile = sa.Column(sa.String(length=50), nullable=True)
|
|
||||||
mobile_opt_in = sa.Column(sa.Boolean(), nullable=True)
|
|
||||||
phone = sa.Column(sa.String(length=50), nullable=True)
|
|
||||||
primary_address_address1 = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
primary_address_address2 = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
primary_address_city = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
primary_address_state = sa.Column(sa.String(length=50), nullable=True)
|
|
||||||
primary_address_zip = sa.Column(sa.String(length=10), nullable=True)
|
|
||||||
primary_image_url_ssl = sa.Column(sa.String(length=255), nullable=True)
|
|
||||||
note = sa.Column(sa.Text(), nullable=True)
|
|
||||||
created_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
updated_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return normalize_full_name(self.first_name, self.last_name)
|
|
||||||
|
|
||||||
def has_tag(self, tag):
|
|
||||||
if self.tags:
|
|
||||||
for value in parse_list(self.tags):
|
|
||||||
if value == tag:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCacheDonation(model.Base):
|
|
||||||
"""
|
|
||||||
Represents a Donation record in NationBuilder.
|
|
||||||
|
|
||||||
https://apiexplorer.nationbuilder.com/nationbuilder#Donations
|
|
||||||
"""
|
|
||||||
__tablename__ = 'nationbuilder_cache_donation'
|
|
||||||
__table_args__ = (
|
|
||||||
sa.UniqueConstraint('id', name='nationbuilder_cache_donation_uq_id'),
|
|
||||||
)
|
|
||||||
__versioned__ = {}
|
|
||||||
model_title = "NationBuilder Donation"
|
|
||||||
model_title_plural = "NationBuilder Donations"
|
|
||||||
|
|
||||||
uuid = model.uuid_column()
|
|
||||||
|
|
||||||
id = sa.Column(sa.Integer(), nullable=False)
|
|
||||||
author_id = sa.Column(sa.Integer(), nullable=True)
|
|
||||||
membership_id = sa.Column(sa.Integer(), nullable=True)
|
|
||||||
donor_id = sa.Column(sa.Integer(), nullable=True)
|
|
||||||
donor_external_id = sa.Column(sa.String(length=50), nullable=True)
|
|
||||||
email = sa.Column(sa.String(length=255), nullable=True)
|
|
||||||
amount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
|
|
||||||
payment_type_name = sa.Column(sa.String(length=100), nullable=True)
|
|
||||||
check_number = sa.Column(sa.String(length=255), nullable=True)
|
|
||||||
tracking_code_slug = sa.Column(sa.String(length=255), nullable=True)
|
|
||||||
note = sa.Column(sa.Text(), nullable=True)
|
|
||||||
created_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
succeeded_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
failed_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
canceled_at = sa.Column(sa.DateTime(), nullable=True)
|
|
||||||
updated_at = sa.Column(sa.DateTime(), nullable=True)
|
|
|
@ -1,27 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
rattail-nationbuilder importing
|
|
||||||
"""
|
|
||||||
|
|
||||||
from . import model
|
|
|
@ -1,39 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
rattail-nationbuilder model importers
|
|
||||||
"""
|
|
||||||
|
|
||||||
from rattail.importing.model import ToRattail
|
|
||||||
from rattail_nationbuilder.db import model
|
|
||||||
|
|
||||||
|
|
||||||
##############################
|
|
||||||
# nationbuilder cache
|
|
||||||
##############################
|
|
||||||
|
|
||||||
class NationBuilderCachePersonImporter(ToRattail):
|
|
||||||
model_class = model.NationBuilderCachePerson
|
|
||||||
|
|
||||||
class NationBuilderCacheDonationImporter(ToRattail):
|
|
||||||
model_class = model.NationBuilderCacheDonation
|
|
|
@ -1,182 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
NationBuilder -> Rattail importing
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import decimal
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from rattail import importing
|
|
||||||
from rattail_nationbuilder import importing as nationbuilder_importing
|
|
||||||
from rattail_nationbuilder.nationbuilder.webapi import NationBuilderWebAPI
|
|
||||||
|
|
||||||
|
|
||||||
class FromNationBuilderToRattail(importing.ToRattailHandler):
|
|
||||||
"""
|
|
||||||
Import handler for NationBuilder -> Rattail
|
|
||||||
"""
|
|
||||||
host_key = 'nationbuilder'
|
|
||||||
host_title = "NationBuilder"
|
|
||||||
generic_host_title = "NationBuilder"
|
|
||||||
|
|
||||||
def get_importers(self):
|
|
||||||
importers = OrderedDict()
|
|
||||||
importers['NationBuilderCachePerson'] = NationBuilderCachePersonImporter
|
|
||||||
importers['NationBuilderCacheDonation'] = NationBuilderCacheDonationImporter
|
|
||||||
return importers
|
|
||||||
|
|
||||||
|
|
||||||
class FromNationBuilder(importing.Importer):
|
|
||||||
"""
|
|
||||||
Base class for all NationBuilder importers
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
super().setup()
|
|
||||||
self.setup_api()
|
|
||||||
|
|
||||||
def setup_api(self):
|
|
||||||
self.nationbuilder = NationBuilderWebAPI(self.config)
|
|
||||||
|
|
||||||
def normalize_timestamp(self, value):
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
|
|
||||||
dt = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S%z')
|
|
||||||
dt = self.app.localtime(dt)
|
|
||||||
return self.app.make_utc(dt)
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCachePersonImporter(FromNationBuilder, nationbuilder_importing.model.NationBuilderCachePersonImporter):
|
|
||||||
"""
|
|
||||||
Importer for NB Person cache
|
|
||||||
"""
|
|
||||||
key = 'id'
|
|
||||||
|
|
||||||
primary_address_fields = [
|
|
||||||
'primary_address_address1',
|
|
||||||
'primary_address_address2',
|
|
||||||
'primary_address_city',
|
|
||||||
'primary_address_state',
|
|
||||||
'primary_address_zip',
|
|
||||||
]
|
|
||||||
|
|
||||||
supported_fields = [
|
|
||||||
'id',
|
|
||||||
'created_at',
|
|
||||||
'email',
|
|
||||||
'email_opt_in',
|
|
||||||
'external_id',
|
|
||||||
'first_name',
|
|
||||||
'middle_name',
|
|
||||||
'last_name',
|
|
||||||
'mobile',
|
|
||||||
'mobile_opt_in',
|
|
||||||
'note',
|
|
||||||
'phone',
|
|
||||||
'primary_image_url_ssl',
|
|
||||||
'signup_type',
|
|
||||||
'tags',
|
|
||||||
'updated_at',
|
|
||||||
] + primary_address_fields
|
|
||||||
|
|
||||||
def get_host_objects(self):
|
|
||||||
return self.nationbuilder.get_people(page_size=100,
|
|
||||||
progress=self.progress)
|
|
||||||
|
|
||||||
def normalize_host_object(self, person):
|
|
||||||
|
|
||||||
# nb. some fields may not be present in person dict
|
|
||||||
data = dict([(field, person.get(field))
|
|
||||||
for field in self.fields])
|
|
||||||
if data:
|
|
||||||
|
|
||||||
for field in ('created_at', 'updated_at'):
|
|
||||||
data[field] = self.normalize_timestamp(data[field])
|
|
||||||
|
|
||||||
if 'tags' in self.fields:
|
|
||||||
tags = data['tags']
|
|
||||||
if tags:
|
|
||||||
data['tags'] = self.config.make_list_string(tags)
|
|
||||||
else:
|
|
||||||
data['tags'] = None
|
|
||||||
|
|
||||||
if self.fields_active(self.primary_address_fields):
|
|
||||||
address = person.get('primary_address')
|
|
||||||
if address:
|
|
||||||
data.update({
|
|
||||||
'primary_address_address1': address['address1'],
|
|
||||||
'primary_address_address2': address['address2'],
|
|
||||||
'primary_address_state': address['state'],
|
|
||||||
'primary_address_city': address['city'],
|
|
||||||
'primary_address_zip': address['zip'],
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCacheDonationImporter(FromNationBuilder, nationbuilder_importing.model.NationBuilderCacheDonationImporter):
|
|
||||||
"""
|
|
||||||
Importer for NB Donation cache
|
|
||||||
"""
|
|
||||||
key = 'id'
|
|
||||||
supported_fields = [
|
|
||||||
'id',
|
|
||||||
'author_id',
|
|
||||||
'membership_id',
|
|
||||||
'donor_id',
|
|
||||||
'donor_external_id',
|
|
||||||
'email',
|
|
||||||
'amount',
|
|
||||||
'payment_type_name',
|
|
||||||
'check_number',
|
|
||||||
'tracking_code_slug',
|
|
||||||
'note',
|
|
||||||
'created_at',
|
|
||||||
'succeeded_at',
|
|
||||||
'failed_at',
|
|
||||||
'canceled_at',
|
|
||||||
'updated_at',
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_host_objects(self):
|
|
||||||
return self.nationbuilder.get_donations(page_size=500)
|
|
||||||
|
|
||||||
def normalize_host_object(self, donation):
|
|
||||||
|
|
||||||
# nb. some fields may not be present in donation dict
|
|
||||||
data = dict([(field, donation.get(field))
|
|
||||||
for field in self.fields])
|
|
||||||
if data:
|
|
||||||
|
|
||||||
donor = donation.get('donor')
|
|
||||||
data['donor_external_id'] = donor.get('external_id') if donor else None
|
|
||||||
|
|
||||||
data['amount'] = decimal.Decimal('{:0.2f}'.format(donation['amount_in_cents'] / 100))
|
|
||||||
|
|
||||||
for field in ('created_at', 'succeeded_at', 'failed_at', 'canceled_at', 'updated_at'):
|
|
||||||
data[field] = self.normalize_timestamp(data[field])
|
|
||||||
|
|
||||||
return data
|
|
|
@ -1,49 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
rattail-nationbuilder version importing
|
|
||||||
"""
|
|
||||||
|
|
||||||
from rattail.importing import versions as base
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderVersionMixin(object):
|
|
||||||
|
|
||||||
def add_nationbuilder_importers(self, importers):
|
|
||||||
importers['NationBuilderCachePerson'] = NationBuilderCachePersonImporter
|
|
||||||
importers['NationBuilderCacheDonation'] = NationBuilderCacheDonationImporter
|
|
||||||
return importers
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCachePersonImporter(base.VersionImporter):
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_model_class(self):
|
|
||||||
return self.model.NationBuilderCachePerson
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderCacheDonationImporter(base.VersionImporter):
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host_model_class(self):
|
|
||||||
return self.model.NationBuilderCacheDonation
|
|
|
@ -1,37 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
NationBuilder utils
|
|
||||||
"""
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
|
|
||||||
def get_nationbuilder_url(config):
|
|
||||||
warnings.warn("get_nationbuilder_url() function is deprecated; "
|
|
||||||
"please use nationbuilder_handler.get_url() instead",
|
|
||||||
DeprecationWarning, stacklevel=2)
|
|
||||||
|
|
||||||
app = config.get_app()
|
|
||||||
nationbuilder = app.get_nationbuilder_handler()
|
|
||||||
return nationbuilder.get_url()
|
|
|
@ -24,14 +24,9 @@
|
||||||
NationBuilder Web API
|
NationBuilder Web API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class NationBuilderWebAPI(object):
|
class NationBuilderWebAPI(object):
|
||||||
"""
|
"""
|
||||||
Simple web API for NationBuilder.
|
Simple web API for NationBuilder.
|
||||||
|
@ -43,6 +38,7 @@ class NationBuilderWebAPI(object):
|
||||||
max_retries=None, **kwargs):
|
max_retries=None, **kwargs):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.app = self.config.get_app()
|
self.app = self.config.get_app()
|
||||||
|
self.session = None
|
||||||
|
|
||||||
self.base_url = base_url or self.config.require(
|
self.base_url = base_url or self.config.require(
|
||||||
'nationbuilder', 'api.base_url')
|
'nationbuilder', 'api.base_url')
|
||||||
|
@ -73,8 +69,8 @@ class NationBuilderWebAPI(object):
|
||||||
params['access_token'] = self.access_token
|
params['access_token'] = self.access_token
|
||||||
|
|
||||||
if request_method == 'GET':
|
if request_method == 'GET':
|
||||||
response = self.session.get('{}/{}'.format(self.base_url, api_method),
|
response = requests.get('{}/{}'.format(self.base_url, api_method),
|
||||||
params=params)
|
params=params)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("unknown request method: {}".format(
|
raise NotImplementedError("unknown request method: {}".format(
|
||||||
|
@ -89,16 +85,12 @@ class NationBuilderWebAPI(object):
|
||||||
"""
|
"""
|
||||||
return self._request('GET', api_method, params=params)
|
return self._request('GET', api_method, params=params)
|
||||||
|
|
||||||
def get_people(self, page_size=10, max_pages=None, progress=None, **kwargs):
|
def get_people(self, page_size=100, progress=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Retrieve all Person records.
|
Retrieve all Person records.
|
||||||
|
|
||||||
https://apiexplorer.nationbuilder.com/nationbuilder#People
|
https://apiexplorer.nationbuilder.com/nationbuilder#People
|
||||||
"""
|
"""
|
||||||
# nb. found this limit in practice, but it is not documented?!
|
|
||||||
if page_size > 100:
|
|
||||||
raise ValueError("page_size cannot be more than 100")
|
|
||||||
|
|
||||||
response = self.get('/api/v1/people/count')
|
response = self.get('/api/v1/people/count')
|
||||||
count = response.json()['people_count']
|
count = response.json()['people_count']
|
||||||
pages = count // page_size
|
pages = count // page_size
|
||||||
|
@ -113,16 +105,12 @@ class NationBuilderWebAPI(object):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
people.extend(data['results'])
|
people.extend(data['results'])
|
||||||
url['next'] = data['next']
|
url['next'] = data['next']
|
||||||
log.debug("have fetched %s pages", page + 1)
|
|
||||||
|
|
||||||
pages = list(range(pages))
|
self.app.progress_loop(fetch, range(pages), progress,
|
||||||
if max_pages:
|
|
||||||
pages = pages[:max_pages]
|
|
||||||
self.app.progress_loop(fetch, pages, progress,
|
|
||||||
message="Fetching Person data from NationBuilder")
|
message="Fetching Person data from NationBuilder")
|
||||||
return people
|
return people
|
||||||
|
|
||||||
def get_people_with_tag(self, tag, page_size=10, max_pages=None, **kwargs):
|
def get_people_with_tag(self, tag, page_size=100, max_pages=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Retrieve all Person records with the given tag.
|
Retrieve all Person records with the given tag.
|
||||||
|
|
||||||
|
@ -148,62 +136,3 @@ class NationBuilderWebAPI(object):
|
||||||
pages += 1
|
pages += 1
|
||||||
|
|
||||||
return people
|
return people
|
||||||
|
|
||||||
def get_donations(self, page_size=10, max_pages=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Retrieve all Donation records.
|
|
||||||
|
|
||||||
https://apiexplorer.nationbuilder.com/nationbuilder#Donations
|
|
||||||
"""
|
|
||||||
donations = []
|
|
||||||
|
|
||||||
# get first page
|
|
||||||
url = f'/api/v1/donations?limit={page_size}'
|
|
||||||
response = self.get(url)
|
|
||||||
data = response.json()
|
|
||||||
donations.extend(data['results'])
|
|
||||||
pages = 1
|
|
||||||
|
|
||||||
# get more pages, until complete
|
|
||||||
while data['next']:
|
|
||||||
if max_pages and pages >= max_pages:
|
|
||||||
break
|
|
||||||
response = self.get(data['next'])
|
|
||||||
data = response.json()
|
|
||||||
donations.extend(data['results'])
|
|
||||||
pages += 1
|
|
||||||
log.debug("have fetched %s pages", pages)
|
|
||||||
|
|
||||||
return donations
|
|
||||||
|
|
||||||
def search_donations(self, page_size=10, max_pages=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Search for matching Donation records.
|
|
||||||
|
|
||||||
https://apiexplorer.nationbuilder.com/nationbuilder#Donations
|
|
||||||
"""
|
|
||||||
donations = []
|
|
||||||
|
|
||||||
# get first page
|
|
||||||
url = f'/api/v1/donations/search?limit={page_size}'
|
|
||||||
for field in ('created_since', 'succeeded_since', 'failed_since'):
|
|
||||||
value = kwargs.get(field)
|
|
||||||
if value:
|
|
||||||
value = value.strftime('%Y-%m-%dT%H:%M:%S%z')
|
|
||||||
url += f"&{field}={value}"
|
|
||||||
response = self.get(url)
|
|
||||||
data = response.json()
|
|
||||||
donations.extend(data['results'])
|
|
||||||
pages = 1
|
|
||||||
|
|
||||||
# get more pages, until complete
|
|
||||||
while data['next']:
|
|
||||||
if max_pages and pages >= max_pages:
|
|
||||||
break
|
|
||||||
response = self.get(data['next'])
|
|
||||||
data = response.json()
|
|
||||||
donations.extend(data['results'])
|
|
||||||
pages += 1
|
|
||||||
log.debug("have fetched %s pages", pages)
|
|
||||||
|
|
||||||
return donations
|
|
||||||
|
|
71
setup.py
Normal file
71
setup.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
rattail-nationbuilder 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_nationbuilder', '_version.py')).read())
|
||||||
|
README = open(os.path.join(here, 'README.md')).read()
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = "rattail-nationbuilder",
|
||||||
|
version = __version__,
|
||||||
|
author = "Lance Edgar",
|
||||||
|
author_email = "lance@edbob.org",
|
||||||
|
url = "https://rattailproject.org/",
|
||||||
|
license = "GNU GPL v3",
|
||||||
|
description = "Rattail integration package for NationBuilder",
|
||||||
|
long_description = README,
|
||||||
|
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'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 = [
|
||||||
|
'rattail',
|
||||||
|
|
||||||
|
# TODO: these may be needed to build/release package
|
||||||
|
#'build',
|
||||||
|
#'invoke',
|
||||||
|
#'twine',
|
||||||
|
],
|
||||||
|
packages = find_packages(),
|
||||||
|
include_package_data = True,
|
||||||
|
|
||||||
|
entry_points = {
|
||||||
|
},
|
||||||
|
)
|
15
tasks.py
15
tasks.py
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2024 Lance Edgar
|
# Copyright © 2010-2023 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -30,17 +30,22 @@ import shutil
|
||||||
from invoke import task
|
from invoke import task
|
||||||
|
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
exec(open(os.path.join(here, 'rattail_nationbuilder', '_version.py')).read())
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def release(c):
|
def release(c):
|
||||||
"""
|
"""
|
||||||
Release a new version of rattail-nationbuilder
|
Release a new version of rattail-nationbuilder
|
||||||
"""
|
"""
|
||||||
# rebuild package
|
# rebuild local tar.gz file for distribution
|
||||||
if os.path.exists('dist'):
|
|
||||||
shutil.rmtree('dist')
|
|
||||||
if os.path.exists('rattail_nationbuilder.egg-info'):
|
if os.path.exists('rattail_nationbuilder.egg-info'):
|
||||||
shutil.rmtree('rattail_nationbuilder.egg-info')
|
shutil.rmtree('rattail_nationbuilder.egg-info')
|
||||||
c.run('python -m build --sdist')
|
c.run('python -m build --sdist')
|
||||||
|
|
||||||
|
# filename of built package
|
||||||
|
filename = 'rattail-nationbuilder-{}.tar.gz'.format(__version__)
|
||||||
|
|
||||||
# upload to PyPI
|
# upload to PyPI
|
||||||
c.run('twine upload dist/*')
|
c.run('twine upload dist/{}'.format(filename))
|
||||||
|
|
Loading…
Reference in a new issue