r1248 - in wit: . css js js/components
Author: smaisonneuve Date: 2015-04-23 16:01:40 +0000 (Thu, 23 Apr 2015) New Revision: 1248 Url: http://forge.nuiton.org/projects/sandbox/repository/revisions/1248 Log: [Wit] - Add user activity monitoring Added: wit/css/timeline.less wit/css/userActivity.less wit/js/UserActivity.js wit/js/UserActivityService.js wit/js/components/Timeline.js Modified: wit/index.html wit/js/main.js wit/package.json Added: wit/css/timeline.less =================================================================== --- wit/css/timeline.less (rev 0) +++ wit/css/timeline.less 2015-04-23 16:01:40 UTC (rev 1248) @@ -0,0 +1,45 @@ +@timeLabelsDefaultHeight: 40px; + +@timelineDefaultWidth: 100%; +@timelineDefaultHeight: 100px; + +.timeline { + + .sessions { + display: flex; + + width: @timelineDefaultWidth; + height: @timelineDefaultHeight; + + background: red; + + .slice { + height: @timelineDefaultHeight; + line-height: @timelineDefaultHeight; + + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + cursor: pointer; + + color:white; + } + } + + .timeLabels { + + height: @timeLabelsDefaultHeight; + line-height: @timeLabelsDefaultHeight; + + color: black; + padding: 0 5px; + + .start { + float:left; + } + .end { + float:right; + } + } +} Added: wit/css/userActivity.less =================================================================== --- wit/css/userActivity.less (rev 0) +++ wit/css/userActivity.less 2015-04-23 16:01:40 UTC (rev 1248) @@ -0,0 +1,14 @@ +.userActivity { + display: flex; + flex-direction: column; + justify-content: center; + + height: 100%; + width: 100%; + + h1 { + position: absolute; + top: 50px; + color: black; + } +} \ No newline at end of file Modified: wit/index.html =================================================================== --- wit/index.html 2015-04-22 19:39:12 UTC (rev 1247) +++ wit/index.html 2015-04-23 16:01:40 UTC (rev 1248) @@ -13,16 +13,30 @@ <script type="text/jsx" src="js/components/ActionsBar.js"></script> <script type="text/jsx" src="js/components/FilterBar.js"></script> <script type="text/jsx" src="js/components/FilterLogs.js"></script> + <script type="text/jsx" src="js/components/Timeline.js"></script> <script type="text/jsx" src="js/App.js"></script> <script type="text/jsx" src="js/Rapport.js"></script> + <script type="text/jsx" src="js/UserActivity.js"></script> <script type="text/jsx" src="js/main.js"></script> <!-- For Less --> <link rel="stylesheet" href="./node_modules/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet/less" type="text/css" href="css/style.less" /> + <link rel="stylesheet/less" type="text/css" href="css/timeline.less" /> + <link rel="stylesheet/less" type="text/css" href="css/userActivity.less" /> <script type="text/javascript" src="node_modules/less/dist/less.min.js"></script> + <script type="text/javascript"> + function userActivity () { + var gui = require('nw.gui'); + var new_win = gui.Window.open('index.html#userActivity', { + position: 'center', + toolbar: true + }); + } + </script> + </head> <body> <div class="container-fluid"> @@ -38,6 +52,7 @@ <li class="pull-left"><a href="#rapport">Rapport</a></li> <li class="pull-left"><a href="#">Tags</a></li> <li class="pull-left"><a href="#">Options</a></li> + <li class="pull-left" onClick="userActivity()"><a href="#">Help !</a></li> </ul> </div> </div> Added: wit/js/UserActivity.js =================================================================== --- wit/js/UserActivity.js (rev 0) +++ wit/js/UserActivity.js 2015-04-23 16:01:40 UTC (rev 1248) @@ -0,0 +1,13 @@ +/** @jsx React.DOM */ + +var UserActivity = React.createClass({ + + render: function() { + return ( + <div className="userActivity"> + <h1> What the hell did i do today ? </h1> + <Timeline /> + </div> + ); + } +}); \ No newline at end of file Added: wit/js/UserActivityService.js =================================================================== --- wit/js/UserActivityService.js (rev 0) +++ wit/js/UserActivityService.js 2015-04-23 16:01:40 UTC (rev 1248) @@ -0,0 +1,128 @@ +var x11 = require('x11'); +var xprop = require('xprop'); +var moment = require('moment'); + +var windowSessions = []; + +const _NET_ACTIVE_WINDOW = 334; + +exports.getSessions = function (startDate, endDate) { + var result = windowSessions, m; + + if (startDate) { + m = moment(startDate); + result = result.filter(function(s) { + return m.isBefore(s.startDate); + }); + } + + if (endDate) { + m = moment(endDate); + result = result.filter(function(s) { + return !s.endDate || m.isAfter(s.endDate); // if no endDate, it means that this is the current session + }); + } + + return result; +} + +/* Inspired by : +http://stackoverflow.com/questions/19840459/linux-get-notification-on-focuse... window-change/19847998#19847998 +*/ +x11.createClient(function(err, display) { + var X = display.client; + + X.ChangeWindowAttributes(display.screen[0].root, { + eventMask:x11.eventMask.PropertyChange + }); + + // Init windows session with the currently active window + + updateWindowSessions(X, display.screen[0].root) + + // Listen to client display events to identify when the currently active window has change + + X.on('event', onActiveWindowChange.bind(null, X)); +}); + + +var onActiveWindowChange = function (X, ev) { + if(ev.name == 'PropertyNotify') { + + X.GetAtomName(ev.atom, function(err, name) { + + // if the property change refers to the currently active window, retrieve new active window name + + if (name == '_NET_ACTIVE_WINDOW') { + updateWindowSessions(X, ev.wid) + } + }); + } +}; + +var updateWindowSessions = function (X, wid) { + return getWindowId(X, wid) + .then(getWindowName) + .then(createNewSession); +}; + +var getWindowId = function (X, wid) { + return new Promise(function (resolve, reject) { + + X.GetProperty(0, wid, _NET_ACTIVE_WINDOW, X.atoms.WINDOW, 0, 4, function(err, prop) { + if (err) { + + console.log("Couldn't retrieve active window property"); + reject(); + + } else { + + resolve(prop.data.readUInt32LE(0)); + + } + }); + }); +} + +var getWindowName = function (windowId) { + return new Promise(function (resolve, reject) { + + xprop({prop:'_NET_WM_NAME', id:windowId}, function(err, properties) { + + if (err) { + + console.log("Couldn't retrieve window name for id :", windowId); + reject(); + + } else { + + resolve(properties.length && properties[0].value); + + } + }); + }); +}; + +var createNewSession = function (windowName) { + if (windowSessions.length) { + var now = Date.now(); + var last = windowSessions[windowSessions.length - 1]; + last.endDate = moment(); + last.duration = last.endDate.diff(last.startDate); + } + + // Check if a session corresponding to the given name already exists, + // so that we can retrieve some properties + + var prev = getSessionByName(windowName); + + windowSessions.push({ + name: windowName, + color: prev && prev.color || "#" + (Math.floor(Math.random() * (999 - 100 + 1)) + 100), + startDate: moment() + }); +}; + +var getSessionByName = function (name) { + return windowSessions.filter(function (s) { return s.name == name })[0]; +} \ No newline at end of file Added: wit/js/components/Timeline.js =================================================================== --- wit/js/components/Timeline.js (rev 0) +++ wit/js/components/Timeline.js 2015-04-23 16:01:40 UTC (rev 1248) @@ -0,0 +1,62 @@ +var moment = require('moment'); + +var Timeline = React.createClass({ + poolingInterval: 5 * 1000, + + getInitialState: function() { + return this._refreshSessions(); + }, + + componentDidMount: function () { + var self = this; + setInterval(function () { + self.setState(self._refreshSessions()); + }, this.poolingInterval); + }, + + _refreshSessions: function () { + // Retrieve window focus activity + + var startDate = moment().subtract(1, "m"), + endDate = moment(); + + var sessions = user.getSessions(startDate, endDate); + + return { + sessions: sessions, + startDate: startDate, + endDate: endDate + }; + }, + + render: function() { + var startDate = this.state.startDate; + var endDate = this.state.endDate; + + // Build timeline + + var items = this.state.sessions.map(function(session) { + var style = { + background: session.color, + width: session.duration ? (session.duration / endDate.diff(startDate) * 100 ) +"%":"initial", + flexGrow: !session.duration ? "1":"0" + }; + var title = session.name; + return ( + <div className="slice" style={style} title={title}>{title}</div> + ); + }); + + return ( + <div className="timeline"> + <div className="timeLabels"> + <div className="start"> | An hour ago...</div> + <div className="end">Now |</div> + </div> + <div className="sessions"> + {items} + </div> + </div> + ); + } +}); \ No newline at end of file Modified: wit/js/main.js =================================================================== --- wit/js/main.js 2015-04-22 19:39:12 UTC (rev 1247) +++ wit/js/main.js 2015-04-23 16:01:40 UTC (rev 1248) @@ -1,9 +1,21 @@ /** @jsx React.DOM */ var timer = require('./js/TimerService.js'); +var user = require('./js/UserActivityService.js'); (function() { - var app = location.hash == "#rapport" ? <Rapport/> : <App/>; + var app, hash = location.hash; + switch (hash) { + case "#rapport": + app = <Rapport/>; + break; + case "#userActivity": + app = <UserActivity/>; + break; + default: + app = <App/> + break; + } React.render( app, Modified: wit/package.json =================================================================== --- wit/package.json 2015-04-22 19:39:12 UTC (rev 1247) +++ wit/package.json 2015-04-23 16:01:40 UTC (rev 1248) @@ -15,10 +15,12 @@ "moment": "^2.10.2", "nedb": "^1.1.2", "react": "^0.13.1", - "x11": "^1.0.3" + "x11": "^1.0.3", + "xprop": "^0.0.0" }, "devDependencies": { "grunt": "^0.4.5", "grunt-node-webkit-builder": "^1.0.2" - } + }, + "chromium-args":"--javascript-harmony" }
participants (1)
-
smaisonneuveďź users.nuiton.org