scout-gear 10.7.2 → 10.7.3
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 +46 -32
- data/VERSION +1 -1
- data/lib/scout/association/index.rb +4 -0
- data/lib/scout/association/item.rb +1 -1
- data/lib/scout/association.rb +28 -9
- data/lib/scout/entity/identifiers.rb +2 -2
- data/lib/scout/entity/property.rb +1 -0
- data/lib/scout/knowledge_base/enrichment.rb +9 -0
- data/lib/scout/knowledge_base/entity.rb +143 -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 +173 -0
- data/lib/scout/knowledge_base/traverse.rb +329 -0
- data/lib/scout/knowledge_base.rb +91 -0
- data/lib/scout/tsv/annotation.rb +4 -4
- data/lib/scout/tsv/index.rb +0 -2
- data/lib/scout/tsv/parser.rb +1 -1
- data/lib/scout/tsv/stream.rb +3 -3
- data/lib/scout/tsv.rb +2 -0
- data/lib/scout/workflow/step/info.rb +10 -1
- data/scout-gear.gemspec +24 -6
- 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/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 +16 -0
- data/test/scout/knowledge_base/test_traverse.rb +245 -0
- data/test/scout/test_association.rb +17 -3
- data/test/scout/test_entity.rb +0 -15
- data/test/scout/test_knowledge_base.rb +27 -0
- data/test/test_helper.rb +17 -0
- metadata +23 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 469b6e128a39e5612de5698005de8f4859c015bf3d65a5490a58f2f9f0312a39
|
4
|
+
data.tar.gz: 0a76edd86052c35af31f6de227c0bc43c71bb6b64fdc757e1179bf88a4ce8701
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8753c031fda58c461afdefa3412a0d7996c6d5139a30653b1e6fcfaeba7fdf31b4d300bb2cb07060e7eb12d63891fc7a157cec3964c59d5cc68584ffc8823c8
|
7
|
+
data.tar.gz: 05cbb772edf0384f2889ad1be2cc597425d071da8c488364a8c72b23e6b7098aea0089247c180da09165f3eb7d899de494dca9ce2a00f285dcfaa564c8dc3e68
|
data/.vimproject
CHANGED
@@ -38,38 +38,11 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
38
38
|
orchestrator.rb
|
39
39
|
}
|
40
40
|
}
|
41
|
-
semaphore.rb
|
42
41
|
work_queue.rb
|
43
42
|
work_queue=work_queue{
|
44
43
|
socket.rb
|
45
44
|
worker.rb
|
46
45
|
}
|
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
46
|
tsv.rb
|
74
47
|
tsv=tsv{
|
75
48
|
util.rb
|
@@ -101,6 +74,30 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
101
74
|
open.rb
|
102
75
|
csv.rb
|
103
76
|
}
|
77
|
+
persist=persist{
|
78
|
+
engine.rb
|
79
|
+
engine=engine{
|
80
|
+
tokyocabinet.rb
|
81
|
+
fix_width_table.rb
|
82
|
+
tkrzw.rb
|
83
|
+
packed_index.rb
|
84
|
+
sharder.rb
|
85
|
+
}
|
86
|
+
tsv.rb
|
87
|
+
tsv=tsv{
|
88
|
+
adapter.rb
|
89
|
+
serialize.rb
|
90
|
+
adapter=adapter{
|
91
|
+
base.rb
|
92
|
+
|
93
|
+
fix_width_table.rb
|
94
|
+
packed_index.rb
|
95
|
+
tkrzw.rb
|
96
|
+
tokyocabinet.rb
|
97
|
+
sharder.rb
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
104
101
|
entity.rb
|
105
102
|
entity=entity{
|
106
103
|
property.rb
|
@@ -111,17 +108,26 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
111
108
|
}
|
112
109
|
association.rb
|
113
110
|
association=association{
|
114
|
-
fields.rb
|
115
111
|
index.rb
|
112
|
+
fields.rb
|
116
113
|
item.rb
|
117
114
|
}
|
118
|
-
|
115
|
+
knowledge_base.rb
|
116
|
+
knowledge_base=knowledge_base{
|
117
|
+
registry.rb
|
118
|
+
entity.rb
|
119
|
+
query.rb
|
120
|
+
traverse.rb
|
121
|
+
enrichment.rb
|
122
|
+
list.rb
|
123
|
+
}
|
119
124
|
offsite.rb
|
120
125
|
offsite=offsite{
|
121
126
|
ssh.rb
|
122
127
|
sync.rb
|
123
128
|
step.rb
|
124
129
|
}
|
130
|
+
semaphore.rb
|
125
131
|
}
|
126
132
|
scout-gear.rb
|
127
133
|
workflow-scout.rb
|
@@ -138,10 +144,14 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
138
144
|
update
|
139
145
|
template
|
140
146
|
offsite
|
141
|
-
|
147
|
+
kb=kb{
|
148
|
+
config
|
149
|
+
entities
|
142
150
|
register
|
143
|
-
query
|
144
151
|
show
|
152
|
+
query
|
153
|
+
traverse
|
154
|
+
list
|
145
155
|
}
|
146
156
|
workflow=workflow{
|
147
157
|
task
|
@@ -164,8 +174,12 @@ scout-gear=/$PWD filter="*.rb *.yaml" {
|
|
164
174
|
test_helper.rb
|
165
175
|
test_scout-gear.rb
|
166
176
|
test_scout.rb
|
167
|
-
data=data{
|
177
|
+
data=data filter="*"{
|
168
178
|
person=person{
|
179
|
+
brothers
|
180
|
+
identifiers
|
181
|
+
marriages
|
182
|
+
parents
|
169
183
|
}
|
170
184
|
}
|
171
185
|
scout=scout{
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
10.7.
|
1
|
+
10.7.3
|
@@ -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,7 +6,9 @@ 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
|
options = TSV.parse_options(obj).merge(kwargs)
|
@@ -29,9 +31,12 @@ module Association
|
|
29
31
|
|
30
32
|
type, identifiers = IndiferentHash.process_options options, :type, :identifiers
|
31
33
|
|
32
|
-
if source_format
|
34
|
+
if source_format || target_format
|
33
35
|
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 }
|
36
|
+
translation_files.collect!{|f| (Path.is_filename?(f, false) && options[:namespace]) ? Path.setup(f.gsub(/\[?NAMESPACE\]?/, options[:namespace])) : f }
|
37
|
+
end
|
38
|
+
|
39
|
+
if source_format
|
35
40
|
source_index = begin
|
36
41
|
TSV.translation_index(translation_files, source_header, source_format)
|
37
42
|
rescue
|
@@ -40,8 +45,6 @@ module Association
|
|
40
45
|
end
|
41
46
|
|
42
47
|
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
48
|
target_index = begin
|
46
49
|
TSV.translation_index(translation_files, field_headers.first, target_format)
|
47
50
|
rescue
|
@@ -82,7 +85,7 @@ module Association
|
|
82
85
|
|
83
86
|
if source_index.nil? && target_index.nil?
|
84
87
|
if TSV === obj
|
85
|
-
IndiferentHash.pull_keys
|
88
|
+
IndiferentHash.pull_keys options, :persist
|
86
89
|
type = options[:type] || obj.type
|
87
90
|
res = obj.reorder original_source_header, all_fields.values_at(*field_pos), **options.merge(type: type, merge: true)
|
88
91
|
else
|
@@ -109,8 +112,24 @@ module Association
|
|
109
112
|
transformer
|
110
113
|
end
|
111
114
|
|
112
|
-
def self.database(*args, **kwargs)
|
113
|
-
|
114
|
-
|
115
|
+
def self.database(file, *args, **kwargs)
|
116
|
+
persist_options = IndiferentHash.pull_keys kwargs, :persist
|
117
|
+
|
118
|
+
database_persist_options = IndiferentHash.add_defaults persist_options.dup, persist: true,
|
119
|
+
prefix: "Association::Index", serializer: :list,
|
120
|
+
other_options: kwargs
|
121
|
+
|
122
|
+
Persist.tsv(file, kwargs, engine: "BDB", persist_options: database_persist_options) do |data|
|
123
|
+
tsv = open(file, *args, **kwargs)
|
124
|
+
if TSV::Transformer === tsv
|
125
|
+
tsv.tsv(merge: true, data: data)
|
126
|
+
elsif data.respond_to?(:persistence_path)
|
127
|
+
data.merge!(tsv)
|
128
|
+
tsv.annotate(data)
|
129
|
+
data
|
130
|
+
else
|
131
|
+
tsv
|
132
|
+
end
|
133
|
+
end
|
115
134
|
end
|
116
135
|
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
|
@@ -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,143 @@
|
|
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 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
|
+
identifier_files = identifier_files(name)
|
77
|
+
identifier_files.concat Entity.identifier_files(source(name)) if defined? Entity
|
78
|
+
identifier_files.uniq!
|
79
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, namespace))} if namespace
|
80
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, db_namespace(name)))} if not namespace and db_namespace(name)
|
81
|
+
identifier_files.reject!{|f| f.match(/\bNAMESPACE\b/)}
|
82
|
+
TSV.translation_index identifier_files, nil, source(name), :persist => true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def target_index(name)
|
87
|
+
Persist.memory("Target index #{name}: KB directory #{dir}") do
|
88
|
+
identifier_files = identifier_files(name)
|
89
|
+
identifier_files.concat Entity.identifier_files(target(name)) if defined? Entity
|
90
|
+
identifier_files.uniq!
|
91
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, namespace))} if self.namespace
|
92
|
+
identifier_files.collect!{|f| f.annotate(f.gsub(/\bNAMESPACE\b/, db_namespace(name)))} if namespace.nil? and db_namespace(name)
|
93
|
+
identifier_files.reject!{|f| f.match(/\bNAMESPACE\b/)}
|
94
|
+
TSV.translation_index identifier_files, nil, target(name), :persist => true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def identify_source(name, entity)
|
99
|
+
return :all if entity == :all
|
100
|
+
index = begin source_index(name) rescue nil end
|
101
|
+
return entity if index.nil?
|
102
|
+
if Array === entity
|
103
|
+
entity.collect{|e| index[e] || e }
|
104
|
+
else
|
105
|
+
index[entity] || entity
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def identify_target(name, entity)
|
110
|
+
return :all if entity == :all
|
111
|
+
index = begin target_index(name) rescue nil end
|
112
|
+
return entity if index.nil?
|
113
|
+
if Array === entity
|
114
|
+
entity.collect{|e| index[e] || e }
|
115
|
+
else
|
116
|
+
index[entity] || entity
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def identify(name, entity)
|
121
|
+
identify_source(name, entity) || identify_target(name, entity)
|
122
|
+
end
|
123
|
+
|
124
|
+
def define_entity_modules
|
125
|
+
entity_options.each do |entity,options|
|
126
|
+
next unless options[:identifiers]
|
127
|
+
identifiers = options[:identifiers]
|
128
|
+
identifiers = identifiers.split(",") unless Array === identifiers
|
129
|
+
m = begin
|
130
|
+
Object.const_get entity
|
131
|
+
rescue
|
132
|
+
m = Module.new
|
133
|
+
m.extend Entity
|
134
|
+
m.include Entity::Identified
|
135
|
+
Object.const_set entity, m
|
136
|
+
end
|
137
|
+
|
138
|
+
identifiers.each do |file|
|
139
|
+
m.add_identifiers Path.setup(file), self.format
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
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
|