geeklets 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ === 0.0.2 2010-02-07
2
+
3
+ * Added weather script from Ted Wise.
4
+
5
+ === 0.0.1 2010-02-13
6
+
7
+ * 1 major enhancement:
8
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 John F. Schank III
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ = geeklets
2
+
3
+ Geeklets is a collection of scripts for use with GeekTool.
4
+ Here is the breakdown of the scripts
5
+
6
+ == Note about these scripts.
7
+
8
+ At the moment - they are all focused on my particular needs, so
9
+ they are currently hardcoded for my area of Virginia, USA. As this
10
+ project develops I will be trying to find a way to refactor that depency
11
+ away, and allow individual users to customize for their needs.
12
+
13
+ Just be aware, that right now, unless you live in the Washington Metro area,
14
+ some of these scripts won't be useful for you.
15
+
16
+ Run them with a -h parameter for details.
17
+
18
+ == get_weather_icon
19
+
20
+ Gets the current weather icon from yahoo, and saves it to the tmp directory.
21
+
22
+ == next_months_calendar
23
+
24
+ Runs the unix "cal" command for whatever the next month from now is.
25
+
26
+ == OPM_alerts
27
+
28
+ Gets the Office of Personnel Management alert for the current day.
29
+ This if for things like Federal Government closings and delays due to weather.
30
+
31
+ == trash_usage
32
+
33
+ A wrapper for the unix "du" command to display the amount of space taken up by
34
+ the traash on an OS X mac.
35
+
36
+ == VRE_alerts
37
+
38
+ Shows the alerts for today for the Virginia Rail Express
39
+
40
+ == WMATA_alerts
41
+
42
+ Shows the alerts and delays for the Washington Metro Area Transit Authority,
43
+ the "Metro" subway and trains.
44
+
45
+ == Note on Patches/Pull Requests
46
+
47
+ * Fork the project.
48
+ * Make your feature addition or bug fix.
49
+ * Add tests for it. This is important so I don't break it in a
50
+ future version unintentionally.
51
+ * Commit, do not mess with rakefile, version, or history.
52
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
53
+ * Send me a pull request. Bonus points for topic branches.
54
+
55
+ == Copyright
56
+
57
+ Copyright (c) 2010 John F. Schank III. See LICENSE for details.
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "geeklets"
8
+ gem.summary = %Q{Scripts for GeekTool}
9
+ gem.description = %Q{A collection of useful scripts for use with GeekTool}
10
+ gem.email = "jschank@mac.com"
11
+ gem.homepage = "http://github.com/jschank/geeklets"
12
+ gem.authors = ["John F. Schank III"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.files = FileList['lib/**/*', 'bin/*', '[A-Z]*', 'spec/**/*', 'vendor/**/*'].to_a
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "geeklets #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../vendor'
5
+
6
+ require 'geeklets'
7
+
8
+ Geeklets.run(ARGV)
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'mechanize'
3
+ require 'rio'
4
+
5
+ class Get_Weather_Icon
6
+
7
+ def run(params)
8
+ agent = Mechanize.new
9
+ agent.get("http://weather.yahoo.com/forecast/USVA0262.html")
10
+ icon = agent.page.at(".forecast-icon")
11
+ style = icon.attributes["style"].value
12
+ icon_url = style.split("'")[1]
13
+
14
+ rio(icon_url) > rio("/tmp/weather-icon.png")
15
+ end
16
+
17
+ end
@@ -0,0 +1,9 @@
1
+ class Next_Months_Calendar
2
+
3
+ def run(params)
4
+ today = Date.today
5
+ nextmonth = today >> 1
6
+ system("cal","#{nextmonth.month}","#{nextmonth.year}")
7
+ end
8
+
9
+ end
@@ -0,0 +1,61 @@
1
+ # coding: UTF-8
2
+ require 'rubygems'
3
+ require 'open-uri'
4
+ require 'nokogiri'
5
+
6
+ URL = "http://www.opm.gov/status/index.aspx"
7
+
8
+ class OPM_Alerts
9
+
10
+ def die
11
+ puts <<-EOS
12
+ opm-status
13
+
14
+ USAGE:
15
+
16
+ \topm-status [wrapping-width]
17
+
18
+ Returns U.S. Office of Personnel Management status Alerts.
19
+
20
+ [wrapping-width] is an integer to limit the width of the descriptions
21
+ Defaults to: 40
22
+
23
+ EOS
24
+
25
+ exit
26
+ end
27
+
28
+ def run(params)
29
+ die if params[0] == "-h"
30
+
31
+ width = (params[1] and params[1].to_i) || 40
32
+
33
+ doc = Nokogiri::HTML(open(URL))
34
+
35
+ date_str = doc.css('#_ctl0__ctl0_DisplayDateSpan').text.strip
36
+ begin
37
+ date = Date.parse(date_str)
38
+ date_str = date.strftime("%x")
39
+ rescue
40
+ date_str = Date.today.strftime("%x") if date_str.length == 0
41
+ end
42
+
43
+ title = doc.css('h3').text.strip
44
+ title = "VRE Status" if title.length == 0
45
+
46
+ status = doc.css('.statusbox').text.strip
47
+
48
+ # Deal with the flaky case where OPM decides to
49
+ # display an image instead of actual text content
50
+ images = doc.xpath("//img")
51
+ status = images[0].attributes["alt"].text.strip if ((status.length == 0) && (images.count == 1))
52
+
53
+ status = "Not found" if status.length == 0
54
+
55
+ # display results
56
+ puts Utility.wrap_text("#{date_str} - #{title}", width, date_str.length + 3, :outdent)
57
+ puts "-" * width
58
+ puts Utility.wrap_text(status, width)
59
+ end
60
+
61
+ end
@@ -0,0 +1,8 @@
1
+ class Trash_Usage
2
+
3
+ def run(params)
4
+ system("du -sh ~/.Trash/ | awk '{print \"Trash is using \"$1}'")
5
+ end
6
+
7
+ end
8
+
@@ -0,0 +1,123 @@
1
+ # coding: UTF-8
2
+ require 'rubygems'
3
+ require 'chronic'
4
+ require 'mechanize'
5
+
6
+ FEED_URL = "http://www.vre.org/service/daily-download.html"
7
+ TRAIN_LINES = {"fbg_delay" => :fredericksburg, "mss_delay" => :manassas}
8
+
9
+ class VRE_Alerts
10
+
11
+ def die
12
+ puts <<-EOS
13
+ vre-alert
14
+
15
+ USAGE:
16
+
17
+ \tvre-alert [rail_lines] [wrapping-width] [specific-date]
18
+
19
+ Returns Virgina Railway Express (VRE) Alerts.
20
+
21
+ [rail_lines] specifies which rail lines you want alerts for.
22
+ Can be:
23
+ \tfredericsburg - for the Fredericsburg line
24
+ \tmanassas - for the Manassas line
25
+ \tboth - for both lines.
26
+ Defaults to: both
27
+
28
+ [wrapping-width] is an integer to limit the width of the descriptions
29
+ Defaults to: 40
30
+
31
+ [specific-date] is a string parseable by Date::parse which is the date from
32
+ the VRE daily download to display. NOTE: this does not access history, it only
33
+ allows you to choose a specific entry from the Daily Download to render. Mostly
34
+ used for debugging.
35
+ Defaults to: The current system Date.
36
+
37
+ EOS
38
+
39
+ exit
40
+ end
41
+
42
+ # >> notice.search("tr")[2].search("td")[0].attributes["class"].value
43
+ # => "fbg2"
44
+ # >> notice.search("tr")[2].search("td")[0].text
45
+ # => "Train 300"
46
+ def parse_notice(notice)
47
+ detail = {}
48
+ detail[:summary] = notice.attributes["summary"].text
49
+ detail[:morning] = []
50
+ detail[:evening] = []
51
+ rows = notice.search("tr")
52
+ service = nil
53
+
54
+ rows.each do |row|
55
+ cells = row.search("td")
56
+ case cells.count
57
+ when 0 :
58
+ (detail[:date] = Date.parse(row.children[0].text)) if (row && row.children && row.children[0] && row.children[0].name == "th")
59
+ when 1 :
60
+ service = :morning if row.children[0].text =~ /Morning/
61
+ service = :evening if row.children[0].text =~ /Evening/
62
+ when 2 :
63
+ # sometimes they split a description across multiple rows, when they do, they leave the train cell blank
64
+ # in that case, append the description text to the last trains description.
65
+ if cells[0].text.empty? && detail[service][-1]
66
+ detail[service][-1][:description] << cells[1].text
67
+ next
68
+ end
69
+
70
+ cell_class = cells[0].attributes["class"].value unless cells[0].text.empty?
71
+ detail[service] << {:line => TRAIN_LINES[cell_class], :train => cells[0].text, :description => cells[1].text} if @rail_lines == :both || @rail_lines == TRAIN_LINES[cell_class]
72
+ else
73
+ next
74
+ end
75
+ end
76
+ detail
77
+ end
78
+
79
+ def render_trains(trains, width)
80
+ puts " No delays" and return if trains.empty?
81
+ trains.each do |train|
82
+ puts " #{train[:line].to_s.capitalize} line #{train[:train]}"
83
+ Utility.wrap_text(train[:description], width, 3, :all)
84
+ end
85
+ end
86
+
87
+ def render_notice(notice, width)
88
+ if (notice.nil? || (notice[:morning].empty? && notice[:evening].empty?))
89
+ puts "No VRE notices for today"
90
+ return
91
+ end
92
+
93
+ puts notice[:summary]
94
+ puts "#{'-' * notice[:summary].length}"
95
+ puts "Morning Service"
96
+ render_trains(notice[:morning], width)
97
+ puts
98
+ puts "Afternoon/Evening Service"
99
+ render_trains(notice[:evening], width)
100
+ end
101
+
102
+ def run(params)
103
+ die if params[0] == "-h"
104
+
105
+ @rail_lines = (params[0] || "both").to_sym
106
+ width = (params[1] and params[1].to_i) || 40
107
+ alert_date = (params[2] and Date.parse(params[2])) || Date.today
108
+
109
+ #debug - set a date of interest, and rail line of interest
110
+ # alert_date = Date.civil(2010, 1, 13)
111
+ # @rail_lines = :fredericksburg
112
+
113
+ agent = Mechanize.new
114
+ agent.get(FEED_URL)
115
+ notices = agent.page.search(".format").map{ |notice| parse_notice(notice) }
116
+
117
+ #debug
118
+ #puts notices.to_yaml
119
+
120
+ render_notice(notices.select{ |notice| notice[:date] == alert_date }.shift, width)
121
+ end
122
+
123
+ end
@@ -0,0 +1,96 @@
1
+ # coding: UTF-8
2
+ require 'rubygems'
3
+ require 'rss'
4
+ require 'open-uri'
5
+ require 'chronic'
6
+ require 'htmlentities'
7
+
8
+ REPLACEMENTS =
9
+ [
10
+ ["p.m.", "pm"],
11
+ ["a.m.", "am"]
12
+ ]
13
+
14
+ FEED_URL = "http://www.wmata.com/rider_tools/metro_service_status/feeds/rail.xml"
15
+
16
+ class WMATA_Alerts
17
+
18
+ def die
19
+ puts <<-EOS
20
+ metro-feed
21
+
22
+ USAGE:
23
+
24
+ \tmetro-feed [history] [wrapping-width]
25
+
26
+ Returns WMATA feed information for metro lines.
27
+
28
+ [history] is how many days of feed history to display.
29
+ \tdefaults to 7 days. Can be anything parseable by Chronic gem
30
+
31
+ [wrapping-width] is the limit to how wide the text can be
32
+ \tdefaults to 40 characters. should be a positive integer value.
33
+
34
+ Requires gems: chronic, htmlentities
35
+
36
+
37
+ EOS
38
+
39
+ exit
40
+ end
41
+
42
+ def run(params)
43
+
44
+ die if params[0] == "-h"
45
+
46
+ @cutoff_string = params[0] || "7 days ago"
47
+ @width = params[1] || "40"
48
+ @width = @width.to_s.to_i
49
+
50
+ @cutoff = Chronic.parse(@cutoff_string)
51
+
52
+ @rss_content = ""
53
+
54
+ open(FEED_URL) do |f|
55
+ @rss_content = f.read
56
+ end
57
+
58
+ @rss = RSS::Parser.parse(@rss_content, false)
59
+
60
+ @item_hash = {}
61
+
62
+ # filter down to the items we want.
63
+ @rss.items.each do |item|
64
+ @item_hash[item.pubDate] = item unless item.pubDate < @cutoff
65
+ end
66
+
67
+ if @item_hash.empty?
68
+ puts "No data from WMATA feed."
69
+ else
70
+ @coder = HTMLEntities.new
71
+ @item_hash.keys.sort.reverse.each do |key|
72
+ item = @item_hash[key]
73
+
74
+ title = "#{item.pubDate.strftime('%x')} - #{item.title}"
75
+ puts title
76
+ puts ("-" * title.length)
77
+
78
+ message = @coder.decode(item.description)
79
+ REPLACEMENTS.each do |r|
80
+ message = message.gsub(r[0], r[1])
81
+ end
82
+ subject, *body = message.split(".")
83
+
84
+ puts Utility.wrap_text(subject+"...", @width)
85
+ body.each do |sentence|
86
+ puts Utility.wrap_text(sentence+".", @width, 3, :all)
87
+ end
88
+
89
+ puts
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+
96
+ end