sports_db 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ require 'digest/md5'
2
+
3
+ class ScoreNotification < ActiveRecord::Base
4
+ belongs_to :game
5
+
6
+ def set_digest
7
+ str = self["message"] + TimeService.now.to_s
8
+ self["digest"] = Digest::SHA1.hexdigest(str)
9
+ end
10
+
11
+ end
@@ -0,0 +1,47 @@
1
+ module SportsDb
2
+ class NewsBuilder
3
+
4
+ def self.update_player_news
5
+ p "Updating SN Player news ..."
6
+ config = SimpleConfig.for(:feeds)
7
+ require 'open-uri'
8
+
9
+ articles = []
10
+
11
+ unless !config.player_news_url.blank?
12
+ p "No sporting news player new URL given."
13
+ return
14
+ end
15
+
16
+ open( config.player_news_url ) do |file|
17
+ doc = Nokogiri::XML(file.read)
18
+
19
+ doc.xpath('//row').each do |player_element|
20
+ player = Player.find_by_sporting_news_id( player_element['PLAYER_ID'].to_i )
21
+ if player
22
+ content = "#{player_element['COMMENT']}. #{player_element['IMPACT']}."
23
+ content.gsub!("..",".")
24
+
25
+ article = Article.new
26
+ article.title = "News for #{player.first_name} #{player.last_name}"
27
+ article.category = "Player News"
28
+ article.player = player
29
+ article.contents = content
30
+ articles << article
31
+
32
+ p "#{player.first_name} #{player.last_name}"
33
+ end
34
+ end
35
+ end
36
+
37
+ Article.transaction do
38
+ Article.delete_all("player_id is not null")
39
+ articles.each {|article| article.save }
40
+ end
41
+
42
+ rescue Exception => e
43
+ Zumobi::ExceptionHandler.error e
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,530 @@
1
+ module SportsDb
2
+ class SportingNewsFeedBuilder
3
+
4
+ def self.update_news_feed
5
+ config_feeds = SimpleConfig.for(:feeds)
6
+
7
+ Article.transaction do
8
+ # #delete existing articles older than 30 days
9
+ # remove_date = TimeService.now - 30.day
10
+ # sn_articles_old = Article.find(:all, :conditions => ["source = 'Sporting News' and published_at > ?", remove_date])
11
+ # sn_articles_old.each {|article| article.destroy }
12
+
13
+ update_webgen_feeds(config_feeds.news_feeds, "news")
14
+ update_webgen_feeds(config_feeds.breaking_news_feeds, "breaking")
15
+
16
+
17
+ if CONFIG.affiliation_key == 'l.nfl.com'
18
+ update_webgen_feeds(config_feeds.team_feeds, "pro_team_news")
19
+ elsif CONFIG.affiliation_key == 'l.nba.com'
20
+ update_webgen_feeds(config_feeds.team_feeds, "pro_team_news")
21
+ elsif CONFIG.affiliation_key == 'l.mlb.com'
22
+ update_webgen_feeds(config_feeds.team_feeds, "pro_team_news")
23
+ update_webgen_feeds(config_feeds.fantasy_news_feeds, "fantasy")
24
+ elsif CONFIG.affiliation_key.match('l.ncaa.org')
25
+ update_webgen_feeds(ExternalFeed.find(:all, :conditions => ["content_type = ? and provider = ?", "news", "Sporting News"]), "ncaa_team_news")
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.update_webgen_feeds(feed_list, list_type)
31
+ if list_type == "ncaa_team_news"
32
+ feed_list.each do |feed_obj|
33
+ feed_team = Team.find(feed_obj.team_id)
34
+
35
+ if !feed_team.nil?
36
+ feed_url = feed_obj.woven_feed_url
37
+ if CONFIG.woven_feed_server != "woven.zumobi.net"
38
+ feed_url = feed_url.gsub("woven.zumobi.net", CONFIG.woven_feed_server)
39
+ end
40
+ list_type = "team_news"
41
+
42
+ rss = retrieve_feed(feed_url)
43
+ parse_webgen_feeds(rss, feed_team.key, feed_url, list_type, feed_team.city_name)
44
+ end
45
+ end
46
+ else
47
+ feed_list.each do |feed_title, url|
48
+ if list_type == "pro_team_news"
49
+ feed_key = feed_title
50
+ if Team.column_names.include?("tsn_key") && feed_key.to_s.match(CONFIG.affiliation_key)
51
+ feed_key = tsn_keys_to_stats_key(feed_key)
52
+ end
53
+ team = Team.find_by_key(feed_key)
54
+ feed_title = (!team.nil?) ? team.city_name : feed_title
55
+ list_type = "team_news"
56
+ elsif list_type == "breaking"
57
+ feed_key = CONFIG.affiliation_key
58
+ elsif list_type == "news"
59
+ feed_key = CONFIG.affiliation_key
60
+ elsif list_type == "fantasy"
61
+ feed_key = "fantasy"
62
+ end
63
+
64
+ if !feed_key.blank?
65
+ rss = retrieve_feed(url)
66
+ parse_webgen_feeds(rss, feed_key, url, list_type, feed_title)
67
+ end
68
+ end
69
+ end
70
+ rescue Exception => e
71
+ Zumobi::ExceptionHandler.error e
72
+ end
73
+
74
+ def self.parse_webgen_feeds(rss, feed_key, url, list_type, feed_title)
75
+ p "News - #{feed_title} - #{url}"
76
+
77
+ article_count = 0
78
+
79
+ doc = Nokogiri::XML(rss)
80
+ source = "Sporting News"
81
+ if !doc.nil?
82
+
83
+ doc.xpath('//item').each do |node|
84
+ article_count += 1
85
+
86
+ if feed_key == CONFIG.affiliation_key
87
+ article_link = node.xpath('link').text
88
+ if !article_link.match(CONFIG.sn_breaking_news_match)
89
+ next
90
+ end
91
+ end
92
+
93
+ guid = node.xpath('guid').text
94
+ item_title = node.xpath('title').text
95
+
96
+ if Article.find_by_digest(guid).nil? && Article.find_by_title(item_title).nil?
97
+ article_obj = Article.new
98
+
99
+ article_obj.title = node.xpath('title').text
100
+ article_obj.published_at = node.xpath('pubDate').text
101
+ article_obj.link = node.xpath('link').text
102
+ article_obj.digest = guid
103
+ article_obj.source = source
104
+
105
+ if list_type == "team_news" || list_type == "team_news_no_filters"
106
+ article_obj.category = "Team News"
107
+ elsif list_type == "breaking" || list_type == "news"
108
+ article_obj.category = "League News"
109
+ elsif list_type == "fantasy"
110
+ article_obj.category = "Fantasy News"
111
+ else
112
+ article_obj.category = "News"
113
+ end
114
+
115
+ if guid.blank?
116
+ article_obj.set_digest
117
+ end
118
+
119
+ if list_type == "breaking"
120
+ article_obj.author = (!node.xpath('author').nil?) ? node.xpath('author').text : ""
121
+ article_obj.contents = (!node.xpath('description').nil?) ? node.xpath('description').text.strip : ""
122
+ else
123
+ article_obj.author = (!node.xpath('dc:creator').nil?) ? node.xpath('dc:creator').text : ""
124
+ article_obj.contents = (!node.xpath('content:encoded').nil?) ? node.xpath('content:encoded').text.strip : ""
125
+ thumb_image = (!node.xpath('media:thumbnail').nil?) ? node.xpath('media:thumbnail/@url').text : ""
126
+ if !thumb_image.blank?
127
+ if CONFIG.affiliation_key == 'l.nfl.com'
128
+ article_obj.thumb_image_url = CONFIG.image_service + 'crop/w/110/url/' + CGI::escape(CGI::escape(thumb_image))
129
+ article_obj.article_image_url = CONFIG.image_service + 'transform/w/480/h/480/url/' + CGI::escape(CGI::escape(thumb_image))
130
+ else
131
+ article_obj.thumb_image_url = CONFIG.image_service + 'crop/w/55/url/' + CGI::escape(CGI::escape(thumb_image))
132
+ article_obj.article_image_url = CONFIG.image_service + 'transform/w/280/h/280/url/' + CGI::escape(CGI::escape(thumb_image))
133
+ end
134
+ end
135
+ end
136
+
137
+ if article_obj.title.blank? || article_obj.title == "." || article_obj.contents.blank? || article_obj.contents == "."
138
+ next
139
+ end
140
+
141
+ if list_type == "fantasy"
142
+ set_fantasy_article_filter(article_obj, feed_key)
143
+ else
144
+ set_article_filter_association(article_obj, feed_key)
145
+ end
146
+
147
+ article_obj.save
148
+ else
149
+ Article.transaction do
150
+ article_obj = Article.find_by_digest(guid)
151
+ if article_obj.nil?
152
+ article_obj = Article.find_by_title(item_title)
153
+ end
154
+
155
+ article_obj.title = node.xpath('title').text
156
+ article_obj.published_at = node.xpath('pubDate').text
157
+ article_obj.link = node.xpath('link').text
158
+
159
+ if list_type == "breaking"
160
+ article_obj.author = (!node.xpath('author').nil?) ? node.xpath('author').text : ""
161
+ article_obj.contents = (!node.xpath('description').nil?) ? node.xpath('description').text.strip : ""
162
+ else
163
+ article_obj.author = (!node.xpath('dc:creator').nil?) ? node.xpath('dc:creator').text : ""
164
+ article_obj.contents = (!node.xpath('content:encoded').nil?) ? node.xpath('content:encoded').text.strip : ""
165
+ thumb_image = (!node.xpath('media:thumbnail').nil?) ? node.xpath('media:thumbnail/@url').text : ""
166
+
167
+ if !thumb_image.blank?
168
+ if CONFIG.affiliation_key == 'l.nfl.com'
169
+ article_obj.thumb_image_url = CONFIG.image_service + 'crop/w/110/url/' + CGI::escape(CGI::escape(thumb_image))
170
+ article_obj.article_image_url = CONFIG.image_service + 'transform/w/480/h/480/url/' + CGI::escape(CGI::escape(thumb_image))
171
+ else
172
+ article_obj.thumb_image_url = CONFIG.image_service + 'crop/w/55/url/' + CGI::escape(CGI::escape(thumb_image))
173
+ article_obj.article_image_url = CONFIG.image_service + 'transform/w/280/h/280/url/' + CGI::escape(CGI::escape(thumb_image))
174
+ end
175
+ end
176
+ end
177
+
178
+ if list_type == "fantasy"
179
+ set_fantasy_article_filter(article_obj, feed_key)
180
+ else
181
+ set_article_filter_association(article_obj, feed_key)
182
+ end
183
+ article_obj.save
184
+ end
185
+ end
186
+
187
+ p "Article - #{article_obj.title}"
188
+
189
+ remove_flash_elements(article_obj)
190
+
191
+ if (list_type == "team_news" || list_type == "breaking") && CONFIG.enable_notifications
192
+ if list_type == "team_news"
193
+ if Team.column_names.include?("tsn_key")
194
+ team = Team.find_by_key(feed_key)
195
+ notify_feed_key = team.tsn_key
196
+ else
197
+ notify_feed_key = feed_key
198
+ end
199
+ send_notification(article_obj, notify_feed_key, list_type)
200
+ else
201
+ send_notification(article_obj, feed_key, list_type)
202
+ end
203
+ end
204
+ end
205
+
206
+ p "#{article_count} articles processed"
207
+ end
208
+ end
209
+
210
+ def self.set_article_filter_association(article, feed_key)
211
+ #all
212
+ if !article.news_filters.find(:first, :conditions => "news_filter_key = 'all'")
213
+ filter = NewsFilter.find(:first, :conditions => ["news_filter_key = 'all'"])
214
+ filter.articles << article
215
+ filter.save
216
+ end
217
+
218
+ if feed_key != CONFIG.affiliation_key
219
+ #team
220
+ if Team.column_names.include?("tsn_key") && feed_key.to_s.match(CONFIG.affiliation_key)
221
+ feed_key = tsn_keys_to_stats_key(feed_key)
222
+ end
223
+ team = Team.find_by_key(feed_key)
224
+
225
+ if !team.nil?
226
+ if !article.news_filters.find(:first, :conditions => ["news_filter_key = ?", team.key])
227
+ filter_team = NewsFilter.find(:first, :conditions => ["news_filter_key = ?", team.key])
228
+ if !filter_team.nil?
229
+ filter_team.articles << article
230
+ end
231
+ end
232
+
233
+ #conference
234
+ if CONFIG.affiliation_key.match('l.ncaa.org')
235
+ conference_key = team.conference.key
236
+ else
237
+ conference_key = team.conference
238
+ end
239
+ if !article.news_filters.find(:first, :conditions => ["news_filter_key = ?", conference_key])
240
+ filter_conference = NewsFilter.find(:first, :conditions => ["news_filter_key = ?", conference_key])
241
+ if !filter_conference.nil?
242
+ filter_conference.articles << article
243
+ end
244
+ end
245
+ end
246
+ else
247
+ #league
248
+ if !article.news_filters.find(:first, :conditions => ["news_filter_key = ?", feed_key])
249
+ filter_conference = NewsFilter.find(:first, :conditions => ["news_filter_key = ?", feed_key])
250
+ if !filter_conference.nil?
251
+ filter_conference.articles << article
252
+ end
253
+ end
254
+ end
255
+ end
256
+
257
+ def self.set_fantasy_article_filter(article, feed_key)
258
+ if !article.news_filters.find(:first, :conditions => "news_filter_key = 'fantasy'")
259
+ filter = NewsFilter.find(:first, :conditions => ["news_filter_key = 'fantasy'"])
260
+ if !filter.nil?
261
+ filter.articles << article
262
+ filter.save
263
+ end
264
+ end
265
+ end
266
+
267
+ def self.remove_flash_elements(article)
268
+ doc = Nokogiri::HTML(article.contents)
269
+ objects = doc.search("embed")
270
+ objects.each do |obj|
271
+ obj.remove
272
+ end
273
+
274
+ contents = doc.children[1].to_html
275
+ contents = contents.gsub("<html><body>\n", '')
276
+ contents = contents.gsub('</body></html>', '')
277
+
278
+ article.contents = contents
279
+ article.save
280
+ end
281
+
282
+ def self.send_notification(article, team_key, notify_type)
283
+ #checks to see if the article is new enough
284
+ #checks if notification has been sent yet. if there's a row in the notifications table, then it's been sent
285
+ if !article.nil? && !article.digest.blank?
286
+ n = Notification.find(:first, :conditions => ['title = ? and category = ?', article.title, team_key])
287
+
288
+ if (n.nil? && article.published_at > CONFIG.send_notifications_since.call())
289
+ n = Notification.new
290
+ n.title = article.title
291
+ n.article_digest = article.digest
292
+ n.category = team_key
293
+ n.article_id = article.id
294
+ n.article_link = article.link
295
+ n.notify_sent = 0
296
+
297
+ if team_key != CONFIG.affiliation_key
298
+ team = Team.find_by_tsn_key(team_key)
299
+ if !team.nil?
300
+ n.team_id = team.id
301
+ end
302
+ end
303
+
304
+ n.save
305
+
306
+ elsif (!n.nil? && n.notify_sent == 0 && article.published_at > CONFIG.send_notifications_since.call())
307
+ #putting a delay in for sending out notify rather than sending right when we get the article.
308
+ #i'm sure that there's a caching issue between the backend and front end. hopefully this will help work that out.
309
+ alert_title = article.title[0,90]
310
+ if article.title.length > 90
311
+ alert_title += "..."
312
+ end
313
+
314
+ n.notify_sent = 1
315
+ n.save
316
+
317
+ if notify_type == "team_news" && !n.team_id.nil?
318
+ send_push_notification(alert_title, [team_key], article.digest)
319
+ elsif notify_type == "breaking"
320
+ send_push_notification(alert_title, [team_key], article.digest)
321
+ end
322
+
323
+ else
324
+ if (!n.nil?)
325
+ #Rails.logger.info("Already sent (Notification_id: #{n.id})")
326
+ else
327
+ #Rails.logger.info("Article too old to notify (#{article.digest})")
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ # Sends a notification for the specified app / notification class combo.
334
+ #
335
+ # Parameters:
336
+ # application_name - The name of the application the notification is intended for.
337
+ # tag - data goes into "tags" field
338
+ # alert - The text to display in the notification.
339
+ # digest - An additional field added for NFL - this is the article id.
340
+ # tag_key - An additional field added for NFL - this is the tag that the notifs is subscribed to team or league.
341
+ # sound (optional) - Whether or not to play a sound when the notification is sent. A value of ÔøΩtrueÔøΩ, ÔøΩ1ÔøΩ, or ÔøΩyesÔøΩ means to play the sound. Any other values will not play a sound.
342
+ def self.send_push_notification(alert_text, tags, digest)
343
+ p "--Notified! '#{alert_text[0,50]}' at #{DateTime.now.to_s}: Tag: #{tag}, Response: #{res.code} #{res.message}"
344
+
345
+ notifier = Zumobi::Notifier.new
346
+ notifier.push({
347
+ :aps => {:alert => alert_text, :sound => "1"},
348
+ :android => {:alert => alert_text, :extra => "#{tags.join(',')}|#{digest}"},
349
+ :tags => tags,
350
+ :tag_key => tags.join(','),
351
+ :digest => digest
352
+ })
353
+ end
354
+
355
+ def self.retrieve_feed(url)
356
+ body = make_request(url)
357
+ body
358
+ end
359
+
360
+
361
+ def self.remove_dup_and_old_articles()
362
+ teams = Team.find(:all, :conditions => ["division is not null"])
363
+ teams.each do |t|
364
+ p "#{t.city_name} #{t.team_name}"
365
+
366
+ news_filter = NewsFilter.find(:first, :conditions => ["news_filter_key = ?", t.key])
367
+ remove_by_news_filter(news_filter)
368
+ end
369
+
370
+ p "All"
371
+ news_filter = NewsFilter.find(:first, :conditions => ["news_filter_key = ?", "all"])
372
+ remove_by_news_filter(news_filter)
373
+ end
374
+
375
+ def self.remove_by_news_filter(news_filter)
376
+ if !news_filter.nil?
377
+ article_titles = {}
378
+ team_articles = news_filter.articles.find(:all)
379
+
380
+ team_articles.each do |a|
381
+ article_digest = Digest::SHA1.hexdigest(a.sn_url)
382
+
383
+ if article_titles[article_digest].blank?
384
+ article_titles[article_digest] = a
385
+ p "---- #{a.title}"
386
+ else
387
+ p "Dup! - #{a.title}"
388
+
389
+ this_article = a
390
+ saved_article = article_titles[article_digest]
391
+
392
+ if this_article.published_at > saved_article.published_at
393
+ article_titles[article_digest] = this_article
394
+ saved_article.destroy
395
+ else
396
+ this_article.destroy
397
+ end
398
+ end
399
+ end
400
+ end
401
+ end
402
+
403
+ private
404
+
405
+ def self.make_request(url)
406
+ begin
407
+ c = Curl::Easy.perform(url) do |curl|
408
+ curl.connect_timeout = 120
409
+ curl.follow_location = true
410
+ curl.max_redirects = 10
411
+ curl.timeout = 120
412
+ curl.encoding = 'gzip'
413
+ # curl.verbose = true
414
+ end
415
+ c.body_str
416
+ rescue Curl::Err::TimeoutError
417
+ #
418
+ rescue Curl::Err::GotNothingError
419
+ #
420
+ rescue Curl::Err::RecvError
421
+ #
422
+ end
423
+ end
424
+
425
+ def self.tsn_keys_to_stats_key(tsn_key)
426
+ #static map for stats global-id to tsn team_key
427
+ keys = {
428
+ "l.nfl.com-t.1" => 324,
429
+ "l.nfl.com-t.2" => 338,
430
+ "l.nfl.com-t.3" => 345,
431
+ "l.nfl.com-t.4" => 348,
432
+ "l.nfl.com-t.5" => 352,
433
+ "l.nfl.com-t.6" => 366,
434
+ "l.nfl.com-t.7" => 327,
435
+ "l.nfl.com-t.8" => 329,
436
+ "l.nfl.com-t.9" => 365,
437
+ "l.nfl.com-t.10" => 356,
438
+ "l.nfl.com-t.11" => 336,
439
+ "l.nfl.com-t.12" => 332,
440
+ "l.nfl.com-t.13" => 339,
441
+ "l.nfl.com-t.14" => 341,
442
+ "l.nfl.com-t.15" => 357,
443
+ "l.nfl.com-t.16" => 361,
444
+ "l.nfl.com-t.17" => 355,
445
+ "l.nfl.com-t.18" => 331,
446
+ "l.nfl.com-t.19" => 351,
447
+ "l.nfl.com-t.20" => 354,
448
+ "l.nfl.com-t.21" => 363,
449
+ "l.nfl.com-t.22" => 326,
450
+ "l.nfl.com-t.23" => 334,
451
+ "l.nfl.com-t.24" => 335,
452
+ "l.nfl.com-t.25" => 347,
453
+ "l.nfl.com-t.26" => 362,
454
+ "l.nfl.com-t.27" => 323,
455
+ "l.nfl.com-t.28" => 343,
456
+ "l.nfl.com-t.29" => 364,
457
+ "l.nfl.com-t.30" => 350,
458
+ "l.nfl.com-t.31" => 359,
459
+ "l.nfl.com-t.32" => 325,
460
+ "l.mlb.com-t.1" => 225,
461
+ "l.mlb.com-t.2" => 226,
462
+ "l.mlb.com-t.3" => 234,
463
+ "l.mlb.com-t.4" => 254,
464
+ "l.mlb.com-t.5" => 238,
465
+ "l.mlb.com-t.6" => 228,
466
+ "l.mlb.com-t.7" => 229,
467
+ "l.mlb.com-t.8" => 230,
468
+ "l.mlb.com-t.9" => 231,
469
+ "l.mlb.com-t.10" => 233,
470
+ "l.mlb.com-t.11" => 227,
471
+ "l.mlb.com-t.12" => 235,
472
+ "l.mlb.com-t.13" => 236,
473
+ "l.mlb.com-t.14" => 237,
474
+ "l.mlb.com-t.15" => 239,
475
+ "l.mlb.com-t.16" => 252,
476
+ "l.mlb.com-t.17" => 244,
477
+ "l.mlb.com-t.18" => 245,
478
+ "l.mlb.com-t.19" => 246,
479
+ "l.mlb.com-t.20" => 240,
480
+ "l.mlb.com-t.21" => 241,
481
+ "l.mlb.com-t.22" => 242,
482
+ "l.mlb.com-t.23" => 232,
483
+ "l.mlb.com-t.24" => 247,
484
+ "l.mlb.com-t.25" => 248,
485
+ "l.mlb.com-t.26" => 253,
486
+ "l.mlb.com-t.27" => 251,
487
+ "l.mlb.com-t.28" => 243,
488
+ "l.mlb.com-t.29" => 249,
489
+ "l.mlb.com-t.30" => 250,
490
+ "l.nba.com-t.1" => 2,
491
+ "l.nba.com-t.2" => 14,
492
+ "l.nba.com-t.3" => 17,
493
+ "l.nba.com-t.4" => 18,
494
+ "l.nba.com-t.5" => 19,
495
+ "l.nba.com-t.6" => 20,
496
+ "l.nba.com-t.7" => 27,
497
+ "l.nba.com-t.8" => 1,
498
+ "l.nba.com-t.9" => 3,
499
+ "l.nba.com-t.10" => 4,
500
+ "l.nba.com-t.11" => 5,
501
+ "l.nba.com-t.12" => 8,
502
+ "l.nba.com-t.13" => 11,
503
+ "l.nba.com-t.14" => 15,
504
+ "l.nba.com-t.15" => 28,
505
+ "l.nba.com-t.16" => 6,
506
+ "l.nba.com-t.17" => 7,
507
+ "l.nba.com-t.18" => 10,
508
+ "l.nba.com-t.19" => 29,
509
+ "l.nba.com-t.20" => 16,
510
+ "l.nba.com-t.21" => 24,
511
+ "l.nba.com-t.22" => 26,
512
+ "l.nba.com-t.23" => 9,
513
+ "l.nba.com-t.24" => 12,
514
+ "l.nba.com-t.25" => 13,
515
+ "l.nba.com-t.26" => 21,
516
+ "l.nba.com-t.27" => 22,
517
+ "l.nba.com-t.28" => 23,
518
+ "l.nba.com-t.29" => 25,
519
+ "l.nba.com-t.32" => 5312
520
+ }
521
+
522
+ if !tsn_key.blank? && !keys[tsn_key].blank?
523
+ return keys[tsn_key]
524
+ else
525
+ return ''
526
+ end
527
+ end
528
+
529
+ end
530
+ end
@@ -0,0 +1,78 @@
1
+ module SportsDb
2
+ class TeamAndRosterBuilder
3
+
4
+ def self.update_player_image_urls
5
+ p "update_player_image_urls ..."
6
+ config_feeds = SimpleConfig.for(:feeds)
7
+ require 'open-uri'
8
+ open( config_feeds.player_images_url ) do |file|
9
+ doc = Nokogiri::XML(file.read)
10
+ doc.xpath('//row').each do |player_element|
11
+ # The date is in format '08-MAY-77' and we need '8/5/1977' as a string. However, this still doesn't
12
+ # strip leading zeroes off days, which we need to do to match this date as a string.
13
+ # Convert to string, replace last bit so it says "19xx", parse to date, and then format
14
+ # in the D/M/Y format. Then finally remove leading zeroes.
15
+ if !player_element['BIRTH_DATE'].blank?
16
+ adj_birth_date = DateTime.strptime(player_element['BIRTH_DATE'].to_s, "%m/%d/%Y").to_s
17
+ end
18
+
19
+ # Try only the name first.
20
+ matches = Player.find_by_last_comma_first( player_element['PLAYER_NAME'] )
21
+ # But if there's more than one, then attempt to whittle it down with the birthday
22
+ matches = matches.select {|p| p.birth_date == adj_birth_date } if (matches.length > 1)
23
+
24
+ if (matches.length == 1)
25
+ player = matches[0]
26
+ player.image_url = player_element['PLAYER_MUG']
27
+ player.sporting_news_id = player_element['PLAYER_ID'].to_i
28
+ player.save
29
+
30
+ p "img- #{player_element['PLAYER_NAME']} - #{player_element['PLAYER_MUG']}"
31
+ else
32
+ p "Unable to Match -- #{player_element['PLAYER_NAME']}"
33
+ end
34
+
35
+ end
36
+ end
37
+ rescue Exception => e
38
+ Zumobi::ExceptionHandler.error e
39
+ end
40
+
41
+ def self.update_team_top25_rank
42
+ p "Set Top 25 Rank"
43
+
44
+ rank_issuer_key = map_poll_key(CONFIG.rank_based_on_poll)
45
+
46
+ rankings = PollRanking.all(:conditions => ["issuer_key = ?", rank_issuer_key])
47
+ rankings.each do |obj|
48
+ team_obj = Team.find(obj.team_id)
49
+ # Only rank 1-25 (bug 24359)
50
+ if team_obj.present? && obj.rank.to_i < 26
51
+ p "#{team_obj.city_name} ##{obj.rank}"
52
+ team_obj.rank = obj.rank
53
+ team_obj.save
54
+ end
55
+ end
56
+ end
57
+
58
+ def self.map_poll_key(poll_key)
59
+ case poll_key
60
+ when "poll-ap"
61
+ "1"
62
+ when "poll-usat"
63
+ "2"
64
+ when "ranking-bcs"
65
+ "3"
66
+ when "ap"
67
+ "ap"
68
+ when "coaches"
69
+ "coaches"
70
+ when "rpi"
71
+ "rpi"
72
+ else
73
+ "1"
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -1,3 +1,3 @@
1
1
  module SportsDb
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/sports_db.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  require "sports_db/engine"
2
2
  require "sports_db/external_feed_builder"
3
3
  require "sports_db/media_builder"
4
+ require "sports_db/news_builder"
4
5
  require "sports_db/news_filters_builder"
6
+ require "sports_db/sporting_news_feed_builder"
7
+ require "sports_db/team_and_roster_builder"
5
8
  require "sports_db/twitter_builder"
9
+ require "time_service"
6
10
 
7
11
  module SportsDb
8
12
 
@@ -2,3 +2,103 @@
2
2
  # task :sports_db do
3
3
  # # Task goes here
4
4
  # end
5
+
6
+ namespace 'zu' do
7
+
8
+ task :set_logger => :environment do
9
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
10
+ end
11
+
12
+ namespace 'sports' do
13
+ desc "Run sports db migrations."
14
+ task :migrate => 'zu:set_logger' do
15
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
16
+ ActiveRecord::Migrator.migrate("vendor/plugins/sports_db/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
17
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
18
+ end
19
+
20
+ desc "Drops and recreates the sports_db database (as well as any application-specific migrations)."
21
+ task :reset => [ 'db:drop', 'db:create', 'zu:sports:migrate', 'db:migrate'] do
22
+ # Renamed this reset because there's a db:reset task that does the same thing...
23
+ end
24
+
25
+ desc "Incrementally update the database based on job's frequency. Usage: 'rake zu:sports:update[1]'"
26
+ task :update, [:frequency] => 'zu:set_logger' do |t, args|
27
+ case args.frequency
28
+ when "1" then SportsBuildManager.every_1_minute
29
+ when "15" then SportsBuildManager.every_15_minutes
30
+ when "60" then SportsBuildManager.every_60_minutes
31
+ when "720" then SportsBuildManager.every_720_minutes
32
+ when "1440" then SportsBuildManager.every_1440_minutes
33
+ end
34
+ end
35
+
36
+ desc "Run game_updater (sub-1 minute task)"
37
+ task :run_games_updater, [] => ['zu:set_logger'] do |t|
38
+ SportsBuildManager.games_updater
39
+ end
40
+
41
+ desc "Build the entire database 'from scratch' without reference to job scheduling"
42
+ task :build, [] => ['zu:set_logger'] do |t|
43
+ %w(one_time every_1440_minutes every_720_minutes every_15_minutes every_1_minute).each do |m|
44
+ SportsBuildManager.send(m)
45
+ end
46
+ end
47
+
48
+ desc "Run one time and 12 hour builds prior to starting FeedFetcher on a clean instance of TSN server"
49
+ task :build_to_day, [] => ['zu:set_logger'] do |t|
50
+ SportsBuildManager.one_time
51
+ SportsBuildManager.every_1440_minutes
52
+ end
53
+
54
+ desc "Parse team info xml from sporting news and fetch all team logos to local disk."
55
+ task :get_team_logos => 'zu:set_logger' do
56
+
57
+ mkdir "#{Rails.root.to_s}/public/images/logos/" unless File.exists?( "#{Rails.root.to_s}/public/images/logos/" )
58
+
59
+ config = SimpleConfig.for(:feeds)
60
+ require 'open-uri'
61
+ open( config.team_info_url ) do |file|
62
+
63
+ doc = Nokogiri::XML(file.read)
64
+ doc.xpath('//row').each do |team_element|
65
+ %w{small medium large}.each do |size|
66
+ image = open( team_element['TEAM_LOGO'].gsub( /large/, size ) )
67
+ path = "#{Rails.root.to_s}/public/images/logos/#{size}"
68
+ mkdir path unless File.exists?( path )
69
+ /\.(\w+)$/ =~ team_element['TEAM_LOGO']
70
+ File.open("#{path}/#{team_element['TEAM_NAME_SHORT']}.#{$1}", "w") do |file|
71
+ file.puts( image.read )
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ desc "Set up Stats LLC database, same schema as the mlb database, just with a different name, for testing"
79
+ task :create_statsllc_database => 'zu:set_logger' do
80
+ ActiveRecord::Base.configurations = YAML::load(IO.read("#{Rails.root.to_s}/config/statsllc_database.yml"))
81
+
82
+ ActiveRecord::Base.establish_connection("#{Rails.env}")
83
+ ActiveRecord::Base.connection.drop_database(ActiveRecord::Base.configurations[Rails.env]["database"])
84
+ ActiveRecord::Base.connection.create_database(ActiveRecord::Base.configurations[Rails.env]["database"], ActiveRecord::Base.configurations[Rails.env])
85
+
86
+ ActiveRecord::Base.establish_connection("#{Rails.env}")
87
+
88
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
89
+ ActiveRecord::Migrator.migrate("vendor/plugins/sports_db/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
90
+ ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
91
+ end
92
+
93
+ desc "Run migrations on statsllc db"
94
+ task :migrate_statsllc_database => 'zu:set_logger' do
95
+ ActiveRecord::Base.configurations = YAML::load(IO.read("#{Rails.root.to_s}/config/statsllc_database.yml"))
96
+
97
+ ActiveRecord::Base.establish_connection("#{Rails.env}")
98
+
99
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
100
+ ActiveRecord::Migrator.migrate("vendor/plugins/sports_db/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
101
+ ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,9 @@
1
+ class TimeService
2
+ def self.now
3
+ config = SimpleConfig.for(:application)
4
+ config.timeservice.call()
5
+ rescue
6
+ Rails.logger.error("Could not call time service: SimpleConfig is not initialized, there's no application scope, no timeservice property, or it's not a lambda.")
7
+ return DateTime.now.utc
8
+ end
9
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
8
+ - 3
9
+ version: 0.0.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Alx Dark
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2013-04-22 00:00:00 -07:00
17
+ date: 2013-04-26 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -71,16 +71,21 @@ files:
71
71
  - app/models/player_stat.rb
72
72
  - app/models/poll_ranking.rb
73
73
  - app/models/score.rb
74
+ - app/models/score_notification.rb
74
75
  - app/models/twitter.rb
75
76
  - config/routes.rb
76
77
  - lib/sports_db/engine.rb
77
78
  - lib/sports_db/external_feed_builder.rb
78
79
  - lib/sports_db/media_builder.rb
80
+ - lib/sports_db/news_builder.rb
79
81
  - lib/sports_db/news_filters_builder.rb
82
+ - lib/sports_db/sporting_news_feed_builder.rb
83
+ - lib/sports_db/team_and_roster_builder.rb
80
84
  - lib/sports_db/twitter_builder.rb
81
85
  - lib/sports_db/version.rb
82
86
  - lib/sports_db.rb
83
87
  - lib/tasks/sports_db_tasks.rake
88
+ - lib/time_service.rb
84
89
  - MIT-LICENSE
85
90
  - Rakefile
86
91
  - README.rdoc