tariochbctools 1.3.0__py2.py3-none-any.whl → 1.4.1__py2.py3-none-any.whl

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.
File without changes
@@ -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.1
4
4
  Summary: Importers, plugins and price fetchers for Beancount
5
5
  Home-page: https://github.com/tarioch/beancounttools/
6
6
  Author: Patrick Ruckstuhl
@@ -16,10 +16,10 @@ Classifier: Intended Audience :: Developers
16
16
  Classifier: Topic :: Office/Business :: Financial
17
17
  Classifier: Topic :: Office/Business :: Financial :: Accounting
18
18
  Classifier: Topic :: Office/Business :: Financial :: Investment
19
- Classifier: License :: OSI Approved :: MIT License
20
19
  Description-Content-Type: text/x-rst; charset=UTF-8
21
20
  License-File: LICENSE.txt
22
21
  Requires-Dist: importlib-metadata; python_version < "3.8"
22
+ Requires-Dist: awardwallet
23
23
  Requires-Dist: beancount>=3
24
24
  Requires-Dist: beangulp
25
25
  Requires-Dist: beanprice
@@ -29,7 +29,7 @@ Requires-Dist: pyyaml
29
29
  Requires-Dist: ibflex
30
30
  Requires-Dist: requests
31
31
  Requires-Dist: camelot-py
32
- Requires-Dist: opencv-python
32
+ Requires-Dist: opencv-python-headless
33
33
  Requires-Dist: blockcypher
34
34
  Requires-Dist: imap-tools
35
35
  Requires-Dist: undictify
@@ -1,5 +1,8 @@
1
1
  tariochbctools/__init__.py,sha256=pH5i4Fj1tbXLqLtTVIdoojiplZssQn0nnud8-HXodRE,577
2
2
  tariochbctools/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ tariochbctools/importers/awardwalletimp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ tariochbctools/importers/awardwalletimp/config.py,sha256=EPwvUE9WBdCk9xxleJ8qDLfPy8GwcgAtG1uGSShyt4w,2460
5
+ tariochbctools/importers/awardwalletimp/importer.py,sha256=0G3DcxYP_chy1zUYMCijDOwNZBBBdX8hR2sPKiVHZS8,5071
3
6
  tariochbctools/importers/bcge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
7
  tariochbctools/importers/bcge/importer.py,sha256=bAQpkBLURatdsfSoMQdiha-8QfExAXXvpzAgnFFMwoE,1176
5
8
  tariochbctools/importers/bitst/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -50,9 +53,9 @@ tariochbctools/plugins/check_portfolio_sum.py,sha256=naJ2j6BFpQhJhT2c-gfjyIdcYe0
50
53
  tariochbctools/plugins/generate_base_ccy_prices.py,sha256=4CDzUosjMWCZfsBJMLrf-i5WNCHexI2rdm5vIDYW-AI,1314
51
54
  tariochbctools/plugins/prices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
55
  tariochbctools/plugins/prices/ibkr.py,sha256=GYCjnlF-MK-ZFPEr0M6T4iO3Etq0tMmrMlsHGInXUO8,1405
53
- tariochbctools-1.3.0.dist-info/licenses/LICENSE.txt,sha256=VR2hkz3p9Sw4hSXc7S5iZTOXGeV4h-i8AO_q0zEmtkE,1074
54
- tariochbctools-1.3.0.dist-info/METADATA,sha256=qafViFxIs-0JxGrPrvw9iHUCxRKHch1hdh7G1IXXwpM,2190
55
- tariochbctools-1.3.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
56
- tariochbctools-1.3.0.dist-info/entry_points.txt,sha256=9xrCCY1wx2zCIsQUOWZelLHDmHw9Oc-ZBAKUIY9lKTA,88
57
- tariochbctools-1.3.0.dist-info/top_level.txt,sha256=CiA_NepCI6zDNsaORA55zmpuJFSnTvLESraIL13xiOQ,15
58
- tariochbctools-1.3.0.dist-info/RECORD,,
56
+ tariochbctools-1.4.1.dist-info/licenses/LICENSE.txt,sha256=VR2hkz3p9Sw4hSXc7S5iZTOXGeV4h-i8AO_q0zEmtkE,1074
57
+ tariochbctools-1.4.1.dist-info/METADATA,sha256=x5jAZ6K1do4OMGOoJYbteq1nf3Qw4dW1DR7693B1XCk,2175
58
+ tariochbctools-1.4.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
59
+ tariochbctools-1.4.1.dist-info/entry_points.txt,sha256=bo7wO1u-PIDHNuqsTEekC56VCAmn2i2vTRcKXxqc770,158
60
+ tariochbctools-1.4.1.dist-info/top_level.txt,sha256=CiA_NepCI6zDNsaORA55zmpuJFSnTvLESraIL13xiOQ,15
61
+ tariochbctools-1.4.1.dist-info/RECORD,,
@@ -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