cartos 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +6 -0
- data/README.md +55 -0
- data/Rakefile +1 -0
- data/bin/cartos +4 -0
- data/cartos.gemspec +32 -0
- data/lib/cartos.rb +36 -0
- data/lib/cartos/cashbase.rb +99 -0
- data/lib/cartos/cli.rb +27 -0
- data/lib/cartos/generators/config.rb +17 -0
- data/lib/cartos/generators/config/config.yml +9 -0
- data/lib/cartos/google/google.rb +6 -0
- data/lib/cartos/google/sheet.rb +89 -0
- data/lib/cartos/google/spreadsheet.rb +24 -0
- data/lib/cartos/import/cashbase.rb +25 -0
- data/lib/cartos/import/file.rb +15 -0
- data/lib/cartos/import/import.rb +2 -0
- data/lib/cartos/spreadsheet/cartos_spreadsheet.rb +66 -0
- data/lib/cartos/spreadsheet/sheets/month.rb +52 -0
- data/lib/cartos/spreadsheet/sheets/sheet.rb +21 -0
- data/lib/cartos/spreadsheet/sheets/summary.rb +57 -0
- data/lib/cartos/spreadsheet/spreadsheet.rb +4 -0
- data/lib/cartos/version.rb +3 -0
- metadata +202 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
License
|
2
|
+
Copyright [2013] [Farruco Sanjurjo Arcay]
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
5
|
+
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Cartos
|
2
|
+
|
3
|
+
This gem allows anyone who's using [cashbase](https://www.cashbasehq.com/) to export all of his data to a Google Spreadsheet. The data on one spreasheet has to belong to one single year. There will be one sheet for each month and one extra sheet summarizing the whole year.
|
4
|
+
|
5
|
+
You can see [here](https://docs.google.com/spreadsheet/ccc?key=0AqwuU2RQe3lzdEt5Rm1qZEp4RnNqOEsyRWJHcUQybnc&usp=sharing) an example of one of this Spreadsheets.
|
6
|
+
|
7
|
+
Homepage : [https://github.com/madtrick/cartos](https://github.com/madtrick/cartos)
|
8
|
+
|
9
|
+
Author : Farruco Sanjurjo ([@madtrick](https://twitter.com/madtrick))
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
$ gem install cartos
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
### Configuration
|
18
|
+
|
19
|
+
Firs of all you must create the config file that ```cartos``` needs. To create it, run:
|
20
|
+
|
21
|
+
$ cartos init
|
22
|
+
|
23
|
+
This command will create an empty config file in ```$HOME/.cartos.yml```. Following is a litle explanation of each config value:
|
24
|
+
|
25
|
+
* ```google_spreadsheet.key```: this is the key of the spreasheet that cartos will use. You can find it in the url of the spreeadsheet
|
26
|
+
|
27
|
+
![Spreadsheet key](https://raw.github.com/madtrick/cartos/screenshoots/spreadsheet_url.png)
|
28
|
+
|
29
|
+
* ```google_spreadsheet.username```: your google drive username.
|
30
|
+
* ```google_spreadsheet.password```: your google drive password.
|
31
|
+
* ```cashbasehq.account_name```: the wallet that you want to export.
|
32
|
+
* ```cashbasehq.username```: your cashbase username.
|
33
|
+
* ```cashbasehq.password```: your cashbase password.
|
34
|
+
|
35
|
+
### Exporting
|
36
|
+
|
37
|
+
For exporting your cashbase data to Google you have two choices:
|
38
|
+
|
39
|
+
1. Download yourself an export from Cashbase and give it as input to cartos
|
40
|
+
|
41
|
+
$ cartos export -f /path/to/the/export.csv -y 2012
|
42
|
+
|
43
|
+
2. Let cartos handle it for you. If you have included your cashbase credentials in the config file, cartos will download itself your cashbase data.
|
44
|
+
|
45
|
+
$ cartos export -y 2012
|
46
|
+
|
47
|
+
Notice that in both cases you have to especify the year.
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
1. Fork it
|
52
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
53
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
54
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
55
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/cartos
ADDED
data/cartos.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cartos/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cartos"
|
8
|
+
spec.version = Cartos::VERSION
|
9
|
+
spec.authors = ["Farruco Sanjurjo"]
|
10
|
+
spec.email = ["farruco.sanjurjo@gmail.com"]
|
11
|
+
spec.description = <<-EOF
|
12
|
+
Cartos is litle gem for loading into Google Spreadsheet data exported from Cashbase (https://www.cashbasehq.com/) for further analysis
|
13
|
+
EOF
|
14
|
+
spec.summary = %q{Load Cashbase exports into Google Spreadsheet}
|
15
|
+
spec.homepage = ""
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency "httparty", "~> 0.11.0"
|
24
|
+
spec.add_dependency "nokogiri", "1.5.0"
|
25
|
+
spec.add_dependency "google_drive", "~> 0.3.6"
|
26
|
+
spec.add_dependency "sugarfree-config", "~> 2.0.0"
|
27
|
+
spec.add_dependency "logging", "~> 1.8.1"
|
28
|
+
spec.add_dependency "thor", "~> 0.18.1"
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
31
|
+
spec.add_development_dependency "rake"
|
32
|
+
end
|
data/lib/cartos.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "sugarfree-config"
|
2
|
+
require "logging"
|
3
|
+
require "cartos/version"
|
4
|
+
|
5
|
+
include Logging.globally
|
6
|
+
Logging.logger.root.appenders = Logging.appenders.stdout
|
7
|
+
Logging.logger.root.level = :info
|
8
|
+
|
9
|
+
module Cartos
|
10
|
+
autoload :Cashbase , "cartos/cashbase"
|
11
|
+
autoload :Import , "cartos/import/import"
|
12
|
+
autoload :Google , "cartos/google/google"
|
13
|
+
autoload :Spreadsheet , "cartos/spreadsheet/spreadsheet"
|
14
|
+
|
15
|
+
def self.config=(config)
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
@config
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_from_cashbase
|
24
|
+
Cartos::Cashbase.importer = Cartos::Import::Cashbase.new
|
25
|
+
Cartos::Cashbase.load
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.load_from_file(filename)
|
29
|
+
importer = Cartos::Import::File.new filename
|
30
|
+
Cartos::Cashbase.importer = importer
|
31
|
+
Cartos::Cashbase.load
|
32
|
+
end
|
33
|
+
|
34
|
+
self.config = SugarfreeConfig.init env: 'config', file: File.expand_path("~/.cartos.yml"), reload: false
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "csv"
|
2
|
+
module Cartos
|
3
|
+
module Cashbase
|
4
|
+
def self.importer=(importer)
|
5
|
+
@importer = importer
|
6
|
+
end
|
7
|
+
|
8
|
+
#def self.load(data)
|
9
|
+
def self.load
|
10
|
+
data_array = CSV.parse @importer.import
|
11
|
+
data_array.shift #Remove header row
|
12
|
+
entries = data_array.inject([]) do |acc, row|
|
13
|
+
if row[4].force_encoding('UTF-8') == Cartos.config.cashbasehq.account_name
|
14
|
+
entry = Cartos::Cashbase::Entry.new
|
15
|
+
date_string = row[0].split("-")
|
16
|
+
entry.date = Time.new date_string[0], date_string[1], date_string[2]
|
17
|
+
entry.amount = row[1].to_f
|
18
|
+
entry.category = row[2]
|
19
|
+
entry.description = row[3]
|
20
|
+
acc << entry
|
21
|
+
else
|
22
|
+
acc
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
collection = Collection.new
|
27
|
+
collection.elements = entries
|
28
|
+
collection
|
29
|
+
end
|
30
|
+
class Collection
|
31
|
+
attr_accessor :elements
|
32
|
+
|
33
|
+
def categories
|
34
|
+
result = self.elements.inject([]) do |memo, element|
|
35
|
+
memo << element.category
|
36
|
+
end
|
37
|
+
|
38
|
+
result.uniq
|
39
|
+
end
|
40
|
+
|
41
|
+
def size
|
42
|
+
self.elements.size
|
43
|
+
end
|
44
|
+
|
45
|
+
def filter_by_year(year)
|
46
|
+
self.elements.select! do |element|
|
47
|
+
element.date.year == year
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def each_month(&block)
|
53
|
+
months_hash = self.elements.inject({}) do |memo, element|
|
54
|
+
(memo[element.date.month] ||= []) << element
|
55
|
+
memo
|
56
|
+
end
|
57
|
+
|
58
|
+
months_hash.each_pair do |month, elements|
|
59
|
+
collection = self.class.new
|
60
|
+
collection.elements = elements
|
61
|
+
yield month, collection
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def each_category(&block)
|
66
|
+
categories_hash = self.elements.inject({}) do |memo, element|
|
67
|
+
(memo[element.category] ||= []) << element
|
68
|
+
memo
|
69
|
+
end
|
70
|
+
|
71
|
+
categories_hash.each_pair do |category, elements|
|
72
|
+
collection = self.class.new
|
73
|
+
collection.elements = elements
|
74
|
+
yield category, elements
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def filter_by_month(month)
|
79
|
+
self.elements.select! do |element|
|
80
|
+
element.date.month == month
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def each(&block)
|
86
|
+
self.elements.each &block if block_given?
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_a
|
90
|
+
self.elements
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
class Entry
|
96
|
+
attr_accessor :date, :amount, :description, :category
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/cartos/cli.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "cartos"
|
2
|
+
require "cartos/generators/config"
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
module Cartos
|
6
|
+
class CLI < Thor
|
7
|
+
desc "init", "Creates an empty $HOME/.cartos.yml config file"
|
8
|
+
def init
|
9
|
+
Cartos::Generators::Config.start
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "export", "Export data to Google Spreadsheet"
|
13
|
+
option :file, aliases: "-f", desc: "Path to file which contains a Cashbase export"
|
14
|
+
option :year, aliases: "-y", type: :numeric, required: true, desc: "Year used to filter the entries that will be exported to Google"
|
15
|
+
def export
|
16
|
+
entries = if (options[:file])
|
17
|
+
Cartos.load_from_file File.absolute_path(options[:file])
|
18
|
+
else
|
19
|
+
Cartos.load_from_cashbase
|
20
|
+
end
|
21
|
+
entries.filter_by_year options[:year]
|
22
|
+
spread_sheet = Cartos::Spreadsheet::CartosSpreadsheet.new Cartos.config.google_spreadsheet.key
|
23
|
+
spread_sheet.monthly entries
|
24
|
+
spread_sheet.summary entries
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "thor/group"
|
2
|
+
|
3
|
+
module Cartos
|
4
|
+
module Generators
|
5
|
+
class Config < Thor::Group
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
File.dirname(__FILE__) + "/config"
|
10
|
+
end
|
11
|
+
|
12
|
+
def copy_config
|
13
|
+
template("config.yml", "#{File.expand_path "~"}/.cartos.yml")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Cartos
|
2
|
+
module Google
|
3
|
+
class Sheet
|
4
|
+
|
5
|
+
class Formulas
|
6
|
+
def initialize
|
7
|
+
@buffer = []
|
8
|
+
end
|
9
|
+
def sum(*numbers)
|
10
|
+
@buffer.unshift ["SUM", numbers]
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def filter(sourceArray, *conditions)
|
15
|
+
@buffer.unshift ["FILTER", [sourceArray, conditions]]
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def abs(number = nil)
|
20
|
+
@buffer.unshift ["ABS", number]
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def unique(array)
|
25
|
+
@buffer.unshift ["UNIQUE", array]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
result = @buffer.inject("") do |memo, element|
|
30
|
+
name, *params = *element
|
31
|
+
params = params.flatten.compact
|
32
|
+
if params.empty?
|
33
|
+
memo = "#{name}(#{memo})"
|
34
|
+
else
|
35
|
+
memo = "#{name}(#{params.join(",")})"
|
36
|
+
end
|
37
|
+
memo
|
38
|
+
end
|
39
|
+
"=" + result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ALPHABET = "A".upto("Z").to_a
|
44
|
+
|
45
|
+
def initialize(sheet)
|
46
|
+
@sheet = sheet
|
47
|
+
@last_rows = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def push_row(column, value)
|
51
|
+
column_number = ensure_column_as_number column
|
52
|
+
result = set_row (self.last_row column), column_number, value
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_row(row, column, value)
|
57
|
+
@sheet[row, column] = value
|
58
|
+
@last_rows[column] = row + 1
|
59
|
+
[row, column]
|
60
|
+
end
|
61
|
+
|
62
|
+
def last_row(column)
|
63
|
+
@last_rows[ensure_column_as_number column] ||= 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def save
|
67
|
+
@sheet.save
|
68
|
+
end
|
69
|
+
|
70
|
+
def column_letter_to_integer(column)
|
71
|
+
ALPHABET.index(column) + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
def row_range(column, first, last)
|
75
|
+
"#{column}#{first}:#{column}#{last}"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def ensure_column_as_number(column)
|
80
|
+
case column
|
81
|
+
when String
|
82
|
+
column_letter_to_integer column
|
83
|
+
when Fixnum
|
84
|
+
column
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "google_drive"
|
2
|
+
|
3
|
+
module Cartos
|
4
|
+
module Google
|
5
|
+
class Spreadsheet
|
6
|
+
def initialize(key)
|
7
|
+
@session = GoogleDrive.login Cartos.config.google_spreadsheet.username, Cartos.config.google_spreadsheet.password
|
8
|
+
@spreadsheet = @session.spreadsheet_by_key key
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_sheet(name)
|
12
|
+
worksheet = @spreadsheet.worksheet_by_title name.to_s
|
13
|
+
worksheet.delete unless worksheet.nil?
|
14
|
+
|
15
|
+
worksheet = @spreadsheet.add_worksheet name
|
16
|
+
Cartos::Google::Sheet.new worksheet
|
17
|
+
end
|
18
|
+
|
19
|
+
def url
|
20
|
+
@spreadsheet.human_url
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "httparty"
|
2
|
+
module Cartos
|
3
|
+
module Import
|
4
|
+
class Cashbase
|
5
|
+
include HTTParty
|
6
|
+
base_uri "https://www.cashbasehq.com"
|
7
|
+
|
8
|
+
def import
|
9
|
+
signin
|
10
|
+
response = self.class.get "/export_all/csv"
|
11
|
+
response.body
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def signin
|
16
|
+
resp = self.class.get ""
|
17
|
+
cookies = resp.headers["set-cookie"]
|
18
|
+
match = cookies.match /PHPSESSID=([\w\d]+);/
|
19
|
+
sessid = match[1]
|
20
|
+
self.class.cookies :PHPSESSID => sessid
|
21
|
+
self.class.post "/signin", {:body => {:email => Cartos.config.cashbasehq.username, :password => Cartos.config.cashbasehq.password}}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Cartos
|
2
|
+
module Spreadsheet
|
3
|
+
class CartosSpreadsheet
|
4
|
+
def initialize(spreeadsheet_key)
|
5
|
+
@spreadsheet = Cartos::Google::Spreadsheet.new spreeadsheet_key
|
6
|
+
logger.info "Exporting data to #{@spreadsheet.url}"
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def monthly(entries)
|
11
|
+
entries.each_month do |month, month_entries|
|
12
|
+
new_month month, month_entries
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def summary(entries)
|
17
|
+
summary_sheet = Cartos::Spreadsheet::Summary.new @spreadsheet.new_sheet "summary"
|
18
|
+
categories = {}
|
19
|
+
totals = {}
|
20
|
+
totals["expendings"] = {}
|
21
|
+
totals["earnings"] = {}
|
22
|
+
totals["total"] = {}
|
23
|
+
entries.each_month do |month, entries|
|
24
|
+
totals["expendings"][month] = 0
|
25
|
+
totals["earnings"][month] = 0
|
26
|
+
entries.each do |entry|
|
27
|
+
entry.amount < 0 ? totals["expendings"][month] += entry.amount : totals["earnings"][month] += entry.amount
|
28
|
+
end
|
29
|
+
totals["total"][month] = totals["earnings"][month] + totals["expendings"][month]
|
30
|
+
totals["expendings"][month] = totals["expendings"][month]
|
31
|
+
entries.each_category do |category, elements|
|
32
|
+
categories[category] ||= {}
|
33
|
+
categories[category][month] = elements.inject(0) {|total, element| total += element.amount}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
summary_sheet.push_categories categories
|
37
|
+
summary_sheet.push_totals totals
|
38
|
+
summary_sheet.save
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def new_month(month, entries)
|
43
|
+
month_sheet = Cartos::Spreadsheet::Month.new @spreadsheet.new_sheet month.to_s
|
44
|
+
entries.each do |entry|
|
45
|
+
month_sheet.push_entry entry.date.strftime("%F"), entry.amount, entry.description, entry.category
|
46
|
+
end
|
47
|
+
|
48
|
+
entries.categories.each do |category|
|
49
|
+
month_sheet.push_category category
|
50
|
+
end
|
51
|
+
|
52
|
+
earnings_total, expendings_total = 0, 0
|
53
|
+
entries.each do |entry|
|
54
|
+
if entry.amount > 0
|
55
|
+
earnings_total += entry.amount
|
56
|
+
else
|
57
|
+
expendings_total += entry.amount
|
58
|
+
end
|
59
|
+
end
|
60
|
+
month_sheet.push_summary expendings_total, earnings_total
|
61
|
+
|
62
|
+
month_sheet.save
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Cartos
|
2
|
+
module Spreadsheet
|
3
|
+
class Month
|
4
|
+
DATE = "A"
|
5
|
+
AMOUNT = "B"
|
6
|
+
DESCRIPTION = "C"
|
7
|
+
CATEGORY = "D"
|
8
|
+
|
9
|
+
CATEGORIES = "F"
|
10
|
+
EXPENDINGS_BY_CATEGORY = "G"
|
11
|
+
|
12
|
+
EARNINGS_TOTAL = "F"
|
13
|
+
EXPENDINGS_TOTAL = "G"
|
14
|
+
|
15
|
+
def initialize(sheet)
|
16
|
+
@sheet = sheet
|
17
|
+
end
|
18
|
+
|
19
|
+
def push_entry(date, amount, description, category)
|
20
|
+
push_row DATE, date
|
21
|
+
push_row AMOUNT, amount
|
22
|
+
push_row DESCRIPTION, description
|
23
|
+
push_row CATEGORY, category
|
24
|
+
end
|
25
|
+
|
26
|
+
def push_category(category)
|
27
|
+
push_row CATEGORIES, category
|
28
|
+
formula = Cartos::Google::Sheet::Formulas.new
|
29
|
+
entry_range = @sheet.row_range(AMOUNT, 1, @sheet.last_row(AMOUNT))
|
30
|
+
category_range = @sheet.row_range(CATEGORY, 1, @sheet.last_row(CATEGORY))
|
31
|
+
push_row EXPENDINGS_BY_CATEGORY, formula.sum.filter(entry_range, "#{category_range} = \"#{category}\"")
|
32
|
+
end
|
33
|
+
|
34
|
+
def push_summary(expendings_total, earnings_total)
|
35
|
+
push_row EARNINGS_TOTAL, "Earnings"
|
36
|
+
push_row EARNINGS_TOTAL, earnings_total
|
37
|
+
push_row EXPENDINGS_TOTAL, "Expendings"
|
38
|
+
push_row EXPENDINGS_TOTAL, expendings_total
|
39
|
+
end
|
40
|
+
|
41
|
+
def push_row(column, value)
|
42
|
+
@sheet.push_row column, value
|
43
|
+
end
|
44
|
+
|
45
|
+
def save
|
46
|
+
@sheet.save
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Cartos
|
2
|
+
module Spreadsheet
|
3
|
+
class Sheet
|
4
|
+
def initialize(sheet)
|
5
|
+
@sheet = sheet
|
6
|
+
end
|
7
|
+
|
8
|
+
def push_row(column, value)
|
9
|
+
@sheet.push_row column, value
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_row(column, row, value)
|
13
|
+
@sheet.set_row column, row, value
|
14
|
+
end
|
15
|
+
|
16
|
+
def save
|
17
|
+
@sheet.save
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Cartos
|
2
|
+
module Spreadsheet
|
3
|
+
class Summary < Sheet
|
4
|
+
TOTALS = "A"
|
5
|
+
CATEGORIES = "A"
|
6
|
+
|
7
|
+
def initialize(sheet)
|
8
|
+
super sheet
|
9
|
+
@last_col_used = 1
|
10
|
+
@month_cols = {}
|
11
|
+
@category_row = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def push_totals(totals)
|
15
|
+
row, _ = push_row TOTALS, "Totals"
|
16
|
+
push_summnary row, TOTALS, totals
|
17
|
+
end
|
18
|
+
def push_categories(categories)
|
19
|
+
row, _ = push_row CATEGORIES, "Categories"
|
20
|
+
push_summnary row, CATEGORIES, categories
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def push_summnary(start_row, type_column, elements)
|
28
|
+
push_months start_row, elements.values.map {|v| v.keys}.flatten.uniq
|
29
|
+
elements.each do |element, months|
|
30
|
+
row, column = push_row type_column, element
|
31
|
+
months.each do |month, amount|
|
32
|
+
push_month_amount month, row, amount
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def push_month_amount(month, row, amount)
|
38
|
+
set_row row, col_for_month(month), amount
|
39
|
+
end
|
40
|
+
|
41
|
+
def push_months(row,months)
|
42
|
+
months.each do |month|
|
43
|
+
set_row row, col_for_month(month), month
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def col_for_month(month)
|
48
|
+
if @month_cols[month].nil?
|
49
|
+
@month_cols[month] = @last_col_used + 1
|
50
|
+
@last_col_used += 1
|
51
|
+
end
|
52
|
+
|
53
|
+
@month_cols[month]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cartos
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Farruco Sanjurjo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.11.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.11.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.5.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.5.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: google_drive
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.3.6
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.6
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sugarfree-config
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: logging
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.8.1
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.8.1
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: thor
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.18.1
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.18.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: bundler
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.3'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rake
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: ! ' Cartos is litle gem for loading into Google Spreadsheet data exported
|
143
|
+
from Cashbase (https://www.cashbasehq.com/) for further analysis
|
144
|
+
|
145
|
+
'
|
146
|
+
email:
|
147
|
+
- farruco.sanjurjo@gmail.com
|
148
|
+
executables:
|
149
|
+
- cartos
|
150
|
+
extensions: []
|
151
|
+
extra_rdoc_files: []
|
152
|
+
files:
|
153
|
+
- .gitignore
|
154
|
+
- Gemfile
|
155
|
+
- LICENSE.txt
|
156
|
+
- README.md
|
157
|
+
- Rakefile
|
158
|
+
- bin/cartos
|
159
|
+
- cartos.gemspec
|
160
|
+
- lib/cartos.rb
|
161
|
+
- lib/cartos/cashbase.rb
|
162
|
+
- lib/cartos/cli.rb
|
163
|
+
- lib/cartos/generators/config.rb
|
164
|
+
- lib/cartos/generators/config/config.yml
|
165
|
+
- lib/cartos/google/google.rb
|
166
|
+
- lib/cartos/google/sheet.rb
|
167
|
+
- lib/cartos/google/spreadsheet.rb
|
168
|
+
- lib/cartos/import/cashbase.rb
|
169
|
+
- lib/cartos/import/file.rb
|
170
|
+
- lib/cartos/import/import.rb
|
171
|
+
- lib/cartos/spreadsheet/cartos_spreadsheet.rb
|
172
|
+
- lib/cartos/spreadsheet/sheets/month.rb
|
173
|
+
- lib/cartos/spreadsheet/sheets/sheet.rb
|
174
|
+
- lib/cartos/spreadsheet/sheets/summary.rb
|
175
|
+
- lib/cartos/spreadsheet/spreadsheet.rb
|
176
|
+
- lib/cartos/version.rb
|
177
|
+
homepage: ''
|
178
|
+
licenses:
|
179
|
+
- MIT
|
180
|
+
post_install_message:
|
181
|
+
rdoc_options: []
|
182
|
+
require_paths:
|
183
|
+
- lib
|
184
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
none: false
|
192
|
+
requirements:
|
193
|
+
- - ! '>='
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubyforge_project:
|
198
|
+
rubygems_version: 1.8.24
|
199
|
+
signing_key:
|
200
|
+
specification_version: 3
|
201
|
+
summary: Load Cashbase exports into Google Spreadsheet
|
202
|
+
test_files: []
|