Monday, October 31, 2005

Bad Customer Service, Part II

It happened again. Poor customer service is the norm these days, but I feel that I have to take a moment to mention exceptionally poor customer service.

Some quick background: A few months back, I bought a file cabinet. The bottoms of the drawers were made of a cardboard-like material that could support about 1 or 2 pounds for a few weeks before buckling and splitting in two. Having this knowledge in hand, I proceeded to put about 10 or 15 pounds in each drawer and hope for the best.

Well, this weekend, the proverbial straw finally broke the camel’s back and my drawer's bottom. I was sort of expecting this, so I knew that when the time came, I'd simply go to Home Depot, get some sort of panel board, and re-build the bottoms of the drawers. So off to the Depot I went, thinking this would be a simple, uncomplicated trip. Wrong, wrong, wrong…

Upon entering the store, I make my way over to the wood section, gawking that they already have Christmas trees on display! I quickly find the boards that I need, and begin my search for an employee. They must know when you're looking for them, as when you don’t need one, you will have at least 3 or 4 of them ask you for help. Finally, after waiting in an ad-hoc queue, I got my turn.

"Hi, I need these two boards cut down to 12 ½ inches by 26 ½ inches please."

"No, not today."

"Why not?"

"Our saw is broken."

"Don't you, like, sell saws here, too?"

"Yes, they are different."

"But the ones you sell – they cut wood, no?"

"Yes."

"So not THAT different."

"No, but ours is broken…"

This went on for a couple more minutes, as I could tell that some other customers were quite amused by the irony I was pointing out. I can’t imagine that I was the only one who needed a piece of wood cut that weekend, and for them to simply accept the fact that their saw was busted is just poor customer service at its best. Luckily for me, I have a good neighbor who has every tool under the sun. He gladly helped me out and cut the boards down to size in just a few seconds. They fit perfectly, and my file cabinet is now back up and running, so to speak.

Competition has its benefits. It fosters alternatives and good service. Take away competition, and there goes the alternatives and good service. The Home Depot that I frequent is just a couple of miles from my house. The nearest Lowe’s are anywhere from 9 to 13 miles away. Not really that far distance-wise, but if you know the DC area, you understand that a drive from Vienna to either Sterling or Alexandria on a weekend day takes most of that weekend day. It's all about travel time, not distance.

The people at the Home Depot know this too: no matter how poor their service is, its still better than dealing with the Mixing Bowl or the Dulles Toll Road.

Thus, another day, another miserable customer service story. This one doesn't have as happy an ending as my other one, as I will likely go back to that Home Depot many times in the future, because it is just so close and convenient. If someone from Lowe's is reading this, you now know where your next store needs to be…

Thursday, October 27, 2005

Game Reset

Fans of NCAA Basketball will understand what I mean by a "Game Reset". For the benefit of those who don’t, let me take a moment to explain: As the game proceeds on into the 2nd half, the last few minutes typically take longer than the rest of the half. This allows the corporate sponsors ample opportunities to remind us which SUV(s) we should have, why Miller Lite tastes better than Bud Lite or vice versa, and why we should refinance our house for the 4th time this year.

Since the networks are sensitive to these breaks, and we have such short attention spans these days, when they come back from commercial, they will typically put up a graphic entitled "Game Reset". This graphic will show the current score, time remaining, number of fouls, and any other relevant statistic (such as 3 players with 4 fouls). It basically allows the viewer to become reacquainted with the game that they left for 60 seconds or less.

I like this concept, as sometimes you’re not always paying 100% attention to the game. More than once I’ve watched my beloved Orangemen dominate their opponent for the 1st half, only to gradually give back the lead in the second. All too often I get overconfident and stop paying attention until I see one of these "Game Resets" and say to myself "how did we lose that 20 point lead!"

So why on earth would I blog about commercials (or adverts for those in the UK) and basketball? Developing an application, particularly in HTML DB, can be compared to a college basketball game. There's an initial sprint to get as far ahead as possible. Once you establish some good progress, you try to regroup, and hope rest of the project goes as smoothly. Often, requirements are changing hourly, features get added and removed and then added back again, and things generally go downhill. Before you know it, you're looking at code in your application that you're not even sure how it got there, let along what it does. Time for a game reset.

I've found it extraordinarily helpful to every now and again stop everything and take a good look at all of the pages and code that I've assembled. More often than not, I'm able to tighten things up considerably, remove obsolete objects, and generally make a better application. With tight deadlines and limited resources, this type of activity is typically on the end of your "to do" list, but I can personally attest that it has helped me tremendously.

One of HTML DB's strengths is that it lets developers rapidly build applications. One of HTML DB's weaknesses is that it lets developers rapidly build applications. This is not a typo! It doesn't take a genius to realize that applications built quickly and "under the gun" are much more susceptible to poor design than those which are built at a more leisurely and regulated pace.

Make sure that you get in the habit of taking an occasional Game Reset to see where your applications stand. You'll be more than happy you did.

Tuesday, October 25, 2005

AJAX Select List Code Generator

Carl Backstrom has provided some truly amazing HTML DB AJAX & DHTML examples on his demo page on htmldb.oracle.com. Most of them are documented, but they assume that you have more of a technical background than not.

With Carl’s blessings, I took one of his examples – AJAX Process on Demand – and created a working example of an AJAX-based Select List. The example is quite simple: select an Employee Name, and the Job field will automatically be populated with the corresponding Job from the EMP table. This is a common behavior in Oracle Forms (when a user select one thing, another field is auto-populated) and will make the transition for those coming from a Forms environment a bit easier.

To take it one step further, I created an AJAX code generator of sorts. Give it the required six fields, and it will automatically generate the PL/SQL Application Process, the JavaScript Event and the JavaScript function. Now you can harness the power of AJAX (or at least this one simple example) without knowing J or X!

Friday, October 14, 2005

MAOP Tech Talk: Oracle HTML DB

I'll be presenting at the first MAOP (Mid-Atlantic Association of Oracle Professionals) TechTalk on Friday, October 21st, at the Federal Trade Commission in Washington, DC at 1pm. My topic will be: Oracle HTML DB: Where Does it Fit?

In my presentation - which is my first public speaking engagement since leaving Oracle - I will be discussing all of the places that I have seen HTML DB used by a wide variety of customers - from Federal Government to Commercial, and everything and anything in between. I'll also spend some time going over what's new in Release 2.0.

If you're going to be in the DC area that Friday, please feel free to attend. NOTE: If you do plan on attending, please RSVP to mike.licht@casetech.net in advance, otherwise you will not be allowed into the buidling.

Wednesday, October 12, 2005

Generate PDF files from HTML DB w/out Java!

The question: How do I print PDF documents with HTML DB?

The answer (maybe): PL/PDF

Looks like the folks at PL/PDF have some instructions on how to set it up in an HTML DB environment. For only $300, it seems like a good deal. It's not WYSIWYG, but the code for a complex master-detail report, which contains page breaks and headings on each page, is not that bad.

If you host your HTML DB site with Revion, PL/PDF is even included as a part of their package.

I have not tested this out yet, but am anxious to do so, as I'm sure it will come in handy!

Wednesday, October 05, 2005

Manipulating Images with the... Database?

A recent thread on the OTN HTML DB Forum asked about how to determine the width & height of an image stored as a BLOB in an Oracle table. I mentioned in that thread that I have some code to manipulate an image stored in a BLOB column. This is particularly useful if you’re going to let users upload images, and you want to re-size them to display as a thumbnail.

Thanks to Oracle interMedia, it is trivial to manipulate the width, height, and other attributes of images stored in an Oracle table. I’ve created a sample application here which demonstrates Oracle interMedia and HTML DB in action. Feel free to have a look. You can download this application from HTML DB Studio as well.

Basically, this application allows you to upload images and perform an operation on the image as it is inserted into the PHOTO_CATALOG table. There are two places where some PL/SQL code is required: an After Submit process on page 2, and a procedure to display the images.

Here is the PL/SQL for the After Submit process with some comments in-line:

declare
l_photo blob;
l_thumb blob;
begin
-- Fetch the 2 BLOB columns into local variables
select blob_content a, blob_content b into l_photo, l_thumb
from wwv_flow_files where name = :P2_PHOTO_NAME;

-- Rezise the PHOTO column so that it is proportionally 400x400
ordimage.process(l_photo, 'maxScale=400 400');

-- Determine which operation was selected, and then perform
-- the appropriate Oracle interMedia function on that image,
-- using the user defined width & height
if :P2_OPERATION = 'Proportional' then
ordimage.process(l_thumb, 'maxScale=&P2_WIDTH. &P2_HEIGHT.');
elsif :P2_OPERATION = 'Fixed' then
ordimage.process(l_thumb, 'fixedScale=&P2_WIDTH. &P2_HEIGHT.');
elsif :P2_OPERATION = 'Flip' then
ordimage.process(l_thumb, 'flip');
elsif :P2_OPERATION = 'Mirror' then
ordimage.process(l_thumb, 'mirror');
else
null;
end if;

-- Insert the data into the PHOTO_CATALOG table as a SELECT from
-- WWV_FLOW_FILES and by referencing HTML DB items
insert into photo_catalog
(photo_catalog_id, photo_name, description, uploaded_on,
mime_type, photo, thumbnail)
select :P2_PHOTO_CATALOG_ID, :P2_PHOTO_NAME, :P2_DESCRIPTION,
sysdate, mime_type, l_photo, l_thumb
from wwv_flow_files where name = :P2_PHOTO_NAME;

-- Remove the image from the HTML DB wwv_flows_files table
delete from wwv_flow_files where name = :P2_PHOTO_NAME;
end;

The code for the procedure used to display the image is similar to the CUSTOM_IMAGE_DISPLAY procedure code supplied as a part of the Sample Application.

create or replace procedure display_thumb (p_photo_id in number)
as
l_mime varchar2(255);
l_length number;
l_file_name varchar2(2000);
lob_loc BLOB;
begin
select mime_type, thumbnail, photo_name, dbms_lob.getlength(thumbnail)
into l_mime, lob_loc, l_file_name, l_length
from photo_catalog where photo_catalog_id = p_photo_id;

-- Set up HTTP header
-- Use an NVL around the mime type and if it is a null, set it to
-- application/octect - which may launch a download window from windows
owa_util.mime_header(nvl(l_mime,'application/octet'), FALSE );

-- Set the size so the browser knows how much to download
htp.p('Content-length: ' || l_length);

-- The filename will be used by the browser if the users does a "Save as"
htp.p('Content-Disposition: filename="' || l_file_name || '"');

-- Close the headersowa_util.http_header_close;
-- Download the BLOB
wpg_docload.download_file( Lob_loc );
end;

Finally, the query on Page 1 has some HTML embedded into it so that it can display the THUMBNAIL column in the report. Here’s the SQL for that query:

select "PHOTO_CATALOG_ID", "PHOTO_NAME", "DESCRIPTION", "UPLOADED_ON",
"MIME_TYPE", '<img src="#OWNER#.display_thumb?p_photo_id=' ||
nvl(photo_catalog_id,0) || '" />' thumbnail
from "PHOTO_CATALOG"

That’s all you need in order to do some basic image manipulation with Oracle interMedia and HTML DB!

Tuesday, October 04, 2005

Forming an LLC

DISCLAIMER: I am not an attorney, nor have I sought the advice of one for creating an LLC. The steps which I outline below may be incomplete, erroneous, or just plain wrong. They are meant for informational purposes only, and should not be used as a guide in any way.

A few days and $100 later, the first of several steps required to start an LLC in the Commonwealth of Virginia is complete:




I have officially filed with the State Corporation Commission of the Commonwealth of Virginia – which ironically, I have demoed several Oracle products to. I can say with almost 100% certainty that this is not one of them.

Once I hear back from the SCC, it’s off to irs.gov to get an EIN. After that, I have to fill out a form for the Virginia Department of Taxation, check with my county & town governments, and then I’m officially in business!

I also ordered QuickBooks Pro so that I can keep my own books. This is not something that I’m 100% comfortable with, but I have used Intuit’s TurboTax before, and if QuickBooks is anything like TurboTax, I should manage. Worse comes to worse, I know a couple of CPAs that may be able to be persuaded to help me out.

For the next few days at least, I’m waiting to hear back from the Virginia SCC, as all of the preceding steps depend on the LLC being successfully registered.