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,3 @@
1
+ """Some Beancount plugins."""
2
+
3
+ from __future__ import annotations
@@ -0,0 +1,107 @@
1
+ """Beancount plugin to link entries to documents.
2
+
3
+ It goes through all entries with a `document` metadata-key, and tries to
4
+ associate them to Document entries. For transactions, it then also adds a link
5
+ from the transaction to documents, as well as the "#linked" tag.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections import defaultdict
11
+ from pathlib import Path
12
+ from typing import Any
13
+ from typing import TYPE_CHECKING
14
+
15
+ from rustfava.beans.abc import Document
16
+ from rustfava.beans.account import get_entry_accounts
17
+ from rustfava.beans.funcs import get_position
18
+ from rustfava.beans.helpers import replace
19
+ from rustfava.helpers import BeancountError
20
+ from rustfava.util.sets import add_to_set
21
+
22
+ if TYPE_CHECKING: # pragma: no cover
23
+ from collections.abc import Sequence
24
+
25
+ from rustfava.beans.abc import Directive
26
+
27
+
28
+ class DocumentError(BeancountError):
29
+ """Document-linking related error."""
30
+
31
+
32
+ __plugins__ = ["link_documents"]
33
+
34
+
35
+ def link_documents(
36
+ entries: Sequence[Directive],
37
+ _: Any,
38
+ ) -> tuple[Sequence[Directive], list[DocumentError]]:
39
+ """Link entries to documents."""
40
+ errors = []
41
+
42
+ # All document indices by their full file path.
43
+ by_fullname = {}
44
+ # All document indices by their file basename.
45
+ by_basename = defaultdict(list)
46
+
47
+ for index, entry in enumerate(entries):
48
+ if isinstance(entry, Document):
49
+ by_fullname[entry.filename] = index
50
+ by_basename[Path(entry.filename).name].append((index, entry))
51
+
52
+ new_entries = list(entries)
53
+ for index, entry in enumerate(entries):
54
+ disk_docs = [
55
+ value
56
+ for key, value in entry.meta.items()
57
+ if key.startswith("document") and isinstance(value, str)
58
+ ]
59
+
60
+ if not disk_docs:
61
+ continue
62
+
63
+ # Link the documents by using the entry date. This means that this will
64
+ # not necessarily be unique, but it shows which date the linked entry
65
+ # is on (and it will probably narrow it down enough)
66
+ entry_link = f"dok-{entry.date}"
67
+ entry_accounts = get_entry_accounts(entry)
68
+ entry_filename, _ = get_position(entry)
69
+ for disk_doc in disk_docs:
70
+ documents = [
71
+ j
72
+ for j, document in by_basename[disk_doc]
73
+ if document.account in entry_accounts
74
+ ]
75
+ disk_doc_path = str(
76
+ (Path(entry_filename).parent / disk_doc).resolve()
77
+ )
78
+ if disk_doc_path in by_fullname:
79
+ documents.append(by_fullname[disk_doc_path])
80
+
81
+ if not documents:
82
+ errors.append(
83
+ DocumentError(
84
+ entry.meta, f"Document not found: '{disk_doc}'", entry
85
+ ),
86
+ )
87
+ continue
88
+
89
+ for j in documents:
90
+ # Since we might link a document multiple times, we have to use
91
+ # the index for the replacement here.
92
+ doc: Document = new_entries[j] # type: ignore[assignment]
93
+ new_entries[j] = replace(
94
+ doc,
95
+ links=add_to_set(doc.links, entry_link),
96
+ tags=add_to_set(doc.tags, "linked"),
97
+ )
98
+
99
+ # Not all entry types support links, so only add links for the ones
100
+ # that do.
101
+ if hasattr(entry, "links"):
102
+ new_entries[index] = replace(
103
+ entry,
104
+ links=add_to_set(entry.links, entry_link), # ty:ignore[invalid-argument-type]
105
+ )
106
+
107
+ return new_entries, errors
@@ -0,0 +1,44 @@
1
+ """Beancount plugin to tag discovered documents.
2
+
3
+ It looks through all Document entries that were added by Beancount
4
+ automatically through file discovery and adds the tag "#discovered".
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from rustfava.beans.abc import Document
12
+ from rustfava.beans.helpers import replace
13
+ from rustfava.util.sets import add_to_set
14
+
15
+ if TYPE_CHECKING: # pragma: no cover
16
+ from collections.abc import Iterator
17
+ from collections.abc import Sequence
18
+
19
+ from rustfava.beans.abc import Directive
20
+ from rustfava.beans.types import BeancountOptions
21
+ from rustfava.helpers import BeancountError
22
+
23
+ __plugins__ = ["tag_discovered_documents"]
24
+
25
+
26
+ def tag_discovered_documents(
27
+ entries: Sequence[Directive],
28
+ options_map: BeancountOptions,
29
+ ) -> tuple[Sequence[Directive], list[BeancountError]]:
30
+ """Tag automatically added documents."""
31
+ if not options_map["documents"]: # pragma: no cover
32
+ return entries, []
33
+
34
+ def _tag_discovered() -> Iterator[Directive]:
35
+ for entry in entries:
36
+ if isinstance(entry, Document) and entry.meta["lineno"] == 0:
37
+ yield replace(
38
+ entry,
39
+ tags=add_to_set(entry.tags, "discovered"),
40
+ )
41
+ else:
42
+ yield entry
43
+
44
+ return list(_tag_discovered()), []
rustfava/py.typed ADDED
File without changes
@@ -0,0 +1,31 @@
1
+ """Rustledger WASM integration for Fava.
2
+
3
+ This module provides a Python interface to rustledger via wasmtime-py,
4
+ replacing beancount for parsing, validation, and querying.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from rustfava.rustledger.engine import RustledgerEngine
10
+ from rustfava.rustledger.loader import load_string
11
+ from rustfava.rustledger.loader import load_uncached
12
+
13
+
14
+ def is_encrypted_file(path: str) -> bool:
15
+ """Check if a file is GPG encrypted.
16
+
17
+ Args:
18
+ path: Path to file
19
+
20
+ Returns:
21
+ True if file is encrypted
22
+ """
23
+ return RustledgerEngine.get_instance().is_encrypted(path)
24
+
25
+
26
+ __all__ = [
27
+ "RustledgerEngine",
28
+ "is_encrypted_file",
29
+ "load_string",
30
+ "load_uncached",
31
+ ]
@@ -0,0 +1,76 @@
1
+ """Type constants for rustledger - replaces beancount type constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import Enum
6
+
7
+
8
+ # Account type sentinel - used in Custom entries to identify account values
9
+ # This matches beancount's '<AccountDummy>' sentinel
10
+ ACCOUNT_TYPE = "<AccountDummy>"
11
+
12
+
13
+ class Booking(Enum):
14
+ """Booking methods for inventory reduction."""
15
+
16
+ STRICT = "STRICT"
17
+ STRICT_WITH_SIZE = "STRICT_WITH_SIZE"
18
+ FIFO = "FIFO"
19
+ LIFO = "LIFO"
20
+ HIFO = "HIFO"
21
+ AVERAGE = "AVERAGE"
22
+ NONE = "NONE"
23
+
24
+
25
+ class Missing:
26
+ """Sentinel for missing/incomplete posting amounts.
27
+
28
+ This represents an amount that should be interpolated by the booking system.
29
+ """
30
+
31
+ _instance: Missing | None = None
32
+
33
+ def __new__(cls) -> Missing:
34
+ if cls._instance is None:
35
+ cls._instance = super().__new__(cls)
36
+ return cls._instance
37
+
38
+ def __repr__(self) -> str:
39
+ return "MISSING"
40
+
41
+ def __bool__(self) -> bool:
42
+ return False
43
+
44
+
45
+ # Singleton instance
46
+ MISSING = Missing()
47
+
48
+
49
+ # All directive type names
50
+ ALL_DIRECTIVES = frozenset([
51
+ "Transaction",
52
+ "Balance",
53
+ "Open",
54
+ "Close",
55
+ "Commodity",
56
+ "Pad",
57
+ "Event",
58
+ "Query",
59
+ "Note",
60
+ "Document",
61
+ "Price",
62
+ "Custom",
63
+ # Rustledger prefixed versions
64
+ "RLTransaction",
65
+ "RLBalance",
66
+ "RLOpen",
67
+ "RLClose",
68
+ "RLCommodity",
69
+ "RLPad",
70
+ "RLEvent",
71
+ "RLQuery",
72
+ "RLNote",
73
+ "RLDocument",
74
+ "RLPrice",
75
+ "RLCustom",
76
+ ])