-
Notifications
You must be signed in to change notification settings - Fork 7
/
parameters.clj
114 lines (97 loc) · 3.51 KB
/
parameters.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
(ns com.yetanalytics.datasim.input.parameters
"Parameter input specs and parsing."
(:require [clojure.spec.alpha :as s]
[java-time.api :as t]
[xapi-schema.spec :as xs]
[com.yetanalytics.pan.objects.profile :as prof]
[com.yetanalytics.pan.objects.pattern :as pat]
[com.yetanalytics.datasim.math.random :as random]
[com.yetanalytics.datasim.util.errors :as errs])
(:import [clojure.lang ExceptionInfo]
[java.time.zone ZoneRulesException]
[java.time Instant]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Specs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; All options are optional, but everything except `end` will get defaults
;; (optional) start of the simulation (inclusive), 8601 stamp
(s/def ::start
::xs/timestamp)
;; (optional) start of the returned statements (if after ::start).
;; This lets us page through sims to later times. Defaults to ::start
(s/def ::from
::xs/timestamp)
;; (optional) end of the simulation (exclusive), 8601 stamp
(s/def ::end
(s/nilable ::xs/timestamp))
(defn- timezone-string? [s]
(try (t/zone-id s)
(catch ExceptionInfo exi
(if (= ZoneRulesException (type (ex-cause exi)))
false
(throw exi)))))
;; (optional) timezone, defaults to UTC
(s/def ::timezone
(s/and string?
not-empty
timezone-string?))
;; Seed is required, but will be generated if not present
(s/def ::seed
int?)
;; Max number of statements returned
(s/def ::max
pos-int?)
;; Restrict Generation to these profile IDs
(s/def ::gen-profiles
(s/every ::prof/id))
;; Restrict Generation to these pattern IDs
(s/def ::gen-patterns
(s/every ::pat/id))
(defn- ordered-timestamps?
"Are the `start`, `from`, and `end` timestamps ordered properly?"
[{:keys [start from end]}]
(let [start-t (t/instant start)
?from-t (some->> from t/instant)
?end-t (some->> end t/instant)]
(and (or (not ?end-t)
(t/before? start-t ?end-t))
(or (not ?end-t)
(not ?from-t)
(t/before? ?from-t ?end-t))
(or (not ?from-t)
(= ?from-t start-t)
(t/before? start-t ?from-t)))))
(s/def ::parameters
(s/and
(s/keys :req-un [::start
::timezone
::seed]
:opt-un [::end
::from
::max
::gen-profiles
::gen-patterns])
ordered-timestamps?))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Validation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn validate-parameters
[parameters]
(some->> (s/explain-data ::parameters parameters)
(errs/explain-to-map-coll ::parameters)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Defaults
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn apply-defaults
"Apply defaults to `params` with the current time and a random seed.
If `params` is not provided simply return the default parameters."
([]
(apply-defaults {}))
([{:keys [start from timezone seed] :as params}]
(merge
params
(let [start (or start (.toString (Instant/now)))]
{:start start
:from (or from start)
:timezone (or timezone "UTC")
:seed (or seed (random/rand-unbound-int (random/rng)))}))))