I went away from the computer for an hour and only 400 or so had been imported! This is a 2019 iMac with 128 GB RAM, i9 processor, etc.
Fortunately, I can just import the historical data and then only add the daily data for each day, but it had been so long since I'd attempted anything in a FMP loop that I had forgotten what a show-stopper that approach can be.
Using FMP to import these data would take about 14 hours... I recall there are some performance workarounds, but I didn't take the time to find those again and I really don't want to have to change my workflow to accommodate FMP.
If I didn't already have the historical data, I would have to, at this point, write yet another JDBC program + Java JSON parsing and have that program stuff the data into FMP which would probably take about a minute or two (plus the time to write the code -- probably 30 mins).
If anyone is interested, I can post the CSV I converted from the JSON at the URL above.
(Spoiler alert: The same logic in Java to parse the JSON and to write all data to Maria DB takes less than 3 milliseconds)
Since the GROUP BY queries were nearly hanging FMP, I moved the project to MariaDB using a data tool which has all the graphics tools built in. That tool can also import JSON.
Insert from URL [ Select ; With dialog: Off ; Target: $json ; "https://covidtracking.com/api/states" ]
Go to Layout [ “covid_history” (covid_history) ; Animation: None ]
make sure we haven't already downloaded today's data
Set Error Capture [ On ]
Set Variable [ $today ; Value: JSONGetElement($json; "[0].date") ]
Enter Find Mode [ Pause: Off ]
Set Field [ covid::thedate ; $today ]
Perform Find []
Set Variable [ $loopCounter ; Value: 0 ]
Set Variable [ $numValues ; Value: // get total number of JSON elements. Pick one that's not likely to have dup in text PatternCount ( $json ; "inICUCumulative" ) ]
If [ Get (FoundCount) = 0 ]
Loop
# new record
New Record/Request
Set Field [ covid_history::hospitalizedCurrently ; $today ]
Set Field [ covid_history::date ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".date") ]
Set Field [ covid_history::state ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".state") ]
Set Field [ covid_history::positive ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".positive") ]
Set Field [ covid_history::negative ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".negative") ]
Set Field [ covid_history::pending ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".pending") ]
Set Field [ covid_history::hospitalizedCurrently ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".hospitalizedCurrently") ]
Set Field [ covid_history::hospitalizedCumulative ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".hospitalizedCumulative") ]
Set Field [ covid_history::inIcuCurrently ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".inIcuCurrently") ]
Set Field [ covid_history::inIcuCumulative ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".inIcuCumulative") ]
Set Field [ covid_history::onVentilatorCurrently ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".onVentilatorCurrently") ]
Set Field [ covid_history::onVentilatorCumulative ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".onVentilatorCumulative") ]
Set Field [ covid_history::dataQualityGrade ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".dataQualityGrade") ]
Set Field [ covid_history::lastUpdateEt ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".lastUpdateEt") ]
Set Field [ covid_history::dateModified ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".dateModified") ]
Set Field [ covid_history::checkTimeEt ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".checkTimeEt") ]
Set Field [ covid_history::death ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".death") ]
Set Field [ covid_history::hospitalized ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".hospitalized") ]
Set Field [ covid_history::dateChecked ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".dateChecked") ]
Set Field [ covid_history::totalTestsViral ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".totalTestsViral") ]
Set Field [ covid_history::positiveTestsViral ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".positiveTestsViral") ]
Set Field [ covid_history::negativeTestsViral ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".negativeTestsViral") ]
Set Field [ covid_history::positiveCasesViral ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".positiveCasesViral") ]
Set Field [ covid_history::fips ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".fips") ]
Set Field [ covid_history::positiveIncrease ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".positiveIncrease") ]
Set Field [ covid_history::negativeIncrease ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".negativeIncrease") ]
Set Field [ covid_history::total ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".total") ]
Set Field [ covid_history::totalTestResults ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".totalTestResults") ]
Set Field [ covid_history::totalTestResultsIncrease ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".totalTestResultsIncrease") ]
Set Field [ covid_history::posNeg ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".posNeg") ]
Set Field [ covid_history::deathIncrease ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".deathIncrease") ]
Set Field [ covid_history::hospitalizedIncrease ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".hospitalizedIncrease") ]
Set Field [ covid_history::hash ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".hash") ]
Set Field [ covid_history::commercialScore ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".commercialScore") ]
Set Field [ covid_history::negativeRegularScore ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".negativeRegularScore") ]
Set Field [ covid_history::negativeScore ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".negativeScore") ]
Set Field [ covid_history::positiveScore ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".positiveScore") ]
Set Field [ covid_history::score ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".score") ]
Set Field [ covid_history::grade ; JSONGetElement ( $json ; "[" & $loopCounter & "]" & ".grade") ]
#
Set Variable [ $loopCounter ; Value: $loopCounter +1 ]
# are we done?
Exit Loop If [ $loopCounter > $numValues ]
End Loop
Else
Show Custom Dialog [ "Already Imported" ; "Data for today already imported." ]
End If
I also think, that if you were to reduce the list of elements as you go through it, it will get faster. I was able to create all 5000 records in about 3-4 mins. At the same time, rewriting the variable every time may not actually make it faster.
You will gain an important speed improvement if you first isolate the loopCounter structure before you extract the structure's elements. FileMaker's JSONGetElement function parses the JSON from the beginning every time it is called.
As for deleting the structure every time you process it as @jormond suggests… I don't know. I suppose it depends on the difference between the time it takes FileMaker to write the new JSON to memory and the time saved barely parsing the JSON.
thanks to Christian you can just implement this or take one of his sample files and be done in 15 seconds including creating the table and the fields for the results. URL can afterwards be shortened to only get the data set of the current day.
One ceveat though: it's only imported as text, so field types have to be set afterwards
I shortened the Script for readability but is's all here
Go to Layout [ “CoronaData” (CoronaData) ; Animation: None ]
Insert from URL [ Select ; With dialog: Off ; CoronaData::gJsonImport ; "https://covidtracking.com/api/states/daily" ]
Set Variable [ $s ; Value: MBS("[JSON.Import.Status]") ]
Exit Loop If [ $s ≠"Working" ]
If [ MBS("[ProgressDialog.GetCancel]") = 1 ]
Set Variable [ $r ; Value: MBS("[ProgressDialog.Hide]") ]
Set Variable [ $r ; Value: MBS("[JSON.Import.Cancel]") ]
Exit Script [ Text Result: ]
End If
End Loop
Set Variable [ $r ; Value: MBS("[ProgressDialog.Hide]") ]
Set Variable [ $StoppTS ; Value: Get( CurrentTimeUTCMilliseconds ) ]
Show Custom Dialog [ "JSON Import" ; $r & " " & $s ]
End If
Cleanup prorgress bar
Set Variable [ $r ; Value: MBS("[ProgressDialog.Hide]") ]
...but I really wanted to try that JSON.Import thing
and that is blazingly fast in this situation...
I use JSON.Parse a lot too and it is really great stuff
I like MBS a lot, but don't want to be dependent on it or need to pay hundreds of dollars for a server version. And, I'm no longer doing this particular project using FileMaker.
Take a look at the video output below where I did the import into MariaDB using Java/JDBC and a (compiled) prepared statement -- from the URL directly.
The video shows, even with outputting progress to the screen (for the video), which takes some time, the entire JSON process, including reading from the URL and INSERTing all historical data into MariaDB takes >>> 2 seconds <<< .
If I just time the loop that parses the JSON and then writes the data, currently about 5,600 rows and 39 fields, to MariaDB, that takes less than 3 milliseconds.
Now that these data are in MariaDB, all SQL queries happen in about 1/10th of a second or less.
Since it doesn't use any proprietary SQL, this code is (mostly, if not all) database independent. Therefore, by just changing the login user/password, the same code should work with FileMaker, SQL Server, Oracle, etc.
Well, maybe not....You would think after four releases, FMP 16, FMP 17, FMP 18, and now, FMP 19, that JSON handling would be improved. If it has been, I don't see it or recall any feature improvement announcements. JSON in FMP still feels half done (at most) to me.