rvgp 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +23 -0
  4. data/LICENSE +504 -0
  5. data/README.md +223 -0
  6. data/Rakefile +32 -0
  7. data/bin/rvgp +8 -0
  8. data/lib/rvgp/application/config.rb +159 -0
  9. data/lib/rvgp/application/descendant_registry.rb +122 -0
  10. data/lib/rvgp/application/status_output.rb +139 -0
  11. data/lib/rvgp/application.rb +170 -0
  12. data/lib/rvgp/base/command.rb +457 -0
  13. data/lib/rvgp/base/grid.rb +531 -0
  14. data/lib/rvgp/base/reader.rb +29 -0
  15. data/lib/rvgp/base/reconciler.rb +434 -0
  16. data/lib/rvgp/base/validation.rb +261 -0
  17. data/lib/rvgp/commands/cashflow.rb +160 -0
  18. data/lib/rvgp/commands/grid.rb +70 -0
  19. data/lib/rvgp/commands/ireconcile.rb +95 -0
  20. data/lib/rvgp/commands/new_project.rb +296 -0
  21. data/lib/rvgp/commands/plot.rb +41 -0
  22. data/lib/rvgp/commands/publish_gsheets.rb +83 -0
  23. data/lib/rvgp/commands/reconcile.rb +58 -0
  24. data/lib/rvgp/commands/rotate_year.rb +202 -0
  25. data/lib/rvgp/commands/validate_journal.rb +59 -0
  26. data/lib/rvgp/commands/validate_system.rb +44 -0
  27. data/lib/rvgp/commands.rb +160 -0
  28. data/lib/rvgp/dashboard.rb +252 -0
  29. data/lib/rvgp/fakers/fake_feed.rb +245 -0
  30. data/lib/rvgp/fakers/fake_journal.rb +57 -0
  31. data/lib/rvgp/fakers/fake_reconciler.rb +88 -0
  32. data/lib/rvgp/fakers/faker_helpers.rb +25 -0
  33. data/lib/rvgp/gem.rb +80 -0
  34. data/lib/rvgp/journal/commodity.rb +453 -0
  35. data/lib/rvgp/journal/complex_commodity.rb +214 -0
  36. data/lib/rvgp/journal/currency.rb +101 -0
  37. data/lib/rvgp/journal/journal.rb +141 -0
  38. data/lib/rvgp/journal/posting.rb +156 -0
  39. data/lib/rvgp/journal/pricer.rb +267 -0
  40. data/lib/rvgp/journal.rb +24 -0
  41. data/lib/rvgp/plot/gnuplot.rb +478 -0
  42. data/lib/rvgp/plot/google-drive/output_csv.rb +44 -0
  43. data/lib/rvgp/plot/google-drive/output_google_sheets.rb +434 -0
  44. data/lib/rvgp/plot/google-drive/sheet.rb +67 -0
  45. data/lib/rvgp/plot.rb +293 -0
  46. data/lib/rvgp/pta/hledger.rb +237 -0
  47. data/lib/rvgp/pta/ledger.rb +308 -0
  48. data/lib/rvgp/pta.rb +311 -0
  49. data/lib/rvgp/reconcilers/csv_reconciler.rb +424 -0
  50. data/lib/rvgp/reconcilers/journal_reconciler.rb +41 -0
  51. data/lib/rvgp/reconcilers/shorthand/finance_gem_hacks.rb +48 -0
  52. data/lib/rvgp/reconcilers/shorthand/international_atm.rb +152 -0
  53. data/lib/rvgp/reconcilers/shorthand/investment.rb +144 -0
  54. data/lib/rvgp/reconcilers/shorthand/mortgage.rb +195 -0
  55. data/lib/rvgp/utilities/grid_query.rb +190 -0
  56. data/lib/rvgp/utilities/yaml.rb +131 -0
  57. data/lib/rvgp/utilities.rb +44 -0
  58. data/lib/rvgp/validations/balance_validation.rb +68 -0
  59. data/lib/rvgp/validations/duplicate_tags_validation.rb +48 -0
  60. data/lib/rvgp/validations/uncategorized_validation.rb +15 -0
  61. data/lib/rvgp.rb +66 -0
  62. data/resources/README.MD/2022-cashflow-google.png +0 -0
  63. data/resources/README.MD/2022-cashflow.png +0 -0
  64. data/resources/README.MD/all-wealth-growth-google.png +0 -0
  65. data/resources/README.MD/all-wealth-growth.png +0 -0
  66. data/resources/gnuplot/default.yml +80 -0
  67. data/resources/i18n/en.yml +192 -0
  68. data/resources/iso-4217-currencies.json +171 -0
  69. data/resources/skel/Rakefile +5 -0
  70. data/resources/skel/app/grids/cashflow_grid.rb +27 -0
  71. data/resources/skel/app/grids/monthly_income_and_expenses_grid.rb +25 -0
  72. data/resources/skel/app/grids/wealth_growth_grid.rb +35 -0
  73. data/resources/skel/app/plots/cashflow.yml +33 -0
  74. data/resources/skel/app/plots/monthly-income-and-expenses.yml +17 -0
  75. data/resources/skel/app/plots/wealth-growth.yml +20 -0
  76. data/resources/skel/config/csv-format-acme-checking.yml +9 -0
  77. data/resources/skel/config/google-secrets.yml +5 -0
  78. data/resources/skel/config/rvgp.yml +0 -0
  79. data/resources/skel/journals/prices.db +0 -0
  80. data/rvgp.gemspec +6 -0
  81. data/test/assets/ledger_total_monthly_liabilities_with_empty.xml +383 -0
  82. data/test/assets/ledger_total_monthly_liabilities_with_empty2.xml +428 -0
  83. data/test/test_command_base.rb +61 -0
  84. data/test/test_commodity.rb +270 -0
  85. data/test/test_csv_reconciler.rb +60 -0
  86. data/test/test_currency.rb +24 -0
  87. data/test/test_fake_feed.rb +228 -0
  88. data/test/test_fake_journal.rb +98 -0
  89. data/test/test_fake_reconciler.rb +60 -0
  90. data/test/test_journal_parse.rb +545 -0
  91. data/test/test_ledger.rb +102 -0
  92. data/test/test_plot.rb +133 -0
  93. data/test/test_posting.rb +50 -0
  94. data/test/test_pricer.rb +139 -0
  95. data/test/test_pta_adapter.rb +575 -0
  96. data/test/test_utilities.rb +45 -0
  97. metadata +268 -0
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ # This module contains helper methods used throughout RVGP. These are just common
5
+ # codepaths, that have little in common, save for their general utility.
6
+ module Utilities
7
+ # This returns each month in a series from the first date, to the last, in the
8
+ # provided array of dates
9
+ # @overload months_through(date, ...)
10
+ # @param [Array<Date>] date A date, that will be used to calculate the range of months to construct a range from.
11
+ # @param [Array<Date>] ... More dates. This method will automatically select the max and min from the sample
12
+ # provided.
13
+ # @return [Array<Date>] An array, containing a Date, set to the first of every month, in the provided range.
14
+ def months_through(*args)
15
+ dates = args.flatten.uniq.sort
16
+
17
+ ret = []
18
+ unless dates.empty?
19
+ d = Date.new dates.first.year, dates.first.month, 1 # start_at
20
+ while d <= Date.new(dates.last.year, dates.last.month, 1) # end_at
21
+ ret << d
22
+ d = d >> 1
23
+ end
24
+ end
25
+ ret
26
+ end
27
+
28
+ # Convert the provided string, into a Regexp. Note that the the ixm suffixes are supported, unlike
29
+ # ruby's Regexp.new(str) method
30
+ # @param [String] str A string, in the 'standard' regexp format: '/Running (?:to|at) the Park/i'
31
+ # @return [Regexp] The conversion to a useable regexp, for the provided string
32
+ def string_to_regex(str)
33
+ if %r{\A/(.*)/([imx]?[imx]?[imx]?)\Z}.match str
34
+ Regexp.new(::Regexp.last_match(1), ::Regexp.last_match(2).chars.map do |c|
35
+ case c
36
+ when 'i' then Regexp::IGNORECASE
37
+ when 'x' then Regexp::EXTENDED
38
+ when 'm' then Regexp::MULTILINE
39
+ end
40
+ end.reduce(:|))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ module Validations
5
+ # This validation asserts that the ledger-reported balance, matches a provided
6
+ # balance, on a given day. These balances, should be stipulated in a section
7
+ # of your reconciler, that looks like this:
8
+ # ```
9
+ # balances:
10
+ # '2022-01-01': $ 105.63
11
+ # '2022-09-01': $ 300.29
12
+ # '2022-10-01': $ 400.33
13
+ # ```
14
+ # These balances are expected to come from a bank statement, and this validation
15
+ # ensures that rvgp is matching the records of your financial institution
16
+ class BalanceValidation < RVGP::Base::JournalValidation
17
+ # If there are no checkpoints in the 'balances' line of the reconciler, this
18
+ # fires a warning. If there are checkpoints, then, we scan the register to
19
+ # ensure that the balance of the reconciler.from, on the checkpoint date,
20
+ # matches the ledger/hledger balance, on that date. (and if it doesnt,
21
+ # fires an error)
22
+ def validate
23
+ if reconciler.balances.nil? || reconciler.balances.empty?
24
+ warning! 'No balance checkpoints found.'
25
+ else
26
+ is_account_valid = true
27
+ cite_balances = reconciler.balances.map do |d, expected_balance_s|
28
+ expected_balance = expected_balance_s.to_commodity
29
+
30
+ balances_on_day = pta.balance format('^%s$', reconciler.from),
31
+ depth: 1,
32
+ end: d.to_s,
33
+ file: RVGP.app.config.project_journal_path
34
+
35
+ balances_found = balances_on_day.accounts.map(&:amounts).flatten.find_all do |amount|
36
+ amount.code == expected_balance.code
37
+ end
38
+
39
+ found = if balances_found.empty?
40
+ # Rather than operate from nil, we'll establish that we're '0' of units
41
+ # of the expected symbol
42
+ RVGP::Journal::Commodity.from_symbol_and_amount expected_balance.code, 0
43
+ else
44
+ balances_found.sum
45
+ end
46
+
47
+ found_as_s = if found
48
+ format('Found: %s', found.to_s)
49
+ else
50
+ found = RVGP::Journal::Commodity.from_symbol_and_amount expected_balance.code, 0
51
+ '(Nil)'
52
+ end
53
+
54
+ is_valid = expected_balance == found
55
+ is_account_valid = false unless is_valid
56
+
57
+ format('(%<day>s) Expected: %<expected>s %<indicator>s',
58
+ day: d.to_s,
59
+ expected: expected_balance.to_s,
60
+ indicator: RVGP.pastel.send(is_valid ? :green : :red, found_as_s))
61
+ end
62
+
63
+ error! 'Failed Checkpoint(s):', cite_balances unless is_account_valid
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ module Validations
5
+ # This class implements a journal validation that ensures a given transfer, hasn't
6
+ # been tagged more than once, with the same tag.
7
+ class DuplicateTagsValidation < RVGP::Base::JournalValidation
8
+ # Reviews every transfer, and post, to ensure that there are no tags occurring more than once, in
9
+ # any given entry. Unlike most of the validations in RVGP, this one doesn't use ledger or hledger
10
+ # to validate. This validation parses the file itself, in ruby, and ensures based on the contents.
11
+ def validate
12
+ journal = RVGP::Journal.parse File.read(reconciler.output_file)
13
+ dupe_messages = []
14
+
15
+ journal.postings.each do |posting|
16
+ posting_tag_names = posting.tags.map(&:key)
17
+ found_dupes = posting_tag_names.find_all { |tag| posting_tag_names.count(tag) > 1 }.uniq
18
+
19
+ if found_dupes.empty?
20
+ posting.transfers.each do |transfer|
21
+ transfer_tag_names = transfer.tags.map(&:key) + posting_tag_names
22
+
23
+ found_dupes = transfer_tag_names.find_all { |tag| transfer_tag_names.count(tag) > 1 }.uniq
24
+
25
+ next if found_dupes.empty?
26
+
27
+ dupe_messages << format('Line %<line>d: %<date>s %<desc>s (Transfer: %<tags>s)',
28
+ line: posting.line_number,
29
+ date: posting.date,
30
+ desc: posting.description,
31
+ tags: found_dupes.join(', '))
32
+ end
33
+ else
34
+ dupe_messages << format('Line %<line>d: %<date>s %<desc>s (%<tags>s)',
35
+ line: posting.line_number,
36
+ date: posting.date,
37
+ desc: posting.description,
38
+ tags: found_dupes.join(','))
39
+ end
40
+ end
41
+
42
+ unless dupe_messages.empty?
43
+ error! 'These postings have been tagged with the same tag, more than once', dupe_messages
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ # This module provides a number of basic, and common, validations for use in your projects
5
+ module Validations
6
+ # This class implements a journal validation that ensures there are no uncategorized
7
+ # expenses in the journal
8
+ class UncategorizedValidation < RVGP::Base::JournalValidation
9
+ # Ensures that there is no balance for 'Unknown' categories in a journal
10
+ def validate
11
+ validate_no_balance 'Uncategorized Transactions', 'Unknown'
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/rvgp.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rvgp/utilities/yaml'
4
+ require_relative 'rvgp/application'
5
+ require_relative 'rvgp/commands'
6
+ require_relative 'rvgp/base/reconciler'
7
+
8
+ # NOTE: Reconcilers & shorthand may want to go into a registry system at
9
+ # some point, akin to grids/validations.
10
+ require_relative 'rvgp/reconcilers/csv_reconciler'
11
+ require_relative 'rvgp/reconcilers/journal_reconciler'
12
+ require_relative 'rvgp/reconcilers/shorthand/mortgage'
13
+ require_relative 'rvgp/reconcilers/shorthand/investment'
14
+ require_relative 'rvgp/reconcilers/shorthand/international_atm'
15
+
16
+ require_relative 'rvgp/base/validation'
17
+
18
+ require_relative 'rvgp/journal'
19
+ require_relative 'rvgp/pta/ledger'
20
+ require_relative 'rvgp/pta/hledger'
21
+
22
+ require_relative 'rvgp/base/grid'
23
+
24
+ # Gem Paths / Resources:
25
+ require_relative 'rvgp/gem'
26
+
27
+ I18n.load_path << Dir[RVGP::Gem.root('resources/i18n/*.yml')]
28
+ RVGP::Journal::Currency.currencies_config = RVGP::Gem.root('resources/iso-4217-currencies.json')
29
+
30
+ # The base module, under which all RVGP code is filed
31
+ module RVGP
32
+ # @param from_path [String] The directory path, to an RVGP project.
33
+ # @return [RVGP::Application] The initialized application, that was stored in RVGP.app
34
+ def self.initialize_app(from_path)
35
+ raise StandardError, 'Application is already initialized' if @app
36
+
37
+ @app = Application.new from_path
38
+ end
39
+
40
+ # @return [RVGP::Application] The currently-initialized RVGP:Application
41
+ def self.app
42
+ @app
43
+ end
44
+
45
+ # @return [Pastel] The global pastel object, used to output to the console.
46
+ def self.pastel
47
+ @pastel ||= Pastel.new enabled: $stdout.tty?
48
+ end
49
+
50
+ # @!attribute [r] self.commands
51
+ # Contains an array of all available objects, with parent of type {RVGP::Base::Command}.
52
+ # @return [Array<RVGP::Base::Command>] the commands that are available in this project
53
+ # @!attribute [r] self.grids
54
+ # Contains an array of all available objects, with parent of type {RVGP::Base::Grid}.<br>
55
+ # **NOTE:** RVGP.app.task_names => Array<String>, will return all grid-building tasks that have been defined in the
56
+ # project.
57
+ # @return [Array<RVGP::Base::Grid>] the grids that are available in this project
58
+ # @!attribute [r] self.journal_validations
59
+ # Contains an array of all available objects, with parent of type {RVGP::Base::JournalValidation}.
60
+ # @return [Array<RVGP::Base::JournalValidation>] the journal validations that are available in this project
61
+ # @!attribute [r] self.system_validations
62
+ # Contains an array of all available objects, with parent of type {RVGP::Base::SystemValidation}.<br>
63
+ # **NOTE:** RVGP.system_validations.task_names => Array<String>, will return all system validation tasks that have
64
+ # been defined in the project.
65
+ # @return [Array<RVGP::Base::SystemValidation>] the system validations that are available in this project
66
+ end
@@ -0,0 +1,80 @@
1
+ colors:
2
+ base:
3
+ # Solarized Light:
4
+ base03: '#002b36'
5
+ base02: '#073642'
6
+ base01: '#586e75'
7
+ base00: '#657b83'
8
+ base0: '#839496'
9
+ base1: '#93a1a1'
10
+ base2: '#eee8d5'
11
+ base3: '#fdf6e3'
12
+ yellow: '#b58900'
13
+ orange: '#cb4b16'
14
+ red: '#dc322f'
15
+ magenta: '#d33682'
16
+ violet: '#6c71c4'
17
+ blue: '#268bd2'
18
+ cyan: '#2aa198'
19
+ green: '#859900'
20
+ font: :base03
21
+ title: :font
22
+ grid: :base02
23
+ axis: :base02
24
+ key_text: :font
25
+ background: :base3
26
+ series:
27
+ # https://www.heavy.ai/blog/12-color-palettes-for-telling-better-stories-with-your-data
28
+ # Spring Pastels
29
+ - "#7eb0d5"
30
+ - "#fd7f6f"
31
+ - "#b2e061"
32
+ - "#bd7ebe"
33
+ - "#ffb55a"
34
+ - "#ffee65"
35
+ - "#beb9db"
36
+ - "#fdcce5"
37
+ - "#8bd3c7"
38
+ # Retro Metro
39
+ - "#ea5545"
40
+ - "#f46a9b"
41
+ - "#ef9b20"
42
+ - "#edbf33"
43
+ - "#ede15b"
44
+ - "#bdcf32"
45
+ - "#87bc45"
46
+ - "#27aeef"
47
+ # http://www.gnuplotting.org/data/dark2.pal
48
+ - '#1B9E77'
49
+ - '#D95F02'
50
+ - '#7570B3'
51
+ - '#E7298A'
52
+ - '#66A61E'
53
+ - '#E6AB02'
54
+ - '#A6761D'
55
+ # Blue to Yellow
56
+ - "#115f9a"
57
+ - "#1984c5"
58
+ - "#22a7f0"
59
+ - "#48b5c4"
60
+ - "#76c68f"
61
+ - "#a6d75b"
62
+ - "#c9e52f"
63
+ - "#d0ee11"
64
+ - "#d0f400"
65
+ header: |+
66
+ set datafile separator ","
67
+ set title "%{title}" font "sans,11" textcolor rgb "%{title_rgb}" enhanced
68
+ set terminal wxt size 1200,400 persist enhanced font 'sans,10' background rgb '%{background_rgb}'
69
+ set style line 102 lc rgb '%{grid_rgb}' lt 0 lw 1
70
+ set grid back ls 102
71
+ set style line 101 lc rgb '%{axis_rgb}' lt 1 lw 1
72
+ set border 3 front ls 101
73
+ set xtics font ",9"
74
+ set ytics font ",11"
75
+ set timefmt "%%m-%%y"
76
+ set format x "%%b-%%y"
77
+ set format y "$ %%'.0f"
78
+ unset colorbox
79
+ set key on under center textcolor rgb "%{key_text_rgb}" enhanced font 'sans,9'
80
+ set decimal locale
@@ -0,0 +1,192 @@
1
+ en:
2
+ help:
3
+ usage: 'Usage: %{program} [OPTION]... [COMMAND] [TARGET]...'
4
+ indent: ' '
5
+ description: >
6
+ A plain text accounting workflow tool for: (r)econciling, (v)alidating, (g)rid-production and (p)lotting your finances with ruby. See the program github for more details.
7
+
8
+ command_introduction: >
9
+ COMMAND - This is a required parameter. Command-specific options are
10
+ further available, once you've specified a command mode. See the 'Available Commands' section for a list of supported commands.
11
+ target_introduction: >
12
+ TARGET(s) - Depending on which command you select, a list of targets can be supplied. Typically,
13
+ supplying the -l option, in lieu of any targets, will output the targets supported by the command.
14
+ Note that any asterisk encountered in a target parameter, will expand to include all targets that
15
+ can be matched given the provided (target) string.
16
+ command_list_introduction: 'Available Commands:'
17
+ global_option_introduction: >
18
+ Global Options:
19
+ The following options are available to all commands:
20
+
21
+ -d --dir[=PATH] Use the supplied PATH as the active rvgp directory.
22
+ If unsupplied, this option defaults to the basedir of
23
+ the LEDGER_FILE environment variable.
24
+ commands:
25
+ cashflow:
26
+ description: 'Output a convenient dashboard with income/expense flows, based on intention'
27
+ options:
28
+ all: 'Output a dashboard for every intention in the system'
29
+ list: 'List all available intention-tag values'
30
+ date: 'Specify a date to use, when calculatingg the "recent months" to display. Defaults to "today".'
31
+ new_project:
32
+ description: 'Create a new plain text accounting project, populated with random data, in the path specified by --dir.'
33
+ plot:
34
+ description: 'Build one or more gnuplots, based on a grid variant.'
35
+ options:
36
+ all: 'Build all available plots'
37
+ list: 'List all available plots'
38
+ stdout: 'Output build to STDOUT instead of output_file'
39
+ publish_gsheets:
40
+ description: 'Publish plots, both as a spreadsheet, as well as a graph, into a google sheets workbook.'
41
+ options:
42
+ all: 'Publish all available plots'
43
+ list: 'List all available plot variants'
44
+ csvdir: 'Output the provided plots, as csvs, with grid hacks applied, into the specified directory. Mostly useful for debugging.'
45
+ title: 'The title of the Google doc being published. Defaults to "RVGP Finance Report %m/%d/%y %H:%M". '
46
+ sleep: 'Seconds to sleep, between each sheet upload. This sleep prevents Google from aborting the publish, under the auspice of api spamming. Defaults to 5.'
47
+ ireconcile:
48
+ description: 'Open an interactive vim session, to edit and build a reconcile'
49
+ options:
50
+ all: 'Process all available reconcilers'
51
+ list: 'List all available reconcilers'
52
+ vsplit: 'Split the input and output panes vertically, instead of horizontally (the default)'
53
+ grid:
54
+ description: "Generate Grid csv's in build/grids"
55
+ options:
56
+ all: 'Process all available grids'
57
+ list: 'List all available grids'
58
+ reconcile:
59
+ description: 'Create/Update the build/*.journal, based on ./reconcilers/*.yml'
60
+ options:
61
+ stdout: 'Output build to STDOUT instead of output_file'
62
+ all: 'Process all available reconcilers'
63
+ list: 'List all available reconcilers'
64
+ concise: "Concise output mode. Strips output that's unrelated to errors and warnings. (Mostly used by the ireconcile command)"
65
+ rotate_year:
66
+ description: "Rotate the given reconcilers, for a new year. And, move the last year's input files into the historical directory under your project feeds."
67
+ options:
68
+ all: 'Rotate all eligible reconcilers'
69
+ list: 'List reconcilers eligible for rotation'
70
+ validate_journal:
71
+ description: 'Validate reconciled journals, using the app/validations'
72
+ options:
73
+ all: 'Process all available journals'
74
+ list: 'List all available journals'
75
+ validate_system:
76
+ description: 'Run validations on the ledger, once the individual journals are valid'
77
+ options:
78
+ all: 'Process all available system validations'
79
+ list: 'List all available system validations'
80
+ commands:
81
+ cashflow:
82
+ list_targets: 'The following cashflow intentions are available:'
83
+ account: 'Account'
84
+ expenses: 'Expenses'
85
+ income: 'Income'
86
+ cash_flow: 'Cash Flow'
87
+ errors:
88
+ screen_too_small: 'Screen width "%{screen_width}" is too small. Minimum width is %{minimum_width}.'
89
+ unrecognized_path: 'Unrecognized path: %{file}'
90
+ new_project:
91
+ directory_exists_prompt: "The directory \"%{dir}\" already exists. Continuing will overwrite some of its contents. Are you sure you wish to continue creating a new project in this directory? (Type \"Yes\" to continue) : "
92
+ project_name_prompt: "Whose project is this? A person's full name or a company name will work: "
93
+ project_name_confirmation: "You entered \"%{project_name}\". Is that correct? (Type \"Yes\" to continue) : "
94
+ confirm_operation: "Yes"
95
+ operation_aborted: "\"new_project\" project operation aborted"
96
+ completed_banner: "\nThe new project has been generated successfully.\nThough you may want to add the following line to your ~/.bashrc:\n export LEDGER_FILE=\"%{journal_path}\"\n\nYou're ready to begin working on this project. Try cd'ing into its directory, and running `rake`."
97
+ initialize:
98
+ project_directory: "Project directory"
99
+ bank_feeds: 'Randomized bank feeds'
100
+ reconcilers: 'Randomized reconcilers'
101
+ errors:
102
+ missing_app_dir: "An application directory was not provided, and is required. Set this parameter via --dir argument, or via the LEDGER_FILE environment variable"
103
+ directory_exists: "The directory \"%{dir}\" could not be created because it already exists."
104
+ plot:
105
+ target_description: 'Build the %{name} plot file, and its variants'
106
+ rotate_year:
107
+ list_targets: 'The following reconcilers are eligible for annual rotation:'
108
+ operations_header: 'The following command(s) will be executed:'
109
+ operation_rotate: 'Rotate "%{name}"'
110
+ operation_mkdir: 'mkdir %{path}'
111
+ confirm_operation: "Yes"
112
+ confirm_operation_prompt: "Would you like to run the above operations? (Type \"Yes\" to continue) "
113
+ operation_aborted: "\"rotate_year\" project operation aborted"
114
+ operation_element: ' 🟢 %{operation}'
115
+ publish_gsheets:
116
+ errors:
117
+ missing_google_secrets: 'Missing a readable config/google-secrets.yml file in your project directory'
118
+ unable_to_write_to_csvdir: 'Unable to write to path "%{csvdir}"'
119
+ grid:
120
+ list_targets: 'The following grids are available:'
121
+ target_description: '%{description} for %{year}'
122
+ rescan_grids:
123
+ target_description: 'Re-scan the journal directory, and register previously unavaible grid targets into the running build'
124
+ rescan_plots:
125
+ target_description: 'Re-scan the grid directory, and register previously unavaible plot targets into the running build'
126
+ reconcile:
127
+ list_targets: 'The following reconcilers are available:'
128
+ target_description: 'Reconcile the "%{input_file}" journal'
129
+ errors:
130
+ journal_missing: 'Journal(s) Missing.'
131
+ either_concise_or_stdout: 'Either -concise or -stdout mode can be specified. But, alas, not both.'
132
+ validate_journal:
133
+ list_targets: 'The following journal validations are available:'
134
+ target_description: 'Validate the "%{input_file}" journal'
135
+ validate_system:
136
+ list_targets: 'The following system validations are available:'
137
+
138
+ error:
139
+ error: 'Error'
140
+ warning: 'Warning'
141
+ missing_entry_in_prices_db: ": The following entry is missing in the prices database:\n P %{time} %{from} %{to}"
142
+ end_of_args: 'Missing an expected parameter value, in the argument list'
143
+ no_targets: "No targets specified"
144
+ missing_target: "One or more targets not found : %{targets}"
145
+ missing_command: 'No command specified. Try running --help to view the available program modes'
146
+ command_unrecognized: '"%{command}" command is unrecognized. Consult the help page.'
147
+ command_errors: "Unable to %{command}:"
148
+ command_error: " 🟢 %{error}"
149
+ no_application_dir: "Unable to determine the directory of your application. Consider setting the LEDGER_FILE environment variable, or providing a directory via the --dir parameter"
150
+ invalid_application_dir: 'Invalid or missing project found at the directory "%{directory}"'
151
+ status:
152
+ indicators:
153
+ complete: '🟢'
154
+ complete_and: '%{left} & %{right} '
155
+ fill: '.'
156
+ indent: ' '
157
+ truncated: '…'
158
+ attention1: '🟢'
159
+ attention2: ' ▩ '
160
+ commands:
161
+ reconcile:
162
+ icon: '🏗️ '
163
+ header: Building Journals from Feeds
164
+ prefix: Expanding
165
+ generate:
166
+ icon: 🏦
167
+ header: Generating Automatic Transactions
168
+ prefix: Making
169
+ validate_journal:
170
+ icon: 📒
171
+ header: Inspecting Individual Journal Files
172
+ prefix: Validating
173
+ validate_system:
174
+ icon: 📚
175
+ header: Inspecting System State
176
+ prefix: Validating
177
+ grid:
178
+ icon: ▦
179
+ header: Generating Grids
180
+ prefix: Calculating
181
+ plot:
182
+ icon: 📈
183
+ header: Generating Plots
184
+ prefix: Plotting
185
+ publish_gsheets:
186
+ icon: 📑
187
+ header: Publishing to Google Sheets
188
+ prefix: Sheeting
189
+ new_project:
190
+ icon: 📖
191
+ header: New Project
192
+ prefix: Initializing