rustfava 0.1.0__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.
Files changed (187) hide show
  1. rustfava/__init__.py +30 -0
  2. rustfava/_ctx_globals_class.py +55 -0
  3. rustfava/api_models.py +36 -0
  4. rustfava/application.py +534 -0
  5. rustfava/beans/__init__.py +6 -0
  6. rustfava/beans/abc.py +327 -0
  7. rustfava/beans/account.py +79 -0
  8. rustfava/beans/create.py +377 -0
  9. rustfava/beans/flags.py +20 -0
  10. rustfava/beans/funcs.py +38 -0
  11. rustfava/beans/helpers.py +52 -0
  12. rustfava/beans/ingest.py +75 -0
  13. rustfava/beans/load.py +31 -0
  14. rustfava/beans/prices.py +151 -0
  15. rustfava/beans/protocols.py +82 -0
  16. rustfava/beans/str.py +454 -0
  17. rustfava/beans/types.py +63 -0
  18. rustfava/cli.py +187 -0
  19. rustfava/context.py +13 -0
  20. rustfava/core/__init__.py +729 -0
  21. rustfava/core/accounts.py +161 -0
  22. rustfava/core/attributes.py +145 -0
  23. rustfava/core/budgets.py +207 -0
  24. rustfava/core/charts.py +301 -0
  25. rustfava/core/commodities.py +37 -0
  26. rustfava/core/conversion.py +229 -0
  27. rustfava/core/documents.py +87 -0
  28. rustfava/core/extensions.py +132 -0
  29. rustfava/core/fava_options.py +255 -0
  30. rustfava/core/file.py +542 -0
  31. rustfava/core/filters.py +484 -0
  32. rustfava/core/group_entries.py +97 -0
  33. rustfava/core/ingest.py +509 -0
  34. rustfava/core/inventory.py +167 -0
  35. rustfava/core/misc.py +105 -0
  36. rustfava/core/module_base.py +18 -0
  37. rustfava/core/number.py +106 -0
  38. rustfava/core/query.py +180 -0
  39. rustfava/core/query_shell.py +301 -0
  40. rustfava/core/tree.py +265 -0
  41. rustfava/core/watcher.py +219 -0
  42. rustfava/ext/__init__.py +232 -0
  43. rustfava/ext/auto_commit.py +61 -0
  44. rustfava/ext/portfolio_list/PortfolioList.js +34 -0
  45. rustfava/ext/portfolio_list/__init__.py +29 -0
  46. rustfava/ext/portfolio_list/templates/PortfolioList.html +15 -0
  47. rustfava/ext/rustfava_ext_test/RustfavaExtTest.js +42 -0
  48. rustfava/ext/rustfava_ext_test/__init__.py +207 -0
  49. rustfava/ext/rustfava_ext_test/templates/RustfavaExtTest.html +45 -0
  50. rustfava/ext/rustfava_ext_test/templates/RustfavaExtTestInclude.html +1 -0
  51. rustfava/help/__init__.py +15 -0
  52. rustfava/help/_index.md +29 -0
  53. rustfava/help/beancount_syntax.md +156 -0
  54. rustfava/help/budgets.md +31 -0
  55. rustfava/help/conversion.md +29 -0
  56. rustfava/help/extensions.md +111 -0
  57. rustfava/help/features.md +179 -0
  58. rustfava/help/filters.md +103 -0
  59. rustfava/help/import.md +27 -0
  60. rustfava/help/options.md +289 -0
  61. rustfava/helpers.py +30 -0
  62. rustfava/internal_api.py +221 -0
  63. rustfava/json_api.py +952 -0
  64. rustfava/plugins/__init__.py +3 -0
  65. rustfava/plugins/link_documents.py +107 -0
  66. rustfava/plugins/tag_discovered_documents.py +44 -0
  67. rustfava/py.typed +0 -0
  68. rustfava/rustledger/__init__.py +31 -0
  69. rustfava/rustledger/constants.py +76 -0
  70. rustfava/rustledger/engine.py +485 -0
  71. rustfava/rustledger/loader.py +273 -0
  72. rustfava/rustledger/options.py +202 -0
  73. rustfava/rustledger/query.py +331 -0
  74. rustfava/rustledger/types.py +830 -0
  75. rustfava/serialisation.py +220 -0
  76. rustfava/static/app.css +2988 -0
  77. rustfava/static/app.css.map +7 -0
  78. rustfava/static/app.js +12854 -0
  79. rustfava/static/app.js.map +7 -0
  80. rustfava/static/beancount-JFV44ZVZ.css +5 -0
  81. rustfava/static/beancount-JFV44ZVZ.css.map +7 -0
  82. rustfava/static/beancount-VTTKRGSK.js +4642 -0
  83. rustfava/static/beancount-VTTKRGSK.js.map +7 -0
  84. rustfava/static/bql-MGFRUMBP.js +333 -0
  85. rustfava/static/bql-MGFRUMBP.js.map +7 -0
  86. rustfava/static/chunk-E7ZF4ASL.js +23061 -0
  87. rustfava/static/chunk-E7ZF4ASL.js.map +7 -0
  88. rustfava/static/chunk-V24TLQHT.js +12673 -0
  89. rustfava/static/chunk-V24TLQHT.js.map +7 -0
  90. rustfava/static/favicon.ico +0 -0
  91. rustfava/static/fira-mono-cyrillic-400-normal-BLAGXRCE.woff2 +0 -0
  92. rustfava/static/fira-mono-cyrillic-500-normal-EN7JUAAW.woff2 +0 -0
  93. rustfava/static/fira-mono-cyrillic-ext-400-normal-EX7VARTS.woff2 +0 -0
  94. rustfava/static/fira-mono-cyrillic-ext-500-normal-ZDPTUPRR.woff2 +0 -0
  95. rustfava/static/fira-mono-greek-400-normal-COGHKMOA.woff2 +0 -0
  96. rustfava/static/fira-mono-greek-500-normal-4EN2PKZT.woff2 +0 -0
  97. rustfava/static/fira-mono-greek-ext-400-normal-DYEQIJH7.woff2 +0 -0
  98. rustfava/static/fira-mono-greek-ext-500-normal-SG73CVKQ.woff2 +0 -0
  99. rustfava/static/fira-mono-latin-400-normal-NA3VLV7E.woff2 +0 -0
  100. rustfava/static/fira-mono-latin-500-normal-YC77GFWD.woff2 +0 -0
  101. rustfava/static/fira-mono-latin-ext-400-normal-DIKTZ5PW.woff2 +0 -0
  102. rustfava/static/fira-mono-latin-ext-500-normal-ZWY4UO4V.woff2 +0 -0
  103. rustfava/static/fira-mono-symbols2-400-normal-UITXT77Q.woff2 +0 -0
  104. rustfava/static/fira-mono-symbols2-500-normal-VWPC2EFN.woff2 +0 -0
  105. rustfava/static/fira-sans-cyrillic-400-normal-KLQMBCA6.woff2 +0 -0
  106. rustfava/static/fira-sans-cyrillic-500-normal-NFG7UD6J.woff2 +0 -0
  107. rustfava/static/fira-sans-cyrillic-ext-400-normal-GWO44OPC.woff2 +0 -0
  108. rustfava/static/fira-sans-cyrillic-ext-500-normal-SP47E5SC.woff2 +0 -0
  109. rustfava/static/fira-sans-greek-400-normal-UMQBTLC3.woff2 +0 -0
  110. rustfava/static/fira-sans-greek-500-normal-4ZKHN4FQ.woff2 +0 -0
  111. rustfava/static/fira-sans-greek-ext-400-normal-O2DVJAJZ.woff2 +0 -0
  112. rustfava/static/fira-sans-greek-ext-500-normal-SK6GNWGO.woff2 +0 -0
  113. rustfava/static/fira-sans-latin-400-normal-OYYTPMAV.woff2 +0 -0
  114. rustfava/static/fira-sans-latin-500-normal-SMQPZW5A.woff2 +0 -0
  115. rustfava/static/fira-sans-latin-ext-400-normal-OAUP3WK5.woff2 +0 -0
  116. rustfava/static/fira-sans-latin-ext-500-normal-LY3YDR5Y.woff2 +0 -0
  117. rustfava/static/fira-sans-vietnamese-400-normal-OBMQ72MR.woff2 +0 -0
  118. rustfava/static/fira-sans-vietnamese-500-normal-Y4NZR5EU.woff2 +0 -0
  119. rustfava/static/source-code-pro-cyrillic-400-normal-TO22V6M3.woff2 +0 -0
  120. rustfava/static/source-code-pro-cyrillic-500-normal-OGBWWWYW.woff2 +0 -0
  121. rustfava/static/source-code-pro-cyrillic-ext-400-normal-XH44UCIA.woff2 +0 -0
  122. rustfava/static/source-code-pro-cyrillic-ext-500-normal-3Z6MMVM6.woff2 +0 -0
  123. rustfava/static/source-code-pro-greek-400-normal-OUXXUQWK.woff2 +0 -0
  124. rustfava/static/source-code-pro-greek-500-normal-JA2Z5UXO.woff2 +0 -0
  125. rustfava/static/source-code-pro-greek-ext-400-normal-WCDKMX7U.woff2 +0 -0
  126. rustfava/static/source-code-pro-greek-ext-500-normal-ZHVI4VKW.woff2 +0 -0
  127. rustfava/static/source-code-pro-latin-400-normal-QOGTXED5.woff2 +0 -0
  128. rustfava/static/source-code-pro-latin-500-normal-X57QEOLQ.woff2 +0 -0
  129. rustfava/static/source-code-pro-latin-ext-400-normal-QXC74NBF.woff2 +0 -0
  130. rustfava/static/source-code-pro-latin-ext-500-normal-QGOY7MTT.woff2 +0 -0
  131. rustfava/static/source-code-pro-vietnamese-400-normal-NPDCDTBA.woff2 +0 -0
  132. rustfava/static/source-code-pro-vietnamese-500-normal-M6PJKTR5.woff2 +0 -0
  133. rustfava/static/tree-sitter-beancount-MLXFQBZ5.wasm +0 -0
  134. rustfava/static/web-tree-sitter-RNOQ6E74.wasm +0 -0
  135. rustfava/template_filters.py +64 -0
  136. rustfava/templates/_journal_table.html +156 -0
  137. rustfava/templates/_layout.html +26 -0
  138. rustfava/templates/_query_table.html +88 -0
  139. rustfava/templates/beancount_file +18 -0
  140. rustfava/templates/help.html +23 -0
  141. rustfava/templates/macros/_account_macros.html +5 -0
  142. rustfava/templates/macros/_commodity_macros.html +13 -0
  143. rustfava/translations/bg/LC_MESSAGES/messages.mo +0 -0
  144. rustfava/translations/bg/LC_MESSAGES/messages.po +618 -0
  145. rustfava/translations/ca/LC_MESSAGES/messages.mo +0 -0
  146. rustfava/translations/ca/LC_MESSAGES/messages.po +618 -0
  147. rustfava/translations/de/LC_MESSAGES/messages.mo +0 -0
  148. rustfava/translations/de/LC_MESSAGES/messages.po +618 -0
  149. rustfava/translations/es/LC_MESSAGES/messages.mo +0 -0
  150. rustfava/translations/es/LC_MESSAGES/messages.po +619 -0
  151. rustfava/translations/fa/LC_MESSAGES/messages.mo +0 -0
  152. rustfava/translations/fa/LC_MESSAGES/messages.po +618 -0
  153. rustfava/translations/fr/LC_MESSAGES/messages.mo +0 -0
  154. rustfava/translations/fr/LC_MESSAGES/messages.po +618 -0
  155. rustfava/translations/ja/LC_MESSAGES/messages.mo +0 -0
  156. rustfava/translations/ja/LC_MESSAGES/messages.po +618 -0
  157. rustfava/translations/nl/LC_MESSAGES/messages.mo +0 -0
  158. rustfava/translations/nl/LC_MESSAGES/messages.po +617 -0
  159. rustfava/translations/pt/LC_MESSAGES/messages.mo +0 -0
  160. rustfava/translations/pt/LC_MESSAGES/messages.po +617 -0
  161. rustfava/translations/pt_BR/LC_MESSAGES/messages.mo +0 -0
  162. rustfava/translations/pt_BR/LC_MESSAGES/messages.po +618 -0
  163. rustfava/translations/ru/LC_MESSAGES/messages.mo +0 -0
  164. rustfava/translations/ru/LC_MESSAGES/messages.po +617 -0
  165. rustfava/translations/sk/LC_MESSAGES/messages.mo +0 -0
  166. rustfava/translations/sk/LC_MESSAGES/messages.po +623 -0
  167. rustfava/translations/sv/LC_MESSAGES/messages.mo +0 -0
  168. rustfava/translations/sv/LC_MESSAGES/messages.po +618 -0
  169. rustfava/translations/uk/LC_MESSAGES/messages.mo +0 -0
  170. rustfava/translations/uk/LC_MESSAGES/messages.po +618 -0
  171. rustfava/translations/zh/LC_MESSAGES/messages.mo +0 -0
  172. rustfava/translations/zh/LC_MESSAGES/messages.po +618 -0
  173. rustfava/translations/zh_Hant_TW/LC_MESSAGES/messages.mo +0 -0
  174. rustfava/translations/zh_Hant_TW/LC_MESSAGES/messages.po +618 -0
  175. rustfava/util/__init__.py +157 -0
  176. rustfava/util/date.py +576 -0
  177. rustfava/util/excel.py +118 -0
  178. rustfava/util/ranking.py +79 -0
  179. rustfava/util/sets.py +18 -0
  180. rustfava/util/unreachable.py +20 -0
  181. rustfava-0.1.0.dist-info/METADATA +102 -0
  182. rustfava-0.1.0.dist-info/RECORD +187 -0
  183. rustfava-0.1.0.dist-info/WHEEL +5 -0
  184. rustfava-0.1.0.dist-info/entry_points.txt +2 -0
  185. rustfava-0.1.0.dist-info/licenses/AUTHORS +11 -0
  186. rustfava-0.1.0.dist-info/licenses/LICENSE +21 -0
  187. rustfava-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,289 @@
1
+ # Options
2
+
3
+ To customize some of rustfava's behaviour, you can add custom entries like the
4
+ following to your Beancount file.
5
+
6
+ <pre><textarea is="beancount-textarea">
7
+ 2016-06-14 custom "fava-option" "default-file"
8
+ 2016-04-14 custom "fava-option" "auto-reload" "true"
9
+ 2016-04-14 custom "fava-option" "currency-column" "100"</textarea></pre>
10
+
11
+ Below is a list of all possible options for rustfava.
12
+
13
+ ______________________________________________________________________
14
+
15
+ ## `language`
16
+
17
+ Default: Not set
18
+
19
+ If this setting is not specified, rustfava will try to guess the language from your
20
+ browser settings. Rustfava currently ships translations into the following
21
+ languages:
22
+
23
+ - Bulgarian (`bg`)
24
+ - Catalan (`ca`)
25
+ - Chinese (`zh_CN` and `zh_TW`)
26
+ - Dutch (`nl`)
27
+ - English (`en`)
28
+ - French (`fr`)
29
+ - German (`de`)
30
+ - Japanese (`ja`)
31
+ - Persian (`fa`)
32
+ - Portuguese (`pt` and `pt_BR`)
33
+ - Russian (`ru`)
34
+ - Slovak (`sk`)
35
+ - Spanish (`es`)
36
+ - Swedish (`sv`)
37
+ - Ukrainian (`uk`)
38
+
39
+ ______________________________________________________________________
40
+
41
+ ## `locale`
42
+
43
+ Default: Not set or `en` if the Beancount `render_commas` option is set.
44
+
45
+ This sets the locale that is used to render out numbers. For example, with the
46
+ locale `en_IN` the number `1111111.33` will be rendered `11,11,111.33`,
47
+ `1,111,111.33` with locale `en`, or `1.111.111,33` with locale `de`.
48
+
49
+ ______________________________________________________________________
50
+
51
+ ## `default-file`
52
+
53
+ Use this option to specify a default file for the editor to open. This option
54
+ may optionally take a value of a filename to be the default. If you don't
55
+ provide a filename, the file this custom option is in is used.
56
+
57
+ ______________________________________________________________________
58
+
59
+ ## `default-page`
60
+
61
+ Default: `income_statement/`
62
+
63
+ Use this option to specify the page to be redirected to when visiting rustfava. If
64
+ this option is not specified, you are taken to the income statement. You may
65
+ also use this option to set filters. For example, a `default-page` of
66
+ `balance_sheet/?time=year-2+-+year` would result in you being redirected to a
67
+ balance sheet reporting the current year and the two previous years.
68
+
69
+ Note that the supplied path is relative. It is probably easiest to navigate to
70
+ the URL in your browser and copy the portion of the URL after the 'title' of
71
+ your beancount file into this option.
72
+
73
+ ______________________________________________________________________
74
+
75
+ ## `fiscal-year-end`
76
+
77
+ Default: `12-31`
78
+
79
+ The last day of the fiscal (financial or tax) period for accounting purposes in
80
+ `%m-%d` format. Allows for the use of `FY2018`, `FY2018-Q3`, `fiscal_year` and
81
+ `fiscal_quarter` in the time filter, and `FY2018` as the start date, end date,
82
+ or both dates in a date range in the time filter. Month can be a value larger
83
+ than `12` to have `FY2018` end in 2019 for example.
84
+
85
+ Examples are:
86
+
87
+ - `04-05` - UK
88
+ - `06-30` - Australia / NZ
89
+ - `09-30` - US federal government
90
+ - `15-31` - Japan
91
+
92
+ See [Fiscal Year on WikiPedia](https://en.wikipedia.org/wiki/Fiscal_year) for
93
+ more examples.
94
+
95
+ ______________________________________________________________________
96
+
97
+ ## `indent`
98
+
99
+ Default: 2.
100
+
101
+ The number spaces for indentation.
102
+
103
+ ______________________________________________________________________
104
+
105
+ ## `insert-entry`
106
+
107
+ Default: Not set.
108
+
109
+ This option can be used to specify where entries are inserted. The argument to
110
+ this option should be a regular expression matching account names. This option
111
+ can be given multiple times. When adding an entry, the account of the entry (for
112
+ a transaction, the account of the last posting is used) is matched against all
113
+ `insert-entry` options and the entry will be inserted before the datewise latest
114
+ of the matching options before the entry date. If the entry is a Transaction and
115
+ no `insert-entry` option matches the account of the last posting the account of
116
+ the second to last posting and so on will be tried. If no `insert-entry` option
117
+ matches or none is given, the entry will be inserted at the end of the default
118
+ file.
119
+
120
+ ______________________________________________________________________
121
+
122
+ ## `auto-reload`
123
+
124
+ Default: `false`
125
+
126
+ Set this to `true` to make rustfava automatically reload the page whenever a file
127
+ changes is detected. By default only a notification is shown which you can click
128
+ to reload the page. If the file change is due to user interaction, e.g.,
129
+ uploading a document or adding a transaction, rustfava will always reload the page
130
+ automatically.
131
+
132
+ ______________________________________________________________________
133
+
134
+ ## `unrealized`
135
+
136
+ Default: `Unrealized`
137
+
138
+ The subaccount of the Equity account to post unrealized gains to if the account
139
+ trees are shown at market value.
140
+
141
+ ______________________________________________________________________
142
+
143
+ ## `currency-column`
144
+
145
+ Default: `61`
146
+
147
+ This option can be used to configure how posting lines are aligned when saved to
148
+ file or when using 'Align Amounts' in the editor. rustfava tries to align so that
149
+ the currencies all occur in the given column. Also, rustfava will show a vertical
150
+ line before this column in the editor.
151
+
152
+ ______________________________________________________________________
153
+
154
+ ## `sidebar-show-queries`
155
+
156
+ Default: `5`
157
+
158
+ The maximum number of queries to link to in the sidebar. Set this value to `0`
159
+ to hide the links altogether.
160
+
161
+ ______________________________________________________________________
162
+
163
+ ## `upcoming-events`
164
+
165
+ Default: `7`
166
+
167
+ Show a notification bubble in the sidebar displaying the number of events less
168
+ than `upcoming-events` days away. Set this value to `0` to disable this feature.
169
+
170
+ ______________________________________________________________________
171
+
172
+ ## `show-closed-accounts`
173
+
174
+ Default: `false`
175
+
176
+ ## `show-accounts-with-zero-transactions`
177
+
178
+ Default: `true`
179
+
180
+ ## `show-accounts-with-zero-balance`
181
+
182
+ Default: `true`
183
+
184
+ These three options specify which accounts (not) to show in the account trees,
185
+ like on the income statement. Accounts with a non-zero balance will always be
186
+ shown.
187
+
188
+ ______________________________________________________________________
189
+
190
+ ## `collapse-pattern`
191
+
192
+ Default: Not set
193
+
194
+ This option is used to specify accounts that will be collapsed in the displayed
195
+ account trees. The argument to this option is a regular expression matching
196
+ account names. This option can be specified multiple times.
197
+
198
+ Collapsing all accounts below a specific depth in the account tree can be
199
+ accomplished by a regex such as: `.*:.*:.*` (this example collapses all accounts
200
+ that are three levels deep).
201
+
202
+ ______________________________________________________________________
203
+
204
+ ## `use-external-editor`
205
+
206
+ Default: `false`
207
+
208
+ If `true`, instead of using the internal editor, the `beancount://` URL scheme
209
+ is used. See the
210
+ [Beancount urlscheme](https://github.com/aumayr/beancount_urlscheme) project for
211
+ details.
212
+
213
+ ______________________________________________________________________
214
+
215
+ ## `account-journal-include-children`
216
+
217
+ Default: `true`
218
+
219
+ This determines if the journal in the account report includes entries of
220
+ sub-accounts.
221
+
222
+ ______________________________________________________________________
223
+
224
+ ## `uptodate-indicator-grey-lookback-days`
225
+
226
+ Default: `60`
227
+
228
+ If there has been no activity in given number of days since the last balance
229
+ entry, then the grey uptodate-indicator is shown.
230
+
231
+ ______________________________________________________________________
232
+
233
+ ## `import-config`
234
+
235
+ Default: Not set
236
+
237
+ Path to a Beancount import configuration file. See the [Import](./import) help
238
+ page for details.
239
+
240
+ ______________________________________________________________________
241
+
242
+ ## `import-dirs`
243
+
244
+ Default: Not set
245
+
246
+ Set a directory to be scanned by the Beancount import mechanism. This option can
247
+ be specified multiple times to add multiple directories.
248
+
249
+ ______________________________________________________________________
250
+
251
+ ## `invert-gains-losses-colors`
252
+
253
+ Default: `false`
254
+
255
+ By default, rustfava uses green for unrealized gains (positive values) and red for
256
+ unrealized losses (negative values) in the Balance Sheet and Trial Balance when
257
+ displaying at market value.
258
+
259
+ Set this to `true` to invert the color scheme, using red for gains and green for
260
+ losses. This is useful for users from regions where the opposite convention is
261
+ used, such as Chinese stock markets where red represents gains and green
262
+ represents losses.
263
+
264
+ ______________________________________________________________________
265
+
266
+ ## `invert-income-liabilities-equity`
267
+
268
+ Default: False
269
+
270
+ In Beancount the Income, Liabilities and Equity accounts tend to have a negative
271
+ balance (see
272
+ [Types of Accounts](https://beancount.github.io/docs/the_double_entry_counting_method.html#types-of-accounts)).
273
+
274
+ This rustfava option flips the sign of these three accounts in the income statement
275
+ and the balance sheet. This way, the net profit chart will show positive numbers
276
+ if the income is greater than the expenses for a given timespan.
277
+
278
+ Note: To keep consistency with the internal accounting of beancount, the journal
279
+ and the individual account pages are not affected by this configuration option.
280
+
281
+ ______________________________________________________________________
282
+
283
+ ## `conversion-currencies`
284
+
285
+ Default: Not set
286
+
287
+ When set, the currency conversion select dropdown in all charts will show the
288
+ list of currencies specified in this option. By default, rustfava lists all
289
+ operating currencies and those currencies that match ISO 4217 currency codes.
rustfava/helpers.py ADDED
@@ -0,0 +1,30 @@
1
+ """Exceptions and module base class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING: # pragma: no cover
9
+ from rustfava.beans.abc import Directive
10
+ from rustfava.beans.abc import Meta
11
+
12
+
13
+ @dataclass(frozen=True, slots=True)
14
+ class BeancountError:
15
+ """Dataclass for a Beancount-style error."""
16
+
17
+ source: Meta | None
18
+ message: str
19
+ entry: Directive | None
20
+
21
+
22
+ class RustfavaAPIError(Exception):
23
+ """Fava's base exception class."""
24
+
25
+ def __init__(self, message: str) -> None:
26
+ super().__init__()
27
+ self.message = message
28
+
29
+ def __str__(self) -> str:
30
+ return self.message
@@ -0,0 +1,221 @@
1
+ """Internal API.
2
+
3
+ This is used to pre-process some data that is used in the templates, allowing
4
+ this part of the functionality to be tested and allowing some end-to-end tests
5
+ for the frontend data validation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING
12
+
13
+ from flask import current_app
14
+ from flask import url_for
15
+ from flask_babel import gettext
16
+
17
+ from rustfava.context import g
18
+ from rustfava.util.excel import HAVE_EXCEL
19
+
20
+ if TYPE_CHECKING: # pragma: no cover
21
+ from collections.abc import Sequence
22
+ from typing import Literal
23
+
24
+ from rustfava.beans.abc import Meta
25
+ from rustfava.beans.abc import Query
26
+ from rustfava.core.accounts import AccountDict
27
+ from rustfava.core.charts import DateAndBalance
28
+ from rustfava.core.charts import DateAndBalanceWithBudget
29
+ from rustfava.core.extensions import ExtensionDetails
30
+ from rustfava.core.fava_options import RustfavaOptions
31
+ from rustfava.core.tree import SerialisedTreeNode
32
+ from rustfava.helpers import BeancountError
33
+ from rustfava.util.date import Interval
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class SerialisedError:
38
+ """A Beancount error, as passed to the frontend."""
39
+
40
+ type: str
41
+ source: Meta | None
42
+ message: str
43
+
44
+ @staticmethod
45
+ def from_beancount_error(err: BeancountError) -> SerialisedError:
46
+ """Get a serialisable error from a Beancount error."""
47
+ source = dict(err.source) if err.source is not None else None
48
+ if source is not None:
49
+ source.pop("__tolerances__", None)
50
+ return SerialisedError(err.__class__.__name__, source, err.message)
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class LedgerData:
55
+ """This is used as report-independent data in the frontend."""
56
+
57
+ accounts: Sequence[str]
58
+ account_details: AccountDict
59
+ base_url: str
60
+ currencies: Sequence[str]
61
+ currency_names: dict[str, str]
62
+ errors: Sequence[SerialisedError]
63
+ fava_options: RustfavaOptions
64
+ incognito: bool
65
+ have_excel: bool
66
+ links: Sequence[str]
67
+ options: dict[str, str | Sequence[str]]
68
+ payees: Sequence[str]
69
+ precisions: dict[str, int]
70
+ tags: Sequence[str]
71
+ years: Sequence[str]
72
+ user_queries: Sequence[Query]
73
+ upcoming_events_count: int
74
+ extensions: Sequence[ExtensionDetails]
75
+ sidebar_links: Sequence[tuple[str, str]]
76
+ other_ledgers: Sequence[tuple[str, str]]
77
+
78
+
79
+ def get_errors() -> list[SerialisedError]:
80
+ """Serialise errors (do not pass entry as that might fail serialisation."""
81
+ return [SerialisedError.from_beancount_error(e) for e in g.ledger.errors]
82
+
83
+
84
+ def _get_options() -> dict[str, str | Sequence[str]]:
85
+ options = g.ledger.options
86
+ return {
87
+ "documents": options["documents"],
88
+ "filename": options["filename"],
89
+ "include": options["include"],
90
+ "operating_currency": options["operating_currency"],
91
+ "title": options["title"],
92
+ "name_assets": options["name_assets"],
93
+ "name_liabilities": options["name_liabilities"],
94
+ "name_equity": options["name_equity"],
95
+ "name_income": options["name_income"],
96
+ "name_expenses": options["name_expenses"],
97
+ }
98
+
99
+
100
+ def get_ledger_data() -> LedgerData:
101
+ """Get the report-independent ledger data."""
102
+ ledger = g.ledger
103
+ all_queries = ledger.all_entries_by_type.Query
104
+
105
+ return LedgerData(
106
+ ledger.attributes.accounts,
107
+ ledger.accounts,
108
+ url_for("index"),
109
+ ledger.attributes.currencies,
110
+ ledger.commodities.names,
111
+ get_errors(),
112
+ ledger.fava_options,
113
+ current_app.config["INCOGNITO"],
114
+ HAVE_EXCEL,
115
+ ledger.attributes.links,
116
+ _get_options(),
117
+ ledger.attributes.payees,
118
+ ledger.format_decimal.precisions,
119
+ ledger.attributes.tags,
120
+ ledger.attributes.years,
121
+ all_queries[: ledger.fava_options.sidebar_show_queries],
122
+ len(ledger.misc.upcoming_events),
123
+ ledger.extensions.extension_details,
124
+ ledger.misc.sidebar_links,
125
+ [
126
+ (ledger.options["title"], url_for("index", bfile=file_slug))
127
+ for (file_slug, ledger) in current_app.config["LEDGERS"].items()
128
+ if file_slug != g.beancount_file_slug
129
+ ],
130
+ )
131
+
132
+
133
+ @dataclass(frozen=True)
134
+ class BalancesChart:
135
+ """Data for a balances chart."""
136
+
137
+ label: str
138
+ data: Sequence[DateAndBalance]
139
+ type: Literal["balances"] = "balances"
140
+
141
+
142
+ @dataclass(frozen=True)
143
+ class BarChart:
144
+ """Data for a bar chart."""
145
+
146
+ label: str
147
+ data: Sequence[DateAndBalanceWithBudget]
148
+ type: Literal["bar"] = "bar"
149
+
150
+
151
+ @dataclass(frozen=True)
152
+ class HierarchyChart:
153
+ """Data for a hierarchy chart."""
154
+
155
+ label: str
156
+ data: SerialisedTreeNode
157
+ type: Literal["hierarchy"] = "hierarchy"
158
+
159
+
160
+ if TYPE_CHECKING: # pragma: no cover
161
+ ChartData = BalancesChart | BarChart | HierarchyChart
162
+
163
+
164
+ class ChartApi:
165
+ """Functions to generate chart data."""
166
+
167
+ @staticmethod
168
+ def account_balance(account_name: str) -> ChartData:
169
+ """Generate data for an account balances chart."""
170
+ return BalancesChart(
171
+ gettext("Account Balance"),
172
+ g.ledger.charts.linechart(
173
+ g.filtered,
174
+ account_name,
175
+ g.conv,
176
+ ),
177
+ )
178
+
179
+ @staticmethod
180
+ def hierarchy(
181
+ account_name: str,
182
+ *,
183
+ label: str | None = None,
184
+ ) -> ChartData:
185
+ """Generate data for an account hierarchy chart."""
186
+ return HierarchyChart(
187
+ label or account_name,
188
+ g.ledger.charts.hierarchy(
189
+ g.filtered,
190
+ account_name,
191
+ g.conv,
192
+ ),
193
+ )
194
+
195
+ @staticmethod
196
+ def interval_totals(
197
+ interval: Interval,
198
+ account_name: str | tuple[str, ...],
199
+ label: str | None = None,
200
+ *,
201
+ invert: bool = False,
202
+ ) -> ChartData:
203
+ """Generate data for an account per interval chart."""
204
+ return BarChart(
205
+ label or str(account_name),
206
+ g.ledger.charts.interval_totals(
207
+ g.filtered,
208
+ interval,
209
+ account_name,
210
+ g.conv,
211
+ invert=invert,
212
+ ),
213
+ )
214
+
215
+ @staticmethod
216
+ def net_worth() -> ChartData:
217
+ """Generate data for net worth chart."""
218
+ return BalancesChart(
219
+ gettext("Net Worth"),
220
+ g.ledger.charts.net_worth(g.filtered, g.interval, g.conv),
221
+ )