Skip to content

Instantly share code, notes, and snippets.

@jimka2001
Created February 14, 2025 14:03
Show Gist options
  • Save jimka2001/9a88d93eb040f5a4cdb203df0e7c103e to your computer and use it in GitHub Desktop.
Save jimka2001/9a88d93eb040f5a4cdb203df0e7c103e to your computer and use it in GitHub Desktop.
(ns check-activities
(:require
[clojure.java.shell :refer [sh]]
[clojure.data.csv :as csv]
[clojure.java.io :as io])
)
(defn member
"Determines whether the given target is an element of the given sequence (or given set)."
[target items]
(boolean (cond
(empty? items) false
(nil? target) (some nil? items)
(false? target) (some false? items)
(set? items) (contains? items target)
:else (reduce (fn [_acc item]
(if (= item target)
(reduced true)
false)) false items))))
(defn download-csv
"Returns a lazy sequence of hashmaps each with the following keys
corresponding to the headers of the output csv generated from
forge submissions tags-report.
i.e.: id, status, error, ref, groupSlug, assignmentUri,
receivedAt, author, job_id, job_successPercent, job_status
`expected` is a sequence of strings (typically user and tag), any line in the csv
which DOES NOT contain BOTH of these will be ignored and not included as a hashmap
in the return value.
"
[tenant course expected]
;; e.g. tenant = "epita-ing"
;; course = "2025-clojure-elective-promo-2027"
(let [forge (or (first (filter (fn [path]
(.exists (io/file path)))
[(format "%s/.local/bin/forge" (System/getProperty "user.home"))
"/usr/local/bin/forge"]))
"forge")
csv-file (format "%s.csv" course)
;; to install the `forge` program
;; pip install 'git+ssh://[email protected]/forge/tools/cli'
;;
;; to authenticate the first time
;; forge auth token
;;
;; if the authenticate gets corrupted
;; rm ~/.config/forge-cli-credentials.json
;; forge auth token
cmd [forge "submissions" "tags-report"
(format "%s/%s" tenant course)
csv-file]
_ (println [:cmd cmd])
csv-out (apply sh cmd)
_ (println [:csv-out csv-out])
_ (println [:reading csv-file])
lines (csv/read-csv (slurp csv-file) :separator \,)
headers ["id" "status" "error" "ref" "groupSlug" "assignmentUri"
"receivedAt" "author" "job_id" "job_successPercent" "job_status"]]
(assert (= 0 (:exit csv-out))
(:err csv-out))
(assert (= (first lines)
headers))
(for [line (rest lines)
:when (every? (fn [word] (member word line)) expected) ]
(zipmap headers line))))
(defn validate [grouped known-status errors succeeded others]
(doseq [key (keys grouped)]
(assert (member key known-status)
(format "unknown status %s" key)))
(assert (empty? (for [hash errors]
(do (println [:status (get hash "status")
:error (get hash "error")
:tag (get hash "ref")
:hash hash])
hash))))
(doseq [hash succeeded]
(println [:status (get hash "status")
:grade (get hash "job_successPercent")
:tag (get hash "ref")])
(assert (= "xyzzy" (get hash "job_successPercent"))
(format "Grade %s != xyzzy for %s" (get hash "job_successPercent") (get hash "ref"))))
(doseq [hash others]
(println [:status (get hash "status")
:tag (get hash "ref")])))
(defn check []
(let [[user tenant course & tags] *command-line-args*
get-status (fn [hash] (get hash "status"))
get-ref (fn [hash] (get hash "ref"))
known-status ["SUCCEEDED" "ERROR" "PROCESSING" "IDLE"]
]
(loop [tags tags]
(let [hashes (filter (fn [hash]
(and (= user (get hash "author"))
(some (fn [tag]
(= tag (get hash "ref")))
tags)))
(download-csv tenant course [user]))
grouped (group-by get-status
hashes)
errors (get grouped "ERROR")
error-tags (map get-ref errors)
succeeded (get grouped "SUCCEEDED")
succeeded-tags (map get-ref succeeded)
completed-tags (concat succeeded-tags error-tags)
;; compute the hashes which are not completed
others (filter (fn [hash]
(not (member (get-ref hash) completed-tags)))
hashes)]
(validate grouped known-status errors succeeded others)
(let [waiting (filter (fn [tag] (not (member tag completed-tags)))
tags)]
(if (empty? waiting)
;; exit immediately, if we allow clojure to exit on its own, it takes a long
;; time for some reason I don't understand.
(System/exit 0))
(Thread/sleep 10000)
(println [:waiting waiting])
(recur waiting))))))
(check)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment