appydave-tools 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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