watcard 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +94 -0
- data/Rakefile +2 -0
- data/bin/watcard +32 -0
- data/lib/watcard.rb +5 -0
- data/lib/watcard/history.rb +138 -0
- data/lib/watcard/version.rb +3 -0
- data/watcard.gemspec +26 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9661609eb1149caaeed55ce608bbac94c11c73fb
|
4
|
+
data.tar.gz: 1d5f0f4a35af2a4ed0a5ff82d41094d2f9a4d4a8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 832d8d7441444973fbe74fdf784b6810ed6aafd44db08e50b7dc10549f65caa678c03de4b34ecd36a1a291ca08b4f0361874c9e4f3dec4a42793c7b36ff7a06a
|
7
|
+
data.tar.gz: 71c216a8a29ddfae5686377b55f30860f0c4c28ff537ec38a7fcf918315e6367e794b7ed9e5296844fa3f32cc0934319f5522f33e06f81f368c3fa7513015249
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tristan Hume
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Watcard
|
2
|
+
|
3
|
+
A command line tool for accessing your watcard history and balance.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem with:
|
8
|
+
|
9
|
+
$ gem install watcard
|
10
|
+
|
11
|
+
Create the config file `~/.watcard.yml`:
|
12
|
+
|
13
|
+
```yaml
|
14
|
+
---
|
15
|
+
# Student id and watcard pin
|
16
|
+
id: 12345678
|
17
|
+
pin: 1234
|
18
|
+
# Daily food budget to use for reference (in dollars)
|
19
|
+
budget: 19
|
20
|
+
# Ledger output file
|
21
|
+
ledger: ~/Box/Life/watcard.ldg
|
22
|
+
# Accounts to use in Ledger output for different watcard balances
|
23
|
+
accounts:
|
24
|
+
# Meal Plan are account 1
|
25
|
+
1: ["Expenses:Food:Meals","Assets:WatCard:Meal Plan"]
|
26
|
+
# Flex Dollars are account 4
|
27
|
+
4: ["Expenses:Misc University","Assets:WatCard:Flex"]
|
28
|
+
```
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
### History
|
33
|
+
|
34
|
+
Fetches history for a day and compares it with the budget (if there is one).
|
35
|
+
|
36
|
+
$ watcard hist
|
37
|
+
# Fetching history for 2014-09-05 19:53:54 -0400
|
38
|
+
Breakfast: $3.14 @ V1 Cafeteria
|
39
|
+
Dinner: $5.78 @ V1 Cafeteria
|
40
|
+
= $8.92 out of $19 surplus: 10.08
|
41
|
+
$ watcard hist 1 # One day ago
|
42
|
+
# Fetching history for 2014-09-04 19:53:01 -0400
|
43
|
+
Breakfast: $6.24 @ V1 Cafeteria
|
44
|
+
Lunch: $7.70 @ Liquid Assets
|
45
|
+
Dinner: $6.28 @ V1 Cafeteria
|
46
|
+
= $20.22 out of $19 surplus: -1.22
|
47
|
+
|
48
|
+
### Ledger
|
49
|
+
|
50
|
+
Outputs double entry transactions in [ledger](http://ledger-cli.org/) format and optionally appends them to a file.
|
51
|
+
|
52
|
+
$ watcard ledger 1
|
53
|
+
# Fetching history for 2014-09-04 20:06:31 -0400
|
54
|
+
# Transactions:
|
55
|
+
|
56
|
+
2014/09/04 Breakfast at V1 Cafeteria
|
57
|
+
Expenses:Food:Meals $6.24
|
58
|
+
Assets:WatCard:Meal Plan
|
59
|
+
|
60
|
+
2014/09/04 Lunch at Liquid Assets
|
61
|
+
Expenses:Food:Meals $7.70
|
62
|
+
Assets:WatCard:Meal Plan
|
63
|
+
|
64
|
+
2014/09/04 Dinner at V1 Cafeteria
|
65
|
+
Expenses:Food:Meals $6.28
|
66
|
+
Assets:WatCard:Meal Plan
|
67
|
+
# Add to file [yN]: n
|
68
|
+
|
69
|
+
You can use this with `ledger`'s budgeting and accounting features to do lots of cool things like determine your current watcard balance and see budgeting stuff.
|
70
|
+
|
71
|
+
$ ledger -f watcard.ldg --period 'this week' --budget register expenses
|
72
|
+
14-Sep-01 Budget transaction Expenses:Food:Meals $-19.00 $-19.00
|
73
|
+
14-Sep-01 Lunch at V1 Cafeteria Expenses:Food:Meals $7.74 $-11.26
|
74
|
+
14-Sep-02 Budget transaction Expenses:Food:Meals $-19.00 $-30.26
|
75
|
+
14-Sep-02 Breakfast at V1 Caf.. Expenses:Food:Meals $1.34 $-28.92
|
76
|
+
14-Sep-03 Budget transaction Expenses:Food:Meals $-19.00 $-47.92
|
77
|
+
14-Sep-03 Breakfast at V1 Caf.. Expenses:Food:Meals $5.40 $-42.52
|
78
|
+
14-Sep-04 Budget transaction Expenses:Food:Meals $-19.00 $-61.52
|
79
|
+
14-Sep-04 Breakfast at V1 Caf.. Expenses:Food:Meals $6.24 $-55.28
|
80
|
+
14-Sep-04 Lunch at Liquid Ass.. Expenses:Food:Meals $7.70 $-47.58
|
81
|
+
14-Sep-04 Dinner at V1 Cafete.. Expenses:Food:Meals $6.28 $-41.30
|
82
|
+
14-Sep-05 Budget transaction Expenses:Food:Meals $-19.00 $-60.30
|
83
|
+
14-Sep-05 Breakfast at V1 Caf.. Expenses:Food:Meals $3.14 $-57.16
|
84
|
+
14-Sep-05 Dinner at V1 Cafete.. Expenses:Food:Meals $5.78 $-51.38
|
85
|
+
$ ledger -f watcard.ldg balance
|
86
|
+
$2197.88 Assets:WatCard
|
87
|
+
$191.50 Flex
|
88
|
+
$2006.38 Meal Plan
|
89
|
+
$-2250.00 Equity:RESP
|
90
|
+
$52.12 Expenses
|
91
|
+
$43.62 Food:Meals
|
92
|
+
$8.50 Misc University
|
93
|
+
--------------------
|
94
|
+
0
|
data/Rakefile
ADDED
data/bin/watcard
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "watcard"
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
FILE = File.expand_path("~/.watcard.yml")
|
7
|
+
|
8
|
+
begin
|
9
|
+
yaml_dat = IO.read(FILE)
|
10
|
+
conf = YAML.load(yaml_dat)
|
11
|
+
rescue Exception => e
|
12
|
+
STDERR.puts "Can't load config file"
|
13
|
+
p e
|
14
|
+
STDERR.puts e.message
|
15
|
+
end
|
16
|
+
|
17
|
+
command = ARGV.shift
|
18
|
+
case command
|
19
|
+
when "hist"
|
20
|
+
hist = Watcard::History.new(conf)
|
21
|
+
days_ago = (ARGV.shift || 0).to_i
|
22
|
+
hist.output_history(days_ago)
|
23
|
+
when "ledger"
|
24
|
+
hist = Watcard::History.new(conf)
|
25
|
+
days_ago = (ARGV.shift || 0).to_i
|
26
|
+
hist.output_ledger(days_ago)
|
27
|
+
else
|
28
|
+
puts "Watcard CLI v#{Watcard::VERSION}"
|
29
|
+
puts "By Tristan Hume"
|
30
|
+
puts ""
|
31
|
+
puts "Unknown command"
|
32
|
+
end
|
data/lib/watcard.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "net/http"
|
3
|
+
require "uri"
|
4
|
+
require "time"
|
5
|
+
require "facets"
|
6
|
+
|
7
|
+
module Watcard
|
8
|
+
class History
|
9
|
+
def initialize(config)
|
10
|
+
@conf = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def log(msg)
|
14
|
+
STDERR.puts msg
|
15
|
+
end
|
16
|
+
|
17
|
+
def history_page(date)
|
18
|
+
uri = URI.parse("https://account.watcard.uwaterloo.ca/watgopher661.asp")
|
19
|
+
date_txt = date.strftime("%m/%d/%Y")
|
20
|
+
args = {
|
21
|
+
"acnt_1"=>@conf['id'],
|
22
|
+
"acnt_2"=>@conf['pin'],
|
23
|
+
"DBDATE"=>date_txt,
|
24
|
+
"DEDATE"=>date_txt,
|
25
|
+
"PASS"=>"PASS",
|
26
|
+
"STATUS"=>"HIST",
|
27
|
+
"watgopher_title"=>"WatCard History Report",
|
28
|
+
"watgopher_regex"=>'<hr>([\s\S]*wrong[\s\S]*)<p></p>|(<form[\s\S]*?(</center>|</form>))|(<pre><p>[\s\S]*</pre>)',
|
29
|
+
"watgopher_style"=>'onecard_narrow',
|
30
|
+
}
|
31
|
+
log "# Fetching history for #{date}"
|
32
|
+
Net::HTTP.post_form(uri, args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_loc(loc)
|
36
|
+
return "V1 Cafeteria" if loc =~ /WAT-FS-V1/
|
37
|
+
return "Liquid Assets" if loc =~ /WAT-FS-LA/
|
38
|
+
"Campus"
|
39
|
+
end
|
40
|
+
|
41
|
+
def history(date)
|
42
|
+
page_body = history_page(date).body
|
43
|
+
doc = Nokogiri::HTML(page_body)
|
44
|
+
table = doc.css('#oneweb_financial_history_table')
|
45
|
+
table.xpath('.//tr').map do |row|
|
46
|
+
cols = row.xpath('./td').map(&:inner_text)
|
47
|
+
next if cols.length < 4
|
48
|
+
mult = (cols[3] == "1") ? 2 : 1
|
49
|
+
{
|
50
|
+
time: Time.parse(cols[1], date),
|
51
|
+
amount: -(cols[2].strip.to_f)*mult,
|
52
|
+
loc: parse_loc(cols.last),
|
53
|
+
balance: cols[3].to_i
|
54
|
+
}
|
55
|
+
end.compact.reverse
|
56
|
+
end
|
57
|
+
|
58
|
+
def bundle_transactions(hist)
|
59
|
+
return hist if hist.empty?
|
60
|
+
hist.each do |a|
|
61
|
+
h = a[:time].hour
|
62
|
+
type = if h < 12
|
63
|
+
"Breakfast"
|
64
|
+
elsif h < 17
|
65
|
+
"Lunch"
|
66
|
+
else
|
67
|
+
"Dinner"
|
68
|
+
end
|
69
|
+
a[:meal] = type
|
70
|
+
end
|
71
|
+
meals = [hist.shift]
|
72
|
+
hist.each do |a|
|
73
|
+
# if last meal is same type treat them as one
|
74
|
+
if a[:meal] == meals.last[:meal]
|
75
|
+
meals.last[:amount] += a[:amount]
|
76
|
+
else
|
77
|
+
meals << a
|
78
|
+
end
|
79
|
+
end
|
80
|
+
meals
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_accounts(hist)
|
84
|
+
accounts = @conf['accounts']
|
85
|
+
hist.each do |a|
|
86
|
+
a[:account] = accounts[a[:balance]] || accounts[4]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def fetch_meals(days_ago)
|
91
|
+
hist = history(Time.now.less(days_ago, :days))
|
92
|
+
if hist.empty?
|
93
|
+
log "No Transactions"
|
94
|
+
exit
|
95
|
+
end
|
96
|
+
bundle_transactions(hist)
|
97
|
+
end
|
98
|
+
|
99
|
+
def ledger_transaction(m)
|
100
|
+
date_str = m[:time].strftime("%Y/%m/%d")
|
101
|
+
transact = <<END
|
102
|
+
|
103
|
+
#{date_str} #{m[:meal]} at #{m[:loc]}
|
104
|
+
#{m[:account][0]} $#{sprintf('%.2f', m[:amount])}
|
105
|
+
#{m[:account][1]}
|
106
|
+
END
|
107
|
+
end
|
108
|
+
|
109
|
+
def output_ledger(days_ago)
|
110
|
+
meals = fetch_meals(days_ago)
|
111
|
+
add_accounts(meals)
|
112
|
+
log "# Transactions:"
|
113
|
+
out = meals.map {|m| ledger_transaction(m)}.join('')
|
114
|
+
puts out
|
115
|
+
if STDIN.tty? && @conf['ledger']
|
116
|
+
print "# Add to file [yN]: "
|
117
|
+
ans = gets.chomp
|
118
|
+
exit if ans != "y"
|
119
|
+
file = File.expand_path(@conf['ledger'])
|
120
|
+
File.open(file, 'a') {|f| f.puts out}
|
121
|
+
puts "# Added to #{file}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def output_history(days_ago)
|
126
|
+
meals = fetch_meals(days_ago)
|
127
|
+
total = 0
|
128
|
+
meals.each do |m|
|
129
|
+
total += m[:amount]
|
130
|
+
puts "#{m[:meal]}: $#{sprintf('%.2f', m[:amount])} @ #{m[:loc]}"
|
131
|
+
end
|
132
|
+
budget = @conf['budget']
|
133
|
+
print "= $#{total}"
|
134
|
+
print " out of $#{budget} surplus: #{sprintf('%.2f',budget-total)}" if budget
|
135
|
+
puts ''
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/watcard.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'watcard/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "watcard"
|
8
|
+
spec.version = Watcard::VERSION
|
9
|
+
spec.authors = ["Tristan Hume"]
|
10
|
+
spec.email = ["tris.hume@gmail.com"]
|
11
|
+
spec.summary = %q{Command line interface for University of Waterloo WatCard}
|
12
|
+
spec.description = %q{Command line interface for University of Waterloo WatCard. Includes commands to see purchase history and output ledger.}
|
13
|
+
spec.homepage = "https://github.com/trishume/watcard-cli"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "nokogiri"
|
22
|
+
spec.add_dependency "facets"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: watcard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tristan Hume
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: facets
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Command line interface for University of Waterloo WatCard. Includes commands
|
70
|
+
to see purchase history and output ledger.
|
71
|
+
email:
|
72
|
+
- tris.hume@gmail.com
|
73
|
+
executables:
|
74
|
+
- watcard
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/watcard
|
84
|
+
- lib/watcard.rb
|
85
|
+
- lib/watcard/history.rb
|
86
|
+
- lib/watcard/version.rb
|
87
|
+
- watcard.gemspec
|
88
|
+
homepage: https://github.com/trishume/watcard-cli
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Command line interface for University of Waterloo WatCard
|
112
|
+
test_files: []
|