<commit_before>(ns frontend.models.build
  (:require [frontend.datetime :as datetime]
            [frontend.models.project :as proj]
            [frontend.state :as state]
            [frontend.utils :as utils :include-macros true]
            [frontend.utils.github :as github]
            [goog.string :as gstring]
            goog.string.format))

;; TODO paths should use secretary
(defn path-for [project build]
  (str "/gh/" (proj/project-name project) "/" (:build_num build)))

(defn path-for-parallelism [build]
  (str "/gh/" (:username build) "/" (:reponame build) "/edit#parallel-builds"))

(defn github-revision [build]
  (when (:vcs_revision build)
    (subs (:vcs_revision build) 0 7)))

(defn github-commit-url [build]
  (when (:vcs_revision build)
    (gstring/format "%s/commit/%s" (:vcs_url build) (:vcs_revision build))))

(defn vcs-ref-name [build]
  (cond
    (:branch build) (.replace (:branch build) (js/RegExp. "^remotes\\/origin\\/") "")
    (:vcs_tag build) (:vcs_tag build)
    :else "unknown"))

(defn author [build]
  (or (:author_name build) (:author_email build)))

(defn committer [build]
  (or (:committer_name build) (:committer_email build)))

(defn author-isnt-committer [{:keys [committer_email author_email committer_name author_name] :as build}]
  (or (not= committer_email author_email)
      (not= committer_name author_name)))

(defn ui-user [build]
  (let [user (:user build)]
    (cond
      (not-empty (:name user)) (:name user)
      ;; Currently, if there's no user associated with a build, we get a "user",
      ;; but its login is "none". Until we have a better representation,
      ;; recognize that as a sentinel value.
      (not= "none" (:login user)) (:login user))))

(defn status-icon [build]
  (cond (= "success" (:outcome build)) "fa-check"
        (= "running" (:status build)) "fa-refresh"
        (= "scheduled" (:status build)) "fa-calendar-o"
        (#{"canceled" "killed" "failed" "timedout" "no_tests"} (:status build)) "fa-times"
        (#{"queued" "not_running"} (:status build)) "fa-clock-o"
        (#{"not_run" "infrastructure_fail"} (:status build)) "fa-ban"
        :else nil))

(defn running? [build]
  (and (:start_time build)
       (not (:stop_time build))))

(defn duration [{:keys [start_time stop_time] :as build}]
  (let [start-time (when start_time (js/Date.parse start_time))
        stop-time (when stop_time (js/Date.parse stop_time))]
    (cond (= "canceled" (:status build)) "canceled"
          (and start-time stop-time) (datetime/as-duration (- stop-time start-time))
          start-time (datetime/as-duration (- (.getTime (js/Date.)) start-time))
          :else nil)))

(defn pretty-start-time [build]
  (str (datetime/time-ago (js/Date.parse (:start_time build)))
       " ago"))

(defn finished? [build]
  (or (:stop_time build) (:canceled build)))

(defn in-usage-queue? [build]
  (and (not (finished? build))
       (not (:queued_at build))))

(defn in-run-queue? [build]
  (and (not (finished? build))
       (:queued_at build)
       (not (:start_time build))))

(defn run-queued-time [{:keys [start_time stop_time queued_at] :as build}]
  (cond (and start_time queued_at) (- (js/Date.parse start_time) (js/Date.parse queued_at))
        ;; canceled before left queue
        (and queued_at stop_time) (- (js/Date.parse stop_time) (js/Date.parse queued_at))
        queued_at (- (.getTime (js/Date.)) (js/Date.parse queued_at))
        :else 0))

(defn usage-queued-time [{:keys [stop_time queued_at usage_queued_at] :as build}]
  (cond (and usage_queued_at queued_at) (- (js/Date.parse queued_at) (js/Date.parse usage_queued_at))
        ;; canceled before left queue
        (and usage_queued_at stop_time) (- (js/Date.parse stop_time) (js/Date.parse usage_queued_at))
        usage_queued_at (- (.getTime (js/Date.)) (js/Date.parse usage_queued_at))
        :else 0))

(defn queued-time [build]
  (+ (usage-queued-time build)
     (run-queued-time build)))

(defn queued-time-summary [build]
  (if (> 0 (run-queued-time build))
    (gstring/format "%s waiting + %s in queue"
                    (datetime/as-duration (usage-queued-time build))
                    (datetime/as-duration (run-queued-time build)))
    (gstring/format "%s waiting for builds to finish"
                    (datetime/as-duration (usage-queued-time build)))))

(defn not-run-words [build]
  (case (:dont_build build)
    "ci-skip"            "skipped"
    "branch-blacklisted" "skipped"
    "org-not-paid"       "not paid"
    "user-not-paid"      "not paid"
    "not run"))

(defn status-words [build]
  (condp = (:status build)
    "infrastructure_fail" "circle bug"
    "timedout" "timed out"
    "no_tests" "no tests"
    "not_run" (not-run-words build)
    "not_running" "not running"
    (:status build)))

(defn status-icon-v2 [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "Status-Failed"
        (#{"retried" "canceled" "infrastructure_fail" "killed" "not_run"} (:status build)) "Status-Cancelled"
        (= "success" (:outcome build)) "Status-Passed"
        (= "running" (:status build)) "Status-Running"
        (#{"queued" "not_running"} (:status build)) "Status-Queued"
        :else nil))

(defn status-class [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "fail"
        (#{"infrastructure_fail" "killed" "not_run" "retried" "canceled"} (:status build)) "stop"
        (= "success" (:outcome build)) "pass"
        (= "running" (:status build)) "busy"
        (#{"queued" "not_running"} (:status build)) "queued"
        :else nil))

(defn favicon-color [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "red"
        (#{"infrastructure_fail" "killed" "not_run"} (:status build)) "orange"
        (= "success" (:outcome build)) "green"
        (= "running" (:status build)) "blue"
        (#{"queued" "not_running" "scheduled" "retried"} (:status build)) "grey"
        ;; undefined is the default dark blue
        :else "undefined"))

(defn can-cancel? [build]
  (and (not= "canceled" (:status build))
       (#{"not_running" "running" "queued" "scheduled"} (:lifecycle build))))

(defn current-user-ssh?
  "Whether the given user has SSH access to the build"
  [build user]
  (->> build :ssh_users (map :github_id) (some #{(:github_id user)})))

(defn someone-else-ssh?
  "Whether a user other than the given one has SSH access to the build"
  [build user]
  (->> build :ssh_users (map :github_id) (not-any? #{(:github_id user)})))

(defn ssh-enabled-now?
  "Whether anyone can currently SSH into the build"
  [build]
  (and (seq (:ssh_users build))<commit_msg>Scheduled state uses Queued icon.<commit_after>(ns frontend.models.build
  (:require [frontend.datetime :as datetime]
            [frontend.models.project :as proj]
            [frontend.state :as state]
            [frontend.utils :as utils :include-macros true]
            [frontend.utils.github :as github]
            [goog.string :as gstring]
            goog.string.format))

;; TODO paths should use secretary
(defn path-for [project build]
  (str "/gh/" (proj/project-name project) "/" (:build_num build)))

(defn path-for-parallelism [build]
  (str "/gh/" (:username build) "/" (:reponame build) "/edit#parallel-builds"))

(defn github-revision [build]
  (when (:vcs_revision build)
    (subs (:vcs_revision build) 0 7)))

(defn github-commit-url [build]
  (when (:vcs_revision build)
    (gstring/format "%s/commit/%s" (:vcs_url build) (:vcs_revision build))))

(defn vcs-ref-name [build]
  (cond
    (:branch build) (.replace (:branch build) (js/RegExp. "^remotes\\/origin\\/") "")
    (:vcs_tag build) (:vcs_tag build)
    :else "unknown"))

(defn author [build]
  (or (:author_name build) (:author_email build)))

(defn committer [build]
  (or (:committer_name build) (:committer_email build)))

(defn author-isnt-committer [{:keys [committer_email author_email committer_name author_name] :as build}]
  (or (not= committer_email author_email)
      (not= committer_name author_name)))

(defn ui-user [build]
  (let [user (:user build)]
    (cond
      (not-empty (:name user)) (:name user)
      ;; Currently, if there's no user associated with a build, we get a "user",
      ;; but its login is "none". Until we have a better representation,
      ;; recognize that as a sentinel value.
      (not= "none" (:login user)) (:login user))))

(defn status-icon [build]
  (cond (= "success" (:outcome build)) "fa-check"
        (= "running" (:status build)) "fa-refresh"
        (= "scheduled" (:status build)) "fa-calendar-o"
        (#{"canceled" "killed" "failed" "timedout" "no_tests"} (:status build)) "fa-times"
        (#{"queued" "not_running"} (:status build)) "fa-clock-o"
        (#{"not_run" "infrastructure_fail"} (:status build)) "fa-ban"
        :else nil))

(defn running? [build]
  (and (:start_time build)
       (not (:stop_time build))))

(defn duration [{:keys [start_time stop_time] :as build}]
  (let [start-time (when start_time (js/Date.parse start_time))
        stop-time (when stop_time (js/Date.parse stop_time))]
    (cond (= "canceled" (:status build)) "canceled"
          (and start-time stop-time) (datetime/as-duration (- stop-time start-time))
          start-time (datetime/as-duration (- (.getTime (js/Date.)) start-time))
          :else nil)))

(defn pretty-start-time [build]
  (str (datetime/time-ago (js/Date.parse (:start_time build)))
       " ago"))

(defn finished? [build]
  (or (:stop_time build) (:canceled build)))

(defn in-usage-queue? [build]
  (and (not (finished? build))
       (not (:queued_at build))))

(defn in-run-queue? [build]
  (and (not (finished? build))
       (:queued_at build)
       (not (:start_time build))))

(defn run-queued-time [{:keys [start_time stop_time queued_at] :as build}]
  (cond (and start_time queued_at) (- (js/Date.parse start_time) (js/Date.parse queued_at))
        ;; canceled before left queue
        (and queued_at stop_time) (- (js/Date.parse stop_time) (js/Date.parse queued_at))
        queued_at (- (.getTime (js/Date.)) (js/Date.parse queued_at))
        :else 0))

(defn usage-queued-time [{:keys [stop_time queued_at usage_queued_at] :as build}]
  (cond (and usage_queued_at queued_at) (- (js/Date.parse queued_at) (js/Date.parse usage_queued_at))
        ;; canceled before left queue
        (and usage_queued_at stop_time) (- (js/Date.parse stop_time) (js/Date.parse usage_queued_at))
        usage_queued_at (- (.getTime (js/Date.)) (js/Date.parse usage_queued_at))
        :else 0))

(defn queued-time [build]
  (+ (usage-queued-time build)
     (run-queued-time build)))

(defn queued-time-summary [build]
  (if (> 0 (run-queued-time build))
    (gstring/format "%s waiting + %s in queue"
                    (datetime/as-duration (usage-queued-time build))
                    (datetime/as-duration (run-queued-time build)))
    (gstring/format "%s waiting for builds to finish"
                    (datetime/as-duration (usage-queued-time build)))))

(defn not-run-words [build]
  (case (:dont_build build)
    "ci-skip"            "skipped"
    "branch-blacklisted" "skipped"
    "org-not-paid"       "not paid"
    "user-not-paid"      "not paid"
    "not run"))

(defn status-words [build]
  (condp = (:status build)
    "infrastructure_fail" "circle bug"
    "timedout" "timed out"
    "no_tests" "no tests"
    "not_run" (not-run-words build)
    "not_running" "not running"
    (:status build)))

(defn status-icon-v2 [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "Status-Failed"
        (#{"retried" "canceled" "infrastructure_fail" "killed" "not_run"} (:status build)) "Status-Cancelled"
        (= "success" (:outcome build)) "Status-Passed"
        (= "running" (:status build)) "Status-Running"
        (#{"queued" "not_running" "scheduled"} (:status build)) "Status-Queued"
        :else nil))

(defn status-class [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "fail"
        (#{"infrastructure_fail" "killed" "not_run" "retried" "canceled"} (:status build)) "stop"
        (= "success" (:outcome build)) "pass"
        (= "running" (:status build)) "busy"
        (#{"queued" "not_running" "scheduled"} (:status build)) "queued"
        :else nil))

(defn favicon-color [build]
  (cond (#{"failed" "timedout" "no_tests"} (:status build)) "red"
        (#{"infrastructure_fail" "killed" "not_run"} (:status build)) "orange"
        (= "success" (:outcome build)) "green"
        (= "running" (:status build)) "blue"
        (#{"queued" "not_running" "scheduled" "retried"} (:status build)) "grey"
        ;; undefined is the default dark blue
        :else "undefined"))

(defn can-cancel? [build]
  (and (not= "canceled" (:status build))
       (#{"not_running" "running" "queued" "scheduled"} (:lifecycle build))))

(defn current-user-ssh?
  "Whether the given user has SSH access to the build"
  [build user]
  (->> build :ssh_users (map :github_id) (some #{(:github_id user)})))

(defn someone-else-ssh?
  "Whether a user other than the given one has SSH access to the build"
  [build user]
  (->> build :ssh_users (map :github_id) (not-any? #{(:github_id user)})))

(defn ssh-enabled-now?
  "Whether anyone can currently SSH into the build"
  [build]
  (and (seq (:ssh_users build))
{'fraction_non_alphanumeric': 0.16864840457555688, 'fraction_numerical': 0.0009030704394942806, 'mean_word_length': 3.351342501637197, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '110794', 'n_tokens_mistral': 4761, 'n_tokens_neox': 4548, 'n_words': 1235}