scout-gear 10.7.2 → 10.7.4
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.
- checksums.yaml +4 -4
- data/.vimproject +47 -32
- data/VERSION +1 -1
- data/bin/scout +15 -2
- data/lib/scout/association/index.rb +4 -0
- data/lib/scout/association/item.rb +1 -1
- data/lib/scout/association.rb +32 -10
- data/lib/scout/entity/identifiers.rb +2 -2
- data/lib/scout/entity/property.rb +2 -2
- data/lib/scout/knowledge_base/enrichment.rb +9 -0
- data/lib/scout/knowledge_base/entity.rb +152 -0
- data/lib/scout/knowledge_base/list.rb +95 -0
- data/lib/scout/knowledge_base/query.rb +96 -0
- data/lib/scout/knowledge_base/registry.rb +175 -0
- data/lib/scout/knowledge_base/traverse.rb +329 -0
- data/lib/scout/knowledge_base.rb +91 -0
- data/lib/scout/persist/engine/tokyocabinet.rb +85 -77
- data/lib/scout/persist/tsv/adapter/base.rb +8 -22
- data/lib/scout/tsv/annotation.rb +4 -4
- data/lib/scout/tsv/index.rb +0 -2
- data/lib/scout/tsv/parser.rb +11 -1
- data/lib/scout/tsv/stream.rb +3 -3
- data/lib/scout/tsv/transformer.rb +12 -0
- data/lib/scout/tsv/util/process.rb +2 -2
- data/lib/scout/tsv.rb +2 -0
- data/lib/scout/workflow/definition.rb +6 -2
- data/lib/scout/workflow/deployment/trace.rb +1 -1
- data/lib/scout/workflow/step/dependencies.rb +3 -6
- data/lib/scout/workflow/step/info.rb +17 -3
- data/lib/scout/workflow/task/info.rb +99 -0
- data/lib/scout/workflow/task.rb +1 -0
- data/scout-gear.gemspec +27 -7
- data/scout_commands/doc +3 -3
- data/scout_commands/kb/config +33 -0
- data/scout_commands/kb/entities +35 -0
- data/scout_commands/kb/list +39 -0
- data/scout_commands/{db → kb}/query +6 -11
- data/scout_commands/{db → kb}/register +9 -8
- data/scout_commands/{db → kb}/show +6 -16
- data/scout_commands/kb/traverse +66 -0
- data/scout_commands/workflow/task +7 -2
- data/test/data/person/brothers +1 -1
- data/test/scout/entity/test_identifiers.rb +3 -3
- data/test/scout/knowledge_base/test_enrichment.rb +0 -0
- data/test/scout/knowledge_base/test_entity.rb +38 -0
- data/test/scout/knowledge_base/test_list.rb +40 -0
- data/test/scout/knowledge_base/test_query.rb +39 -0
- data/test/scout/knowledge_base/test_registry.rb +35 -0
- data/test/scout/knowledge_base/test_traverse.rb +245 -0
- data/test/scout/persist/test_tsv.rb +1 -0
- data/test/scout/test_association.rb +32 -3
- data/test/scout/test_entity.rb +0 -15
- data/test/scout/test_knowledge_base.rb +27 -0
- data/test/scout/test_tsv.rb +15 -0
- data/test/scout/tsv/test_parser.rb +4 -0
- data/test/scout/tsv/test_transformer.rb +13 -0
- data/test/scout/workflow/step/test_info.rb +11 -0
- data/test/scout/workflow/task/test_info.rb +22 -0
- data/test/test_helper.rb +17 -0
- metadata +26 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92262edf62404f5935bf749d3ab71d2ce39ffc7292a1b3d860fd5dc712501fcb
|
4
|
+
data.tar.gz: 47f69a80c0a135d3a9b3b42558e7b91e766b7781ff2360cd8344ef9c0f7ac8b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d532c6a4ac2bd3d0b5584761531b0364f381c317a9864b85b22c7a23bd61dbf2b31d1655f5fc7085c893c843f1b3f36a1b23fffae4c4b9dc2253352f3f5febf
|
7
|
+
data.tar.gz: 50e36c694239f0d74528b9156435b8769d16ae172dd87029ee741c0002b8ef3606f5088fc2c8f881d413b78f1f00f66134f06fc14129da3015648546130809c6
|
data/.vimproject
CHANGED
@@ -31,6 +31,7 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
31
31
|
task=task{
|
32
32
|
dependencies.rb
|
33
33
|
inputs.rb
|
34
|
+
info.rb
|
34
35
|
}
|
35
36
|
deployment.rb
|
36
37
|
deployment=deployment{
|
@@ -38,38 +39,11 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
38
39
|
orchestrator.rb
|
39
40
|
}
|
40
41
|
}
|
41
|
-
semaphore.rb
|
42
42
|
work_queue.rb
|
43
43
|
work_queue=work_queue{
|
44
44
|
socket.rb
|
45
45
|
worker.rb
|
46
46
|
}
|
47
|
-
|
48
|
-
persist=persist{
|
49
|
-
engine.rb
|
50
|
-
engine=engine{
|
51
|
-
tokyocabinet.rb
|
52
|
-
fix_width_table.rb
|
53
|
-
tkrzw.rb
|
54
|
-
packed_index.rb
|
55
|
-
sharder.rb
|
56
|
-
}
|
57
|
-
tsv.rb
|
58
|
-
tsv=tsv{
|
59
|
-
adapter.rb
|
60
|
-
serialize.rb
|
61
|
-
adapter=adapter{
|
62
|
-
base.rb
|
63
|
-
|
64
|
-
fix_width_table.rb
|
65
|
-
packed_index.rb
|
66
|
-
tkrzw.rb
|
67
|
-
tokyocabinet.rb
|
68
|
-
sharder.rb
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
47
|
tsv.rb
|
74
48
|
tsv=tsv{
|
75
49
|
util.rb
|
@@ -101,6 +75,30 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
101
75
|
open.rb
|
102
76
|
csv.rb
|
103
77
|
}
|
78
|
+
persist=persist{
|
79
|
+
engine.rb
|
80
|
+
engine=engine{
|
81
|
+
tokyocabinet.rb
|
82
|
+
fix_width_table.rb
|
83
|
+
tkrzw.rb
|
84
|
+
packed_index.rb
|
85
|
+
sharder.rb
|
86
|
+
}
|
87
|
+
tsv.rb
|
88
|
+
tsv=tsv{
|
89
|
+
adapter.rb
|
90
|
+
serialize.rb
|
91
|
+
adapter=adapter{
|
92
|
+
base.rb
|
93
|
+
|
94
|
+
fix_width_table.rb
|
95
|
+
packed_index.rb
|
96
|
+
tkrzw.rb
|
97
|
+
tokyocabinet.rb
|
98
|
+
sharder.rb
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
104
102
|
entity.rb
|
105
103
|
entity=entity{
|
106
104
|
property.rb
|
@@ -111,17 +109,26 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
111
109
|
}
|
112
110
|
association.rb
|
113
111
|
association=association{
|
114
|
-
fields.rb
|
115
112
|
index.rb
|
113
|
+
fields.rb
|
116
114
|
item.rb
|
117
115
|
}
|
118
|
-
|
116
|
+
knowledge_base.rb
|
117
|
+
knowledge_base=knowledge_base{
|
118
|
+
registry.rb
|
119
|
+
entity.rb
|
120
|
+
query.rb
|
121
|
+
traverse.rb
|
122
|
+
enrichment.rb
|
123
|
+
list.rb
|
124
|
+
}
|
119
125
|
offsite.rb
|
120
126
|
offsite=offsite{
|
121
127
|
ssh.rb
|
122
128
|
sync.rb
|
123
129
|
step.rb
|
124
130
|
}
|
131
|
+
semaphore.rb
|
125
132
|
}
|
126
133
|
scout-gear.rb
|
127
134
|
workflow-scout.rb
|
@@ -138,10 +145,14 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
138
145
|
update
|
139
146
|
template
|
140
147
|
offsite
|
141
|
-
|
148
|
+
kb=kb{
|
149
|
+
config
|
150
|
+
entities
|
142
151
|
register
|
143
|
-
query
|
144
152
|
show
|
153
|
+
query
|
154
|
+
traverse
|
155
|
+
list
|
145
156
|
}
|
146
157
|
workflow=workflow{
|
147
158
|
task
|
@@ -164,8 +175,12 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
164
175
|
test_helper.rb
|
165
176
|
test_scout-gear.rb
|
166
177
|
test_scout.rb
|
167
|
-
data=data{
|
178
|
+
data=data filter="*"{
|
168
179
|
person=person{
|
180
|
+
brothers
|
181
|
+
identifiers
|
182
|
+
marriages
|
183
|
+
parents
|
169
184
|
}
|
170
185
|
}
|
171
186
|
scout=scout{
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
10.7.
|
1
|
+
10.7.4
|
data/bin/scout
CHANGED
@@ -52,6 +52,16 @@ if dev_dir
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
requires = nil
|
56
|
+
if _i = ARGV.index("--require")
|
57
|
+
requires = ARGV[_i+1].split(",")
|
58
|
+
ARGV.delete_at _i + 1
|
59
|
+
ARGV.delete_at _i
|
60
|
+
requires.each do |p|
|
61
|
+
require p
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
55
65
|
require 'scout-gear'
|
56
66
|
|
57
67
|
require 'scout/simple_opt'
|
@@ -97,8 +107,11 @@ if config_keys = options.delete(:config_keys)
|
|
97
107
|
end
|
98
108
|
end
|
99
109
|
|
100
|
-
|
101
|
-
|
110
|
+
#$scout_command_dir = Scout.bin.scout
|
111
|
+
#$scout_command_dir.path_maps[:scout_commands] = File.join(File.dirname(__dir__), "{PATH/bin\\/scout/scout_commands}")
|
112
|
+
|
113
|
+
$scout_command_dir = Scout.scout_commands
|
114
|
+
#$scout_command_dir.path_maps[:scout_commands] = File.join(File.dirname(__dir__), "{PATH/bin\\/scout/scout_commands}")
|
102
115
|
|
103
116
|
SOPT.description =<<EOF
|
104
117
|
This command controls many aspects of the Scout framework, from configuration tasks to running applications.
|
@@ -2,6 +2,10 @@ require 'scout/annotation'
|
|
2
2
|
module Association
|
3
3
|
|
4
4
|
def self.index(file, source: nil, target: nil, source_format: nil, target_format: nil, format: nil, **kwargs)
|
5
|
+
IndiferentHash.setup(kwargs)
|
6
|
+
source = kwargs.delete :source if kwargs.include?(:source)
|
7
|
+
target = kwargs.delete :target if kwargs.include?(:target)
|
8
|
+
|
5
9
|
persist_options = IndiferentHash.pull_keys kwargs, :persist
|
6
10
|
index_persist_options = IndiferentHash.add_defaults persist_options.dup, persist: true,
|
7
11
|
prefix: "Association::Index",
|
@@ -102,7 +102,7 @@ module AssociationItem
|
|
102
102
|
property :info => :array2single do
|
103
103
|
fields = self.info_fields
|
104
104
|
|
105
|
-
|
105
|
+
next [{}] * self.length if fields.nil? or fields.empty?
|
106
106
|
|
107
107
|
value = self.value
|
108
108
|
value.collect{|v|
|
data/lib/scout/association.rb
CHANGED
@@ -6,10 +6,14 @@ require_relative 'association/item'
|
|
6
6
|
|
7
7
|
module Association
|
8
8
|
def self.open(obj, source: nil, target: nil, fields: nil, source_format: nil, target_format: nil, format: nil, **kwargs)
|
9
|
-
|
9
|
+
IndiferentHash.setup(kwargs)
|
10
|
+
source = kwargs.delete :source if kwargs.include?(:source)
|
11
|
+
target = kwargs.delete :target if kwargs.include?(:target)
|
10
12
|
|
11
13
|
if Path.is_filename?(obj)
|
12
|
-
|
14
|
+
tsv_header_options = TSV.parse_options(obj)
|
15
|
+
tsv_header_options = tsv_header_options.slice(TSV.acceptable_parser_options)
|
16
|
+
options = tsv_header_options.merge(kwargs)
|
13
17
|
else
|
14
18
|
options = kwargs.dup
|
15
19
|
end
|
@@ -29,9 +33,12 @@ module Association
|
|
29
33
|
|
30
34
|
type, identifiers = IndiferentHash.process_options options, :type, :identifiers
|
31
35
|
|
32
|
-
if source_format
|
36
|
+
if source_format || target_format
|
33
37
|
translation_files = [TSV.identifier_files(obj), Entity.identifier_files(source_format), identifiers].flatten.compact
|
34
|
-
translation_files.collect!{|f| Path.is_filename?(f, false) ? Path.setup(f.gsub(/\[?NAMESPACE\]?/, options[:namespace])) : f }
|
38
|
+
translation_files.collect!{|f| (Path.is_filename?(f, false) && options[:namespace]) ? Path.setup(f.gsub(/\[?NAMESPACE\]?/, options[:namespace])) : f }
|
39
|
+
end
|
40
|
+
|
41
|
+
if source_format
|
35
42
|
source_index = begin
|
36
43
|
TSV.translation_index(translation_files, source_header, source_format)
|
37
44
|
rescue
|
@@ -40,8 +47,6 @@ module Association
|
|
40
47
|
end
|
41
48
|
|
42
49
|
if target_format
|
43
|
-
translation_files = [TSV.identifier_files(obj), Entity.identifier_files(target_format), identifiers].flatten.compact
|
44
|
-
translation_files.collect!{|f| Path.is_filename?(f, false) ? Path.setup(f.gsub(/\[?NAMESPACE\]?/, options[:namespace])) : f }
|
45
50
|
target_index = begin
|
46
51
|
TSV.translation_index(translation_files, field_headers.first, target_format)
|
47
52
|
rescue
|
@@ -82,7 +87,7 @@ module Association
|
|
82
87
|
|
83
88
|
if source_index.nil? && target_index.nil?
|
84
89
|
if TSV === obj
|
85
|
-
IndiferentHash.pull_keys
|
90
|
+
IndiferentHash.pull_keys options, :persist
|
86
91
|
type = options[:type] || obj.type
|
87
92
|
res = obj.reorder original_source_header, all_fields.values_at(*field_pos), **options.merge(type: type, merge: true)
|
88
93
|
else
|
@@ -109,8 +114,25 @@ module Association
|
|
109
114
|
transformer
|
110
115
|
end
|
111
116
|
|
112
|
-
def self.database(*args, **kwargs)
|
113
|
-
|
114
|
-
|
117
|
+
def self.database(file, *args, **kwargs)
|
118
|
+
persist_options = IndiferentHash.pull_keys kwargs, :persist
|
119
|
+
|
120
|
+
database_persist_options = IndiferentHash.add_defaults persist_options.dup, persist: true,
|
121
|
+
prefix: "Association::Index", serializer: :double,
|
122
|
+
other_options: kwargs
|
123
|
+
|
124
|
+
Persist.tsv(file, kwargs, engine: "BDB", persist_options: database_persist_options) do |data|
|
125
|
+
tsv = open(file, *args, **kwargs)
|
126
|
+
data.serializer = TSVAdapter.serializer_module(tsv.type) if data.respond_to?(:serializer)
|
127
|
+
if TSV::Transformer === tsv
|
128
|
+
tsv.tsv(merge: true, data: data)
|
129
|
+
elsif data.respond_to?(:persistence_path)
|
130
|
+
data.merge!(tsv)
|
131
|
+
tsv.annotate(data)
|
132
|
+
data
|
133
|
+
else
|
134
|
+
tsv
|
135
|
+
end
|
136
|
+
end
|
115
137
|
end
|
116
138
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Entity
|
2
2
|
def self.identifier_files(field)
|
3
|
-
entity_type = Entity.formats[field]
|
3
|
+
entity_type = Entity.formats[Entity.formats.find(field)]
|
4
4
|
return [] unless entity_type and entity_type.include? Entity::Identified
|
5
5
|
entity_type.identifier_files
|
6
6
|
end
|
@@ -48,7 +48,7 @@ module Entity
|
|
48
48
|
def identifier_files
|
49
49
|
files = identity_type.identifier_files.dup
|
50
50
|
return [] if files.nil?
|
51
|
-
files.collect!{|f| f.annotate f.gsub(/\b#{NAMESPACE_TAG}\b/, namespace.to_s) } if
|
51
|
+
files.collect!{|f| f.annotate f.gsub(/\b#{NAMESPACE_TAG}\b/, namespace.to_s) } if annotation_hash.include? :namespace and self.namespace
|
52
52
|
if files.select{|f| f =~ /\b#{NAMESPACE_TAG}\b/ }.any?
|
53
53
|
Log.warn "Rejecting some identifier files for lack of 'namespace': " << files.select{|f| f =~ /\b#{NAMESPACE_TAG}\b/ } * ", "
|
54
54
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'scout/exceptions'
|
1
2
|
module Entity
|
2
3
|
class << self
|
3
4
|
attr_accessor :entity_property_cache
|
@@ -61,7 +62,6 @@ module Entity
|
|
61
62
|
|
62
63
|
properties.push name
|
63
64
|
|
64
|
-
|
65
65
|
entity_class = self
|
66
66
|
if type == :multiple
|
67
67
|
self.define_method(real_method) do |*args,**kwargs|
|
@@ -88,7 +88,7 @@ module Entity
|
|
88
88
|
|
89
89
|
new_responses = missing.instance_exec(*args, **kwargs, &block)
|
90
90
|
|
91
|
-
missing.each do |item
|
91
|
+
missing.each do |item|
|
92
92
|
responses[item] = Entity::Property.persist(name, item, type, options) do
|
93
93
|
Array === new_responses ? new_responses[item.container_index] : new_responses[item]
|
94
94
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rbbt/knowledge_base/registry'
|
2
|
+
class KnowledgeBase
|
3
|
+
def enrichment(name, entities, options = {})
|
4
|
+
require 'rbbt/statistics/hypergeometric'
|
5
|
+
database = get_database(name, options)
|
6
|
+
entities = identify_source name, entities
|
7
|
+
database.enrichment entities, database.fields.first, :persist => false
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require_relative '../entity'
|
2
|
+
|
3
|
+
class KnowledgeBase
|
4
|
+
|
5
|
+
def select_entities(name, entities, options = {})
|
6
|
+
index = get_index(name, options)
|
7
|
+
|
8
|
+
source_field = index.source_field
|
9
|
+
target_field = index.target_field
|
10
|
+
|
11
|
+
source_type = source_type name
|
12
|
+
target_type = target_type name
|
13
|
+
|
14
|
+
source_entities = entities[:source] || entities[source_field] || entities[Entity.formats[source_field].to_s] || entities[:both]
|
15
|
+
target_entities = entities[:target] || entities[target_field] || entities[Entity.formats[target_field].to_s] || entities[:both]
|
16
|
+
|
17
|
+
[source_entities, target_entities]
|
18
|
+
end
|
19
|
+
|
20
|
+
def entity_options_for(type, database_name = nil)
|
21
|
+
entity_options = self.entity_options
|
22
|
+
IndiferentHash.setup entity_options if entity_options and not IndiferentHash === entity_options
|
23
|
+
options = entity_options[type.to_s] || entity_options[Entity.formats[type].to_s] || {}
|
24
|
+
options[:format] = @format[type] if Hash === @format && @format.include?(type)
|
25
|
+
namespace = self.namespace
|
26
|
+
namespace = db_namespace(database_name) if namespace.nil? and database_name
|
27
|
+
if database_name
|
28
|
+
database = get_database(database_name)
|
29
|
+
if database.entity_options and (database.entity_options[type] or database.entity_options[Entity.formats[type.to_s].to_s])
|
30
|
+
options = options.merge(database.entity_options[type] || database.entity_options[Entity.formats[type.to_s].to_s])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
options
|
34
|
+
end
|
35
|
+
|
36
|
+
def annotate(entities, type, database = nil)
|
37
|
+
format = @format[type] || type
|
38
|
+
entity_options = entity_options_for(type, database)
|
39
|
+
Entity.prepare_entity(entities, format, entity_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def translate(entities, type)
|
43
|
+
if format = @format[type] and (entities.respond_to? :format and format != entities.format)
|
44
|
+
entities.to format
|
45
|
+
else
|
46
|
+
entities
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def source_type(name)
|
51
|
+
Entity.formats[Entity.formats.find(source(name))]
|
52
|
+
end
|
53
|
+
|
54
|
+
def target_type(name)
|
55
|
+
Entity.formats[Entity.formats.find(target(name))]
|
56
|
+
end
|
57
|
+
|
58
|
+
def entities
|
59
|
+
all_databases.inject([]){|acc,name| acc << source(name); acc << target(name)}.uniq
|
60
|
+
end
|
61
|
+
|
62
|
+
def entity_types
|
63
|
+
entities.collect{|entity| Entity.formats[entity] }.uniq
|
64
|
+
end
|
65
|
+
|
66
|
+
def database_identifier_files(name)
|
67
|
+
get_database(name).identifier_files.dup + self.identifier_files
|
68
|
+
end
|
69
|
+
|
70
|
+
def db_namespace(name)
|
71
|
+
get_database(name).namespace
|
72
|
+
end
|
73
|
+
|
74
|
+
def source_index(name)
|
75
|
+
Persist.memory("Source index #{name}: KB directory #{dir}") do
|
76
|
+
if @identifier_files && @identifier_files.any?
|
77
|
+
identifier_files = @identifier_files
|
78
|
+
else
|
79
|
+
identifier_files = database_identifier_files(name)
|
80
|
+
end
|
81
|
+
identifier_files.concat Entity.identifier_files(source(name)) if defined? Entity
|
82
|
+
identifier_files.uniq!
|
83
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, namespace))} if namespace
|
84
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, db_namespace(name)))} if not namespace and db_namespace(name)
|
85
|
+
identifier_files.reject!{|f| f.match(/\bNAMESPACE\b/)}
|
86
|
+
TSV.translation_index identifier_files, nil, source(name), :persist => true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def target_index(name)
|
91
|
+
Persist.memory("Target index #{name}: KB directory #{dir}") do
|
92
|
+
identifier_files = identifier_files(name)
|
93
|
+
identifier_files.concat Entity.identifier_files(target(name)) if defined? Entity
|
94
|
+
identifier_files.uniq!
|
95
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, namespace))} if self.namespace
|
96
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, db_namespace(name)))} if namespace.nil? and db_namespace(name)
|
97
|
+
identifier_files.reject!{|f| f.match(/\bNAMESPACE\b/)}
|
98
|
+
TSV.translation_index identifier_files, nil, target(name), :persist => true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def identify_source(name, entity)
|
103
|
+
return :all if entity == :all
|
104
|
+
index = begin
|
105
|
+
source_index(name)
|
106
|
+
rescue
|
107
|
+
Log.exception $!
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
return entity if index.nil?
|
111
|
+
if Array === entity
|
112
|
+
entity.collect{|e| index[e] || e }
|
113
|
+
else
|
114
|
+
index[entity] || entity
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def identify_target(name, entity)
|
119
|
+
return :all if entity == :all
|
120
|
+
index = begin target_index(name) rescue nil end
|
121
|
+
return entity if index.nil?
|
122
|
+
if Array === entity
|
123
|
+
entity.collect{|e| index[e] || e }
|
124
|
+
else
|
125
|
+
index[entity] || entity
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def identify(name, entity)
|
130
|
+
identify_source(name, entity) || identify_target(name, entity)
|
131
|
+
end
|
132
|
+
|
133
|
+
def define_entity_modules
|
134
|
+
entity_options.each do |entity,options|
|
135
|
+
next unless options[:identifiers]
|
136
|
+
identifiers = options[:identifiers]
|
137
|
+
identifiers = identifiers.split(",") unless Array === identifiers
|
138
|
+
m = begin
|
139
|
+
Object.const_get entity
|
140
|
+
rescue
|
141
|
+
m = Module.new
|
142
|
+
m.extend Entity
|
143
|
+
m.include Entity::Identified
|
144
|
+
Object.const_set entity, m
|
145
|
+
end
|
146
|
+
|
147
|
+
identifiers.each do |file|
|
148
|
+
m.add_identifiers Path.setup(file), self.format
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'scout/annotation'
|
2
|
+
class KnowledgeBase
|
3
|
+
|
4
|
+
def list_file(id, entity_type = nil)
|
5
|
+
id = Path.sanitize_filename(id)
|
6
|
+
|
7
|
+
entity_type = entity_type.to_s.split(":").last
|
8
|
+
|
9
|
+
raise "Ilegal list id: #{ id }" unless Misc.path_relative_to(dir, File.join(dir, id))
|
10
|
+
|
11
|
+
if entity_type
|
12
|
+
if entity_type.to_s == "simple"
|
13
|
+
path = dir.lists[entity_type.to_s][id]
|
14
|
+
else
|
15
|
+
path = dir.lists[entity_type.to_s][id + ".tsv"]
|
16
|
+
end
|
17
|
+
else
|
18
|
+
path = dir.lists.glob("*/#{id}").first
|
19
|
+
path ||= dir.lists.glob("*/#{id}.tsv").first
|
20
|
+
raise "List not found #{id}" if path.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
path.find
|
24
|
+
end
|
25
|
+
|
26
|
+
def save_list(id, list)
|
27
|
+
if AnnotatedArray === list
|
28
|
+
path = list_file(id, list.base_entity)
|
29
|
+
else
|
30
|
+
path = list_file(id, :simple)
|
31
|
+
end
|
32
|
+
|
33
|
+
Open.lock path do
|
34
|
+
begin
|
35
|
+
if AnnotatedArray === list
|
36
|
+
Open.write(path, Annotation.tsv(list, :all).to_s)
|
37
|
+
else
|
38
|
+
Open.write(path, list * "\n")
|
39
|
+
end
|
40
|
+
rescue
|
41
|
+
FileUtils.rm(path) if File.exist?(path)
|
42
|
+
raise $!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_list(id, entity_type = nil)
|
48
|
+
if entity_type
|
49
|
+
path = list_file(id, entity_type)
|
50
|
+
path = list_file(id) unless path.exists?
|
51
|
+
else
|
52
|
+
path = list_file(id)
|
53
|
+
end
|
54
|
+
|
55
|
+
raise "List not found: #{ id }" unless path and path.exists?
|
56
|
+
|
57
|
+
begin
|
58
|
+
if path.get_extension == 'tsv'
|
59
|
+
list = Annotation.load_tsv path.tsv
|
60
|
+
list.extend AnnotatedArray
|
61
|
+
list
|
62
|
+
else
|
63
|
+
path.list
|
64
|
+
end
|
65
|
+
rescue
|
66
|
+
Log.exception $!
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def lists
|
72
|
+
lists = {}
|
73
|
+
dir.lists.glob("*").each do |list_dir|
|
74
|
+
lists[list_dir.basename] = list_dir.glob("*").
|
75
|
+
collect(&:unset_extension).
|
76
|
+
collect(&:basename)
|
77
|
+
end
|
78
|
+
lists
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_list(id, entity_type = nil)
|
82
|
+
path = list_file(id, entity_type)
|
83
|
+
path = list_file(id) unless path.exists?
|
84
|
+
|
85
|
+
"This list does not belong to #{ user }: #{[entity_type, id] * ": "}" unless File.exist? path
|
86
|
+
|
87
|
+
Open.lock path do
|
88
|
+
begin
|
89
|
+
FileUtils.rm path if File.exist? path
|
90
|
+
rescue
|
91
|
+
raise $!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'entity'
|
2
|
+
|
3
|
+
class KnowledgeBase
|
4
|
+
|
5
|
+
def setup(name, matches, reverse = false)
|
6
|
+
AssociationItem.setup matches, self, name, reverse
|
7
|
+
end
|
8
|
+
|
9
|
+
def _subset(name, source = :all, target = :all, options = {})
|
10
|
+
repo = get_index name, options
|
11
|
+
|
12
|
+
repo.subset(source, target)
|
13
|
+
end
|
14
|
+
|
15
|
+
def subset(name, entities, options = {}, &block)
|
16
|
+
entities, options = options, entities if entities.nil? and Hash === options
|
17
|
+
|
18
|
+
entities = case entities
|
19
|
+
when :all
|
20
|
+
{:target => :all, :source => :all}
|
21
|
+
when AnnotatedArray
|
22
|
+
format = entities.format if entities.respond_to? :format
|
23
|
+
format ||= entities.base_entity.to_s
|
24
|
+
{format => entities.purge}
|
25
|
+
when Hash
|
26
|
+
entities
|
27
|
+
else
|
28
|
+
raise "Entities are not a Hash or an AnnotatedArray: #{Log.fingerprint entities}"
|
29
|
+
end
|
30
|
+
|
31
|
+
identify, identify_source, identify_target = entities.merge(options || {}).values_at :identify, :identify_source, :identify_target
|
32
|
+
|
33
|
+
source, target = select_entities(name, entities, options)
|
34
|
+
|
35
|
+
source = identify_source(name, source) if identify_source
|
36
|
+
target = identify_target(name, target) if identify_target
|
37
|
+
|
38
|
+
source = identify(name, source) if identify && !identify_source
|
39
|
+
target = identify(name, target) if identify && !identify_target
|
40
|
+
|
41
|
+
return [] if source.nil? or target.nil?
|
42
|
+
return [] if Array === target and target.empty?
|
43
|
+
return [] if Array === source and source.empty?
|
44
|
+
|
45
|
+
matches = _subset name, source, target, options
|
46
|
+
|
47
|
+
setup(name, matches)
|
48
|
+
|
49
|
+
matches = matches.select(&block) if block_given?
|
50
|
+
|
51
|
+
matches
|
52
|
+
end
|
53
|
+
|
54
|
+
def all(name, options={})
|
55
|
+
repo = get_index name, options
|
56
|
+
setup name, repo.keys
|
57
|
+
end
|
58
|
+
|
59
|
+
def _children(name, entity)
|
60
|
+
repo = get_index name
|
61
|
+
repo.match(entity)
|
62
|
+
end
|
63
|
+
|
64
|
+
def children(name, entity)
|
65
|
+
entity = identify_source(name, entity)
|
66
|
+
setup(name, _children(name, entity))
|
67
|
+
end
|
68
|
+
|
69
|
+
def _parents(name, entity)
|
70
|
+
repo = get_index name
|
71
|
+
repo.reverse.match(entity)
|
72
|
+
end
|
73
|
+
|
74
|
+
def parents(name, entity)
|
75
|
+
entity = identify_target(name, entity)
|
76
|
+
matches = _parents(name, entity)
|
77
|
+
#matches.each{|m| m.replace(m.partition("~").reverse*"") } unless undirected(name)
|
78
|
+
setup(name, matches, true)
|
79
|
+
end
|
80
|
+
|
81
|
+
def _neighbours(name, entity)
|
82
|
+
if undirected(name) and source(name) == target(name)
|
83
|
+
{:children => _children(name, entity)}
|
84
|
+
else
|
85
|
+
{:parents => _parents(name, entity), :children => _children(name, entity)}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def neighbours(name, entity)
|
90
|
+
hash = _neighbours(name, entity)
|
91
|
+
IndiferentHash.setup(hash)
|
92
|
+
setup(name, hash[:children]) if hash[:children]
|
93
|
+
setup(name, hash[:parents], true) if hash[:parents]
|
94
|
+
hash
|
95
|
+
end
|
96
|
+
end
|