Tiling Window Managers and Productivity

On a recent walk whilst listening to one of my favorite podcast, the Stack Overflow Podcast, Joel and Jeff started talking about the productivity of dual monitors.

It is a [fairly] undisputed fact that having a second monitor makes any worker more productive. The reason for this is quite simple, more desktop real-estate means being able to see more applications at a time. This makes even more sense when you factor in all the “tabbing” between applications that you save, sometimes in my case this means tabbing multiple times because I had forgotten what I had just read.

Jeff made the point that often times he doesn’t bother with moving and resizing windows, and usually just brings it to the monitor he wants and then makes it full-screen.  His point was that this makes him even more productive, because you no longer worry about placement of windows.  I couldn’t agree more.

This is exactly the place where tiling window managers come in to play.  A tiling window manager is one in which you do spend your time moving and resizing windows, as they are automatically placed for you.  This allows you to focus on your work at hand, rather than tabbing to see all your windows, or moving and resizing to see two at once.

When you combine the power of a tiling window manager with dual screens, something magical happens.  Now you have the increased desktop space in addition to no longer having to manage your windows.  In my tiling window manager, XMonad, you treat each monitor as a separate “window” into your virtual desktops.  By this I mean that you setup windows in a particular virtual desktop, and then have the ability to show that virtual desktop on whichever monitor you choose.

03-2009 XMonad Screenshot

03-2009 XMonad Screenshot

I cannot emphasize enough the benefits of both dual screens and a tiling window manager.  Some good managers for Linux are:

Easy Email Disk Space Alerts

The other day I ran into an issue where a server of mine ran out of disk space. The server is part of a surveillance system I have and collects ~2 gigs of jpeg’s a day, and so can fill up every couple of months if I’m not careful. Not having an alert system finally caught up with me and I was greeted when I returned from work with a couple dozen failure e-mails from cron, indicating the system had run out of space.

Not to be caught in this situation again, I’ve added the below to my crontab. It’s a simple bash command that will e-mail me if my server gets over 80% utilized. I use the second line to send an SMS message to myself if the server becomes 90% or over utilized.

#Email when storage gets low
00 *  * * * full=`/bin/df | /bin/egrep "(100\%|[89][0-9]\%)"` && echo $full | /usr/bin/mail -s "[$HOSTNAME] Warning: harddrive space issue" EMAIL_ADDRESS@PROVIDER.COM
00 *  * * * full=`/bin/df | /bin/egrep "(100\%|9[0-9]\%)"` && echo $full | /usr/bin/mail -s "[$HOSTNAME] URGENT: harddrive space issue" EMAIL_ADDRESS@PROVIDER.COM

Amazon.com Wishlist Tracker

Amazon.com is one of my favorite online retailers. Their prices are [usually] great, they have incredibly fast shipping (free 2-day with Amazon Prime membership), and have an enormous inventory of merchandise. Amazon has a nice feature when you leave things in your cart, in that they will tell you when you return whether your saved cart items have increased or decreased in price. While an awesome feature, the caveat is that you have to check their site multiple times a day to increase your chances of seeing some of the really good deals.

Solution? Amazon.com’s API. Amazon provides an API to their database called Amazon Web Services for free (registration required). This opens up a huge opportunity for some great tools. Take, for instance, an automatic price updater, which automatically tells you when something you’ve had your eye on is having a sale.

The program I wrote to do this runs in three parts. Using a public wishlist where I add my items to track (instead of my shopping cart), the first script loads all my wishlist items and find anything that was added, adding them to a mysql database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/perl
 
use Net::Amazon;
use Net::Amazon::Request::Wishlist;
use DBI;
use Product;
 
 
sub createRecord{
  my $rec = shift;
  print "creating record for: " . $rec->getAsin() . "\n";
 
  my $db='amazon';
  my $host="localhost";
  my $uid="USERNAME";
  my $passwd="PASSWORD";
  my $conn="dbi:mysql:$db:$host";
 
  my $asin = $rec->getAsin();
  my $name = $rec->getName();
  my $desc = "";
 
  # quotes will ruin our query, replace with underscore
  $name =~ s/'//g;
 
  my $dbh = DBI->connect($conn,$userid,$passwd);
  my $query = "insert into products(asin,name,description) values('$asin','$name','$desc')";
 
  my $sth = $dbh->prepare($query);
  $sth->execute();
 
}
 
 
my $ua = Net::Amazon->new(token=>'YOUR_AMAZON_TOKEN');
my $req = Net::Amazon::Request::Wishlist->new(wishlist=>'YOUR_WISHLIST_ID');
my @products;
my @wishlist;
 
my $db='amazon';
my $host="localhost";
my $uid="USERNAME";
my $passwd="YOUR_PASSWORD";
my $conn="dbi:mysql:$db:$host";
 
my $dbh = DBI->connect($conn,$userid,$passwd);
my $query = "select asin,name,description from products";
 
my $sth = $dbh->prepare($query);
$sth->execute();
$sth->bind_columns(\$asin, \$name, \$description);
 
while($sth->fetch()){
  #print "found: $asin | $name | $description\n";
  push(@products, Product->new($asin, $name, $description));
}
 
my $resp = $ua->request($req);
foreach my $item ($resp->properties){
 push(@wishlist, Product->new($item->ASIN(), $item->ProductName(),));
}
 
#foreach(@products){
#  print "Found a product: " .  $_->getAsin() . "\n";
#}
 
foreach my $wish (@wishlist){
  #print "Found a wishlist: " . $_->getAsin() . "\n";
  my $flag = 0;
  foreach my $prod (@products){
    if($wish->getAsin() eq $prod->getAsin()){
      $flag = 1;
    }
  }
  if($flag == 0){
    createRecord($wish);
  }
}
 
exit 0;

The second script can then run, which looks up all the items in the mysql database and finds their updated prices. If the price of an item has changed, it adds a new record to a table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/perl
 
use Net::Amazon;
use Net::Amazon::Request::Wishlist;
use Net::Amazon::Request::ASIN;
use DBI;
use Product;
use Price;
 
 
## Create a record in the "prices" table
sub createRecord{
  my $rec = shift;
  #print "creating record for: " . $rec->getAsin() . "\n";
 
  my $db='amazon';
  my $host="localhost";
  my $uid="USERNAME";
  my $passwd="PASSWORD";
  my $conn="dbi:mysql:$db:$host";
 
  my $asin = $rec->getAsin();
  my $price = $rec->getPrice();
 
  my $dbh = DBI->connect($conn,$userid,$passwd);
  my $query = "insert into prices(asin,price) values('$asin','$price')";
 
  my $sth = $dbh->prepare($query);
  $sth->execute();
 
}
 
## Get a Product details from amazon
sub getAmazonPrice{
  my $ua = Net::Amazon->new(token=>'YOUR_AMAZON_TOKEN');
  my $asin = shift;
 
  my $req = Net::Amazon::Request::ASIN->new(asin=>$asin);
  my $resp = $ua->request($req);
  foreach my $item ($resp->properties){
    my $tPrice = $item->OurPrice();
    $tPrice =~ s/\$//g;
    return $tPrice;
  }
}
 
my @products;
my @amazonPrices;
 
my $db='amazon';
my $host="localhost";
my $uid="USERNAME";
my $passwd="PASSWORD";
my $conn="dbi:mysql:$db:$host";
 
my $dbh = DBI->connect($conn,$userid,$passwd);
my $query  = "select asin,price from prices where id in (select max(id) from prices group by asin)";
my $query2 = "select distinct asin from products";
 
my @prodAsins;
my $sth2 = $dbh->prepare($query2);
my $prodAsin;
$sth2->execute();
$sth2->bind_columns(\$prodAsin);
while($sth2->fetch()){
  push(@prodAsins, $prodAsin);
}
 
my $sth = $dbh->prepare($query);
$sth->execute();
$sth->bind_columns(\$asin, \$price);
 
while($sth->fetch()){
  push(@products, Price->new($asin, $price));
}
 
foreach(@prodAsins){
    push(@amazonPrices, Price->new($_, getAmazonPrice($_)));
}
 
foreach my $amazonAsin (@amazonPrices){
  my $flag = 2;
  foreach my $prod (@products){
    if($amazonAsin->getAsin() eq $prod->getAsin()){
      if($amazonAsin->getPrice() != $prod->getPrice()){
        $flag = 1;
      }else{
        $flag = 0;
      }
    }
  }
  if($flag > 0){
    print "Creating record for " . $amazonAsin->getAsin() . " | price is now " . $amazonAsin->getPrice() . "\n";
    createRecord($amazonAsin);
  }
}
 
exit 0;

The third piece is a php script which shows in a rudamentary way what the current status of your items are.  Green highlighted items are currently at the lowest price you’ve seen, red are at the highest, and white have always been the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<html>
<head>
  <title>A Little Bit of PHP</title>
  <style type="text/css">
    td {font-size: 8pt; font-family: Arial;}
  </style>
</head>
 
<?php
 
mysql_connect('localhost', 'USERNAME', 'PASSWORD');
@mysql_select_db('amazon') or die("Unable to select database");
 
 
#$query = "call gen_report();";
$query = 'select * from products p left join ( select p.asin, p.price as min_price, max(p.creation_date) as min_date from  (select asin,min(price) as price from prices group by asin) as x inner join prices as p on x.asin = p.asin and x.price = p.price group by asin ) as A on p.asin = A.asin left join ( select p.asin, p.price as max_price, max(p.creation_date) as max_date from   (select asin,max(price) as price from prices group by asin) as x inner join prices as p on x.asin = p.asin and x.price = p.price group by asin) as B on A.asin = B.asin left join ( select p.asin, p.price as curr_price, p.creation_date as curr_date from prices p where id in (select max(id) from prices group by asin) ) as C on B.asin = C.asin';
 
$result = mysql_query($query);
 
mysql_close();
 
$num = mysql_numrows($result);
?>
 
 
<body>
<table border="1">
<tr>
  <th>ASIN</th>
  <th>NAME</th>
  <th>LOWEST</th>
  <th>DATE</th>
  <th>HIGHEST</th>
  <th>DATE</th>
  <th>CURRENT</th>
  <th>DATE</th>
</tr>
<?php
 
$i = 0;
while($i < $num){
  $min_price = mysql_result($result, $i, "min_price");
  $max_price = mysql_result($result, $i, "max_price");
  $curr_price = mysql_result($result, $i, "curr_price");
 
  if(($min_price == $curr_price) && ($min_price != $max_price)){
    print '<tr style="background-color: lightgreen;">';
  }else if(($max_price == $curr_price) && ($min_price != $max_price)){
    print '<tr style="background-color: pink;">';
  }else if(($max_price > $curr_price) && ($min_price < $curr_price)){
    print '<tr style="background-color: #eeee44;">';
  }else{
    print "<tr>";
  }
    	print "<td>" . mysql_result($result, $i, "asin") . "</td>";
    	print "<td>" . mysql_result($result, $i, "name") . "</td>";
    	print "<td>" . $min_price . "</td>";
    	print "<td style=\"background-color: #eeeeee;\">" . mysql_result($result, $i, "min_date") . "</td>";
    	print "<td>" . $max_price . "</td>";
    	print "<td style=\"background-color: #eeeeee;\">" . mysql_result($result, $i, "max_date") . "</td>";
    	print "<td>" . $curr_price . "</td>";
    	print "<td style=\"background-color: #eeeeee;\">" . mysql_result($result, $i, "curr_date") . "</td>";
    	print "</tr>\n";
      $i++;
    }
 
    mysql_free_result($result); //we're done using the results, so set it free
  ?>
</table>
 
</body>
</html>

To set the scripts up on a schedule, just add them to your cron. The below example looks for new items in your wishlist every hour on the hour, and new prices five minutes later.

1
2
00 * * * * /home/jon/amazon/updateProductsFromWishlist.pl
05 * * * * /home/jon/amazon/updatePrices.pl

I’ve uploaded all the source code. I plan to add more features as time permits, and depending on Amazon’s license I hope to open a full site where user’s can sign up for automated alerts.

Monitor Network Usage With VNSTAT

With the recent threats from ISP’s lately to implement bandwidth caps on their customers, I began questioning how much bandwidth my family uses in a typical month.

I spent a few hours on google trying to find a program which could do just that, and give me both raw statistics as well as summations at the hourly, weekly, and most importantly monthly increments.

Having not found a suitable program, I began writing my own.  While struggling with the 32-bit limit set on the netdev stats counter, however, I came across Crunchbang Linux, and found a post on their blog about vnstat. VNStat is a program that gives you everything you want from a network bandwidth usage standpoint, including the aforementioned statistics.

VNStat Example

Even better, when coupled with the vnstat php frontend, seen above, you can show all the users of your network what your utilization is.

Of course, to set this up for a network this machine has to either be setup as your network’s router, or can do some changes with your default gateway if that is not possible.

Digital Drip … not just a blank page anymore!

Hi Everyone!  I’m just getting setup here with Wordpress, so please don’t mind the mess while I get things organized.  Subscribe to the rss feed so you don’t miss a beat!