spreadskos 0.0.4 → 0.0.5
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.
- data/lib/spreadskos.rb +173 -168
- metadata +1 -1
data/lib/spreadskos.rb
CHANGED
|
@@ -1,268 +1,273 @@
|
|
|
1
1
|
# encoding: UTF-8
|
|
2
|
-
require 'linkeddata'
|
|
3
|
-
require 'roo'
|
|
4
|
-
require 'logger'
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
module Spreadskos
|
|
4
|
+
require 'linkeddata'
|
|
5
|
+
require 'roo'
|
|
6
|
+
require 'logger'
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
# Creates SKOS files from vocabulary data in a spreadsheet template.
|
|
9
|
+
class Converter
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
@log.info("init")
|
|
11
|
+
def initialize(filepath=nil)
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
@log = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
|
14
|
+
@log.info("init")
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
@filepath = filepath
|
|
17
|
+
@default_lang = :en
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
@skos_objs = []
|
|
20
|
+
setup_skos
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
load_spreadsheet(filepath)
|
|
22
|
+
# start new SKOS doc output
|
|
23
|
+
@graph = RDF::Graph.new
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
# Load spreadsheet data
|
|
26
|
+
@spreadsheet = nil
|
|
27
|
+
load_spreadsheet(filepath)
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
# set up vocab basic info
|
|
30
|
+
@namespace = "http://example.com/your_namespace/"
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
@concepts = []
|
|
34
|
-
add_concepts
|
|
32
|
+
setup_vocab_info
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
# Add concepts and related information
|
|
35
|
+
@concepts = []
|
|
36
|
+
add_concepts
|
|
37
|
+
|
|
38
|
+
end
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
# Load SKOS itself to be able to render labels etc later.
|
|
43
|
+
def setup_skos
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
#skosfile = File.dirname(__FILE__) + ::File::SEPARATOR + "skos.rdf"
|
|
46
|
+
skosfile = File.expand_path("skos.rdf", File.dirname(__FILE__))
|
|
47
|
+
@log.info("Load SKOS itself to be able to render labels etc later. File: " + skosfile)
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
RDF::Reader.open(skosfile) do |reader|
|
|
50
|
+
reader.each_statement do |statement|
|
|
51
|
+
if statement.predicate == "http://www.w3.org/2000/01/rdf-schema#label" then
|
|
52
|
+
@skos_objs << {:obj => statement.subject.to_s.sub("http://www.w3.org/2004/02/skos/core#",""), :label => statement.object.to_s.downcase.strip}
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
@log.info("Adding #{statement.object.to_s.downcase.strip}, #{statement.subject.to_s}")
|
|
55
|
+
end
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
|
-
end
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
end
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
def load_spreadsheet(filepath)
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
@log.info("Loading #{filepath}")
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
if filepath then
|
|
68
|
+
@spreadsheet = Roo::Spreadsheet.open(filepath)
|
|
69
|
+
else
|
|
70
|
+
raise "Spreadsheet not found."
|
|
71
|
+
end
|
|
69
72
|
end
|
|
70
|
-
end
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
def setup_vocab_info
|
|
77
|
+
|
|
78
|
+
# Build concept scheme info from first sheet
|
|
79
|
+
info_sheet = @spreadsheet.sheet(0)
|
|
80
|
+
|
|
81
|
+
title = ""
|
|
82
|
+
description = ""
|
|
83
|
+
version = ""
|
|
84
|
+
creators = ""
|
|
85
|
+
contributors = ""
|
|
86
|
+
|
|
87
|
+
1.upto(info_sheet.last_row) do |row_no|
|
|
88
|
+
case info_sheet.cell(row_no, 1)
|
|
89
|
+
when "Title:"
|
|
90
|
+
title = info_sheet.cell(row_no, 2)
|
|
91
|
+
when "Description:"
|
|
92
|
+
description = info_sheet.cell(row_no, 2)
|
|
93
|
+
when "Version:"
|
|
94
|
+
version = info_sheet.cell(row_no, 2)
|
|
95
|
+
when "Vocabulary identifier:"
|
|
96
|
+
@namespace = info_sheet.cell(row_no, 2)
|
|
97
|
+
when "Default language:"
|
|
98
|
+
@default_lang = info_sheet.cell(row_no, 2).strip
|
|
99
|
+
when "Creators:"
|
|
100
|
+
creators = info_sheet.cell(row_no, 2).strip.split(",")
|
|
101
|
+
when "Contributors:"
|
|
102
|
+
contributors = info_sheet.cell(row_no, 2).strip.split(",")
|
|
103
|
+
else
|
|
104
|
+
@log.info("Unknown property: " + info_sheet.cell(row_no, 1))
|
|
105
|
+
end
|
|
103
106
|
end
|
|
104
|
-
end
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
# Write it
|
|
109
|
+
@graph << [RDF::URI.intern(@namespace), RDF.type, RDF::SKOS.ConceptScheme]
|
|
110
|
+
@graph << [RDF::URI.intern(@namespace), RDF::RDFS.label, RDF::Literal.new(title, :language => @default_lang)]
|
|
111
|
+
@graph << [RDF::URI.intern(@namespace), RDF::DC.description, RDF::Literal.new(description, :language => @default_lang)]
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
add_creators(creators)
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
add_contributors(contributors)
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
end
|
|
116
118
|
|
|
117
119
|
|
|
118
120
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
def add_creators(creators)
|
|
122
|
+
@log.info("adding creators")
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
creators.each do |creator|
|
|
125
|
+
@graph << [RDF::URI.new(@namespace), RDF::DC.creator, RDF::Literal.new(creator.strip)]
|
|
126
|
+
end
|
|
124
127
|
end
|
|
125
|
-
end
|
|
126
128
|
|
|
127
129
|
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
def add_contributors(contributors)
|
|
132
|
+
@log.info("adding contributors")
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
contributors.each do |contributor|
|
|
135
|
+
@graph << [RDF::URI.new(@namespace), RDF::DC.contributor, RDF::Literal.new(contributor.strip)]
|
|
136
|
+
end
|
|
134
137
|
end
|
|
135
|
-
end
|
|
136
138
|
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
def add_concepts
|
|
139
141
|
|
|
140
|
-
|
|
142
|
+
@log.info("adding concepts")
|
|
141
143
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
# Add concepts and labels from second worksheet
|
|
145
|
+
concept_sheet = @spreadsheet.sheet(1)
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
columns = []
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
# Build column model
|
|
150
|
+
1.upto(concept_sheet.last_column) do |col_no|
|
|
151
|
+
@log.info("\tcol: " + concept_sheet.cell(1, col_no))
|
|
152
|
+
columns << concept_sheet.cell(1, col_no)
|
|
153
|
+
end
|
|
152
154
|
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
# Iterate rows
|
|
157
|
+
2.upto(concept_sheet.last_row) do |row_no|
|
|
156
158
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
# Concepts fragments are in the first column
|
|
160
|
+
concept_fragment_id = concept_sheet.cell(row_no, 1)
|
|
159
161
|
|
|
160
|
-
|
|
162
|
+
if concept_fragment_id.size > 0 then
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
concept = uri_for_concept_fragment(concept_fragment_id)
|
|
165
|
+
@log.info("concept: " + concept)
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
# Add concept to graph
|
|
168
|
+
@graph << [concept, RDF.type, RDF::SKOS.Concept]
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
# Connect it to the concept scheme
|
|
171
|
+
add_concept_to_scheme(concept)
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
#loop columns for concept
|
|
174
|
+
2.upto(concept_sheet.last_column) do |col_no|
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
# What property is this?
|
|
177
|
+
property, lang = skosname_and_lang_from_column_head(columns[col_no - 1])
|
|
178
|
+
@log.info("Prop+lang: #{property}, #{lang}")
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
#Value
|
|
181
|
+
value = concept_sheet.cell(row_no, col_no)
|
|
182
|
+
@log.info("Val: #{value}")
|
|
181
183
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
# literal?
|
|
185
|
+
if value and value.strip.size > 0
|
|
186
|
+
if value.start_with?("#") or value.start_with?("http")
|
|
187
|
+
# Add this property to the graph
|
|
188
|
+
add_property_to_graph(concept, property, value)
|
|
189
|
+
else
|
|
190
|
+
value = RDF::Literal.new(value, :language => lang)
|
|
191
|
+
add_property_to_graph(concept, property, value)
|
|
192
|
+
end
|
|
190
193
|
end
|
|
191
|
-
end
|
|
192
194
|
|
|
195
|
+
end
|
|
193
196
|
end
|
|
197
|
+
|
|
194
198
|
end
|
|
195
199
|
|
|
196
200
|
end
|
|
197
201
|
|
|
198
|
-
end
|
|
199
|
-
|
|
200
202
|
|
|
201
203
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
def add_property_to_graph(concept, property, value)
|
|
205
|
+
@graph << [concept, property, value]
|
|
206
|
+
end
|
|
205
207
|
|
|
206
208
|
|
|
207
209
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
def add_concept_to_scheme(concept)
|
|
211
|
+
@graph << [concept, RDF::SKOS.inScheme, @namespace]
|
|
212
|
+
end
|
|
211
213
|
|
|
212
214
|
|
|
213
215
|
|
|
214
216
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
def uri_for_concept_fragment(fragment)
|
|
218
|
+
if fragment.downcase.start_with?("http") then
|
|
219
|
+
# user edited their own concept identifier
|
|
220
|
+
return RDF::URI.new(fragment)
|
|
221
|
+
else
|
|
222
|
+
# make local identifier
|
|
223
|
+
return RDF::URI.new(@namespace + fragment)
|
|
224
|
+
end
|
|
222
225
|
end
|
|
223
|
-
end
|
|
224
226
|
|
|
225
227
|
|
|
226
228
|
|
|
227
|
-
|
|
229
|
+
def skosname_and_lang_from_column_head(column)
|
|
228
230
|
|
|
229
|
-
|
|
231
|
+
@log.info("\t Mapping column #{column}")
|
|
230
232
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
# find skos property from column header text (e.g. "Preferred label (en)")
|
|
234
|
+
label, lang = column.split("(")
|
|
235
|
+
if lang then
|
|
236
|
+
lang = lang.sub(")","").to_sym
|
|
237
|
+
else
|
|
238
|
+
# Default lang
|
|
239
|
+
lang = @default_lang
|
|
240
|
+
end
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
skosprop = skos_from_label(label)
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
@log.info("skosprop: #{skosprop}")
|
|
243
245
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
return skosprop, lang
|
|
247
|
+
end
|
|
246
248
|
|
|
247
249
|
|
|
248
250
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
+
def skos_from_label(label)
|
|
252
|
+
@log.info("Looking up >#{label.downcase.strip}<")
|
|
251
253
|
|
|
252
|
-
|
|
254
|
+
prop = @skos_objs.select {|o| o[:label] == label.downcase.strip }
|
|
253
255
|
|
|
254
|
-
|
|
256
|
+
@log.info(prop)
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
return eval("RDF::SKOS." + prop[0][:obj])
|
|
259
|
+
end
|
|
258
260
|
|
|
259
261
|
|
|
260
|
-
|
|
262
|
+
def write_graph(filename="result.rdf", format=:rdfxml)
|
|
261
263
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
File.open(filename, 'w:UTF-8') { |file|
|
|
265
|
+
file.write(@graph.dump(format))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
end
|
|
265
269
|
|
|
266
270
|
end
|
|
267
271
|
|
|
272
|
+
|
|
268
273
|
end
|