theThought's thoughts

Kevin A Gray - Creative Strategy Guy

Taking a step back: Data Collection creates XML to populate OpenXML Custom Part

 

Over the last week I have been attempting to complete the project so that I can ensure I document the last few steps correctly.  In the last post I created the Data Collection routing for generating the XML that needs to be pushed into the Custom Part.  Since then I managed to create a automaton for pushing the XML into the custom part, however, when testing it in a test chassis I found that Word was struggling to understand attributes.  The XML I created manually was fine but the XML generated through the script did not work.  Having determined this but not determined why I have decided to take a step back and change the way that Data Collection creates the XML so that Word will work.

What do I mean: “Word does not work”?  Well when I manually created the CustomPart and bound it to the Form Fields you could see the results on the page (binding form fields) as shown in the image below.  Once I had automated the creation of the XML and the pushing of that XML into Word the content of the custom part was not showing in the document (the form was blank). 

Image001

If I then went into Design mode to view the Form Field details and then switched back out of Design Mode I would get an error.  It took me quite some time to realise that this error was simply saying that the Form Fields are empty.  This seems a bad error (one that Microsoft needs to change).

Image002

Furthermore as the PASW name for Data Collection has been dropped by IBM, I decided to change my namespace from PASW to CaO2.  CaO2 is an old term from a previous life, it has nothing to do with SPSS or IBM but I like using it (for example I use it as a domain name for my e-Mail: kgray@cao2.net)

To explain the change, let’s examine the name node of the xml.  In the previous post the following XML would have been generated by Data Collection:

<cao2:name cao2:title=”Mr” cao2:fname=”Kevin” cao2:lname=”Gray” />

Word did not seem to like the namespace qualified attributes.  Dropping the namespace for each attribute did work. So I could change the XML to:

<cao2:name title=”Mr” fname=”Kevin” lname=”Gray” />

However I am not happy doing this I like the namespace to be qualified properly.  Consequently I have decided to only use textnodes and not use attributes.  This will get me through this exercise but is not a perfect solution.  As a consequence I will have to return to this in a later blog to ensure I can use attributes in the long run.  The new XML will, consequently look like this:

<cao2:name>
                <cao2:title>Mr</cao2:title>
                <cao2:fname>Kevin</cao2:fname>
                <cao2:lname>Gray</cao2:lname>

</cao2:name>

This format is much more verbose but it does work when used in Word.  In order to have this format of output the routing in Data Collection needs to change.  The creation of an attribute was, previously,  executed through the following code (which creates the name node and then adds a title attribute):

set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:name", "http://www.kevinagray.co.uk/railcomplaint")
xmlPersonal.appendChild(xmlNode)     

if Title.Response.Value <> null then
                xmlNode.setAttribute("cao2:title", format(Title.Response,"b"))
else
                xmlNode.setAttribute("cao2:title", "")
end if

The new code will still create the name node but then will create another node for title.  The title node is always created whether or not there is a title, however the textnode within the new node is only created if there is response on the form, otherwise the title node will be null.

set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:name", "http://www.kevinagray.co.uk/railcomplaint")
xmlPersonal.appendChild(xmlNode)

set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:title", "http://www.kevinagray.co.uk/railcomplaint")
xmlNode.appendChild(xmlParent)

if Title.Response.Value <> null then
                set xmlText =  xmlPart.createTextNode(format(Title.Response,"b"))
                xmlParent.appendChild(xmlText)
end if

The full code in the routing is now:

Routing(Web)

Dim xmlPart, xmlNode, xmlRoot, xmlPersonal, xmlIncident, xmlParent, xmlText
Const Node_Element = 1

                IOM.LayoutTemplate = "railcomplaint"
                IOM.MustAnswer = false            

                Personal.Ask()

                Incident.Ask()

                set xmlPart = CreateObject("Microsoft.XMLDOM")
                set xmlRoot = xmlPart.createNode(Node_Element, "cao2:root", "http://www.kevinagray.co.uk/railcomplaint")
                xmlPart.appendChild(xmlRoot)

                set xmlPersonal = xmlPart.CreateNode(Node_Element, "cao2:personal", "http://www.kevinagray.co.uk/railcomplaint")
                xmlRoot.appendChild(xmlPersonal)

                set xmlIncident = xmlPart.CreateNode(Node_Element, "cao2:incident", "http://www.kevinagray.co.uk/railcomplaint")
                xmlRoot.appendChild(xmlIncident)

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:when", "http://www.kevinagray.co.uk/railcomplaint")
                xmlIncident.appendChild(xmlParent)

                if IncidentDate.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentDate.Response.Value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:destination", "http://www.kevinagray.co.uk/railcomplaint")
                xmlIncident.appendChild(xmlParent)

                if IncidentDestination.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentDestination.Response.Value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:name", "http://www.kevinagray.co.uk/railcomplaint")
                xmlPersonal.appendChild(xmlNode)     

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:title", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if Title.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(format(Title.Response,"b"))
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:fname", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if fname.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(fname.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:lname", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if lname.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(lname.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:address", "http://www.kevinagray.co.uk/railcomplaint")
                xmlPersonal.appendChild(xmlNode)

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:line1", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if address1.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(address1.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:line2", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if address2.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(address2.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:town", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if Town.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(town.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:postcode", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if PostCode.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(postcode.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:contact", "http://www.kevinagray.co.uk/railcomplaint")
                xmlPersonal.appendChild(xmlNode)

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:work", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if Phone.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(phone.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:home", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if Phone2.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(Phone2.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:mobile", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if mobile.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(mobile.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:email", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if email.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(email.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:departure", "http://www.kevinagray.co.uk/railcomplaint")
                xmlIncident.appendChild(xmlNode)

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:from", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if IncidentDepart.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentDepart.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:expected", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if IncidentExpected.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentExpected.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:actual", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if IncidentActual.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentActual.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:impact", "http://www.kevinagray.co.uk/railcomplaint")
                xmlIncident.appendChild(xmlNode)

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:severity", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if IncidentSeverity.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentSeverity.Response. value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:outcome", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if ExpectedOutcome.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(ExpectedOutcome.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlParent = xmlPart.CreateNode(Node_Element, "cao2:cost", "http://www.kevinagray.co.uk/railcomplaint")
                xmlNode.appendChild(xmlParent)

                if CostIncurred.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(CostIncurred.Response.value)
                                xmlParent.appendChild(xmlText)
                end if

                set xmlNode = xmlPart.CreateNode(Node_Element, "cao2:description", "http://www.kevinagray.co.uk/railcomplaint")
                xmlIncident.appendChild(xmlNode)

                if IncidentDescription.Response.Value <> null then
                                set xmlText =  xmlPart.createTextNode(IncidentDescription.Response.Value)
                                xmlParent.appendChild(xmlText)
                end if

                debug.Log(xmlPart.xml)

End Routing

Filed under  //   CustomPart   IBM   IBM SPSS Data Collection   OpenXML   Routing   SPSS  

Building the core components - Data Collection Populates an OpenXML form using Custom Parts (The Survey)

There are three core components that have to built to simulate and then develop the process of using PASW Data Collection to populate an OpenXML document using custom parts:

·         The Survey

·         The Custom Part

·         The Word Template

This post (and the following two) will explain each of these components provide the script that was used and explain (where relevant) where outside agencies were used to gain an understanding of how the process should be created.

The Survey
As stated in the previous post (project definition) a two page survey is required that captures the details of the individual and the details of the complaint.  This can either be constructed in Author or Author Professional, however Author Professional was used in this case.  As can be seen by the metadata definition below, not only were the questions themselves defined but also the overall look and feel of each question.  The setting of a lablestyle width and the positioning of the control (to the right of the label) has been achieved within the metadata to create a more form like appearance.  This means that the routing is kept very simple (selection of layout template and the asking of the two page questions).

Metadata

Metadata(en-GB, Question, Label)

      TitleList "" define
            {
                  Mr "
Mr",
                  Mrs "
Mrs",
                  Ms "
Ms",
                  Dr "
Dr"
            } asc;
      SeverityList "" define
      {
            Mild "
Mild",
            Annoying "
Annoying",
            Concerning "
Concerning",
            Upsetting "
Upsetting",
            Severe "
Severe"
      };

      title "Title"
            style(ElementAlign = "
Right",
                  Control(Type = "
DropList"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      categorical [1..1]
      {
            use TitleList –

      };
      fname "
First Name:"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..50];

      lname "Last Name:"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..50];

      Address1 "Address:"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..100];

      Address2 " "
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..100];

      Town "Town:"
            style(ElementAlign = "
Right", Width = "200px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..100];

      PostCode "Postcode:"
            style(ElementAlign = "
Right", Width = "100px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..10];

      eMail "e-Mail:"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..100];

      Phone "Phone (Home)"
            style(ElementAlign = "
Right", Width = "150px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..20];

      Mobile "Mobile"
            style(ElementAlign = "
Right", Width = "150px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..20];

      Phone2 "Phone (Work)"
            style(ElementAlign = "
Right", Width = "150px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..20];

      IncidentDate "Date:"
            style(ElementAlign = "
Right", Width = "150px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      date;

      IncidentDestination "Destination"
            style(ElementAlign = "
Right", Width = "200px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..50];

      IncidentDepart "Depart From:"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..50];

      IncidentExpected "Depart Time (Expected)"
            style(ElementAlign = "
Right", Width = "100px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..10];

      IncidentActual "Depart Time (Actual)"
            style(ElementAlign = "
Right", Width = "100px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..10];

      IncidentSeverity "Severity"
            style(ElementAlign = "
Right", Height = "17px",
                  Control(Type = "
DropList"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
            categorical [1..1]
            {
                  use SeverityList -
            };

      IncidentDescription "Description"
            style(Width = "
500px", Height = "68px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
      text [..1000];

      ExpectedOutcome "Expected Outcome"
            style(ElementAlign = "
Right", Width = "300px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      text [..200];

      CostIncurred "Cost Incurred (£)"
            style(ElementAlign = "
Right", Width = "100px", Height = "17px",
                  Control(Type = "
SingleLineEdit"))
            labelstyle(Width = "
70px")
            templates(Question = "
SameLineQuestion.htm")
      long;

      Personal -
      page(title, fname, lname, Address1, Address2, Town, PostCode, eMail, Phone, Mobile, Phone2);

      Incident -
      page(IncidentDate, IncidentDestination, IncidentDepart,     IncidentExpected, IncidentActual, IncidentSeverity, ExpectedOutcome, CostIncurred, IncidentDescription);
End Metadata

Routing

Routing(Web)

      IOM.LayoutTemplate = "RailComplaint.htm"

      Personal.Ask()

      Incident.Ask()

End Routing

 

For those of you with PASW Data Collection ver sion 6 or later.   The metadata and routing are located in the attached mdd.  If you would like to know more about the metadata and routing contained in this post, please download the DDL (a technical manual on Data Collection) from Data Collection Document Library.

 

Click here to download:
Complaint.mdd (279 KB)

 

Filed under  //   Custom Part   Dimensions   Metadata   Microsoft Word   OpenXML   PASW DATA Collection   Routing   SPSS  

OpenXML / PASW Data Collection - Project 1

My presentation at SPSS Directions in Las Vegas (9th – 13th Nov) will require me to provide several working examples of OpenXML / PASW Data Collection integration.  I am currently looking for three different examples with increasing levels of complexity.  Two will probably be around the survey metadata document and Word while the third will be around a reporting script that is exporting to PowerPoint.

So in order to not overwhelm the project and suffer from the inevitable procrastination I have decided to start with a topic that is often requested by clients.

Is it possible to create a printable copy of the responses to a survey so that it can be sent either to the respondent or a stake-holder?

Scenario:
A Rail company wishes to provide an online complaints form that allows travellers to submit details of complaints regarding late trains or poor service while on trains.  The information would then be escalated to the relevant account manager with the complainant receiving a copy of the details of the complaint for their own records.

The Survey:
This will consist of two pages of questions.  The first will obtain general details of the complainant while the second will obtain details of the complaint.  Once both pages have been captured the complainant will be asked to confirm submission and then will be e-Mailed a PDF copy of the complaint.  Their e-Mail address is a mandatory field in the first page.

The Steps:
The following steps will be required to complete the project:

·         Construct Survey Metadata

·         Construct Survey Routing to ask questions

·         Watch Microsoft Videos on Custom Parts

·         Build Routing to construct an XML document of the responses provided

·         Construct a Word document defining the layout of the complaint record form

·         Pass the XML across to the document as a Custom Part

·         Insert Placeholders into the document to ensure the information appears in the right locations in the document

·         Create a dll that receives the XML from PASW Data Collection, inserts it into the custom part within the document template, generates a PDF from the resulting document and sends it to the complainant

These steps have been defined before I have had chance to watch the videos on the implementation of custom parts.  I am documenting my intentions at this point so that I can show what has to change once I better understand the way custom parts work.

Filed under  //   Custom Part   Metadata   OpenXML   OpenXML SDK 2.0   PASW DATA Collection   Routing   SPSS   SPSS Directions