aspire_budget 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 19c4c01c53dfa0197f18c5c9106e860982bbc3456e3104f7c9587505cad730c1
4
+ data.tar.gz: a7665ac0e37ae4f6b9a6d125eb3845ed1ebac66228b7aad8b95dda2e725e3593
5
+ SHA512:
6
+ metadata.gz: 48e8701f8b7ded9c829d2c83e3d2dcf8f3ce51c8ca5ee5605574d3ce65bd1db0db089cac7a79a1d0f95ce31a1abe5f12122d343d3bd957efc41e89bf02c5259e
7
+ data.tar.gz: 227a8bf87a812c688592d8fac317bebe51abd9517d5ad5176416ee79aa50dd8d5eb4a50da023a28a1a8abaeaf7021a162121b527bdc405554cd02c1688e04e1d
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,11 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'bin/console'
4
+
5
+ NewCops: enable
6
+
7
+ Metrics/AbcSize:
8
+ Max: 20
9
+
10
+ Style/Documentation:
11
+ Enabled: false
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in aspire_budget.gemspec
6
+ gemspec
7
+
8
+ gem 'pry'
9
+ gem 'rake', '~> 12.0'
10
+ gem 'rspec', '~> 3.0'
11
+ gem 'rubocop'
@@ -0,0 +1,109 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aspire_budget (0.0.1)
5
+ google_drive (~> 3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ ast (2.4.1)
13
+ coderay (1.1.3)
14
+ declarative (0.0.10)
15
+ declarative-option (0.1.0)
16
+ diff-lcs (1.4.1)
17
+ faraday (1.0.1)
18
+ multipart-post (>= 1.2, < 3)
19
+ google-api-client (0.41.0)
20
+ addressable (~> 2.5, >= 2.5.1)
21
+ googleauth (~> 0.9)
22
+ httpclient (>= 2.8.1, < 3.0)
23
+ mini_mime (~> 1.0)
24
+ representable (~> 3.0)
25
+ retriable (>= 2.0, < 4.0)
26
+ signet (~> 0.12)
27
+ google_drive (3.0.5)
28
+ google-api-client (>= 0.11.0, < 1.0.0)
29
+ googleauth (>= 0.5.0, < 1.0.0)
30
+ nokogiri (>= 1.5.3, < 2.0.0)
31
+ googleauth (0.13.0)
32
+ faraday (>= 0.17.3, < 2.0)
33
+ jwt (>= 1.4, < 3.0)
34
+ memoist (~> 0.16)
35
+ multi_json (~> 1.11)
36
+ os (>= 0.9, < 2.0)
37
+ signet (~> 0.14)
38
+ httpclient (2.8.3)
39
+ jwt (2.2.1)
40
+ memoist (0.16.2)
41
+ method_source (1.0.0)
42
+ mini_mime (1.0.2)
43
+ mini_portile2 (2.4.0)
44
+ multi_json (1.14.1)
45
+ multipart-post (2.1.1)
46
+ nokogiri (1.10.9)
47
+ mini_portile2 (~> 2.4.0)
48
+ os (1.1.0)
49
+ parallel (1.19.2)
50
+ parser (2.7.1.4)
51
+ ast (~> 2.4.1)
52
+ pry (0.13.1)
53
+ coderay (~> 1.1)
54
+ method_source (~> 1.0)
55
+ public_suffix (4.0.5)
56
+ rainbow (3.0.0)
57
+ rake (12.3.3)
58
+ regexp_parser (1.7.1)
59
+ representable (3.0.4)
60
+ declarative (< 0.1.0)
61
+ declarative-option (< 0.2.0)
62
+ uber (< 0.2.0)
63
+ retriable (3.1.2)
64
+ rexml (3.2.4)
65
+ rspec (3.9.0)
66
+ rspec-core (~> 3.9.0)
67
+ rspec-expectations (~> 3.9.0)
68
+ rspec-mocks (~> 3.9.0)
69
+ rspec-core (3.9.2)
70
+ rspec-support (~> 3.9.3)
71
+ rspec-expectations (3.9.2)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.9.0)
74
+ rspec-mocks (3.9.1)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.9.0)
77
+ rspec-support (3.9.3)
78
+ rubocop (0.86.0)
79
+ parallel (~> 1.10)
80
+ parser (>= 2.7.0.1)
81
+ rainbow (>= 2.2.2, < 4.0)
82
+ regexp_parser (>= 1.7)
83
+ rexml
84
+ rubocop-ast (>= 0.0.3, < 1.0)
85
+ ruby-progressbar (~> 1.7)
86
+ unicode-display_width (>= 1.4.0, < 2.0)
87
+ rubocop-ast (0.0.3)
88
+ parser (>= 2.7.0.1)
89
+ ruby-progressbar (1.10.1)
90
+ signet (0.14.0)
91
+ addressable (~> 2.3)
92
+ faraday (>= 0.17.3, < 2.0)
93
+ jwt (>= 1.5, < 3.0)
94
+ multi_json (~> 1.10)
95
+ uber (0.1.0)
96
+ unicode-display_width (1.7.0)
97
+
98
+ PLATFORMS
99
+ ruby
100
+
101
+ DEPENDENCIES
102
+ aspire_budget!
103
+ pry
104
+ rake (~> 12.0)
105
+ rspec (~> 3.0)
106
+ rubocop
107
+
108
+ BUNDLED WITH
109
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Drowze
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ # Aspire Budget - Ruby
2
+
3
+ This is an independent project implementing a Ruby for Aspire Budgeting spreadsheets, leveraging from the use of another great gem: `google_drive`.
4
+ The idea of this gem is to enable a good API to be easily implemented, allowing more powerful and complex tools to emerge.
5
+
6
+ If you don't know Aspire Budgeting please refer to: https://aspirebudget.com/
7
+
8
+ ## Installation
9
+
10
+ todo
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ session = GoogleDrive::Session.from_config('path_to_your_credentials.json')
16
+ client = AspireBudget::Client.new(session: session, spreadsheet_key: 'YOUR_SPREADSHEET_KEY')
17
+ ```
18
+
19
+ ## Development
20
+
21
+ todo
22
+
23
+ ## Contributing
24
+
25
+ Bug reports, feature requests and pull requests are welcome.
26
+
27
+
28
+ ## License
29
+
30
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'aspire_budget'
7
+ spec.version = AspireBudget::VERSION
8
+ spec.authors = ['Drowze']
9
+ spec.email = ['gibim6+aspire@gmail.com']
10
+
11
+ spec.summary = 'Aspire Budget Ruby Wrapper'
12
+ spec.description = 'Aspire Budget Ruby Wrapper'
13
+ spec.homepage = 'https://google.com'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/drowze/aspirebudgeting_ruby'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_runtime_dependency 'google_drive', '~> 3.0'
30
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'pry'
5
+ require 'google_drive'
6
+
7
+ require 'bundler/setup'
8
+ require 'client'
9
+
10
+ session = GoogleDrive::Session.from_config(ENV['GOOGLE_CREDENTIALS_PATH'])
11
+ client = AspireBudget::Client.new(session: session, spreadsheet_key: ENV['SPREADSHEET_KEY'])
12
+
13
+ binding.pry
14
+
15
+ # params = { date: '25/06/2020', outflow: 10.0, inflow: 12.0, category: 'test', account: 'AIB', memo: 'ruby', status: :pending }
16
+ # client.insert_transaction(params)
17
+
18
+ # client.transactions_list
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'worksheets/backend_data'
4
+ require 'worksheets/transactions'
5
+
6
+ require 'models/transaction'
7
+
8
+ module AspireBudget
9
+ class Client
10
+ def initialize(session:, spreadsheet_key:)
11
+ @session = session
12
+ @spreadsheet_key = spreadsheet_key
13
+ end
14
+
15
+ def categories
16
+ backend_data.categories
17
+ end
18
+
19
+ def transaction_list
20
+ transactions.all
21
+ end
22
+
23
+ def insert_transaction(params)
24
+ transaction = Models::Transaction.new(params)
25
+ transactions.insert(transaction)
26
+ end
27
+
28
+ private
29
+
30
+ def transactions
31
+ @transactions ||= Worksheets::Transactions.new(agent: agent)
32
+ end
33
+
34
+ def backend_data
35
+ @backend_data ||= Worksheets::BackendData.new(agent: agent)
36
+ end
37
+
38
+ def agent
39
+ @agent ||= begin
40
+ @session.spreadsheet_by_key(@spreadsheet_key)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'utils'
4
+
5
+ module AspireBudget
6
+ module Models
7
+ class Transaction
8
+ attr_reader :date, :outflow, :inflow, :category, :account, :memo, :status
9
+
10
+ def self.from_row(header, row)
11
+ params = header.zip(row).to_h
12
+
13
+ params.tap do |h|
14
+ h[:date] = Utils.parse_date(h[:date])
15
+ h[:outflow] = Utils.parse_currency(h[:outflow])
16
+ h[:inflow] = Utils.parse_currency(h[:inflow])
17
+ h[:status] = Utils.parse_status(h[:status])
18
+ end
19
+
20
+ new(**params)
21
+ end
22
+
23
+ def initialize(date:, outflow:, inflow:, category:, account:, memo:, status:)
24
+ @date = Utils.parse_date(date) || Date.today
25
+ @outflow = outflow || 0.0
26
+ @inflow = inflow || 0.0
27
+ @category = category
28
+ @account = account
29
+ @memo = memo
30
+ @status = status
31
+ end
32
+
33
+ def to_row(header)
34
+ header.map do |h|
35
+ value = send(h)
36
+ next Utils.serialize_date(value) if h == :date
37
+ next Utils.serialize_status(value) if h == :status
38
+ next Utils.serialize_currency(value) if %i[inflow outflow].include?(h)
39
+
40
+ value
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AspireBudget
4
+ module Utils
5
+ class << self
6
+ DATE_FORMAT = '%d/%m/%y'
7
+ CURRENCY_SYMBOL = '€'
8
+ TRANSACTION_STATUS_MAPPING = {
9
+ '✅' => :approved,
10
+ '🅿️' => :pending,
11
+ '*️⃣' => :reconciliation
12
+ }.freeze
13
+
14
+ def parse_date(value)
15
+ return value unless value.is_a?(String)
16
+
17
+ Date.strptime(value, DATE_FORMAT)
18
+ end
19
+
20
+ def serialize_date(value)
21
+ return value unless value.respond_to?(:strftime)
22
+
23
+ value.strftime(DATE_FORMAT)
24
+ end
25
+
26
+ def parse_currency(value)
27
+ value && value[/\d+\.\d+/].to_f || 0.0
28
+ end
29
+
30
+ def serialize_currency(value)
31
+ "#{CURRENCY_SYMBOL}#{format('%.2f', value)}"
32
+ end
33
+
34
+ def parse_status(value)
35
+ TRANSACTION_STATUS_MAPPING.fetch(value)
36
+ end
37
+
38
+ def serialize_status(value)
39
+ TRANSACTION_STATUS_MAPPING.key(value)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AspireBudget
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './worksheet_base'
4
+
5
+ module AspireBudget
6
+ module Worksheets
7
+ class BackendData < WorksheetBase
8
+ WS_TITLE = 'BackendData'
9
+
10
+ def categories
11
+ fetch_data(data_title: 'Categories')
12
+ end
13
+
14
+ def category_exists?(category)
15
+ categories.include?(category)
16
+ end
17
+
18
+ def fetch_data(data_title: 'Categories')
19
+ col = (1..ws.num_cols).find_index do |i|
20
+ ws[1, i] == data_title
21
+ end
22
+
23
+ ws.rows.transpose[col].reject(&:empty?) - [data_title]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'worksheets/worksheet_base'
4
+ require 'models/transaction'
5
+
6
+ module AspireBudget
7
+ module Worksheets
8
+ class Transactions < WorksheetBase
9
+ WS_TITLE = 'Transactions'
10
+ MARGIN_LEFT = 1
11
+
12
+ def all
13
+ transaction_rows.map do |row|
14
+ Models::Transaction.from_row(transactions_header, row)
15
+ end
16
+ end
17
+
18
+ def insert(transaction, sync: true)
19
+ row = transaction.to_row(transactions_header)
20
+ ws.update_cells(ws.rows.size + 1, MARGIN_LEFT + 1, [row])
21
+ ws.synchronize if sync
22
+ Models::Transaction.from_row(transactions_header, sanitize(ws.rows.last))
23
+ end
24
+
25
+ private
26
+
27
+ def sanitize(row)
28
+ return if row.all?(&:empty?)
29
+
30
+ row.drop(MARGIN_LEFT)
31
+ end
32
+
33
+ def transaction_rows
34
+ ws.rows(transactions_header_location)
35
+ .map(&method(:sanitize)).compact
36
+ end
37
+
38
+ def transactions_header
39
+ @transactions_header ||=
40
+ ws.rows(transactions_header_location - 1)
41
+ .first
42
+ .drop(MARGIN_LEFT)
43
+ .map(&:downcase)
44
+ .map(&:to_sym)
45
+ end
46
+
47
+ def transactions_header_location
48
+ @transactions_header_location ||=
49
+ ((MARGIN_LEFT + 1)..ws.num_rows).find { |i| ws[i, MARGIN_LEFT + 1] == 'DATE' }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'utils'
4
+
5
+ module AspireBudget
6
+ module Worksheets
7
+ class WorksheetBase
8
+ include Utils
9
+
10
+ def initialize(agent:)
11
+ @agent = agent
12
+ end
13
+
14
+ private
15
+
16
+ def ws
17
+ worksheets[self.class::WS_TITLE]
18
+ end
19
+
20
+ def worksheets
21
+ @worksheets ||=
22
+ @agent.worksheets.reduce({}) { |h, sheet| h.merge(sheet.title => sheet) }
23
+ end
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aspire_budget
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Drowze
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google_drive
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ description: Aspire Budget Ruby Wrapper
28
+ email:
29
+ - gibim6+aspire@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - ".travis.yml"
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - aspire_budget.gemspec
44
+ - bin/console
45
+ - bin/setup
46
+ - lib/client.rb
47
+ - lib/models/transaction.rb
48
+ - lib/utils.rb
49
+ - lib/version.rb
50
+ - lib/worksheets/backend_data.rb
51
+ - lib/worksheets/transactions.rb
52
+ - lib/worksheets/worksheet_base.rb
53
+ homepage: https://google.com
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ homepage_uri: https://google.com
58
+ source_code_uri: https://github.com/drowze/aspirebudgeting_ruby
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.3.0
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.1.2
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Aspire Budget Ruby Wrapper
78
+ test_files: []