triannon 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56a7669bd9da9a9b2606b8e69581aefa06ba3758
4
- data.tar.gz: 647d6a29caedcec5c4050d6fa2fcb52024e07248
3
+ metadata.gz: d962055a1eab01f2506013e1b964c52c4e8f53b8
4
+ data.tar.gz: 2015fcbfba7f07fed353a59250a044376863aad0
5
5
  SHA512:
6
- metadata.gz: d1a31dcf5fd7f41d9cd07a5b516c6fda3f3c90c8ae9eeffbbdd4ee50dba79d0cedc9bc27e6af741ac7834186d8a7bc930824e508ab4cad8eafae635abd0a8f29
7
- data.tar.gz: 646d9abc0b152ab17e852295f3e0e7c8a58d0343572f5be6de8b596be10540fb1664e27eed4881be2af19b2c9ab20306edf2c5e48c9c42baf8c4357e9ab4bcd1
6
+ metadata.gz: a267c5b3a4c2fd76e386ca1b9f6e196dd5905a94c9590c9698ec906add9614df8c512d6382ce2c17f7aad619dc608435904dfe28fbd22748b2df87532310340e
7
+ data.tar.gz: c22b8ddb6e7b659598f879aceefa008345b35995b8945b8aa57a218162f5b29a5cc2e2bc420034727e02af14624fc113949165acfb11ab7331d7c9f6b7073d03
data/README.md CHANGED
@@ -50,7 +50,7 @@ Set up caching for jsonld context documents:
50
50
  ** add to Gemfile:
51
51
 
52
52
  ```ruby
53
- gem 'rest-client', '~> 1.7.2'
53
+ gem 'rest-client', '~> 1.7.3' # problem with rest-client 1.8.0 and rest-client-components
54
54
  gem 'rack-cache'
55
55
  gem 'rest-client-components'
56
56
  ```
@@ -0,0 +1,72 @@
1
+ # methods to support RDF response formats
2
+ module RdfResponseFormats
3
+ extend ActiveSupport::Concern
4
+
5
+ # find first mime type from request.accept that matches return mime type
6
+ def mime_type_from_accept(return_mime_types)
7
+ @mime_type_from_accept ||= begin
8
+ if request.accept && request.accept.is_a?(String)
9
+ accept_mime_types = request.accept.split(',')
10
+ accept_mime_types.each { |mime_type|
11
+ mime_str = mime_type.split("; profile=").first.strip
12
+ if return_mime_types.include? mime_str
13
+ return mime_str
14
+ end
15
+ }
16
+ end
17
+ end
18
+ end
19
+
20
+ # set format to jsonld if it isn't already set
21
+ def default_format_jsonld
22
+ if ((!request.accept || request.accept.empty?) && (!params[:format] || params[:format].empty?))
23
+ request.format = "jsonld"
24
+ end
25
+ end
26
+
27
+ # parse the Accept HTTP header for the value of profile if it is a request for jsonld or json
28
+ # e.g. Accept: application/ld+json; profile="http://www.w3.org/ns/oa-context-20130208.json"
29
+ # @return [String] url for jsonld @context or nil if missing or non-jsonld/json format
30
+ def context_url_from_accept
31
+ if request.format == "jsonld" || request.format == "json"
32
+ accept_str = request.accept
33
+ if accept_str && accept_str.split("profile=") && accept_str.split("profile=").last
34
+ context_url = accept_str.split("profile=").last.strip
35
+ context_url = context_url[1, context_url.size] if context_url.start_with?('"')
36
+ context_url = context_url[0, context_url.size-1] if context_url.end_with?('"')
37
+ case context_url
38
+ when Triannon::JsonldContext::OA_DATED_CONTEXT_URL,
39
+ Triannon::JsonldContext::OA_CONTEXT_URL,
40
+ Triannon::JsonldContext::IIIF_CONTEXT_URL
41
+ context_url
42
+ else
43
+ nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ # parse the Accept HTTP Link for the value of rel if it is a request for jsonld or json
50
+ # e.g. Link: http://www.w3.org/ns/oa.json; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
51
+ # note that the "type" part is optional
52
+ # @return [String] url for jsonld @context or nil if missing or non-jsonld/json format
53
+ def context_url_from_link
54
+ if request.format == "jsonld" || request.format == "json"
55
+ link_str = request.headers["Link"]
56
+ if link_str && link_str.split("; rel=") && link_str.split("; rel=").first
57
+ context_url = link_str.split("; rel=").first.strip
58
+ case context_url
59
+ when Triannon::JsonldContext::OA_DATED_CONTEXT_URL,
60
+ Triannon::JsonldContext::OA_CONTEXT_URL,
61
+ Triannon::JsonldContext::IIIF_CONTEXT_URL
62
+ context_url
63
+ else
64
+ nil
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+
72
+ end
@@ -2,6 +2,8 @@ require_dependency "triannon/application_controller"
2
2
 
3
3
  module Triannon
4
4
  class AnnotationsController < ApplicationController
5
+ include RdfResponseFormats
6
+
5
7
  before_action :default_format_jsonld, only: [:show]
6
8
  before_action :set_annotation, only: [:show, :update, :destroy]
7
9
  rescue_from Triannon::ExternalReferenceError, with: :ext_ref_error
@@ -51,7 +53,7 @@ module Triannon
51
53
  end
52
54
 
53
55
  # NOT YET IMPLEMENTED
54
- # GET /annotations/1/edit
56
+ # GET /annotations/1/edit
55
57
  # def edit
56
58
  # end
57
59
 
@@ -71,7 +73,7 @@ module Triannon
71
73
  content_type = request.headers["Content-Type"]
72
74
  @annotation = Annotation.new({:data => request.body.read, :expected_content_type => content_type})
73
75
  end
74
-
76
+
75
77
  if @annotation.save
76
78
  default_format_jsonld # NOTE: this must be here and not in before_filter or we get Missing template errors
77
79
  flash[:notice] = "Annotation #{@annotation.id} was successfully created."
@@ -124,83 +126,18 @@ module Triannon
124
126
  @annotation.destroy
125
127
  redirect_to annotations_url, status: 204, notice: 'Annotation was successfully destroyed.'
126
128
  end
127
-
129
+
128
130
  private
129
131
 
130
132
  def set_annotation
131
133
  @annotation = Annotation.find(params[:id])
132
134
  end
133
-
134
- # set format to jsonld if it isn't already set
135
- def default_format_jsonld
136
- if ((!request.accept || request.accept.empty?) && (!params[:format] || params[:format].empty?))
137
- request.format = "jsonld"
138
- end
139
- end
140
-
141
- # find first mime type from request.accept that matches return mime type
142
- def mime_type_from_accept(return_mime_types)
143
- @mime_type_from_accept ||= begin
144
- if request.accept && request.accept.is_a?(String)
145
- accept_mime_types = request.accept.split(',')
146
- accept_mime_types.each { |mime_type|
147
- mime_str = mime_type.split("; profile=").first.strip
148
- if return_mime_types.include? mime_str
149
- return mime_str
150
- end
151
- }
152
- end
153
- end
154
- end
155
-
156
- # parse the Accept HTTP header for the value of profile if it is a request for jsonld or json
157
- # e.g. Accept: application/ld+json; profile="http://www.w3.org/ns/oa-context-20130208.json"
158
- # @return [String] url for jsonld @context or nil if missing or non-jsonld/json format
159
- def context_url_from_accept
160
- if request.format == "jsonld" || request.format == "json"
161
- accept_str = request.accept
162
- if accept_str && accept_str.split("profile=") && accept_str.split("profile=").last
163
- context_url = accept_str.split("profile=").last.strip
164
- context_url = context_url[1, context_url.size] if context_url.start_with?('"')
165
- context_url = context_url[0, context_url.size-1] if context_url.end_with?('"')
166
- case context_url
167
- when Triannon::JsonldContext::OA_DATED_CONTEXT_URL,
168
- Triannon::JsonldContext::OA_CONTEXT_URL,
169
- Triannon::JsonldContext::IIIF_CONTEXT_URL
170
- context_url
171
- else
172
- nil
173
- end
174
- end
175
- end
176
- end
177
-
178
- # parse the Accept HTTP Link for the value of rel if it is a request for jsonld or json
179
- # e.g. Link: http://www.w3.org/ns/oa.json; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"
180
- # note that the "type" part is optional
181
- # @return [String] url for jsonld @context or nil if missing or non-jsonld/json format
182
- def context_url_from_link
183
- if request.format == "jsonld" || request.format == "json"
184
- link_str = request.headers["Link"]
185
- if link_str && link_str.split("; rel=") && link_str.split("; rel=").first
186
- context_url = link_str.split("; rel=").first.strip
187
- case context_url
188
- when Triannon::JsonldContext::OA_DATED_CONTEXT_URL,
189
- Triannon::JsonldContext::OA_CONTEXT_URL,
190
- Triannon::JsonldContext::IIIF_CONTEXT_URL
191
- context_url
192
- else
193
- nil
194
- end
195
- end
196
- end
197
- end
198
135
 
199
136
  # handle Triannon::ExternalReferenceError
200
137
  def ext_ref_error(exception)
201
138
  render plain: exception.message, status: 403
202
139
  end
203
-
140
+
204
141
  # render json_ld respecting requested context
205
142
  # @param [String] req_context set to "iiif" or "oa". Default is oa
206
143
  # @param [String] mime_type the mime type to be set in the Content-Type header of the HTTP response
@@ -0,0 +1,46 @@
1
+ require_dependency "triannon/application_controller"
2
+
3
+ module Triannon
4
+ class SearchController < ApplicationController
5
+ include RdfResponseFormats
6
+
7
+ before_action :default_format_jsonld, only: [:find]
8
+
9
+ def find
10
+ anno_graphs_array = solr_searcher.find(params)
11
+
12
+ # add id to iiif_anno_list
13
+ @list_hash = Triannon::IIIFAnnoList.anno_list(anno_graphs_array)
14
+ @list_hash["@id"] = request.original_url if @list_hash
15
+
16
+ respond_to do |format|
17
+ format.jsonld { render :json => @list_hash.to_json, content_type: "application/ld+json" }
18
+ format.ttl {
19
+ accept_return_type = mime_type_from_accept(["application/x-turtle", "text/turtle"])
20
+ render :body => RDF::Graph.new.from_jsonld(@list_hash.to_json).to_ttl, content_type: accept_return_type if accept_return_type
21
+ }
22
+ format.rdfxml {
23
+ accept_return_type = mime_type_from_accept(["application/rdf+xml", "text/rdf+xml", "text/rdf"])
24
+ render :body => RDF::Graph.new.from_jsonld(@list_hash.to_json).to_rdfxml, content_type: accept_return_type if accept_return_type }
25
+ format.json {
26
+ accept_return_type = mime_type_from_accept(["application/json", "text/x-json", "application/jsonrequest"])
27
+ render :json => @list_hash.to_json, content_type: accept_return_type
28
+ }
29
+ format.xml {
30
+ accept_return_type = mime_type_from_accept(["application/xml", "text/xml", "application/x-xml"])
31
+ render :xml => RDF::Graph.new.from_jsonld(@list_hash.to_json).to_rdfxml, content_type: accept_return_type if accept_return_type }
32
+ format.html { render :find }
33
+ end
34
+ end
35
+
36
+
37
+ protected
38
+
39
+ def solr_searcher
40
+ @ss ||= Triannon::SolrSearcher.new
41
+ end
42
+
43
+
44
+ end # SearchController
45
+
46
+ end # Triannon
@@ -102,7 +102,7 @@ protected
102
102
 
103
103
  # Add annotation to Solr as a Solr document
104
104
  def solr_save
105
- solr_writer.write(graph) if graph && graph.id_as_url && !graph.id_as_url.empty?
105
+ solr_writer.write(graph) if id_as_url && !id_as_url.empty?
106
106
  end
107
107
 
108
108
  # Delete annotation from Solr
@@ -0,0 +1,164 @@
1
+ module Triannon
2
+ class SolrSearcher
3
+
4
+ # convert RSolr::Response object into an array of Triannon::Graph objects,
5
+ # where each graph object contains a single annotation returned in the response docs
6
+ # @param [Hash] rsolr_response an RSolr response to a query. It's actually an
7
+ # RSolr::HashWithResponse but let's not quibble
8
+ # @return [Array<Triannon::Graph>]
9
+ def self.anno_graphs_array(rsolr_response)
10
+ result = []
11
+ # TODO: deal with Solr pagination
12
+ rsolr_response['response']['docs'].each { |solr_doc_hash|
13
+ result << Triannon::Graph.new(RDF::Graph.new.from_jsonld(solr_doc_hash['anno_jsonld']))
14
+ }
15
+ result
16
+ end
17
+
18
+ # @note hardcoded Solr search service expectation in generated search params
19
+ # @note hardcoded mapping of REST params for /search to Solr params
20
+ #
21
+ # Convert action request params to appropriate params
22
+ # to be sent to the search service as part of a search request
23
+ #
24
+ # request params are given in "Annotation Lists in Triannon" by Robert Sanderson
25
+ # in Google Docs:
26
+ #
27
+ # - targetUri, value is a URI
28
+ # - bodyUri, value is a URI
29
+ # - bodyExact, value is a string
30
+ # - bodyKeyword, value is a string
31
+ # - bodyType, value is a URI
32
+ # - motivatedBy, value is a URI (or just the fragment portion)
33
+ # - annotatedBy, value is a URI
34
+ # - annotatedAt, value is a datetime
35
+ #
36
+ # @param [Hash<String => String>] controller_params params from Controller
37
+ # @return [Hash] params to send to Solr as a Hash
38
+ def self.solr_params(controller_params)
39
+ solr_params_hash = {}
40
+ q_terms_array = []
41
+ fq_terms_array = []
42
+
43
+ controller_params.each_pair { |k, v|
44
+ case k.downcase
45
+ when 'targeturi'
46
+ q_terms_array << q_terms_for_url("target_url", v)
47
+ when 'bodyuri'
48
+ q_terms_array << q_terms_for_url("body_url", v)
49
+ when 'bodyexact'
50
+ # no need to Solr escape value because it's in quotes
51
+ q_terms_array << "body_chars_exact:\"#{v}\""
52
+ when 'motivatedby'
53
+ case
54
+ when v.include?('#')
55
+ # we want fragment portion of URL value only, as that
56
+ # is what is in Solr
57
+ fq_terms_array << "motivation:#{RSolr.solr_escape(v.sub(/^.*#/, ''))}"
58
+ when v == "http://www.shared-canvas.org/ns/painting", v == "sc:painting"
59
+ fq_terms_array << "motivation:painting"
60
+ else
61
+ fq_terms_array << "motivation:#{RSolr.solr_escape(v)}"
62
+ end
63
+ when 'bodykeyword'
64
+ solr_params_hash[:kqf] = 'body_chars_exact^3 body_chars_unstem^2 body_chars_stem'
65
+ solr_params_hash[:kpf] = 'body_chars_exact^15 body_chars_unstem^10 body_chars_stem^5'
66
+ solr_params_hash[:kpf3] = 'body_chars_exact^9 body_chars_unstem^6 body_chars_stem^3'
67
+ solr_params_hash[:kpf2] = 'body_chars_exact^6 body_chars_unstem^4 body_chars_stem^2'
68
+ q_terms_array << '_query_:"{!dismax qf=$kqf pf=$kpf pf3=$kpf3 pf2=$kpf2}' + RSolr.solr_escape(v) + '"'
69
+
70
+ # TODO: add'l params to implement:
71
+ # targetType - fq
72
+ # bodyType - fq
73
+ # annotatedAt - fq (deal with time format and wildcard for specificity)
74
+ # annotatedBy - q (may be incomplete string)
75
+ end
76
+ }
77
+
78
+ q_terms_array.flatten
79
+ if q_terms_array.size > 0
80
+ solr_params_hash[:q] = q_terms_array.join(' AND ')
81
+ solr_params_hash[:defType] = "lucene"
82
+ end
83
+ if fq_terms_array.size > 0
84
+ solr_params_hash[:fq] = fq_terms_array
85
+ end
86
+
87
+ solr_params_hash
88
+
89
+ # TODO: integration tests for
90
+ # target_url with and without the scheme prefix
91
+ # target_url with and without fragment
92
+ # bodykeyword single terms, multiple terms, quoted strings ...
93
+
94
+ end # solr_params
95
+
96
+
97
+ # If the url contains a fragment, query terms should only match the exact
98
+ # url given (with the specific fragment). (i.e. foo.org#bar does not
99
+ # match foo.org)
100
+ # If the url does NOT contain a fragment, query terms should match the
101
+ # url given (no fragment) AND any urls that are the same with a fragment
102
+ # added. (i.e. foo.org matches foo.org#bar)
103
+ # @param [String] fieldname the name of the Solr field to be searched with url as a value
104
+ # @param [String] url the url value sought in the Solr field
105
+ # @return [Array<String>] an array of query terms to be added to the Solr q argument
106
+ def self.q_terms_for_url(fieldname, url)
107
+ q_terms = []
108
+ q_terms << "#{fieldname}:#{RSolr.solr_escape(url)}"
109
+ if !url.include? '#'
110
+ # Note: do NOT Solr escape the # (unnec) or the * (want Solr to view it as wildcard)
111
+ q_terms << "#{fieldname}:#{RSolr.solr_escape(url)}#*"
112
+ end
113
+ q_terms
114
+ end
115
+
116
+
117
+ def initialize
118
+ @rsolr_client = RSolr.connect :url => Triannon.config[:solr_url]
119
+ @logger = Rails.logger
120
+ @max_retries = Triannon.config[:max_solr_retries] || 5
121
+ @base_sleep_seconds = Triannon.config[:base_sleep_seconds] || 1
122
+ @max_sleep_seconds = Triannon.config[:max_sleep_seconds] || 5
123
+ end
124
+
125
+ # to be called from controller:
126
+ # 1. converts controller params to solr params
127
+ # 2. sends request to Solr
128
+ # 3. converts Solr response object to array of anno graphs
129
+ # @param [Hash<String => String>] controller_params params from Controller
130
+ # @return [Array<Triannon::Graph>] array of Triannon::Graph objects,
131
+ # where each graph object contains a single annotation returned in the response docs
132
+ def find(controller_params)
133
+ solr_params = self.class.solr_params(controller_params)
134
+ solr_response = search(solr_params)
135
+ anno_graphs_array = self.class.anno_graphs_array(solr_response)
136
+ end
137
+
138
+
139
+ protected
140
+
141
+ # send params to Solr 'select' with POST, retrying if an error occurs.
142
+ # See https://github.com/ooyala/retries for info on with_retries.
143
+ # @param [Hash] solr_params the params to send to Solr
144
+ # @return RSolr::Response object
145
+ def search(solr_params = {})
146
+ handler = Proc.new do |exception, attempt_cnt, total_delay|
147
+ @logger.debug "#{exception.inspect} on Solr search attempt #{attempt_cnt} for #{solr_params.inspect}"
148
+ end
149
+
150
+ response = nil
151
+ with_retries(:handler => handler,
152
+ :max_tries => @max_retries,
153
+ :base_sleep_seconds => @base_sleep_seconds,
154
+ :max_sleep_seconds => @max_sleep_seconds) do |attempt|
155
+ @logger.debug "Solr search attempt #{attempt} for #{solr_params.inspect}"
156
+ # use POST in case of long params
157
+ response = @rsolr_client.post 'select', :params => solr_params
158
+ @logger.info "Successfully searched Solr on attempt #{attempt}"
159
+ end
160
+ response
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,3 @@
1
+ <pre>
2
+ <%= @list_hash.to_json.html_safe %>
3
+ </pre>
@@ -1,26 +1,31 @@
1
1
  Triannon::Engine.routes.draw do
2
+
2
3
  root to: 'annotations#index'
3
4
 
4
- # show action must explicitly forbid "new", "iiif" and "oa" as id values; couldn't
5
- # figure out how to do it with regexp constraint since beginning and end regex
6
- # matchers aren't allowed when enforcing formats for segment (e.g. :id)
7
- get '/annotations/:id(.:format)', to: 'annotations#show',
8
- constraints: lambda { |request|
5
+ resources :annotations, except: [:update, :edit],
6
+ # show action must explicitly forbid "new", "iiif" and "oa" as id values; couldn't
7
+ # figure out how to do it with regexp constraint since beginning and end regex
8
+ # matchers aren't allowed when enforcing formats for segment (e.g. :id)
9
+ constraints: lambda { |request|
9
10
  id = request.env["action_dispatch.request.path_parameters"][:id]
10
- id !~ /^new$/ && id !~ /^iiif$/ && id !~ /^oa$/
11
- }
12
-
13
- resources :annotations, :except => [:update, :edit, :show]
11
+ id !~ /^iiif$/ && id !~ /^oa$/ && id !~ /^search$/
12
+ } do
13
+ collection do
14
+ get 'search', to: 'search#find'
15
+ end
16
+ end
17
+
18
+ get '/search', to: 'search#find'
14
19
 
15
20
  # allow jsonld context in path (only allow iiif or oa as values)
16
21
  # must explicitly forbid "new" as id values; couldn't
17
- # figure out how to do it with regexp constraint since beginning and end regex
18
- # matchers aren't allowed when enforcing formats for segment (e.g. :id)
19
- get '/annotations/:jsonld_context/:id(.:format)', to: 'annotations#show',
20
- constraints: lambda { |request|
22
+ # figure out how to do it with regexp constraint since beginning and end regex
23
+ # matchers aren't allowed when enforcing formats for segment (e.g. :id)
24
+ get '/annotations/:jsonld_context/:id(.:format)', to: 'annotations#show',
25
+ constraints: lambda { |request|
21
26
  jsonld_context = request.env["action_dispatch.request.path_parameters"][:jsonld_context]
22
27
  id = request.env["action_dispatch.request.path_parameters"][:id]
23
28
  (jsonld_context =~ /^iiif$/ || jsonld_context =~ /^oa$/ ) && id !~ /^new$/
24
29
  }
25
-
30
+
26
31
  end
@@ -30,7 +30,7 @@ production:
30
30
  end
31
31
 
32
32
  def add_linked_data_caching
33
- gem 'rest-client', '~> 1.7.2'
33
+ gem 'rest-client', '~> 1.7.3' # problem with rest-client 1.8.0 and rest-client-components
34
34
  gem 'rack-cache'
35
35
  gem 'rest-client-components'
36
36
 
@@ -13,6 +13,7 @@ module Triannon
13
13
  require "triannon/engine"
14
14
  require "triannon/error"
15
15
  require "triannon/graph"
16
+ require "triannon/iiif_anno_list"
16
17
  require "triannon/jsonld_context"
17
18
 
18
19
  class << self
@@ -0,0 +1,59 @@
1
+ module Triannon
2
+
3
+ class IIIFAnnoList
4
+
5
+ # from http://iiif.io/api/presentation/2/annotationList_frame.json
6
+ ANNO_LIST_FRAME = JSON.parse('
7
+ {
8
+ "@context" : "http://iiif.io/api/presentation/2/context.json",
9
+ "@type": "sc:AnnotationList",
10
+ "resources": [{
11
+ "@type": "oa:Annotation",
12
+ "on" : [{
13
+ "@embed" : false
14
+ }]
15
+ }]
16
+ }')
17
+
18
+ # from http://iiif.io/api/presentation/2/annotation_frame.json
19
+ ANNO_FRAME = JSON.parse('
20
+ {
21
+ "@context" : "http://iiif.io/api/presentation/2/context.json",
22
+ "@type": "oa:Annotation",
23
+ "on" : [{
24
+ "@embed" : false
25
+ }]
26
+ }')
27
+
28
+ # Class Methods ----------------------------------------------------------------
29
+
30
+ # take an Array of annos as Triannon::Graph objects and return a Hash representation
31
+ # of IIIF Annotation List
32
+ # @param [Array<Triannon::Graph>] tgraph_array annotations as Triannon::Graph objects
33
+ # @return [Hash] IIIF Annotation List as a Hash, containing the annotations in the array
34
+ def self.anno_list(tgraph_array)
35
+ if tgraph_array
36
+ result = {
37
+ "@context" => Triannon::JsonldContext::IIIF_CONTEXT_URL,
38
+ "@type" => "sc:AnnotationList",
39
+ "within" => {"@type" => "sc:Layer", "total" => tgraph_array.size },
40
+ "resources" => tgraph_array.map { |g|
41
+ embedded_body = JSON::LD::API.frame(JSON.parse(g.jsonld_iiif), ANNO_FRAME)
42
+ resource = embedded_body["@graph"]
43
+ if resource.is_a?(Array) && resource.size == 1
44
+ resource = resource.first
45
+ end
46
+ resource
47
+ }
48
+ }
49
+
50
+ # remove context from each anno as it is redundant
51
+ result["resources"].each { |anno_hash|
52
+ anno_hash.delete("@context")
53
+ }
54
+ result
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module Triannon
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: triannon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Beer
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-03-27 00:00:00.000000000 Z
13
+ date: 2015-04-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -284,14 +284,14 @@ dependencies:
284
284
  requirements:
285
285
  - - "~>"
286
286
  - !ruby/object:Gem::Version
287
- version: 1.7.2
287
+ version: 1.7.3
288
288
  type: :development
289
289
  prerelease: false
290
290
  version_requirements: !ruby/object:Gem::Requirement
291
291
  requirements:
292
292
  - - "~>"
293
293
  - !ruby/object:Gem::Version
294
- version: 1.7.2
294
+ version: 1.7.3
295
295
  - !ruby/object:Gem::Dependency
296
296
  name: rest-client-components
297
297
  requirement: !ruby/object:Gem::Requirement
@@ -335,8 +335,10 @@ files:
335
335
  - app/assets/javascripts/triannon/application.js
336
336
  - app/assets/stylesheets/triannon/annotations.css
337
337
  - app/assets/stylesheets/triannon/application.css.scss
338
+ - app/controllers/concerns/rdf_response_formats.rb
338
339
  - app/controllers/triannon/annotations_controller.rb
339
340
  - app/controllers/triannon/application_controller.rb
341
+ - app/controllers/triannon/search_controller.rb
340
342
  - app/helpers/triannon/application_helper.rb
341
343
  - app/models/triannon/annotation.rb
342
344
  - app/models/triannon/annotation_ldp.rb
@@ -344,6 +346,7 @@ files:
344
346
  - app/services/triannon/ldp_to_oa_mapper.rb
345
347
  - app/services/triannon/ldp_writer.rb
346
348
  - app/services/triannon/root_annotation_creator.rb
349
+ - app/services/triannon/solr_searcher.rb
347
350
  - app/services/triannon/solr_writer.rb
348
351
  - app/views/layouts/triannon/application.html.erb
349
352
  - app/views/triannon/annotations/_form.html.erb
@@ -351,6 +354,7 @@ files:
351
354
  - app/views/triannon/annotations/index.html.erb
352
355
  - app/views/triannon/annotations/new.html.erb
353
356
  - app/views/triannon/annotations/show.html.erb
357
+ - app/views/triannon/search/find.html.erb
354
358
  - config/initializers/mime_types.rb
355
359
  - config/routes.rb
356
360
  - config/solr/solr.xml
@@ -365,6 +369,7 @@ files:
365
369
  - lib/triannon/engine.rb
366
370
  - lib/triannon/error.rb
367
371
  - lib/triannon/graph.rb
372
+ - lib/triannon/iiif_anno_list.rb
368
373
  - lib/triannon/jsonld_context.rb
369
374
  - lib/triannon/version.rb
370
375
  homepage: