theThought's thoughts

Kevin A Gray - Creative Strategy Guy

Week 2 (Part 2) - Emerging from Social Networking this week

As well as spending time discovering new theories and technologies (well for me any way).  I have taken great delight in being at the right place (at least the right website) at the right time to encounter some of the latest technologies to hit the Social Networking community.

It has been a busy week with the launch of two new technologies with very different types of community reactions and very different approaches.

Chronologically the first was the launch of pip.io a new type of aggregation tool that aims to provide a full experience in a variety of technologies rather than just being another listing interface.  The second was the launch of Google Buzz which is an extension of Google Mail (GMail) that allows more real-time discussions and the ability to view media without having to open new browser pages.  Lastly although by no means a new technology I had an opportunity to further explore the music world through theSixtyOne.

Pip.io (My Page)
Pip.io is a new Browser based utility that aims to bring together various forms of social networking media into a single location.  Unlike many of the others on the market it aims to provide a richer more engaging experience.  For example it has its own chat engine but this can be used for visual chat as well as Instant Messaging.  It promises lots of richly featured apps but currently only provides an RSS reader (in addition to the chat capability).  I linked up with an old friend from Lites Ltd who now lives in Germany while reviewing some of the output of a number of my favourite blog writers. 

The visual experience is definitely richer than usual but most of the functionality seems to be missing at this time.  There is lots of noise about its possibilities but still too many "coming soon" banners.  The problem with this plethora of Social Networking facilities and the even larger number of aggregators is that it is difficult to know where to go to look and often if you use two or more you find yourself reading posts you have.

Google Buzz (Introduction)
There does seem to be a lot more interest in this compared to Pip.io, probably because its Google and its seems like it has more functionality.  Some people seem to have jumped in with both feet and are getting very excited by it.  But me? not so much.

So what is Buzz?
Well its kind of a social extension for e-Mail.  It is another attempt by Google to storm the social market.  Google Wave seems to have stalled and so now Google are trying to find another way to get their mass of e-Mail users to utilise their tool to access the social scene.  

The principle is that Buzz will look at what you do and provide you insight into what other people do that you may have missed.  For example You follow person x, person x follows person y, you do not follow person y.  If person y says something interesting you will usually miss it because you do not follow them.  In Buzz, if person x says I find this interesting Buzz will show it to you because you have said you find person x interesting.  

Do you get the idea? do you get the math?  There are thousands (probably millions) of people using social tools if you calculate the number of indirect connections you have (LinkedIn like to do this for you) even a small number of direct followers will give you millions of indirect ones.   The result is a barrage of words, not information, not value just words.  This makes the product name very apt.  It creates a buzz rather like the effect a night in a rave has on your ears.

Despite this buzz, or maybe because at the beginning the buzz was just a hum.  Using Buzz resulted in the receipt of a very kind e-Mail from Richard Kottler who, along with Colin Linsky, was responsible for my employment at SPSS five years ago.  Through buzz he had found my blog entry (No Product is just One Man) and agreed that it was good to see that IBM SPSS Data Collection has continued to prosper once he had left as captain of the ship.  Richard was one of several people who created the concept behind Data Collection and ensured that that concept became a reality.  His reality is as far away from market research as you can get these days as shown by is latest venture (www.holidayrentalmanagement.com)

Overall I think that Buzz is going to have to calm down before I see any real value from it.  Maybe you could change my mind for me by providing comments on this and my other blogs via buzz so that I can understand its value.

TheSixtyOne (My Page)
Those that know me well know that my hearing is terrible.  This is probably attributed to the fact that there always seems to be something stuck in my ears.  I am firmly attached to my iPod and a firm follower of the music podcast scene.  My interests are primarily around ambient, chill, electronica and get most of my daily sound from podcasts.  One of my favourites (the chillcast by Anji Bee) introduced me to TheSixtyOne.

This website recently was ridiculed by its followers for dramatically changing the look of its site.  I like the new look and I like many of the sounds it makes.  It allows independant artists to share their music and for listeners to explore and support these artists.

Although I like the overall look of the site its capabilities are far from intuitive.  It has taken me some time to get used to how to use it and I probably have not yet mastered it all (for example I do not seem to be able to replace the image of route 66 with something distinctly more English).

I am considering writing an online manual in a future blog to tell everyone how it works.  Lets see if I manage it.  In the meantime if anyone knows how to change the background image, please drop me a line.

Filed under  //   Google   Google Buzz   Google Wave   Pip.io   SPSS   thesixtyone  

Success IBM SPSS Data Collection integrates with WordML (OpenXML)

SUCCESS

It’s taken a few weeks, in which the company has changed name, the product has changed name (again) and I have been busy getting ready for everything.  But 10:00 am (GMT) today I managed to run my first survey where responses from the survey were posted into a Custom Part within a Word Document so that the Word document is automatically updated.  If nothing else this proves that IBM SPSS Data Collection can integrate with OpenXML.

Although there are a few other things I would like to do, generate a PDF from the document, do all the work in memory so that the document is not actually written to file, send the file as an attachment to an email.  But this is a great start.

So Monday will see me move to my third project (the above being my second, after a very simple manipulation of WordML in my first).  I will be moving from Word to PowerPoint and I will be moving from Collection to analysis.  I would like to be able to show that output from an MRS (Reporting Script) can push data into an existing Powerpoint presentation without losing any formatting.

One work day off then and then back to the grindstone (what I really mean is one day with my Flash project before moving back to OpenXML – but I can pretend it’s a day off.)

I hope you have enjoyed the posts so far and will continue to read about my endeavours in the future.

Filed under  //   Data Collection   IBM   Microsoft Word   OpenXML   SPSS   WordML  

IBM SPSS Data Collection Consumes DLL to push data into OpenXML

 

So the class required to push data into an OpenXML Word document has now been created (the class), there are just three steps left:

·         Create the DLL wrapper and publish

·         Create a test Chassis to ensure that the DLL works and resolve issues

·         Integrate the DLL into the Data Collection routing and test the final integration

Details of each of these steps is below:

Create the DLL wrapper and publish

Data Collection is able to integrate with processes that are exposed as COM components.  At present the DLL that has been created is not exposed in this way consequently some changes have to be made. This is done by changing the definition of the class and is described by the following article (com objects in vb.net).  Many of these steps have already taken place.  As this particular class does not use or consume events I did not create an Events Id.  Also I used Visual Basic Express 2008 to create my DLL and this does not have the GUID creating tool referenced in the document.  Consequently I used a website to do this (oh the beauty of the internet),  this site is fantastic in its simplicity (create a GUID).

Effectively I did two things:

I replaced the Class definition

Public Class Complaint

with

<System.Runtime.InteropServices.ProgId("cao2.Complaint"), ComClass(Complaint.ClassId, Complaint.InterfaceId)> Public Class Complaint

   Public Const ClassId As String = "CB3F9E3E-D1EB-4852-9C34-7C0A6DC4F7F8"
   Public Const InterfaceId As String = "9C10A919-8E4B-419E-9AAE-3868A1466EC4"

Following this two Constants are generated for the ClassID and InterfaceID GUIDs.  They are kept as constants to make sure that they are the same between compilations of the DLL

Lastly the Properties of the Project are changed so that the Assembly knows it needs to make the assemblies COM visible.  This setting is shown in the following diagram:

Image001

Create a test Chassis to ensure that the DLL works and resolve issues

Once the DLL has been constructed it needs to be tested.  A test chassis is needed to be able to define the DLL and pass the UpdateCustomPart method with the necessary details.  I used a project I had created earlier (first project) that I built as a chassis as it has a form interface and already has the right references. I added a RichTextBox so that the output from the survey (survey output) could be copied into the Chassis and passed to the DLL.  The form now like the one shown below:

Image002

Once the form was finished the references within the project were changed so that the chassis is aware of the newly created DLL.  The reference can be seen on the second line of the following diagram.  It was created by adding a reference and pointing to the DLL that was created when the DLL was compiled.

Image003

Next the start button was re-coded so that it creates a new instance of the RailComplaint class and then calls the UpdateCustomPart passing the relevant information into the method.  The information it passes is the name and location of the Word Document (hard coded for this test), the name of the CustomPart (again hard coded), and the content of the rich text box that should contain the XML string from the survey.

Private Sub pbnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles pbnStart.Click

‘Update Custom Part Test

Dim RailComplaint As New RailComplaint.Complaint
Dim Result As Boolean

Result = RailComplaint.UpdateCustomPart("C:\projects\OpenXML\Samples\RailComplaint\Customer Complaint Test.docx", "item1", rtbXML.Text)

End Sub

Once this is complete IBM SPSS Data Collection – Author Professional is run  and the survey is loaded.  Auto-Answer is used to populate the responses and generate the XML.  The last line of code sends the XML to the output window: Debug.log(xmlPart.xml).  Once the survey is complete the contents of the output window can be copied into the RichTextbox in the test Chassis.  Finally, the start button can be pressed and the code executed.  The script provided in this and other posts has already gone through this process so should work without issue.

Integrate the DLL into the Data Collection routing and test the final integration

The final stage of the process is to call the DLL from the survey.  The code used to do this is very similar to that used in the test harness.  DCScript variables are not typed so the declarations are simpler.  The last line just makes sure that the RailComplaint object is completely removed from memory.

Dim Result
Dim RailComplaint          

                set RailComplaint = CreateObject("cao2.Complaint")
                Result = RailComplaint.UpdateCustomPart("C:\projects\OpenXML\Samples\RailComplaint\Customer Complaint Test.docx", "item1", xmlPart.xml)
                set RailComplaint = null

Once this has been done it is possible to run the survey, complete the two pages of questions and then Open the Customer Complaint Text.docx file to see the result.

Filed under  //   CreateObject   DCScript   DLL   IBM   OpenXML   PASW DATA Collection   SPSS   WordML  

Data Collection to OpenXML a simple class for populating custom parts (the code)

The previous post provided a technical specification for the DLL required to link IBM SPSS Data Collection to the Custom Part in a Word document so that the responses in the survey can be pushed into the Word document.  This post will show the code used to implement that specification.  The code has been broken down into a simplified form to help me learn and to make it easier to explain.  There will be shortcuts that you may want to adopt, you may want less functions preferring to write the code in a single method, however I prefer the small is beautiful approach.

Defining the class:
As OpenXML constructs are going to be manipulated it is necessary to define the right references within the project and to import them into the class itself.  As streams are going to be used to read and write xml files into the Word document System.IO has to be imported.  There is no constructor required as the key actions will be on the main method (UpdateCustomPart). Consequently the class definition is as follows:

Imports DocumentFormat.OpenXml.Packaging

Imports System.IO

Public Class Complaint

End Class

UpdateCustomPart (Public Method)
The first and main method is the UpdateCustomPart, this will receive three parameters and will, for the purpose of this example, output a Boolean indicating success or fail.   The process this method follows is:

Ø  Parse XML string into a new XML Document

Ø  Open the Word Document

Ø  Locate the Custom Part

Ø  Update the Custom Part

Within this process the XML String, the name and location of the Word document and the name of the Custom Part are passed as parameters so that the same class could be used to update any custom part within any Word document.  The code for this method is as follows:

Public Function UpdateCustomPart(ByVal theFileName As String, ByVal theCustomPart As String, ByVal theXML As String) As Boolean

Dim WordDoc As WordprocessingDocument
Dim CustomPart As DocumentFormat.OpenXml.Packaging.OpenXmlPart
Dim xmlUpdate As Xml.XmlDocument

    xmlUpdate = New Xml.XmlDocument

    Try
        xmlUpdate.LoadXml(theXML)
    Catch ex As Exception
        Return False
    End Try

    WordDoc = OpenWordDoc(theFileName)

    If Worddoc Is Not Nothing Then CustomPart = FindCustomPart(WordDoc, theCustomPart)

    If CustomPart Is Not Nothing Then

Return changeCustomPart(CustomPart, xmlUpdate)

   End If

Return True

End Function

In the above function the try … catch is used to capture any errors generated from loading the XML (caused by badly formed xml).  Each of the subsequent functions are detailed below, if any fail the subsequent actions are not performed.

OpenWordDoc (Private Function)
The purpose of this function is to open the Word document identified by the path/filename string and to return the opened document to the calling routine.  Rather than just attempt to open the file (only to have it fail because the file does not exist) the function first uses FindFile to locate the document.  Only if this returns details of the file will the system attempt to open it. 

Private Function OpenWordDoc(ByVal theFileName As String) As WordprocessingDocument

Dim WordDoc As WordprocessingDocument
Dim FindFile As System.IO.FileInfo

    FindFile = New System.IO.FileInfo(theFileName)

    If Not FindFile Is Nothing Then
        WordDoc = WordprocessingDocument.Open(theFileName, True)
        Return WordDoc
    Else
        Return Nothing
    End If

End Function

FindCustomPart (Private Function)
This function was actually quite difficult to work out, it maybe that it is not actually the best way of doing it.  The word document is a package that contains a significant number of XML sub-documents.  It is necessary to find the right XML document to update.  The word document was created from within Word and the Word 2007 Content Control Toolkit.  As a consequence I did not name the Custom Part it was named for me.  Its name is Item1.xml.  It is contained in the customXml folder.  This document can be located using the relationships definition for the Main document part.  If this relationship definition is examined it can be seen that there is a relationship described for the custom part.

<?xml version="1.0" encoding="utf-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml" Id="rId3" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml" Id="rId7" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml" Id="rId2" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/glossaryDocument" Target="glossary/document.xml" Id="rId6" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml" Id="rId5" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml" Id="rId4" />
                <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" Target="../customXml/item1.xml" Id="R456ea5aec8a94caf" />
</Relationships>

The target attribute is the URI of the part.  Based on the fact that the name of the part is provided by the consumer of the DLL this URI can be constructed through some simple text manipulation (“customXml/” + thename + “.xml”).  There may be several custom parts within a document so the Find function iterates through all the custom parts matching the URI of the custom part with a calculated URI.  When it finds a match it returns the matching part.  If it does not find a matching URI the function returns nothing.

Private Function FindCustomPart(ByVal theDoc As WordprocessingDocument, ByVal theName As String) As DocumentFormat.OpenXml.Packaging.CustomXmlPart

Dim CustomPart As DocumentFormat.OpenXml.Packaging.CustomXmlPart
Dim MainParts As IEnumerable(Of CustomXmlPart)
Dim ID As String
Dim matchURI As Uri
Dim CalculatedLocation As String = "/customXml/" & theName & ".xml"

    MainParts = theDoc.MainDocumentPart.CustomXmlParts

    matchURI = New Uri(CalculatedLocation, UriKind.Relative)

    For Each CustomPart In MainParts
        ID = theDoc.MainDocumentPart.GetIdOfPart(CustomPart)

        If CustomPart.Uri = matchURI Then
            Return CustomPart
        End If

    Next

    Return Nothing

End Function

ChangeCustomPart (Private Function)
This function uses a stream to Create new content for the custom part using the XML document created from the XML string passed to the DLL.  It effectively ties all the pieces together.  The writing of the stream updates the file (effectively saves it) this means that there is no need to save the document itself.

Private Function changeCustomPart(ByVal thePart As DocumentFormat.OpenXml.Packaging.OpenXmlPart, ByVal theXML As Xml.XmlDocument) As Boolean

Dim XMLStream As Stream

    XMLStream = thePart.GetStream(FileMode.Create, FileAccess.ReadWrite)

    Using (XMLStream)
        theXML.Save(XMLStream)
    End Using

End Function

Filed under  //   IBM   IBM SPSS Data Collection   OpenXML   SPSS   VB.net  

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  

Updating a Custom Part from PASW Data Collection when using OpenXML for Word (WordML)

Interlude
During the writing of this post the formal announcement regarding the acquisition of SPSS by IBM was made (IBM acquires SPSS Inc).  I now, officially work for SPSS (UK Limited), an IBM Company.  The product I run is no long PASW Data Collection but IBM SPSS Data Collection.  As a result there will be no more references to PASW.
End if Interlude

In previous posts I have explored the components required to publish a Word Document that contains a form that can be populated from IBM SPSS Data Collection.  The Word document was created using Form Fields within a table.  The Word 2007 Content Control toolkit (available here) was used to create a custom part, a slice of custom XML defined not by Word but by the developer.  The same tool was then used to link the custom parts to the form fields in the table (Binding custom parts to form fields).  All this work has resulted in a Word document that is using custom XML to define the content of the table.

The purpose of this post is to go back to IBM SPSS Data Collection to build the XML ready to be sent to Word for posting into the Word document.  There are two ways in which this can be done:

Option 1
As the custom part already exists and contains the correct structure for the XML updating the document should be just a case of updating the attributes contained within the XML.  To ensure that the custom part contains the correct information all existing attributes would have to be updated.

Option 2
Create a completely new custom part that replaces the existing custom part but keeps the same ID. 

It may be that these two techniques will blend together.  The current custom part will be read through a stream, updated and then written back.  There are only two key questions to be answered:

  • Whether it necessary to take any notice of the content that has been read into the stream?
  • Whether PASW Data Collection should assemble the new XML as a single data construct and then pass it to the dll that will post the information into the custom Part?

In the latter case the alternative is for IBM SPSS Data Collection to converse with the dll sending attribute/value pairs each time it wants to update an element of the document.  As the latter is likely to result in multiple reads of the Word document it is not considered suitable for this particular scenario.  If the document was being collaborated on such that two or more users may want to see the changes taking place in real-time it is likely that this approach would be used.

Consequently the plan at this time is to have the routing of the survey create an XML structure from the responses provided and to send that XML data structure to Word via a DLL that handles the updating of the custom part.

Creating the XML to send to the custom part
IBM SPSS Data Collection writes scripts in a VBscript like language.  It is very extendible being able to access the capabilities of any external com components available to it.  The syntax used, consequently, to build a new XML file is very recognisable to those who have written VBscript or DCScript (Data Collection Script) before:

set xmlPart = CreateObject("Microsoft.XMLDOM")

Data Collection Script (DCScript) does not used strongly typed variables but the variables do have to be declared.  Consequently a Dim statement for xmlPart is required before this script.  Once this is done it is possible to create the relevant nodes.  The first is the root node that appears at the top of the XML.  This would usually be done using createElement method, however, this method does not support namespaces so the more complex createNode is required:

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

The createNode method has a constant Node_Element that has a value of 1.  This constant is declared at the top of the script.  The createNode is followed by an appendChild method to add this node to the XML document.  This process can be repeated for the personal and incident nodes.  In the case of the incident node there is two attributes associated to this node.  The code used to generated these two attributes is the same as the code used to generate almost all the attributes in the XML document.  In each case an IF statement is used to check whether there was a response to the question.  If a response is present the value is applied to the Incident Node using the setAttribute method, otherwise the setAttribute method is used to create a blank value.  This ensures that the attribute is present so that the Word document can be mapped correctly and the structure of the XML is consistent.

if IncidentDate.Response.Value <> null then
                xmlIncident.setAttribute("cao2:when", IncidentDate.Response.Value)
else
                xmlIncident.setAttribute("cao2:when", "")
end if

if IncidentDestination.Response.Value <> null then
                xmlIncident.setAttribute("cao2:destination", IncidentDestination.Response.Value)
else
                xmlIncident.setAttribute("cao2:destination", "")
end if

The exception to this methodology is the Title and Severity questions.  Both of these questions are categorical (single punch) and so the response.value is an array (e.g. {Mr}).  Even though, being single punch these responses only ever have one element in the array it is necessary to turn the response into a text string before applying it to the XML.  Data Collection provides the format function to convert (amongst other things) categorical responses into either their categorical name or their categorical label.

Format(questionname.response.value, “a”) provides the name of the category

Format(questionname.response.value,”b”) provides the label of the category

Although as a rule DCScript is not case sensitive.  This function is one of the exceptions.  It is essential that the code (“a” or “b”) is in lower case to deliver the right result.  As a consequence the writing of these responses into the XML looks like this:

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

The whole routing is as follows:

Routing(Web)

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

                IOM.LayoutTempla te = "RailComplaint.htm"

                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)

                if IncidentDate.Response.Value <> null then
                                xmlIncident.setAttribute("cao2:when", IncidentDate.Response.Value)
                else
                                xmlIncident.setAttribute("cao2:when", "")
                end if

                if IncidentDestination.Response.Value <> null then
                                xmlIncident.setAttribute("cao2:destination", IncidentDestination.Response.Value)
               
else
                                xmlIncident.setAttribute("cao2:destination", "")
                end if

                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.Value,"b"))
                else
                                xmlNode.setAttribute("cao2:title", "")
                end if

                if fname.Response.Value <> null then
                                xmlNode.setAttribute("cao2:fname", fname.response.Value)
                else
                                xmlNode.setAttribute("cao2:fname", "")
                end if

                if lname.Response.Value <> null then
                                xmlNode.setAttribute("cao2:lname", lname.response.Value)
                else
                                xmlNode.setAttribute("cao2:lname", "")
                end if

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

                if address1.Response.Value <> null then
                                xmlNode.setAttribute("cao2:line1", address1.Response.Value)
                else
                                xmlNode.setAttribute("cao2:line1", "")
                end if

                if address2.Response.Value <> null then
                                xmlNode.setAttribute("cao2:line2", address2.Response.Value)
                else
                                xmlNode.setAttribute("cao2:line2", "")
                end if

                if Town.Response.Value <> null then
                                xmlNode.setAttribute("cao2:town", town.Response.Value)
                else
                                xmlNode.setAttribute("cao2:town", "")
                end if

                if postcode.Response.Value <> null then
                                xmlNode.setAttribute("cao2:postcode", postcode.Response.Value)
                else
                                xmlNode.setAttribute("cao2:postcode", "")
                end if

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

                if Phone.Response.Value <> null then
                                xmlNode.setAttribute("cao2:work", Phone.Response.Value)
                else
                                xmlNode.setAttribute("cao2:work", "")
                end if

                if Phone2.Response.Value <> null then
                                xmlNode.setAttribute("cao2:home", Phone2.Response.Value)
                else
                                xmlNode.setAttribute("cao2:home", "")
                end if

                if Mobile.Response.Value <> null then
                                xmlNode.setAttribute("cao2:mobile", Mobile.Response.Value)
               
else
                                xmlNode.setAttribute("cao2:mobile", "")
                end if

                if eMail.Response.Value <> null then
                                xmlNode.setAttribute("cao2:email", eMail.Response.Value)
                else
                                xmlNode.setAttribute("cao2:email", "")
                end if

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

                if IncidentDepart.Response.Value <> null then
                                xmlNode.setAttribute("cao2:from", IncidentDepart.Response.Value)
                else
                                xmlNode.setAttribute("cao2:from", "")
                end if

                if IncidentExpected.Response.Value <> null then
                                xmlNode.setAttribute("cao2:expected", IncidentExpected.Response.Value)
                else
                                xmlNode.setAttribute("cao2:expected", "")
                end if

                if IncidentActual.Response.Value <> null then
                                xmlNode.setAttribute("cao2:actual", IncidentActual.Response.Value)
                else
                                xmlNode.setAttribute("cao2:actual", "")
                end if

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

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

                if ExpectedOutcome.Response.Value <> null then
                                xmlNode.setAttribute("cao2:outcome", ExpectedOutcome.Response.Value)
                else
                                xmlNode.setAttribute("cao2:outcome", "")
                end if

                if CostIncurred.Response.Value <> null then
                                xmlNode.setAttribute("cao2:cost", CostIncurred.Response.Value)
                else
                                xmlNode.setAttribute("cao2:cost", "")
                end if

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

                if IncidentSeverity.Response.Value <> null then
                                xmlPart.createTextNode(IncidentDescription.Response.Value)
                else
                                xmlPart.createTextNode("")
                end if

                debug.Log(xmlPart.xml)

End Routing

 

Filed under  //   DCScript   IBM   IBM SPSS Data Collection   OpenXML   PASW Data Collection   SPSS   VBScript   XML  

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 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