Thursday, October 22, 2009

Global Adding Workflows In Echo

For those of you unfamiliar with the "workflows" functionality in Echo, please allow me to give a brief explanation. Workflows were designed to function similarly to checklists but with the purpose of managing tasks necessary to complete parts of, or your entire credentialing process from beginning to end. In fact, if you were to download the Quick Start Library (QSL) by using the "Download HLS Content" button* on the "Echo Updates" screen, you would notice that many workflows are subsequently added to your database. In examining these workflows you would see that many of them are named in such a way that they correspond to certain components of your credentialing process. Going further, you'd notice that each line item is a step needed to complete each component successfully. And it's processed just like these that workflows were designed to managed.

For those of you already familiar with workflows you may have noticed that unlike checklists, the ability Global Add workflows to a provider does not exist. Although seemingly a major road block, do not fear because there is an elegant solution** to this problem which has the extra benefit of being able to be event scheduled (currently not available in Global Update/Add). The solution is to:

1.) Create a filter, which grabs a list of provider to whom you'd like to add a workflow
2.) Create and export map which only exports the provider ID
3.) Create a corresponding provider import map which virtuals in the workflow code, line 1 date of the workflow, and the facility code. If you complete all these steps you should be able to successfully add workflow records to providers.

Now for a few words of caution... pitfall:
1.) Ensure that you have selected the "Add new records" option
2.) If you would like to add the workflow globally, you'll still need to virtual the facility code in the import map, just don't put any value in the "Fill Value" field.
3.) When selecting the workflow code in the import map, make sure to click the "Type (Filter):" ellipsis button and select the "Workflow" option.


As always, please feel free to comment, ask questions, and discuss the solution amongst yourselves.

*The "Download HLS Content" button is available in versions 4.04 and higher.
**This "elegant solution" is only available to those clients who have been licensed for Data Exchange.

Thursday, October 2, 2008

Multivalue / Multivalued Fields vs Subtabling

A coworker of mine recently asked me how I would redesign a part of a our product's database if I were given the opportunity to start from scratch. Currently we have a security field that is a column in everyone of our user's records. It contains a string of Y's, N's, and R's. Y's stand for read and write, R's for read only, and N's for no rights at all. Each character's position is linked to a right for a particular section of our product. For example let's say our product was designed to manage a Burger Shop and there were there sections of the product to which we wanted to assign separate rights. Let's call these three sections burger inventory, payroll, and income. The way our product is currently designed we'd assign inventory to position 1 in the string, payroll to position two, and income to position three. The string would end up looking something like this:

RYN
(In this case this string would mean that the user had read rights to inventory, read and write rights to payroll, and no rights at all to income)

This design works nicely for some things and quite miserably for others. I'll start with the good:

1. When checking a right it is very easy to write a SQL statement to grab the contents of one column in a user's record.

2. It is very easy to update just one column in a user's record.

The Bad:

1. When writing code a programmer will often have to isolate one of the characters in this security string, which means they'll have to do something like so Substring(securityString, 2, 1). There are two problems with this:

a. For future programmers looking at this code, there's nothing there that tells the user which right the programmer was originally trying to examine. In fact, even if the programmer has access to the database and looks at the securityString column in the user's record they still won't be able to tell which right has been assigned to area of the product. This means that a separate list has to be maintained somewhere that the programmer can reference, so that he/she knows which string position is assigned to which right. This approach is cumbersome though and requires that all programmers and personnel update the list whenever a new right is added, even though the list is nonessential for the structure to function (it is essential for readability though).

b. Secondly, when even one right is update/modified the whole string must be updated which leaves room for error (i.e. accidentally changing one of the other string positions).

This leads me to the alternative design. In this design we subtable out the rights like so. First we create a master list of all the rights in a table called Rights. The rights table has a key field, a description field. In our case our rights table might look something like this:


Rights:

Key     |Description       

INCOME  |Income area right

PAYROLL |Payroll area right

INV     |Inventory area right






Then we create another table called USER_RIGHTS which has three fields. One with a foreign key to USER table's primary key, one with a value of Y, R, or N, and one with foreign key to the RIGHTS table's primary key. This table would look as follows:



L_USER |VALUE |RIGHT   

1      |R     |INV
1      |Y     |PAYROLL
1      |N     |PAYROLL



The above example gives us the same results as having a multivalue field populated with RYN. The great part about this design is that now it becomes very obvious as to what each right represents. It also becomes very easy to add new rights and to access existing rights. As a programmer, if I can't remember which code a particular area uses for it's right I can just look in the RIGHTS table and check the descriptions and hopefully I can find it there (assuming that good descriptions were used).



Drawbacks:



1. For every right added that another record for every user. For instance if you have 100 users and a 100 rights that's 10000 records in the USER_RIGHTS table. However this could be cut down considerable by not including the records that a user has no rights to, in the USER_RIGHTS table. For instance in the above example we stated the user had no rights to the income area by adding a record that said N for INCOME. Instead we could just take the absence of a record with the INC key in it, as meaning that the user has no rights to that particular area. This makes it even easier to add new rights to the RIGHTS table because not every USER_RIGHTS table would have to be updated.



2. When updating the USER_RIGHTS table if you are updating more than one right at a time, it would require more than one update statement. In fact, it would require one update statement for Y, R, and N (or a delete statement for N, if you decided that the absence of a record means that the user has no rights). In the multivalue design only one update is ever required because it is all stored in one field. The upshot to having to right individual update statements is that it becomes much more difficult to accidentally update rights for other areas.



All in all the subtable method is much more readable and much more flexible. At the end of our conversation we both decided that it was the best choice.

Thursday, September 25, 2008

The located assembly's manifest definition with name xxx.dll does not match the assembly reference

A client was getting this error on a set of ASP.NET pages when I sent them a new compiled DLL for the C# code-behind (ASP.NET 1.1), and I wasn't sure what was going on. I then noticed something in the stack trace which clued me in to what they were doing wrong:


Assembly Load Trace: The following information can be helpful to determine why the assembly 'xxxold' could not be loaded.


=== Pre-bind state information ===
LOG: DisplayName = xxxold
(Partial)
LOG: Appbase = file:///c:/inetpub/wwwroot/xxx
LOG: Initial PrivatePath = bin
Calling assembly : (Unknown).
===


It was the xxxold in there that tipped me off to what they had done wrong. Apparently, as a way to back up the old DLL before putting the new DLL in the ./bin folder, they just renamed the old one xxxold.dll, which causes C# to throw this error. If fact, after I tested it, any DLL that isn't explicitly referenced in the DLL will cause an error if it is placed in the ./bin folder. To fix the problem all I had the client do was rename the DLL xxx.old instead. This worked perfectly.

Thursday, September 11, 2008

12 Coins Problem with Non-Adaptive Weighing

I recently came across a problem on the net which went as follow:

You are given 12 coins, one of which is defective. The defective coin is either lighter or heavier than the rest of the coins. You are given a balance scale and are told to determine which coin is defective by using three non-adaptive weighs. This means that you cannot change which coins you decide to weigh based on the any of the previous measurements. Meaning there is nothing like this allowed:

IF COIN X weighs the same as COIN Y
THEN WEIGH COIN Z against COIN W

Instead you must basically come up with your plan of attack before weighing any coins, and then not stray from that plan once you've started.

Here is the solution that I came up with:

KEY:
L - Left side
R - Right side
U - Unused
H - Heavy
F - Light

Notes:
When I write something like this:
{1 2 3 4 5 6 7 8}:{H F}

It means that the defective coin could be 1..8 and I don't know if it is Heavy or Light yet

When I write something like this:
{1}:{F}

It means that the defective coin is 1 and it is Light.

/************FIRST WEIGH******************************************/

L {1 2 3 4}     R {5 6 7 8}     U {9 10 11 12}

At this point:
     IF unbalanced THEN {1 2 3 4 5 6 7 8}:{H F}
     ELSE {9 10 11 12}:{H F}

/************SECOND WEIGH*****************************************/

L {6 2 10 12}     R {5 1 7 11}     U {3 4 8 9}

At this point:
     IF {1 2 3 4 5 6 7 8} THEN
          IF balanced THEN {3 4 8}:{H F}
          ELSE IF unbalanced and in the opposite position
               as the first weigh THEN {6 1}:{H F}
          ELSE IF unbalanced and in the same position
               as the first weigh THEN {5 2 7}:{H F}
     ELSE {9 10 11 12}
          IF balanced THEN {9}:{H F}
          ELSE {10 11 12}:{H F}

/************THIRD WEIGH******************************************/

L {7 3 9 10}     R {6 4 5 12}     U {1 11 2 8}

At this point:
     IF {6 1} THEN
          IF balanced
               IF R was light (second weigh) THEN {1}:{F}
               ELSE {1}:{H}
          ELSE
               IF R is light THEN {6}:{F}
               ELSE {6}:{H}
     IF {9} THEN
          IF L is light THEN {9}:{F}
          ELSE {9}:{H}
     IF {5 2 7} THEN
          IF balanced THEN
               IF L was light (second weigh) THEN {2}:{F}
               ELSE {2}:{H}
          ELSE
               IF R is heavy THEN
                    IF R was heavy (second weigh) THEN {5}:{H}
                    ELSE {7}:{F}
               ELSE
                    IF R was light (second weigh) THEN {5}:{F}
                    ELSE {7}:{H}
     IF {3 4 8} THEN
          IF balanced THEN
               IF R was light (first weigh) THEN {8}:{F}
               ELSE {8}:{H}
          ELSE
               IF L is heavy THEN
                    IF L was heavy (first weigh) THEN {3}:{H}
                    ELSE {4}:{H}
               ELSE
                    IF L was light (first weigh) THEN {3}:{F}
                    ELSE {4}:{F}
     If {10 11 12} THEN
          IF balanced THEN
               IF R was heavy (second weigh) THEN {11}:{H} ELSE {11}:{F}
          ELSE
               IF L is heavy THEN
                    IF L was heavy (second weigh) THEN {10}:{H}
                    ELSE {12}:{F}
               ELSE
                    IF L was light {second weight} THEN {10}:{F}
                    ELSE {12}:{H}

Thursday, September 4, 2008

Retrieve Data from Either the Form or Querystring Object

This may have been known by everyone for quite some time now but it's news to me. Apparently in classic ASP there is an alternative to using Request.Form("key") and Request.QueryString("key"). Instead, Request("key") can be used which will check both the QueryString and the Form with one line of code. If a form value and a querystring value get sent over with the same key, I'm not sure which one take precedence though. If you feel like trying this experiment, please send me the results (I'm too lazy to try it myself).

Friday, August 29, 2008

Detecting a loop in a linked list

I was asked an interesting computer science question today which left me stumped. The basic problem seems simple enough but it's more complex than at first glance. The idea is you have singly linked list that can either be standard linked list, a circular linked list (starts an ends in the same place), or a circuitous linked list (has a loop in it that doesn't necessarily go back to the start). How do you detect if a the linked list contains a loop without knowing the length of the linked list, and without being able to modify any of the data.

My initial thought was to just check and see if any of the nodes have null as their "next" value but this is an obvious pitfall because if it has a loop the code would run forever.

My next thought was to keep track of the memory location of each node and check to see if the next node's memory location has already appeared in the list, but this is not elegant, and could take a lot of memory (think of a long linked list).

After that I thought that at each position you could check the memory locations of each node up to the current one and compare them to the "next" node's memory location. This is essentially the same as my second idea but without having to store every node's memory location. This isn't a good solution either because it requires too many traversals of the list (aka SLOW!).

Next up I thought you could reverse the list and see if the the end node of the reverse list is the start node of the original list. This is good but it requires either of the two options:
a. Modifying the list (not allowed)
b. Making a copy of the list but in reverse order
Technically a could be remedied by then re-reversing the list but still then you'd have to go through the whole process twice.

Finally I had to ask for the answer and here is what I was told. Create two node objects one that is going to traverse the list quickly and one that is going to traverse the list slowly. The quick traverser moves two nodes ahead for every one node that the slow one moves. If the quick traverser ever catches the slow traverser then their must be a circuit. Why didn't I think of that? Such a simple solution.

Later I found a second answer that is pretty cool too. This one basically says always have one node to check held in memory. Basically start off with it being the second node and then check four more nodes to see if it gets selected again. If it doesn't then set the fourth node after it to the check node and check 8 nodes after that to see if any of them are the same. If they aren't set the eight node after the last check node to the new check node and then check 16 nodes after that to see if any of them are the same as the check node, and keep going until eventually one of the nodes that is traversed over is the same node as the check node, or a node with a "next" value of null is reached. I thought this solution was particularly interesting because it only requires one traverser.

I guess that's all I have on linked lists for now.

Monday, August 4, 2008

Unable to read the project file <project.csproj>. The system cannot find the path specified.

I recently received a new computer at work, after my old one blew up, and I was trying to setup my web project from SourceSafe and I kept getting the error listing in the title of this entry. I checked IIS to make sure that I had created the virtual directory correctly, and the path seemed to be fine. I also tried opening just the .csproj file instead of the entire .sln (there are several .csproj files in the solution I was working with), it opened just fine. Finally I came across an article that suggested opening Visual Studio and clicking File -> Source Control -> Open from Source Control and then selecting my solution folder. I did that and voila, my problem was fixed. I'm not sure why this was an issue, but I'd guess that it has something to do with source safe and my local project/solution files not syncing up correctly.