Jeeves is an easy-to-use framework used to create narrowcasting systems. Jeeves is built on top of Meteor, making it a powerful tool for creating data dependent narrowcasting systems.
Jeeves, your personal valet, works by providing developers and enthusiasts alike an easy interface for creating 'slides' and managing the system. Some of the features included with Jeeves are:
- Slide configuration. (when do you want a slide to be shown? For how long? etc.)
- Screen manipulation. (instantly switch slides on registered screen)
- Easy and rapid slide development using Meteor.
- Predefined templates to use with your slides.
A slide is what we define to be the 'view' of the application. You can show any kind of information you want on your slide, be it the number of people currently visiting your website or the number of purchases done through your ecommerce system. The slides can be developed with HTML/CSS/JS.
Slides are, in a way, the plugins of Jeeves. This means that you can download slides other people created and add them to your own installation to fully personalise your narrowcasting system.
You'll need to have nodejs installed before starting.
First, you will need to download and cd to the source code by cloning the git repository:
git clone [email protected]:peperzaken/jeeves.git
cd Jeeves
Secondly, you will need Meteor to run the app with. Note that Jeeves was tested to work with Meteor 1.1. Although it should work with lower versions, to be completely sure just install the newest Meteor version:
curl https://install.meteor.com/ | sh
After installing all the required prerequisites you'll be able to run Jeeves by running:
meteor
This will make meteor download the required packages and start Jeeves afterwards. Now if you'll go to http://localhost:3000, you'll be greeted by Jeeves!
Before you start developing slides for Jeeves you'll need to learn about Meteor and its practices. Here are some resources which will get you started:
We've automated some best practices for you and created a CLI for Jeeves. This tool will easily create all the necessary files for you to create your own slide. Inside of the root folder, execute the following commands:
cd .cli/
python jeeves.py create_slide <name of your slide>
This will create the necessary files for you in client/slides/<name of your slide>
and add your slide to the router. It will also create a directory in public/slides/<name of your slide>/
in which you can put your assets such as js libs, images, etc. Now go ahead and edit the files to create your own slide!
There's two ways of creating slides. The first being making a slide without using external data-sources. Creating a slide like this will only require the files created by the CLI tool. The second way to create a slide is to create one with external data-sources, more information about this method can be found further down this page.
For more information, in particular about the possibilities of Meteor, consult the Meteor documentation.
The directory structure as used by Meteor has been preserved as well as possible, expanding on their best practices where necessary. (You can read more about Meteor and it's directory structure here)
Client-side structure:
client/ --> Files in this directory will be sent to the client.
├── admin/ --> Files regarding the admin panel are stored here.
├── index.html
├── lobby/ --> Index screen for registering your application
├── main.global-css.css --> This file is used for global CSS values.
├── main.global-helpers.js --> This file is used for global JS functions/values
├── slides/ --> This directory is used to store all the slides. Note: Only place DIRECTORIES directly into this map!
│ ├── beertime/
│ │ ├── beertime.css --> Slide file for storing CSS.
│ │ ├── beertime.html --> Slide file for HTML.
│ │ └── beertime.js --> Slide file for JS functions.
│ ├── beertime-time/
│ ├── druppel/
│ ├── global/ --> Directory for storing global templates (clock, users, etc.)
│ ├── jiraProgress/
│ ├── jiraReleases/
│ ├── toggl/
│ └── users/
└── welcome/
Server-side structure:
server/ --> Files in this directory will NOT be sent to the client.
├── main.js --> This script is used to start up and initialise the app correclty.
└── slides/ --> This directory is used to store all data dependent slides.
├── druppel/
│ └── sources.js --> File containing data calls to external services.
├── jiraProgress/
├── jiraReleases/
├── toggl/
└── users/
Note that there's a public/
map which serves all files in it to the client. For every slide you make you should also create a directory in public/slides/nameOfYourSlide/
contain at least one file (you can use a .gitkeep
file for ease of use). This should be done for the admin system to recognise how many slides the system has and which one are still available for installation.
Sometimes you want to use data that comes from elsewhere, doing so for Jeeves is relatively simple. There are multiple ways to get data from external sources but for now we'll focus on polling as our data acquisition method. We'll take the Jira slide as an example.
First, let's create a collection in lib/database.js
to store our data in.
//#lib/database.js
/**
* Collection for storing Jira data
* @type {Meteor.Collection}
*/
Jira = new Meteor.Collection( 'jira' );
And let's create a directory in server/slides/jiraProgress
with a sources.js
file to put our code in. We'll include some libraries from NPM which we'll use to query the API.
//#server/slides/jiraProgress/sources.js
var JiraApi = Meteor.require('jira').JiraApi;
var jira = new JiraApi('https', 'jira.mycompany.com', '8080', 'my-account', 'my-pw', '2');
And to put it all together, here's our function:
//#server/slides/jiraProgress/sources.js
function getJiraProgress() {
var onComplete = Meteor.bindEnvironment(function( err, res ) {
if (err) log.error(err);
Jira.update(
{ date: new Date().toDateString() },
{ $set: { progress: issues } },
{ upsert: true }
);
}, function( err ) {
log.error(err);
});
var options = {
maxResults: 4,
fields: ['*navigable']
};
jira.searchJira('status = "In Progress" ORDER BY updatedDate', options, onComplete);
}
Note that in order to run meteor methods inside asynchronous methods, such as searchJira
in the example above, you'll have to wrap your code inside Meteor.bindEnvironment
. (learn more about this mind-fuck here)
But we're not done yet since the method isn't run anywhere! We'll make use of Meteor's internal Meteor.startup
to setup the polling.
Meteor.startup( function() {
getJiraProgress();
Meteor.setInterval( function() {
getJiraProgress();
}, 60 * 1000);
});
By calling Meteor.startup
, we're making sure that this code gets run on startup of the server, making sure that our polling is set-up correctly.
And with that we've set-up our first external data-source.
Though we don't have access to it on the client yet. To do that, we'll have to set up the necessary publish/subscribe functions, which will look something like this:
//#server/main.js
Meteor.publish('jira', function() {
return Jira.find( { date: new Date().toDateString() } );
});
//#client/slides/jiraProgress/jiraProgress.js
Meteor.subscribe( 'jira' );
Jeeves comes with a set of administration tools which you can use to customise and control your own installation. In order to set-up your installation you'll have to add slides. This can be done via the administration tool, available at /admin
For first time users, an admin account has been created with the username admin
and password admin
. Please be sure to change this password as soon as possible.
During the development of Jeeves, a wide array of slides have been developed. Some of the developed slides include:
Although we'd love to share these slides with you, we were unable to open source them. If, however, you'd like to have these slides, please contact me at [email protected]
To show you the complete process of developing a slide, we're going to create a slide which shows tweets which contain a predefined hashtag. Let's get started!
First off, let's run the generator from the root of our project which will create most of the files for us.
$ cd .cli/
$ python jeeves.py create_slide twitter
Done creating files for "twitter" slide.
What happened was the following:
- A directory with client templates was created at
client/slides/twitter/
- A directory was created at
public/slides/twitter/
containing a.gitkeep
file. - A route to the twitter template was added to
lib/routes.js
making the resource available at/twitter
This process will create all files needed for creating a slide which doesn't need access to an external data source. We however do need to integrate the Twitter API, so for that we're going to create a javascript file at server/slides/twitter
called source.js
Next we'll need a place to store our data, so we'll go ahead and create a collection in lib/database.js
which we'll use to store the relevant tweets, just like so:
/**
* Collection for storing tweets
* @type {Meteor.Collection}
*/
Tweets = new Meteor.Collection( 'tweets' );
For convenience we're going to use the twitter package from npm to query for tweets. To do this we're going to add the npm package to the packages.json
file in the root directory like so:
{
"twitter": "0.2.9"
}
This will make sure that the twitter npm package is available to Meteor. Next, we're going to use it to obtain the tweets. We're going to open the sources.js
and write our program.
var twitter = Meteor.require('twitter');
var twit = new twitter({
consumer_key: 'YOUR KEY HERE',
consumer_secret: 'YOUR SECRET HERE',
access_token_key: 'YOUR KEY HERE',
access_token_secret: 'YOUR SECRET HERE'
});
var onComplete = Meteor.bindEnvironment(function( tweet ) {
// Upsert the received tweet
Tweets.upsert( { date: new Date().toDateString() }, { $push: { tweets: tweet } } );
}, function( err ) {
log.error("Error while getting tweets:", err);
});
// Set up the twitter stream
twit.stream( 'filter', { track: '#foobar' }, function( stream ) {
stream.on( 'data', onComplete );
});
Here we setup a streaming connection with the Twitter API, receiving the tweets with #foobar
when they're available instead of polling. On receive we pass the tweet to our onComplete
function which saves the tweet in the database.
Now that we've setup the data connection with Twitter, we'd like to show the data to our user. To do this we'll have to first setup the publish/subscribe connections. Open server/main.js
and add the following line:
Meteor.publish( 'tweets', function() {
return Tweets.find();
});
This will setup the publishing of the tweet collection. To subscribe to this collection, open client/slides/twitter/twitter.js
and add the following line:
Meteor.subscribe( 'tweets' );
By doing this you're making sure that you'll be able to receive the data on the client.
Next, we'll need two helper functions. One function which grabs the last five tweets and one function which shows us the amount of tweets in our collection.
Template.twitter.tweetCount = function() {
var tweets = Tweets.findOne( { date: new Date().toDateString() });
return tweets ? tweets.tweets.length : 0
};
Template.twitter.lastTweets = function() {
var tweets = Tweets.findOne( { date: new Date().toDateString() });
if( tweets ) {
return tweets.tweets.slice( Math.max( tweets.tweets.length - 5,0 ) );
}
};
We're almost done with developing the slide. Now we just need to create a HTML template for the data to reside in. You can do this anyway you'd like, but just for teaching purposes I've created a really simple template in client/slides/twitter/twitter.html
:
<template name="twitter">
<div id="outer-twitter" class="container-fluid inactive">
<div class="row-fluid top-row-twitter">
<div class="col-md-12 header-text-twitter">twitter</div>
</div>
<div class="row-fluid center-row center-row-twitter">
<div class="col-md-12">
{{#isolate}}
<h1 class="tweet-count">Number of tweets: {{ tweetCount }}</h1>
<div class="last-tweets">
<ul>
{{#each lastTweets}}
<li>{{text}}</li>
{{/each}}
</ul>
</div>
{{/isolate}}
</div>
</div>
<div class="row-fluid bottom-row">
<div class="col-md-11 app-stars">
{{> slideCountdown}}
</div>
<div class="col-md-1">
{{> clock}}
</div>
</div>
{{#isolate}}
{{current_slide "twitter"}}
{{/isolate}}
</div>
</template>
And we're done developing the slide! You can now make it available to the scheduler by browsing to /admin
and using the "Add slide" functionality.
Dennis Stolmeijer for taking the time to fix this broken project. You will be rewarded with beer when near Peperzaken HQ