tariochbctools 1.3.0__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/PKG-INFO +2 -1
  2. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/importers.rst +49 -0
  3. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/setup.cfg +2 -0
  4. tariochbctools-1.4.0/src/tariochbctools/importers/awardwalletimp/config.py +93 -0
  5. tariochbctools-1.4.0/src/tariochbctools/importers/awardwalletimp/importer.py +160 -0
  6. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/PKG-INFO +2 -1
  7. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/SOURCES.txt +4 -0
  8. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/entry_points.txt +1 -0
  9. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/requires.txt +1 -0
  10. tariochbctools-1.4.0/tests/tariochbctools/importers/test_awardwallet.py +315 -0
  11. tariochbctools-1.4.0/tests/tariochbctools/plugins/__init__.py +0 -0
  12. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/.coveragerc +0 -0
  13. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/.isort.cfg +0 -0
  14. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/.pre-commit-config.yaml +0 -0
  15. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/.readthedocs.yml +0 -0
  16. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/LICENSE.txt +0 -0
  17. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/MANIFEST.in +0 -0
  18. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/README.rst +0 -0
  19. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/_static/.gitignore +0 -0
  20. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/conf.py +0 -0
  21. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/index.rst +0 -0
  22. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/installation.rst +0 -0
  23. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/license.rst +0 -0
  24. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/plugins.rst +0 -0
  25. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/price_fetchers.rst +0 -0
  26. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/docs/requirements.txt +0 -0
  27. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/pyproject.toml +0 -0
  28. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/setup.py +0 -0
  29. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/__init__.py +0 -0
  30. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/__init__.py +0 -0
  31. {tariochbctools-1.3.0/src/tariochbctools/importers/bcge → tariochbctools-1.4.0/src/tariochbctools/importers/awardwalletimp}/__init__.py +0 -0
  32. {tariochbctools-1.3.0/src/tariochbctools/importers/bitst → tariochbctools-1.4.0/src/tariochbctools/importers/bcge}/__init__.py +0 -0
  33. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/bcge/importer.py +0 -0
  34. {tariochbctools-1.3.0/src/tariochbctools/importers/blockchain → tariochbctools-1.4.0/src/tariochbctools/importers/bitst}/__init__.py +0 -0
  35. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/bitst/importer.py +0 -0
  36. {tariochbctools-1.3.0/src/tariochbctools/importers/cembrastatement → tariochbctools-1.4.0/src/tariochbctools/importers/blockchain}/__init__.py +0 -0
  37. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/blockchain/importer.py +0 -0
  38. {tariochbctools-1.3.0/src/tariochbctools/importers/general → tariochbctools-1.4.0/src/tariochbctools/importers/cembrastatement}/__init__.py +0 -0
  39. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/cembrastatement/importer.py +0 -0
  40. {tariochbctools-1.3.0/src/tariochbctools/importers/ibkr → tariochbctools-1.4.0/src/tariochbctools/importers/general}/__init__.py +0 -0
  41. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/general/deduplication.py +0 -0
  42. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/general/mailAdapterImporter.py +0 -0
  43. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/general/mt940importer.py +0 -0
  44. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/general/priceLookup.py +0 -0
  45. {tariochbctools-1.3.0/src/tariochbctools/importers/neon → tariochbctools-1.4.0/src/tariochbctools/importers/ibkr}/__init__.py +0 -0
  46. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/ibkr/importer.py +0 -0
  47. {tariochbctools-1.3.0/src/tariochbctools/importers/netbenefits → tariochbctools-1.4.0/src/tariochbctools/importers/neon}/__init__.py +0 -0
  48. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/neon/importer.py +0 -0
  49. {tariochbctools-1.3.0/src/tariochbctools/importers/nordigen → tariochbctools-1.4.0/src/tariochbctools/importers/netbenefits}/__init__.py +0 -0
  50. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/netbenefits/importer.py +0 -0
  51. {tariochbctools-1.3.0/src/tariochbctools/importers/postfinance → tariochbctools-1.4.0/src/tariochbctools/importers/nordigen}/__init__.py +0 -0
  52. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/nordigen/importer.py +0 -0
  53. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/nordigen/nordigen_config.py +0 -0
  54. {tariochbctools-1.3.0/src/tariochbctools/importers/quickfile → tariochbctools-1.4.0/src/tariochbctools/importers/postfinance}/__init__.py +0 -0
  55. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/postfinance/importer.py +0 -0
  56. {tariochbctools-1.3.0/src/tariochbctools/importers/radicant → tariochbctools-1.4.0/src/tariochbctools/importers/quickfile}/__init__.py +0 -0
  57. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/quickfile/importer.py +0 -0
  58. {tariochbctools-1.3.0/src/tariochbctools/importers/revolut → tariochbctools-1.4.0/src/tariochbctools/importers/radicant}/__init__.py +0 -0
  59. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/radicant/importer.py +0 -0
  60. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/raiffeisench/importer.py +0 -0
  61. {tariochbctools-1.3.0/src/tariochbctools/importers/schedule → tariochbctools-1.4.0/src/tariochbctools/importers/revolut}/__init__.py +0 -0
  62. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/revolut/importer.py +0 -0
  63. {tariochbctools-1.3.0/src/tariochbctools/importers/swisscard → tariochbctools-1.4.0/src/tariochbctools/importers/schedule}/__init__.py +0 -0
  64. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/schedule/importer.py +0 -0
  65. {tariochbctools-1.3.0/src/tariochbctools/importers/transferwise → tariochbctools-1.4.0/src/tariochbctools/importers/swisscard}/__init__.py +0 -0
  66. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/swisscard/importer.py +0 -0
  67. {tariochbctools-1.3.0/src/tariochbctools/importers/truelayer → tariochbctools-1.4.0/src/tariochbctools/importers/transferwise}/__init__.py +0 -0
  68. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/transferwise/importer.py +0 -0
  69. {tariochbctools-1.3.0/src/tariochbctools/importers/viseca → tariochbctools-1.4.0/src/tariochbctools/importers/truelayer}/__init__.py +0 -0
  70. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/truelayer/importer.py +0 -0
  71. {tariochbctools-1.3.0/src/tariochbctools/importers/zak → tariochbctools-1.4.0/src/tariochbctools/importers/viseca}/__init__.py +0 -0
  72. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/viseca/importer.py +0 -0
  73. {tariochbctools-1.3.0/src/tariochbctools/importers/zkb → tariochbctools-1.4.0/src/tariochbctools/importers/zak}/__init__.py +0 -0
  74. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/zak/importer.py +0 -0
  75. {tariochbctools-1.3.0/src/tariochbctools/plugins/prices → tariochbctools-1.4.0/src/tariochbctools/importers/zkb}/__init__.py +0 -0
  76. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/importers/zkb/importer.py +0 -0
  77. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/plugins/__init__.py +0 -0
  78. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/plugins/check_portfolio_sum.py +0 -0
  79. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/plugins/generate_base_ccy_prices.py +0 -0
  80. {tariochbctools-1.3.0/tests → tariochbctools-1.4.0/src/tariochbctools/plugins/prices}/__init__.py +0 -0
  81. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools/plugins/prices/ibkr.py +0 -0
  82. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/dependency_links.txt +0 -0
  83. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/not-zip-safe +0 -0
  84. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/src/tariochbctools.egg-info/top_level.txt +0 -0
  85. {tariochbctools-1.3.0/tests/tariochbctools → tariochbctools-1.4.0/tests}/__init__.py +0 -0
  86. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/conftest.py +0 -0
  87. {tariochbctools-1.3.0/tests/tariochbctools/importers → tariochbctools-1.4.0/tests/tariochbctools}/__init__.py +0 -0
  88. {tariochbctools-1.3.0/tests/tariochbctools/plugins → tariochbctools-1.4.0/tests/tariochbctools/importers}/__init__.py +0 -0
  89. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/importers/test_quickfile.py +0 -0
  90. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/importers/test_truelayer.py +0 -0
  91. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/entry_already_exists_expected.beancount +0 -0
  92. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/entry_already_exists_input.beancount +0 -0
  93. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/issue122_expected.beancount +0 -0
  94. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/issue122_input.beancount +0 -0
  95. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/missing_fx_expected.beancount +0 -0
  96. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/missing_fx_input.beancount +0 -0
  97. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/normal_expected.beancount +0 -0
  98. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/data/generate_base_ccy_prices/normal_input.beancount +0 -0
  99. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tests/tariochbctools/plugins/test_generate_base_ccy_prices.py +0 -0
  100. {tariochbctools-1.3.0 → tariochbctools-1.4.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tariochbctools
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Importers, plugins and price fetchers for Beancount
5
5
  Home-page: https://github.com/tarioch/beancounttools/
6
6
  Author: Patrick Ruckstuhl
@@ -20,6 +20,7 @@ Classifier: License :: OSI Approved :: MIT License
20
20
  Description-Content-Type: text/x-rst; charset=UTF-8
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: importlib-metadata; python_version < "3.8"
23
+ Requires-Dist: awardwallet
23
24
  Requires-Dist: beancount>=3
24
25
  Requires-Dist: beangulp
25
26
  Requires-Dist: beanprice
@@ -443,3 +443,52 @@ Import PDF from `radicant <https://radicant.com/>`__
443
443
  from tariochbctools.importers.radicant import importer as radicant
444
444
 
445
445
  CONFIG = [radicant.Importer("Account.*\.pdf", "Assets:Radicant")]
446
+
447
+ AwardWallet
448
+ ------------------------------
449
+
450
+ Import from `AwardWallet <https://awardwallet.com/>`__ using their `Account Access API <https://awardwallet.com/api/account>`__.
451
+
452
+ As of 2025 AwardWallet integrates over 460 airline, hotel, shopping and other loyalty programmes.
453
+
454
+ Follow the instructions in the `API documentation <https://awardwallet.com/api/account#introduction>`__ to register for a free Business account and create an API key.
455
+
456
+ The API key is restricted to the **allowed IP addresses** you specify in the Business interface API Settings.
457
+
458
+ Link and authorize personal accounts using the included ``awardwallet-conf`` CLI tool:
459
+
460
+ .. code-block:: console
461
+
462
+ awardwallet-conf --api-key YOUR_API_KEY get_link_url
463
+
464
+
465
+ Generate a config file for all linked users called (or ending with) ``awardwallet.yaml`` in your import location (e.g. download folder) and edit it to your needs.
466
+ Note that not all providers support retrieval of transaction history.
467
+
468
+ .. code-block:: console
469
+
470
+ awardwallet-conf --api-key YOUR_API_KEY generate > awardwallet.yaml
471
+
472
+ Example configuration file:
473
+
474
+ .. code-block:: yaml
475
+
476
+ api_key: YOUR_API_KEY
477
+ users:
478
+ 12345:
479
+ name: John Smith
480
+ all_history: false
481
+ accounts:
482
+ 7654321:
483
+ provider: "British Airways Club"
484
+ account: Assets:Current:Points
485
+ currency: AVIOS
486
+
487
+
488
+ Finally, initialize the importer:
489
+
490
+ .. code-block:: python
491
+
492
+ from tariochbctools.importers.awardwalletimp import importer as awimp
493
+
494
+ CONFIG = [awimp.Importer()]
@@ -29,6 +29,7 @@ package_dir =
29
29
  =src
30
30
  install_requires =
31
31
  importlib-metadata; python_version<"3.8"
32
+ awardwallet
32
33
  beancount>=3
33
34
  beangulp
34
35
  beanprice
@@ -61,6 +62,7 @@ testing =
61
62
  [options.entry_points]
62
63
  console_scripts =
63
64
  nordigen-conf = tariochbctools.importers.nordigen.nordigen_config:run
65
+ awardwallet-conf = tariochbctools.importers.awardwalletimp.config:run
64
66
 
65
67
  [test]
66
68
  extras = True
@@ -0,0 +1,93 @@
1
+ import argparse
2
+ import sys
3
+ import uuid
4
+ from typing import Any
5
+
6
+ import yaml
7
+ from awardwallet import AwardWalletClient
8
+ from awardwallet.api import AccessLevel
9
+
10
+
11
+ def get_link_url(client):
12
+ connection_url = client.get_connection_link(
13
+ platform="desktop",
14
+ access_level=AccessLevel.READ_ALL_EXCEPT_PASSWORDS,
15
+ state=str(uuid.uuid4()),
16
+ )
17
+ print( # noqa: T201
18
+ "Redirect your user to this URL to authorize the connection."
19
+ "\nThis link expires in 10 minutes:"
20
+ )
21
+ print(connection_url) # noqa: T201
22
+
23
+
24
+ def generate(client):
25
+ """
26
+ Generate a config for a user including user_id and account_id list.
27
+ Output in yaml format.
28
+ """
29
+ config = {}
30
+ config["api_key"] = client.api_key
31
+ config["users"] = {}
32
+
33
+ connected_users = client.list_connected_users()
34
+
35
+ for user in connected_users:
36
+ user_id = user["userId"]
37
+ user_details = client.get_connected_user_details(user_id)
38
+ account_config = {}
39
+
40
+ for account in user_details.get("accounts", []):
41
+ account_config[account["accountId"]] = {
42
+ "provider": account["displayName"],
43
+ "account": "Assets:Current:Points", # Placeholder, user should adjust
44
+ "currency": "POINTS",
45
+ }
46
+
47
+ config["users"][user_id] = {
48
+ "name": user["userName"],
49
+ "all_history": False,
50
+ "accounts": account_config,
51
+ }
52
+
53
+ yaml.dump(config, sys.stdout, sort_keys=False)
54
+
55
+
56
+ def parse_args(args: Any) -> Any:
57
+ parser = argparse.ArgumentParser(description="awardwallet-config")
58
+ sub_parsers = parser.add_subparsers(dest="mode", required=True)
59
+
60
+ parser.add_argument(
61
+ "--api-key",
62
+ required=True,
63
+ help="API key, can be generated on AwardWallet Business interface",
64
+ )
65
+
66
+ sub_parsers.add_parser(
67
+ "get_link_url", help="Generate a connection link URL for user authorization"
68
+ )
69
+ sub_parsers.add_parser(
70
+ "generate", help="Generate a configuration template for connected users"
71
+ )
72
+
73
+ return parser.parse_args(args)
74
+
75
+
76
+ def main(args: Any) -> None:
77
+ args = parse_args(args)
78
+
79
+ client = AwardWalletClient(args.api_key)
80
+
81
+ if args.mode == "get_link_url":
82
+ get_link_url(client)
83
+ elif args.mode == "generate":
84
+ generate(client)
85
+
86
+
87
+ def run() -> None:
88
+ """Entry point for console_scripts"""
89
+ main(sys.argv[1:])
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main(sys.argv[1:])
@@ -0,0 +1,160 @@
1
+ import logging
2
+ from os import path
3
+ from typing import Any
4
+
5
+ import beangulp
6
+ import dateutil.parser
7
+ import yaml
8
+ from awardwallet import AwardWalletClient
9
+ from beancount.core import amount, data
10
+ from beancount.core.number import D
11
+
12
+
13
+ class Importer(beangulp.Importer):
14
+ """An importer for AwardWallet"""
15
+
16
+ def _configure(self, filepath: str, existing: data.Entries) -> None:
17
+ with open(filepath, "r") as f:
18
+ self.config = yaml.safe_load(f)
19
+ self.api_key = self.config["api_key"]
20
+
21
+ def identify(self, filepath: str) -> bool:
22
+ return path.basename(filepath).endswith("awardwallet.yaml")
23
+
24
+ def account(self, filepath: str) -> data.Account:
25
+ return ""
26
+
27
+ def extract(self, filepath: str, existing: data.Entries = None) -> data.Entries:
28
+ self._configure(filepath, existing)
29
+ client = AwardWalletClient(self.api_key)
30
+ entries = []
31
+
32
+ for user_id, user in self.config["users"].items():
33
+ user_details = client.get_connected_user_details(user_id)
34
+
35
+ if user.get("accounts"):
36
+ if user.get("all_history", False):
37
+ entries.extend(self._extract_account_history(user, client))
38
+ else:
39
+ entries.extend(self._extract_user_history(user, user_details))
40
+ else:
41
+ logging.warning(
42
+ "Ignoring user ID %s: no accounts configured",
43
+ user_id,
44
+ )
45
+
46
+ return entries
47
+
48
+ def _extract_user_history(
49
+ self, user: dict, user_details: dict
50
+ ) -> list[data.Transaction]:
51
+ """
52
+ User history is limited to the last 10 history elements per account
53
+ """
54
+ entries = []
55
+ for account in user_details["accounts"]:
56
+ account_id = account["accountId"]
57
+
58
+ if account_id in user["accounts"]:
59
+ logging.info("Extracting account ID %s", account_id)
60
+ account_config = user["accounts"][account_id]
61
+
62
+ entries.extend(
63
+ self._extract_transactions(
64
+ account["history"], account_config, account_id
65
+ )
66
+ )
67
+ else:
68
+ logging.warning(
69
+ "Ignoring account ID %s: %s", account_id, account["displayName"]
70
+ )
71
+ return entries
72
+
73
+ def _extract_account_history(
74
+ self, user: dict, client: AwardWalletClient
75
+ ) -> list[data.Transaction]:
76
+ entries = []
77
+ for account_id, account_config in user["accounts"].items():
78
+ logging.info("Extracting account ID %s", account_id)
79
+ account = client.get_account_details(account_id)["account"]
80
+
81
+ entries.extend(
82
+ self._extract_transactions(
83
+ account["history"], account_config, account_id
84
+ )
85
+ )
86
+ return entries
87
+
88
+ def _extract_transactions(
89
+ self,
90
+ history: list,
91
+ account_config: dict,
92
+ account_id: str,
93
+ ) -> list[data.Transaction]:
94
+ local_account = account_config["account"]
95
+ currency = account_config["currency"]
96
+
97
+ logging.debug(
98
+ "Extracting %i transactions for account %s",
99
+ len(history),
100
+ account_id,
101
+ )
102
+
103
+ entries = []
104
+ for trx in history:
105
+ entries.extend(
106
+ self._extract_transaction(trx, local_account, currency, account_id)
107
+ )
108
+ return entries
109
+
110
+ def _extract_transaction(
111
+ self,
112
+ trx: dict[str, Any],
113
+ local_account: data.Account,
114
+ currency: str,
115
+ account_id: str,
116
+ ) -> list[data.Transaction]:
117
+ entries = []
118
+ metakv = {"account-id": str(account_id)}
119
+
120
+ trx_date = None
121
+ trx_description = None
122
+ trx_amount = None
123
+
124
+ for f in trx.get("fields", []):
125
+ if f["code"] == "PostingDate":
126
+ trx_date = dateutil.parser.parse(f["value"]["value"]).date()
127
+ if f["code"] == "Description":
128
+ trx_description = f["value"]["value"].replace("\n", " ")
129
+ if f["code"] == "Miles":
130
+ trx_amount = D(f["value"]["value"])
131
+ if f["code"] == "Info":
132
+ name = f["name"].lower().replace(" ", "-")
133
+ metakv[name] = f["value"]["value"].replace("\n", " ")
134
+
135
+ assert trx_date
136
+ assert trx_description
137
+ assert trx_amount is not None, f"No amount in trx: {trx}"
138
+
139
+ meta = data.new_metadata("", 0, metakv)
140
+ entry = data.Transaction(
141
+ meta,
142
+ trx_date,
143
+ "*",
144
+ "",
145
+ trx_description,
146
+ data.EMPTY_SET,
147
+ data.EMPTY_SET,
148
+ [
149
+ data.Posting(
150
+ local_account,
151
+ amount.Amount(trx_amount, currency),
152
+ None,
153
+ None,
154
+ None,
155
+ None,
156
+ ),
157
+ ],
158
+ )
159
+ entries.append(entry)
160
+ return entries
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tariochbctools
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Importers, plugins and price fetchers for Beancount
5
5
  Home-page: https://github.com/tarioch/beancounttools/
6
6
  Author: Patrick Ruckstuhl
@@ -20,6 +20,7 @@ Classifier: License :: OSI Approved :: MIT License
20
20
  Description-Content-Type: text/x-rst; charset=UTF-8
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: importlib-metadata; python_version < "3.8"
23
+ Requires-Dist: awardwallet
23
24
  Requires-Dist: beancount>=3
24
25
  Requires-Dist: beangulp
25
26
  Requires-Dist: beanprice
@@ -27,6 +27,9 @@ src/tariochbctools.egg-info/not-zip-safe
27
27
  src/tariochbctools.egg-info/requires.txt
28
28
  src/tariochbctools.egg-info/top_level.txt
29
29
  src/tariochbctools/importers/__init__.py
30
+ src/tariochbctools/importers/awardwalletimp/__init__.py
31
+ src/tariochbctools/importers/awardwalletimp/config.py
32
+ src/tariochbctools/importers/awardwalletimp/importer.py
30
33
  src/tariochbctools/importers/bcge/__init__.py
31
34
  src/tariochbctools/importers/bcge/importer.py
32
35
  src/tariochbctools/importers/bitst/__init__.py
@@ -81,6 +84,7 @@ tests/__init__.py
81
84
  tests/conftest.py
82
85
  tests/tariochbctools/__init__.py
83
86
  tests/tariochbctools/importers/__init__.py
87
+ tests/tariochbctools/importers/test_awardwallet.py
84
88
  tests/tariochbctools/importers/test_quickfile.py
85
89
  tests/tariochbctools/importers/test_truelayer.py
86
90
  tests/tariochbctools/plugins/__init__.py
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ awardwallet-conf = tariochbctools.importers.awardwalletimp.config:run
2
3
  nordigen-conf = tariochbctools.importers.nordigen.nordigen_config:run
@@ -1,3 +1,4 @@
1
+ awardwallet
1
2
  beancount>=3
2
3
  beangulp
3
4
  beanprice
@@ -0,0 +1,315 @@
1
+ import json
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import pytest
5
+ from beancount.core.number import D
6
+
7
+ from tariochbctools.importers.awardwalletimp import importer as awimp
8
+
9
+ # pylint: disable=protected-access
10
+
11
+ TEST_CONFIG = b"""
12
+ api_key: deadc0dedeadc0dedeadc0dedeadc0de
13
+ users:
14
+ 12:
15
+ name: John Smith
16
+ all_history: false
17
+ accounts:
18
+ 7654321:
19
+ provider: "British Airways Club"
20
+ account: Assets:Current:Points
21
+ currency: AVIOS
22
+ 6543210:
23
+ provider: "Virgin Atlantic Club"
24
+ account: Assets:Current:Points
25
+ currency: VIRGINPTS
26
+ 34:
27
+ name: User with dummy account
28
+ accounts:
29
+ 0:
30
+ """
31
+
32
+ TEST_TRX = b"""
33
+ {
34
+ "fields": [
35
+ {
36
+ "name": "Transaction Date",
37
+ "code": "PostingDate",
38
+ "value": {"value": "9/30/12", "type": "string"}
39
+ },
40
+ {
41
+ "name": "Description",
42
+ "code": "Description",
43
+ "value": {"value": "Expired Points", "type": "string"}
44
+ },
45
+ {
46
+ "name": "Type",
47
+ "code": "Info",
48
+ "value": {"value": "Adjustments", "type": "string"}
49
+ },
50
+ {
51
+ "name": "Points",
52
+ "code": "Miles",
53
+ "value": {"value": "-1,042", "type": "miles"}
54
+ }
55
+ ]
56
+ }
57
+ """
58
+
59
+ TEST_USER_DETAILS = b"""
60
+ {
61
+ "userId": 12,
62
+ "fullName": "John Smith",
63
+ "status": "Free",
64
+ "userName": "JSmith",
65
+ "email": "JSmith@email.com",
66
+ "forwardingEmail": "JSmith@AwardWallet.com",
67
+ "accessLevel": "Regular",
68
+ "connectionType": "Connected",
69
+ "accountsAccessLevel": "Full control",
70
+ "accountsSharedByDefault": true,
71
+ "editConnectionUrl": "https://business.awardwallet.com/members/connection/112233",
72
+ "accountListUrl": "https://business.awardwallet.com/account/list#/?agentId=112233",
73
+ "timelineUrl": "https://business.awardwallet.com/timeline/?agentId=166765#/112233",
74
+ "bookingRequestsUrl": "https://business.awardwallet.com/awardBooking/queue?user_filter=332211",
75
+ "accounts": [
76
+ {
77
+ "accountId": 7654321,
78
+ "code": "british",
79
+ "displayName": "British Airways (Executive Club)",
80
+ "kind": "Airlines",
81
+ "login": "johnsmith",
82
+ "autologinUrl": "https://business.awardwallet.com/account/redirect?ID=7654321",
83
+ "updateUrl": "https://business.awardwallet.com/account/edit/7654321?autosubmit=1",
84
+ "editUrl": "https://business.awardwallet.com/account/edit/7654321",
85
+ "balance": "146,780",
86
+ "balanceRaw": 146780,
87
+ "owner": "John Smith",
88
+ "errorCode": 1,
89
+ "lastDetectedChange": "+750",
90
+ "expirationDate": "2018-12-10T00:00:00+00:00",
91
+ "lastRetrieveDate": "2016-01-15T00:00:00+00:00",
92
+ "lastChangeDate": "2016-01-15T00:49:33+00:00",
93
+ "properties": [
94
+ {
95
+ "name": "Next Elite Level",
96
+ "value": {"value": "Bronze", "type": "string"},
97
+ "kind": 9
98
+ },
99
+ {
100
+ "name": "Date of joining the club",
101
+ "value": {"value": "20 Jun 2013", "type": "string"},
102
+ "kind": 5
103
+ },
104
+ {
105
+ "name": "Lifetime Tier Points",
106
+ "value": {"value": "35,000", "type": "string"}
107
+ },
108
+ {
109
+ "name": "Executive Club Tier Points",
110
+ "value": {"value": "35,000", "type": "string"}
111
+ },
112
+ {
113
+ "name": "Card expiry date",
114
+ "value": {"value": "31 Mar 2017", "type": "string"}
115
+ },
116
+ {
117
+ "name": "Membership year ends",
118
+ "value": {"value": "08 Feb 2016", "type": "string"}
119
+ },
120
+ {
121
+ "name": "Last Activity",
122
+ "value": {"value": "10-Dec-15", "type": "string"},
123
+ "kind": 13
124
+ },
125
+ {
126
+ "name": "Name",
127
+ "value": {"value": "Mr Smith", "type": "string"},
128
+ "kind": 12
129
+ },
130
+ {
131
+ "name": "Level",
132
+ "value": {"value": "Blue", "type": "string"},
133
+ "rank": 0,
134
+ "kind": 3
135
+ },
136
+ {
137
+ "name": "Membership no",
138
+ "value": {"value": "1122334455", "type": "string"},
139
+ "kind": 1
140
+ }
141
+ ],
142
+ "history": [
143
+ {
144
+ "fields": [
145
+ {
146
+ "name": "Transaction Date",
147
+ "code": "PostingDate",
148
+ "value": {"value": "3/31/14", "type": "string"}
149
+ },
150
+ {
151
+ "name": "Description",
152
+ "code": "Description",
153
+ "value": {"value": "Expired Points", "type": "string"}
154
+ },
155
+ {
156
+ "name": "Type",
157
+ "code": "Info",
158
+ "value": {"value": "Adjustments", "type": "string"}
159
+ },
160
+ {
161
+ "name": "Points",
162
+ "code": "Miles",
163
+ "value": {"value": "-100", "type": "miles"}
164
+ }
165
+ ]
166
+ },
167
+ {
168
+ "fields": [
169
+ {
170
+ "name": "Transaction Date",
171
+ "code": "PostingDate",
172
+ "value": {"value": "12/11/13", "type": "string"}
173
+ },
174
+ {
175
+ "name": "Description",
176
+ "code": "Description",
177
+ "value": {"value": "Google Wallet", "type": "string"}
178
+ },
179
+ {
180
+ "name": "Type",
181
+ "code": "Info",
182
+ "value": {"value": "Other Earning", "type": "string"}
183
+ },
184
+ {
185
+ "name": "Points",
186
+ "code": "Miles",
187
+ "value": {"value": "+100", "type": "miles"}
188
+ }
189
+ ]
190
+ },
191
+ {
192
+ "fields": [
193
+ {
194
+ "name": "Transaction Date",
195
+ "code": "PostingDate",
196
+ "value": {"value": "9/30/12", "type": "string"}
197
+ },
198
+ {
199
+ "name": "Description",
200
+ "code": "Description",
201
+ "value": {"value": "Expired Points", "type": "string"}
202
+ },
203
+ {
204
+ "name": "Type",
205
+ "code": "Info",
206
+ "value": {"value": "Adjustments", "type": "string"}
207
+ },
208
+ {
209
+ "name": "Points",
210
+ "code": "Miles",
211
+ "value": {"value": "-1,042", "type": "miles"}
212
+ }
213
+ ]
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ "accountId": 6543210,
219
+ "code": "virgin",
220
+ "displayName": "Virgin Atlantic (Flying Club)",
221
+ "kind": "Airlines",
222
+ "balance": "146,780",
223
+ "balanceRaw": 146780,
224
+ "owner": "John Smith",
225
+ "errorCode": 1,
226
+ "lastDetectedChange": "+750",
227
+ "properties": [
228
+ {
229
+ "name": "Next Elite Level",
230
+ "value": {"value": "Bronze", "type": "string"},
231
+ "kind": 9
232
+ }
233
+ ],
234
+ "history": [
235
+ ]
236
+ }
237
+ ]
238
+ }
239
+ """
240
+
241
+
242
+ @pytest.fixture(name="tmp_config")
243
+ def tmp_config_fixture(tmp_path):
244
+ config = tmp_path / "awardwallet.yaml"
245
+ config.write_bytes(TEST_CONFIG)
246
+ yield config
247
+
248
+
249
+ @pytest.fixture(name="importer")
250
+ def awardwallet_importer_fixture(tmp_config):
251
+ importer = awimp.Importer()
252
+ importer._configure(tmp_config, [])
253
+ yield importer
254
+
255
+
256
+ @pytest.fixture(name="importer_factory")
257
+ def awardwallet_importer_factory(tmp_path):
258
+ """A awardwallet importer factory for
259
+ generating an importer with a custom config
260
+ """
261
+
262
+ def _importer_with_config(custom_config):
263
+ config = tmp_path / "awardwallet.yaml"
264
+ config.write_bytes(custom_config)
265
+ importer = awimp.Importer()
266
+ importer._configure(config, [])
267
+ return importer
268
+
269
+ yield _importer_with_config
270
+
271
+
272
+ @pytest.fixture(name="tmp_trx")
273
+ def tmp_trx_fixture():
274
+ yield json.loads(TEST_TRX)
275
+
276
+
277
+ @pytest.fixture(name="tmp_user_details")
278
+ def tmp_user_details_fixture():
279
+ yield json.loads(TEST_USER_DETAILS, strict=False)
280
+
281
+
282
+ def test_identify(importer, tmp_config):
283
+ assert importer.identify(tmp_config)
284
+
285
+
286
+ def test_extract_transaction_simple(importer, tmp_trx):
287
+ entries = importer._extract_transaction(tmp_trx, "Assets:Other", "POINTS", 7654321)
288
+ assert entries[0].postings[0].units.number == D(
289
+ tmp_trx["fields"][3]["value"]["value"]
290
+ )
291
+
292
+
293
+ def test_extract_user_history(importer, tmp_user_details):
294
+ entries = importer._extract_user_history(
295
+ importer.config["users"][12],
296
+ tmp_user_details,
297
+ )
298
+ assert len(entries) == 3
299
+
300
+
301
+ @patch("tariochbctools.importers.awardwalletimp.importer.AwardWalletClient")
302
+ def test_extract_all_users(mock_api, importer, tmp_config, tmp_user_details):
303
+ importer._extract_user_history = MagicMock()
304
+
305
+ importer.extract(tmp_config)
306
+ assert importer._extract_user_history.call_count == 2
307
+
308
+
309
+ @patch("tariochbctools.importers.awardwalletimp.importer.AwardWalletClient")
310
+ def test_extract_all_accounts(mock_api, importer, tmp_config, tmp_user_details):
311
+ importer._extract_transactions = MagicMock()
312
+ mock_api.return_value.get_connected_user_details.return_value = tmp_user_details
313
+
314
+ importer.extract(tmp_config)
315
+ assert importer._extract_transactions.call_count == 2
File without changes
File without changes