Permalink
Browse files

Added websocket functionality

Added Socket.io/Node.js daemon
Updated Readme
Added a license
Added jQuery 1.7.2 minified
Updated database schema
  • Loading branch information...
DarkstarDev committed Mar 26, 2012
1 parent 291d590 commit 7495a65145fc28a1d9e6d31638dabe771f7aa27c
View
@@ -0,0 +1,7 @@
Copyright (c) 2011 Orlando Marin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -19,10 +19,8 @@ The new version is written using Zend Framework 1.11.11.
It can fetch from cache, check for staleness and fetch from woot if the cache is stale. It can fetch from woot, shirt.woot, wine.woot, sellout.woot, kids.woot, and moofi. It can also show you a historical list of items sold on woot and their progress over time.
8. **What about the Android and iOS apps you promised?**
You're not paying attention, are you?
**What works right now?**
Fetching from Woot!, caching, and fetching said cache. It will also fetch the images for each item if they're not already on the disk. It will also implement a lockout to only allow one user to refresh the cache from Woot! to avoid hammering their servers.
9. **What works right now?**
Fetching from Woot!, caching, and fetching said cache. It will also fetch the images for each item if they're not already on the disk. It will also implement a lockout to only allow one user to refresh the cache from Woot! to avoid hammering their servers.
##INSTALLATION##
1. Set up a MySQL database.
@@ -31,4 +29,7 @@ Fetching from Woot!, caching, and fetching said cache. It will also fetch the i
4. Set up a virtualhost or htaccess using public/ as the document root (I've include an .htaccess file for convenience).
5. Change group ownership of public/ to the user your web server runs as (e.g. chown -R :apache public/)
6. Change permissions of public/images/products/ to 775 (e.g. chown 0775 public/images/products/)
7. Hit your URL and rejoice! It doesn't look pretty right now but it works.
7. Configure and compile Node.js. You may need to install some dependencies (e.g. cd externals/node/; ./configure; make; sudo make install)
8. Install socket.io (e.g. npm install socket.io)
9. Run socketserver.js as a daemon (e.g. nohup node socketserver.js)
10. Hit your URL and rejoice! It doesn't look pretty right now but it works.
@@ -15,10 +15,12 @@ resources.db.adapter = "Mysqli"
resources.db.params.dbname = "dswoot"
resources.db.params.username = "dswoot"
resources.db.params.password = "secretpassword"
socketServer.url = "http://localhost:8080/this/isnt/configured"
[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
dswoot.application.debug = 1
resources.db.params.username = "dswoot"
resources.db.params.password = "secretpassword"
resources.db.params.password = "secretpassword"
socketServer.url = "http://localhost:8080/this/isnt/configured"
@@ -1,6 +1,6 @@
<?php
/**
* Class description
* Item Controller
*
* @author Orlando Marin
*/
@@ -38,6 +38,7 @@ public function viewAction()
}
$this->view->productData = $productData;
$this->view->socketServer = Zend_Registry::get('config')->socketServer->url;
}
public function listAction()
@@ -6,6 +6,7 @@
<?php $this->headLink()->appendStylesheet('/css/common.css') ?>
<?php $this->headLink()->appendStylesheet('/css/'.$this->site.'.css') ?>
<?php echo $this->headLink(); ?>
<?php echo $this->headScript(); ?>
</head>
<body>
<div id="main-wrapper">
@@ -17,4 +18,4 @@
<?php echo $this->placeholder('secondary'); ?>
</div>
</body>
</html>
</html>
@@ -1,4 +1,9 @@
<?php //var_dump($this->productData); ?>
<?php
$this->headScript()->appendScript('var site = "' . $this->site . '"; var socketServer = "' . $this->socketServer . '";');
$this->headScript()->appendFile('/js/jquery-1.7.2.min.js', 'text/javascript');
$this->headScript()->appendFile($this->socketServer . '/socket.io/socket.io.js', 'text/javascript');
$this->headScript()->appendFile('/js/product-update.js', 'text/javascript');
?>
<div id="product-container">
<div id="image">
<img alt="<?php echo str_replace('"','&quote;',$this->productData['title']); ?>" src="/images/products/<?php echo $this->productData['id'] ?><?php echo $this->productData['file_extension']; ?>" />
@@ -51,6 +56,7 @@
<a href="<?php echo $this->productData['thread']; ?>" target="_blank"><?php echo $current['comments']; ?> comment(s)</a>
</div>
</div>
<div id="time-updated"></div>
<div class="clearfix">
<div class="triangle"></div>
</div>
@@ -1,6 +1,6 @@
<?php
/**
* Class description
* This class fetches, parses, and saves Woot! RSS data
*
* @author Orlando Marin
*/
@@ -62,8 +62,8 @@ public function getCurrentProduct($site)
$data = $item;
if (!$this->_dao->getLockStatus()) {
$this->_dao->lockApplicationUpdates();
if (!$this->_dao->getLockStatus($site)) {
$this->_dao->lockApplicationUpdates($site);
try {
$this->_db->beginTransaction();
$feed = $this->_fetchWootRss($wootSite);
@@ -79,15 +79,15 @@ public function getCurrentProduct($site)
$this->_dao->insertProducts($feed['item']['id'], $feed['products']);
}
$this->_dao->insertItemHistory($feed['item']['id'], $feed['history']);
$this->_dao->unlockApplicationUpdates();
$this->_dao->unlockApplicationUpdates($site);
$this->_db->commit();
$data = $feed['item'];
$data['history'][0] = $feed['history'];
$data['products'] = $feed['products'];
} catch (Exception $e) {
//Unable to fetch data from woot or insert data.
$this->_db->rollBack();
$this->_dao->unlockApplicationUpdates();
$this->_dao->unlockApplicationUpdates($site);
throw new Exception($e->getMessage(), 500, $e);
}
}
@@ -200,6 +200,7 @@ protected function _parseWootRss($xml)
$history['comments'] = (int)$wootNamespace->comments;
$history['sold_out'] = (strtolower((string)$wootNamespace->soldout) == 'true') ? true : false;
$history['percent_sold'] = (float)$wootNamespace->soldoutpercentage;
$history['updated'] = date('c');
$images['standard'] = (string)$wootNamespace->standardimage;
$images['detail'] = (string)$wootNamespace->detailimage;
@@ -1,6 +1,6 @@
<?php
/**
* Class description
* WootFeed Data Access Object
*
* @author Orlando Marin
*/
@@ -190,7 +190,7 @@ protected function _recordReduce($records)
'comments' => $record['comments'],
'sold_out' => (bool)$record['sold_out'],
'percent_sold' => round($record['percent_sold'],2),
'updated' => $record['updated']
'updated' => date('c', strtotime($record['updated']))
));
$historyMap[] = $record['history_id'];
}
@@ -228,24 +228,25 @@ public function insertProducts($itemId, array $row)
public function insertItemHistory($itemId, array $row)
{
unset($row['updated']);
$row['id'] = sha1(uniqid());
$this->_db->insert('history', array_merge(array('item_id' => $itemId), $row));
}
public function getLockStatus()
public function getLockStatus($site)
{
$sql = 'SELECT `locked` FROM `status`';
$lock = $this->_db->fetchOne($sql);
$sql = 'SELECT `locked` FROM `status` WHERE site = ?';
$lock = $this->_db->fetchOne($sql, array($site));
return $lock;
}
public function lockApplicationUpdates()
public function lockApplicationUpdates($site)
{
$this->_db->update('status', array('locked' => 1));
$this->_db->update('status', array('locked' => 1), array('site = ?' => $site));
}
public function unlockApplicationUpdates()
public function unlockApplicationUpdates($site)
{
$this->_db->update('status', array('locked' => 0));
$this->_db->update('status', array('locked' => 0), array('site = ?' => $site));
}
}
View
@@ -25,6 +25,7 @@
#product-container {
margin: 0 auto;
width: 811px;
position: relative;
}
#secondary-wrapper {
@@ -140,4 +141,11 @@ body {
#comment-container {
font-size: 15px;
}
#time-updated {
position: absolute;
bottom: 0;
right: 0;
margin: 0 40px 10px 0;
}

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1,39 @@
var socket = io.connect(socketServer + '/' + site);
var productId;
socket.on('product', function (data) {
var productData = JSON.parse(data);
if (productId != productData.id) {
productId = productData.id;
var permalink = $('#permalink');
$('#permalink').attr('href', productData.link);
$('#title').html(productData.title);
permalink.appendTo('#title');
$('#price').text('$' + productData.price);
$('#shipping').text('+ $' + productData.shipping + ' shipping');
$('#condition dd').text(productData.condition);
$('#product dd').remove();
for(var i in productData.products) {
var product = productData.products[i];
$('<dd>'+ product.quantity + ' ' + product.name +'</dd>').insertBefore('#product .clearfix');
}
$('#comment-container a').attr('href', productData.thread);
$('#subtitle').text(productData.subtitle);
$('#teaser').text(productData.teaser);
$('#image img').attr('alt', productData.title.replace('"','\"'));
$('#image img').attr('src', '/images/products/' + productId + productData.file_extension);
}
if (productData.history[0].sold_out) {
$('#buy-link').text('Sold out!');
} else {
if (parseFloat(productData.history[0].percent_sold) < .9) {
$('#buy-link').html('<a href="'+ productData.purchase_url +'">' + "I want one! (They're almost gone!)" +'</a>');
} else {
$('#buy-link').html('<a href="'+ productData.purchase_url +'">I want one!</a>');
}
}
$('#comment-container a').text(productData.history[0].comments + ' comment(s)');
var timeUpdated = new Date(productData.history[0].updated);
$('#time-updated').text('last updated at ' + timeUpdated.toLocaleTimeString());
});
View
@@ -0,0 +1,128 @@
/**
* @var siteUrl string
* @example subdomain.domain.tld
*/
var siteUrl = "not.configured.tld";
var io = require('socket.io').listen(8080),
http = require('http');
//All connections to be used
var connections = {
woot: io.of('/woot')
.on('connection', function (socket) {
socket.join('woot');
}
),
home: io.of('/home')
.on('connection', function (socket) {
socket.join('home');
}
),
kids: io.of('/kids')
.on('connection', function (socket) {
socket.join('kids');
}
),
shirt: io.of('/shirt')
.on('connection', function (socket) {
socket.join('shirt');
}
),
sellout: io.of('/sellout')
.on('connection', function (socket) {
socket.join('sellout');
}
),
wine: io.of('/wine')
.on('connection', function (socket) {
socket.join('wine');
}
),
moofi: io.of('/moofi')
.on('connection', function (socket) {
socket.join('moofi');
}
)
}
//Mimic an AJAX request
var XMLHttpRequestHeader = {
'X-Requested-With': 'XMLHttpRequest'
};
//Request options
var options = {
woot: {
host: siteUrl,
port: 80,
path: '/woot',
headers: XMLHttpRequestHeader
},
kids: {
host: siteUrl,
port: 80,
path: '/kids',
headers: XMLHttpRequestHeader
},
home: {
host: siteUrl,
port: 80,
path: '/home',
headers: XMLHttpRequestHeader
},
shirt: {
host: siteUrl,
port: 80,
path: '/shirt',
headers: XMLHttpRequestHeader
},
sellout: {
host: siteUrl,
port: 80,
path: '/sellout',
headers: XMLHttpRequestHeader
},
wine: {
host: siteUrl,
port: 80,
path: '/wine',
headers: XMLHttpRequestHeader
},
moofi: {
host: siteUrl,
port: 80,
path: '/moofi',
headers: XMLHttpRequestHeader
}
};
//Fetch data every 30 seconds for all sites
var siteData = {};
setInterval((function() {
for (var i in options) {
var fetch = function(site, data) {
var req = http.get(options[site], function(res) {
res.setEncoding('utf8');
res.on('data', function(chunk) {
//If data is different, send it
if (data[site] !== chunk) {
data[site] = chunk;
connections[site].emit('product', data[site]);
}
});
});
req.on('error', function(e) {
console.log('ERROR: ' + JSON.stringify(e));
});
}(i, siteData);
}
}),
30000
);
Oops, something went wrong.

0 comments on commit 7495a65

Please sign in to comment.