spread2rdf 0.0.1pre.1 → 0.0.1
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 +8 -8
- data/Gemfile.ocra +6 -0
- data/VERSION +1 -1
- data/bin/spread2rdf +1 -1
- data/lib/spread2rdf/attributes.rb +1 -0
- data/lib/spread2rdf/cli.rb +128 -34
- data/lib/spread2rdf/coord.rb +49 -0
- data/lib/spread2rdf/mapping/cell.rb +105 -0
- data/lib/spread2rdf/mapping/column.rb +63 -0
- data/lib/spread2rdf/mapping/column_block.rb +23 -0
- data/lib/spread2rdf/mapping/default_cell_mappings.rb +26 -0
- data/lib/spread2rdf/mapping/element.rb +64 -0
- data/lib/spread2rdf/mapping/resource.rb +95 -0
- data/lib/spread2rdf/mapping/sheet.rb +80 -0
- data/lib/spread2rdf/mapping/spreadsheet.rb +56 -0
- data/lib/spread2rdf/mapping/statement.rb +22 -0
- data/lib/spread2rdf/mapping/worksheet.rb +12 -0
- data/lib/spread2rdf/namespace.rb +6 -2
- data/lib/spread2rdf/roo_helper.rb +45 -0
- data/lib/spread2rdf/schema/column.rb +37 -0
- data/lib/spread2rdf/{spreadsheet/sub_sheet.rb → schema/column_block.rb} +3 -2
- data/lib/spread2rdf/{spreadsheet → schema}/element.rb +12 -16
- data/lib/spread2rdf/schema/schema.rb +19 -0
- data/lib/spread2rdf/schema/sheet.rb +87 -0
- data/lib/spread2rdf/schema/sheet_dsl.rb +54 -0
- data/lib/spread2rdf/schema/spreadsheet.rb +49 -0
- data/lib/spread2rdf/schema/spreadsheet_dsl.rb +42 -0
- data/lib/spread2rdf/schema/statement_mapping_schema.rb +26 -0
- data/lib/spread2rdf/schema/worksheet.rb +47 -0
- data/lib/spread2rdf.rb +34 -2
- data/ontologies/unit-v1.1.ttl +8330 -0
- data/spread2rdf.gemspec +4 -2
- metadata +50 -26
- data/lib/spread2rdf/helper.rb +0 -14
- data/lib/spread2rdf/spreadsheet/column.rb +0 -48
- data/lib/spread2rdf/spreadsheet/column_mapping_context.rb +0 -156
- data/lib/spread2rdf/spreadsheet/coord.rb +0 -51
- data/lib/spread2rdf/spreadsheet/mapping_context.rb +0 -67
- data/lib/spread2rdf/spreadsheet/mapping_dsl.rb +0 -23
- data/lib/spread2rdf/spreadsheet/sheet.rb +0 -128
- data/lib/spread2rdf/spreadsheet/sheet_dsl.rb +0 -34
- data/lib/spread2rdf/spreadsheet/sheet_mapping_context.rb +0 -90
- data/lib/spread2rdf/spreadsheet/sub_sheet_mapping_context.rb +0 -55
- data/lib/spread2rdf/spreadsheet/worksheet.rb +0 -49
- data/lib/spread2rdf/spreadsheet.rb +0 -92
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTJjZDkxNmJhNmM2ZjFmYjM4ZGJlZjE2MGQyZmZkNWMzMWI1NGQ4Yw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjFmZjA5ZmZmOTk5ZWJjODAwOTgwZDNkZjkyYWY3NTgwMjllYzBkOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTEzM2JjNjkyNTkyMGJiNDUwZTE1OTE0NjE4ZjcyZmNkZTk5MWFjMDYyYzU5
|
10
|
+
Nzk0NWU5MDVhMmIwYmJkNjFiMjA5ZmI5NjU1Yjg4NzMxNWM5Yjk3MzFjMTgy
|
11
|
+
MGRjMWJiN2VlMzU5OWNkYmNhMDkxZjNlN2ExOWUwN2ZhYmQzOGI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2MxZGQ4YjMxYmI2Y2IyN2NiMjFiN2U4NmRkZjA4NGJlMWNkNzgxYjg5MWU2
|
14
|
+
YTkyZmY1ZGM5YWIyZTkwMjFmODI5ZDUwZTc4ZWQ2ZmFlMzVmMzc3NGE1Nzcz
|
15
|
+
ZGIzZDBmMTA2MWRhNjJmZDJmMGY3NzM3NDEyOGNiODFlOTQwNGU=
|
data/Gemfile.ocra
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
data/bin/spread2rdf
CHANGED
data/lib/spread2rdf/cli.rb
CHANGED
@@ -1,63 +1,119 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require 'singleton'
|
2
3
|
|
3
4
|
module Spread2RDF
|
4
5
|
class Cli
|
5
|
-
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_accessor :mapping_schema
|
9
|
+
|
10
|
+
def run(options = {})
|
11
|
+
@running = true
|
12
|
+
init(options)
|
6
13
|
parse_command_line!
|
14
|
+
case
|
15
|
+
when compile? then compile(@mapping_schema)
|
16
|
+
else convert
|
17
|
+
end
|
18
|
+
self
|
19
|
+
rescue => e
|
20
|
+
if Spread2RDF.debug_mode
|
21
|
+
raise e
|
22
|
+
else
|
23
|
+
abort e.to_s
|
24
|
+
end
|
7
25
|
end
|
8
26
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
27
|
+
def init(options)
|
28
|
+
@options = options
|
29
|
+
@input_file = @options.delete(:input_file) if @options[:input_file]
|
30
|
+
self.mapping_schema = @options.delete(:schema) if @options[:schema]
|
31
|
+
end
|
32
|
+
private :init
|
33
|
+
|
34
|
+
def running?
|
35
|
+
@running
|
36
|
+
end
|
37
|
+
|
38
|
+
def compile?
|
39
|
+
!!@options[:compile]
|
40
|
+
end
|
41
|
+
|
42
|
+
def mapping_schema=(schema)
|
43
|
+
@mapping_schema =
|
44
|
+
case schema
|
45
|
+
when nil then nil
|
46
|
+
when Schema::Spreadsheet then schema
|
47
|
+
when String
|
48
|
+
abort "no schema specification file given" if schema.nil?
|
49
|
+
abort "couldn't find schema specification file #{schema}" unless
|
50
|
+
File.exist?(schema)
|
51
|
+
load schema
|
52
|
+
abort "no schema specification found" if Schema.definitions.empty?
|
53
|
+
Schema.definitions.first
|
54
|
+
else raise ArgumentError
|
55
|
+
end
|
21
56
|
end
|
22
57
|
|
23
58
|
private
|
24
59
|
|
25
|
-
|
26
|
-
def parse_command_line!(options={})
|
27
|
-
@options = options
|
60
|
+
def parse_command_line!
|
28
61
|
optparse = OptionParser.new do |opts|
|
29
|
-
|
62
|
+
if mapping_schema
|
63
|
+
opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] SPREAD_SHEET_FILE"
|
64
|
+
else
|
65
|
+
opts.banner = 'Usage: spread2rdf [options] -s SPEC_FILE SPREAD_SHEET_FILE'
|
66
|
+
end
|
30
67
|
|
31
68
|
opts.on( '-h', '--help', 'Display this information' ) do
|
32
69
|
puts opts
|
33
70
|
exit
|
34
71
|
end
|
35
72
|
|
36
|
-
|
37
|
-
|
38
|
-
|
73
|
+
opts.on( '-v', '--version', 'Print version information' ) do
|
74
|
+
puts "Spread2RDF #{VERSION}"
|
75
|
+
exit
|
76
|
+
end
|
77
|
+
|
78
|
+
if not mapping_schema
|
79
|
+
opts.on( '-c', '--compile', 'Compile the schema specification to an executable' ) do
|
80
|
+
@options[:compile] = true
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on( '-s', '--schema SPEC_FILE', 'Schema specification file (required)' ) do |file|
|
84
|
+
if @options[:compile]
|
85
|
+
@mapping_schema = file
|
86
|
+
else
|
87
|
+
self.mapping_schema = file
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
@options[:output_dir] ||= '.'
|
93
|
+
opts.on( '-o', '--output DIR', "Output directory (default: #{@options[:output_dir]})" ) do |dir|
|
94
|
+
abort "Output directory #{dir} doesn't exist" unless compile? or Dir.exist?(dir)
|
39
95
|
@options[:output_dir] = dir
|
40
96
|
end
|
41
97
|
|
42
|
-
@options[:output_format]
|
98
|
+
@options[:output_format] ||= 'ttl'
|
43
99
|
opts.on( '-f', '--output-format FORMAT', 'Serialization format for the RDF data',
|
44
|
-
"FORMAT being one of: nt, n3, ttl, rdf, xml, html, json (default:
|
45
|
-
#format = 'turtle' if format == 'ttl'
|
100
|
+
"FORMAT being one of: nt, n3, ttl, rdf, xml, html, json (default: #{@options[:output_format]})") do |format|
|
46
101
|
@options[:output_format] = format.strip.downcase
|
47
102
|
end
|
48
103
|
|
49
|
-
|
50
|
-
|
51
|
-
@options[:schema_spec_file] = file
|
104
|
+
opts.on( '-d', '--debug', 'Run in debug mode' ) do
|
105
|
+
Spread2RDF.debug_mode = true
|
52
106
|
end
|
53
107
|
|
54
108
|
end
|
55
|
-
|
56
109
|
optparse.parse!
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
110
|
+
if compile?
|
111
|
+
@mapping_schema ||= ARGV.first or raise OptionParser::ParseError, 'required schema specification file missing'
|
112
|
+
else
|
113
|
+
raise OptionParser::ParseError, 'required schema specification file missing' if @mapping_schema.nil?
|
114
|
+
@input_file = ARGV.first or @input_file or
|
115
|
+
raise OptionParser::ParseError, 'required file arguments missing'
|
116
|
+
end
|
61
117
|
rescue OptionParser::ParseError => e
|
62
118
|
puts e.message
|
63
119
|
puts optparse.help
|
@@ -72,8 +128,8 @@ module Spread2RDF
|
|
72
128
|
|
73
129
|
def write_output
|
74
130
|
filename = output_filename
|
75
|
-
abort 'No RDF data to write!' if @
|
76
|
-
graph = @
|
131
|
+
abort 'No RDF data to write!' if @mapping.try(:graph).blank?
|
132
|
+
graph = @mapping.graph
|
77
133
|
puts "Writing #{graph.count} RDF statements to #{filename} ... "
|
78
134
|
# TODO: base_uri: ... for writer constructor
|
79
135
|
RDF::Writer.open(filename) do |writer|
|
@@ -85,7 +141,45 @@ module Spread2RDF
|
|
85
141
|
end
|
86
142
|
graph.each_statement { |statement| writer << statement }
|
87
143
|
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def convert
|
148
|
+
puts "Reading #{@input_file} ..."
|
149
|
+
@mapping = mapping_schema.map(@input_file)
|
150
|
+
write_output
|
151
|
+
end
|
152
|
+
|
153
|
+
def compile(mapping_file)
|
154
|
+
output = if File.directory?(@options[:output_dir])
|
155
|
+
output_dir = @options[:output_dir]
|
156
|
+
output_file = File.basename(mapping_file, File.extname(mapping_file)) + '.exe'
|
157
|
+
File.join(output_dir, output_file)
|
158
|
+
else
|
159
|
+
@options[:output_dir] += '.exe' unless File.extname(@options[:output_dir]) == '.exe'
|
160
|
+
@options[:output_dir]
|
161
|
+
end
|
162
|
+
ocra_options = [
|
163
|
+
'--gem-full',
|
164
|
+
'--add-all-core',
|
165
|
+
'--no-autoload',
|
166
|
+
'--no-dep-run',
|
167
|
+
'--no-enc',
|
168
|
+
'--console'
|
169
|
+
]
|
170
|
+
#ocra_options << '--quiet' unless Spread2RDF.debug_mode
|
171
|
+
ocra_options << '--debug' if Spread2RDF.debug_mode
|
172
|
+
ocra_gemfile = File.join(Spread2RDF::ROOT, 'Gemfile.ocra')
|
173
|
+
ocra_options << "--gemfile #{ocra_gemfile}"
|
174
|
+
ocra_options << "--output #{output}"
|
175
|
+
ocra_cmd = "ocra #{ocra_options.join(' ')} #{mapping_file}"
|
176
|
+
puts "compiling #{mapping_file} to #{output}"
|
177
|
+
#puts ocra_cmd
|
178
|
+
Kernel.system(ocra_cmd)
|
88
179
|
end
|
180
|
+
|
89
181
|
self
|
90
182
|
end
|
91
|
-
|
183
|
+
|
184
|
+
CLI = Cli.instance
|
185
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
class Coord < Struct.new(:column, :row)
|
3
|
+
def initialize(*args)
|
4
|
+
case args.length
|
5
|
+
when 2 then super
|
6
|
+
when 1
|
7
|
+
case args = args.first
|
8
|
+
when Hash
|
9
|
+
super(args[:column], args[:row])
|
10
|
+
when Symbol, String
|
11
|
+
coord = args.to_s
|
12
|
+
raise "Invalid cell coordinates #{coord}" unless coord =~ /(\w+)(\d+)/
|
13
|
+
super(Regexp.last_match[1], Regexp.last_match[2].to_i)
|
14
|
+
else raise ArgumentError, "can't handle argument #{args}"
|
15
|
+
end
|
16
|
+
else raise ArgumentError, "too many arguments: #{args}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def column_as_number
|
21
|
+
Roo::Base.letter_to_number(column)
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_as_index
|
25
|
+
column_as_number - 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def increment_column(count = 1)
|
29
|
+
self.class.increment_column(self.column, count)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"#{column}#{row}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_sym
|
37
|
+
to_s.to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
alias [] new
|
42
|
+
|
43
|
+
def increment_column(column, count=1)
|
44
|
+
Roo::Base.number_to_letter(Roo::Base.letter_to_number(column) + count)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
module Mapping
|
3
|
+
class Cell < Element
|
4
|
+
|
5
|
+
attr_reader :row
|
6
|
+
|
7
|
+
def_delegators :parent, :subject, :worksheet, :resource
|
8
|
+
def_delegators :schema, :predicate
|
9
|
+
|
10
|
+
def initialize(sheet, parent, row)
|
11
|
+
super(sheet, parent)
|
12
|
+
@row = row
|
13
|
+
map
|
14
|
+
end
|
15
|
+
|
16
|
+
def map
|
17
|
+
exec(&schema.block) unless empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def coord
|
21
|
+
Coord[column: schema.coord, row: row]
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
@value ||= worksheet.cell_value(coord)
|
26
|
+
end
|
27
|
+
|
28
|
+
def object
|
29
|
+
@object ||= value && map_to_object(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def empty?
|
33
|
+
value.blank?
|
34
|
+
end
|
35
|
+
|
36
|
+
def map_to_object(value)
|
37
|
+
case schema.object_mapping_mode
|
38
|
+
when :to_string then value
|
39
|
+
when :resource_ref then resolve_resource_ref
|
40
|
+
when :new_resource then create_resource_object
|
41
|
+
when :custom then exec(&schema.cell_mapping)
|
42
|
+
else raise 'internal error: unknown column mapping type'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
private :map_to_object
|
46
|
+
|
47
|
+
def resolve_resource_ref
|
48
|
+
source = schema.object[:from]
|
49
|
+
source = { worksheet: source } if source.is_a? Symbol
|
50
|
+
raise ArgumentError, "expecting a Hash as source, but got #{source}" unless source.is_a? Hash
|
51
|
+
source_worksheet = source[:worksheet]
|
52
|
+
source_worksheet = spreadsheet.worksheet(source_worksheet)
|
53
|
+
raise "#{self}: couldn't find source worksheet #{source[:worksheet]}" if source_worksheet.nil?
|
54
|
+
source_predicate = source[:predicate] || RDF::RDFS.label
|
55
|
+
result = source_worksheet.graph.query([nil, source_predicate, value])
|
56
|
+
raise "#{self}: couldn't find a resource for #{value} in #{source_worksheet}" if result.empty?
|
57
|
+
raise "#{self}: found multiple resources for #{value} in #{source_worksheet}: #{result.map(&:subject)}" if result.count > 1
|
58
|
+
result.first.subject
|
59
|
+
end
|
60
|
+
private :resolve_resource_ref
|
61
|
+
|
62
|
+
def create_resource_object
|
63
|
+
case
|
64
|
+
when (schema.object.try(:fetch, :uri, nil) || object) == :bnode
|
65
|
+
RDF::Node.new
|
66
|
+
else
|
67
|
+
raise NotImplementedError
|
68
|
+
end
|
69
|
+
end
|
70
|
+
private :create_resource_object
|
71
|
+
|
72
|
+
##########################################################################
|
73
|
+
# for the DSL for column statement blocks
|
74
|
+
|
75
|
+
def value_of_column(name)
|
76
|
+
sheet_schema = parent.parent.schema
|
77
|
+
other_column = sheet_schema.column(name)
|
78
|
+
raise "couldn't find column #{name} when mapping #{self}" if
|
79
|
+
other_column.nil?
|
80
|
+
worksheet.cell_value(column: other_column.coord, row: row)
|
81
|
+
end
|
82
|
+
|
83
|
+
def object_of_column(name)
|
84
|
+
other_column = resource.column!(name)
|
85
|
+
raise "couldn't find column #{name} when mapping #{self}" if
|
86
|
+
other_column.nil?
|
87
|
+
other_column.cell!(row).object
|
88
|
+
end
|
89
|
+
|
90
|
+
def exec(&block)
|
91
|
+
#puts "executing block of #{@___column___} in row #{row}"
|
92
|
+
self.instance_exec(value, &block) if block_given?
|
93
|
+
end
|
94
|
+
private :exec
|
95
|
+
|
96
|
+
##########################################################################
|
97
|
+
# Element#_children_
|
98
|
+
|
99
|
+
def _children_
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
module Mapping
|
3
|
+
class Column < Element
|
4
|
+
include Statement
|
5
|
+
|
6
|
+
def_delegators :parent, :subject, :worksheet, :row_range
|
7
|
+
def_delegators :schema, :predicate
|
8
|
+
|
9
|
+
alias resource parent
|
10
|
+
|
11
|
+
def initialize(sheet, parent)
|
12
|
+
super
|
13
|
+
@cells = {}
|
14
|
+
map(row_range)
|
15
|
+
end
|
16
|
+
|
17
|
+
def map(range)
|
18
|
+
#puts "mapping #{self} in #{range} ..."
|
19
|
+
case range
|
20
|
+
when Integer
|
21
|
+
cell = cell!(range)
|
22
|
+
statements_to_object(cell.object) unless cell.empty?
|
23
|
+
when Range
|
24
|
+
range.each { |row| self.map(row) }
|
25
|
+
else raise ArgumentError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def objects
|
30
|
+
cells.map(&:object)
|
31
|
+
end
|
32
|
+
|
33
|
+
def cell_coord(row)
|
34
|
+
case row
|
35
|
+
when Integer then Coord[column: schema.coord, row: row]
|
36
|
+
when Coord then Coord
|
37
|
+
when Hash then Coord[row]
|
38
|
+
else raise ArgumentError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##########################################################################
|
43
|
+
# Mapping::Element structure
|
44
|
+
|
45
|
+
def cells
|
46
|
+
@cells.values
|
47
|
+
end
|
48
|
+
alias _children_ cells
|
49
|
+
|
50
|
+
def cell(coord)
|
51
|
+
coord = cell_coord(coord)
|
52
|
+
@cells[coord.to_sym] # TODO: search @sub_sheet_mappings also
|
53
|
+
end
|
54
|
+
|
55
|
+
def cell!(coord)
|
56
|
+
coord = cell_coord(coord)
|
57
|
+
@cells[coord.to_sym] ||= Cell.new(schema, self, coord.row)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
module Mapping
|
3
|
+
class ColumnBlock < Sheet
|
4
|
+
include Statement
|
5
|
+
|
6
|
+
def_delegators :parent, :subject, :row_range
|
7
|
+
def_delegators :schema, :predicate
|
8
|
+
|
9
|
+
def map
|
10
|
+
super
|
11
|
+
@resources.each do |resource|
|
12
|
+
statements_to_object(resource.subject) unless resource.empty?
|
13
|
+
end
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def objects
|
18
|
+
@resources.map(&:subject)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
module Mapping
|
3
|
+
class Cell
|
4
|
+
module Default
|
5
|
+
def self.unit_mapping(value)
|
6
|
+
@qudt ||= RDF::Graph.load File.join(ONTOLOGY_DIR, 'unit-v1.1.ttl')
|
7
|
+
query = RDF::Query.new(
|
8
|
+
unit: { RDF::URI.new('http://qudt.org/schema/qudt#symbol') =>
|
9
|
+
RDF::Literal.new(value, datatype: RDF::XSD.string) })
|
10
|
+
solutions = query.execute(@qudt)
|
11
|
+
raise "unit #{value} is not unique; possible candidates:
|
12
|
+
#{solutions.map(&:unit).ai}" if solutions.count > 1
|
13
|
+
raise "couldn't find a QUDT unit for unit '#{value}''" if solutions.empty?
|
14
|
+
solutions.first.unit
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.uri_normalization(string)
|
18
|
+
string
|
19
|
+
.gsub(', ', '-')
|
20
|
+
.gsub(' ', '-')
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Spread2RDF
|
4
|
+
module Mapping
|
5
|
+
class Element
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :schema
|
9
|
+
attr_reader :parent
|
10
|
+
|
11
|
+
def_delegators :parent, :spreadsheet
|
12
|
+
|
13
|
+
def initialize(schema, parent)
|
14
|
+
@graph = RDF::Repository.new
|
15
|
+
@schema = schema
|
16
|
+
@parent = parent
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{self.class.name.split('::').last}-mapping of #{schema}"
|
21
|
+
end
|
22
|
+
|
23
|
+
##########################################################################
|
24
|
+
# children
|
25
|
+
|
26
|
+
def _children_
|
27
|
+
raise NotImplementedError, 'subclasses must implement this method'
|
28
|
+
end
|
29
|
+
|
30
|
+
def empty?
|
31
|
+
_children_.empty? or _children_.all?(&:empty?)
|
32
|
+
end
|
33
|
+
|
34
|
+
##########################################################################
|
35
|
+
# RDF graph
|
36
|
+
|
37
|
+
def graph
|
38
|
+
if _children_
|
39
|
+
_children_.inject(@graph.clone) { |graph, child| graph << child.graph }
|
40
|
+
else
|
41
|
+
@graph
|
42
|
+
end
|
43
|
+
end
|
44
|
+
alias to_rdf graph
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def add_statement(*args)
|
49
|
+
args = args.first if args.count == 1 and args.first.is_a? Array
|
50
|
+
#puts "adding statement: #{args.inspect}"
|
51
|
+
raise "internal error: trying to add a bad triple with nil value: #{args}" if args.count != 3 or args.one? { |arg| arg.nil? }
|
52
|
+
@graph << RDF::Statement.new(*args)
|
53
|
+
end
|
54
|
+
alias statement add_statement
|
55
|
+
|
56
|
+
def add_statements(*args)
|
57
|
+
args = args.first if args.count == 1 and args.first.is_a? Array
|
58
|
+
args.each { |arg| statement(arg) }
|
59
|
+
end
|
60
|
+
alias statements add_statements
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Spread2RDF
|
2
|
+
module Mapping
|
3
|
+
class Resource < Element
|
4
|
+
|
5
|
+
attr_reader :row_range
|
6
|
+
|
7
|
+
def_delegators :parent, :worksheet
|
8
|
+
|
9
|
+
def initialize(sheet, parent, row_range)
|
10
|
+
super(sheet, parent)
|
11
|
+
@columns = {}
|
12
|
+
@row_range = row_range
|
13
|
+
map
|
14
|
+
end
|
15
|
+
|
16
|
+
def map
|
17
|
+
#puts "processing #{self} in #{row_range}"
|
18
|
+
object_columns = parent.schema.columns - [parent.schema.subject_column]
|
19
|
+
object_columns.each { |column| column!(column) }
|
20
|
+
subject_description unless empty?
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
##########################################################################
|
25
|
+
# subject mapping
|
26
|
+
|
27
|
+
def subject
|
28
|
+
@subject ||=
|
29
|
+
case schema.subject_mapping_mode
|
30
|
+
when :bnode then RDF::Node.new
|
31
|
+
when :from_column then subject_resource_from_column
|
32
|
+
else raise 'unknown subject mapping type'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias subject_resource subject
|
36
|
+
|
37
|
+
def subject_name_suffix
|
38
|
+
cells = row_range.map do |row|
|
39
|
+
parent.cell_value(row: row, column: schema.subject_column.coord).presence
|
40
|
+
end.compact
|
41
|
+
raise "no subject found for Resource in #{row_range} of #{sheet}" if cells.empty?
|
42
|
+
raise "multiple subjects found for Resource in #{row_range} of #{sheet}: #{cells.inspect}" if cells.count > 1
|
43
|
+
cells.first
|
44
|
+
end
|
45
|
+
private :subject_name_suffix
|
46
|
+
|
47
|
+
def subject_resource_from_column
|
48
|
+
namespace = schema.subject_namespace
|
49
|
+
subject_suffix = Mapping::Cell::Default.uri_normalization(subject_name_suffix)
|
50
|
+
#puts "subject resource for #{sheet} in #{range}: " + RDF::URI.new("#{namespace}#{subject_suffix}" )
|
51
|
+
RDF::URI.new("#{namespace}#{subject_suffix}")
|
52
|
+
end
|
53
|
+
private :subject_resource_from_column
|
54
|
+
|
55
|
+
def subject_description
|
56
|
+
type = schema.subject_resource_type
|
57
|
+
statement(subject, RDF.type, type) unless type.nil?
|
58
|
+
if type == RDF::RDFS.Class &&
|
59
|
+
super_class = schema.subject.try(:fetch, :sub_class_of, nil)
|
60
|
+
statement(subject, RDF::RDFS.subClassOf, super_class)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
private :subject_description
|
64
|
+
|
65
|
+
##########################################################################
|
66
|
+
# Mapping::Element structure
|
67
|
+
|
68
|
+
def columns
|
69
|
+
@columns.values
|
70
|
+
end
|
71
|
+
alias _children_ columns
|
72
|
+
|
73
|
+
def column(name)
|
74
|
+
@columns[column_schema(name).name]
|
75
|
+
end
|
76
|
+
|
77
|
+
def column!(name)
|
78
|
+
column_schema = column_schema(name)
|
79
|
+
@columns[column_schema.name] ||= case column_schema
|
80
|
+
when Schema::Column then Column.new(column_schema, self)
|
81
|
+
when Schema::ColumnBlock then ColumnBlock.new(column_schema, self).map
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def column_schema(name)
|
86
|
+
case name
|
87
|
+
when Schema::Column, Schema::ColumnBlock then name
|
88
|
+
when String, Symbol then schema.column(name)
|
89
|
+
else raise ArgumentError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|