iqvoc 4.0.8 → 4.0.9

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.
Files changed (209) hide show
  1. data/CHANGELOG.md +11 -0
  2. data/Gemfile +5 -1
  3. data/Gemfile.lock +35 -18
  4. data/README.md +3 -2
  5. data/app/assets/stylesheets/iqvoc/components.css.scss +0 -6
  6. data/app/controllers/collections_controller.rb +30 -17
  7. data/app/controllers/concepts/alphabetical_controller.rb +15 -9
  8. data/app/{models/collection/member/concept.rb → controllers/concepts/expired_controller.rb} +13 -15
  9. data/app/controllers/concepts/hierarchical_controller.rb +3 -0
  10. data/app/controllers/concepts_controller.rb +2 -3
  11. data/app/controllers/instance_configuration_controller.rb +5 -6
  12. data/app/controllers/search_results_controller.rb +1 -2
  13. data/app/helpers/application_helper.rb +1 -5
  14. data/app/helpers/concepts_helper.rb +1 -1
  15. data/app/helpers/rdf_helper.rb +1 -1
  16. data/app/helpers/widget_helper.rb +3 -3
  17. data/app/models/collection/base.rb +64 -46
  18. data/app/models/collection/member/base.rb +12 -0
  19. data/app/models/collection/member/skos/base.rb +13 -12
  20. data/app/models/collection/skos/base.rb +1 -1
  21. data/app/models/collection/{member/collection.rb → skos/unordered.rb} +1 -16
  22. data/app/models/collection/unordered.rb +7 -0
  23. data/app/models/concept/base.rb +26 -107
  24. data/app/models/concept/relation/base.rb +4 -0
  25. data/app/models/concept/relation/skos/base.rb +17 -7
  26. data/app/models/concept/skos/base.rb +10 -1
  27. data/app/models/concept/skos/scheme.rb +1 -1
  28. data/app/models/concept/validations.rb +117 -0
  29. data/app/models/label/base.rb +4 -3
  30. data/app/models/labeling/skos/base.rb +24 -13
  31. data/app/models/match/skos/base.rb +24 -5
  32. data/app/models/note/skos/base.rb +17 -14
  33. data/app/views/collections/_form.html.erb +2 -2
  34. data/app/views/collections/_header.html.erb +2 -2
  35. data/app/views/collections/_sidebar.html.erb +3 -3
  36. data/app/views/collections/index.html.erb +1 -1
  37. data/app/views/collections/show.html.erb +1 -1
  38. data/app/views/collections/show.iqrdf +1 -1
  39. data/app/views/concepts/alphabetical/index.html.erb +1 -1
  40. data/app/views/concepts/expired/index.html.erb +16 -0
  41. data/app/views/concepts/notifications/_referenced_concepts.html.erb +1 -1
  42. data/app/views/concepts/sidebars/_plural.html.erb +4 -0
  43. data/app/views/dashboard/index.html.erb +1 -1
  44. data/app/views/layouts/_controls.html.erb +25 -24
  45. data/app/views/layouts/_navigation.html.erb +1 -1
  46. data/app/views/layouts/application.html.erb +0 -6
  47. data/app/views/partials/concept/_edit_link_base.html.erb +2 -1
  48. data/app/views/partials/labeling/skos/_base.html.erb +1 -2
  49. data/app/views/partials/note/_edit_base.html.erb +1 -1
  50. data/app/views/partials/note/skos/_edit_change_note.html.erb +1 -1
  51. data/app/views/rdf/scheme.iqrdf +1 -1
  52. data/app/views/search_results/_header.html.erb +2 -2
  53. data/app/views/search_results/_sidebar.html.erb +2 -2
  54. data/app/views/search_results/index.html.erb +1 -1
  55. data/app/views/triplestore_sync/index.html.erb +2 -2
  56. data/config/locales/de.yml +9 -2
  57. data/config/locales/en.yml +11 -4
  58. data/config/locales/pt.yml +8 -3
  59. data/config/routes.rb +15 -6
  60. data/db/migrate/20130227145825_fix_collection_type.rb +10 -0
  61. data/db/schema.rb +1 -1
  62. data/lib/generators/app/template.rb +20 -0
  63. data/lib/iqvoc/configuration/collection.rb +5 -5
  64. data/lib/iqvoc/configuration/concept.rb +7 -7
  65. data/lib/iqvoc/configuration/core.rb +11 -10
  66. data/lib/iqvoc/controller_extensions.rb +2 -5
  67. data/lib/iqvoc/inline_data_helper.rb +25 -3
  68. data/lib/iqvoc/instance_configuration.rb +1 -1
  69. data/lib/iqvoc/rdfapi.rb +56 -0
  70. data/lib/iqvoc/skos_importer.rb +88 -40
  71. data/lib/iqvoc/version.rb +1 -1
  72. data/lib/multi_logger.rb +38 -0
  73. data/lib/tasks/importer.rake +6 -1
  74. data/test/integration/browse_concepts_and_labels_test.rb +16 -0
  75. data/test/integration/instance_configuration_test.rb +5 -5
  76. data/test/integration/search_test.rb +18 -17
  77. data/test/unit/concept_test.rb +53 -4
  78. data/test/unit/inline_data_test.rb +61 -0
  79. data/test/unit/origin_test.rb +1 -1
  80. data/test/unit/rdfapi_test.rb +83 -0
  81. data/test/unit/skos_import_test.rb +1 -1
  82. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  83. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_flat_55_fbec88_40x100.png +0 -0
  84. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  85. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  86. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  87. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  88. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  89. data/{public/assets/jquery-ui → vendor/assets/images}/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  90. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_217bc0_256x240.png +0 -0
  91. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_2e83ff_256x240.png +0 -0
  92. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_469bdd_256x240.png +0 -0
  93. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_6da8d5_256x240.png +0 -0
  94. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_cd0a0a_256x240.png +0 -0
  95. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_d8e7f3_256x240.png +0 -0
  96. data/{public/assets/jquery-ui → vendor/assets/images}/ui-icons_f9bd01_256x240.png +0 -0
  97. data/vendor/assets/stylesheets/{jquery-ui-1.8.23.custom.css → jquery-ui-1.8.23.custom.css.scss} +17 -17
  98. metadata +29 -131
  99. data/app/controllers/collections/hierarchical_controller.rb +0 -44
  100. data/public/assets/ajax-loader-dada1fbb71d7fb80d8dde7a031ae6cd0.gif +0 -0
  101. data/public/assets/ajax-loader.gif +0 -0
  102. data/public/assets/arrow_down-07c5dea12928d65fcc31c08279366170.gif +0 -0
  103. data/public/assets/arrow_down.gif +0 -0
  104. data/public/assets/arrow_up-51ede92c113b9a870e7a306907360ba8.gif +0 -0
  105. data/public/assets/arrow_up.gif +0 -0
  106. data/public/assets/bootstrap/bootstrap-2012a68e8267282c0a2c177d7005ae6d.css +0 -9
  107. data/public/assets/bootstrap/bootstrap-2012a68e8267282c0a2c177d7005ae6d.css.gz +0 -0
  108. data/public/assets/bootstrap/bootstrap-35fd77cdf54a8cb63a39d8d0cea7a746.js +0 -19
  109. data/public/assets/bootstrap/bootstrap-35fd77cdf54a8cb63a39d8d0cea7a746.js.gz +0 -0
  110. data/public/assets/bootstrap/bootstrap-responsive-609b376f4bb032588c054b563d4bca1f.css +0 -9
  111. data/public/assets/bootstrap/bootstrap-responsive-609b376f4bb032588c054b563d4bca1f.css.gz +0 -0
  112. data/public/assets/bootstrap/bootstrap-responsive.css +0 -9
  113. data/public/assets/bootstrap/bootstrap-responsive.css.gz +0 -0
  114. data/public/assets/bootstrap/bootstrap.css +0 -9
  115. data/public/assets/bootstrap/bootstrap.css.gz +0 -0
  116. data/public/assets/bootstrap/bootstrap.js +0 -19
  117. data/public/assets/bootstrap/bootstrap.js.gz +0 -0
  118. data/public/assets/bootstrap/glyphicons-halflings-0e7ff93d8f48fe1f5f762e4312e78da7.png +0 -0
  119. data/public/assets/bootstrap/glyphicons-halflings-white-ef3d5b8d6a297804352b4a46fdb5466d.png +0 -0
  120. data/public/assets/bootstrap/glyphicons-halflings-white.png +0 -0
  121. data/public/assets/bootstrap/glyphicons-halflings.png +0 -0
  122. data/public/assets/file-58ce0b58f19dfe871b25270e98cff66c.gif +0 -0
  123. data/public/assets/file.gif +0 -0
  124. data/public/assets/folder-5c5b1fd2f1ece710f9159f05cc2aaaa8.gif +0 -0
  125. data/public/assets/folder-closed-8b61fc55011d534f0216c1150994f229.gif +0 -0
  126. data/public/assets/folder-closed.gif +0 -0
  127. data/public/assets/folder.gif +0 -0
  128. data/public/assets/html5-05e1be9157c8366db72fb7a1bcc8daff.js +0 -3
  129. data/public/assets/html5-05e1be9157c8366db72fb7a1bcc8daff.js.gz +0 -0
  130. data/public/assets/html5.js +0 -3
  131. data/public/assets/html5.js.gz +0 -0
  132. data/public/assets/iqvoc/ie_fixes-6a106c21d979ccdd1f6bf1cb129a0e69.css +0 -1
  133. data/public/assets/iqvoc/ie_fixes-6a106c21d979ccdd1f6bf1cb129a0e69.css.gz +0 -0
  134. data/public/assets/iqvoc/ie_fixes.css +0 -1
  135. data/public/assets/iqvoc/ie_fixes.css.gz +0 -0
  136. data/public/assets/iqvoc_logo-316859a511c646918fe75e92fa0bf6ac.png +0 -0
  137. data/public/assets/iqvoc_logo.png +0 -0
  138. data/public/assets/jquery-ui/ui-bg_flat_0_aaaaaa_40x100-a1eb3e0764573ed4b261ca742ed96ac3.png +0 -0
  139. data/public/assets/jquery-ui/ui-bg_flat_55_fbec88_40x100-439ee8e6c8ce855f43ed4d7b90684720.png +0 -0
  140. data/public/assets/jquery-ui/ui-bg_glass_75_d0e5f5_1x400-9357836db77954d0d8c5feb259635223.png +0 -0
  141. data/public/assets/jquery-ui/ui-bg_glass_85_dfeffc_1x400-7beb11d1590fb2fb1ef5c754100a078a.png +0 -0
  142. data/public/assets/jquery-ui/ui-bg_glass_95_fef1ec_1x400-c723e9a2b50006c6054836a10b76bb84.png +0 -0
  143. data/public/assets/jquery-ui/ui-bg_gloss-wave_55_5c9ccc_500x100-cb26f48ac4912c23ce301c69c474d306.png +0 -0
  144. data/public/assets/jquery-ui/ui-bg_inset-hard_100_f5f8f9_1x100-519299e2aa31bffdd4fed34b4fac603d.png +0 -0
  145. data/public/assets/jquery-ui/ui-bg_inset-hard_100_fcfdfd_1x100-27e08524d3d56bb59e354435b663c3ac.png +0 -0
  146. data/public/assets/jquery-ui/ui-icons_217bc0_256x240-ed26778199d8722ae064038a1e841bc8.png +0 -0
  147. data/public/assets/jquery-ui/ui-icons_2e83ff_256x240-94086d0ce953eb0887ab1b8140903af9.png +0 -0
  148. data/public/assets/jquery-ui/ui-icons_469bdd_256x240-1c055f2d65517ef8faf1d1999c8ac2fa.png +0 -0
  149. data/public/assets/jquery-ui/ui-icons_6da8d5_256x240-8f5b417c5604f1b7b5965c98754c33b9.png +0 -0
  150. data/public/assets/jquery-ui/ui-icons_cd0a0a_256x240-8b44e266bdc1f57f1393579591f89222.png +0 -0
  151. data/public/assets/jquery-ui/ui-icons_d8e7f3_256x240-1b415e411069df902e6f417cc014172c.png +0 -0
  152. data/public/assets/jquery-ui/ui-icons_f9bd01_256x240-4553d0576c0498ca4c776aa77aa80833.png +0 -0
  153. data/public/assets/json2-b08b7b9af3e715f42c088ac728f62a2b.js +0 -157
  154. data/public/assets/json2-b08b7b9af3e715f42c088ac728f62a2b.js.gz +0 -0
  155. data/public/assets/json2.js +0 -157
  156. data/public/assets/json2.js.gz +0 -0
  157. data/public/assets/manifest-3ce8edde69d5d291841ef7c49daaa9cb.js +0 -21
  158. data/public/assets/manifest-3ce8edde69d5d291841ef7c49daaa9cb.js.gz +0 -0
  159. data/public/assets/manifest-c0eca563fd9f16907d302af9a2dcae6b.css +0 -51
  160. data/public/assets/manifest-c0eca563fd9f16907d302af9a2dcae6b.css.gz +0 -0
  161. data/public/assets/manifest.css +0 -51
  162. data/public/assets/manifest.css.gz +0 -0
  163. data/public/assets/manifest.js +0 -21
  164. data/public/assets/manifest.js.gz +0 -0
  165. data/public/assets/manifest.yml +0 -47
  166. data/public/assets/minus-1d0fae0720bab52bd98598218e8345c0.gif +0 -0
  167. data/public/assets/minus.gif +0 -0
  168. data/public/assets/plus-1a29e46532f839ed9c977ef4613d079f.gif +0 -0
  169. data/public/assets/plus.gif +0 -0
  170. data/public/assets/spinner_16x16-17dcf941457ae4e702f4600e714b21c1.gif +0 -0
  171. data/public/assets/spinner_16x16.gif +0 -0
  172. data/public/assets/spinner_24x24-78c707c1921b93962ea5edd7f4a74134.gif +0 -0
  173. data/public/assets/spinner_24x24.gif +0 -0
  174. data/public/assets/treeview-black-c46d96020e5d79c652af0fb0c6656384.gif +0 -0
  175. data/public/assets/treeview-black-line-0903c58efd34c3203d132e6f873049f6.gif +0 -0
  176. data/public/assets/treeview-black-line.gif +0 -0
  177. data/public/assets/treeview-black.gif +0 -0
  178. data/public/assets/treeview-default-ee6298d311205d21dc57c01c0696df70.gif +0 -0
  179. data/public/assets/treeview-default-line-2c4106cfd4d322c6e6565f80702c27cb.gif +0 -0
  180. data/public/assets/treeview-default-line.gif +0 -0
  181. data/public/assets/treeview-default.gif +0 -0
  182. data/public/assets/treeview-famfamfam-20dfef13cd5fd1e08fb61ee68eb9d3fa.gif +0 -0
  183. data/public/assets/treeview-famfamfam-line-a02cce2b80977e066df83137aae71fa8.gif +0 -0
  184. data/public/assets/treeview-famfamfam-line.gif +0 -0
  185. data/public/assets/treeview-famfamfam.gif +0 -0
  186. data/public/assets/treeview-gray-01bfdcd116e081e23daf13024fd3e4e0.gif +0 -0
  187. data/public/assets/treeview-gray-line-029b3a7b92ed10ea6c4b6289cb9fa0e3.gif +0 -0
  188. data/public/assets/treeview-gray-line.gif +0 -0
  189. data/public/assets/treeview-gray.gif +0 -0
  190. data/public/assets/treeview-red-2a5abbce598d05c864d845998381eef6.gif +0 -0
  191. data/public/assets/treeview-red-line-f51e6c30aa3b0ff856900ba85d769f1c.gif +0 -0
  192. data/public/assets/treeview-red-line.gif +0 -0
  193. data/public/assets/treeview-red.gif +0 -0
  194. data/vendor/assets/images/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  195. data/vendor/assets/images/jquery-ui/ui-bg_flat_55_fbec88_40x100.png +0 -0
  196. data/vendor/assets/images/jquery-ui/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  197. data/vendor/assets/images/jquery-ui/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  198. data/vendor/assets/images/jquery-ui/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  199. data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  200. data/vendor/assets/images/jquery-ui/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  201. data/vendor/assets/images/jquery-ui/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  202. data/vendor/assets/images/jquery-ui/ui-icons_217bc0_256x240.png +0 -0
  203. data/vendor/assets/images/jquery-ui/ui-icons_2e83ff_256x240.png +0 -0
  204. data/vendor/assets/images/jquery-ui/ui-icons_469bdd_256x240.png +0 -0
  205. data/vendor/assets/images/jquery-ui/ui-icons_6da8d5_256x240.png +0 -0
  206. data/vendor/assets/images/jquery-ui/ui-icons_cd0a0a_256x240.png +0 -0
  207. data/vendor/assets/images/jquery-ui/ui-icons_d8e7f3_256x240.png +0 -0
  208. data/vendor/assets/images/jquery-ui/ui-icons_f9bd01_256x240.png +0 -0
  209. data/vendor/assets/javascripts/json2.js +0 -487
@@ -9,13 +9,13 @@ module Iqvoc
9
9
  Iqvoc.first_level_class_configuration_modules << self
10
10
 
11
11
  mattr_accessor :base_class_name, :root_class_name,
12
- :broader_relation_class_name, :further_relation_class_names,
13
- :pref_labeling_class_name,
14
- :match_class_names,
15
- :note_class_names,
16
- :additional_association_class_names,
17
- :view_sections,
18
- :include_module_names
12
+ :broader_relation_class_name, :further_relation_class_names,
13
+ :pref_labeling_class_name,
14
+ :match_class_names,
15
+ :note_class_names,
16
+ :additional_association_class_names,
17
+ :view_sections,
18
+ :include_module_names
19
19
 
20
20
  self.base_class_name = 'Concept::SKOS::Base'
21
21
  self.root_class_name = 'Concept::SKOS::Scheme'
@@ -14,8 +14,11 @@ module Iqvoc
14
14
  :first_level_class_configuration_modules,
15
15
  :ability_class_name,
16
16
  :navigation_items,
17
+ :localized_routes,
17
18
  :core_assets
18
19
 
20
+ self.localized_routes = [] # routing extensibility hook
21
+
19
22
  self.navigation_items = [
20
23
  {
21
24
  :content => proc { link_to "Dashboard", dashboard_path },
@@ -58,7 +61,6 @@ module Iqvoc
58
61
  bootstrap/bootstrap.css
59
62
  bootstrap/bootstrap-responsive.css
60
63
  iqvoc/ie_fixes.css
61
- json2.js
62
64
  bootstrap/bootstrap.js
63
65
  html5.js
64
66
  )
@@ -90,9 +92,9 @@ module Iqvoc
90
92
  # initialize
91
93
  self.config.register_settings({
92
94
  "title" => "iQvoc",
93
- "available_languages" => ["en", "de"],
94
95
  "languages.pref_labeling" => ["en", "de"],
95
- "languages.further_labelings.Labeling::SKOS::AltLabel" => ["en", "de"]
96
+ "languages.further_labelings.Labeling::SKOS::AltLabel" => ["en", "de"],
97
+ "note_languages" => ["en", "de"]
96
98
  })
97
99
  self.config.initialize_cache
98
100
  end
@@ -148,8 +150,12 @@ module Iqvoc
148
150
  return config["title"]
149
151
  end
150
152
 
151
- def available_languages
152
- return config["available_languages"]
153
+ def note_languages
154
+ return config["note_languages"]
155
+ end
156
+
157
+ def all_languages
158
+ (Iqvoc::Concept.pref_labeling_languages + Iqvoc::Concept.further_labeling_class_names.values.flatten + note_languages).compact.map(&:to_s).uniq
153
159
  end
154
160
 
155
161
  # @deprecated
@@ -158,11 +164,6 @@ module Iqvoc
158
164
  self.config.register_setting("title", value)
159
165
  end
160
166
 
161
- # @deprecated
162
- def available_languages=(value)
163
- ActiveSupport::Deprecation.warn "available_languages has been moved into instance configuration", caller
164
- self.config.register_setting("available_languages", value)
165
- end
166
167
  end
167
168
 
168
169
  end
@@ -19,7 +19,6 @@ module Iqvoc
19
19
 
20
20
  def default_url_options(options = nil)
21
21
  { :format => params[:format], :lang => I18n.locale }.
22
- reject { |key, value| key == :lang and value.to_s.strip.blank? }. # Strip out the lang parameter if it's empty.
23
22
  merge(options || {})
24
23
  end
25
24
 
@@ -53,9 +52,7 @@ module Iqvoc
53
52
  end
54
53
 
55
54
  def set_locale
56
- if Iqvoc::Concept.pref_labeling_languages.include?(nil)
57
- I18n.locale = " "
58
- elsif params[:lang] && Iqvoc::Concept.pref_labeling_languages.include?(params[:lang])
55
+ if params[:lang].present? && Iqvoc::Concept.pref_labeling_languages.include?(params[:lang])
59
56
  I18n.locale = params[:lang]
60
57
  else
61
58
  I18n.locale = Iqvoc::Concept.pref_labeling_languages.first
@@ -65,7 +62,7 @@ module Iqvoc
65
62
  def concept_widget_data(concept, rank = nil)
66
63
  data = {
67
64
  :id => concept.origin,
68
- :name => concept.pref_label.value.to_s + (concept.additional_info ? " (#{concept.additional_info })" : "")
65
+ :name => (concept.pref_label && concept.pref_label.value.presence || ":#{concept.origin}") + (concept.additional_info ? " (#{concept.additional_info })" : "")
69
66
  }
70
67
  data[:rank] = rank if rank
71
68
  data
@@ -14,12 +14,34 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ require 'csv'
18
+
17
19
  module Iqvoc
18
20
  module InlineDataHelper
19
21
 
20
- # delimiters for strings representing a list of values
21
- Joiner = ", "
22
- Splitter = /[,\n] */
22
+ # delimiters for strings representing a list of values - XXX: lacks encapsulation
23
+ JOINER = ", "
24
+ SPLITTER = /[,\n] */
25
+
26
+ CSV_OPTIONS = {
27
+ :col_sep => ", ",
28
+ :quote_char => '"'
29
+ }
30
+
31
+ def self.parse_inline_values(inline_values)
32
+ options = CSV_OPTIONS.clone
33
+ options[:col_sep] = options[:col_sep].strip
34
+ begin
35
+ values = inline_values.parse_csv(options)
36
+ rescue CSV::MalformedCSVError => exc
37
+ values = inline_values.parse_csv(CSV_OPTIONS)
38
+ end
39
+ values ? values.map(&:strip) : []
40
+ end
41
+
42
+ def self.generate_inline_values(values)
43
+ values.to_csv(CSV_OPTIONS).strip
44
+ end
23
45
 
24
46
  end
25
47
  end
@@ -69,7 +69,7 @@ module Iqvoc
69
69
 
70
70
  # retrieve individual setting, using default value as fallback
71
71
  def [](key)
72
- initialize_cache # relying on ActiveRecord query cache -- XXX: inefficient (caching doesn't include processing/indexing)
72
+ initialize_cache if @settings.blank?
73
73
  return @settings[key]
74
74
  end
75
75
 
@@ -0,0 +1,56 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright 2011 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
+ module Iqvoc
18
+ module RDFAPI
19
+ FIRST_LEVEL_OBJECT_CLASSES = [Iqvoc::Concept.base_class, Iqvoc::Collection.base_class]
20
+ SECOND_LEVEL_OBJECT_CLASSES = Iqvoc::Concept.labeling_classes.keys +
21
+ Iqvoc::Concept.note_classes +
22
+ Iqvoc::Concept.relation_classes +
23
+ Iqvoc::Concept.match_classes +
24
+ [Iqvoc::Collection.member_class]
25
+
26
+ OBJECT_DICTIONARY = FIRST_LEVEL_OBJECT_CLASSES.inject({}) do |hash, klass|
27
+ hash["#{klass.rdf_namespace}:#{klass.rdf_class}"] = klass
28
+ hash
29
+ end
30
+
31
+ PREDICATE_DICTIONARY = SECOND_LEVEL_OBJECT_CLASSES.inject({}) do |hash, klass|
32
+ hash["#{klass.rdf_namespace}:#{klass.rdf_predicate}"] = klass
33
+ hash
34
+ end
35
+
36
+ def self.devour(rdf_subject, rdf_predicate, rdf_object)
37
+ case rdf_predicate
38
+ when 'a', 'rdf:type'
39
+ case rdf_object
40
+ when String
41
+ target = OBJECT_DICTIONARY[rdf_object] || rdf_object.constantize
42
+ else
43
+ target = rdf_object
44
+ end
45
+ target.find_or_initialize_by_origin(rdf_subject)
46
+ when String
47
+ # dictionary lookup
48
+ target = PREDICATE_DICTIONARY[rdf_predicate] || rdf_predicate.constantize
49
+ target.build_from_rdf(rdf_subject, target, rdf_object)
50
+ else # is a class
51
+ rdf_predicate.build_from_rdf(rdf_subject, rdf_predicate, rdf_object)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -1,15 +1,16 @@
1
+ require 'iqvoc/rdfapi'
2
+
1
3
  module Iqvoc
2
4
  class SkosImporter
3
5
 
4
6
  FIRST_LEVEL_OBJECT_CLASSES = [Iqvoc::Concept.base_class, Iqvoc::Collection.base_class]
5
7
  SECOND_LEVEL_OBJECT_CLASSES = Iqvoc::Concept.labeling_classes.keys +
6
- Iqvoc::Concept.note_classes +
7
- Iqvoc::Concept.relation_classes +
8
- Iqvoc::Concept.match_classes +
9
- Iqvoc::Collection.member_classes
8
+ Iqvoc::Concept.note_classes +
9
+ Iqvoc::Concept.relation_classes +
10
+ Iqvoc::Concept.match_classes +
11
+ [Iqvoc::Collection.member_class]
10
12
 
11
13
  def initialize(file, default_namespace_url, logger = Rails.logger)
12
-
13
14
  @logger = logger
14
15
 
15
16
  unless file.is_a?(File) || file.is_a?(Array)
@@ -28,69 +29,90 @@ module Iqvoc
28
29
  @prefixes[uri] = "#{pref.to_s}:"
29
30
  end
30
31
 
31
- @seen_first_level_objects = {}
32
+ @seen_first_level_objects = {} # Concept cache (don't load any concept twice from db)
33
+ @new_subjects = [] # Concepts to be published later
34
+
35
+ # Triples the importer doesn't understand immediately. Example:
36
+ #
37
+ # :a skos:prefLabel "foo". # => What is :a? Remember this and try again later
38
+ # ....
39
+ # :a rdf:type skos:Concept # => Now I know :a, good I remebered it's prefLabel...
40
+ @unknown_second_level_tripes = []
41
+
42
+ # Hash of arrays of arrays: { "_:n123" => [["pred1", "obj1"], ["pred2", "obj2"]] }
32
43
  @blank_nodes = {}
33
44
 
34
45
  @existing_origins = {} # To prevent the creation of first level objects we already have
35
- FIRST_LEVEL_OBJECT_CLASSES.each do |klass|
36
- klass.select("origin").all.each do |thing|
46
+ Iqvoc::RDFAPI::FIRST_LEVEL_OBJECT_CLASSES.each do |klass|
47
+ klass.select('origin').all.each do |thing|
37
48
  @existing_origins[thing.origin] = klass
38
49
  end
39
50
  end
40
51
 
41
- import(file)
52
+ import file
42
53
  end
43
54
 
44
55
  private
45
56
 
46
57
  def import(file)
47
- # Collect blank nodes
48
- file.each do |line|
49
- identify_blank_nodes(*extract_triple(line))
50
- end
58
+ ActiveSupport.run_load_hooks(:skos_importer_before_import, self)
59
+
60
+ start = Time.now
51
61
 
52
- file.rewind if file.is_a?(IO)
53
- types = {} # type identifier ("namespace:SomeClass") to Iqvoc class assignment hash
62
+ first_level_types = {} # type identifier ("namespace:SomeClass") to Iqvoc class assignment hash
54
63
  FIRST_LEVEL_OBJECT_CLASSES.each do |klass|
55
- types["#{klass.rdf_namespace}:#{klass.rdf_class}"] = klass
64
+ first_level_types["#{klass.rdf_namespace}:#{klass.rdf_class}"] = klass
65
+ end
66
+ second_level_types = {}
67
+ SECOND_LEVEL_OBJECT_CLASSES.each do |klass|
68
+ second_level_types["#{klass.rdf_namespace}:#{klass.rdf_predicate}"] = klass
56
69
  end
57
70
 
58
71
  file.each do |line|
59
- import_first_level_objects(types, *extract_triple(line))
72
+ identify_blank_nodes(*extract_triple(line)) ||
73
+ import_first_level_objects(first_level_types, *extract_triple(line)) ||
74
+ import_second_level_objects(second_level_types, false, *extract_triple(line))
60
75
  end
61
- new_subjects = @seen_first_level_objects.dup # Remember the objects seen yet, because they are the ones to be published later
62
76
 
63
- file.rewind if file.is_a?(IO)
64
- types = {}
65
- SECOND_LEVEL_OBJECT_CLASSES.each do |klass|
66
- types["#{klass.rdf_namespace}:#{klass.rdf_predicate}"] = klass
67
- end
68
- file.each do |line|
69
- import_second_level_objects(types, *extract_triple(line))
77
+ @logger.debug("Computing 'forward' defined triples...")
78
+ @unknown_second_level_tripes.each do |s, p, o|
79
+ import_second_level_objects(second_level_types, true, s, p, o)
70
80
  end
71
81
 
72
- new_subjects.each do |id, subject|
82
+ first_import_step_done = Time.now
83
+ @logger.debug("Basic import done (took #{(first_import_step_done - start).to_i} seconds).")
84
+
85
+ @logger.debug("Publishing #{@new_subjects.count} new subjects...")
86
+ published = 0
87
+ @new_subjects.each do |subject|
73
88
  if subject.valid_with_full_validation?
74
89
  subject.publish
75
90
  subject.save!
91
+ published += 1
76
92
  else
77
- @logger.warn "WARNING: Subject not valid: '#{subject.origin}'. Won't be published automatically.."
93
+ @logger.warn "WARNING: Publishing failed! Subject ('#{subject.origin}') invalid: #{subject.errors.to_hash.inspect}"
78
94
  end
79
95
  end
80
96
 
97
+ done = Time.now
98
+ @logger.debug("Publishing of #{published} subjects done (took #{(done - first_import_step_done).to_i} seconds). #{@new_subjects.count - published} where invalid.")
99
+ puts "Imported #{published} valid and #{@new_subjects.count - published} invalid subjects in #{(done - start).to_i} seconds."
100
+ puts " First step took #{(first_import_step_done - start).to_i} seconds, publishing took #{(done - first_import_step_done).to_i} seconds."
101
+
102
+ ActiveSupport.run_load_hooks(:skos_importer_after_import, self)
81
103
  end
82
104
 
83
105
  def identify_blank_nodes(subject, predicate, object)
84
106
  if blank_node?(subject)
85
107
  @blank_nodes[subject] ||= []
86
108
  @blank_nodes[subject] << [predicate, object]
109
+ true
110
+ else
111
+ false
87
112
  end
88
113
  end
89
114
 
90
115
  def import_first_level_objects(types, subject, predicate, object)
91
- @logger.debug "types: #{types}"
92
- @logger.debug "predicate: #{predicate}"
93
- @logger.debug "subject: #{subject}"
94
116
  if (predicate == "rdf:type" && types[object] && subject =~ /^:(.+)$/)
95
117
  # We've found a subject definition with a class we know and which is in our responsibility (":")
96
118
  origin = $1
@@ -103,20 +125,29 @@ module Iqvoc
103
125
  end
104
126
  else
105
127
  @seen_first_level_objects[origin] = types[object].create!(:origin => origin)
128
+ @new_subjects << @seen_first_level_objects[origin]
106
129
  end
130
+ true
131
+ else
132
+ false
107
133
  end
108
-
109
134
  end
110
135
 
111
- def import_second_level_objects(types, subject, predicate, object)
136
+ def import_second_level_objects(types, final, subject, predicate, object)
112
137
  return unless (subject =~ /^:(.*)$/ && types[predicate]) # We're not responsible for this
113
138
 
139
+ initial_triple = [subject, predicate, object]
140
+
114
141
  # Load the subject and replace the string by the respective data object
115
142
  subject_origin = $1
116
143
  subject = load_first_level_object(subject_origin)
117
144
  unless subject
118
- @logger.warn "Iqvoc::SkosImporter: Couldn't find Subject with origin '#{subject_origin}. Skipping entry '#{subject} #{predicate} #{object}.'"
119
- return
145
+ if final
146
+ @logger.warn "Iqvoc::SkosImporter: Couldn't find Subject with origin '#{subject_origin}. Skipping entry '#{subject} #{predicate} #{object}.'"
147
+ else
148
+ @unknown_second_level_tripes << initial_triple
149
+ end
150
+ return false
120
151
  end
121
152
 
122
153
  # Load the data object for the object string if this is representing a thing in our domain
@@ -124,13 +155,28 @@ module Iqvoc
124
155
  object_origin = $1
125
156
  object = load_first_level_object(object_origin)
126
157
  unless object
127
- @logger.warn "Iqvoc::SkosImporter: Couldn't find Object with origin '#{object_origin}. Skipping entry ':#{subject_origin} #{predicate} #{object}.'"
128
- return
158
+ if final
159
+ @logger.warn "Iqvoc::SkosImporter: Couldn't find Object with origin '#{object_origin}. Skipping entry ':#{subject_origin} #{predicate} #{object}.'"
160
+ else
161
+ @unknown_second_level_tripes << initial_triple
162
+ end
163
+ return false
129
164
  end
130
165
  end
131
166
 
167
+ # If not in final mode every :my_concept :bla _:blank_node. triple should
168
+ # be saved for final mode. Why? Example:
169
+ #
170
+ # :a iqvoc:changeNote _:b01 # => I do not know know anything about the blank node now
171
+ # _:b01 dc:author "DHH"...
172
+ #
132
173
  if blank_node?(object)
133
- object = @blank_nodes[object]
174
+ if final
175
+ object = @blank_nodes[object]
176
+ else
177
+ @unknown_second_level_tripes << initial_triple
178
+ return false
179
+ end
134
180
  end
135
181
 
136
182
  types[predicate].build_from_rdf(subject, predicate, object)
@@ -138,11 +184,12 @@ module Iqvoc
138
184
 
139
185
  def load_first_level_object(origin)
140
186
  unless @seen_first_level_objects[origin]
141
- FIRST_LEVEL_OBJECT_CLASSES.each do |klass|
187
+ klass = @existing_origins[origin]
188
+ if klass
142
189
  @seen_first_level_objects[origin] = klass.by_origin(origin).last
143
- break if @seen_first_level_objects[origin]
144
190
  end
145
191
  end
192
+
146
193
  @seen_first_level_objects[origin]
147
194
  end
148
195
 
@@ -151,7 +198,7 @@ module Iqvoc
151
198
  end
152
199
 
153
200
  def extract_triple(line)
154
- raise "'#{line}' doesn't look like valid ntriples data." unless line =~ /^(.*)\.\w*$/
201
+ raise "'#{line}' doesn't look like valid ntriples data." unless line =~ /^(.*)\.\s*$/
155
202
  line = $1.squish
156
203
 
157
204
  triple = line.split(' ', 3) # The first one are uris the last can be a literal too
@@ -171,5 +218,6 @@ module Iqvoc
171
218
  triple
172
219
  end
173
220
 
221
+ ActiveSupport.run_load_hooks(:skos_importer, self)
174
222
  end
175
223
  end
data/lib/iqvoc/version.rb CHANGED
@@ -15,5 +15,5 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module Iqvoc
18
- VERSION = "4.0.8"
18
+ VERSION = "4.0.9"
19
19
  end
@@ -0,0 +1,38 @@
1
+ class MultiLogger
2
+
3
+ attr_reader :loggers
4
+
5
+ def initialize(*args)
6
+ @loggers = args
7
+ end
8
+
9
+ def level=(level)
10
+ @loggers.each { |logger| logger.level = level }
11
+ end
12
+
13
+ def levels
14
+ @loggers.map(&:level)
15
+ end
16
+
17
+ def min_level
18
+ levels.min
19
+ end
20
+
21
+ def close
22
+ @loggers.map(&:close)
23
+ end
24
+
25
+ def add(level, *args)
26
+ @loggers.each { |logger| logger.add(level, *args) }
27
+ end
28
+
29
+ Logger::Severity.constants.each do |level|
30
+ define_method(level.downcase) do |*args|
31
+ @loggers.each { |logger| logger.send(level.downcase, *args) }
32
+ end
33
+
34
+ define_method("#{ level.downcase }?".to_sym) do
35
+ min_level <= Logger::Severity.const_get(level)
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,5 @@
1
+ require 'multi_logger'
2
+
1
3
  namespace :iqvoc do
2
4
 
3
5
  namespace :import do
@@ -8,7 +10,10 @@ namespace :iqvoc do
8
10
 
9
11
  raise "You have to specify an url for the data file to be imported. Example: rake iqvoc:import:url URL=... NAMESPACE=" unless ENV['URL']
10
12
  raise "You have to specify a default namespace for the data to be imported. Example: rake iqvoc:import:url URL=... NAMESPACE=" unless ENV['NAMESPACE']
11
- Iqvoc::SkosImporter.new(open(URI.parse(ENV['URL']).to_s), URI.parse(ENV['NAMESPACE']).to_s)
13
+
14
+ stdout_logger = Logger.new(STDOUT)
15
+ stdout_logger.level = Logger::INFO
16
+ Iqvoc::SkosImporter.new(open(URI.parse(ENV['URL']).to_s), URI.parse(ENV['NAMESPACE']).to_s, MultiLogger.new(stdout_logger, Rails.logger))
12
17
  end
13
18
 
14
19
  end
@@ -58,4 +58,20 @@ class BrowseConceptsAndLabelsTest < ActionDispatch::IntegrationTest
58
58
  "'#{@concepts[1].origin} a skos:Concept' missing in turtle view"
59
59
  end
60
60
 
61
+ test "showing expired concepts" do
62
+ # prepare database with expired concept
63
+ concepts = [[:en, "Method"], [:de, "Methode"]].map do |lang, text|
64
+ FactoryGirl.create(:concept,
65
+ :expired_at => 2.days.ago,
66
+ :pref_labelings => [
67
+ FactoryGirl.create(:pref_labeling, :target => FactoryGirl.create(:pref_label, :language => lang, :value => text))
68
+ ])
69
+ end
70
+
71
+ visit hierarchical_concepts_path(:lang => 'en')
72
+ click_link_or_button('Expired')
73
+ click_link_or_button('M')
74
+ assert page.has_content?(concepts.first.pref_label.to_s), 'should have one expired concept'
75
+ end
76
+
61
77
  end
@@ -20,7 +20,7 @@ require 'integration_test_helper'
20
20
  class InstanceConfigurationTest < ActionDispatch::IntegrationTest
21
21
 
22
22
  test "configuration privileges" do
23
- uri = "/config"
23
+ uri = "/en/config"
24
24
 
25
25
  # guest
26
26
  visit uri
@@ -37,7 +37,7 @@ class InstanceConfigurationTest < ActionDispatch::IntegrationTest
37
37
  visit uri
38
38
  assert_equal "/en/config.html", page.current_path
39
39
  assert page.has_css?("input#config_title")
40
- assert page.has_css?("input#config_available_languages")
40
+ assert page.has_css?("input#config_note_languages")
41
41
  assert page.has_selector?(:xpath, '//input[@id="config_languages.pref_labeling"]')
42
42
  assert page.has_selector?(:xpath, '//input[@id="config_languages.further_labelings.Labeling::SKOS::AltLabel"]')
43
43
 
@@ -48,7 +48,7 @@ class InstanceConfigurationTest < ActionDispatch::IntegrationTest
48
48
  assert page.find("a.brand").has_content? "iQvoc"
49
49
 
50
50
  login "administrator"
51
- visit "/config"
51
+ visit "/en/config"
52
52
 
53
53
  fill_in "config_title", :with => "lorem ipsum"
54
54
  click_button "Save"
@@ -62,9 +62,9 @@ class InstanceConfigurationTest < ActionDispatch::IntegrationTest
62
62
  }
63
63
  ["reader", "editor", "publisher", "administrator"].each do |role|
64
64
  login role
65
- check_page.call "/config"
65
+ check_page.call "/en/config"
66
66
  logout
67
- check_page.call "/search"
67
+ check_page.call "/en/search"
68
68
  end
69
69
 
70
70
  # TODO: test routes-by-language availability, post-modification
@@ -31,7 +31,8 @@ class SearchTest < ActionDispatch::IntegrationTest
31
31
  end
32
32
 
33
33
  # create collection
34
- @collection = FactoryGirl.create(:collection, :concepts => @concepts,
34
+ @collection = FactoryGirl.create(:collection,
35
+ :members => @concepts.map { |c| Iqvoc::Collection.member_class.new(:target => c) },
35
36
  :labelings => [], :pref_labelings => [
36
37
  FactoryGirl.create(:pref_labeling,
37
38
  :target => FactoryGirl.create(:pref_label, :language => :en, :value => "Alpha"))
@@ -49,9 +50,9 @@ class SearchTest < ActionDispatch::IntegrationTest
49
50
  :type => 'Labels', :query => 'Forest', :query_type => 'contains',
50
51
  :amount => 1, :result => 'Forest'
51
52
  }].each { |q|
52
- select q[:type], :from => "t"
53
+ find("#t").select q[:type]
53
54
  fill_in "Search term(s)", :with => q[:query]
54
- select q[:query_type], :from => "qt"
55
+ find("#qt").select q[:query_type]
55
56
 
56
57
  # select all languages
57
58
  page.all(:css, ".lang_check").each do |cb|
@@ -72,8 +73,8 @@ class SearchTest < ActionDispatch::IntegrationTest
72
73
  test "collection/concept filter" do
73
74
  visit search_path(:lang => 'en', :format => 'html')
74
75
 
75
- select "Labels", :from => "t"
76
- select "contains", :from => "qt"
76
+ find("#t").select "Labels"
77
+ find("#qt").select "contains"
77
78
  fill_in "Search term(s)", :with => "Alpha"
78
79
  click_button("Search")
79
80
  assert page.has_css?("#search_results dt", :count => 1)
@@ -90,10 +91,10 @@ class SearchTest < ActionDispatch::IntegrationTest
90
91
  test "searching within collections" do
91
92
  visit search_path(:lang => 'en', :format => 'html')
92
93
 
93
- select "Labels", :from => "t"
94
- select "contains", :from => "qt"
94
+ find("#t").select "Labels"
95
+ find("#qt").select "contains"
95
96
  fill_in "Search term(s)", :with => "res"
96
- select @collection.to_s, :from => "c"
97
+ find("#c").select @collection.to_s
97
98
 
98
99
  # select all languages
99
100
  page.all(:css, ".lang_check").each do |cb|
@@ -128,10 +129,10 @@ class SearchTest < ActionDispatch::IntegrationTest
128
129
 
129
130
  visit search_path(:lang => 'en', :format => 'html')
130
131
 
131
- select "Notes", :from => "t"
132
- select "contains", :from => "qt"
132
+ find("#t").select "Notes"
133
+ find("#qt").select "contains"
133
134
  fill_in "Search term(s)", :with => "ipsum"
134
- select @collection.to_s, :from => "c"
135
+ find("#c").select @collection.to_s
135
136
 
136
137
  # select all languages
137
138
  page.all(:css, ".lang_check").each do |cb|
@@ -147,10 +148,10 @@ class SearchTest < ActionDispatch::IntegrationTest
147
148
  test "empty query with selected collection should return all collection members" do
148
149
  visit search_path(:lang => 'en', :format => 'html')
149
150
 
150
- select "Labels", :from => "t"
151
- select "exact match", :from => "qt"
151
+ find("#t").select "Labels"
152
+ find("#qt").select "exact match"
152
153
  fill_in "Search term(s)", :with => ""
153
- select @collection.to_s, :from => "c"
154
+ find("#c").select @collection.to_s
154
155
 
155
156
  # select all languages
156
157
  page.all(:css, ".lang_check").each do |cb|
@@ -175,8 +176,8 @@ class SearchTest < ActionDispatch::IntegrationTest
175
176
 
176
177
  visit search_path(:lang => 'en', :format => 'html')
177
178
 
178
- select "Labels", :from => "t"
179
- select "contains", :from => "qt"
179
+ find("#t").select "Labels"
180
+ find("#qt").select "contains"
180
181
  fill_in "Search term(s)", :with => "sample_"
181
182
 
182
183
  click_button("Search")
@@ -184,7 +185,7 @@ class SearchTest < ActionDispatch::IntegrationTest
184
185
  assert page.has_css?("#search_results dt", :count => 5)
185
186
  assert page.has_css?(".pagination .page", :count => 3)
186
187
 
187
- click_link("3")
188
+ find(".pagination").all(".page").last.find("a").click
188
189
 
189
190
  assert page.has_css?("#search_results dt", :count => 2)
190
191