artbase 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +0 -10
- data/Rakefile +4 -3
- data/lib/artbase/tool.rb +28 -13
- data/lib/artbase/version.rb +2 -2
- data/lib/artbase.rb +12 -78
- metadata +11 -21
- data/lib/artbase/attributes.rb +0 -83
- data/lib/artbase/collection/base.rb +0 -329
- data/lib/artbase/collection/image.rb +0 -39
- data/lib/artbase/collection/opensea.rb +0 -297
- data/lib/artbase/collection/token.rb +0 -400
- data/lib/artbase/collection.rb +0 -12
- data/lib/artbase/helper.rb +0 -169
- data/lib/artbase/image/sample.rb +0 -31
- data/lib/artbase/image.rb +0 -31
- data/lib/artbase/retry.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1060552f98ae102ea9d98593be2cb3dafd13d07e7d5ef20eb77cc4176c7fd94
|
4
|
+
data.tar.gz: 239322b6fcab84714305f14c2bfc91a488dba35b653b906f484f7690a99abec4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c99a8d16e494bf0572db121c2b19843d2db20b8084df874ce123c10a99cfc39a3508733f9b7eb8da450cf95bea019fe8de66cbbb7771f8e007fbaf828bcb65b
|
7
|
+
data.tar.gz: aadfdb81650d27d1f8830755b41c5ab314b2b3de9efe54c76a2b2afa121809123f686438d415d0fb55df02b5fdf498ba6bd9333a8b9e8db4fd4d6dc60e7fad44
|
data/Manifest.txt
CHANGED
@@ -4,15 +4,5 @@ README.md
|
|
4
4
|
Rakefile
|
5
5
|
bin/artbase
|
6
6
|
lib/artbase.rb
|
7
|
-
lib/artbase/attributes.rb
|
8
|
-
lib/artbase/collection.rb
|
9
|
-
lib/artbase/collection/base.rb
|
10
|
-
lib/artbase/collection/image.rb
|
11
|
-
lib/artbase/collection/opensea.rb
|
12
|
-
lib/artbase/collection/token.rb
|
13
|
-
lib/artbase/helper.rb
|
14
|
-
lib/artbase/image.rb
|
15
|
-
lib/artbase/image/sample.rb
|
16
|
-
lib/artbase/retry.rb
|
17
7
|
lib/artbase/tool.rb
|
18
8
|
lib/artbase/version.rb
|
data/Rakefile
CHANGED
@@ -26,9 +26,10 @@ Hoe.spec 'artbase' do
|
|
26
26
|
self.history_file = 'CHANGELOG.md'
|
27
27
|
|
28
28
|
self.extra_deps = [
|
29
|
-
['cocos', '>= 0.1
|
30
|
-
['
|
31
|
-
['
|
29
|
+
['artbase-cocos', '>= 0.0.1'],
|
30
|
+
# ['artbase-importers', '>= 0.0.1'], ## note: make "heavy" sql/sqlite db support "soft" dependency
|
31
|
+
['artserve'],
|
32
|
+
['artq'],
|
32
33
|
]
|
33
34
|
|
34
35
|
self.licenses = ['Public Domain']
|
data/lib/artbase/tool.rb
CHANGED
@@ -74,19 +74,7 @@ class Tool
|
|
74
74
|
if File.exist?( "./#{name}/collection.yml" )
|
75
75
|
path = "./#{name}/collection.yml"
|
76
76
|
puts "==> reading collection config >#{path}<..."
|
77
|
-
|
78
|
-
|
79
|
-
## todo - use TokenCollection.read( ) or such -- why? why not?
|
80
|
-
## or TokenCollection.build( hash ) ?? - why? why not?
|
81
|
-
self.collection = TokenCollection.new(
|
82
|
-
config['slug'],
|
83
|
-
config['count'],
|
84
|
-
token_base: config['token_base'],
|
85
|
-
image_base: config['image_base'],
|
86
|
-
format: config['format'],
|
87
|
-
source: config['source'],
|
88
|
-
offset: config['offset'] || 0
|
89
|
-
)
|
77
|
+
self.collection = TokenCollection.read( path )
|
90
78
|
else
|
91
79
|
## todo/check: keep config.rb alternate name - why? why not?
|
92
80
|
## or use collection.rb only ???
|
@@ -131,6 +119,8 @@ class Tool
|
|
131
119
|
dump_attributes
|
132
120
|
elsif ['x', 'exp', 'export'].include?( command )
|
133
121
|
export_attributes
|
122
|
+
elsif ['b', 'build'].include?( command )
|
123
|
+
build_database
|
134
124
|
elsif ['c', 'composite'].include?( command )
|
135
125
|
make_composite( limit: options[ :limit],
|
136
126
|
mirror: options[ :mirror ])
|
@@ -145,6 +135,31 @@ class Tool
|
|
145
135
|
puts "bye"
|
146
136
|
end
|
147
137
|
|
138
|
+
|
139
|
+
|
140
|
+
def self.build_database
|
141
|
+
puts "===> build database"
|
142
|
+
|
143
|
+
### note. load database "heavy" machinery only on-demand
|
144
|
+
## make it a "soft" dependency for now - why? why not?
|
145
|
+
require 'artbase-importers'
|
146
|
+
|
147
|
+
|
148
|
+
slug = @collection.slug
|
149
|
+
|
150
|
+
importer = Importer.read( "./#{slug}/build.rb" )
|
151
|
+
|
152
|
+
columns = importer.metadata_columns
|
153
|
+
pp columns
|
154
|
+
|
155
|
+
Database.connect( "./#{slug}/artbase.db" )
|
156
|
+
Database.auto_migrate!( columns )
|
157
|
+
|
158
|
+
@collection.import( importer )
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
|
148
163
|
def self.make_composite( limit: nil, mirror: false )
|
149
164
|
puts "==> make composite"
|
150
165
|
@collection.make_composite( limit: limit, mirror: mirror )
|
data/lib/artbase/version.rb
CHANGED
data/lib/artbase.rb
CHANGED
@@ -1,78 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
### add (shared) "global" config
|
16
|
-
module Artbase
|
17
|
-
class Configuration
|
18
|
-
|
19
|
-
#######################
|
20
|
-
## accessors
|
21
|
-
|
22
|
-
## todo/check: keep trailing / in ipfs_gateway - why? why not?
|
23
|
-
def ipfs_gateway() @ipfs_gateway || 'https://ipfs.io/ipfs/'; end
|
24
|
-
def ipfs_gateway=(value) @ipfs_gateway = value; end
|
25
|
-
end # class Configuration
|
26
|
-
|
27
|
-
|
28
|
-
## lets you use
|
29
|
-
## Artbase.configure do |config|
|
30
|
-
## config.ipfs_gateway = 'https://cloudflare-ipfs.com/ipfs/'
|
31
|
-
## end
|
32
|
-
def self.configure() yield( config ); end
|
33
|
-
def self.config() @config ||= Configuration.new; end
|
34
|
-
end # module Artbase
|
35
|
-
|
36
|
-
|
37
|
-
require_relative 'artbase/image'
|
38
|
-
|
39
|
-
|
40
|
-
require_relative 'artbase/helper'
|
41
|
-
require_relative 'artbase/retry' ## (global) retry_on_error helper
|
42
|
-
|
43
|
-
require_relative 'artbase/collection'
|
44
|
-
require_relative 'artbase/attributes'
|
45
|
-
|
46
|
-
|
47
|
-
require_relative 'artbase/tool'
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
######
|
52
|
-
## move to helper - why? why not?
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
## quick ipfs (interplanetary file system) hack
|
57
|
-
## - make more reuseable
|
58
|
-
## - different name e.g. ipfs_to_http or such - why? why not?
|
59
|
-
## change/rename parameter str to url or suc - why? why not?
|
60
|
-
def handle_ipfs( str, normalize: true,
|
61
|
-
ipfs_gateway: Artbase.config.ipfs_gateway )
|
62
|
-
|
63
|
-
if normalize && str.start_with?( 'https://ipfs.io/ipfs/' )
|
64
|
-
str = str.sub( 'https://ipfs.io/ipfs/', 'ipfs://' )
|
65
|
-
end
|
66
|
-
|
67
|
-
if str.start_with?( 'ipfs://' )
|
68
|
-
str = str.sub( 'ipfs://', ipfs_gateway ) # use/replace with public gateway
|
69
|
-
end
|
70
|
-
str
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
puts Artbase.banner
|
78
|
-
puts Artbase.root
|
1
|
+
|
2
|
+
require 'artbase-cocos'
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
## our own code
|
7
|
+
require_relative 'artbase/version'
|
8
|
+
require_relative 'artbase/tool'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
puts Artbase.banner # say hello
|
metadata
CHANGED
@@ -1,57 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: artbase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: cocos
|
14
|
+
name: artbase-cocos
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.1
|
19
|
+
version: 0.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.1
|
26
|
+
version: 0.0.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: artserve
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: artq
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rdoc
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,16 +102,6 @@ files:
|
|
102
102
|
- Rakefile
|
103
103
|
- bin/artbase
|
104
104
|
- lib/artbase.rb
|
105
|
-
- lib/artbase/attributes.rb
|
106
|
-
- lib/artbase/collection.rb
|
107
|
-
- lib/artbase/collection/base.rb
|
108
|
-
- lib/artbase/collection/image.rb
|
109
|
-
- lib/artbase/collection/opensea.rb
|
110
|
-
- lib/artbase/collection/token.rb
|
111
|
-
- lib/artbase/helper.rb
|
112
|
-
- lib/artbase/image.rb
|
113
|
-
- lib/artbase/image/sample.rb
|
114
|
-
- lib/artbase/retry.rb
|
115
105
|
- lib/artbase/tool.rb
|
116
106
|
- lib/artbase/version.rb
|
117
107
|
homepage: https://github.com/pixelartexchange/artbase
|
data/lib/artbase/attributes.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def counter_to_text( counter )
|
4
|
-
|
5
|
-
counter = counter.to_a
|
6
|
-
|
7
|
-
attribute_counter = counter[0]
|
8
|
-
more_counter = counter[1..-1]
|
9
|
-
|
10
|
-
|
11
|
-
puts "Attribute Counts\n"
|
12
|
-
trait_type, h = attribute_counter
|
13
|
-
|
14
|
-
total = h[:by_type].values.reduce(0) { |sum,count| sum+count }
|
15
|
-
|
16
|
-
|
17
|
-
types = h[:by_type]
|
18
|
-
types = types.sort { |l,r| l[0]<=>r[0] } ## sort by name
|
19
|
-
|
20
|
-
puts "\n"
|
21
|
-
puts "|Name|Total (%)|"
|
22
|
-
puts "|--------|----------:|"
|
23
|
-
|
24
|
-
types.each do |rec|
|
25
|
-
name = rec[0]
|
26
|
-
count = rec[1]
|
27
|
-
percent = Float(count*100)/Float(total)
|
28
|
-
|
29
|
-
puts "| **#{name} Attributes** | #{count} (#{'%.2f' % percent}) |"
|
30
|
-
end
|
31
|
-
puts "\n"
|
32
|
-
|
33
|
-
more_counter.each_with_index do |(trait_type, h),i|
|
34
|
-
print " · " if i > 0 ## add separator
|
35
|
-
print "#{trait_type } (#{h[:by_type].size})"
|
36
|
-
end
|
37
|
-
puts "\n\n"
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
more_counter.each do |trait_type, h|
|
42
|
-
print "### #{trait_type } (#{h[:by_type].size}) - "
|
43
|
-
print "∑Total #{h[:count]}/#{total}\n"
|
44
|
-
|
45
|
-
puts "\n"
|
46
|
-
puts "|Name|Total (%)|"
|
47
|
-
puts "|--------|----------:|"
|
48
|
-
|
49
|
-
types = h[:by_type]
|
50
|
-
types = types.sort do |l,r|
|
51
|
-
# sort by 1) by count
|
52
|
-
# 2) by name a-z
|
53
|
-
res = r[1] <=> l[1]
|
54
|
-
res = l[0] <=> r[0] if res == 0
|
55
|
-
res
|
56
|
-
end ## sort by count
|
57
|
-
types.each do |rec|
|
58
|
-
name = rec[0]
|
59
|
-
count = rec[1]
|
60
|
-
percent = Float(count*100)/Float(total)
|
61
|
-
|
62
|
-
puts "| **#{name}** | #{count} (#{'%.2f' % percent}) |"
|
63
|
-
end
|
64
|
-
puts "\n\n"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def counter_to_csv( counter )
|
72
|
-
|
73
|
-
puts "type, name, count"
|
74
|
-
counter.each do |trait_type, h|
|
75
|
-
puts "#{trait_type}, ∑ Total, #{h[:count]}"
|
76
|
-
h[:by_type].each do |trait_value, count|
|
77
|
-
puts "#{trait_type}, #{trait_value}, #{count}"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
|
@@ -1,329 +0,0 @@
|
|
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
|