artbase 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,329 @@
1
+
2
+ module Artbase
3
+ class Base ## "abstract" Base collection - check -use a different name - why? why not?
4
+
5
+
6
+ def convert_images( overwrite: )
7
+ image_dir = "./#{slug}/token-i"
8
+ Image.convert( image_dir, from: 'jpg', to: 'png', overwrite: overwrite )
9
+ Image.convert( image_dir, from: 'gif', to: 'png', overwrite: overwrite )
10
+ Image.convert( image_dir, from: 'svg', to: 'png', overwrite: overwrite )
11
+ end
12
+
13
+
14
+
15
+ def make_strip
16
+ composite_count = @count - @excludes.size
17
+
18
+ composite = ImageComposite.new( 9, 1,
19
+ width: @width,
20
+ height: @height )
21
+
22
+ i = 0
23
+ each_image do |img, id|
24
+ puts "==> [#{i+1}/9] #{id}"
25
+ composite << img
26
+
27
+ i += 1
28
+ break if i >= 9
29
+ end
30
+
31
+
32
+ composite.save( "./#{@slug}/tmp/#{@slug}-strip.png" )
33
+ end
34
+
35
+
36
+
37
+ def make_composite( limit: nil,
38
+ mirror: false )
39
+ ### use well-known / pre-defined (default) grids
40
+ ## (cols x rows) for now - why? why not?
41
+
42
+ composite_count = if limit
43
+ limit
44
+ else
45
+ @count - @excludes.size
46
+ end
47
+
48
+ cols, rows = case composite_count
49
+ when 99 then [10, 10]
50
+ when 100 then [10, 10]
51
+ when 150 then [15, 10]
52
+ when 314 then [15, 21]
53
+ when 500 then [25, 20]
54
+ when 1000 then [25, 40]
55
+ when 2200 then [50, 44]
56
+ when 2222 then [50, 45]
57
+ when 2469 then [50, 50]
58
+ when 3000 then [100, 30] ## or use 50*60 - why? why not?
59
+ when 3500 then [100, 35] ## or use 50*x ??
60
+ when 3979 then [100, 40]
61
+ when 4000 then [100, 40] ## or use 50x80 - why? why not?
62
+ when 4444 then [100, 45] ## or use 50x??
63
+ when 5000 then [100, 50] ## or use 50x100 - why? why not?
64
+ when 5555 then [100, 56] # 5600 (45 left empty)
65
+ when 6666 then [100, 67] # 6700 (34 left empty)
66
+ when 6688 then [100, 67] # 6700 (12 left empty)
67
+ when 6969 then [100, 70] # 7000 (31 left empty)
68
+ when 7500 then [100, 75]
69
+ when 8888 then [100, 89]
70
+ when 9969 then [100,100]
71
+ when 10000 then [100,100]
72
+ else
73
+ raise ArgumentError, "sorry - unknown composite count #{composite_count}/#{@count} for now"
74
+ end
75
+
76
+ composite = ImageComposite.new( cols, rows,
77
+ width: @width,
78
+ height: @height )
79
+
80
+
81
+ count = 0
82
+ each_image do |img, id|
83
+ puts "==> #{id}"
84
+ composite << if mirror
85
+ img.mirror
86
+ else
87
+ img
88
+ end
89
+
90
+ count += 1
91
+ break if limit && count >= limit
92
+ end
93
+
94
+
95
+ slug = "#{@slug}"
96
+ slug += "#{limit}" if limit
97
+ slug += "_left" if mirror
98
+
99
+ path = "./#{@slug}/tmp/#{slug}-#{@width}x#{@height}.png"
100
+ puts " saving #{path}..."
101
+ composite.save( path )
102
+
103
+ if composite_count < 1000
104
+ path = "./#{@slug}/tmp/#{slug}-#{@width}x#{@height}@2x.png"
105
+ puts " saving 2x #{path}..."
106
+ composite.zoom(2).save( path )
107
+ end
108
+ end
109
+
110
+
111
+
112
+
113
+ def calc_attribute_counters ## todo/check: use a different name _counts/_stats etc - why? why not?
114
+
115
+ attributes_by_count = { count: 0,
116
+ by_count: Hash.new(0)
117
+ }
118
+ counter = {}
119
+
120
+
121
+ each_meta do |meta, id| ## todo/fix: change id to index
122
+ traits = meta.traits
123
+ # print "#{traits.size} - "
124
+ # pp traits
125
+
126
+ print "#{id}.." if id % 100 == 0 ## print progress report
127
+
128
+ attributes_by_count[ :count ] +=1
129
+ attributes_by_count[ :by_count ][ traits.size ] += 1
130
+
131
+ traits.each do |trait_type, trait_value|
132
+ trait_type = _normalize_trait_type( trait_type )
133
+ trait_value = _normalize_trait_value( trait_value )
134
+
135
+
136
+ rec = counter[ trait_type ] ||= { count: 0,
137
+ by_type: Hash.new(0)
138
+ }
139
+ rec[ :count ] +=1
140
+ rec[ :by_type ][ trait_value ] += 1
141
+ end
142
+ end
143
+
144
+ print "\n"
145
+ puts
146
+
147
+ ## return all-in-one hash
148
+ {
149
+ total: attributes_by_count,
150
+ traits: counter,
151
+ }
152
+ end
153
+
154
+
155
+ def dump_attributes
156
+ stats = calc_attribute_counters
157
+
158
+ total = stats[:total]
159
+ counter = stats[:traits]
160
+
161
+ puts
162
+ puts "attribute usage / counts:"
163
+ pp total
164
+ puts
165
+
166
+ puts "#{counter.size} attribute(s):"
167
+ counter.each do |trait_name, trait_rec|
168
+ puts " #{trait_name} #{trait_rec[:count]} (#{trait_rec[:by_type].size} uniques)"
169
+ end
170
+
171
+ puts
172
+ pp counter
173
+ end
174
+
175
+
176
+
177
+
178
+ ## order - allow "custom" attribute order export
179
+ ## renames - allow renames of attributes
180
+ def export_attributes(
181
+ order: [],
182
+ renames: {}
183
+ )
184
+
185
+ ## step 1: get counters
186
+ stats = calc_attribute_counters
187
+
188
+ total = stats[:total]
189
+ counter = stats[:traits]
190
+
191
+ puts
192
+ puts "attribute usage / counts:"
193
+ pp total
194
+ puts
195
+
196
+ puts "#{counter.size} attribute(s):"
197
+ counter.each do |trait_name, trait_rec|
198
+ puts " #{trait_name} #{trait_rec[:count]} (#{trait_rec[:by_type].size} uniques)"
199
+ end
200
+
201
+
202
+ trait_names = []
203
+ trait_names += order ## get attributes if any in pre-defined order
204
+ counter.each do |trait_name, _|
205
+ if trait_names.include?( trait_name )
206
+ next ## skip already included
207
+ else
208
+ trait_names << trait_name
209
+ end
210
+ end
211
+
212
+
213
+ recs = []
214
+
215
+
216
+ ## step 2: get tabular data
217
+ each_meta do |meta, id| ## todo/fix: change id to index
218
+
219
+ traits = meta.traits
220
+ # print "#{traits.size} - "
221
+ # pp traits
222
+
223
+ print "#{id}.." if id % 100 == 0 ## print progress report
224
+
225
+ ## setup empty hash table (with all attributes)
226
+ rec = {}
227
+
228
+ ## note: use __Slug__& __Name__
229
+ ## to avoid conflict with attribute names
230
+ ## e.g. attribute with "Name" will overwrite built-in and so on
231
+
232
+ rec['__Slug__'] = if respond_to?( :_meta_slugify )
233
+ _meta_slugify( meta, id )
234
+ else
235
+ ## default to id (six digits) as string with leading zeros
236
+ ## for easy sorting using strings
237
+ ## e.g. 1 => '000001'
238
+ ## 2 => '000002'
239
+ '%06d' % id
240
+ end
241
+
242
+ rec['__Name__'] = meta.name
243
+
244
+ ## add all attributes/traits names/keys
245
+ trait_names.reduce( rec ) { |h,value| h[value] = []; h }
246
+ ## pp rec
247
+
248
+ ## note: use an array (to allow multiple values for attributes)
249
+ traits.each do |trait_type, trait_value|
250
+ trait_type = _normalize_trait_type( trait_type )
251
+ trait_value = _normalize_trait_value( trait_value )
252
+
253
+ values = rec[ trait_type ]
254
+ values << trait_value
255
+ end
256
+ recs << rec
257
+ end
258
+ print "\n"
259
+
260
+ ## pp recs
261
+
262
+ ## flatten recs
263
+ data = []
264
+ recs.each do |rec|
265
+ row = rec.values.map do |value|
266
+ if value.is_a?( Array )
267
+ value.join( ' / ' )
268
+ else
269
+ value
270
+ end
271
+ end
272
+ data << row
273
+ end
274
+
275
+
276
+ ## sort by slug
277
+ data = data.sort {|l,r| l[0] <=> r[0] }
278
+ pp data
279
+
280
+ ### save dataset
281
+ ## note: change first colum Slug to ID - only used for "internal" sort etc.
282
+ headers = ['ID', 'Name']
283
+ headers += trait_names.map do |trait_name| ## check for renames
284
+ renames[trait_name] || trait_name
285
+ end
286
+
287
+
288
+ path = "./#{@slug}/tmp/#{@slug}.csv"
289
+ dirname = File.dirname( path )
290
+ FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
291
+
292
+ File.open( path, 'w:utf-8' ) do |f|
293
+ f.write( headers.join( ', ' ))
294
+ f.write( "\n" )
295
+ ## note: replace ID with our own internal running (zero-based) counter
296
+ data.each_with_index do |row,i|
297
+ f.write( ([i]+row[1..-1]).join( ', '))
298
+ f.write( "\n" )
299
+ end
300
+ end
301
+ end
302
+
303
+
304
+
305
+
306
+ #############
307
+ # "private" helpers
308
+
309
+ def _normalize_trait_type( trait_type )
310
+ if @patch && @patch[:trait_types]
311
+ @patch[:trait_types][ trait_type ] || trait_type
312
+ else
313
+ trait_type
314
+ end
315
+ end
316
+
317
+ def _normalize_trait_value( trait_value )
318
+ if @patch && @patch[:trait_values]
319
+ @patch[:trait_values][ trait_value ] || trait_value
320
+ else
321
+ trait_value
322
+ end
323
+ end
324
+
325
+
326
+
327
+
328
+ end # class Base
329
+ end # module Artbase
@@ -1,39 +1,39 @@
1
-
2
-
3
- class ImageCollection
4
-
5
- attr_reader :slug, :count
6
-
7
- def initialize( slug, count,
8
- image_base: ) # check: rename count to items or such - why? why not?
9
- @slug = slug
10
- @count = count
11
- @image_base = image_base
12
- end
13
-
14
- def download_images( range=(0...@count) )
15
- start = Time.now
16
- delay_in_s = 0.3
17
-
18
- range.each do |offset|
19
- image_src = @image_base.sub( '{id}', offset.to_s )
20
-
21
- puts "==> #{offset} - #{@slug}..."
22
-
23
- ## note: will auto-add format file extension (e.g. .png, .jpg)
24
- ## depending on http content type!!!!!
25
- copy_image( image_src, "./#{@slug}/image-i/#{offset}" )
26
-
27
- stop = Time.now
28
- diff = stop - start
29
-
30
- mins = diff / 60 ## todo - use floor or such?
31
- secs = diff % 60
32
- puts "up #{mins} mins #{secs} secs (total #{diff} secs)"
33
-
34
- puts "sleeping #{delay_in_s}s..."
35
- sleep( delay_in_s )
36
- end
37
- end
38
- end # class ImageCollection
39
-
1
+
2
+
3
+ class ImageCollection
4
+
5
+ attr_reader :slug, :count
6
+
7
+ def initialize( slug, count,
8
+ image_base: ) # check: rename count to items or such - why? why not?
9
+ @slug = slug
10
+ @count = count
11
+ @image_base = image_base
12
+ end
13
+
14
+ def download_images( range=(0...@count) )
15
+ start = Time.now
16
+ delay_in_s = 0.3
17
+
18
+ range.each do |offset|
19
+ image_src = @image_base.sub( '{id}', offset.to_s )
20
+
21
+ puts "==> #{offset} - #{@slug}..."
22
+
23
+ ## note: will auto-add format file extension (e.g. .png, .jpg)
24
+ ## depending on http content type!!!!!
25
+ copy_image( image_src, "./#{@slug}/image-i/#{offset}" )
26
+
27
+ stop = Time.now
28
+ diff = stop - start
29
+
30
+ mins = diff / 60 ## todo - use floor or such?
31
+ secs = diff % 60
32
+ puts "up #{mins} mins #{secs} secs (total #{diff} secs)"
33
+
34
+ puts "sleeping #{delay_in_s}s..."
35
+ sleep( delay_in_s )
36
+ end
37
+ end
38
+ end # class ImageCollection
39
+