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