Data Extraction

5 Posts tagged with the json tag

The previous posts on this application have provided some insight on how to get general profile and report data.  We have also parsed the results that were returned assuming that we used the JSON data format.  So now we are just about ready to create a REST URL that will allow us to fetch some actual report data.  But before we do there is one last task to be performed and that is getting a list of measures that are available for our desired report.  We need to do that just in case we want to limit the measures return to specific measures rather than all.  One other element we are interested in knowing is whether or not we can use the “search” functionality to limit the results based on contents in the dimension data.  Whether or not we can do that is determined by whether or not the report is multi-dimensional or hierarchical.

 

The REST URL to make this request to get what is referred to as the report meta data is shown below.  This URL clearly defines both the Profile and the Report desired via their ID values.  The “info” field defines this as a request for the report meta data.

 

     https://ws.webtrends.com/v1_1/ReportService/profiles/YcL4a5dufF6/reports/95df19b6d9f2/info/?format=json

 

This request is made in the same way that previous requests are made and were discussed in my prior posts.  The resulting data in JSON format will look something like the data below.  Remember, however, that there are no spaces or other extraneous characters returned.  These are shown here to make this easy to read.

 

{
    "accountID" : 18079,
    "profileID" : "YcL4a5dufF6",
    "ID" : "95df19b6d9f2",
    "name" : "Browsers",
    "language" : null,
    "type" : "dimensional",
    "properties" : {
        "isHierarchy" : false,
        "intervalsEnabled" : false,
        "internalID" : "topbrowsers_v",
        "IsRealTimeCompatible" : true,
        "ProfileCategory" : null
    },
    "dimension" : {
        "ID" : "Desc",
        "name" : "Browsers",
        "type" : "data",
        "Range" : null,
        "Properties" : null,
        "SubDimension" : null
    },
    "measures" : [
        {
            "name" : "Views",
            "accumulationType" : null,
            "ID" : "Hits-0",
            "columnID" : 0,
            "measureFormatType" : "numeric",
            "AllowTotals" : true
        },
        {
            "name" : "Visits",
            "accumulationType" : null,
            "ID" : "Users-0",
            "columnID" : 0,
            "measureFormatType" : "numeric",
            "AllowTotals" : true
        }
    ]
}

 

The entire result set is enclosed in curly brackets – “{  }”.  There are within this set of data three (3) main segments of data.  The first segment is general report information.  Here we have such information as the Profile ID, the Report ID, the Report Name, and other general properties.  It is here that we find a couple of critical pieces of data. These are both in the properties area: isHierarchy”, and "intervalsEnabled".  The “isHierarchy” value tells us whether or not we can use the search functionality.  A value of “false” indicates this is a single dimension report and that we can do this.  On the other hand, a value of “true” indicates that the report contains more than a single dimension and that we cannot use the search functionality.  The second property item that may be of interest to us is the "intervalsEnabled" item.  The value of this item tells whether or not trends data is available and we can use those time periods to retrieve data. Again, a value of “false” means interval/trend data is not available and “true” means that interval/trend data is available.  This sample application has not implemented or enabled this feature, so for our discussion here the point is moot.

 

The second segment of data in our result is a list of dimensions.  In a single dimension report this list is obviously short and appears as shown above.  In a multi-dimensional report the sub dimensions are listed as shown below.

 

"dimension" : {

        "ID" : "contentgroup",

        "name" : "Content Group",

        "type" : "data",

        "Range" : null,

        "Properties" : null,

        "SubDimension" : {

            "ID" : "xIfaV44n7l5",

            "name" : "Content Sub-Group",

            "type" : "data",

            "Range" : null,

            "Properties" : null,

            "SubDimension" : null

        }

    },

 

Note that each sub dimension is listed as a hierarchical item of the dimension above it.

 

The key set of data that we want out of this result data is the list of measures.  The measures are all in a third segment bounded by square brackets – “[  ]”.  Each individual measure is defined within its own space bounded by curly brackets – “{  }”.  For each measure there are a number of definition items including, "name", "accumulationType", "ID","columnID", "measureFormatType", and "AllowTotals".  For purposes of our application we only need two of these values: “name” and “ID”.  The name filed is used for display to the user in the interface and the ID is used when we create the REST URL to request our data.  In the application we store this data in a custom data type.  This data type is shown below.

 

 
Public Type MEASUREDATA
    name As String
    id As String
    accumulationType As String
    columnID As String
    measureFormatType As String
    allowTotals As String
End Type
Public g_Measures() As MEASUREDATA
 

The function below is used to extract the data we need from the JSON result.  Once extracted the list of measures is available for the user to select. A check is also made to either enable or disable the use of the “search” functionality based on this being a single or multi-dimensional report.

 

Public Sub GetReportMeta()

   

    ' Get Report Meta so we can have list of measures

    Dim m_intWhere As Integer

    Dim m_strIsHier As String

   

    frmProfileList.lstMeasures.Clear                       ' Clear form we will display list on

    g_intMeasureCnt = 0                                    ' Initialize the profile count to 0

       

    ' Check to see if the user has selected the Profile Summary

    If frmProfileList.lstReports.ListIndex = frmProfileList.lstReports.ListCount - 1 Then

        frmProfileList.optMultiple.Enabled = False

        frmProfileList.optSingle.Enabled = False

        frmProfileList.optSEPeriods.Value = True

        Exit Sub                ' Exit this sub because there are no choices for measures

    Else

        frmProfileList.optMultiple.Enabled = True

        frmProfileList.optSingle.Enabled = True

        frmProfileList.optSEPeriods.Enabled = True

    End If

   

    ' Set up URL for fetching Report Meta

    g_strURL = g_strBaseREST & g_strSelectedProfileID & g_strReportsREST & g_Reports(frmProfileList.lstReports.ListIndex).id & g_strReportsMetaREST

   

    ' Call function to execute REST URL

    If Not RunREST(g_strURL, g_strUserName, g_strPassword) Then

        Exit Sub

    End If

       

    ' Check to see if the report is hierarchical - no search on those

    m_intWhere = InStr(1, g_strResult, "isHierarchy")                ' Find start point for measures list

    m_strIsHier = Mid(g_strResult, m_intWhere + 13, 4)

    If m_strIsHier = "true" Then

        frmProfileList.txtSearch.Enabled = False            ' disable text entry in search box

    Else

        frmProfileList.txtSearch.Enabled = True             ' enable text entry in search box

    End If

   

    ' Display the JSON URL Result

    m_intWhere = InStr(1, g_strResult, "[")                ' Find start point for measures list

    g_strResulta = Mid(g_strResult, m_intWhere + 1, Len(g_strResult) - (m_intWhere + 2))

   

    g_blnMore = True

    Do While g_blnMore

        g_intSplit = InStr(1, g_strResulta, "},")                 ' See if there is more than one entry

        If g_intSplit = 0 Then

            g_strResultb = Mid(g_strResulta, 2, Len(g_strResulta) - 2)

        Else

            g_strResultb = Mid(g_strResulta, 2, g_intSplit - 2)   ' Grab the first set of data

        End If

        g_strResultb = Replace(g_strResultb, """", "")            ' Get rid of the extra " characters

        g_strArr = Split(g_strResultb, ",")                       ' Create an array with this set of data

        ReDim Preserve g_Measures(g_intMeasureCnt)                ' Make room for new measure

        g_Measures(g_intMeasureCnt).name = Mid(g_strArr(0), 6)           ' Store the measure name

        frmProfileList.lstMeasures.AddItem g_Measures(g_intMeasureCnt).name    ' Add name for selection from form

        g_Measures(g_intMeasureCnt).id = Mid(g_strArr(2), 4)             ' Store the Measure ID

        g_Measures(g_intMeasureCnt).accumulationType = Mid(g_strArr(1), 18) ' Store the Accumulation Type

        g_Measures(g_intMeasureCnt).allowTotals = Mid(g_strArr(5), 13) ' Store the Allow Totals value

        g_Measures(g_intMeasureCnt).columnID = Mid(g_strArr(3), 10) ' Store the Column ID

               

       

        ' Check to see if we are done parsing

        If g_intSplit = 0 Then

            g_blnMore = False                             ' We're done

        End If

        g_strResulta = Right(g_strResulta, Len(g_strResulta) - g_intSplit - 1)   ' Set up for next profile

        g_intMeasureCnt = g_intMeasureCnt + 1               ' Bump the profile count

    Loop

   

    Application.Cursor = xlNorthwestArrow           ' Set the application cursor back to normal (arrow)

   

End Sub

 

So, now we have all of our data available in the user interface for the user to select what data will be included in the requested report.  The three keys areas of selection are the time periods, the search data (if available), and the selected measures.  Other selections include limiting what rows to return and how to deal with report totals.  I won’t go into all of the logic on these selections and the interactions, but there are certain checks that are made to ensure consistency and validity of the selected items.  But once these selections are made the user can create the URL and then copy it to the worksheet for eventual execution.

 

That’s all for this post.  The next and last post on this application will deal with processing the results of the report data requests.

 

Michael Love

Senior Solutions Engineer

0 Comments Permalink

This second post on dissecting the Sample Application deals with the process of requesting two separate, but related, sets of report data: the List of Available Reports for a Profile and the List of Report Periods for which data may be available.  I say “may be available” because it is possible that a report may not have existed when the Profile initially began analyzing.  A custom report may have been added some time after that initial set up process.  In that case the report would not have any data until when it had been enabled for the profile and the report template(s) available to the user.  This point really has nothing to do with the application.  I provided this comment only as a point of report data clarification.

 

The process in place within the application is that these two data requests will take place when the user clicks on a Profile Name in the combo list.  The request for Report List is made first followed by the List of Periods.  There is nothing that requires that one be done before the other; it’s just the sequence in which I chose to do them.  The REST URL to request a Report List looks like the following:

 

https://ws.webtrends.com/v1_1/ReportService/profiles/YcL4a5dufF6/reports/?format=json

 

Note that again I use the JSON data format to keep down the amount of data returned.  The process used to create this URL and send it is much like that used for the Profile List.  The difference, of course, is in the format of the REST URL where we need to include the Profile ID and then add the fields to identify the request as one for the Report List.

 

So, the data format that I get back looks very similar to the format used for the Profile List.

 

[

{"accountID":18079,

"profileID":null,

"name":"Creatives",

"ID":"WcTGeY1bNj5",

"language":null,

"type":null,

"Category":"Marketing",

"IsHierarchy":false,

"IntervalsEnabled":false,

"IsRealtimeCompatible":false,

"properties":null

},

{

"accountID":18079,

"profileID":null,

"name":"Creative Types",

"ID":"uZK6QjyBNj5",

"language":null,

"type":null,

"Category":"Marketing",

"IsHierarchy":false,

"IntervalsEnabled":false,

"IsRealtimeCompatible":false,

"properties":null

}

]

 

The formatting here has been added only to make reading the data easier.  The JSON format returns one long string bounded by square brackets – “[  ]”.  Within these brackets each report is detailed within a set of curly brackets – “{  }”.  Within those brackets are the individual meta items that define the report in a general format.  The two items that are really important to us here are the “name” and the “ID”.  The application parses this data using the following code.

 

' Display the JSON URL Result

    ' We need to parse out the results to get the data we need

    frmProfileList.lstReports.Clear                  ' Clear form we will display list on

    g_intReportCnt = 0                               ' Initialize the profile count to 0

    g_strResulta = Mid(g_strResult, 2, Len(g_strResult) - 2)       ' Strip the [] characters from each end

    g_blnMore = True

    Do While g_blnMore

        g_intSplit = InStr(1, g_strResulta, "},")    ' See if there is more than one entry

        If g_intSplit = 0 Then

            g_strResultb = Mid(g_strResulta, 2, Len(g_strResulta) - 2)  ' Grab the enry when only one left

        Else

            g_strResultb = Mid(g_strResulta, 2, g_intSplit - 2)     ' Grab the first set of data

        End If

        g_strResultb = Replace(g_strResultb, """", "")    ' Get rid of the extra " characters

        g_strArr = Split(g_strResultb, ",")               ' Create an array with this set of data

        ReDim Preserve g_Reports(g_intReportCnt)

        g_Reports(g_intReportCnt).id = Mid(g_strArr(3), 4)                      ' Store the Report ID

        g_Reports(g_intReportCnt).name = Mid(g_strArr(2), 6)                    ' Store the Report Name

        g_Reports(g_intReportCnt).accountid = Mid(g_strArr(0), 11)              ' Store the Account ID

        g_Reports(g_intReportCnt).profileid = Mid(g_strArr(1), 11)              ' Store the Profile ID

        g_Reports(g_intReportCnt).language = Mid(g_strArr(4), 10)               ' Store the Language

        g_Reports(g_intReportCnt).type = Mid(g_strArr(5), 6)                    ' Store the Report Type

        g_Reports(g_intReportCnt).category = Mid(g_strArr(6), 10)               ' Store the Report Category

        g_Reports(g_intReportCnt).ishierarchy = Mid(g_strArr(7), 13)            ' Store Hierarchy vlaue

        g_Reports(g_intReportCnt).intervalsenabled = Mid(g_strArr(8), 18)       ' Store Interval enablement

        g_Reports(g_intReportCnt).isrealtimecompatible = Mid(g_strArr(9), 22)   ' Store Realtime

        g_Reports(g_intReportCnt).properties = Mid(g_strArr(10), 12)            ' Store Properties

       

        ' Check to see if we are done parsing

        If g_intSplit = 0 Then

            g_blnMore = False                             ' We're done

        End If

        g_strResulta = Right(g_strResulta, Len(g_strResulta) - g_intSplit - 1)   ' Set up for next report

        g_intReportCnt = g_intReportCnt + 1               ' Bump the report count

    Loop

   

    ' Sort the report list arrays

    SortReports

   

    ' Load Report Names into form

    For i = LBound(g_Reports) To UBound(g_Reports)

        frmProfileList.lstReports.AddItem g_Reports(i).name     ' Add name for selection from form

    Next

   

The parsing process here is almost identical to what was done for the List of Profiles, the exception being the actual fields of data that are extracted and stored.  To store the data I have again set up a specific data type for the report data.  It looks like the following:

 

Public Type REPORTDATA

    accountid As String

    profileid As String

    name As String

    id As String

    language As String

    type As String

    category As String

    ishierarchy As String

    intervalsenabled As String

    isrealtimecompatible As String

    properties As String

End Type

Public g_Reports() As REPORTDATA

 

While this application does not yet use this, the “intervalsenabled” value is used to determine whether or not the trends option for report is valid.

Prior to loading the report names into the combo list on the form the application will sort that list into alphabetical sequence.  Unfortunately, the list returned from the request does not do that for you.  Using the custom data type here makes that sorting process easier because all of the report information I kept together.

 

The next step in the process here is to get the List of Periods that are available.  This is yet another REST URL format for this set of data.  This REST URL has the following format:

 

https://ws.webtrends.com/v1_1/ReportService/profiles/YcL4a5dufF6/periods/?format=json

 

This REST URL is almost identical to the Report List request with the one exception that it has the “periods” data request defined.  As before, we still use the JSON format for our data type.  This request is submitted just like all of the others we have done.  The format of the result data is shown in the following:

 

{"Report":["2009m01d04","2009m01d05","2009m01d06","2009m01d07","2009m01d08","2009m01d09","2009m01d10","2009w02","2009m01d11","2009m01d12","2009m01d13","2009m01d14","2009m01d15","2009m01d16","2009m01d17","2009w03","2009m01","2009q01","2009"],"Realtime":[“2009m01d18”]}

 

This format is a bit different than what we have seen with Profiles and Reports.  The outside delimiters in this case are the curly brackets – “{  }” – and the inside delimiters are the square brackets – “[  ]”.  In parsing this data all we care about are the individual periods.  The application, at this time also does not deal with the “Realtime” or express data.  That data will be ignored in this version of the application.  So, all we need to parse are the comma-delimited values, without the quote marks that are contained inside the first set of square brackets.  The code to do that looks like the following:

 

' Parse out the URL Result - set into individual time periods

    If InStr(1, g_strResult, "Realtime") Then

        g_strResultb = Mid(g_strResult, 12, (InStr(1, g_strResult, "Realtime") - 15)) ' Need to account for profiles with Express Data

    Else

        g_strResultb = Mid(g_strResult, 12, Len(g_strResult) - 29)    ' No express data

    End If

    g_strResultb = Replace(g_strResultb, """", "")            ' Get rid of the extra " characters

    g_strPeriods() = Split(g_strResultb, ",")                 ' Create an array with this set of data

       

    ' Load data onto Report Periods Form

    frmProfileList.lstPeriods.Clear                       ' Start with empty list

    For i = 0 To UBound(g_strPeriods)

        ReDim Preserve g_strReportPeriod(i)

        frmProfileList.lstPeriods.AddItem g_strPeriods(i)   ' Save in the form

        g_strReportPeriod(frmProfileList.lstPeriods.ListCount - 1) = g_strPeriods(i)    ' Save in our array for use later

    Next

 

This is fairly straightforward code to do this.  However, there are a few other items that we need to add to the list for the user to pick for time periods.  These are the relative starting point of “current day”, “current month”, and “current year”.  So these are manually added to our pick list since they have no representation in the list of periods returned.  The code to do that is as follow:

 

' Add relative time periods at end of form and array

    ReDim Preserve g_strReportPeriod(i + 3)

    frmProfileList.lstPeriods.AddItem "Today"

    g_strReportPeriod(frmProfileList.lstPeriods.ListCount - 1) = "current_day"

    frmProfileList.lstPeriods.AddItem "This Month"

    g_strReportPeriod(frmProfileList.lstPeriods.ListCount - 1) = "current_month"

    frmProfileList.lstPeriods.AddItem "This Year"

    g_strReportPeriod(frmProfileList.lstPeriods.ListCount - 1) = "current_year"

 

So, that ends this portion of the blog post.  In the next blog post we will be getting additional report meta information and dealing with creating a REST URL to fetch actual report data.

 

So, until the next posting,

 

Michael Love

Senior Solutions Engineer

1 Comments Permalink

Recently I posted a blog entry with a Sample Excel/VBA Application.  The idea behind this application was to provide any developer who is interested a baseline set of code for using the REST API for extracting data from Webtrends Analytics.  The latest version of that application is v4 and it supports the v1.1 REST URL API.  This new version is attached to this blog.  Please note that the attachment is a Microsoft Word document.  The application which is and Excel spreadsheet with VBA driving the action is included in that document as an embedded file.

 

I have decided to follow up that posting with more detail on how that application works.  This follow up work will come in a series of postings that sort of start at the beginning of the application and works its way through to the end.  The focus will be on the process of building the REST URLs and then parsing the resultant data from each call.

 

The sample application assumes that there are no REST URLs to begin with and that the user of the application will need to create them interactively.  This aspect of the application builds off the idea of the REST Generator application at http://generator.webtrends.com.  To make this work requires a series of steps and calls to Webtrends to fetch specific data.  I am assuming for purposes of this discussion that the reader has a fair knowledge of VBA with Excel and I will, therefore, not go into detail on every line of code in this application.  The code is documented on a line by line basis.

 

So, let’s start with the sequence in which the application gets its data and what is does with it.  To dynamically, interactively, generate a REST URL that requests report data we need to do the following:

 

1.       Get a list of the profiles available to us.

2.       Get a list of the reports that exist in the desired profile and the time periods that might exist for the reports.

3.       Get the report data.

 

There’s a bit more detail involved in these and I will cover that as I cover each of the mail points in the list above.  This posting will cover just the process involved in requesting a list of profiles.

 

I will warn you now that I am a brute force programmer and you may have better ideas than I do about specific functions and how they work.  That’s good and I welcome any feedback you have.

 

Let’s begin by looking at the general format for a valid REST URL.  For more detail on this I refer you to the Webtrends developer site at http://product.webtrends.com/dxapi/api_description.html.  The general REST URL has the following format:

 

https://ws.webtrends.com/v1.3.1/ReportService.svc/profiles/faj38jaFH/reports/J893kIL34/?period=2009&format=xml

where,

https: = protocol

ws.webtrends.com = site where data request is sent

v1.3.1 = API version number

ReportService.svc = web service called

Faj38jaFH = profile id

J389kIL34 = report id

period=2009 = report period

format=xml = result data format

 

 

The actual format of the REST URL you will use will depend heavily on what the request is targeted to return.  For a list of profiles the REST URL will look like the following:

https://ws.webtrends.com/v1_1/ReportService.svc/profiles/?format=xml

or,

https://ws.webtrends.com/v1_1/ReportService.svc/profiles/?format=json

 

The difference here is the format in which the result information is returned.  In the sample application I have used JSON.  So, this is the format I will reference in this post.

 

Using VBA to request web data in this form is a simple process.  In fact, in the sample application I have included a standard function to make this retrieval, no matter what data is being requested.  This function looks like this:

 

Private Function RunREST(url As String, ByRef username As String, ByRef password As String)

    ' Function will execute a REST url along with the UserName and Password passed as parameters

    Application.Cursor = xlWait                                  ' Set the Wait cursor while executing the REST Call

   

    ' Set up the object needed for the web service request

    Set g_objHttp = CreateObject("MSXML2.XMLHTTP")

   

    ' Open our connection and pass the user credentials

    g_objHttp.Open "GET", url, False, username, password          ' False indicates the call is synchronous - wait for URL Result

    g_objHttp.send                                                ' Send the request

    g_strResult = g_objHttp.responseText                          ' Get the URL Result

   

    ' Check to see if our credentials passed - display error when they don't

    If g_strResult = "Must authenticate" Then

        Application.Cursor = xlNorthwestArrow                     ' Reset the application cursor to normal (arrow)

        MsgBox "Authentication Failed", vbCritical, "Authentication Error"

        username = ""                                             ' Reset User Name

        password = ""                                             ' Reset Password

        RunREST = False                                           ' Set return value for failure

        Exit Function                                             ' Exit - we're done here

    End If

   

    RunREST = True                                                ' Set return value for success

   

End Function

 

The key statements in this function really are those that do the actual call of the REST URL.  They are:

 

g_objHttp.Open "GET", url, False, username, password

g_objHttp.send

g_strResult = g_objHttp.responseText

 

The g_objHttp.Open statement creates an open connection for the HTTP request.  Asscociated with that statement we pass the type of call, “GET”, the actual URL to be used with the call, and the user name and password.  The parameter shown as “False” indicates how this call is to be handled by the application.  The value “False” indicates that it is a synchronous call, meaning that the application will wait for the response before continuing.  Passing the user name and password prevents the user from being challenged when the call is made.  The g_objHttp.send statement does just what is implied – it sends the URL request via the web.  The last statement – g_strResult = g_objHttp.responseText – assigns whatever the result of the request is to our internal variable for further analysis.

 

The remaining statements in this function simply check to determine whether or not the authentication credentials had been accepted or rejected.  If either the user name or password were not valid, the result returned will simply state “Must authenticate”. That’s what this code checks for and displays an appropriate error and returns ‘False” as the function value.

 

Now that we have a result set it is time to do something with the data.  Remember that the data is returned in JSON format.  The JSON format for a list of profiles looks something like this:

 

[{"ID":"YcL4a5dufF6","name":"...sample: Zedesco 2009","AccountID":18079},

{"ID":"CxoSsHxKlF6","name":"..Sample: ExactTarget 2009","AccountID":18079},

{"ID":"1ZnDMawXD16","name":".sample: Z-2005","AccountID":18079},

{"ID":"1To2KI7wD16","name":"Sample: ET-2005","AccountID":18079}]

 

The paragraph formatting has been added to make this more readable.  The actual result is returned as one log string.  The entire string is bounded by square brackets – [].  Each individual set of profile data is bounded by curly brackets – {}.  Eand each of these is separated by a single comma.  Each set of profile data has three name/value pairs.  The names are: “ID”, “name”, and “AccountID”.  Not that these are all surrounded by double quotes and also separated by commas.  So parsing this data out should not be any serious challenge.  Using my brute force coding style I accomplish this with this set of statements:

 

' Display the JSON URL Result

    ' We need to parse all of the results to get what we want

    frmProfileList.lstProfiles.Clear                           ' Clear form we will display list on

    g_intProfileCnt = 0                                        ' Initialize the profile count to 0

    g_strResulta = Mid(g_strResult, 2, Len(g_strResult) - 2)   ' Strip the [] characters from each end

    g_blnMore = True

    Do While g_blnMore

        g_intSplit = InStr(1, g_strResulta, "},")                 ' See if there is more than one entry

        If g_intSplit = 0 Then

            g_strResultb = Mid(g_strResulta, 2, Len(g_strResulta) - 2)  ' Only one entry - grab it

        Else

            g_strResultb = Mid(g_strResulta, 2, g_intSplit - 2)   ' Grab the first set of data

        End If

        g_strResultb = Replace(g_strResultb, """", "")            ' Get rid of the extra " characters

        g_strArr = Split(g_strResultb, ",")                       ' Create an array with this set of data

       ReDim Preserve g_Profiles(g_intProfileCnt)

        g_Profiles(g_intProfileCnt).id = Mid(g_strArr(0), 4)        ' Save the Profile ID

        g_Profiles(g_intProfileCnt).name = Mid(g_strArr(1), 6)      ' Save the Profile Name

        g_Profiles(g_intProfileCnt).accountid = Mid(g_strArr(2), 11)    ' Save the Profile Account ID

       

       

        ' Check to see if we are done parsing

        If g_intSplit = 0 Then

            g_blnMore = False                               ' We're done

        End If

        g_strResulta = Right(g_strResulta, Len(g_strResulta) - g_intSplit - 1)   ' Set up for next profile

        g_intProfileCnt = g_intProfileCnt + 1               ' Bump the profile count

    Loop

   

    ' Sort the resulting array into sequence

    SortProfiles

   

    ' Now load the form with the Profile Names

    For i = LBound(g_Profiles) To UBound(g_Profiles)

        frmProfileList.lstProfiles.AddItem g_Profiles(i).name              ' Add name for selection from form

    Next

 

If you look for this code in the application, you will find it in the “GetProfiles” subroutine.  One of the methods I used to help store the profile data was to set up a specific data type for it.  That data type, found in the Data_Module module looks like this:

 

Public Type PROFILEDATA

    name As String

    id As String

    accountid As String

End Type

Public g_Profiles() As PROFILEDATA

 

Creating this data type makes it easy to keep all of the profile data for one profile together and then be able to sort the data into “name” sequence.  When we present the profile data to the end user we use the Profile Name.  When we use the profile data to later request a list of reports we will need the corresponding Profile ID.

 

The next blog post will cover creating a REST URL to retrieve a List of Reports and what do with that result.

 

Michael Love

Senior Solutions Engineer

0 Comments Permalink

I did a blog entry a while back in regards to doing asynchronous web requests against REST based web services, which is what the Webtrends Web Services are built against.  This entry I'll show how to setup a synchronous web request for data against the services.

 

Benifits:

  • The request is made and in a top down fashion the response is returned with the respective string or stream object data needed.
  • Easier to test versus asynchronous web requests.

 

Disadvantages:

  • The request is made, and will pause or "freeze" while waiting for the response.  In a Windows Forms, WPF, Silverlight (or Flash, etc) client application this will cause the user interface to become unresponsive while waiting for the response.

 

So how do we make a synchronous call?  First create a HttpWebRequest object and instantiate it.

 

            var req = (HttpWebRequest)WebRequest.Create(@"https://ws.webtrends.com/beta/ReportService/profiles/?format=json");

 

Next you'll want to setup credentials.

 

            req.Credentials = new NetworkCredential(@"YourWebTrendsAccount\YouUserName", "YourPassword");

Now make sure when the request is made request the server to use compression.  This is entirely transparent and only needed at this single stage of the request.

 

            req.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

 

Finally set the method of request and other peripheral properties.

 

            req.Method = "Get";

            req.AllowAutoRedirect = true;

 

Once this is done you're ready to submit the request.

 

                WebResponse res = req.GetResponse();

 

To retrieve a simple string object feed through to the end of a the stream object response that is available via the WebResponse object.  This can also prospectively be done with the actualy HttpWebRequest object as it has a GetResponseStream() method.  I've segmented it here for example.

 

                string jsonResponse = (new StreamReader(res.GetResponseStream()).ReadToEnd());

 

The following is the complete code with exception handling.

 

            // Create a request

            var req = (HttpWebRequest)WebRequest.Create(@"https://ws.webtrends.com/beta/ReportService/profiles/?format=json");


            req.Credentials = new NetworkCredential(@"YourWebTrendsAccount\YouUserName", "YourPassword");

            // Add compression request.  IIS will return a compressed data result.

            req.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

            req.Method = "Get";

            req.AllowAutoRedirect = true;


            // Read the response

            try

            {

                WebResponse res = req.GetResponse();

                string jsonResponse = (new StreamReader(res.GetResponseStream()).ReadToEnd());


            }

            catch (WebException exw)

            {

                WebResponse response = exw.Response;

                string error = (new StreamReader(response.GetResponseStream())).ReadToEnd();

            }

 

Hope that helps!  Leave a comment or question if anything comes up while coding!

 

In a following entry the team will be providing more examples in consumption of these results programmaticaly, but as mentioned by Developer_Donut's entry we'll be releasing a software SDK soon.  This SDK will drammatically simplify and provide guidance on how to access and to utilize the web services.

0 Comments Permalink

As many of you know, awhile back the folks over at Codeplex developed a WT reporting data as a widget on their site...it's a good example of leveraging and sharing analytics data:

codeplexwebtrendswidget.png

 

They develop some cool tools at Codeplex, including a  JSON viewer that's simple but effective.  This is a standalone tool, and a visualizer inside Visual Studio, and they went the extra mile and modified the viewer to work inside Fiddler too.  Bonus points for that move.  Here's how it looks inside Fiddler:

codeplexfiddler.png

0 Comments 0 References Permalink