Skip to content

Instantly share code, notes, and snippets.

@blackjid
Last active May 8, 2018 13:52
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save blackjid/1efd7332c3cf59d3c3f9 to your computer and use it in GitHub Desktop.
Worldcup Brazil 2014 Google Calendar Matches widget for Dashing

Description

A Dashing widget for displaying next Brazil 2014 World Cup matches from calendar event on Google Calendar

It was designed to be used against this calendar worldcupbrazilcalendar

screen shot

Made by platanus in Chile

Dependencies

Add it to dashing's gemfile:

gem 'google-api-client'
gem 'activesupport'

and run bundle install.

Usage

To use this widget, you'll first need to set up a Google API project.

  1. Create and download a new private key for Google API access.

    1. Go to https://code.google.com/apis/console
    2. Click 'Create Project' and accept TOS's
    3. Enable 'Calendar API' service and accept TOS's
    4. Under 'APIs & auth.' click 'Credentials' in the left-hand nav menu
    5. Pick the OAuth option by clicking 'Create new Client ID'
    6. Select 'Service Account' and click 'Create Client ID'. Download of your Private key is about to start. Save it securely.
    7. Note the password for your new private key ('notasecret')
    8. Next to the OAuth tab you will find the details for the service account you just created. Copy it's email address which will look something like this: 210987654321@developer.gserviceaccount.com - you'll need it in environmental variables later.
  2. Subscribe to matches calendar

    1. Go to www.worldcupbrazilcalendar.com and subscribe the matches calender to your google account.
    2. Go to the other calendar settings in google calendar, and the click in the WorldCup 2014 calendar.
    3. Find the callendar id, it should be something like cdadfstm90rcfdsfsdfsadegi2jgf@import.calendar.google.com
  3. Setup your widget

    1. You can install the widget with dashing install 1efd7332c3cf59d3c3f9, or manually add each file in the corresponding location

    2. Setup your environmental variables

      GOOGLE_SERVICE_ACCOUNT_EMAIL # Email of service account
      GOOGLE_SERVICE_PK_FILE # File containing your private key
      GOOGLE_SERVICE_KEY_SECRET # Password to unlock private key 'notasecret'
      WORLDCUP_CALENDAR # Calendar ID.
      WORLDCUP_MY_TEAM # The team you want to follow, refer to the worldcup.rb job to know how to spell it
    3. Add the widget HTML to your dashboard

      <li data-row="1" data-col="4" data-sizex="1" data-sizey="2">
        <div data-id="worldcup"
             data-view="Worldcup"
             data-title="Brazil 2014"
             data-my-team-title="Chile Matches"
             style="background-color:#357A3E;"></div>
        <img class="icon-background" src="/assets/brazil-back.png" />
      </li>

Notes

To set your PK12 certificate in heroku you can follow this guide http://ar.zu.my/how-to-store-private-key-files-in-heroku/

class Dashing.Worldcup extends Dashing.Widget
ready: ->
@refresh()
onData: (data) ->
@refresh(data)
refresh: (data) ->
nextTitle = if @get('next_matches').length > 1 then "Next Matches" else "Next Match"
@set('next-title', nextTitle)
# Set a global next match formatted time
nextMatch = @get('next_matches')[0]
@set('next-match-time', @formatDate(nextMatch.start, nextMatch.end))
# Set the formatted time for each of my team matches
match.start.formatted = @formatDate(match.start, match.end) for match in @get('my_team_matches')
#
true
formatDate: (startDate, endDate) ->
nowMoment = moment()
startMoment = moment(new Date startDate.dateTime)
endMoment = moment(new Date endDate.dateTime)
if nowMoment > startMoment and nowMoment < endMoment
"LIVE"
else if nowMoment > endMoment
"FINISHED"
else
moment(new Date startDate.dateTime).fromNow()
<h1 class="title">
<span data-bind="title"></span>
</h1>
<h4>
<span data-bind="next-title"></span>
</h4>
<div class="next-time">
<span class="team-name" data-bind="next-match-time"></span>
</div>
<table>
<tbody>
<tr class="match" data-foreach-match="next_matches" >
<td class="left-team team-col">
<span class="team-name" data-bind="match.teams[0].code" ></span>
<img data-bind-src="match.teams[0].flag_large" />
</td>
<td class="vs-col">
<span> vs </span>
</td>
<td class="right-team team-col">
<img data-bind-src="match.teams[1].flag_large" />
<span class="team-name" data-bind="match.teams[1].code" ></span>
</td>
</tr>
</tbody>
</table>
<div data-renderif="my_team_matches.length">
<h5 class="my-team-header" data-bind="myTeamTitle"></h5>
<table>
<tbody>
<tr class="match" data-foreach-match="my_team_matches" >
<td class="flag-col">
<img data-bind-src="match.my_team_opponent.flag_mid" />
</td>
<td class="name-col col-base">
<span class="team-name" data-bind="match.my_team_opponent.code" ></span>
</td>
<td class="col-base">
<span class="team-name" data-bind="match.start.formatted"></span>
</td>
</tr>
</tbody>
</table>
</div>
<p class="updated-at" data-bind="updatedAtMessage"></p>
# encoding: UTF-8
require 'google/api_client'
require 'date'
require 'time'
require 'active_support'
require 'active_support/all'
# require 'pry'
# Update these to match your own apps credentials
service_account_email = ENV['GOOGLE_SERVICE_ACCOUNT_EMAIL'] # Email of service account
key_file = ENV['GOOGLE_SERVICE_PK_FILE'] # File containing your private key
key_secret = ENV['GOOGLE_SERVICE_KEY_SECRET'] # Password to unlock private key
calendarID = ENV['WORLDCUP_CALENDAR'] # Calendar ID.
myTeam = ENV['WORLDCUP_MY_TEAM']
# Get the Google API client
client = Google::APIClient.new(:application_name => 'Platanus Dashboard',
:application_version => '0.0.1')
# Load your credentials for the service account
if not key_file.nil? and File.exists? key_file
key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, key_secret)
else
key = OpenSSL::PKey::RSA.new ENV['GOOGLE_SERVICE_PK'], key_secret
end
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/calendar.readonly',
:issuer => service_account_email,
:signing_key => key)
flags = {
"Brazil" => 'bra',
"Croatia" => 'cro',
"Mexico" => 'mex',
"Cameroon" => 'cmr',
"Spain" => 'esp',
"Netherlands" => 'ned',
"Chile" => 'chi',
"Australia" => 'aus',
"Colombia" => 'col',
"Greece" => 'gre',
"Ivory Coast" => 'civ',
"Japan" => 'jpn',
"Uruguay" => 'uru',
"Costa Rica" => 'crc',
"England" => 'eng',
"Italy" => 'ita',
"Switzerland" => 'sui',
"Ecuador" => 'ecu',
"France" => 'fra',
"Honduras" => 'hon',
"Argentina" => 'arg',
"Bosnia and Herzegovina" => 'bih',
"Iran" => 'irn',
"Nigeria" => 'nig',
"Germany" => 'ger',
"Portugal" => 'por',
"Ghana" => 'gha',
"USA" => 'usa',
"Belgium" => 'bel',
"Algeria" => 'alg',
"Russia" => 'rus',
"South Korea" => 'kor'
}
# Start the scheduler
SCHEDULER.every '60s', :first_in => 4 do |job|
# Request a token for our service account
client.authorization.fetch_access_token!
# Get the calendar API
calendar = client.discovered_api('calendar','v3')
# Start and end dates
startDate = (DateTime.now - 20.minutes).rfc3339 #+ 32.days
endDate = Date.parse("2014-07-15").rfc3339
# Get the events
events = client.execute(:api_method => calendar.events.list,
:parameters => {
:calendarId => calendarID,
:timeMin => startDate,
:timeMax => endDate,
:orderBy => 'startTime',
:singleEvents => true
}
)
# The worldcup matches
matches = events.data.items;
matches = matches.each do |match|
match[:teams] = match.summary.match(/(.*) v (.*)/i)[1,2].map do |team|
next if not flags[team]
{
flag_small: "http://img.fifa.com/images/flags/2/#{flags[team]}.png",
flag_mid: "http://img.fifa.com/images/flags/3/#{flags[team]}.png",
flag_large: "http://img.fifa.com/images/flags/4/#{flags[team]}.png",
name: team,
code: flags[team].upcase
}
end
end
# Set the event if there is one found
if events.data.items.count > 0
# Next Match
nextMatch = matches.first
nextMatches = matches.select {|match| match.start.dateTime == nextMatch.start.dateTime}
# My team matches
myTeamMatches = matches.select do |match|
if match.summary.match(/#{myTeam}/i)
match[:my_team_opponent] = match[:teams].find{|team| not team[:name].match(/#{myTeam}/i) }
true
else
false
end
end
end
# Update the dashboard
send_event('worldcup', {
next_matches: nextMatches,
my_team_matches: myTeamMatches
})
end
.widget-worldcup {
.team-name{
font-size: 14px;
}
.team-col{
vertical-align:top;
line-height:16px;
width: 43%;
padding: 4px 0;
}
.vs-col{
text-align:center;
vertical-align:center;
width: 14%;
}
.col-base{
text-align: left;
}
.flag-col{
width: 25%;
text-align: right;
padding-right: 5px;
}
.name-col{
width: 14%;
}
.left-team{
text-align:right;
}
.right-team{
text-align:left;
}
.next-time{
margin-top:-10px;
}
.my-team-header{
margin-top: 10px;
}
}
@agustinf
Copy link

Take into account that @platanus our dashboard uses half sized vertical unit
On a standard dashboard you should use

data-sizey="1"

@loicteixeira
Copy link

I don't know if it's a typo or if Google changed something on their side but I had to turn the 'Calendar API' on, not the 'Analytics API'.

@fheuer
Copy link

fheuer commented May 21, 2014

I would like to know how I have to set the GOOGLE_SERVICE_PK_FILE environment variable.

note:
Google changed their developer console interface. New steps are:

II. Click 'Create Project' and accept TOS's
III. Enable 'Calendar API' service and accept TOS's
IV. Under 'APIs & auth.' click 'Credentials' in the left-hand nav menu
V. Pick the OAuth option by clicking 'Create new Client ID'
VI. Select 'Service Account' and click 'Create Client ID'. Download of your Private key is about to start. Save it securely.
VII. Note the password for your new private key ('notasecret')
VIII. Next to the OAuth tab you will find the details for the service account you just created. Copy it's email address which will look something like this: 210987654321@developer.gserviceaccount.com - you'll need it in environmental variables later.

@loicteixeira
Copy link

You have to set it to the path to the file from the root folder of your dashing dashboard.

For example, if you copy the file to the root folder (beside the config.ru file), set GOOGLE_SERVICE_PK_FILE to '6332.......-privatekey.p12'

@blackjid
Copy link
Author

@loicteixeira you're right, that was because I just copy/past that steps... It is Calendar API

@topka
Copy link

topka commented Jun 3, 2014

Great widget! I think it worth mentioning, however, that it also requires moment.js

@blackjid
Copy link
Author

Thanks!, you're right, I just updated it mentioning about momentjs

@blackjid
Copy link
Author

@fheuer thanks... updated...

@topka
Copy link

topka commented Jun 26, 2014

Any insight if the worldcupbrazilcalendar will update their calendar? The widget just stopped working because there are no country names in the events.
Any alternatives?

@loicteixeira
Copy link

Quick Fix:

  • Subscribe to this calendar instead
  • Update the WORLDCUP_CALENDAR environment variable accordingly.
  • Update line 99 to 108 of worldcup.rb with the following code:
match[:teams] = match.summary.match(/(?:.+: )?(.*) vs (.*)/i) do |team_regex|
  team_regex[1,2].map do |team|
    next if not flags[team]
    {
      flag_small: "http://img.fifa.com/images/flags/2/#{flags[team]}.png",
      flag_mid: "http://img.fifa.com/images/flags/3/#{flags[team]}.png",
      flag_large: "http://img.fifa.com/images/flags/4/#{flags[team]}.png",
      name: team,
      code: flags[team].upcase
    }
  end
end

Notes:

  • As before, it relies on the fact that the owner of the calendar will keep updating it (by replacing "Winner of..." by the team name).
  • At the moment, the playoffs and the final won't show. I tried to make the regex a bit forgiving but because I don't know what the final string will be (whether it will follow other event titles pattern or not), it may need to be updated later.

@topka
Copy link

topka commented Jun 27, 2014

Thanks Loic!
This worked. Appreciate the quick workaround!

@loicteixeira
Copy link

Um.. The owner of the calendar (live) update the title of the event with score information which means the second team does not display properly during the match. Replace the regexp with this one /(?:.+: )?(\w+(?: \w+)*) vs (\w+(?: \w+)*)/i

@blackjid
Copy link
Author

Well, the WorldCup is over!, Germany won, totally deserved...
We'll be updating the widget for Russia 2018. Maybe is a good idea to spin over this widget with something more generic. Champion League, local leagues, etc... maybe one day

@jelofsson
Copy link

jelofsson commented May 8, 2018

@blackjid any update planned for Russia 2018?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment