cwsaylor (owner)

Revisions

gist: 8508 Download_button fork
public
Description:
Script to generate a csv file of your future income, bills, expenses
Public Clone URL: git://gist.github.com/8508.git
Embed All Files: show embed
bill_generator.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#! /usr/bin/env ruby
 
# Usage: ruby bill_generator.rb data.yml 2008-1-1 2008-12-31
 
# Params: data file, begin date, end date
# begin and end date is in the format yyyy-mm-dd
 
# Sample data.yml file entries
# Date format is [yyyy, mm, dd]
#
# # Paid every two weeks
# Income1:
# amount: 1000
# start: [2008, 1, 18]
# repeat: daily
# freq: 14
#
# # Paid twice a month on the 15th and the last day
# Income2:
# amount: 1000
# start: [ [2008, 1, 15], [2008, 1, -1] ]
# repeat: monthly
# freq: 1
#
# # Weekly food expense
# Food:
# amount: -100
# start: [2008, 1, 6]
# repeat: weekly
# freq: 1
#
# # Quarterly bill
# Water:
# amount: -100
# start: [2008, 1, 20]
# repeat: monthly
# freq: 3
#
# # Monthly bill
# Home:
# amount: -1000
# start: [2008, 1, 1]
# repeat: monthly
# freq: 1
 
# v0.1
 
# TODO: Date parser for better dates in the yaml file
# TODO: Extract bill class out into separate file
# TODO: Use command line params lib
 
require 'yaml'
require 'date'
 
class Bill
 
  def initialize(name, params)
    @name = name
    @amount = params['amount']
    @start_dates = params['start']
    @repeat = params['repeat']
    @freq = params['freq'].to_i
  end
  
  attr_accessor :name, :amount
  
  def due?(current_date)
    due = false
    if @start_dates[0].is_a? Array
      @start_dates.each do |start_date|
        # No need to check others if it matches
        next if due == true
        due = check_for_repeat_type(current_date, start_date)
      end
    else
      due = check_for_repeat_type(current_date, @start_dates)
    end
    due
  end
    
  private
  
  def check_for_repeat_type(current_date, start_date)
    case @repeat
    when "daily"
      compare_daily(current_date, start_date)
    when "weekly"
      compare_daily(current_date, start_date, @freq * 7)
    when "monthly"
      compare_monthly(current_date, start_date)
    when "yearly"
      compare_yearly(current_date, start_date)
    else
      raise ArgumentError, "Invalid repeat option. Must be one of daily, weekly, monthly, or yearly"
    end
  end
  
  def compare_daily(current_date, start_date, freq = @freq)
    # Custom freq allows us to convert weekly to daily
    start_date = convert_date(start_date)
    ((current_date - start_date) % freq) == 0
  end
  
  def compare_monthly(current_date, start_date)
    # Check for end of month comparison
    if start_date[2].to_i == -1
      start_date = convert_date(start_date)
      ((current_date.month - start_date.month) % @freq == 0) && current_date.day == end_of_month_day(current_date)
    else
      start_date = convert_date(start_date)
      ((current_date.month - start_date.month) % @freq == 0) && current_date.day == start_date.day
    end
  end
  
  def compare_yearly(current_date, start_date)
    ((current_date.year - start_date.year) % @freq == 0) && current_date.month == start_date.month && current_date.day == start_date.day
  end
  
  # Accepts format of [year, month, day]
  def convert_date(date)
    Date.new(*date)
  end
  
  def end_of_month_day(current_date)
    Date.new(current_date.year, current_date.month, -1).day
  end
end
 
# Begin script
 
file = ARGV[0]
date_start = Date.parse(ARGV[1].to_s)
date_end = Date.parse(ARGV[2].to_s)
 
bills = YAML.load_file(file)
 
(date_start..date_end).each do |date|
  bills.each do |name, info|
    bill = Bill.new(name, info)
    puts "#{bill.name}, #{date.to_s.strip}, #{bill.amount}" if bill.due? date
  end
end