update_predictions.rkt 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #lang racket/base
  2. ; Add new predictions, update predictions, add outcomes
  3. (require racket/cmdline
  4. racket/date
  5. racket/list
  6. db)
  7. (require "scoring_rules.rkt")
  8. (define progname "update_predictions.rkt")
  9. ; load configuration file
  10. (require (file "../config.rkt"))
  11. ; give us the date in YYYY-MM-DD format
  12. (date-display-format 'iso-8601)
  13. ; set up command line arguments
  14. (define mode (command-line
  15. #:program "update_prediction"
  16. #:args ([updatetype "help"]) ; (add, update, list-open, list-closed, help)
  17. updatetype))
  18. ; check a date, if blank return current date
  19. (define (verify-or-get-date datestr)
  20. (if (regexp-match-exact? #px"\\d{4}-\\d{2}-\\d{2}" datestr)
  21. datestr
  22. (date->string (current-date))))
  23. ; print some help
  24. (define (printhelp)
  25. (displayln (string-append "Usage: "
  26. progname
  27. " MODE"))
  28. (newline)
  29. (displayln "Where MODE is one of:")
  30. (displayln " add\t\t - add new prediction to database.")
  31. (displayln " update\t\t - update a prediction with results or cancel a prediction.")
  32. (displayln " list-open\t - Show all predictions that do not yet have outcomes.")
  33. (displayln " list-closed\t - Show all predictions that have outcomes.")
  34. (displayln " help\t\t - Show this help message.")
  35. (newline)
  36. (displayln "Copyright 2019-2020 George C. Privon"))
  37. ; set up a condensed prompt for getting information
  38. (define (getinput prompt)
  39. (display(string-append prompt ": "))
  40. (read-line))
  41. ; add a new prediction
  42. (define (addpred)
  43. ; manually get incremented ID
  44. (define lastID (query-maybe-value conn "SELECT ID FROM predictions ORDER BY ID DESC LIMIT 1"))
  45. (define nID
  46. (if lastID
  47. (+ 1 lastID)
  48. (+ 1 0)))
  49. (define prediction (getinput "Enter the prediction"))
  50. (define fprob (getinput "Enter your forecast probability"))
  51. (define comments (getinput "Comments on the forecast"))
  52. (define categories (getinput "Enter any categories (comma-separated)"))
  53. (define date (getinput "Enter the date of the forecast (YYYY-MM-DD or leave blank to use today's date)"))
  54. (define enterdate (verify-or-get-date date))
  55. (query-exec conn "INSERT INTO predictions (ID, date, prediction, forecast, comments, categories) values (?,?, ?, ?, ?, ?)"
  56. nID enterdate prediction fprob comments categories))
  57. ; print a prediction given an ID. optionally write outcome and Brier score
  58. (define (printpred ID [score #f])
  59. ; print out information on a specific forecast
  60. ; if score is true, print out outcome (1 or 0) and Brier score
  61. (display ((λ (myID)
  62. (define prediction (query-value conn "SELECT prediction FROM predictions WHERE ID=? ORDER BY date ASC LIMIT 1" myID))
  63. (define lastf (query-row conn "SELECT date, forecast FROM predictions WHERE ID=? AND forecast IS NOT NULL ORDER BY date DESC LIMIT 1" myID))
  64. (string-append (number->string myID)
  65. "("
  66. (vector-ref lastf 0)
  67. ") "
  68. prediction
  69. ": "
  70. (number->string (vector-ref lastf 1))))
  71. ID))
  72. (cond
  73. [score (display ((λ (myID)
  74. (define outcome (query-value conn "SELECT outcome FROM predictions WHERE ID=? AND outcome IS NOT NULL LIMIT 1" myID))
  75. (define lastf (query-value conn "SELECT forecast FROM predictions WHERE ID=? AND forecast IS NOT NULL ORDER BY date DESC LIMIT 1" myID))
  76. (string-append ", "
  77. (number->string outcome)
  78. ", "
  79. (real->decimal-string (brier-score lastf outcome) 3)))
  80. ID))])
  81. (newline))
  82. (define (printpred-with-score ID)
  83. (printpred ID #t))
  84. (define (printpred-without-score ID)
  85. (printpred ID #f))
  86. ; update a prediction
  87. (define (updatepred ID)
  88. (define option (string->number (getinput "Enter \"1\" to add an updated prediction or \"2\" to enter an outcome")))
  89. (cond
  90. [(eq? option 1) (reviseprediction ID)]
  91. [(eq? option 2) (addoutcome ID)]))
  92. ; add a new forecast to an existing prediction
  93. (define (reviseprediction ID)
  94. (define newf (string->number (getinput "What is your new predction")))
  95. (define date (getinput "Enter the date of the updated prediction (YYYY-MM-DD or leave blank to use today's date)"))
  96. (define newfdate (verify-or-get-date date))
  97. (define comments (getinput "Comments on the new prediction"))
  98. (query-exec conn "INSERT INTO predictions (ID, date, forecast, comments) values (?, ?, ?, ?)"
  99. ID newfdate newf comments))
  100. ; enter an outcome
  101. (define (addoutcome ID)
  102. (define lastpred (query-value conn "SELECT forecast FROM predictions WHERE ID=? ORDER BY date DESC LIMIT 1" ID))
  103. (define outcome (string->number (getinput "What is the outcome (0 for didn't happen, 1 for happened, -1 to cancel prediction)")))
  104. (define date (getinput "Enter the date of the outcome (YYYY-MM-DD or leave blank to use today's date)"))
  105. (define outcomedate (verify-or-get-date date))
  106. (define comments (getinput "Comments on the outcome"))
  107. (cond
  108. [(not (or (eq? outcome 0)
  109. (eq? outcome 1)
  110. (eq? outcome -1))) (error "Outcome must be 0 or 1.\n")])
  111. (query-exec conn "INSERT INTO predictions (ID, date, outcome, comments) values (?, ?, ?, ?)"
  112. ID outcomedate outcome comments)
  113. ; only show Brier scores if there was an outcome
  114. (cond
  115. [(or (eq? outcome 0)
  116. (eq? outcome 1))
  117. (displayln (string-append "Brier score of most recent forecast: "
  118. (real->decimal-string (brier-score lastpred outcome) 3)))]))
  119. ; print open predictions
  120. (define (printopen)
  121. ; get a list of all IDs
  122. (define allIDs (query-list conn
  123. "SELECT DISTINCT ID FROM predictions"))
  124. ; get list of resolved predictions
  125. (define resIDs (query-list conn
  126. "SELECT DISTINCT ID FROM predictions WHERE outcome IS NOT NULL"))
  127. ; remove the IDs that are resolved, keeping only the open predictions
  128. (define uIDs (filter-map (λ (testID)
  129. (if (member testID resIDs) #f testID))
  130. allIDs))
  131. (cond
  132. [(eq? (length uIDs) 0) (displayln "No open predictions.") ]
  133. [else ; print a header and individual entry information
  134. (displayln "ID(DATE) PREDICTION: LATEST FORECAST")
  135. (map printpred-without-score uIDs)]))
  136. ; print resolved predictions
  137. (define (printres [score #f])
  138. (define uIDs (query-list conn
  139. "SELECT DISTINCT ID FROM predictions WHERE outcome IS NOT NULL"))
  140. (displayln "ID(DATE) PREDICTION: LAST FORECAST, OUTCOME, BRIER SCORE")
  141. (map printpred-with-score uIDs))
  142. ; find unresolved predictions
  143. (define (findpending)
  144. (printopen)
  145. (define upID (getinput "Please enter a prediction number to edit (enter 0 or nothing to exit)"))
  146. (cond
  147. [(eq? (string->number upID) 0) (exit)]
  148. [(string->number upID) (updatepred (string->number upID))]
  149. [else (exit)]))
  150. ; make sure we can use the sqlite3 connection
  151. (cond (not (sqlite3-available?))
  152. (error "Sqlite3 library not available."))
  153. ; open the database file
  154. (define conn (sqlite3-connect #:database dbloc))
  155. ; determine which mode we're in
  156. (cond
  157. [(regexp-match "help" mode) (printhelp)]
  158. [(regexp-match "add" mode) (addpred)]
  159. [(regexp-match "update" mode) (findpending)]
  160. [(regexp-match "list-open" mode) (printopen)]
  161. [(regexp-match "list-closed" mode) (printres)]
  162. [else (error (string-append "Unknown mode. Try " progname " help\n\n"))])
  163. ; close the databse
  164. (disconnect conn)