Posted on March 14, 2008 01:07 by jpager

Last week I explored the PythonEngine object in IronPython. We didn't get any deeper than simply using the API--this week I figured I'd actually put it to use in a real application.

Here is our scenario. You are a developer for a software company that writes a Point of Sale application on the .NET platform. You have hundreds of clients, each with their own in-house IT departments. You'll install everything for them, provide some support when needed, but you simply don't have time to offer each client major customizations. The problem is, each client has its own billing system--some legacy, some more modern--and there is no way for your development team to write a module to interface with each individual system. So, our approach will be to use scripting for the export module. This will allow our clients' in-house development teams to make changes to the export module on their own (or even rewrite the whole thing to their hearts' desire!) without having to even recompile the application. And, you guessed it, we'll use Python for our scripting (the solution and the scripts are attached).

Our first step is to actually build our point of sale application. Sparing all the technical details, I've created a simple Windows Form with a list of products, some fields for quantity, totals, etc., and a "Process" button. When the button is clicked, it will use our python script to send the order out to the billing system. To make things even more customizable, I'll also create an app.config file to store the location of the export python script.

Actually loading the python script is trivial--even more trivial than anything we discussed last week. We need nothing more than a call to the PythonEngine's ExecuteFile() method:

public static void Process(Batch order)
{
   // Do some stuff... Save to database, logs, etc.
   // Send outbound
   string exportScript = ConfigurationManager.AppSettings["ExportScript"];
   using (PythonEngine engine = new PythonEngine())
   {
      engine.ExecuteFile(exportScript);
      bool res = (bool)((IronPython.Runtime.Calls.PythonFunction)engine
         .Globals["Export"]).Call(order);
   }
}

And viola, we're finished with our C# code. We can package this and send it our for our clients. Just to be nice, we'll also provide two simple export scripts. One will be a flat file format that will be dropped off for a legacy system to pick up, and the other will generate simple XML for a more modern system.

Here is our first script:

def Export(batch):
   f = file('export.txt', 'a')
   f.write('BATCH BEGIN [[')
   f.write(str(batch.SubTotal) + '|' + str(batch.Tax) + '|' + str(batch.Total) + '\r\n')
   for item in batch.Items:
      f.write(item.Description + '\r\n')
      f.write('BATCH END ]]\r\n')
   f.close()
   return True

When we run this a couple of times, we get the following output:

BATCH BEGIN [[1.0|0.0850|1.0850 
Mountain Dew 
Mountain Dew 
BATCH END ]] 
BATCH BEGIN [[56.99|4.84415|61.83415 
Mountain Dew 
Mountain Dew 
Arrested Development (DVD) (entire series) 
BATCH END ]]   

And we'll also provide a script that exports it in a simple XML format:

def endl(file):
   file.write('\r\n')
def Export(batch):
   f = file('export.xml', 'a')
   f.write('<batch ')
   f.write('subtotal="'+str(batch.SubTotal)+'" ')
   f.write('tax="'+str(batch.Tax)+'" ')
   f.write('total="'+str(batch.Total)+'">')
   endl(f)
   if(len(batch.Items) == 0):
      f.write('\t<items />')
      endl(f)
   else:
      f.write('\t<items>')
      endl(f)
      for item in batch.Items:
         f.write('\t\t<item description="'+item.Description+'" />')
         endl(f)
      f.write('\t</items>')
      endl(f)
   f.write('</batch>')
   endl(f)
   f.close()
   return True  

This gives us the following output:

<batch subtotal="42.0" tax="3.5700" total="45.5700">
   <items>
       <item description="DVD Player" />
      <item description="Novelty Frisbee" />
      <item description="Novelty Frisbee" />
      <item description="Novelty Frisbee" />
      <item description="Novelty Frisbee" />
   </items>
</batch>
<batch subtotal="19.75" tax="1.67875" total="21.42875">
   <items>
      <item description="Novelty Frisbee" />
      <item description="Novelty Frisbee" />
      <item description="Novelty Frisbee" />
      <item description="Nebraska Replica License Plate" />
      <item description="Erick Dampier jersey (XXL)" />
      <item description="Erick Dampier jersey (XXL)" />
   </items>
</batch>    

And these are just two of the possibilities. Our clients can interact with their external system in any way imaginable--TCP, Message Queue, Web Service, Database--anything that is possible with .NET or python can be done.

Our last consideration is one of efficiency. It would be nice if we could save our compiled script in an assembly and use the pre-compiled version as long as the script is not updated. IronPython allows us to easily create a DLL with our python script using the IronPython.CodeDom namespace, but it is difficult to invoke the compiled code in C#, if it is even possible. A possible workaround may be to write a python script that references the assembly and invoke that script instead. I'll let you know if I ever get that to work.

PythonInterface.zip (15.66 kb)



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Related posts

Add comment


 

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

November 20. 2008 19:51

|