Saturday, July 09, 2011

Too Cool no really it is

     Most of my posts are very code-specific.  This one isn't.  This one is more on development philosophy.

     I ordered a pizza from Papa Johns online.  Being the geek I am, I have an established account in which I have specified my address, phone number, and which store is my local store.  I design the perfect pizza, and click order, and WAM.  The too-cool technology of IP address geo-location kicks in and bites my butt.

     It seems that in an effort to better serve their customers, Papa Johns looked at the physical location of my IP address and.determined the city from which  I was accessing the web page  Problem is that my ISP is out of Hurricane, about twenty minutes away from here on the Interstate, and that is where Papa Johns thought I was located.

They HAD my address on file.  THEY KNEW WHAT STORE I ALWAYS ORDER FROM.  But they were" helpful" and placed the order with the store a half-an-hour away, instead the store which is located less than two minutes from my house.

I had to call the distant store and cancel the order.  While the pizzas where in the oven, The manager was very nice, the order was canceled and everyone is happy.


I'll give you another example.  I automated order entry for certain preprinted applications which print with the birth certificate.  The application has an ID number which links it to our master birth file. 

The automation saves staff time and makes us more efficient.  My probelm was that the address mom gave on the birth certificate may or may not be the one included on the application.  She may have moved since the birth occurred and our staff wasn't double checking the address.  A significant number of orders were returned to us as undelivered.  This was bad customer service and consumed additional staff time to corret the mistakes.

One last example.  On my phone, I have a feature that auto-corrects words that are misspelled when typing a text message with the on-screen keyboard.  More than one of my Facebook updates didn't make much sense because the software 'helped' me when it didn't need to.
 
Microsoft is bad for this type of problem.  Every try to put a asterisk in a Microsoft Word document?  Auto-changes it to a bullet.


These stories illiterate a design principle that programmers should consider. Sometimes, you don't know what you think you know.  Automated data capture/data entry can save time but there are gotcha's you have to keep in mind.  Just remember, just because it's possible to automate something it doesn't mean that it is a wise idea to do so.


  • The data you think you know, you may not know.  It may be stale, or just wrong.
  • .The data may be correct, but you're measuring the wrong thing.  The location of my IP was correct, but the location was the location of my ISP, not my home.
  • The time you save by automation may be consumed quickly by the mistakes such automation breeds.


Sometimes in our rush to make software easier to use, we actually make it less useful by making the user have to fight our programs.

Saturday, June 18, 2011

Windows Event Log by Visual Basic 2008

We've been experiencing Delayed Write Errors, which result in a loss of data.  In an effort to provide enough data points for our IT section to determine the problem, I needed to extract from the Window's Event Log all logged events.


For those who may not know, the Event Log is a system managed place in which the system logs certain events that users may use to diagnose problems.  It only took a few minutes on Google to pick up enough pieces to do what I needed.


Everything we need is found in the System.Diagnostics namespace.


Imports System.Diagnostics


Module Module1


    Sub Main()
        'Here we create an EventLog object
        Dim a As New EventLog
        Dim z As Long
        'This creates a streamwriter through which we will
        'write the log data.
        Dim myOut As New System.IO.StreamWriter("delayed.txt")
        'This is which of the EventLogs we are accessing 
        a.Log = "System"
        'We have to specify the machine name
        a.MachineName = My.Computer.Name

       '----------------------------------------
       '       the good stuff
       '       We iterate through each event log entry,
       '       looking for the word "delayed".  If we
       '       find it, we write out the entry.
       '------------------------------------------
        For Each entry As EventLogEntry In a.Entries
            z += 1
           
            If entry.Message.ToLower.Contains("delayed") Then
                myOut.WriteLine(entry.TimeGenerated & "," & entry.Message & "," & entry.MachineName)
                Console.WriteLine(entry.Message)
            Else
                If z Mod 100 = 0 Then
                    Console.Write(".")
                End If
            End If


        Next
        myOut.Close()


    End Sub


End Module

Friday, June 17, 2011

more RegEx

The more I use Regular Expressions, the more I rely upon the ease of use they provide in pattern matching in real, everyday work.
Want to match a date at the start of a line?  try

^\d*/\d*/\d* \d*:\d*:\d* [A|P]M
The ^ means the start of a line.
The \d matches all numbers
The [A|P] matches to either "A" or "P" as in AM/PM.
The rest are just string literals.

I was needing to clean a log file I extracted from the Windows Event log to report a problem to our IT support staff.
I had several hundred line s like
The description for Event ID '1073741850' in Source 'Application Popup' cannot be found.  The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them.
Where each ID was different.  Solution?  RegEx and Repetition!
I used this as my search query.
The description for Event ID '\d*' in Source 'Application Popup' cannot be found.  The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them.  The following information is part of the event:
The \d is, of course, the shorthand meaning "numeric information."  this is the same as the expression [0-9$.,]
* says "repeat the last search term 0 or more times.

Thursday, September 30, 2010

RegEx in Dot Net

While attempting to parse a QuickBooks export file, I had need to match a rather complicate pattern.  The pattern was easy do express in Regular Expressions, but not so much with normal string handling in Dot Net.

Proving that everything and the kitchen sink is available in the Dot Net Framework, here is how you can use the beauty of Regular Expressions in Visual Basic 2008.

Imports    System.Text.RegularExpressions.
sub amatch
 dim aMatch = Regex.Match(InputField, ":[0-9A-Za-z]* " & Chr(&HB7))
if aMatch.Success then
   'Do Something
end if

Friday, June 18, 2010

More RegEx Goodness

It's hard to believe that I have spent hours copying and pasting and manually reformatting text files when I had the power of regular expressions at my fingertips.  Oh, how true is the maximum "knowledge is power"

I had a need to create a text file with database names and key fields in a CSV format.  What I had was a text file with the database schema like this (I highlighted the parts of the text file I actually wanted.)
CREATE TABLE [dbo].[BuildCheck] (
[key] [uniqueidentifier] NOT NULL ,
[BuildStamp] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[version] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO


CREATE TABLE [dbo].[dtproperties] (
[id] [int] NOT NULL ,
[objectid] [int] NULL ,
[property] [varchar] (64) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[value] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[uvalue] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[lvalue] [image] NULL ,
[version] [int] NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE TABLE [dbo].[tblCountyNames] (
[pkey] [uniqueidentifier] NOT NULL ,
[intCountyNum] [int] NULL ,
[txtCountyName] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[txtpassword] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
What I wanted was this.

"BuildCheck","key"
"dtproperties", "id"
"tblCountyNames", "pkey"



So I turned again to the hidden power of Regular Expressions.  I crafted the following RegEx line
^(CREATE TABLE *)([\d\w\s\]\[\. ]*)( *\(\r\s*)(\[\w*\])([\w,\[\]\(\)\r\s]*Go)
and then did a search and replace, replacing the schema with the second and fourth (one based) items.  I know it looks hopelessly complex, but it's not.

Item One
^(CREATE TABLE *)
Matches a "CREATE TABLE" command at the beginning of the line, and a chain of spaces (represented by the space character and the asterisk, which tells the engine to repeat the previous match)

Item Two
([\d\w\s\]\[\. ]*)
Matches a block of numbers (/d) words (/w) and whitespace (/s), beginning and ending brackets and periods.  This translates to the database name

Item Three
(\s*\(\r\s*)
Match some whitespace, then a left parenthesis, then a carriage return following by a string of whitespace.

Item Four
(\[\w*\])
Item Four is our key field name.  We're looking for an opening and closing bracket with word characters inside.

Item Five
([\w,\[\]\(\)\r\s]*GO)
Item five is everything else up and including to the word GO.

Then, I did a search for
(^\[dbo\]\.\[)(\w*)(\]\s)(,)(\[)(\w*)(\])
Replacing it with
"\2","\6"
and viola!

Friday, June 11, 2010

Filling one copy of a database from another

Say you have two databases. Say that these two databases have a table with the same structure. Say you want to update table 1 with data from table 2. Easy cheesy.


INSERT INTO TableName
(Field1, Field2)
SELECT Field1, Field2
FROM Database.dbo.TableName AS SourceTableAlias

Thursday, June 10, 2010

Regular Expressions

Here is what I love about regular expressions. They can make your life much easier.
I had to take a list of enumerated values, like this. (The list was a lot longer than shown)
Barbour=1,
Berkeley =2,
Boone =3,
Braxton =4,
Brooke =5,
Cabell =6,
Calhoun =7,
Clay =8,
Doddridge =9,
Fayette =10,
Gilmer =11,
Grant =12,
State=0,
Unknown = 99

and turn it into a switch statements in C# like this...
case 1:
value= LocationID.Barbour;
break;
case 2:
value= LocationID.Berkeley;
break;
case 3:
value= LocationID.Boone;
break;
case 4:
value= LocationID.Braxton;
break;
case 5:
value= LocationID.Brooke;
break;
case 6:
value= LocationID.Cabell;
break;
case 7:
value= LocationID.Calhoun;
break;
case 8:
value= LocationID.Clay;
break;
case 9:
value= LocationID.Doddridge;
break;
case 10:
value= LocationID.Fayette;
break;
case 11:
value= LocationID.Gilmer;
break;
case 12:
value= LocationID.Grant;
break;
case 0:
value= LocationID.State;
break;
case 99:
value= LocationID.Unknown;
break;

I did so using Edit Pad Pro and this search and replace code with Regular expressions
Search:

([a-zA-Z]*)( =)([0-9]*)

Replace:

case \3:
value= LocationID.\1;
break;

Dissecting it, [a-zA-Z] Matches all letters, both upper and lower case, while the asterisk at the end tells it to match all occurrences of it. So "a" and "aaaaaa" would both be a match. The [0-9]* does the same for the code number.

The parentheses separate the search into three terms. The replace using only terms 1 and 3 by specifying a backslash and the term I want (\1 for the first term, \3 for the third term).

The two big advantages are that I don't have to mess with copying and pasting values, which is tedious and time consuming, and I can be sure that I didn't paste the wrong value somewhere.