triannon 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: