Skip to content

Instantly share code, notes, and snippets.

@cwsaylor
Created September 3, 2008 00:42
Show Gist options
  • Save cwsaylor/8508 to your computer and use it in GitHub Desktop.
Save cwsaylor/8508 to your computer and use it in GitHub Desktop.
Script to generate a csv file of your future income, bills, expenses
#! /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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment