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!
Very cool Karl, thanks for putting this together. One thing to note: as of 10.0.1 FP2 you can return a JSONNavigator directly (helps with various translation issues). See https://hclpnpsupport.hcltech.com/csm?id=kb_article&sysparm_article=KB0068788 for support details.
http.PreferJSONNavigator = true
set json = http.get(url)
Saves a few lines of code and will save you from some potential headaches with the JSON data.
Thanks for that info, Dave!
That is a great little feature, very helpful.
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
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