publish_my_data 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/README.md +2 -3
  2. data/app/controllers/publish_my_data/application_controller.rb +21 -32
  3. data/app/controllers/publish_my_data/datasets_controller.rb +7 -19
  4. data/app/controllers/publish_my_data/resources_controller.rb +18 -24
  5. data/app/controllers/publish_my_data/sparql_controller.rb +6 -3
  6. data/app/controllers/publish_my_data/themes_controller.rb +28 -0
  7. data/app/helpers/publish_my_data/resources_helper.rb +2 -2
  8. data/app/models/publish_my_data/concept.rb +14 -0
  9. data/app/models/publish_my_data/concept_scheme.rb +16 -0
  10. data/app/models/publish_my_data/dataset.rb +48 -13
  11. data/app/models/publish_my_data/ontology.rb +35 -0
  12. data/app/models/publish_my_data/ontology_class.rb +11 -0
  13. data/app/models/publish_my_data/property.rb +11 -0
  14. data/app/models/publish_my_data/rdf_type.rb +1 -0
  15. data/app/models/publish_my_data/resource.rb +80 -6
  16. data/app/models/publish_my_data/theme.rb +34 -0
  17. data/app/views/layouts/publish_my_data/application.html.erb +0 -1
  18. data/app/views/layouts/publish_my_data/error.html.erb +12 -2
  19. data/app/views/publish_my_data/concept_schemes/_concepts.html.erb +3 -0
  20. data/app/views/publish_my_data/concept_schemes/show.html.erb +9 -0
  21. data/app/views/publish_my_data/concepts/show.html.erb +3 -0
  22. data/app/views/publish_my_data/datasets/index.html.erb +4 -4
  23. data/app/views/publish_my_data/datasets/show.html.erb +1 -1
  24. data/app/views/publish_my_data/errors/bad_request.html.erb +1 -0
  25. data/app/views/publish_my_data/errors/not_found.html.erb +1 -1
  26. data/app/views/publish_my_data/errors/timeout.html.erb +1 -0
  27. data/app/views/publish_my_data/errors/uncaught.html.erb +6 -0
  28. data/app/views/publish_my_data/ontologies/_classes.html.erb +3 -0
  29. data/app/views/publish_my_data/ontologies/_properties.html.erb +3 -0
  30. data/app/views/publish_my_data/ontologies/show.html.erb +17 -0
  31. data/app/views/publish_my_data/properties/show.html.erb +3 -0
  32. data/app/views/publish_my_data/resources/_predicates_table.html.erb +12 -4
  33. data/app/views/publish_my_data/resources/_resource_formats.html.erb +5 -5
  34. data/app/views/publish_my_data/resources/_uri_and_label.html.erb +2 -2
  35. data/app/views/publish_my_data/resources/index.html.erb +4 -4
  36. data/app/views/publish_my_data/resources/show.html.erb +3 -3
  37. data/app/views/publish_my_data/sparql/_pagination.html.erb +2 -2
  38. data/app/views/publish_my_data/themes/index.html.erb +12 -0
  39. data/app/views/publish_my_data/themes/show.html.erb +15 -0
  40. data/config/initializers/{tripod.rb → 00_tripod.rb} +0 -0
  41. data/config/initializers/10_vocabularies.rb +6 -0
  42. data/config/initializers/zz_factories.rb +7 -0
  43. data/config/routes.rb +4 -5
  44. data/lib/publish_my_data/defined_by_ontology.rb +10 -0
  45. data/lib/publish_my_data/paginator.rb +63 -0
  46. data/lib/publish_my_data/render_params/concept_render_params.rb +14 -0
  47. data/lib/publish_my_data/render_params/concept_scheme_render_params.rb +14 -0
  48. data/lib/publish_my_data/render_params/ontology_class_render_params.rb +14 -0
  49. data/lib/publish_my_data/render_params/ontology_render_params.rb +14 -0
  50. data/lib/publish_my_data/render_params/property_render_params.rb +14 -0
  51. data/lib/publish_my_data/render_params/resource_render_params.rb +13 -0
  52. data/lib/publish_my_data/render_params/theme_render_params.rb +23 -0
  53. data/lib/publish_my_data/render_params.rb +7 -0
  54. data/lib/publish_my_data/resource_module.rb +8 -0
  55. data/lib/publish_my_data/version.rb +1 -1
  56. data/lib/publish_my_data.rb +6 -1
  57. data/spec/controllers/publish_my_data/datasets_controller_spec.rb +0 -10
  58. data/spec/controllers/publish_my_data/resources_controller_spec.rb +78 -38
  59. data/spec/controllers/publish_my_data/sparql_controller_spec.rb +4 -4
  60. data/spec/controllers/publish_my_data/themes_controller_spec.rb +110 -0
  61. data/spec/dummy/log/test.log +80255 -0
  62. data/spec/factories/00_theme_factories.rb +11 -0
  63. data/spec/factories/concept_scheme_factories.rb +48 -0
  64. data/spec/factories/dataset_factories.rb +10 -1
  65. data/spec/factories/ontology_factories.rb +55 -0
  66. data/spec/factories/property_factories.rb +2 -2
  67. data/spec/factories/resource_factories.rb +5 -5
  68. metadata +61 -16
  69. data/app/assets/javascripts/publish_my_data/resources.js +0 -2
  70. data/app/assets/stylesheets/publish_my_data/resources.css +0 -4
  71. data/app/views/publish_my_data/datasets/themes.html.erb +0 -3
  72. data/config/initializers/vocabularies.rb +0 -1
data/README.md CHANGED
@@ -8,8 +8,7 @@ The PublishMyData Community Edition is a Rails Engine that adds Linked Data func
8
8
  - A SPARQL Endpoint
9
9
  - Filterable lists of datasets and resources
10
10
 
11
- This is the same code that powers the enterprise, hosted version of PublishMyData. For more details see the [PublishMyData](http://publishmydata.com) website.
12
-
11
+ This is the same core code that powers the enterprise, hosted version of PublishMyData. For more details see the [PublishMyData](http://publishmydata.com) website.
13
12
 
14
13
  ## How to use
15
14
 
@@ -36,7 +35,7 @@ This is the same code that powers the enterprise, hosted version of PublishMyDat
36
35
  - See the Rails guides for [more details on Rails Engines](http://guides.rubyonrails.org/engines.html).
37
36
  - PublishMyData doesn't supply a database. You need to install one. I recommend [Fuseki](http://jena.apache.org/documentation/serving_data/index.html), which runs on port 3030 by default.
38
37
  - The views currently supplied by this Rails engine are very rudimentary. Some nicer default views coming soon, but for now you'll probably just want to override them all in your app.
39
- - Warning: This gem is usable now, but the API is under constant development and flux at the moment
38
+ - Warning: This gem is usable now, but the API is under constant development and flux at the moment, and tbh it's not very well documented! :)
40
39
 
41
40
  ## Licence
42
41
 
@@ -1,48 +1,37 @@
1
1
  module PublishMyData
2
2
  class ApplicationController < ActionController::Base
3
3
 
4
- rescue_from Tripod::Errors::ResourceNotFound, :with => :resource_not_found
5
-
6
- # TODO: handle:
7
- # 500s, timeouts (503) etc.
4
+ # Note: this order matters. Perversely, need to put more general errors first.
5
+ rescue_from Exception, :with => :handle_uncaught_error
6
+ rescue_from Tripod::Errors::ResourceNotFound, :with => :handle_resource_not_found
7
+ rescue_from RestClient::RequestTimeout, :with => :handle_timeout
8
8
 
9
9
  private
10
10
 
11
- def resource_not_found(e)
11
+ # TODO: deal with javaascript errors - respond with 200
12
+
13
+ def handle_uncaught_error(e)
14
+ @e = e
15
+ # TODO: notify error handling service.
12
16
  respond_to do |format|
13
- format.html { render(:template => "publish_my_data/errors/not_found", :layout => 'publish_my_data/error', :status => 404) and return false }
14
- #TODO: ? format.js { render(:template => "publish_my_data/errors/not_found", :status => 200) and return false } # need to return success or the ajax request fails
15
- format.any { head(:status => 404, :content_type => 'text/plain') and return false }
17
+ format.html { render(:template => "publish_my_data/errors/uncaught", :layout => 'publish_my_data/error', :status => 500) and return false }
18
+ format.any{ head(:status => 500, :content_type => 'text/plain') and return false }
16
19
  end
17
- end
18
-
19
- # from the criteria passed in, sets an instance var for @count and return
20
- # a Kaminari::PaginatableArray, or Array (as appropriate to the format)
21
- def paginate_resources(criteria)
22
20
 
23
- get_pagination_params unless @got_pagination_params
24
-
25
- @count = criteria.count #this has to happen first, before we modify the criteria with limit/offset
26
- resources = criteria.limit(@limit).offset(@offset).resources
21
+ end
27
22
 
28
- if request.format.html?
29
- Kaminari.paginate_array(resources.to_a, total_count: @count).page(@page).per(@limit)
30
- else
31
- resources # non html versions just need the raw array
23
+ def handle_timeout(e)
24
+ respond_to do |format|
25
+ format.html { render(:template => "publish_my_data/errors/timeout", :layout => 'publish_my_data/error', :status => 503) and return false }
26
+ format.any { head(:status => 503, :content_type => 'text/plain') and return false }
32
27
  end
33
28
  end
34
29
 
35
- def get_pagination_params
36
- default_page_size = 20
37
-
38
- @per_page = (params[:per_page] || default_page_size).to_i
39
- @per_page = 10000 if @per_page > 10000
40
- @page = (params[:page] || 1).to_i
41
-
42
- @limit = @per_page
43
- @offset = @limit.to_i * (@page.to_i-1)
44
-
45
- @got_pagination_params = true
30
+ def handle_resource_not_found(e)
31
+ respond_to do |format|
32
+ format.html { render(:template => "publish_my_data/errors/not_found", :layout => 'publish_my_data/error', :status => 404) and return false }
33
+ format.any { head(:status => 404, :content_type => 'text/plain') and return false }
34
+ end
46
35
  end
47
36
 
48
37
  end
@@ -7,10 +7,14 @@ module PublishMyData
7
7
 
8
8
  # /datasets/:id (where :id is the dataset 'slug')
9
9
  def show
10
+
10
11
  @dataset = Dataset.find_by_slug(params[:id])
12
+
13
+ @dataset.eager_load_object_triples! # for the owner URI label
14
+
11
15
  @types = RdfType.where('?s a ?uri').graph(@dataset.data_graph_uri).resources
12
16
 
13
- if request.format.html?
17
+ if request.format && request.format.html?
14
18
  @type_resource_counts = {}
15
19
  @types.each do |t|
16
20
  @type_resource_counts[t.uri.to_s] = Resource.where("?uri a <#{t.uri.to_s}>").count
@@ -21,28 +25,12 @@ module PublishMyData
21
25
  end
22
26
 
23
27
  # /datasets?page=2&per_page=10
24
- # /datasets?theme=foo
25
28
  def index
26
29
  dataset_criteria = Dataset.all
27
- dataset_criteria = add_theme_filter(dataset_criteria)
28
- @datasets = paginate_resources(dataset_criteria)
30
+ @pagination_params = PaginationParams.from_request(request)
31
+ @datasets = Paginator.new(dataset_criteria, @pagination_params).paginate
29
32
  respond_with(@datasets)
30
33
  end
31
34
 
32
- def themes
33
- # TODO: decide on how the themes will be represented in the metadata.
34
- # Get a list of themes.
35
- @themes = ['theme1', 'theme2']
36
- end
37
-
38
- private
39
-
40
- def add_theme_filter(criteria)
41
- unless params[:theme].blank?
42
- @theme = params[:theme]
43
- criteria.where("?uri <#{PMD_DS.theme}> '#{@theme}'")
44
- end
45
- criteria
46
- end
47
35
  end
48
36
  end
@@ -14,7 +14,8 @@ module PublishMyData
14
14
  resource_criteria = add_type_filter(resource_criteria)
15
15
  resource_criteria = add_dataset_filter(resource_criteria)
16
16
 
17
- @resources = paginate_resources(resource_criteria)
17
+ @pagination_params = PaginationParams.from_request(request)
18
+ @resources = Paginator.new(resource_criteria, @pagination_params).paginate
18
19
  respond_with(@resources)
19
20
  end
20
21
 
@@ -22,10 +23,7 @@ module PublishMyData
22
23
  def show
23
24
  uri = params[:uri]
24
25
  begin
25
- # try to look it up
26
- @resource = Resource.find(uri)
27
- eager_load_labels()
28
- respond_with(@resource)
26
+ render_resource_with_uri(uri)
29
27
  rescue Tripod::Errors::ResourceNotFound
30
28
  # if it's not there
31
29
  respond_to do |format|
@@ -50,29 +48,30 @@ module PublishMyData
50
48
  # http://example.com/doc/blah
51
49
  def doc
52
50
  uri = Resource.uri_from_host_and_doc_path(request.host, params[:path], params[:format])
53
- @resource = Resource.find(uri)
54
- eager_load_labels() if request.format.html?
55
-
56
- # TODO: special views like ontology, dataset, etc?
57
- respond_with(@resource) do |format|
58
- format.html { render :template => 'publish_my_data/resources/show' }
59
- end
51
+ render_resource_with_uri(uri)
60
52
  end
61
53
 
62
54
  # http://example.com/def/blah
63
55
  def definition
64
56
  uri = 'http://' + request.host + '/def/' + params[:path]
65
- @resource = Resource.find(uri)
66
- eager_load_labels() if request.format.html?
67
-
68
- # TODO: special views like ontology, dataset, etc?
69
- respond_with(@resource) do |format|
70
- format.html { render :template => 'publish_my_data/resources/show' }
71
- end
57
+ render_resource_with_uri(uri)
72
58
  end
73
59
 
74
60
  private
75
61
 
62
+ def render_resource_with_uri(uri)
63
+ resource = Resource.find(uri)
64
+
65
+ if request.format.html?
66
+ resource.eager_load_predicate_triples!
67
+ resource.eager_load_object_triples!
68
+ end
69
+
70
+ respond_with(resource) do |format|
71
+ format.html { render resource.render_params(request) }
72
+ end
73
+ end
74
+
76
75
  # TODO: move the filter management into an object
77
76
  def add_type_filter(criteria)
78
77
  unless params[:type_uri].blank?
@@ -92,11 +91,6 @@ module PublishMyData
92
91
  criteria
93
92
  end
94
93
 
95
- def eager_load_labels
96
- @resource.eager_load_predicate_triples!
97
- @resource.eager_load_object_triples!
98
- end
99
-
100
94
  end
101
95
 
102
96
 
@@ -15,9 +15,12 @@ module PublishMyData
15
15
  @sparql_query = PublishMyData::SparqlQuery.new(@query_text, request.format.to_sym)
16
16
 
17
17
  if @sparql_query.allow_pagination?
18
- get_pagination_params
19
- @sparql_query_result = @sparql_query.paginate(@page, @per_page)
20
- @more_pages = @sparql_query.as_pagination_query(@page, @per_page, 1).count > @per_page if request.format.html?
18
+ @pagination_params = PaginationParams.from_request(request)
19
+ @sparql_query_result = @sparql_query.paginate(@pagination_params.page, @pagination_params.per_page)
20
+ if request.format.html?
21
+ count = @sparql_query.as_pagination_query(@pagination_params.page, @pagination_params.per_page, 1).count
22
+ @more_pages = (count > @pagination_params.per_page)
23
+ end
21
24
  else
22
25
  @sparql_query_result = @sparql_query.execute
23
26
  end
@@ -0,0 +1,28 @@
1
+ require_dependency "publish_my_data/application_controller"
2
+
3
+ module PublishMyData
4
+ class ThemesController < ApplicationController
5
+
6
+ respond_to :html, :ttl, :rdf, :nt, :json
7
+
8
+ def index
9
+ # don't bother paginating this for now - there probably wont be that many themes
10
+ @themes = Theme.all.resources
11
+ respond_with(@themes)
12
+ end
13
+
14
+ def show
15
+ @theme = Theme.by_slug(params[:id])
16
+
17
+ if @theme
18
+ @pagination_params = PaginationParams.from_request(request)
19
+ @datasets = Paginator.new(@theme.datasets_criteria, @pagination_params).paginate
20
+ respond_with(@datasets)
21
+ else
22
+ raise Tripod::Errors::ResourceNotFound
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -2,10 +2,10 @@ module PublishMyData
2
2
  module ResourcesHelper
3
3
 
4
4
  # uses eager loaded data to get the uri or label for a term
5
- def resource_uri_or_label(term)
5
+ def resource_uri_or_label(resource, term)
6
6
 
7
7
  if term.uri?
8
- res = @resource.get_related_resource(term, PublishMyData::Resource)
8
+ res = resource.get_related_resource(term, PublishMyData::Resource)
9
9
  if res
10
10
  link_to((res.label || res.uri.to_s), resource_path_from_uri(res.uri))
11
11
  else
@@ -0,0 +1,14 @@
1
+ module PublishMyData
2
+ class Concept
3
+ include Tripod::Resource
4
+ include PublishMyData::ResourceModule
5
+ field :label, RDF::RDFS.label
6
+ field :in_scheme, RDF::SKOS.inScheme
7
+
8
+ rdf_type RDF::SKOS.Concept
9
+
10
+ def concept_scheme
11
+ ConceptScheme.find(self.in_scheme) rescue nil if self.in_scheme
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,21 @@
1
1
  module PublishMyData
2
2
  class ConceptScheme
3
3
  include Tripod::Resource
4
+ include PublishMyData::ResourceModule #some common methods for resources.
5
+
6
+ rdf_type RDF::SKOS.ConceptScheme
7
+ field :label, RDF::RDFS.label
8
+
9
+ def concepts
10
+ Resource.find_by_sparql(
11
+ "SELECT DISTINCT ?uri ?graph
12
+ WHERE {
13
+ GRAPH ?graph {
14
+ ?uri <#{RDF::SKOS.inScheme.to_s}> <#{self.uri}> .
15
+ ?uri a <#{RDF::SKOS.Concept.to_s}> .
16
+ }
17
+ }"
18
+ )
19
+ end
4
20
  end
5
21
  end
@@ -1,12 +1,38 @@
1
1
  module PublishMyData
2
2
  class Dataset
3
3
  include Tripod::Resource
4
+ include PublishMyData::ResourceModule #some common methods for resources.
4
5
 
6
+ # basics
5
7
  field :title, RDF::DC.title
6
- field :description, RDF::DC.description
7
- field :theme, PMD_DS.theme
8
+ field :comment, RDF::RDFS.comment #short desc
9
+ field :description, RDF::DC.description # long desc
8
10
 
9
- rdf_type PMD_DS.Dataset
11
+ # licence, owner, contact
12
+ field :publisher, RDF::DC.publisher # value is a URI of a publisher
13
+ field :license, RDF::DC.license # value is URI of where licence is defined.
14
+ # NOTE: for contact, use :publisher's foaf:mbox value (in this metadata graph).
15
+
16
+ # quality, updates, maintenance
17
+ field :issued, RDF::DC.issued, :datatype => RDF::XSD.dateTime # value is DateTime literal
18
+ field :modified, RDF::DC.modified, :datatype => RDF::XSD.dateTime # value is DateTime literal
19
+ field :update_periodicity, RDF::DC.accrualPeriodicity # waiting for response on what the value should be.
20
+
21
+ # where to get it
22
+ field :data_dump, RDF::VOID.dataDump # full download URI
23
+
24
+ # what the data is about
25
+ field :theme, RDF::DCAT.theme
26
+ field :tags, RDF::DCAT.keyword, :multivalued => true # values are string literals
27
+
28
+ field :spatial_coverage, RDF::DC.spatial # value is a URI for region covered, e.g. England.
29
+ field :temporal_coverage, RDF::DC.temporal # value is a time interval URI
30
+ field :spatial_granularity, RDF::DC.spatial # value is class of the objects of refArea
31
+ field :temporal_granularity, RDF::DC.temporal # value is class of objects of refPeriod
32
+
33
+ field :size, RDF::VOID.triples # value is integer.
34
+
35
+ rdf_type RDF::PMD_DS.Dataset
10
36
 
11
37
  def slug
12
38
  Dataset.slug_from_uri(self.uri)
@@ -20,19 +46,25 @@ module PublishMyData
20
46
  slug
21
47
  end
22
48
 
23
- class << self
49
+ def resources_in_dataset_criteria
50
+ Resource.all.graph(self.data_graph_uri)
51
+ end
24
52
 
25
- def by_theme_criteria(theme)
26
- Dataset.where("?uri <#{PMD_DS.theme}> '#{theme}'")
27
- end
53
+ def resources_count
54
+ resources_in_dataset_criteria.count
55
+ end
28
56
 
29
- def count_by_theme(theme)
30
- by_theme_criteria(theme).count
31
- end
57
+ def theme_obj
58
+ Theme.find(self.theme) rescue nil
59
+ end
32
60
 
33
- def by_theme(theme)
34
- by_theme_criteria(theme).resources
35
- end
61
+ # use :publisher's foaf:mbox value (in this metadata graph).
62
+ def contact_email
63
+ publisher_obj = Resource.find(self.publisher) if publisher
64
+ publisher_obj.read_predicate(RDF::FOAF.mbox) if publisher_obj
65
+ end
66
+
67
+ class << self
36
68
 
37
69
  # this is the graph that dataset metadata goes in.
38
70
  def metadata_graph_uri(slug)
@@ -56,6 +88,9 @@ module PublishMyData
56
88
  uri.to_s.split('/').last
57
89
  end
58
90
 
91
+ def slug_from_data_graph_uri(data_graph_uri)
92
+ data_graph_uri.to_s.split("/").last
93
+ end
59
94
  end
60
95
  end
61
96
  end
@@ -1,5 +1,40 @@
1
1
  module PublishMyData
2
2
  class Ontology
3
3
  include Tripod::Resource
4
+ include PublishMyData::ResourceModule
5
+
6
+ rdf_type RDF::OWL.Ontology
7
+ field :label, RDF::RDFS.label
8
+
9
+ def ontology_classes
10
+ Resource.find_by_sparql("
11
+ SELECT DISTINCT ?uri ?graph
12
+ WHERE {
13
+ GRAPH ?graph {
14
+ {
15
+ ?uri <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <#{self.uri}> .
16
+ ?uri a <http://www.w3.org/2002/07/owl#Class> .
17
+ }
18
+ UNION
19
+ {
20
+ ?uri <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <#{self.uri}> .
21
+ ?uri a <http://www.w3.org/2000/01/rdf-schema#Class>
22
+ }
23
+ }
24
+ }"
25
+ )
26
+ end
27
+
28
+ def ontology_properties
29
+ Resource.find_by_sparql("
30
+ SELECT DISTINCT ?uri ?graph
31
+ WHERE {
32
+ GRAPH ?graph {
33
+ ?uri <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <#{self.uri}> .
34
+ ?uri a <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> .
35
+ }
36
+ }"
37
+ )
38
+ end
4
39
  end
5
40
  end
@@ -0,0 +1,11 @@
1
+ module PublishMyData
2
+ class OntologyClass
3
+ include Tripod::Resource
4
+ include PublishMyData::ResourceModule
5
+ include PublishMyData::DefinedByOntology
6
+
7
+ field :label, RDF::RDFS.label
8
+ field :defined_by, RDF::RDFS.isDefinedBy
9
+
10
+ end
11
+ end