Saturday, January 6, 2018

Setup Orchard Code Snippet

Setup code snippet:
  1. Clone the library repository (https://github.com/Lombiq/Orchard-Dojo-Library.git) to C:\MyDev\Lib
  2. Tools|Code Snippets Manager ..., Language: CSharp, Add.., choose folder C:\MyDev\Lib\Orchard-Dojo-Library\Library\Utilities\VisualStudioSnippets

Reference: https://orcharddojo.net/orchard-resources

Tuesday, August 1, 2017

mORMot on FHIR - Part 2 - ORM

In this blog, I want to try out some ORM feature of mORMot first.

Fist step, we save the Echo project from previous part to ORM project, and make a copy of SampleData.pas from mORMot's sample 01 to ORM's project folder, add this unit to the project:

Open SampleData.pas, we can see a class TSQLSampleRecord is defined, we can then add this class to the model when a TSQLModel is created so that the model now has one table/class:

Run the application and it has no problem. But we don't see where the table is created as the data server is a memory server, no persistence to any media, it is better to change the server to one of real database server, for convenience, we will use the default Sqlite database server.

The default Sqlite server class is TSQLRestServerDB, so we change the class TECHOServer to inherit from TSQLRestServerDB:

TECHOServer = class (TSQLRestServerDB)
  published
    procedure Echo(Ctxt: TSQLRestServerURIContext);
  end;
And the parameters for creating such a server is different from TSQLRestServerFullMemory, we just need to tell the server where the database file is:
aServer := TECHOServer.Create(aModel,
    ChangeFileExt(ExeVersion.ProgramFileName,'.db3'));
We can then ask the server to create the schema for us:
TSQLRestServerDB(aServer).CreateMissingTables;
That is it! (of cause, you need to add use units as we have changed the class)

After running the application, we can check the database file and there is the table created:


As you can see, without doing any explicit mapping configuration, the database schema is created automatically.

Saturday, July 29, 2017

mORMot on FHIR - Part 1 - RESTful Echo

My goal is to use Delphi mORMot framework to implement a FHIR server.

So what are FHIR and mORMot?

FHIR stands for Fast Health Interoperability Resource, a framework focussed on exchanging health data via RESTful API, thus a FHIR server is normally a RESTful server.

mORMot is a Delphi framework that has features such as ORM an SOA etc, it supports REST service out of the box.

So why mORMot for FHIR?

mORMot is all about REST server, i.e., provides service via RESTful API, taking request and returning response in JSON format, it matches what a FHIR server is doing perfectly.

So how easy can we build up a RESTful server in mORMot to take a JSON request and return a JSON response?

I strongly recommend you to download mORMot source code by using Git client so that you can update the source code easily as it is still being updated actively. So I save the source code in "C:\MyDev\mORMot\Git Version". We will start with a new console project:



and save it as ECHO project:


 The goal of this project is to build a server that can echo a HTTP post. First, we need to define a RESTful server:

TECHOServer = class (TSQLRestServerFullMemory)
  published
    procedure Echo(Ctxt: TSQLRestServerURIContext);
  end;
And the implementation of Echo is quite simple, assigning the input to output and returning HTTP 200 to client:
procedure TECHOServer.Echo(Ctxt: TSQLRestServerURIContext);
  begin
    Ctxt.Call.Outbody := Ctxt.Call.InBody;
    Ctxt.Call.OutStatus :=200;
  end;
Then we need to start the RESTful server, as mORMot ties data model, data server and HTTP server together, thus we need to initialize a model and a server before we start a HTTP server:
aModel := TSQLModel.Create([],'service');
  try
    aServer := TECHOServer.Create(aModel);
    try
      aHTTPServer := TSQLHttpServer.Create('7878',aServer,'+',useHttpApiRegisteringURI);
      write('Press [Enter] to close the server.');
      try
        readln;
      finally
        FreeAndNil(aHTTPServer);
      end;
    finally
      FreeAndNil(aServer);
    end;
  finally
    FreeAndNil(aModel);
  end;

And the whole souce code is as simple as below (less than 50 lines):
program Echo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  mORMot,
  mORMotHttpServer;

type
  TECHOServer = class (TSQLRestServerFullMemory)
  published
    procedure Echo(Ctxt: TSQLRestServerURIContext);
  end;
  procedure TECHOServer.Echo(Ctxt: TSQLRestServerURIContext);
  begin
    Ctxt.Call.Outbody := Ctxt.Call.InBody;
    Ctxt.Call.OutStatus :=200;
  end;

var
  aModel: TSQLModel;
  aServer : TECHOServer;
  aHTTPServer: TSQLHttpServer;

begin
  aModel := TSQLModel.Create([],'service');
  try
    aServer := TECHOServer.Create(aModel);
    try
      aHTTPServer := TSQLHttpServer.Create('7878',aServer,'+',useHttpApiRegisteringURI);
      write('Press [Enter] to close the server.');
      try
        readln;
      finally
        FreeAndNil(aHTTPServer);
      end;
    finally
      FreeAndNil(aServer);
    end;
  finally
    FreeAndNil(aModel);
  end;
end.
Compile and run it:

Now it is time to test it:



 It works!

As you can see, use mORMot to build a RESTful server it pretty simple.

In the next part, I will try to deserialize the input JSON so that we can use the resource as an object internally.






Tuesday, April 4, 2017

Experience with NHAPI is not so happy–how to get a list of group name?

NHapi is a port of the original project HAPI.

NHapi allows Microsoft .NET developers to easily use an HL7 2.x object model. This object model allows for parsing and encoding HL7 2.x data to/from Pipe Delimited or XML formats. A very handy program for use in the health care industry.

With 7 years experience of developing HL7 related applications (HL7 messaging and integration), I have found that in reality, people do not always conformant with HL7’s specification, thus there are always something more or less in the actual HL7 message than what the specification says. This makes the static object model sometime not able to represent the HL7 it is processing, to solve this problem, Terser is introduced to read values from the HL7 regardless the static structure of a particular version of HL7. Terser uses path to locate a particular value. The path consists of segment, field and component, sub component. Unfortunately, the segment consists of group and the name of segment, but group are vary from version to version of HL7, for example, a PRD of REF^I12 in version 2.3.1 is under group PROVIDER, but it is not in any group in version 2.4. Another problem is that I have not been able to find a document in which tells you what groups are in a type of message for each version of HL7, this then leaves you only way to figure out is by guessing. After I have gain more experience of NHAPI, I now can use following code to print out a whole path picture of a specified message:

static void PrintNames(string ident,IStructure s)
        {
            Console.Write(ident);
            Console.Write(s.GetStructureName());
            if (! (s is IGroup))
            {
                Console.WriteLine("");
                return;
            }
            Console.WriteLine("(Group)");

            IGroup g = (IGroup)s;
            foreach (var sn in (g.Names))
            {               
                IStructure ss = g.GetStructure(sn);
                if (ss is IGroup)
                {
                  
                    PrintNames(ident + "    ", ss);
                }
                else
                {
                    Console.Write(ident + "    ");
                    Console.WriteLine(sn);

                }
                //Console.WriteLine("");
            }
        }

 

Call it with

REF_I12 r= new REF_I12;

PrintNames(“”,r)

we can get the path tree:

REF_I12(Group)
    MSH
    RF1
    AUTHORIZATION_CONTACT(Group)
        AUT
        CTD
    PROVIDER(Group)
        PRD
        CTD
    PID
    NK1
    GT1
    INSURANCE(Group)
        IN1
        IN2
        IN3
    ACC
    DG1
    DRG
    AL1
    PROCEDURE(Group)
        PR1
        AUTHORIZATION_CONTACT(Group)
            AUT
            CTD
    OBSERVATION(Group)
        OBR
        NTE
        RESULTS_NOTES(Group)
            OBX
            NTE
    PATIENT_VISIT(Group)
        PV1
        PV2
    NTE

Friday, February 3, 2017

Create multiple folders with command "MD"

The other day, I was trying to create a folder "SQL Server" but I was missing the quotes, so I issued command "md sql server" in the CMD window, then I used "cd sql server" to change the current directly but failed, with a "dir", I found there were two new folders "sql" and "server" instead of "sql server". If you check the help of the command "md" (I checked with Windows 10), there is no such usage in it. It seems that it is an undocumented and nice feature! If the folder contains space, you just need to double quoted your folder's name such as:
    md "folder 1" "folder 2" 

Thursday, October 27, 2016

Expierence of Enhancing existing projects with mORMot

The original blog with the same title is from one of the mORMot author here

What I want to add to the list are:
  1. You can use SynZip to replace your Zip library. In my case, I use Abbrevia component set for Zip and Unzip, but Abbrevia contains many units and if only the Zip and Unzip of functionality are used, it might be worth to using much more small SynZip
  2. You can replace your base64 routines with mORMot's as it is much faster when the data to be encode/decode is big

Thursday, October 6, 2016

A disadvantage of UPX

UPX (Ultimate Packer for Executables) is a free and open source executable packer (https://en.wikipedia.org/wiki/UPX). It makes your executable smaller thus speed up the loading and transfering. I have been using it for many years and have had no problem at all until few day ago I discovered an serious issue with it.

When UPX compress your executable, it not only compress the code inside the executable, but also compress the resource data in the executable, thus the resource data becomes not readable for other applications, one scenario is that you compile message table into your executable and use this executable as the resource file for some settings such as use it as the value of "EventMessageFile" for defining your own Event Log source in the Windows registry, unfortunately, if you do so, firstly, the Event Log view will not find the descriptions for your event's ID and category, secondly it might crash your Windows Event Log service and even worse, the service might not be able to recover even its Recovery setting has been set up.

You can reproduce what I have say in following steps:

  1. Download UPX from https://github.com/upx/upx/releases/tag/v3.91 (latest release at the time this blog is written)
  2. Find an Event Log source from within "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application",  whose EventMessageFile is an EXE and it has some events you can see them in Event Viewer
  3. Use UPX to compress the EXE you have found in step 2
  4. Restart the Event Log viewer if it is open
  5. Check the event then you can see that the first sentence is something lik "The description for Event ID xxx from source yyy cannot be found"
  6. Try to filter on current log to show event only from that chosen source, the event view might crashes because the Windows Event Log service has crashed by now in some cases.

Sunday, September 6, 2015

Migrate SVN repository to Mercurial repository

  1. Install hgsubversion:
    1. hg clone https://hgsubversion.googlecode.com/hg/  ~\hgsubversion
    2. Add below lines to %USERPROFILE%\Mecurial.INI

      [extensions]
      hgsubversion = ~/hgsubversion/hgsubversion

    3. user hg help hgsubversion to confirm above installation
  2. Clone SVN repository:
    1. hg clone https://hostname/svn/repositoryname, if it requires authentication, use below command instead:
      hg clone svn+https://hostname/svn/repositoryname to type in the username and password at the prompt, or
      use “—config” to specify the username and password like hg clone –config hgsubversion.username=username
      --config hgsubversion.password svn+https://hostname/svn/repositoryname
  3. Upload the repository:
    hg push <remote repository url>

 

Referrence:

http://blogs.atlassian.com/2011/03/goodbye_subversion_hello_mercurial/

Thursday, March 19, 2015

Bamboo on Demand–Failed to download artifact in the job

I was new to Bamboo on Demand and I added an Artifact Download Task into the default job trying to download an artifact built from another plan.

It looked nothing could be wrong as all the information were filled by choosing the value from the drop down boxes except that destination path.

But the truth was that the job failed due to failed to download the artifact. I checked the artifact, it was there, I checked the destination path,

no typo, same path used for its own artifacts which were built before I added the download task.

 

Comparing to other plans on the same Bamboo on Demand server, I found that the plan whose artifact was referenced to be download had

a small different setting from other plans. The difference was that the plan had not specified a folder for checking out the source code, i.e.,

in the Source Code Check out task, Checkout Directory is blank. By put in an folder name to this box and use the same folder as the start

folder in the Location box for Artifact setting, the problem solved!

Tuesday, May 20, 2014

I Spy Bad Code – Change Screen.Cursor

Recently, I have seen following code appeared again and again in one of application written in Delphi:

begin

      …
  Screen.Cursor := crHourGlass;
  try
   …

  except
    Screen.Cursor := crDefault;
    …
  end; {try}
  Screen.Cursor := crDefault;
end;

This code works in most of the time, but it does have one issue, it makes an assumption that the cursor was crDefault thus it change the cursor back to default before the end. What if the cursor is not default before entering this code block? We can improve the code as following thus it will restore the cursor to what it is before this code block:

begin

       …

      LSavedCursor  := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  try
    …

  except   
    Screen.Cursor := LSavedCursor;
    …
  end; {try}
  Screen.Cursor := LSavedCursor;
end;

Or more proper way (in my opinion) is to use a try … finally block to protect the code block and it will guarantee the cursor will be restored:

begin

      …

      LSavedCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  try
    try
      …  //doing busy business code insert here

    except
      …
    end; {try}

  finally
    Screen.Cursor := LSavedCursor;

  end;
end;

Can it be improved further? The answer is YES and NO. If you are still using Delphi earlier than Delphi 2009, then the answer is NO, because we are going to use Anonymous Method which was introduced by Delphi 2009 and later version of Delphi.

With above code, you actually need to define the local variable LSaveCursor in each method in which it has above code, so it is a bit trivial, and you cannot refactoring above code to a method because the actually doing busy business code is vary from method to method, refactoring each doing busy business code to a method and then pass the method’s point will be a over kill approach.  But with anonymous method, we don’t need to refactoring doing busy business code to a method, instead, we can pass the code as an anonymous method.

First we need to create a procedure to take care the logic for changing the cursor to hour glass and restore it when it completes. Below is the procedure:

procedure BusyDoing (AProc:TProc);
var
  LSavedCursor : TCursor;
begin
  LSavedCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  try
    AProc;
  finally
    Screen.Cursor := LSavedCursor;
  end;
end;

This procedure has one parameter AProc which is a pre-defined anonymous method which is defined in unit System.SysUtils as

TProc = reference to procedure;

To use this procedure, all we need to do is enclose the doing busy business code as an anonymous method like this:

BusyDoing(
    procedure
      begin
           … //doing busy business code insert here

      end
);

That is it, very neat, isn’t it?


 

Sunday, March 16, 2014

Paint.Net 4.0 Beta is available

Just in case you didn't know, Paint.Net is a free image and photo editing software for PCs that run Windows. It written in .Net and has features similar to those big guys such as Photo Shop, Paint Shop and The GIMP.
Now its version 4.0 beta is available for download. There is a feature in 4.0 which I like the most is “Copy Merged”. What it does is copy from all layers as one image, which is equivalent to what you have to achieve
by many steps in v3.5:
  1. Flatten the layers
  2. Copy all
  3. Undo the flattern

Thursday, March 6, 2014

My First Orchard CMS Website

http://www.orientart.com.au is my first Orchard website. It is built on Orchard 1.7.2.
I have inherited TheThemeMachine as the theme. I have restyled the menu bar so it
is not so techy. I have created a slider module (based on bxslider) for showing the
featured products. I use taxonomies for product category, Amba.ImagePowerTools
for product picture. I do not use Map module for showing Google Map, instead I use
HTML widget.

Wednesday, January 29, 2014

NHibernate Journey (1) – “show_sql”

I am following Your first NHibernate based application to start my NHibernate journey.

In the article, it uses NUnit for test, but I have to use MSTest as I am using VS Express 2013 in which NUnit GUI cannot be installed.

I have set “show_sql” to true in the hibernate.cfg.xml when first time test Add method of product repository and there was not

SQL statement outputted. Until I was running “session.Get<Product>” then there was SELECT statement appearing in the output of the

test. According to the article, there suppose to be INSERT statement appearing in the output as well when adding new product to the

database. What is wrong?

Tuesday, October 29, 2013

Kentico: Running API Examples from within CMS Desk

If API Examples is installed, the examples can be run from Support –> API examples.

Many of the examples have been hard coded with culture “en-us”, they will be most likely failed if the site is using a culture different from “en-us”.

The example code is like below, it fails on the red line:

#region "API examples - Creating documents"

   /// <summary>
   /// Creates the initial document strucutre used for the example. Called when the "Create document structure" button is pressed.
   /// </summary>
   private bool CreateDocumentStructure()
   {
       // Add a new culture to the current site
       CultureInfo culture = CultureInfoProvider.GetCultureInfo("de-de");
       CultureSiteInfoProvider.AddCultureToSite(culture.CultureID, CMSContext.CurrentSiteID);

       // Create new instance of the Tree provider
       TreeProvider tree = new TreeProvider(CMSContext.CurrentUser);

       // Get parent node
      TreeNode parentNode = tree.SelectSingleNode(CMSContext.CurrentSiteName, "/", "en-us");

       if (parentNode != null)
       {
           // Create the API Example folder
           TreeNode newNode = TreeNode.New("CMS.Folder", tree);

           newNode.DocumentName = "API Example";
           newNode.DocumentCulture = "en-us";

           newNode.Insert(parentNode);

           parentNode = newNode;

           // Create the Source folder - the document to be moved will be stored here
           newNode = TreeNode.New("CMS.Folder", tree);

           newNode.DocumentName = "Source";
           newNode.DocumentCulture = "en-us";

           newNode.Insert(parentNode);

           // Create the Target folder - a document will be moved here
           newNode = TreeNode.New("CMS.Folder", tree);

           newNode.DocumentName = "Target";
           newNode.DocumentCulture = "en-us";

           newNode.Insert(parentNode);

           return true;
       }

       return false;
   }

You can modify the culture to your site’s culture and the example should be able to run on your site.

Tuesday, September 3, 2013

Tips for upgrading Kentico CMS site from v6 to v7

Recently I upgraded one web site written in Kentico CMS v6 to v7 and would like to share my experience.

Preparation

Due to the some API changes in v7, you might need to change your code a lot if your web site is mainly using ASPX template.
To track your changes, it is better if the changes can be recorded down and can be revert any time if it is needed. This is where
source control comes in place. If you have not apply source control to your web site yet, this is the good opportunity to start
using source control.

Here are the suggestions for using SVN as the source control for this upgrading purpose:

  1. create a new repository
  2. create trunk and branches in the repository
  3. check out the trunk to a local folder
  4. copy all files and folders from your web site folder into trunk folder
  5. add all files and folders and commit to SVN repository
  6. create a branch into branch folder based on the trunk

The idea behind this is to record all changes to the branch during the upgrading. Depends on the complexity of the web site,
the changes you will make to the source code might take few days/weeks, so that I do not recommend that upgrade the
production directly, instead, you should make a copy to your development machine and upgrade this copy first. All the changes
made to the local instance will be committed into the new created branch. When the upgrade is done and has been test,
production then can be upgraded. The upgrade to the production will be as simple as following steps:

  1. copy files from the production server into trunk.
  2. commit all the modifications if there is any.
  3. merge changes from the branch
  4. copy file back to production from the trunk

Upgrade

The database upgrade script might contains changes to the views. But I have found that in Micorsoft SQL 2008 R2, after execution
of the upgrade script, the views might not be synchronized properly. In my case the error was some data conversion failures.
The root cause seemed to be the changes on base views had not propagated properly to other views which were base on the base
views.

If you need to perform the upgrade manually, it requires to delete a lot files and folders before updating files to new version’s.
The instruction has listed the files and folders, but it is very fussy if you go through the list and delete file/folder one by one.
A easier way is to copy the content of the list to a text file, then add “del “ to the beginning of the full name of the file and add
”del /s/q “ to the beginning of the full name of the path, save the text file as a .BAT file, this batch file can delete files and folders
in one go and save you a lot of time.

There is an undocumented change you need to take care if your page uses CMSForm for editing a document in the same way as
the Form tab in the CMS Desk content page, you need to assign DocumentManager.LocalMessagesPlaceHolder to the the CMSForm
element’s MessagesPlaceHolderto property, otherwise, there will be an exception when the form is submitted.

Tuesday, August 20, 2013

My Weapons (Tool set)

  1. 7-Zip.
  2. Adobe Reader
  3. EditPad and Notepad++: Advanced text editor.
  4. Fiddler Web Debugger: http/https traffic inspector.
  5. firstobject XML Editor: Formatting XML and browsing nodes in a tree view.
  6. Google Chrome: HTML/Javascript/CSS inspector/debugger.
  7. IDE:
    1. Delphi:
      components:
      1. CnPack IDE Wizards
      2. madshi’s madCollection
    2. Microsoft Visual Studio
  8. Kentico CMS.
  9. Link Shell Extension: Managing file’s hard link.
  10. Microsoft Network Monitor.
  11. Nullsoft Install System
  12. Paint.Net: Graphic tool.
  13. Pdf995.
  14. soapUI.
  15. Synergy: one set of mouse and keyboard for multiple PC.
  16. TeamViewer
  17. VisualSVN Server, TortoiseSVN and WinMerge
  18. WinDirStat: shows disk usage in colour blocks
  19. Windows Live Writer: Writing blogs.
    Plugins:
    1. Live Writer Code Prettify Plugin

Sunday, July 28, 2013

An easy to use tool for query Oracle database

LINQPad can be used for query Oracle database as well. All you need to do is download IQ Driver for Oracle.
The steps are:
1. Click “Add connection” in the connection tree.
2. Click “View more drivers …” in the “Choose Data Context” dialog
3. Click “Download & Enable Driver” under the “IQ Driver – for MySQL, SQLite, Oracle” section.

In my case, parameters in Advanced tab of IQ Connection need to be populated to be able to connect to Oracle database directly.

Wednesday, July 10, 2013

A way to validate your email address

http://verify-email.org/index.php?option=com_emailverifier&check=bochen.lin@global-health.com&view=emailverifier&layout=verify&format=raw&2f8b80496c78ca0e01ff56a06da6bc8f=1

×

bochen.lin@global-health.com - Result: Ok
MX record about global-health.com exists.
Connection succeeded to mx.global-health.com SMTP.
220 mailhub.ws.com.au ESMTP

> HELO verify-email.org
250 mailhub.ws.com.au

> MAIL FROM: <check@verify-email.org>
=250 ok

> RCPT TO: <bochen.lin@global-health.com>
=250 ok

Wednesday, June 19, 2013

Upgrade Orchard 1.3 site from MVC3 (VS2010) to MVC4 (VS2012)

  1. Uses NuGet Package to install MVC4 into the project
  2. Manually change Rozer and MVC assembly configuration in the Web.Config files from old version to version 2 and 4. Add to Web.Config in each View folder of the project.
    1. <configuration>

      <system.web.webPages.razor>
              <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
              <pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
                  <namespaces>
                      <add namespace="System.Web.Mvc" />
                      <add namespace="System.Web.Mvc.Ajax" />
                      <add namespace="System.Web.Mvc.Html" />
                      <add namespace="System.Web.Routing" />
                      <add namespace="System.Linq"/>
                      <add namespace="System.Collections.Generic"/>
                      <add namespace="Orchard.Mvc.Html"/>
                  </namespaces>
              </pages>
          </system.web.webPages.razor>

      </configuration>

  3. Remove “@” if it is in and “@{}” block.
  4. Replace “jQueryUtils_TimePicker”