testing sync from airtable

This commit is contained in:
Paul J Stevens 2023-12-30 23:52:32 +01:00
parent e9931f459a
commit 80a3bc7155
9 changed files with 286 additions and 40 deletions

View File

@ -11,7 +11,7 @@ setup:
TRYTOND_DATABASE_URI=postgresql://localhost/$(DBNAME) trytond-admin -d $(DBNAME) --all --verbose
run:
trytond --verbose --dev -c etc/dev.ini
trytond --verbose --dev --coroutine -c etc/dev.ini
frontend: frontend/package.json
cd frontend && yarn install && node_modules/grunt-cli/bin/grunt

151
pdm.lock
View File

@ -5,7 +5,7 @@
groups = ["default", "dev"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
content_hash = "sha256:64707686004a03b86458f59514eed6a8e2615ec63c5dd157086ce2f2552a785d"
content_hash = "sha256:15d622dcdf0e04a6d16b16e7e7e5da4c0fe78ec413e9c47ecd6e026d09b61f98"
[[package]]
name = "annotated-types"
@ -29,6 +29,41 @@ files = [
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
[[package]]
name = "cffi"
version = "1.16.0"
requires_python = ">=3.8"
summary = "Foreign Function Interface for Python calling C code."
groups = ["default"]
marker = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""
dependencies = [
"pycparser",
]
files = [
{file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
{file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
{file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
{file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
{file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
{file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
{file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
{file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
{file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
@ -149,6 +184,69 @@ files = [
{file = "Genshi-0.7.7.tar.gz", hash = "sha256:c100520862cd69085d10ee1a87e91289e7f59f6b3d9bd622bf58b2804e6b9aab"},
]
[[package]]
name = "gevent"
version = "23.9.1"
requires_python = ">=3.8"
summary = "Coroutine-based network library"
groups = ["default"]
dependencies = [
"cffi>=1.12.2; platform_python_implementation == \"CPython\" and sys_platform == \"win32\"",
"greenlet>=3.0rc3; platform_python_implementation == \"CPython\" and python_version >= \"3.11\"",
"zope-event",
"zope-interface",
]
files = [
{file = "gevent-23.9.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c"},
{file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648"},
{file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07"},
{file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9"},
{file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011"},
{file = "gevent-23.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7"},
{file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71"},
{file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e"},
{file = "gevent-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a"},
{file = "gevent-23.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904"},
{file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b"},
{file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e"},
{file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599"},
{file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea"},
{file = "gevent-23.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599"},
{file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303"},
{file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d"},
{file = "gevent-23.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1"},
{file = "gevent-23.9.1.tar.gz", hash = "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34"},
]
[[package]]
name = "greenlet"
version = "3.0.3"
requires_python = ">=3.7"
summary = "Lightweight in-process concurrent programming"
groups = ["default"]
marker = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""
files = [
{file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
{file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
{file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
{file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
{file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
]
[[package]]
name = "idna"
version = "3.6"
@ -356,6 +454,18 @@ files = [
{file = "pyairtable-2.2.1.tar.gz", hash = "sha256:1913cfa5a1fc98c4723fafcafde1b4cbc97546b7ea84f37e51f24d43f641f051"},
]
[[package]]
name = "pycparser"
version = "2.21"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
summary = "C parser in Python"
groups = ["default"]
marker = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""
files = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
[[package]]
name = "pydantic"
version = "2.5.3"
@ -1066,3 +1176,42 @@ files = [
{file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
]
[[package]]
name = "zope-event"
version = "5.0"
requires_python = ">=3.7"
summary = "Very basic event publishing system"
groups = ["default"]
dependencies = [
"setuptools",
]
files = [
{file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"},
{file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"},
]
[[package]]
name = "zope-interface"
version = "6.1"
requires_python = ">=3.7"
summary = "Interfaces for Python"
groups = ["default"]
dependencies = [
"setuptools",
]
files = [
{file = "zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1"},
{file = "zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736"},
{file = "zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605"},
{file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8"},
{file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de"},
{file = "zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1"},
{file = "zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a"},
{file = "zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7"},
{file = "zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d"},
{file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff"},
{file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0"},
{file = "zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b"},
{file = "zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309"},
]

View File

@ -18,6 +18,7 @@ dependencies = [
"pyairtable>=2.2.1",
"setuptools-scm>=8.0.4",
"marshmallow>=3.20.1",
"gevent>=23.9.1",
]
requires-python = ">=3.11"
readme = "README.md"

View File

@ -1,12 +1,16 @@
import json
import sys
import os
import typing
from dataclasses import dataclass
from decimal import Decimal
from datetime import date
import logging
import proteus
from pyairtable import Api
from marshmallow import Schema, fields, EXCLUDE
from marshmallow import Schema, fields, EXCLUDE, post_load
from marshmallow.exceptions import ValidationError
@ -16,63 +20,65 @@ handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
@dataclass
class Member:
name: str
first_name: str
last_name: str
email: str
street: str
postcode: str
city: str
status: str
join_date: date
period: str
amount: Decimal
iban: str
mollie_id: str
email: typing.Optional[str] = None
phone: typing.Optional[str] = None
address: typing.Optional[str] = None
postcode: typing.Optional[str] = None
city: typing.Optional[str] = None
status: typing.Optional[str] = None
since: typing.Optional[date] = None
until: typing.Optional[date] = None
iban: typing.Optional[str] = None
period: typing.Optional[str] = None
amount: typing.Optional[Decimal] = None
mandate: typing.Optional[str] = None
def __init__(self, **kwargs):
for attr, value in kwargs.items():
if attr in self.__annotations__:
setattr(self, attr, value)
class MemberSchema(Schema):
name = fields.Str()
first_name = fields.Str()
last_name = fields.Str()
email = fields.Email()
address = fields.Str()
postcode = fields.Str()
city = fields.Str()
since = fields.Date()
until = fields.Date()
status = fields.Str()
join_date = fields.Date()
periode = fields.Str()
amount = fields.Decimal()
iban = fields.Str()
mollie_id = fields.Str()
period = fields.Str()
amount = fields.Decimal()
mandate = fields.Str()
class Meta:
unknown = EXCLUDE
@post_load
def obj(self, data, **kwargs):
return Member(**data)
class Airtable:
field_map = {
"Name": "name",
"Voornaam": "first_name",
"Achternaam": "last_name",
"Straat": "address",
"Postcode": "postcode",
"Plaatsnaam": "city",
"Lidmaatschapsstatus": "status",
"Inschrijvingsdatum": "join_date",
"Inschrijvingsdatum": "since",
"Uitschrijvingsdatum": "until",
"IBAN": "iban",
"mollie_id": "mandate",
"periode": "period",
}
def __init__(self, key: str, base: str = "BIJ1", table: str = "leden"):
self._api = Api(key)
base, = [x for x in self._api.bases() if x.name == base]
(base,) = [x for x in self._api.bases() if x.name == base]
self._table = base.table(table)
self._schema = MemberSchema()
self.members: list[Member] = []
def iterate(self):
return self._table.iterate()
@ -82,10 +88,10 @@ class Airtable:
total = 0
for page in self.iterate():
for record in page:
records.append(record)
total += 1
if total % 100 == 0:
logger.debug(" %s", total)
records.append(record)
total += 1
if total % 100 == 0:
logger.debug(" %s", total)
json.dump(records, fd)
def load(self, filename: str):
@ -101,22 +107,54 @@ class Airtable:
if isinstance(value, str):
data[key] = value.strip()
all_fields = set()
for record in records:
data = record["fields"]
all_fields |= set(data.keys())
clean(data)
try:
self._schema.load(data)
self.members.append(self._schema.load(data))
except ValidationError as e:
logger.info("%s: %s", e, data)
logger.debug("all fieldnames: %s", all_fields)
logger.info("skipping %s: %s", e, data)
def sync(self, trytond_url: str):
proteus.config.set_xmlrpc(trytond_url)
party_obj = proteus.Model.get("party.party")
logger.info("sync members...")
for member in self.members:
logger.debug("member %s", member)
obj = party_obj.find([
("name", "=", member.name)
])
if obj:
obj = obj[0]
else:
obj = party_obj()
obj.name = member.name
address = obj.addresses[0]
address.street = member.address
address.postal_code = member.postcode
address.city = member.city
if not obj.contact_mechanisms.find([
("party", "=", obj.id),
("type", "=", "email"),
("value", "=", member.email),
]):
obj.contact_mechanisms.new(
type="email",
value=member.email
)
obj.save()
logger.info("sync members done")
def airtable():
if not (key := os.environ.get("AIRTABLE_ACCESS_TOKEN", None)):
logger.error("no access token for airtable!")
logger.error("no AIRTABLE_ACCESS_TOKEN")
return
if not (trytond_url := os.environ.get("TRYTOND_XMLRPC_URL", None)):
logger.error("no TRYTOND_XMLRPC_URL")
return
client = Airtable(key)
cache_file = "airtable.json"
@ -124,6 +162,8 @@ def airtable():
with open(cache_file, "w") as fd:
client.dump(fd)
client.load(cache_file)
client.sync(trytond_url)
if __name__ == "__main__":
airtable()

View File

@ -0,0 +1,8 @@
from trytond.pool import Pool
from . import membership
def register():
Pool.register(
membership.Membership, module='membership', type_='model')

View File

@ -0,0 +1,17 @@
from trytond.model import ModelSQL, fields
class Membership(ModelSQL):
"""Represents membership relations"""
__name__ = "membership.membership"
_rec_name = "membership_membership"
# a single party may have joined and left multiple times
party = fields.Many2One('party.party', "Party", required=True)
# date-range of membership period
since = fields.Date("since")
until = fields.Date("until")
# status of membership
status = fields.Char("status")

View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<tryton>
<data>
<record model="ir.ui.view" id="membership_view_tree">
<field name="model">membership.membership</field>
<field name="type">tree</field>
<field name="arch">
<![CDATA[
<tree string="Memberships">
<field name="party"/>
<field name="since"/>
<field name="until"/>
<field name="status"/>
</tree>
]]>
</field>
</record>
</data>
</tryton>

View File

@ -0,0 +1,9 @@
[tryton]
version=0.0.1
depends:
ir
res
party
xml:
membership.xml

View File

@ -1,8 +1,11 @@
import os
from proteus import config, Model
def test_client_connect():
config.set_xmlrpc("http://admin:admin@localhost:8000/bij1/")
trytond_url = os.environ.get("TRYTOND_XMLRPC_URL", None)
assert trytond_url
config.set_xmlrpc(trytond_url)
Module = Model.get("ir.module")
(party,) = Module.find([("name", "=", "party")])
assert party