iqvoc 4.7.0 → 4.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +11 -11
- data/Gemfile.lock +178 -122
- data/README.md +39 -24
- data/{test/performance/browsing_test.rb → app/aides/inline_data_helper.rb} +23 -6
- data/app/aides/maker.rb +139 -0
- data/{lib → app/aides}/multi_logger.rb +0 -0
- data/app/aides/origin.rb +47 -0
- data/app/aides/rdfapi.rb +59 -0
- data/app/aides/skos_exporter.rb +151 -0
- data/app/aides/skos_importer.rb +348 -0
- data/app/assets/javascripts/iqvoc/entityselect.js.erb +7 -9
- data/app/controllers/application_controller.rb +1 -3
- data/app/controllers/collections/versions_controller.rb +1 -3
- data/app/controllers/concepts/versions_controller.rb +9 -3
- data/app/controllers/concerns/controller_extensions.rb +109 -0
- data/app/{concerns → controllers/concerns}/reverse_match_errors.rb +0 -0
- data/app/controllers/hierarchy_controller.rb +7 -3
- data/app/controllers/instance_configuration_controller.rb +1 -1
- data/app/controllers/pages_controller.rb +10 -0
- data/app/controllers/search_results_controller.rb +2 -2
- data/app/controllers/triplestore_sync_controller.rb +2 -4
- data/app/helpers/application_helper.rb +1 -1
- data/app/helpers/widget_helper.rb +3 -3
- data/app/jobs/export_job.rb +1 -3
- data/app/jobs/import_job.rb +1 -3
- data/app/models/ability.rb +59 -0
- data/app/models/abstract_user.rb +1 -1
- data/app/models/collection/base.rb +12 -3
- data/app/models/collection/member/skos/base.rb +1 -1
- data/app/models/concept/base.rb +15 -8
- data/app/models/concept/relation/base.rb +1 -1
- data/app/models/concept/relation/skos/base.rb +1 -1
- data/app/models/concept/skos/scheme.rb +1 -1
- data/app/models/concept/validations.rb +1 -1
- data/app/models/concerns/deep_cloning.rb +92 -0
- data/app/models/concerns/first_level_object_scopes.rb +9 -0
- data/app/{concerns → models/concerns}/first_level_object_validations.rb +9 -2
- data/app/models/concerns/rankable.rb +31 -0
- data/app/models/{search_extension.rb → concerns/search_extension.rb} +0 -0
- data/app/{concerns → models/concerns}/versioning.rb +0 -6
- data/app/models/configuration_setting.rb +1 -1
- data/app/models/labeling/skos/base.rb +2 -2
- data/app/models/match/skos/base.rb +2 -2
- data/app/models/note/skos/base.rb +7 -6
- data/app/models/note/skos/change_note.rb +1 -1
- data/{lib/iqvoc/rdf_sync.rb → app/services/rdf_sync_service.rb} +3 -3
- data/app/view_models/concept_view.rb +1 -1
- data/app/views/collections/_form.html.erb +2 -2
- data/app/views/concepts/scheme/edit.html.erb +1 -1
- data/app/views/pages/components.html.erb +45 -0
- data/app/views/pages/version.html.erb +6 -0
- data/app/views/partials/concept/_reverse_match_notice.html.erb +0 -1
- data/app/views/search_results/_sidebar.html.erb +3 -3
- data/config/application.rb +4 -1
- data/config/boot.rb +1 -2
- data/config/database.yml.postgresql +23 -0
- data/config/engine.rb +0 -2
- data/config/environments/heroku.rb +1 -1
- data/config/initializers/inflections.rb +9 -3
- data/config/initializers/iqvoc.rb +1 -7
- data/config/initializers/mime_types.rb +0 -1
- data/config/locales/de.yml +2 -1
- data/config/locales/en.yml +11 -10
- data/config/routes.rb +2 -0
- data/config/travis/database.yml.mysql +9 -0
- data/config/travis/database.yml.postgresql +7 -0
- data/config/travis/database.yml.sqlite +5 -0
- data/db/migrate/20141204151558_add_foreign_key_constraints.rb +23 -0
- data/iqvoc.gemspec +2 -2
- data/lib/generators/app/template.rb +15 -7
- data/lib/iqvoc.rb +2 -1
- data/lib/iqvoc/configuration/core.rb +18 -4
- data/lib/iqvoc/configuration/instance_configuration.rb +125 -0
- data/lib/iqvoc/configuration/navigation.rb +63 -0
- data/lib/iqvoc/environments/development.rb +4 -0
- data/lib/iqvoc/environments/production.rb +11 -12
- data/lib/iqvoc/environments/test.rb +4 -1
- data/lib/iqvoc/version.rb +2 -2
- data/lib/tasks/exporter.rake +1 -4
- data/lib/tasks/importer.rake +1 -5
- data/lib/tasks/sync.rake +1 -2
- data/test/controllers/concept_movement_test.rb +11 -11
- data/test/controllers/hierarchy_test.rb +83 -79
- data/test/controllers/reverse_match_test.rb +2 -2
- data/test/integration/alphabetical_test.rb +2 -3
- data/test/integration/browse_concepts_and_labels_test.rb +2 -2
- data/test/integration/collection_circularity_test.rb +6 -6
- data/test/integration/concept_scheme_browsing_test.rb +2 -2
- data/test/integration/edit_concepts_test.rb +1 -1
- data/test/integration/export_test.rb +5 -3
- data/test/integration/import_test.rb +4 -1
- data/test/integration/instance_configuration_browsing_test.rb +2 -2
- data/test/integration/navigation_test.rb +2 -2
- data/test/integration/note_annotations_test.rb +12 -11
- data/test/integration/reverse_match_job_test.rb +19 -10
- data/test/integration/search_test.rb +6 -6
- data/test/integration/tree_test.rb +3 -3
- data/test/integration/untranslated_test.rb +1 -1
- data/test/models/concept_test.rb +13 -14
- data/test/models/inline_data_test.rb +9 -9
- data/test/models/instance_configuration_test.rb +7 -3
- data/test/models/origin_test.rb +9 -59
- data/test/models/rdf_sync_test.rb +2 -4
- data/test/models/rdfapi_test.rb +0 -2
- data/test/models/skos_collection_import_test.rb +3 -4
- data/test/models/skos_export_test.rb +3 -5
- data/test/models/skos_import_test.rb +12 -10
- data/test/test_helper.rb +0 -1
- data/vendor/assets/stylesheets/{jquery-ui.css.scss → jquery-ui.scss} +0 -0
- data/vendor/assets/stylesheets/{jquery-ui.structure.css.scss → jquery-ui.structure.scss} +0 -0
- data/vendor/assets/stylesheets/{jquery-ui.theme.css.scss → jquery-ui.theme.scss} +0 -0
- metadata +34 -28
- data/lib/iqvoc/ability.rb +0 -60
- data/lib/iqvoc/controller_extensions.rb +0 -111
- data/lib/iqvoc/deep_cloning.rb +0 -90
- data/lib/iqvoc/inline_data_helper.rb +0 -45
- data/lib/iqvoc/instance_configuration.rb +0 -123
- data/lib/iqvoc/maker.rb +0 -141
- data/lib/iqvoc/navigation.rb +0 -61
- data/lib/iqvoc/origin.rb +0 -111
- data/lib/iqvoc/rankable.rb +0 -33
- data/lib/iqvoc/rdfapi.rb +0 -60
- data/lib/iqvoc/skos_exporter.rb +0 -153
- data/lib/iqvoc/skos_importer.rb +0 -337
data/README.md
CHANGED
@@ -4,30 +4,34 @@
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/innoq/iqvoc.png)](http://travis-ci.org/innoq/iqvoc)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/innoq/iqvoc.png)](https://codeclimate.com/github/innoq/iqvoc)
|
6
6
|
|
7
|
-
iQvoc is a vocabulary management tool that combines easy-to-use human interfaces
|
7
|
+
iQvoc is a vocabulary management tool that combines easy-to-use human interfaces
|
8
|
+
with Semantic Web interoperability.
|
8
9
|
|
9
|
-
iQvoc supports vocabularies that are common to many knowledge organization
|
10
|
+
iQvoc supports vocabularies that are common to many knowledge organization
|
11
|
+
systems, such as:
|
10
12
|
|
11
13
|
* Thesauri
|
12
14
|
* Taxonomies
|
13
15
|
* Classification schemes
|
14
16
|
* Subject heading systems
|
15
17
|
|
16
|
-
iQvoc provides comprehensive functionality for all aspects of managing such
|
18
|
+
iQvoc provides comprehensive functionality for all aspects of managing such
|
19
|
+
vocabularies:
|
17
20
|
|
18
21
|
* import of existing vocabularies from a SKOS representation
|
19
22
|
* multilingual display and navigation in any Web browser
|
20
23
|
* editorial features for registered users
|
21
24
|
* publishing the vocabulary in the Semantic Web
|
22
25
|
|
23
|
-
iQvoc is built with state-of-the-art technology and can be easily customized
|
24
|
-
|
26
|
+
iQvoc is built with state-of-the-art technology and can be easily customized
|
27
|
+
according to user's needs.
|
25
28
|
## Setup
|
26
29
|
|
27
30
|
### Heroku
|
28
31
|
|
29
|
-
You can easily setup your iQvoc instance in under 5 minutes, we wanted to make
|
30
|
-
In order to deploy to heroku you need to have an
|
32
|
+
You can easily setup your iQvoc instance in under 5 minutes, we wanted to make
|
33
|
+
this process really easy. In order to deploy to heroku you need to have an
|
34
|
+
account and [heroku toolbelt](https://toolbelt.heroku.com) installed.
|
31
35
|
|
32
36
|
```
|
33
37
|
$ bundle install
|
@@ -46,23 +50,26 @@ Remember to visit the Users section and change the default passwords!
|
|
46
50
|
### Custom
|
47
51
|
|
48
52
|
We recommend running [iQvoc as a Rails engine](https://github.com/innoq/iqvoc/wiki/iQvoc-as-a-Rails-Engine).
|
49
|
-
Running the cloned source code is possible but any modifications would require a
|
53
|
+
Running the cloned source code is possible but any modifications would require a
|
54
|
+
fork.
|
50
55
|
|
51
|
-
1. Configure your database via `config/database.template.yml`.
|
56
|
+
1. Configure your database via `config/database.template.yml`.
|
57
|
+
Don't forget to rename it to `database.yml`
|
52
58
|
2. Run `bundle install`
|
53
59
|
3. Run `bundle exec rake db:create` to create the database
|
54
60
|
4. Create the necessary tables by running `rake db:migrate`
|
55
61
|
5. Load some base data by running `rake db:seed`
|
56
62
|
6. Make sure you have got `config/secrets.yml` in place
|
57
|
-
7. Boot up the app using `bundle exec rails s` (or `passenger start`
|
63
|
+
7. Boot up the app using `bundle exec rails s` (or `passenger start`
|
64
|
+
if you use passenger)
|
58
65
|
8. Log in with "admin@iqvoc" / "admin" or "demo@iqvoc" / "cooluri" (cf. step #5)
|
59
66
|
9. Visit the Users section and change the default passwords
|
60
67
|
|
61
68
|
## Background Jobs
|
62
69
|
|
63
|
-
Note that some features like "Import" and "Export" exposed in the Web UI store
|
64
|
-
as jobs. You can either issue a job worker that runs continuously
|
65
|
-
for new jobs via
|
70
|
+
Note that some features like "Import" and "Export" exposed in the Web UI store
|
71
|
+
their workload as jobs. You can either issue a job worker that runs continuously
|
72
|
+
and watches for new jobs via
|
66
73
|
|
67
74
|
```
|
68
75
|
$ rake jobs:work
|
@@ -76,13 +83,14 @@ $ rake jobs:workoff
|
|
76
83
|
|
77
84
|
## Compatibility
|
78
85
|
|
79
|
-
iQvoc is fully compatible with Ruby 1.9, 2.0, 2.1 and JRuby 1.7
|
86
|
+
iQvoc is fully compatible with Ruby 1.9, 2.0, 2.1 and JRuby 1.7
|
87
|
+
(in Ruby 1.9-mode).
|
80
88
|
|
81
89
|
## Customization
|
82
90
|
|
83
|
-
There are many hooks providing support for your own classes and configuration.
|
84
|
-
also works as a Rails Engine. The config residing in `lib/iqvoc.rb`
|
85
|
-
overview of the possibilities.
|
91
|
+
There are many hooks providing support for your own classes and configuration.
|
92
|
+
The core app also works as a Rails Engine. The config residing in `lib/iqvoc.rb`
|
93
|
+
provides a basic overview of the possibilities.
|
86
94
|
|
87
95
|
## Documentation
|
88
96
|
|
@@ -100,13 +108,19 @@ For more information on SemVer, visit http://semver.org/.
|
|
100
108
|
|
101
109
|
If you want to help out there are several options:
|
102
110
|
|
103
|
-
|
104
|
-
|
105
|
-
|
111
|
+
- Found a bug? Just create an issue on the
|
112
|
+
[GitHub Issue tracker](https://github.com/innoq/iqvoc/issues) and/or submit a
|
113
|
+
patch by initiating a pull request
|
114
|
+
- You're welcome to fix bugs listed under
|
115
|
+
[Issues](https://github.com/innoq/iqvoc/issues)
|
116
|
+
- Proposal, discussion and implementation of new features on our mailing list
|
117
|
+
[iqvoc@lists.innoq.com] or on the issue tracker
|
106
118
|
|
107
|
-
If you make changes to existing code please make sure that the test suite stays
|
119
|
+
If you make changes to existing code please make sure that the test suite stays
|
120
|
+
green. Please include tests to your additional contributions.
|
108
121
|
|
109
|
-
Tests can be run via `bundle exec rake test`. We're using
|
122
|
+
Tests can be run via `bundle exec rake test`. We're using Poltergeist for
|
123
|
+
integration tests with JavaScript support.
|
110
124
|
|
111
125
|
## Maintainer & Contributors
|
112
126
|
|
@@ -115,9 +129,10 @@ iQvoc was originally created and is being maintained by [innoQ Deutschland GmbH]
|
|
115
129
|
* Robert Glaser ([mrreynolds](http://github.com/mrreynolds))
|
116
130
|
* Till Schulte-Coerne ([tillsc](http://github.com/tillsc))
|
117
131
|
* Frederik Dohr ([FND](http://github.com/FND))
|
132
|
+
* Marc Jansing ([mjansing](http://github.com/mjansing))
|
118
133
|
|
119
134
|
## License
|
120
135
|
|
121
|
-
Copyright 2014 innoQ Deutschland GmbH
|
136
|
+
Copyright 2014 [innoQ Deutschland GmbH](https://www.innoq.com).
|
122
137
|
|
123
|
-
Licensed under the Apache License, Version 2.0
|
138
|
+
Licensed under the Apache License, Version 2.0.
|
@@ -14,12 +14,29 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
-
require
|
18
|
-
require 'rails/performance_test_help'
|
17
|
+
require 'csv'
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
class InlineDataHelper
|
20
|
+
JOINER = ', '
|
21
|
+
SPLITTER = /[,\n] */
|
22
|
+
|
23
|
+
CSV_OPTIONS = {
|
24
|
+
col_sep: ', ',
|
25
|
+
quote_char: '"'
|
26
|
+
}
|
27
|
+
|
28
|
+
def self.parse_inline_values(inline_values)
|
29
|
+
options = CSV_OPTIONS.clone
|
30
|
+
options[:col_sep] = options[:col_sep].strip
|
31
|
+
begin
|
32
|
+
values = inline_values.parse_csv(options)
|
33
|
+
rescue CSV::MalformedCSVError => exc
|
34
|
+
values = inline_values.parse_csv(CSV_OPTIONS)
|
35
|
+
end
|
36
|
+
values ? values.map(&:strip) : []
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.generate_inline_values(values)
|
40
|
+
values.to_csv(CSV_OPTIONS).strip
|
24
41
|
end
|
25
42
|
end
|
data/app/aides/maker.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
module Maker
|
2
|
+
# labels:
|
3
|
+
# -
|
4
|
+
# value: <string>
|
5
|
+
# <attribute>: <value>
|
6
|
+
# inflectionals: [<string>, ...]
|
7
|
+
# components: [<label>, ...]
|
8
|
+
#
|
9
|
+
# concepts:
|
10
|
+
# -
|
11
|
+
# <attribute>: <value>
|
12
|
+
# pref_labels: [<label>, ...]
|
13
|
+
# alt_labels: [<label>, ...]
|
14
|
+
# broader: <concept>
|
15
|
+
# narrower: <concept>
|
16
|
+
# related: [<concept>, ...]
|
17
|
+
#
|
18
|
+
# NB:
|
19
|
+
# * <label> and <concept> can either be strings referencing a previously
|
20
|
+
# declared entity or objects representing a new entity
|
21
|
+
# XXX: The latter is not currently supported yet!
|
22
|
+
# * order of concepts matters when referencing relations
|
23
|
+
# * order of labels matters when referencing components
|
24
|
+
def self.from_yaml(yml)
|
25
|
+
data = YAML.load(yml)
|
26
|
+
|
27
|
+
labels = {}
|
28
|
+
data['labels'].each { |label| # XXX: use omap to simplify format (making `value` the key instead of an attribute)?
|
29
|
+
term = label.delete('value')
|
30
|
+
|
31
|
+
components = label.delete('components').map { |term|
|
32
|
+
labels[term]
|
33
|
+
} if label['components']
|
34
|
+
|
35
|
+
options = {
|
36
|
+
inflectionals: label.delete('inflectionals'),
|
37
|
+
components: components,
|
38
|
+
label_attributes: label
|
39
|
+
}
|
40
|
+
|
41
|
+
labels[term] = self.label(term, options)
|
42
|
+
} if data['labels']
|
43
|
+
|
44
|
+
concepts = {}
|
45
|
+
data['concepts'].each { |concept| # XXX: use omap to simplify format (using a single pref_label as key)?
|
46
|
+
relations = {}
|
47
|
+
['broader', 'narrower'].each { |type| # TODO: missing related, support for poly-hierarchies
|
48
|
+
relations[type] = concepts[concept.delete(type)] if concept[type]
|
49
|
+
}
|
50
|
+
|
51
|
+
lbls = {} # TODO: rename
|
52
|
+
['pref', 'alt'].each { |type| # TODO: missing hidden
|
53
|
+
key = "#{type}_labels"
|
54
|
+
lbls[type] = concept.delete(key).map { |term|
|
55
|
+
labels[term]
|
56
|
+
} if concept[key]
|
57
|
+
}
|
58
|
+
|
59
|
+
options = {
|
60
|
+
pref_labels: lbls['pref'],
|
61
|
+
alt_labels: lbls['alt'],
|
62
|
+
concept_attributes: concept
|
63
|
+
}
|
64
|
+
|
65
|
+
identifier = options[:pref_labels].first.value
|
66
|
+
concepts[identifier] = self.concept(options)
|
67
|
+
concepts[identifier].send(Iqvoc::Concept.broader_relation_class.name.to_relation_name).
|
68
|
+
create_with_reverse_relation(relations['broader']) if relations['broader']
|
69
|
+
concepts[identifier].send(Iqvoc::Concept.broader_relation_class.narrower_class.name.to_relation_name).
|
70
|
+
create_with_reverse_relation(relations['narrower']) if relations['narrower']
|
71
|
+
} if data['concepts']
|
72
|
+
|
73
|
+
return { concepts: concepts, labels: labels }
|
74
|
+
end
|
75
|
+
|
76
|
+
# optional arguments:
|
77
|
+
# concept_attributes for custom concept attributes
|
78
|
+
# pref_labels is an array of strings or label instances to be used as prefLabels
|
79
|
+
# alt_labels is an array of strings or label instances to be used as altLabels
|
80
|
+
def self.concept(options={})
|
81
|
+
attributes = options[:concept_attributes] || {}
|
82
|
+
pref_labels = options[:pref_labels] || []
|
83
|
+
alt_labels = options[:alt_labels] || []
|
84
|
+
|
85
|
+
defaults = { # NB: must use strings, not symbols as keys due to YAML
|
86
|
+
'published_at' => 3.days.ago
|
87
|
+
}
|
88
|
+
attributes = defaults.merge(attributes)
|
89
|
+
|
90
|
+
concept = Iqvoc::Concept.base_class.create!(attributes)
|
91
|
+
|
92
|
+
pref_labels.each { |term|
|
93
|
+
label = term.is_a?(String) ? self.label(term) : term
|
94
|
+
Iqvoc::Concept.pref_labeling_class.
|
95
|
+
create!(owner: concept, target: label)
|
96
|
+
}
|
97
|
+
alt_labels.each { |term|
|
98
|
+
label = term.is_a?(String) ? self.label(term) : term
|
99
|
+
Iqvoc::Concept.further_labeling_classes.first.first.
|
100
|
+
create!(owner: concept, target: label)
|
101
|
+
}
|
102
|
+
|
103
|
+
return concept
|
104
|
+
end
|
105
|
+
|
106
|
+
# optional arguments:
|
107
|
+
# label_attributes for custom label attributes
|
108
|
+
# inflectionals is an array of strings to be used as inflectionals
|
109
|
+
# components is an array of labels to be used as compound form contents
|
110
|
+
def self.label(value, options={}) # FIXME: move into SKOS-XL extension
|
111
|
+
attributes = options[:label_attributes] || {}
|
112
|
+
inflectionals = options[:inflectionals] || []
|
113
|
+
components = options[:components] || []
|
114
|
+
|
115
|
+
defaults = { # NB: must use strings, not symbols as keys due to YAML
|
116
|
+
value: value, # intentionally not a string; symbol takes precedence
|
117
|
+
'origin' => Origin.new(value).to_s,
|
118
|
+
'language' => Iqvoc::Concept.pref_labeling_languages.first,
|
119
|
+
'published_at' => 2.days.ago
|
120
|
+
}
|
121
|
+
attributes = defaults.merge(attributes)
|
122
|
+
|
123
|
+
klass = Iqvoc::XLLabel rescue Iqvoc::Label # FIXME: breaks encapsulation (hard-coded iqvoc_skosxl dependency)
|
124
|
+
label = klass.base_class.create!(attributes)
|
125
|
+
|
126
|
+
inflectionals.each { |inf|
|
127
|
+
label.inflectionals.create!(value: inf)
|
128
|
+
}
|
129
|
+
|
130
|
+
if components.length > 0
|
131
|
+
compound_form_contents = components.each_with_index.map { |label, i|
|
132
|
+
CompoundForm::Content::Base.new(label: label, order: i)
|
133
|
+
}
|
134
|
+
label.compound_forms.create!(compound_form_contents: compound_form_contents)
|
135
|
+
end
|
136
|
+
|
137
|
+
return label
|
138
|
+
end
|
139
|
+
end
|
File without changes
|
data/app/aides/origin.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011-2013 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
class Origin
|
18
|
+
attr_accessor :initial_value, :value
|
19
|
+
|
20
|
+
def initialize(value = nil)
|
21
|
+
self.initial_value = value
|
22
|
+
self.value = "_#{SecureRandom.hex(4)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
valid = true
|
27
|
+
|
28
|
+
if blank_node = initial_value.match(RDFAPI::BLANK_NODE_REGEXP)
|
29
|
+
# blank node validation, should not contain special chars
|
30
|
+
valid = false if CGI.escape(blank_node[1]) != blank_node[1]
|
31
|
+
else
|
32
|
+
# regular subject validation
|
33
|
+
|
34
|
+
# should not start with a number
|
35
|
+
valid = false if initial_value.match(/^\d.*/)
|
36
|
+
|
37
|
+
# should not contain special chars
|
38
|
+
valid = false if CGI.escape(initial_value) != initial_value
|
39
|
+
end
|
40
|
+
|
41
|
+
valid
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
data/app/aides/rdfapi.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2011-2013 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
class RDFAPI
|
18
|
+
FIRST_LEVEL_OBJECT_CLASSES = [Iqvoc::Concept.base_class, Iqvoc::Collection.base_class]
|
19
|
+
SECOND_LEVEL_OBJECT_CLASSES = Iqvoc::Concept.labeling_classes.keys +
|
20
|
+
Iqvoc::Concept.note_classes +
|
21
|
+
Iqvoc::Concept.relation_classes +
|
22
|
+
Iqvoc::Concept.match_classes +
|
23
|
+
Iqvoc::Concept.notation_classes +
|
24
|
+
Iqvoc::Concept.additional_association_classes.keys +
|
25
|
+
[Iqvoc::Collection.member_class]
|
26
|
+
|
27
|
+
OBJECT_DICTIONARY = FIRST_LEVEL_OBJECT_CLASSES.inject({}) do |hash, klass|
|
28
|
+
hash["#{klass.rdf_namespace}:#{klass.rdf_class}"] = klass
|
29
|
+
hash
|
30
|
+
end
|
31
|
+
|
32
|
+
PREDICATE_DICTIONARY = SECOND_LEVEL_OBJECT_CLASSES.inject({}) do |hash, klass|
|
33
|
+
hash["#{klass.rdf_namespace}:#{klass.rdf_predicate}"] = klass
|
34
|
+
hash
|
35
|
+
end
|
36
|
+
|
37
|
+
URI_REGEXP = /^https?:\/\/[^\s]+$/
|
38
|
+
LITERAL_REGEXP = /^"(.+)"(@(.+))?$/
|
39
|
+
BLANK_NODE_REGEXP = /^_:(.+)/
|
40
|
+
|
41
|
+
def self.devour(rdf_subject, rdf_predicate, rdf_object)
|
42
|
+
case rdf_predicate
|
43
|
+
when 'a', 'rdf:type'
|
44
|
+
case rdf_object
|
45
|
+
when String
|
46
|
+
target = OBJECT_DICTIONARY[rdf_object] || rdf_object.constantize
|
47
|
+
else
|
48
|
+
target = rdf_object
|
49
|
+
end
|
50
|
+
target.find_or_initialize_by(origin: rdf_subject)
|
51
|
+
when String
|
52
|
+
# dictionary lookup
|
53
|
+
target = PREDICATE_DICTIONARY[rdf_predicate] || rdf_predicate.constantize
|
54
|
+
target.build_from_rdf(rdf_subject, target, rdf_object)
|
55
|
+
else # is a class
|
56
|
+
rdf_predicate.build_from_rdf(rdf_subject, rdf_predicate, rdf_object)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'iq_rdf'
|
2
|
+
require 'uri'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class SkosExporter
|
6
|
+
include RdfHelper # necessary to use render_concept helper
|
7
|
+
include RdfNamespacesHelper
|
8
|
+
include Rails.application.routes.url_helpers
|
9
|
+
|
10
|
+
def initialize(file_path, type, default_namespace_url, logger = Rails.logger)
|
11
|
+
default_url_options[:port] = URI.parse(default_namespace_url).port
|
12
|
+
default_url_options[:host] = URI.parse(default_namespace_url).to_s.gsub(/\/$/, '')
|
13
|
+
|
14
|
+
@file_path = file_path
|
15
|
+
@type = type
|
16
|
+
@logger = logger
|
17
|
+
@document = IqRdf::Document.new
|
18
|
+
|
19
|
+
unless ['ttl', 'nt', 'xml'].include? @type
|
20
|
+
raise "SkosExporter: Unknown rdf serialization. Parameter 'type' should be 'ttl' (Turtle), 'nt' (N-Triples) or 'xml' (RDF-XML)."
|
21
|
+
end
|
22
|
+
|
23
|
+
unless @file_path.is_a?(String)
|
24
|
+
raise "SkosExporter#export: Parameter 'file' should be a String."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
export
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def export
|
35
|
+
ActiveSupport.run_load_hooks(:rdf_export_before, self)
|
36
|
+
|
37
|
+
start = Time.now
|
38
|
+
@logger.info 'Starting export...'
|
39
|
+
@logger.info "file_path = #{@file_path}"
|
40
|
+
@logger.info "type = #{@type}"
|
41
|
+
|
42
|
+
# add export data
|
43
|
+
add_namespaces(@document)
|
44
|
+
add_collections(@document)
|
45
|
+
add_concepts(@document)
|
46
|
+
|
47
|
+
ActiveSupport.run_load_hooks(:rdf_export_before_save, self)
|
48
|
+
|
49
|
+
# saving export to disk
|
50
|
+
save_file(@file_path, @type, @document)
|
51
|
+
|
52
|
+
done = Time.now
|
53
|
+
@logger.info "Export Job finished in #{(done - start).to_i} seconds."
|
54
|
+
|
55
|
+
ActiveSupport.run_load_hooks(:rdf_export_after, self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_namespaces(document)
|
59
|
+
@logger.info 'Exporting namespaces...'
|
60
|
+
|
61
|
+
RdfNamespacesHelper.instance_methods.each do |meth|
|
62
|
+
namespaces = send(meth)
|
63
|
+
document.namespaces(namespaces) if namespaces.is_a?(Hash)
|
64
|
+
end
|
65
|
+
|
66
|
+
@logger.info 'Finished exporting namespaces.'
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_collections(document)
|
70
|
+
@logger.info 'Exporting collections...'
|
71
|
+
|
72
|
+
offset = 0
|
73
|
+
while true
|
74
|
+
collections = Iqvoc::Collection.base_class.published.order('id').limit(100).offset(offset)
|
75
|
+
limit = collections.size < 100 ? collections.size : 100
|
76
|
+
break if collections.size == 0
|
77
|
+
|
78
|
+
# Todo: Preloading???
|
79
|
+
collections.each do |collection|
|
80
|
+
render_collection(document, collection)
|
81
|
+
end
|
82
|
+
|
83
|
+
@logger.info "Collections #{offset+1}-#{offset+limit} exported."
|
84
|
+
offset += collections.size # Size is important!
|
85
|
+
end
|
86
|
+
|
87
|
+
@logger.info "Finished exporting collections (#{offset} collections exported)."
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_concepts(document)
|
91
|
+
@logger.info 'Exporting concepts...'
|
92
|
+
|
93
|
+
offset = 0
|
94
|
+
while true
|
95
|
+
concepts = Iqvoc::Concept.base_class.published.order('id').limit(100).offset(offset)
|
96
|
+
limit = concepts.size < 100 ? concepts.size : 100
|
97
|
+
break if concepts.size == 0
|
98
|
+
|
99
|
+
# When in single query mode, AR handles ALL includes to be loaded by that
|
100
|
+
# one query. We don't want that! So let's do it manually :-)
|
101
|
+
ActiveRecord::Associations::Preloader.new.preload(concepts,
|
102
|
+
Iqvoc::Concept.base_class.default_includes + [
|
103
|
+
:matches,
|
104
|
+
:collection_members,
|
105
|
+
:notations,
|
106
|
+
{ relations: :target, labelings: :target, notes: :annotations }
|
107
|
+
])
|
108
|
+
|
109
|
+
concepts.each do |concept|
|
110
|
+
render_concept(document, concept, true)
|
111
|
+
end
|
112
|
+
|
113
|
+
@logger.info "Concepts #{offset+1}-#{offset+limit} exported."
|
114
|
+
offset += concepts.size # Size is important!
|
115
|
+
end
|
116
|
+
|
117
|
+
@logger.info "Finished exporting concepts (#{offset} concepts exported)."
|
118
|
+
end
|
119
|
+
|
120
|
+
def save_file(file_path, type, content)
|
121
|
+
begin
|
122
|
+
@logger.info "Saving export to '#{@file_path}'"
|
123
|
+
create_directory(@file_path)
|
124
|
+
file = File.open(@file_path, 'w')
|
125
|
+
content = serialize_rdf(content, type)
|
126
|
+
file.write(content)
|
127
|
+
rescue IOError => e
|
128
|
+
# some error occur
|
129
|
+
# e.g not writable
|
130
|
+
ensure
|
131
|
+
file.close unless file == nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def create_directory(file_path)
|
136
|
+
dirname = File.dirname(file_path)
|
137
|
+
unless File.directory?(dirname)
|
138
|
+
FileUtils.mkdir_p(dirname)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def serialize_rdf(document, type)
|
143
|
+
if type == 'xml'
|
144
|
+
document.to_xml
|
145
|
+
elsif type == 'ttl'
|
146
|
+
document.to_turtle
|
147
|
+
else
|
148
|
+
document.to_ntriples
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|