Skip to content

Commit

Permalink
Merge pull request asmuth#32 from kazjote/user_feed
Browse files Browse the repository at this point in the history
User feed. LOVE IT! :)
  • Loading branch information
Paul Asmuth committed Jan 11, 2012
2 parents 263f429 + 3a49291 commit 1eae778
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 83 deletions.
2 changes: 2 additions & 0 deletions lib/fnordmetric/app.rb
Expand Up @@ -97,6 +97,8 @@ def current_namespace

events = if params[:type]
current_namespace.events(:by_type, :type => params[:type])
elsif params[:session_key]
current_namespace.events(:by_session_key, :session_key => params[:session_key])
else
find_opts = { :limit => 100 }
find_opts.merge!(:since => params[:since].to_i+1) if params[:since]
Expand Down
128 changes: 68 additions & 60 deletions lib/fnordmetric/event.rb
@@ -1,78 +1,86 @@
class FnordMetric::Event
module FnordMetric
class Event

attr_accessor :time, :type, :event_id
attr_accessor :time, :type, :event_id

#def self.track!(event_type, event_data)
#end
#def self.track!(event_type, event_data)
#end

def self.all(opts)
range_opts = { :withscores => true }
range_opts.merge!(:limit => [0,opts[:limit]]) if opts[:limit]
opts[:redis].zrevrangebyscore(
"#{opts[:namespace_prefix]}-timeline",
'+inf', opts[:since]||'0',
range_opts
).in_groups_of(2).map do |event_id, ts|
next if event_id.blank?
find(event_id, opts).tap{ |e| e.time = ts }
def self.all(opts)
range_opts = { :withscores => true }
range_opts.merge!(:limit => [0,opts[:limit]]) if opts[:limit]
opts[:redis].zrevrangebyscore(
"#{opts[:namespace_prefix]}-timeline",
'+inf', opts[:since]||'0',
range_opts
).in_groups_of(2).map do |event_id, ts|
next if event_id.blank?
find(event_id, opts).tap{ |e| e.time = ts }
end
end
end

def self.by_type(_type, opts)
opts[:redis].lrange(
"#{opts[:namespace_prefix]}-type-#{_type}",
0, 200).map do |event_id|
next if event_id.blank?
find(event_id, opts).tap{ |e| }
def self.by_type(_type, opts)
opts[:redis].lrange(
"#{opts[:namespace_prefix]}-type-#{_type}",
0, 200).map do |event_id|
next if event_id.blank?
find(event_id, opts).tap{ |e| }
end
end
end

def self.find(event_id, opts)
self.new(event_id, opts).tap do |event|
event.fetch!
def self.by_session_key(_session_key, opts)
session = Session.find(_session_key, opts)
session.fetch_event_ids!(200).reject(&:blank?).map do |event_id|
find(event_id, opts)
end
end
end

def initialize(event_id, opts)
@opts = opts
@event_id = event_id
end
def self.find(event_id, opts)
self.new(event_id, opts).tap do |event|
event.fetch!
end
end

def fetch!
@data = JSON.parse(fetch_json).tap do |event|
@type = event.delete("_type")
def initialize(event_id, opts)
@opts = opts
@event_id = event_id
end
end

def fetch_json
@opts[:redis].get(redis_key) || "{}"
end
def fetch!
@data = JSON.parse(fetch_json).tap do |event|
@type = event.delete("_type")
end
end

def redis_key
[@opts[:redis_prefix], :event, @event_id].join("-")
end
def fetch_json
@opts[:redis].get(redis_key) || "{}"
end

def session_key
@data["_session"] ? Digest::MD5.hexdigest(@data["_session"]) : nil
end
def redis_key
[@opts[:redis_prefix], :event, @event_id].join("-")
end

def id
@event_id
end

def data(key=nil)
key ? @data[key.to_s] : @data
end
def session_key
@data["_session"] ? Digest::MD5.hexdigest(@data["_session"]) : nil
end

alias :[] :data
def id
@event_id
end

def data(key=nil)
key ? @data[key.to_s] : @data
end

def to_json
@data.merge!(
:_type => @type,
:_session_key => session_key,
:_eid => @event_id,
:_time => @time
)
end
alias :[] :data

end
def to_json
@data.merge!(
:_type => @type,
:_session_key => session_key,
:_eid => @event_id,
:_time => @time
)
end
end
end
1 change: 1 addition & 0 deletions lib/fnordmetric/namespace.rb
Expand Up @@ -77,6 +77,7 @@ def sessions(_ids, opts={})
def events(_ids, opts={})
return FnordMetric::Event.all(extend_opts(opts)) if _ids == :all
return FnordMetric::Event.by_type(opts.delete(:type), extend_opts(opts)) if _ids == :by_type
return FnordMetric::Event.by_session_key(opts.delete(:session_key), extend_opts(opts)) if _ids == :by_session_key
end

def method_missing(m, *args, &block)
Expand Down
5 changes: 3 additions & 2 deletions lib/fnordmetric/session.rb
Expand Up @@ -89,6 +89,7 @@ def expire(time)

def add_event(event)
@redis.zadd(redis_key(:events), event[:_time], event[:_eid])

add_data(:_picture, event[:url]) if event[:_type] == "_set_picture"
add_data(:_name, event[:name]) if event[:_type] == "_set_name"
add_event_data(event) if event[:_type] == "_set_data"
Expand Down Expand Up @@ -125,7 +126,7 @@ def fetch_meta_key(key, value)

def fetch_event_ids!(since=-1)
# FIXME: use WITHSCORE to get the timestamps and return event objects
@event_ids = @redis.zrange(redis_key(:events), 0, since)
@event_ids = @redis.zrevrange(redis_key(:events), 0, since)
end

end
end
7 changes: 5 additions & 2 deletions pub/fnordmetric.css
Expand Up @@ -107,11 +107,14 @@ text-shadow: 1px 0px 2px rgba(255, 255, 255, 1);


ul.session_list{ list-style-type:none; margin:0; padding:9px 16px 0 11px; }
ul.session_list li{ color:#0A0A0A; margin-bottom:10px; height:18px; overflow:hidden; line-height:18px; padding:4px; }
ul.session_list li{ color:#0A0A0A; height:32px; overflow:hidden; line-height:18px; padding:4px; }
ul.session_list li:hover{ background:#eee; cursor:pointer; }
ul.session_list li input{ float: left; margin-right: 7px; }
ul.session_list li .picture{ height:18px; width:18px; float:left; background:#333; overflow:hidden; }
ul.session_list li .name{ float:left; width:120px; overflow:hidden; margin-left:10px; font-size:12px; }
ul.session_list li .time{ float:right; width:40px; overflow:hidden; text-align:right; font-size:10px; }
ul.session_list li .history{ float:right; color:#999; font-size:10px; line-height: 12px;}
ul.session_list li .history:hover{ color:#333; text-decoration:underline; }

.sessions_feed{ min-width:300px; min-height:100px; float:left; }
.sessions_feed ul.feed_inner{ margin:5px 15px; min-height:100px; padding:0px; }
Expand Down Expand Up @@ -139,4 +142,4 @@ ul.event_type_list li .history:hover{ color:#333; text-decoration:underline; }
height: 0;
}

.highcharts-series circle{ stroke-width:1px; }
.highcharts-series circle{ stroke-width:1px; }
77 changes: 63 additions & 14 deletions pub/fnordmetric.js
Expand Up @@ -739,7 +739,7 @@ var FnordMetric = (function(){
).append(listElem);

var eventsPolledUntil = false;
var eventsFilter = [];
var eventsFilter = {uncheckedTypes: [], checkedSessions: []};
var sessionData = {};
var pollRunning = true;

Expand Down Expand Up @@ -777,10 +777,11 @@ var FnordMetric = (function(){
});
};

function loadEventHistory(event_type){
function loadEventHistory(params){
feedInnerElem.html('');
$.ajax({
url: FnordMetric.p + '/' + currentNamespace+'/events?type='+event_type,
url: FnordMetric.p + '/' + currentNamespace+'/events',
data: params,
success: function(_data, _status){
var data = JSON.parse(_data).events;
for(var n=data.length; n >= 0; n--){
Expand Down Expand Up @@ -813,14 +814,19 @@ var FnordMetric = (function(){
});
};

function setCheckboxesCheckedState(types_state, sessions_state) {
$('.event_type_list .event_type input').attr('checked', types_state);
$('.session_list .session input').attr('checked', sessions_state);
}

function addEventType(type, display){
typeListElem.append(
$('<li class="event_type"></li>').append(
$('<span class="history"></span>').html('history')
.click(function(){
$('.event_type_list .event_type input').attr('checked', false);
setCheckboxesCheckedState(true, true);
$('input', $(this).parent()).attr('checked', true);
updateEventFilter(); loadEventHistory(type);
updateEventFilter(); loadEventHistory({type: type});
})
).append(
$('<input type="checkbox" />').attr('checked', true)
Expand All @@ -838,7 +844,15 @@ var FnordMetric = (function(){
_unchecked_types.push($(v).attr('rel'));
}
});
eventsFilter = _unchecked_types;
eventsFilter.uncheckedTypes = _unchecked_types;

var _checked_sessions = [];
$('ul.session_list li.session').each(function(i,v){
if($('input', v).attr('checked')){
_checked_sessions.push($(v).data().session);
}
});
eventsFilter.checkedSessions = _checked_sessions;
}

function doEventsPoll(){
Expand All @@ -854,32 +868,58 @@ var FnordMetric = (function(){
return (function(_data, _status){
var data = JSON.parse(_data)
var events = data.events;
var timout = 1000;
var timeout = 1000;
var maxevents = 200;
var passesFiltering = function(event_data) {
var passes_type_filtering = false;
var passes_session_filtering = false;
if(eventsFilter.uncheckedTypes.indexOf(event_data._type) == -1) {
if(parseInt(v._time)<=eventsPolledUntil) {
passes_type_filtering = true;
}
}
if(!passes_type_filtering) return false;

if(eventsFilter.checkedSessions.length == 0){
return true; // No filter set - show all events
} else {
if(event_data._session_key){
if(eventsFilter.checkedSessions.indexOf(event_data._session_key) >= 0){
return true; // Filter set and match
} else {
return false; // Filter set but no match
}
} else {
return false; // Filter set but event is not associated with session
}
}
}

if(events.length > 0){
timeout = 200;
eventsPolledUntil = parseInt(events[0]._time)-1;
}
for(var n=events.length-1; n >= 0; n--){
var v = events[n];
if(eventsFilter.indexOf(v._type) == -1){
if(parseInt(v._time)<=eventsPolledUntil){
renderEvent(v);
}
if(passesFiltering(v)) {
renderEvent(v);
}
};
var elems = $("p", feedInnerElem);
for(var n=maxevents; n < elems.length; n++){
$(elems[n]).remove();
}
if(pollRunning){
window.setTimeout(doEventsPoll(), timout);
window.setTimeout(doEventsPoll(), timeout);
}
});
};

function updateSession(session_data){
sessionData[session_data.session_key] = session_data;
var session_key = session_data.session_key;
if(!sessionData[session_key]){
updateEventFilter()
}
sessionData[session_key] = session_data;
renderSession(session_data);
}

Expand Down Expand Up @@ -919,11 +959,20 @@ var FnordMetric = (function(){

listElem.append(
$('<li class="session"></li>').append(
$('<input type="checkbox" />').click(function(){ updateEventFilter(); })
).append(
$('<div class="picture"></div>').html(session_picture)
).append(
$('<span class="name"></span>').html(session_name)
).append(
$('<span class="time"></span>').html(session_time)
).append(
$('<span class="history"></span>').html('history')
.click(function(){
setCheckboxesCheckedState(true, false);
$('input', $(this).parent()).attr('checked', true);
updateEventFilter(); loadEventHistory({session_key: session_data["session_key"]});
})
).attr('data-session', session_data["session_key"])
);

Expand Down

0 comments on commit 1eae778

Please sign in to comment.