Keep up with COVID-19 though Domino!

Are you are suddenly sitting at home with nothing to do, due to the corona virus COVID-19? You can’t go to the gym. You can’t go to your favorite computer store to browse all the latest hardware and plan for your next water-cooled build. You can’t go out to eat.

But what you can do is to try out some of the new functionality in HCL Notes and Domino. Today I started a little project where I try to incorporate a number of the latest and coolest functions in a simple but useful application. I hope to be able to create several blog posts about this project over the next few days.

I came up with the idea for this app when someone sent me a link to a web page where information about COVID-19 is aggregated from all US states. I thought it was a neat page, but then I noticed that they have a public API where the numbers are served up. Now things started to become much more interesting! This is what the JSON data looks like:

[
  {
    "state": "AK",
    "positive": 6,
    "negative": 400,
    "pending": null,
    "death": null,
    "total": 406,
    "lastUpdateEt": "3/18 16:30",
    "checkTimeEt": "3/19 15:12"
  },{
    "state": "AL",
    "positive": 68,
    "negative": 28,
    "pending": null,
    "death": 0,
    "total": 96,
    "lastUpdateEt": "3/19 10:45",
    "checkTimeEt": "3/19 15:15"
  },{
    "state": "AR",
    "positive": 46,
    "negative": 310,
    "pending": 113,
    "death": null,
    "total": 469,
    "lastUpdateEt": "3/19 11:23",
    "checkTimeEt": "3/19 15:16"
  },{
    "state": "AS",
    "positive": 0,
    "negative": null,
    "pending": null,
    "death": 0,
    "total": 0,
    "lastUpdateEt": "3/14 00:00",
    "checkTimeEt": "3/19 16:18"
  },{
    "state": "AZ",
    "positive": 44,
    "negative": 175,
    "pending": 130,
    "death": 0,
    "total": 349,
    "lastUpdateEt": "3/19 00:00",
    "checkTimeEt": "3/19 15:18"
  },{
    "state": "CA",
    "positive": 924,
    "negative": 8787,
    "pending": null,
    "death": 18,
    "total": 9711,
    "lastUpdateEt": "3/19 14:25",
    "checkTimeEt": "3/19 15:20"
  }
...

So what could I do with this data? Why not bring it into a Domino database to start with, and then retrieve the data on a schedule, say every hour?

We should then be able to chart the data for each state over time. In order to not store the same data over and over again, I want to check if the data has been modified since the last time the agent ran. I will just use the lastUpdateEt date and time stamp in combination with the state abbreviation to perform a lookup. If I get any result(s) back, the data was already stored.

So how do you read the JSON from the API? In the past I would have used my own HTTP Request class, but this is not needed anymore. This is thanks to the NotesHTTPRequest class, first introduced in Domino 10 and then improved in Domino 11. In Domino 11 the wizards at HCL in Chelmsford added classes to parse JSON. The NotesJSONNavigator is the base of the parser, then you use NotesJSONArray, NotesJSONObject and NotesJSONElement to traverse through a JSON payload. When you get the hang of it, this is much easier than it maybe sounds at first.

So let’s take a look at my code. This is a scheduled agent, running once an hour:

%REM
  Agent Retrieve Data
  Created Mar 19, 2020 by Karl-Henry Martinsson/DBS
%END REM

Option Public
Option Declare

Sub Initialize
  Dim session As New NotesSession
  Dim db As NotesDatabase
  Dim view As NotesView
  Dim http As NotesHTTPRequest
  Dim json As NotesJSONNavigator
  Dim element As NotesJSONElement
  Dim stateArray As NotesJSONArray
  Dim state As NotesJSONObject
  Dim response As Variant
  Dim url As String

  Set db = session.CurrentDatabase
  Set view = db.GetView("LookupExisting")
  Call view.Refresh()

  Set http = session.CreateHTTPRequest()
  url = "https://covidtracking.com/api/states"
  response = http.get(url)
  Set json = session.CreateJSONNavigator(response)
  Set element = json.GetFirstElement()
  Do Until element Is Nothing
    Set state = element.Value
    Call processState(state, db, view)
    Set element = json.GetNextElement()
  Loop
End Sub

Function processState(state As NotesJSONObject, db As NotesDatabase, view As NotesView)
  Dim doc As NotesDocument
  Dim col As NotesViewEntryCollection
  Dim values List As String 
  Dim element As NotesJSONElement
  Dim key As String
  Dim value As String
  Dim stateName As String
  Dim lastUpdate As String 

  Set doc = New NotesDocument(db)
  doc.Form = "StateData"
  Set element = state.GetFirstElement()
  Do Until element Is Nothing 
    key = element.Name
    value = element.Value
    If key="state" Then
      stateName = value
    End If
    If Right$(key,2)="Et" Then
      lastUpdate = Format$(CDat(value),"mm/dd/yyyy hh:nn AM/PM")
      Call doc.ReplaceItemValue("lastUpdated", lastUpdate)
    Else 
      Call doc.ReplaceItemValue(key, value)	
    End If
    Set element = state.GetNextElement()
  Loop
  Set col = view.GetAllEntriesByKey(stateName+"^"+lastUpdate)
  If col.count=0 Then
    Call doc.Save(True,False)
  End If
End Function

This is all the code you need. Yes, I am serious. You can now consume any data on the web, served up by any system with a REST API, straight into Domino, with just a few lines for code.

The NotesHTTPRequest is very straight forward, so there is not much to say about it. But the classes used to parse JSON may need some explanation.
You start with the NotesJSONNavigator. You then use the value property of the NotesJSONElement class to get a value, an array or an object. The array or object is put into a NotesJSONArray or NotesJSONObject object, and you can then traverse down into the JSON structure. This is very powerful and useful, we have all been asking IBM for this functionality for many years. Now HCL delivers!

I created a hidden view for the lookup to avoid the same data stored multiple times. It only contains one (sorted) column, which is used by the lookup:

That is pretty much it.

I also created a view to display the data:

Soon we will do something more fun with the data. I will just let the scheduled agent run for a while and build up my database first. Keep your eyes open for the next post about this project!

This Post Has 4 Comments

  1. Viet Nguyen

    Hi Karl-Henry,

    I was trying to get some JSON data using a scheduled agent and NotesHTTPRequest object. The code is rather simple, very similar to your code. However, even if I set the URL to the same one as in your code, I cannot get the connection to the site https://covidtracking.com/api/states. The error message from console is “Failed to connect to covidetracking.com port 443”. However, if I run the agent manually, it works. Any idea about this issue?

    Cheers,

    Viet

  2. Viet Nguyen

    Hi Karl,

    Here is the initial code of my schedule agent:

    Dim s As New NotesSession
    url = “https://covidtracking.com/api/states”
    Set http = s.CreateHTTPRequest()

    http.Timeoutsec = 100
    HTTPResponse= http.get(url) ‘ Get the error message – Closing connection 0; Unable to establish connection to remote server
    Set json = s.CreateJSONNavigator(HTTPResponse) ‘ Error on this line as HTTPResponse not set in the previous step

Leave a Reply