Web application development (Part 2)

Thanks for all the great feedback on my previous post. I had previously wondered why I don’t get a lot of comments on my posts and I think I’ve discovered why. I haven’t been making postings that were nerdy enough for my readership. Well, I guess I’ve learned my lesson.

I got a lot of good advice about using Drupal, CakePHP and Rails. I also got some offline advice to look into another PHP framework called Symfony. Symfony has one good thing going for it in that it has a book out which is available locally, which isn’t something any of the other PHP frameworks have going for it. (Drupal has at least one book available locally and Rails has at least a dozen.)

Anyways, after weighing all the good suggestions, I gave a stab at learning CakePHP. I found a few good tutorials online, probably the best of which is Sitepoint’s article The CakePHP Framework: Your First Bite. I combined that with a couple of other tutorials and within a couple of hours had an application with basic user authentication working. It really was pretty easy.

Today I worked on setting up the modeling for my first web application. I want to have a system where I can enter the hourly sales breakdowns from our daily receipts to come up with a schedule predictor of how many staff should be on duty at what times. SUBWAYâ„¢ has a way to do this but it involves employees writing information on dead trees and the manager to calculate by hand this data weekly. Much easier to just enter the raw numbers from the Z-Tape receipt each day and have it calculate things for me.

So I’ve finished modeling the four database tables needed and their relationships, and have entered some basic data with scaffolding. I think the modeling is pretty much set at this point and I can start building the controllers and views.

All in all it’s been fairly easy to get to this point and already I can see all the work that’s needed to get this complete. It’s quite possible I’ll have a working application by tomorrow and can bring the results to my manager at our weekly meeting on Wednesday.

Better yet, as soon as I get the authentication framework a bit more robust I can get my manager access and have her do the weekly data entry and set the employee scheduling herself. And the eventual goal is to get everything we do now in Excel and E-mail to be done via web applications.

Web application development

I’m looking for some advice from my readers. I’m looking at developing some web applications to support my business. A lot of it will be data entry that will be saved in a database and then some simple data processing done to the data. I will also need multi user access with authentication and authorization functions.

My server is currently LAMP based: Debian 4, PHP 5, MySQL 5, Apache 2.2. So a solution built around this platform would be easiest to implement, and I also have basic PHP coding skills.

Here are some of the options I’m considering:

1) Roll my own application from scratch based in PHP. More work, but more flexibility and less learning.

2) Install Drupal or some other CMS and build modules. Will get a nice CMS too but need to learn a bunch of stuff and the type of apps I need to make will still require significant effort.

3) Use some kind of PHP Rapid Development Framework. Again, this will require more learning, but again has the flexibility of not being tied to a particular CMS’s requirements. But then the question is which one? CakePHP, Prado, Horde?

4) Switch over to Ruby on Rails which is supposedly the easiest way ever invented to code web apps. Means installing and learning a new platform and language from scratch.

5) Something else?

Any ideas or feedback?

tcp.com: The end of an era

tcp:root % init 0
tcp:root % svc.startd: The system is coming down. Please wait.
svc.startd: 101 system services are now being stopped.
Jun 1 18:51:51 tcp.com rpcbind: rpcbind terminating on signal.
Jun 1 18:51:51 tcp.com genunix: WARNING: hme0: fault detected in device; service degraded
Jun 1 18:51:51 tcp.com genunix: WARNING: hme0: No response from Ethernet network : Link down -- cable problem?
Jun 1 18:51:51 tcp.com in.ndpd[202]: terminated
Jun 1 18:51:52 tcp.com syslogd: going down on signal 15
umount: /home/jlick busy
umount: /home busy
umount: /d busy
svc.startd: The system is down.
syncing file systems... done
Program terminated
{1} ok

iTunes Plus Stumbles

I was really looking forward to the new DRM-free high-bitrate iTunes downloads. I’m actually more interested in the high-bitrate upgrade rather than the lack of DRM. The iTunes DRM never got in the way of anything I wanted to do, so I was quite content to put up with it.

So now iTunes Music Store has finally rolled out iTunes Plus and it looks quite attractive. There’s quite a lot of good content available, though it is still a bit limited. I was excited to about the prospect of upgrading my current library to the high-bitrate versions, so I eagerly clicked on the button to upgrade the library and then came up against a serious drawback.

The upgrade is all-or-nothing.

Now it’s not a complete disaster for me. My iTunes Music Store purchases have mostly been for things not available on CD and a few individual tracks here and there where I didn’t want to buy a whole album. So I don’t have a lot of upgradeable music. In fact, it’s only 2 albums and two individual tracks, or 18 tracks total.

But even there I face a dilemma with the upgrade. Of the two tracks, one of them I ended up buying the album on CD and ripping it at high-bitrate, so I already have a DRM-free high-bitrate version I made myself. The other individual track is one I’m a bit undecided about upgrading. It’s not one I listen to a whole lot, but I guess I’d probably do it.

As for the albums, one is a must-upgrade. It’s not available on CD, and I’d love to have a high-bitrate version of it. The second album is another one I’m on the fence about. There’s a few tracks on it I’d really like to upgrade, but the rest are so-so.

I thought maybe this was some quirk of the user interface where I didn’t understand which combination of buttons to press to get to upgrade individual tracks or items. But looking at the iTunes Plus FAQ I read, “You cannot choose which songs, music videos or albums to upgrade individually.” So, yes, this is intentional.

Thinking about it, I’ll probably go ahead and do the upgrade. The whole upgrade will cost $5.35 which is less than it would cost to repurchase the one ‘must have’ album upgrade. But this must be a huge decision for someone who has downloaded extensively from iTunes. Those are probably the people most likely to jump at the chance to upgrade, but they face my problem on an even larger scale.

If you have hundreds of tracks that could be upgraded but are like me and find that maybe half of the tracks are still well-liked enough to upgrade, you may have a really tough time deciding if it’s worth spending a lot of money when you really only want to upgrade a few things.

I can understand requiring purchased albums to be upgraded as an entire album. That’s a reasonable restriction. I can’t understand the justification for requiring someone to upgrade EVERYTHING whether they want better versions of everything or not. And as a customer satisfaction issue, I think Apple has taken a serious step backwards with this policy. They should toss it out and let customers upgrade the individual tracks or albums they want.

As it is, Apple has at least one customer who was eager to buy, but then discouraged by this policy. I’m willing to bet I’m not the only one.

Converting mysql InnoDB tables to MyISAM

Now that I have a VPS with limited memory available for my ‘main server’, I’ve been looking at how to optimize usage to make sure I keep well under the memory limit. I found a nice article Optimizing Apache and MySQL for Low Memory Usage, Part 2 which explains some great ways of reducing your mysql memory usage. One of their best suggestions is to disable InnoDB which can save up to 100M of memory.

I didn’t think I was using InnoDB at all but it turns out that Mediawiki by default sets up some tables using InnoDB even though most are setup as MyISAM. The Mediawiki developers note that InnoDB is better for those tables for high load wikis, but since mine doesn’t exactly qualify as high load, I thought it would be safe to convert.

But how to do it? Some sites recommended dumping the db, changing the ENGINE type for the tables in the dump and then reimport it. I’m sure that works, but it seems like a fairly crude way of going about it. Turns out there’s a better way.

First run the ‘mysql’ commend line program and connect to your database. If you have more than one database on your mysql server, you’ll need to check them each one by one:

mysql
use database;

Then get a list of databases using the InnoDB engine:

show table status where engine="innodb";

For each table change the type as follows:

alter table tablename engine="myisam";

For good measure look at the table types again to make sure you got them all:

show table status where engine="innodb";

If it doesn’t show any, then you can go ahead and put ‘skip-innodb’ in your my.cnf as outlined in the document about optimizing mysql above and you’ll suddenly have an extra 100M of memory available!

Flash is insanely cheap!

I mentioned in my previous blog entry “Configuring print spooling with CUPS on OpenWRT”, that USB flash drives work as a good spool device and are quite cheap. When I wrote that, I didn’t realize exactly how cheap they were. The last one I bought was a 512m model which I believe was around US$25 after a pretty substantial rebate. I thought that was pretty good, but that was a while ago and I assumed things had gone down in price a bit.

I went down to one of the large electronics retailers in Taiwan, 燦坤3C (Cankun Electronics), to get one for the router that will go in my second restaurant so that it can be configured for print spooling too. I was intending to get a 512m model again since that’s a pretty generous size for a print spool device. Instead I ended up with a Transcend 1GB JetFlash V30 for TWD449 (USD13.60). 2GB drives were going for TWD699 (USD21.18) and up. They even had models up to 8gb in size for fairly reasonable prices. (They had 512m models still but they weren’t significantly cheaper than the 1gb models.)

This is a higher end brand at a bigger (more expensive) retailer, so it’s a bit more expensive than elsewhere (for example, the same drive is available for USD7.75 on Amazon), but it’s still a very inexpensive price. I saw that there are 16gb and 32gb models on the market, but those are still quite expensive. No wonder laptop makers are talking about replacing disk drives with flash! 16gb of storage on a laptop is plenty of room for most purposes. And a 16gb or 32gb iPod Nano would be pretty nifty.

I feel old.

Configuring print spooling with CUPS on OpenWRT

OpenWRT software provides a way to turn certain wireless routers into general purpose Linux servers. This lets you do interesting things like install VPN software, use it as a file server, DNS server, and all the other things you can do on a basic Linux server. Of course you have to be careful as most routers have very limited memory and flash storage, so you can’t run just anything.

I use OpenWRT at my first restaurant and recently bought a printer/fax for the store so that my manager could print out necessary forms without bothering me. (I have bought an OpenWRT compatible router for my second restaurant but haven’t gotten around to installing it yet. EDIT: Installed OpenWRT with CUPS at the second restaurant and it is working fine now.) I originally followed the OpenWRT Printer Sharing HOWTO and installed the printer using the p910nd software and set up XP to be able to print to it.

p910nd provides very simple non-spooling printer sharing service. Windows XP calls these ‘Raw’ TCP/IP printers. Basically all it does is read in data from the client and then feed it out to the printer port. This has a lot of advantages in that it is simple and does not require storage space to spool the print job. (Remember what I said about limited flash storage?)

Unfortunately it doesn’t handle error conditions very well. It worked well most of the time, but all too frequently something would happen in the middle of printing a page. The printer would stop printing, the client would report an error and spoolsv.exe on XP would go nuts on the CPU usage. And then it was just about impossible to gracefully recover from the error. XP would refuse to remove the job without rebooting, and even then the next job on the printer would come out with the characters all scrambled. That sure is annoying, and wastes time and ink. (Those ink jet cartridges ain’t cheap.)

So what is the alternative? Well, the aforementioned HOWTO also mentions briefly that CUPS is an alternative, but that it isn’t necessarily a good option due to limited storage on most OpenWRT boxes. However, USB flash drives are cheap and make an excellent spooling device. And my OpenWRT box just happens to have a modest 512mb USB flash drive installed which should be plenty for print jobs.

The next frustration is that while there is a CUPS package provided for OpenWRT, I couldn’t find any comprehensive guide for installing or using it on OpenWRT. I also had no experience using this kind of print spooler either. That lead to much flailing around trying to figure out how to get it working, but I was finally able to crack that nut. This document will hopefully help others successfully implement CUPS on their OpenWRT routers.

First, let’s get the prerequisites out of the way. At the least you will need a working OpenWRT router with either a USB or parallel port, and a printer with the same. It is also highly recommended that you add a USB flash drive of 128mb or larger (512mb would be better). (EDIT: Flash drives are so cheap now you should probably get a 512mb or 1gb model.) If you use a USB flash drive and USB printer your router will either need to have two USB ports or you will also need a USB hub. (I haven’t tried it with a hub so please give me feedback if this works for you.)

Next, refer to the OpenWRT Printer Sharing HOWTO and follow step 3.1 if you have a USB printer or step 3.2 for a parallel port printer. Make sure you reboot after installing any ‘kmod’ packages. After this you should see a USB printer device in /dev/usb (e.g. /dev/usb/lp0) or a parallel port printer in /dev/printers (e.g. /dev/printers/0).

If you are using a USB flash drive as the spooling device, also see the OpenWRT USB Storage HOWTO to format and mount your USB storage. (I mount mine as /usb.)

Now you will install the CUPS package for OpenWRT: ipkg install cups

EDIT: You will either need to start cupsd by running ‘/etc/init.d/S60cupsd’ or wait until after installing the cups package to reboot to get lpinfo to work.

Next you will need to do some configuration. First, find out where your printer is by running ‘lpinfo -v’. You will get a list of possible printer ports available. For a USB printer you should see something like:

‘direct usb://HP/Officejet%204300%20series?serial=XXXXXXXXXXXX’

For a parallel printer you should see a device like:

‘direct parallel:/dev/printers/0’

Note that for USB printers you can refer to the printer by either device or by name. The listing above will show the named device if possible. In my case the device is really on /dev/usb/lp0. However, sometimes when a printer is power cycled or the cable is removed and plugged back in, it may come back as /dev/usb/lp1 or some other number. The named device should still work regardless of which USB device the system assigns it.

Now let’s go in and edit /etc/cups/printers.conf. The config that is installed by the package uses a default printer called USB on /dev/usb/lp0 and a printer called LP on /dev/printers/0. Use one of these as your template and remove the other one.

If you have a USB printer, then you’ll want to change the printer name from USB to something more descriptive. My printer is an HP OfficeJet 4355 so I set the first line of printers.conf to read ‘<DefaultPrinter HP4355>’. You can also change the ‘Info’ line to show a description of the printer and the ‘Location’ line to show where it is located. The important part is the DeviceURI. By default it is set to /dev/usb/lp0, but due to the issues described above, I changed it to the named device instead.

If you have a parallel port printer then you would make similar changes there except that your would keep the DeviceURI set to parallel:/dev/printers/0 unless your printer is on a different device.

In the end, my printers.conf looked like:

<DefaultPrinter HP4355>
Info HP OfficeJet 4355 USB Printer
Location Subway Xingtian Temple Store
DeviceURI usb://HP/Officejet%204300%20series?serial=XXXXXXXXXXXX
State Idle
Accepting Yes
JobSheets none none
QuotaPeriod 0
PageLimit 0
KLimit 0
</Printer>

CUPS is typically installed with various filter to understand various conversions of files to the printer’s native format. On OpenWRT, that’s not practical because of the memory limitations. It’s more practical to use the printers as raw printer devices and let the print client provide a formatted file. To enable this, edit /etc/cups/mime.convs and /etc/cups/mime.types. In each, find the line starting with ‘#application/octet-stream’ and uncomment it by removing the ‘#’ character and saving the files.

Now there are a couple of things to edit in /etc/cups/cupsd.conf. By default CUPS is set to require authentication of a system account to print. This essentially means setting up a user account for printing or entering the root password into clients. I didn’t find these options attractive, so instead I set it for anonymous access type and restricted access by IP address. If you do this, remember to make access to the administrative functions more restrictive, otherwise anyone will be able to modify your config. I decided to turn off remote admin functions entirely. The result is something like:

EDIT: I eventually switched back to using user account authentication which has the advantage of being able to print to the printer over the Internet reasonably securely. I added a user with a password and then opened up port 631 by editing /etc/firewall.user and copying the section for opening ssh access and changing the port number from 22 to 631. If you do this you can keep the original configuration for “Location /”. I decided to disable the admin functions even with password protection.

<Location />
AuthClass Anonymous
AuthType None
Order Deny,Allow
Deny From All
Allow From 192.168/16
</Location>

<Location /admin>
Order Deny,Allow
Deny From All
</Location>

That should work without modification when using any private network starting with 192.168.

By default, cups on OpenWRT uses /tmp/cups for storing temporary and spool files. /tmp on OpenWRT is a filesystem using free memory as storage. Many OpenWRT routers only have 16mb of memory and will have very little available for /tmp. Mine has a more robust 32m of memory but typically only has around 15m free with the software I have running.

Depending on your printer, a print job can take quite a bit more than this. For example, on my printer even just one simple page takes 8m and a multi-page document can easily grow to a 60-80m print spool. Needless to say, that’s not going to work too well if only 15m is free. That’s why I’m using a 512m USB flash drive for the print spool instead. Keep in mind that if you use the default config, you will be extremely constrained in what kind of things you can print. (EDIT: The amount of space needed will vary depending on your type of printer, the resolution, rendering engine, and the material being printed.)

That said, let’s say that you’ve mounted your USB flash drive as /usb. Make a print spool in /usb/cups: mkdir /usb/cups

This needs to have drwxrwx–T permissions which can be set with: chmod 1770 /usb/cups

Now change the RequestRoot and TempDir settings in /etc/cups/cupsd.conf to use that spool directory:

RequestRoot /usb/cups
TempDir /usb/cups

Now if you’ve set up everything correctly, you should be able to start up the cupsd server: /etc/init.d/S60cupsd

EDIT: You will need to kill the previous cupsd process before the prior command. ‘ps | grep cupsd’ then kill the process number in the first column.

For the following examples we will assume that the LAN interface of your router is IP address 192.168.1.1. If it is different or if you have set a host name for it in DNS, then change as appropriate.

Open up your browser and type in the address: http://192.168.1.1:631/

You should see the information on your CUPS server and the printers configured. Also make sure that you cannot access the admin functions, otherwise your print server is open to anyone changing your configuration.

Now let’s say that we have a Windows XP system to setup. On the above web page you should be able to browse down to your configured printer and end up with a URL which looks like this: http://192.168.1.1:631/printers/HP4355

(It will show the name you gave your printer instead of HP4355.)

Now bring up the Printers & Faxes explorer in XP and click on ‘Add a printer’. Select ‘network printer’ then ‘Connect to a printer on the Internet or on a home or office network’ and put the above URL in the box below that option. (EDIT: If you use the user account authentication method instead, it will ask for the login and password on the next screen.) Click on ‘next’ and you should be able to select the manufacturer and type of printer you have (or you may have to select ‘Have Disk’ if the printer driver is not included in XP) and the rest of the normal printer setup process.

If everything was done right then you should now be able to print to your network printer.

EDIT: In installing the printer at the second restaurant, I found that some printer drivers don’t show up in the list of available printers if installing as a network printer. The trick to get around this if you run into it is to install it as a USB printer first. Then open the printer properties and change the port to a network printer port.

I would appreciate any comments on whether this worked for you or not, or any significant changes you had to make to get your cups server working. Comments on my blog do not require any registration.

stockgrab fixed

There’s now a fixed version of stockgrab available.

The new version uses LWP::Simple to fetch web pages instead of opening a socket and manually feeding in an HTTP request (this gives you an idea how old the original script is). And instead of using the normal HTML quote page from Yahoo and parsing it, it now uses Yahoo’s quotes.csv service. That service gives you a CSV formatted response of the particular fields you want.

As a side effect, the output format of stockgrab is slightly different than previously. The format is a bit cleaner this way though. Plus it’s easy to modify things to show the data and format you want.

Also the stockbuds list has been updated for today and should work fine going forward.