-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdataset.tcl
More file actions
276 lines (227 loc) · 10.1 KB
/
Copy pathdataset.tcl
File metadata and controls
276 lines (227 loc) · 10.1 KB
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Copyright (c) 2022-2024 Nicolas ROBERT.
# Distributed under MIT license. Please see LICENSE for details.
#
namespace eval ticklecharts {}
# From 'Apache echarts documentation' :
# (https://echarts.apache.org/handbook/en/concepts/dataset/) :
#
# 'dataset' is a component dedicated to manage data. Although you can set
# the data in series.data for every series, we recommend you use the 'dataset'
# to manage the data since ECharts 4 so that the data can be reused by multiple
# components and convenient for the separation of "data and configs".
# After all, data is the most common part to be changed while other
# configurations will mostly not change at runtime.
oo::class create ticklecharts::dataset {
variable _dataset
variable _dimension
constructor {value} {
# Initializes a new dataset Class.
#
# value - dataset args.
#
set _dimension "nothing"
# https://stackoverflow.com/questions/16131296/the-real-namespace-of-a-class
set ns [namespace qualifiers [self class]]
if {![ticklecharts::isListOfList $value]} {
set value [list $value]
}
foreach line [split [lindex [info class constructor [self class]] 1] "\n"] {
if {[string match {*-minversion [0-9]*} $line]} {
# Adds minus sign to key to control
# Simply to check whether properties with a minus sign are also accepted.
${ns}::setdef options -[lindex $line 2] -minversion {} -validvalue {} -type null -default "nothing"
}
}
foreach item $value {
if {[llength $item] % 2} {
error "dataset 'item' list must have an even number of elements."
}
# Keep compatibility with previous versions.
# Removes the minus sign at the beginning of the key.
# Note : Both are accepted, with or without.
foreach {key info} $item {
if {[string range $key 0 0] eq "-"} {
set item [dict remove $item $key]
set newkey [string range $key 1 end]
dict set item $newkey $info
}
}
set source [my source $item sType]
${ns}::setdef options id -minversion 5 -validvalue {} -type str|null -default "nothing"
${ns}::setdef options sourceHeader -minversion 5 -validvalue formatSourceHeader -type str|bool|num|null -default "nothing"
${ns}::setdef options dimensions -minversion 5 -validvalue {} -type list.j|null -default [my dimensions $item]
${ns}::setdef options source -minversion 5 -validvalue {} -type list.$sType|null -default $source
${ns}::setdef options transform -minversion 5 -validvalue {} -type list.o|null -default [my transform $item]
${ns}::setdef options fromDatasetIndex -minversion 5 -validvalue {} -type num|null -default "nothing"
${ns}::setdef options fromDatasetId -minversion 5 -validvalue {} -type str|null -default "nothing"
${ns}::setdef options fromTransformResult -minversion 5 -validvalue {} -type num|null -default "nothing"
if {$sType eq "o"} {
set item [dict remove $item transform dimensions source]
} else {
set item [dict remove $item transform dimensions]
}
# Set dataset.
lappend opts [${ns}::merge $options $item]
set options {}
}
set _dataset [list {*}$opts]
}
}
oo::define ticklecharts::dataset {
method get {} {
# Returns dataset.
return $_dataset
}
method getType {} {
# Returns type.
return "dataset"
}
method dim {} {
# Returns data dimensions.
return $_dimension
}
method dimensions {value} {
# Set dimension.
#
# value - dict
#
# Returns dimension
if {![ticklecharts::keyDictExists "dimensions" $value key]} {
return "nothing"
}
set ns [namespace qualifiers [self class]]
ticklecharts::ldset {d vald} -to ""
# Note : The 'dim' variable can contain, a structure from the eStruct class, a dictionary
# from the eDict class, a simple string value or pure Tcl dictionary.
foreach dim [dict get $value $key] {
if {
[ticklecharts::iseDictClass $dim] || ([ticklecharts::isDict $dim] &&
([dict exists $dim value] || [dict exists $dim name] || [dict exists $dim type]))
} {
if {[ticklecharts::iseDictClass $dim]} {set dim [$dim get]}
${ns}::setdef options name -minversion 5 -validvalue {} -type str|null -default "nothing"
${ns}::setdef options value -minversion 5 -validvalue {} -type num|null -default "nothing"
${ns}::setdef options type -minversion 5 -validvalue formatDimType -type str|null -default "nothing"
lappend d [list [new edict [${ns}::merge $options $dim]] dict]
} elseif {[ticklecharts::iseStructClass $dim]} {
lappend d [list $dim [$dim sType]]
} else {
lappend vald [ticklecharts::mapSpaceString $dim]
}
}
if {![llength $d]} {
set _dimension [list [list $vald list.s]]
} else {
set _dimension [join [list [list [list $vald list.s]] $d]]
}
return $_dimension
}
method transform {value} {
# Transform dataset.
#
# value - dict
#
# Returns list transform value(s)
if {![ticklecharts::keyDictExists "transform" $value key]} {
return "nothing"
}
set ns [namespace qualifiers [self class]]
foreach item [dict get $value $key] {
if {[llength $item] % 2} {
error "transform 'item' list must have an even number of elements."
}
${ns}::setdef options type -minversion 5 -validvalue formatTransform -type str -default "filter"
${ns}::setdef options config -minversion 5 -validvalue {} -type dict|null -default [ticklecharts::config $item]
${ns}::setdef options print -minversion 5 -validvalue {} -type bool -default "False"
# Remove key(s)
set item [dict remove $item config]
lappend opts [${ns}::merge $options $item]
set options {}
}
return [list {*}$opts]
}
method source {value t} {
# Source dataset.
#
# value - dict
# t - upvar type
#
# Returns 'source' data value
upvar 1 $t type
set type "d"
if {![ticklecharts::keyDictExists "source" $value key]} {
return "nothing"
}
# 'source' support 2 types :
# 1) 2d array, where dimension names can be provided in the first row/column,
# or do not provide, only data. :
# set source {
# {"score" "amount" "product"}
# {89.3 58212 "Matcha Latte"}
# {57.1 78254 "Milk Tea"}
# {74.4 41032 "Cheese Cocoa"}
# {50.1 12755 "Cheese Brownie"}
# {...}
# }
# 2) "array of classes" format ('source' should be a 'elist class'):
# Define the dimension of array. In cartesian coordinate system,
# if the type of x-axis is category, map the first dimension to
# x-axis by default, the second dimension to y-axis.
# You can also specify 'series.encode' to complete the map
# without specify dimensions. Please see below.
# set source {
# {product "Matcha Latte" "2015" 43.3 "2016" 85.8 "2017" 93.7}
# {product "Milk Tea" "2015" 83.1 "2016" 73.4 "2017" 55.1}
# {product "Cheese Cocoa" "2015" 86.4 "2016" 65.2 "2017" 82.5}
# {...}
# }
set d [dict get $value $key]
set ns [namespace qualifiers [self class]]
if {[ticklecharts::iseListClass $d]} {
if {![ticklecharts::isListOfList {*}[$d get]]} {
error "'source' should be a list of list."
}
foreach item {*}[$d get] {
if {[llength $item] % 2} {
error "source 'item' list must have an even number of elements."
}
set k {}
foreach {key info} $item {
if {[llength [lsearch -all -exact $item $key]] > 1} {
error "'$key' is duplicated in '$item'"
}
# Replaces spaces if present.
set mapKey [ticklecharts::mapSpaceString $key]
lappend k $key $mapKey
set mytype [ticklecharts::typeOf $info]
if {$mytype ni {str num}} {
error "'$info' should be a string or num for this '$key' value."
}
${ns}::setdef options $mapKey -minversion 5 -validvalue {} -type $mytype -default $info
}
lappend opts [${ns}::merge $options [string map $k $item]]
set options {}
}
set type "o"
return [list {*}$opts]
} else {
if {![ticklecharts::isListOfList $d]} {
error "'source' should be a list of list."
}
return $d
}
}
}
proc ticklecharts::isdatasetClass {value} {
# Check if the value is a 'dataset' class.
#
# value - obj or string
#
# Returns true if 'value' is a dataset class,
# false otherwise.
return [expr {
[ticklecharts::isAObject $value] &&
[string match "*::dataset" [ticklecharts::typeOfClass $value]]
}
]
}