appydave-tools 0.7.0 → 0.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14a6bd2963b58f85f247282a6cf8c537495b9a18300b04c3a3794ef5df4ecaa1
4
- data.tar.gz: 5ec47924ab4268377a297e1f02f6fa6b4f62207179f21fe8ccdcd69d62cd4f8d
3
+ metadata.gz: e4f0fbf0d8d8efc742e691efe1e128bb49f5a1f1423fe2b5eb7a700d9d606c24
4
+ data.tar.gz: 4a6ea3bba78bbb0c8983f462888e0c7e8c1910706e90bbb9db0bfbad99e47405
5
5
  SHA512:
6
- metadata.gz: 253546f33096bad9f9daaa5776fa9d98142a4e7cb43607bf2040ab648d49f83712c8f491b8730de4bd602bcd4080a9360cb9bdd082a1b249df4210d66083ebf6
7
- data.tar.gz: f1f638a4a299df238694c8b4e42c008e8663a08cb2b83931a953796ec6679fa83038bebe3f749b0351a7edd848220cb906c53ff076abbf953564ff0356347b29
6
+ metadata.gz: 85058aef1eccdf67319578d1d4c929a17da0143492ba91095ab7e09702f12648174a17bcb611ab5e2951ab500386b98a7f020c5695772c488ebd782bc275e3a6
7
+ data.tar.gz: fb4b852d1dff53cb256c190e586546d5ee2bf13a675da10b0d25c6abc4d04588d541541763bff650609816677e7e3fe407c9920508dcd843a8ab19ac6c2943ab
data/.rubocop.yml CHANGED
@@ -45,6 +45,9 @@ RSpec/ExampleLength:
45
45
 
46
46
  Metrics/MethodLength:
47
47
  Max: 25
48
+ Exclude:
49
+ - "**/spec/**/*"
50
+ - "bin/*.rb"
48
51
 
49
52
  Layout/LineLength:
50
53
  Max: 200
@@ -121,12 +124,15 @@ Metrics/AbcSize:
121
124
  Max: 25
122
125
  Exclude:
123
126
  - "bin/*"
127
+ - "lib/appydave/tools/subtitle_master/clean.rb"
124
128
  Metrics/CyclomaticComplexity:
125
129
  Exclude:
126
130
  - "**/models/**/*"
127
131
  - "lib/appydave/tools/bank_reconciliation/clean/mapper.rb"
132
+ - "lib/appydave/tools/subtitle_master/clean.rb"
128
133
  Metrics/PerceivedComplexity:
129
134
  Exclude:
130
135
  - "**/models/**/*"
136
+ - "lib/appydave/tools/subtitle_master/clean.rb"
131
137
  RSpec/MultipleMemoizedHelpers:
132
138
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [0.7.0](https://github.com/klueless-io/appydave-tools/compare/v0.6.1...v0.7.0) (2024-05-29)
2
+
3
+
4
+ ### Features
5
+
6
+ * new tool for doing bank reconciliations with chart of account matching ([9b82605](https://github.com/klueless-io/appydave-tools/commit/9b8260571f6046470d5963354ee1c80e493a0f28))
7
+
1
8
  ## [0.6.1](https://github.com/klueless-io/appydave-tools/compare/v0.6.0...v0.6.1) (2024-05-26)
2
9
 
3
10
 
@@ -32,19 +32,42 @@ class BankReconciliationCLI
32
32
  private
33
33
 
34
34
  def clean_transactions(args)
35
- options = {}
35
+ options = { include: [] }
36
36
  OptionParser.new do |opts|
37
37
  opts.banner = 'Usage: bank_reconciliation.rb clean [options]'
38
- opts.on('-i', '--include PATTERN', 'GLOB pattern for source transaction files') { |v| options[:include] = v }
39
- opts.on('-f', '--transaction FOLDER', 'Transaction CSV folder where original banking CSV files are stored') { |v| options[:transaction_folder] = v }
40
- opts.on('-o', '--output FILE', 'Output CSV file name') { |v| options[:output] = v }
38
+
39
+ opts.on('-i', '--include PATTERN', 'GLOB pattern for source transaction files') do |v|
40
+ options[:include] << v
41
+ end
42
+
43
+ opts.on('-f', '--transaction FOLDER', 'Transaction CSV folder where original banking CSV files are stored') do |v|
44
+ options[:transaction_folder] = v
45
+ end
46
+
47
+ opts.on('-o', '--output FILE', 'Output CSV file name') do |v|
48
+ options[:output] = v
49
+ end
50
+
41
51
  opts.on_tail('-h', '--help', 'Show this message') do
42
52
  puts opts
43
53
  exit
44
54
  end
45
55
  end.parse!(args)
46
56
 
47
- # Implement cleaning and normalizing transactions
57
+ transaction_folder = options[:transaction_folder] || '/default/transaction/folder'
58
+ output_file = options[:output] || 'clean_transactions.csv'
59
+ include_patterns = options[:include].empty? ? ['*'] : options[:include]
60
+
61
+ puts "Cleaning transactions with options: #{options}"
62
+
63
+ # Ensure the clean directory exists
64
+ clean_dir = File.dirname(output_file)
65
+ FileUtils.mkdir_p(clean_dir)
66
+
67
+ # Initialize the CleanTransactions class and process the files
68
+ cleaner = Appydave::Tools::BankReconciliation::Clean::CleanTransactions.new(transaction_folder: transaction_folder)
69
+ cleaner.clean_transactions(include_patterns, output_file)
70
+
48
71
  puts "Cleaning transactions with options: #{options}"
49
72
  end
50
73
 
@@ -95,5 +118,7 @@ class BankReconciliationCLI
95
118
  end
96
119
  end
97
120
 
121
+ Appydave::Tools::Configuration::Config.configure
122
+
98
123
  BankReconciliationCLI.new.run
99
124
  # BankReconciliationCLI.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
5
+
6
+ require 'appydave/tools'
7
+
8
+ # Process command line arguments for SubtitleMaster operations
9
+ class SubtitleMasterCLI
10
+ def initialize
11
+ @commands = {
12
+ 'clean' => method(:clean_subtitles),
13
+ 'correct' => method(:correct_subtitles),
14
+ 'split' => method(:split_subtitles),
15
+ 'highlight' => method(:highlight_subtitles),
16
+ 'image_prompts' => method(:generate_image_prompts)
17
+ }
18
+ end
19
+
20
+ def run
21
+ command, *args = ARGV
22
+ if @commands.key?(command)
23
+ @commands[command].call(args)
24
+ else
25
+ puts "Unknown command: #{command}"
26
+ print_help
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def clean_subtitles(args)
33
+ options = parse_options(args, 'clean')
34
+ cleaner = Appydave::Tools::SubtitleMaster::Clean.new(options[:file])
35
+ result = cleaner.clean
36
+ write_output(result, options[:output])
37
+ end
38
+
39
+ def correct_subtitles(args)
40
+ options = parse_options(args, 'correct')
41
+ corrector = Appydave::Tools::SubtitleMaster::Correct.new(options[:file])
42
+ result = corrector.correct
43
+ write_output(result, options[:output])
44
+ end
45
+
46
+ def split_subtitles(args)
47
+ options = parse_options(args, 'split', %i[words_per_group])
48
+ splitter = Appydave::Tools::SubtitleMaster::Split.new(options[:file], options[:words_per_group])
49
+ result = splitter.split
50
+ write_output(result, options[:output])
51
+ end
52
+
53
+ def highlight_subtitles(args)
54
+ options = parse_options(args, 'highlight')
55
+ highlighter = Appydave::Tools::SubtitleMaster::Highlight.new(options[:file])
56
+ result = highlighter.highlight
57
+ write_output(result, options[:output])
58
+ end
59
+
60
+ def generate_image_prompts(args)
61
+ options = parse_options(args, 'image_prompts')
62
+ image_prompter = Appydave::Tools::SubtitleMaster::ImagePrompts.new(options[:file])
63
+ result = image_prompter.generate_prompts
64
+ write_output(result, options[:output])
65
+ end
66
+
67
+ def parse_options(args, command, extra_options = [])
68
+ options = { file: nil, output: nil }
69
+ OptionParser.new do |opts|
70
+ opts.banner = "Usage: subtitle_master.rb #{command} [options]"
71
+
72
+ opts.on('-f', '--file FILE', 'SRT file to process') { |v| options[:file] = v }
73
+ opts.on('-o', '--output FILE', 'Output file') { |v| options[:output] = v }
74
+
75
+ extra_options.each do |opt|
76
+ case opt
77
+ when :words_per_group
78
+ opts.on('-w', '--words-per-group WORDS', 'Number of words per group for splitting') { |v| options[:words_per_group] = v.to_i }
79
+ end
80
+ end
81
+
82
+ opts.on_tail('-h', '--help', 'Show this message') do
83
+ puts opts
84
+ exit
85
+ end
86
+ end.parse!(args)
87
+
88
+ unless options[:file] && options[:output]
89
+ puts 'Missing required options. Use -h for help.'
90
+ exit
91
+ end
92
+
93
+ options
94
+ end
95
+
96
+ def write_output(result, output_file)
97
+ File.write(output_file, result)
98
+ puts "Processed file written to #{output_file}"
99
+ end
100
+
101
+ def print_help
102
+ puts 'Usage: subtitle_master.rb [command] [options]'
103
+ puts 'Commands:'
104
+ puts ' clean Clean and normalize SRT files'
105
+ puts ' correct Correct common typos and mistranslations in SRT files'
106
+ puts ' split Split subtitle groups based on word count'
107
+ puts ' highlight Highlight power words in subtitles'
108
+ puts ' image_prompts Generate image prompts from subtitle text'
109
+ puts "Run 'subtitle_master.rb [command] --help' for more information on a command."
110
+ end
111
+ end
112
+
113
+ SubtitleMasterCLI.new.run
@@ -10,24 +10,31 @@ module Appydave
10
10
  include KLog::Logging
11
11
 
12
12
  attr_reader :transaction_folder
13
+ attr_reader :output_folder
13
14
  attr_reader :transactions
14
15
 
15
16
  # (config_file)
16
- def initialize(transaction_folder: '/Volumes/Expansion/Sync/bank-reconciliation/original-transactions')
17
+ def initialize(transaction_folder: nil, output_folder: nil)
18
+ # needs to use config.bank_reconciliation.transaction_folder
19
+ transaction_folder ||= '/Volumes/Expansion/Sync/bank-reconciliation/original-transactions'
20
+ output_folder ||= File.join(transaction_folder, 'clean')
21
+
17
22
  @transaction_folder = transaction_folder
23
+ @output_folder = output_folder
18
24
  end
19
25
 
20
- def clean_transactions(input_globs, _output_file)
26
+ def clean_transactions(input_globs, output_file)
21
27
  raw_transactions = grab_raw_transactions(input_globs)
22
28
  transactions, duplicates_count = deduplicate(raw_transactions)
23
29
 
24
30
  transactions = Mapper.new.map(transactions)
25
- # tp transactions
31
+
32
+ # tp transactions, Appydave::Tools::BankReconciliation::Models::Transaction.csv_headers
26
33
 
27
34
  log.kv 'Deduped consolidated transactions', duplicates_count if duplicates_count.positive?
28
35
 
29
- # transactions = normalize(transactions)
30
- # save_to_csv(transactions, output_file)
36
+ save_to_csv(transactions, output_file)
37
+
31
38
  @transactions = transactions
32
39
  end
33
40
 
@@ -42,6 +49,7 @@ module Appydave
42
49
 
43
50
  input_globs.each do |glob|
44
51
  Dir.glob(glob).each do |file|
52
+ log.kv 'Reading transactions from', file
45
53
  raw_transactions = ReadTransactions.new(file).read
46
54
  deduped_transactions, duplicates_count = deduplicate(raw_transactions)
47
55
 
@@ -81,8 +89,11 @@ module Appydave
81
89
  end
82
90
 
83
91
  def save_to_csv(transactions, output_file)
92
+ FileUtils.mkdir_p(output_folder)
93
+ output_file = File.join(output_folder, output_file)
94
+
84
95
  CSV.open(output_file, 'w') do |csv|
85
- csv << ReconciledTransaction.csv_headers
96
+ csv << Appydave::Tools::BankReconciliation::Models::Transaction.csv_headers
86
97
  transactions.each do |transaction|
87
98
  csv << transaction.to_csv_row
88
99
  end
@@ -40,7 +40,9 @@ module Appydave
40
40
  trigram_match(transaction, 0.7, '70%') ||
41
41
  trigram_match(transaction, 0.6, '60%') ||
42
42
  trigram_match(transaction, 0.5, '50%') ||
43
- transaction
43
+ start_with_match(transaction) ||
44
+ includes(transaction)
45
+ transaction
44
46
  end
45
47
 
46
48
  def map_bank_account(transaction)
@@ -56,7 +58,7 @@ module Appydave
56
58
 
57
59
  def equality_match(transaction)
58
60
  coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
59
- chart_of_account.narration.to_s.delete(' ') == transaction.narration.delete(' ')
61
+ chart_of_account.narration.to_s.delete(' ').downcase == transaction.narration.delete(' ').downcase
60
62
  end
61
63
 
62
64
  return nil unless coa
@@ -66,6 +68,30 @@ module Appydave
66
68
  transaction
67
69
  end
68
70
 
71
+ def start_with_match(transaction)
72
+ coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
73
+ transaction.narration.to_s.delete(' ').downcase.start_with?(chart_of_account.narration.to_s.downcase)
74
+ end
75
+
76
+ return nil unless coa
77
+
78
+ transaction.coa_match_type = 'starts_with'
79
+ transaction.coa_code = coa.code
80
+ transaction
81
+ end
82
+
83
+ def includes(transaction)
84
+ coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
85
+ transaction.narration.to_s.delete(' ').downcase.include?(chart_of_account.narration.delete(' ').to_s.downcase)
86
+ end
87
+
88
+ return nil unless coa
89
+
90
+ transaction.coa_match_type = 'includes'
91
+ transaction.coa_code = coa.code
92
+ transaction
93
+ end
94
+
69
95
  def trigram_match(transaction, score_threshold, match_type)
70
96
  scored_transactions = config.bank_reconciliation.chart_of_accounts.map do |coa|
71
97
  {
@@ -21,6 +21,8 @@ module Appydave
21
21
  case platform
22
22
  when :bankwest
23
23
  read_bankwest(csv_lines)
24
+ when :bankwest2
25
+ read_bankwest2(csv_lines)
24
26
  end
25
27
  end
26
28
 
@@ -48,11 +50,35 @@ module Appydave
48
50
  @transactions
49
51
  end
50
52
 
53
+ def read_bankwest2(csv_lines)
54
+ @transactions = []
55
+
56
+ # Skip the header line and parse each subsequent line
57
+ CSV.parse(csv_lines.join, headers: true).each do |row|
58
+ transaction = Models::Transaction.new(
59
+ bsb_number: row['BSB / Account Number'].split(' - ').first,
60
+ account_number: row['BSB / Account Number'].split(' - ').last,
61
+ transaction_date: row['Transaction Date'],
62
+ narration: row['Narration'],
63
+ cheque_number: row['Cheque Number'],
64
+ debit: row['Debit'],
65
+ credit: row['Credit'],
66
+ balance: row['Balance'],
67
+ transaction_type: row['Transaction Type']
68
+ )
69
+ @transactions << transaction
70
+ end
71
+
72
+ @transactions
73
+ end
74
+
51
75
  # For bankwest the first row is the CSV will look like:
52
76
  # BSB Number,Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type
53
77
  def detect_platform(csv_lines)
54
78
  return :bankwest if csv_lines.first.start_with?('BSB Number,Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type')
79
+ return :bankwest2 if csv_lines.first.start_with?('Account Name,BSB / Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type')
55
80
 
81
+ puts "Unknown platform detected. CSV columns are: #{csv_lines.first.strip}"
56
82
  raise Appydave::Tools::Error, 'Unknown platform'
57
83
  end
58
84
  end
@@ -48,6 +48,42 @@ module Appydave
48
48
  @coa_match_type = coa_match_type
49
49
  @account_name = account_name
50
50
  end
51
+
52
+ def self.csv_headers
53
+ %i[
54
+ bsb_number
55
+ account_number
56
+ transaction_date
57
+ narration
58
+ cheque_number
59
+ debit
60
+ credit
61
+ balance
62
+ transaction_type
63
+ platform
64
+ coa_code
65
+ coa_match_type
66
+ account_name
67
+ ]
68
+ end
69
+
70
+ def to_csv_row
71
+ [
72
+ @bsb_number,
73
+ @account_number,
74
+ @transaction_date,
75
+ @narration,
76
+ @cheque_number,
77
+ @debit,
78
+ @credit,
79
+ @balance,
80
+ @transaction_type,
81
+ @platform,
82
+ @coa_code,
83
+ @coa_match_type,
84
+ @account_name
85
+ ]
86
+ end
51
87
  end
52
88
  end
53
89
  end
@@ -0,0 +1,3 @@
1
+ # Subtitle Master
2
+
3
+ [ChatGPT](https://chatgpt.com/c/f80dfca5-8168-4561-b5c6-8efed8672a88)
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appydave
4
+ module Tools
5
+ module SubtitleMaster
6
+ # Clean and normalize subtitles
7
+ class Clean
8
+ def initialize(file_path)
9
+ @file_path = file_path
10
+ end
11
+
12
+ def clean
13
+ content = File.read(@file_path, encoding: 'UTF-8')
14
+ content = remove_underscores(content)
15
+ normalize_lines(content)
16
+ end
17
+
18
+ private
19
+
20
+ def remove_underscores(content)
21
+ content.gsub(%r{</?u>}, '')
22
+ end
23
+
24
+ def normalize_lines(content)
25
+ lines = content.split("\n")
26
+ grouped_subtitles = []
27
+ current_subtitle = { text: '', start_time: nil, end_time: nil }
28
+
29
+ lines.each do |line|
30
+ if line =~ /^\d+$/ || line.strip.empty?
31
+ next
32
+ elsif line =~ /^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}$/
33
+ if current_subtitle[:start_time]
34
+ grouped_subtitles << current_subtitle.clone
35
+ current_subtitle = { text: '', start_time: nil, end_time: nil }
36
+ end
37
+
38
+ times = line.split(' --> ')
39
+ current_subtitle[:start_time] = times[0]
40
+ current_subtitle[:end_time] = times[1]
41
+ else
42
+ current_subtitle[:text] += ' ' unless current_subtitle[:text].empty?
43
+ current_subtitle[:text] += line.strip
44
+ end
45
+ end
46
+
47
+ grouped_subtitles << current_subtitle unless current_subtitle[:text].empty?
48
+
49
+ grouped_subtitles = merge_subtitles(grouped_subtitles)
50
+
51
+ build_normalized_content(grouped_subtitles)
52
+ end
53
+
54
+ def merge_subtitles(subtitles)
55
+ merged_subtitles = []
56
+ subtitles.each do |subtitle|
57
+ if merged_subtitles.empty? || merged_subtitles.last[:text] != subtitle[:text]
58
+ merged_subtitles << subtitle
59
+ else
60
+ merged_subtitles.last[:end_time] = subtitle[:end_time]
61
+ end
62
+ end
63
+ merged_subtitles
64
+ end
65
+
66
+ def build_normalized_content(grouped_subtitles)
67
+ normalized_content = []
68
+ grouped_subtitles.each_with_index do |subtitle, index|
69
+ normalized_content << (index + 1).to_s
70
+ normalized_content << "#{subtitle[:start_time]} --> #{subtitle[:end_time]}"
71
+ normalized_content << subtitle[:text]
72
+ normalized_content << ''
73
+ end
74
+
75
+ normalized_content.join("\n")
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.7.0'
5
+ VERSION = '0.8.0'
6
6
  end
7
7
  end
@@ -8,6 +8,7 @@ require 'open3'
8
8
  require 'openai'
9
9
  require 'optparse'
10
10
  require 'k_log'
11
+ require 'pry'
11
12
 
12
13
  require 'appydave/tools/version'
13
14
  require 'appydave/tools/gpt_context/file_collector'
@@ -25,6 +26,8 @@ require 'appydave/tools/bank_reconciliation/clean/read_transactions'
25
26
  require 'appydave/tools/bank_reconciliation/clean/mapper'
26
27
  require 'appydave/tools/bank_reconciliation/models/transaction'
27
28
 
29
+ require 'appydave/tools/subtitle_master/clean'
30
+
28
31
  Appydave::Tools::Configuration::Config.set_default do |config|
29
32
  config.config_path = File.expand_path('~/.config/appydave')
30
33
  config.register(:settings, Appydave::Tools::Configuration::Models::SettingsConfig)
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appydave-tools",
9
- "version": "0.7.0",
9
+ "version": "0.8.0",
10
10
  "devDependencies": {
11
11
  "@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
12
12
  "@semantic-release/changelog": "^6.0.3",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-29 00:00:00.000000000 Z
11
+ date: 2024-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clipboard
@@ -105,6 +105,7 @@ files:
105
105
  - bin/console
106
106
  - bin/gpt_context.rb
107
107
  - bin/setup
108
+ - bin/subtitle_master.rb
108
109
  - images.log
109
110
  - lib/appydave/tools.rb
110
111
  - lib/appydave/tools/bank_reconciliation/_doc.md
@@ -125,6 +126,8 @@ files:
125
126
  - lib/appydave/tools/gpt_context/file_collector.rb
126
127
  - lib/appydave/tools/name_manager/_doc.md
127
128
  - lib/appydave/tools/name_manager/project_name.rb
129
+ - lib/appydave/tools/subtitle_master/_doc.md
130
+ - lib/appydave/tools/subtitle_master/clean.rb
128
131
  - lib/appydave/tools/version.rb
129
132
  - package-lock.json
130
133
  - package.json