Print a forecast of upcoming events from iCal files
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

initial version

joshua stein 52a572a0

+184
+24
LICENSE
··· 1 + Copyright (c) 2010, 2014, 2017 joshua stein <jcs@jcs.org> 2 + 3 + Redistribution and use in source and binary forms, with or without 4 + modification, are permitted provided that the following conditions 5 + are met: 6 + 7 + 1. Redistributions of source code must retain the above copyright 8 + notice, this list of conditions and the following disclaimer. 9 + 2. Redistributions in binary form must reproduce the above copyright 10 + notice, this list of conditions and the following disclaimer in the 11 + documentation and/or other materials provided with the distribution. 12 + 3. The name of the author may not be used to endorse or promote products 13 + derived from this software without specific prior written permission. 14 + 15 + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 16 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+19
README.md
··· 1 + ## `ical_forecast` 2 + 3 + Print a forecast of upcoming events from a list of iCal files. 4 + 5 + ### Installation 6 + 7 + `gem install ri_cal tzinfo` 8 + 9 + ### Usage 10 + 11 + jcs@humble:~> ruby ical_forecast.rb ~/.cals/*.cal 12 + some far out event in 3 weeks, 6 days (wed 1 sep) 13 + something here in 1 day (fri 6 aug to sun 8 aug) 14 + some timed event today at 08:45 (thu 5 aug) 15 + 16 + ### Related 17 + 18 + [remind_forecast](https://github.com/jcs/remind_forecast) which 19 + does the same thing from `remind` or `iCalBuddy`.
+141
ical_forecast.rb
··· 1 + #!/usr/bin/env ruby 2 + # 3 + # ical_forecast.rb 4 + # print upcoming events from iCal files 5 + # 6 + # Copyright (c) 2010, 2014, 2017 joshua stein <jcs@jcs.org> 7 + # 8 + # Redistribution and use in source and binary forms, with or without 9 + # modification, are permitted provided that the following conditions 10 + # are met: 11 + # 12 + # 1. Redistributions of source code must retain the above copyright 13 + # notice, this list of conditions and the following disclaimer. 14 + # 2. Redistributions in binary form must reproduce the above copyright 15 + # notice, this list of conditions and the following disclaimer in the 16 + # documentation and/or other materials provided with the distribution. 17 + # 3. The name of the author may not be used to endorse or promote products 18 + # derived from this software without specific prior written permission. 19 + # 20 + # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 + # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 + # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 + # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 + # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 + # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 + # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 + # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 + # 31 + 32 + require "ri_cal" 33 + require "tzinfo" 34 + require "date" 35 + 36 + FORECAST_SPAN = (60 * 60 * 24 * 7) 37 + 38 + BOLD = "\e[1;1m" 39 + UNBOLD = "\e[0;0m" 40 + GRAY = "\e[38;5;239m" 41 + LIGHTGRAY = "\e[38;5;242m" 42 + RESET = "\e[0;0m" 43 + 44 + upcoming = [] 45 + 46 + if !ARGV.any? || ARGV[0] == "-h" 47 + puts "usage: #{$0} <ical file...>" 48 + exit 1 49 + end 50 + 51 + ARGV.each do |file| 52 + RiCal.parse(File.open(file)).first.events.each do |ev| 53 + ev.occurrences(:count => 10).each do |oc| 54 + begin 55 + start = oc.dtstart.to_time.getlocal 56 + finish = oc.dtend.to_time.getlocal 57 + rescue RiCal::InvalidTimezoneIdentifier 58 + next 59 + end 60 + 61 + if finish < Time.now || start > (Time.now + FORECAST_SPAN) 62 + next 63 + end 64 + 65 + upcoming.push oc 66 + end 67 + end 68 + end 69 + 70 + upcoming.sort_by{|u| u.dtstart.to_time.getlocal }.reverse.each do |ev| 71 + start = ev.dtstart.to_time.getlocal 72 + finish = ev.dtend.to_time.getlocal 73 + 74 + weeks = 0 75 + days = ((start - Time.now) / (60 * 60 * 24)).ceil 76 + if days > 7 77 + weeks = (days.to_f / 7.0).floor 78 + days = days - (weeks * 7) 79 + end 80 + 81 + out = "" 82 + if weeks == 0 && days == 0 83 + out << BOLD 84 + elsif weeks == 1 85 + out << GRAY 86 + elsif weeks > 1 87 + out << LIGHTGRAY 88 + end 89 + 90 + out << ev.summary << " " 91 + 92 + if weeks > 0 93 + out << "in #{weeks} week#{weeks == 1 ? "" : "s"}" 94 + 95 + if days > 0 96 + out << ", #{days} day#{days == 1 ? "" : "s"}" 97 + end 98 + else 99 + if days < -1 100 + out << "#{days.abs} day#{days == -1 ? "" : "s"} ago" 101 + elsif days == -1 102 + out << "yesterday" 103 + elsif days == 0 104 + out << "today" 105 + elsif days == 1 106 + out << "tomorrow" 107 + else 108 + out << "in #{days} day#{days == 1 ? "" : "s"}" 109 + end 110 + end 111 + 112 + all_day = (start != finish && start.hour == 0 && finish.hour == 0 && 113 + (finish.to_i - start.to_i == (60 * 60 * 24))) 114 + 115 + if !all_day 116 + out << " at #{start.strftime("%H:%M")}" 117 + 118 + if start.to_date == Time.now.to_date 119 + mins = ((Time.now - start) / 60).floor 120 + if start < Time.now 121 + out << " (#{mins} minute#{mins == 1 ? "" : "s"} ago)" 122 + else 123 + out << " (in #{mins} minute#{mins == 1 ? "" : "s"})" 124 + end 125 + end 126 + end 127 + 128 + if weeks == 0 && days == 0 129 + out << UNBOLD 130 + end 131 + 132 + out << " (" << start.strftime("%a #{start.day} %b").downcase 133 + if !all_day && (start.to_date != finish.to_date) 134 + out << " to " << finish.strftime("%a #{finish.day} %b").downcase 135 + end 136 + out << ")" 137 + 138 + out << RESET 139 + 140 + puts out 141 + end