theThought's thoughts

Kevin A Gray - Creative Strategy Guy

Data Collection to OpenXML a simple class for populating custom parts (Technical Design)

So far in this project a survey (the survey) has been created that accepts details of a train complaint.  A Word document (the word doc) has been created that provides a printed copy of the complaint details and a custom part (the custom part) has been defined and added to the Word document to facilitate the transfer of information from IBM SPSS Data Collection to Microsoft Word.  In the last post (creating XML in Data Collection) logic was added to the end of the survey that creates an XML document containing all the responses to the survey in the same structure as the custom part in the Word document.  In this post VB will be used to create a simple class that will accept the XML document and re-build the custom part.  It will also include methods to convert the resulting document into a PDF ready for emailing to the respondent.

Technical Design

The purpose of this DLL is to handle the interaction  between Data Collection and an OpenXML document.  The class will receive the XML to be inserted into the document and the details of the custom part to be updated.  IT will then replace the content of the existing custom part with the new XML content.  It will not delete the custom part itself as this would result in the loss of the existing unique ID for the document. This would in turn cause all the document relationships and form field bindings to break.

The second part of the process is to create a PDF version of the document.  Word can now do this without the use of third party components.  The returned PDF can then be attached to an e-Mail for distribution to the survey respondent (complainant)

Update Part method
Inputs:

·         XML (containing the survey responses)

·         Document name (the name of the Word document to change)

·         Custom Part name (name of the custom part to be changed)

Outputs

·         Success/Fail Signal (indicating whether update succeeded)

Create PDF Method

Inputs: None

Outputs:

·         PDF version of document

Internal Functions

As well as methods exposed to other applications (in this case IBM SPSS Data Collection) this dll will require some internal functions.

OpenWordDoc (Opens the document defined by the Update Part Method)

Locate Custom Part (takes the open document and finds the custom part)

Adjust Custom Part (having found the custom part the XML needs to be fed in)

There is no need to save the document as the OpenXML SDK will automatically save when the custom part is changed.

Filed under  //   Custom Part   Data Collection   IBM   IBM SPSS Data Collection   OpenXML   SPSS   VB.net   XML  

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  

OpenXML, Custom Parts and Microsoft Word Document Bindings (from the inside out)

 

The project has gone well so far, I have been able to create the basic building blocks required to have PASW Data Collection feed data into a Word document without having to worry about formatting or structure of the document.  I have created and tested the survey metadata (the survey) , I have defined the XML structure (the custom part) that will be used to carry the data and I have loaded that structure into a custom part and bound it to a word document (the binding) containing form fields that expose the data in the document (the Word document).

Before starting the automation process within the survey I need to better understand how the word document has changed as a result of creating the custom part, the fields and the bindings.  To this end I opened the Word document using WinZip to see it contents:

Image001

As can be seen from this image there are three new documents relating to the custom part.  They are located in a /customxml/ folder within the document:

·         Item1.xml
·         ItemProps1.xml
·         Item1.xml.rels

Item 1 is the actual custom part.  It contains exactly the same structure and content as the original XML document that I created manually (this original file can now be deleted as it is no longer required). ItemProps1.xml indicates that Item1.xml is a custom  part by giving it a data store property.  The contents of this file are very simple:

<ds:datastoreItem ds:itemID="{98ec6525-b02f-47c2-9730-255f5d4ac3b3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml" />

Lastly the item1.xml.rels (which is located in a _rels subfolder) contains the relationship between these two documents:

<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps" Target="/customXml/itemProps1.xml" Id="R14220311e2e8474c935615afbceff9ee" />
</Relationships>

In addition the relationships file for the MainPart of the word document contains a relationship to the customxml part:

    <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" Target="../customXml/item1.xml" Id="R48d0c0ac84aa4cd0" />

The bindings between the XML Part and the document are located in the MainPart document and are embedded within the definition of the table.  The following WordML structure defines the cell that contains the prompt “Date:” in bold:

<w:tc>
<w:tcPr>
            <w:tcW w:w="2094" w:type="dxa" />
      </w:tcPr>
      <w:p w:rsidR="00C96C26" w:rsidRPr="00EE373A" w:rsidRDefault="00C96C26" w:rsidP="00BE254F">
            <w:pPr>
              <w:rPr>
                <w:b />
              </w:rPr>
            </w:pPr>
           <w:r w:rsidRPr="00EE373A">
                  <w:rPr>
                      <w:b />
                  </w:rPr>
                  <w:t>Date:</w:t>
            </w:r>
</w:p>
</w:tc>

This xml snippet defines a table cell (<w:tc>) whose properties define its with (2094 twips).  Inside the cell is a paragraph inside of which there is a run.  Both the paragraph and the run are bold (<w:b />).  The text in the run is Date:.

Following this is a Structured Document Tag (<w:std>).  This is the form field.

<w:sdt>
<w:sdtPr>
            <w:dataBinding w:prefixMappings="xmlns:ns0='http://www.kevinagray.co.uk/railcomplaint'" w:xpath="/ns0:root[1]/ns0:incident[1]/@pasw:when" w:storeItemID="{98ec6525-b02f-47c2-9730-255f5d4ac3b3}" />
            <w:alias w:val="IncidentDate" />
            <w:tag w:val="IncidentDate" />
            <w:id w:val="5078378" />
            <w:placeholder>
              <w:docPart w:val="DefaultPlaceholder_22675703" />
            </w:placeholder>
            <w:showingPlcHdr />
            <w:text />
</w:sdtPr>
      <w:sdtContent>

       <w:tc>
<w:tcPr>
                 <w:tcW w:w="4994" w:type="dxa" />
         </w:tcPr>
         <w:p w:rsidR="00C96C26" w:rsidRPr="005B25BB" w:rsidRDefault="00C96C26" w:rsidP="00C96C26" />
     </w:tc>
</w:sdtContent>
</w:sdt>

The properties of the Structured Document Tag indicate a data binding (w:databinding).  This binding includes the xpath reference to the relevant attribute within the custom part.  How does the document know which custom part it is referring to (if there were multiple custom parts)?  The w:StoreItemID attribute in the Databinding refers to the same attribute in the custom part definition stated earlier in this post.

The strangest part of this structure (to my eyes) is that the table cell containing the Incident Date is contained within the Structured Document Tag rather than the Structured Document Tag being contained within the table cell.  This seems strange as the XML definition is opposite to the way it is visualised within the document.

Generally, this packaging of information is very neat and means that the updating of the data within the document requires only that the custom part Item1.xml be recreated.  As long as the structure of this document remains the same then the bindings will continue to operate.

Data Collection Populates form using custom controls (binding custom parts to form fields)

Having created the Survey, the Form and defined the custom part it is now time to create a working prototype.  This involves creating a custom part within the document and then linking the relevant data from the custom part to each of the forms.  Word does not provide a mechanism for doing this within its interface, however there is a tool available that does do this work for you even though it’s interface is relatively crude.  This tool is called the Word 2007 Content Control Toolkit and is available via the following link (Word 2007 Content Control Toolkit).  Although this is a relatively simple interface it does allow for the creation of custom parts and the binding of form fields to those parts.  It also provides all the source code for the application so it is possible to identify how the custom part was constructed and how binding is defined.

Building the Custom Part

Before using the Word 2007 Content Control Toolkit it is advisable to create an XML document containing a static version of the XML that is to be used in the document.  The reason for this is that although this tool does provide users with the ability to build XML it does not have all the full validation and UI capabilities of a true XML creation document.  My preference is to use Microsoft’s Web Developer Express 2008.  This software is available, free of charge from Microsoft (Microsoft Express Products).  Using this tool I created a static version of the XML defined in a previous post (the custom part) and saved the file to a local directory.

Binding the Custom Part to the Fields
Once an xml document has been created the toolkit can be used to load its structure into the Complaint Record document created in Word.  Once the toolkit has been installed and opened it is necessary to point it at the Word document that contains the form layout and will contain the custom part.  This is done by simply opening the File using File Open.  Once opened the toolkit examines the document for any form fields and lists them In the window on the left (as shown in the following diagram).

Image001

The second step is create a new Custom Part.  This is done by clicking on the Create a new Custom XML Part in the bottom right corner of the dialog. Once done it is possible to load the previously created XML document (this means that the elements and attributes do not have to be created manually).  This is done by clicking on the Open XML document button.  As a result of these actions your dialog should look similar to the following:

Image002

The last step is to bind the form fields listed on the left side with the XML content on the right side.  This is done by switching the left side from Edit view to Bind view.  Bind view provides a hierarchical representation of the XML loaded into the custom part clear depicting elements and attributes.  To perform a binding perform the following steps.

Click on an element or attribute from the binding view

Click and Drag the element to the relevant field on the left side

It is important that the click and the click and drag are two separate actions, if they are done as one the wrong element/attribute from the XML is assigned to the field.  When the item is dropped onto the form field the XPath of that element is updated.  Check this to make sure it is correct.  Once all the bindings have been created the dialog should look similar to the one below:

Image003

Once all the changes have been made it is possible to save the Word Document.  Once this has been done it is possible to open the file in Word.  If the XML part loaded has sample information within it then the document will show the fields populated.  In this case it should look similar to the image below:

Image004

Building the Core Components - Data Collection Populates form using Custom Parts (the Word Doc)

The final component of this process is the Word document itself.  The construction of this document is in two parts:

1.       The layout

2.       The form fields

In previous posts I have created the survey that will capture the customer feedback (the survey) and I have devised the structure of the XML that makes up the custom part (the custom part). In this post I create the final piece of the puzzle before working on the automation of the process.

Step 1: the layout
As the document is basically a form I have used a basic table construct to create the layout, as shown in the following image..

Image001

Step 2: the form fields

The data displayed in the form will be sourced from the custom part.  Word provides a small number of form fields that can be bound to a custom part.  The actual binding cannot be performed within the Word interface but the fields can be inserted ready for binding later.  These fields are available through the Developer ribbon.  This ribbon is not displayed by default,  consequently the first step is to change the default settings of Word so that it is displayed.  To do this perform the following Steps

Ø  Click on the Round Office Logo in the top left corner of Word

Ø  Click on the Word Options button at the bottom of the menu

Ø  Click on the Popular Tab (if it is not already displayed)

Ø  Check the box Show Developer tab in the ribbon

Ø  Click OK

The following image shows the location of this option:

Image002

Once the Developer Tab has been switched on the following ribbon can be displayed by clicking on Developer

Image003

The control section of this ribbon provides access to the form fields.  As this form is for printing and review not for entry only the basic text field is to be used.  This will allow the placement of text fields within the table.

Ø  To insert a text field perform the following steps:

Ø  Click in the document where you would like the field to appear

Ø  Click on the Text Field button

The key option is Design Mode.  When in Design mode it is possible to insert and edit the Form Fields.  The text field will be inserted at the current cursor position.  The text field properties then need to be changed so that it is distinguishable from the other fields that will be placed onto the form.  To edit the field perform the following steps:

Ø  Right Click on the Form Field

Ø  Select Properties

Ø  Enter a Title for the Form

Ø  Click OK

Ø  Select the text inside the field

Ø  Press the delete key to remove it

The following diagram shows the Properties dialog:

Image004

Once all the forms have been created in this way the following form is visible:

Image005

The next stage in the process is to create a static version of the document that contains the values from a custom part.

Filed under  //   Form Fields   Microsoft   OpenXML   PASW Data Collection   custom part   docx  

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