Commit db219627f45001a8a4438229acd44114a3953856
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'virtuoso_integration' into stable
Showing
17 changed files
with
386 additions
and
0 deletions
Show diff stats
plugins/virtuoso/controllers/virtuoso_plugin_admin_controller.rb
0 → 100644
... | ... | @@ -0,0 +1,23 @@ |
1 | +class VirtuosoPluginAdminController < AdminController | |
2 | + | |
3 | + def index | |
4 | + settings = params[:settings] | |
5 | + settings ||= {} | |
6 | + @settings = Noosfero::Plugin::Settings.new(environment, VirtuosoPlugin, settings) | |
7 | + @harvest_running = VirtuosoPlugin::DspaceHarvest.new(environment).find_job.present? | |
8 | + | |
9 | + if request.post? | |
10 | + @settings.save! | |
11 | + session[:notice] = 'Settings successfully saved.' | |
12 | + redirect_to :action => 'index' | |
13 | + end | |
14 | + end | |
15 | + | |
16 | + def force_harvest | |
17 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
18 | + harvest.start(params[:from_start]) | |
19 | + session[:notice] = _('Harvest started') | |
20 | + redirect_to :action => :index | |
21 | + end | |
22 | + | |
23 | +end | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +class VirtuosoPlugin < Noosfero::Plugin | |
2 | + | |
3 | + def self.plugin_name | |
4 | + "Virtuoso integration" | |
5 | + end | |
6 | + | |
7 | + def self.plugin_description | |
8 | + _('Virtuoso integration') | |
9 | + end | |
10 | + | |
11 | + def content_types | |
12 | + [VirtuosoPlugin::TriplesTemplate] | |
13 | + end | |
14 | + | |
15 | + def settings | |
16 | + @settings ||= Noosfero::Plugin::Settings.new(context.environment, VirtuosoPlugin) | |
17 | + end | |
18 | + | |
19 | + def virtuoso_client | |
20 | + @virtuoso_client ||= RDF::Virtuoso::Repository.new("#{settings.virtuoso_uri}/sparql", :update_uri => "#{settings.virtuoso_uri}/sparql-auth", :username => settings.virtuoso_username, :password => settings.virtuoso_password, :auth_method => 'digest', :timeout => 30) | |
21 | + end | |
22 | + | |
23 | + def stylesheet? | |
24 | + true | |
25 | + end | |
26 | + | |
27 | +end | ... | ... |
... | ... | @@ -0,0 +1,80 @@ |
1 | +#inspired by https://github.com/code4lib/ruby-oai/blob/master/lib/oai/harvester/harvest.rb | |
2 | +class VirtuosoPlugin::DspaceHarvest | |
3 | + | |
4 | + DC_CONVERSION = [:title, :creator, :subject, :description, :date, :type, :identifier, :language, :rights, :format] | |
5 | + | |
6 | + def initialize(environment) | |
7 | + @environment = environment | |
8 | + end | |
9 | + | |
10 | + attr_reader :environment | |
11 | + | |
12 | + def plugin | |
13 | + @plugin ||= VirtuosoPlugin.new(self) | |
14 | + end | |
15 | + | |
16 | + delegate :settings, :to => :plugin | |
17 | + | |
18 | + def dspace_client | |
19 | + @dspace_client ||= OAI::Client.new("#{settings.dspace_uri}/oai/request") | |
20 | + end | |
21 | + | |
22 | + def triplify(record) | |
23 | + metadata = VirtuosoPlugin::DublinCoreMetadata.new(record.metadata) | |
24 | + puts "triplify #{record.header.identifier}" | |
25 | + | |
26 | + DC_CONVERSION.each do |c| | |
27 | + values = [metadata.send(c)].flatten.compact | |
28 | + values.each do |value| | |
29 | + query = RDF::Virtuoso::Query.insert_data([RDF::URI.new(metadata.identifier), RDF::URI.new("http://purl.org/dc/elements/1.1/#{c}"), value]).graph(RDF::URI.new(settings.dspace_uri)) | |
30 | + plugin.virtuoso_client.insert(query) | |
31 | + end | |
32 | + end | |
33 | + end | |
34 | + | |
35 | + def run | |
36 | + harvest_time = Time.now.utc | |
37 | + params = settings.last_harvest ? {:from => settings.last_harvest.utc} : {} | |
38 | + puts "starting harvest #{params} #{settings.dspace_uri} #{settings.virtuoso_uri}" | |
39 | + begin | |
40 | + records = dspace_client.list_records(params) | |
41 | + records.each do |record| | |
42 | + triplify(record) | |
43 | + end | |
44 | + rescue OAI::Exception => ex | |
45 | + puts ex.to_s | |
46 | + if ex.code != 'noRecordsMatch' | |
47 | + puts "unexpected error" | |
48 | + raise ex | |
49 | + end | |
50 | + end | |
51 | + settings.last_harvest = harvest_time | |
52 | + settings.save! | |
53 | + puts "ending harvest #{harvest_time}" | |
54 | + end | |
55 | + | |
56 | + def start(from_start = false) | |
57 | + if find_job.empty? | |
58 | + if from_start | |
59 | + settings.last_harvest = nil | |
60 | + settings.save! | |
61 | + end | |
62 | + | |
63 | + job = VirtuosoPlugin::DspaceHarvest::Job.new(@environment.id) | |
64 | + Delayed::Job.enqueue(job) | |
65 | + end | |
66 | + end | |
67 | + | |
68 | + def find_job | |
69 | + Delayed::Job.where(:handler => "--- !ruby/struct:VirtuosoPlugin::DspaceHarvest::Job\nenvironment_id: #{@environment.id}\n") | |
70 | + end | |
71 | + | |
72 | + class Job < Struct.new(:environment_id) | |
73 | + def perform | |
74 | + environment = Environment.find(environment_id) | |
75 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
76 | + harvest.run | |
77 | + end | |
78 | + end | |
79 | + | |
80 | +end | ... | ... |
plugins/virtuoso/lib/virtuoso_plugin/dublin_core_metadata.rb
0 → 100644
... | ... | @@ -0,0 +1,20 @@ |
1 | +class VirtuosoPlugin::DublinCoreMetadata | |
2 | + | |
3 | + include OAI::XPath | |
4 | + | |
5 | + attr_accessor :date, :title, :creator, :subject, :description, :date, :type, :identifier, :language, :rights, :format | |
6 | + | |
7 | + def initialize(element) | |
8 | + @title = xpath(element, './/dc:title') | |
9 | + @creator = xpath(element, './/dc:creator') | |
10 | + @subject = xpath_all(element, './/dc:subject').map(&:text) | |
11 | + @description = xpath(element, './/dc:description') | |
12 | + @date = xpath(element, './/dc:date') | |
13 | + @type = xpath(element, './/dc:type') | |
14 | + @identifier = xpath(element, './/dc:identifier') | |
15 | + @language = xpath(element, './/dc:language') | |
16 | + @rights = xpath_all(element, './/dc:rights').map(&:text) | |
17 | + @format = xpath(element, './/dc:format') | |
18 | + end | |
19 | + | |
20 | +end | ... | ... |
plugins/virtuoso/lib/virtuoso_plugin/triples_template.rb
0 → 100644
... | ... | @@ -0,0 +1,38 @@ |
1 | +class VirtuosoPlugin::TriplesTemplate < Article | |
2 | + | |
3 | + def self.short_description | |
4 | + _('Triples template') | |
5 | + end | |
6 | + | |
7 | + def self.description | |
8 | + _('Triples template') | |
9 | + end | |
10 | + | |
11 | + settings_items :query, :type => :string | |
12 | + settings_items :template, :type => :string | |
13 | + | |
14 | + attr_accessible :query, :template | |
15 | + | |
16 | + def to_html(options = {}) | |
17 | + article = self | |
18 | + proc do | |
19 | + render :file => 'content_viewer/triples_template', :locals => {:article => article} | |
20 | + end | |
21 | + end | |
22 | + | |
23 | + def plugin | |
24 | + @plugin ||= VirtuosoPlugin.new(self) | |
25 | + end | |
26 | + | |
27 | + def template_content | |
28 | + begin | |
29 | + results = plugin.virtuoso_client.query(query) | |
30 | + liquid_template = Liquid::Template.parse("{% for row in results %}#{template}{% endfor %}") | |
31 | + liquid_template.render('results' => results) | |
32 | + rescue => ex | |
33 | + logger.info ex.to_s | |
34 | + "Failed to process the template" | |
35 | + end | |
36 | + end | |
37 | + | |
38 | +end | ... | ... |
plugins/virtuoso/test/functional/virtuoso_plugin_admin_controller_test.rb
0 → 100644
... | ... | @@ -0,0 +1,45 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class VirtuosoPluginAdminControllerTest < ActionController::TestCase | |
4 | + | |
5 | + def setup | |
6 | + @environment = Environment.default | |
7 | + @profile = create_user('profile').person | |
8 | + login_as(@profile.identifier) | |
9 | + end | |
10 | + | |
11 | + attr_reader :environment | |
12 | + | |
13 | + should 'save virtuoso plugin settings' do | |
14 | + post :index, :settings => {'virtuoso_uri' => 'http://virtuoso.noosfero.com', | |
15 | + 'virtuoso_username' => 'username', | |
16 | + 'virtuoso_password' => 'password', | |
17 | + 'dspace_uri' => 'http://dspace.noosfero.com'} | |
18 | + @settings = Noosfero::Plugin::Settings.new(environment.reload, VirtuosoPlugin) | |
19 | + assert_equal 'http://virtuoso.noosfero.com', @settings.settings[:virtuoso_uri] | |
20 | + assert_equal 'username', @settings.settings[:virtuoso_username] | |
21 | + assert_equal 'password', @settings.settings[:virtuoso_password] | |
22 | + assert_equal 'http://dspace.noosfero.com', @settings.settings[:dspace_uri] | |
23 | + assert_redirected_to :action => 'index' | |
24 | + end | |
25 | + | |
26 | + should 'redirect to index after save' do | |
27 | + post :index, :settings => {"virtuoso_uri" => 'http://virtuoso.noosfero.com'} | |
28 | + assert_redirected_to :action => 'index' | |
29 | + end | |
30 | + | |
31 | + should 'create delayed job to start harvest on force action' do | |
32 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
33 | + assert !harvest.find_job.present? | |
34 | + get :force_harvest | |
35 | + assert harvest.find_job.present? | |
36 | + end | |
37 | + | |
38 | + should 'force harvest from start' do | |
39 | + get :force_harvest, :from_start => true | |
40 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
41 | + assert harvest.find_job.present? | |
42 | + assert_equal nil, harvest.settings.last_harvest | |
43 | + end | |
44 | + | |
45 | +end | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class DspaceHarvestTest < ActiveSupport::TestCase | |
4 | + | |
5 | + def setup | |
6 | + @environment = Environment.default | |
7 | + end | |
8 | + | |
9 | + attr_reader :environment | |
10 | + | |
11 | + should 'create delayed job when start' do | |
12 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
13 | + assert !harvest.find_job.present? | |
14 | + harvest.start | |
15 | + assert harvest.find_job.present? | |
16 | + end | |
17 | + | |
18 | + should 'not duplicate harvest job' do | |
19 | + harvest = VirtuosoPlugin::DspaceHarvest.new(environment) | |
20 | + assert_difference "harvest.find_job.count", 1 do | |
21 | + 5.times { harvest.start } | |
22 | + end | |
23 | + end | |
24 | + | |
25 | +end | ... | ... |
... | ... | @@ -0,0 +1,29 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class TriplesTemplateTest < ActiveSupport::TestCase | |
4 | + | |
5 | + def setup | |
6 | + @article = VirtuosoPlugin::TriplesTemplate.new | |
7 | + end | |
8 | + | |
9 | + attr_reader :article | |
10 | + | |
11 | + should 'evaluate template using query results' do | |
12 | + article.stubs(:plugin).returns(mock) | |
13 | + article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | |
14 | + article.plugin.virtuoso_client.expects(:query).returns([{'var' => 'Hello '}, {'var' => 'World'}]) | |
15 | + article.template = "{{row.var}}" | |
16 | + | |
17 | + assert_equal 'Hello World', article.template_content | |
18 | + end | |
19 | + | |
20 | + should 'display error message when failed to execute the query' do | |
21 | + article.stubs(:plugin).returns(mock) | |
22 | + article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | |
23 | + article.plugin.virtuoso_client.expects(:query).raises(RuntimeError.new) | |
24 | + article.template = "{{row.var}}" | |
25 | + | |
26 | + assert_equal "Failed to process the template", article.template_content | |
27 | + end | |
28 | + | |
29 | +end | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class VirtuosoPluginTest < ActiveSupport::TestCase | |
4 | + | |
5 | + def setup | |
6 | + @environment = Environment.default | |
7 | + @plugin = VirtuosoPlugin.new | |
8 | + end | |
9 | + | |
10 | + attr_reader :plugin | |
11 | + | |
12 | + should 'define a new content' do | |
13 | + assert_equal [VirtuosoPlugin::TriplesTemplate], plugin.content_types | |
14 | + end | |
15 | + | |
16 | +end | ... | ... |
plugins/virtuoso/views/cms/virtuoso_plugin/_triples_template.html.erb
0 → 100644
... | ... | @@ -0,0 +1,19 @@ |
1 | +<div class="virtuoso-triples-template"> | |
2 | + <%= required_fields_message %> | |
3 | + | |
4 | + <%= render :file => 'shared/tiny_mce' %> | |
5 | + | |
6 | + <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %> | |
7 | + <%= labelled_form_field(_('SPARQL Query'), text_area(:article, :query, :style => 'width: 98%; height: 120px;')) %> | |
8 | + | |
9 | + <div class="template"> | |
10 | + <span class="label"><%= _('Template') %></span> | |
11 | + <span class="reference" style="float: right;"><a href="https://github.com/Shopify/liquid/wiki/Liquid-for-Designers"><%= _('Template reference') %></a></span> | |
12 | + <span class="input"> | |
13 | + <%= text_area(:article, :template, :style => 'width: 98%; height: 200px;', :class => 'mceEditor') %> | |
14 | + </span> | |
15 | + </div> | |
16 | + | |
17 | + <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true} %> | |
18 | + <%= render :partial => 'general_fields' %> | |
19 | +</div> | ... | ... |
plugins/virtuoso/views/content_viewer/triples_template.html.erb
0 → 100644
plugins/virtuoso/views/virtuoso_plugin_admin/index.html.erb
0 → 100644
... | ... | @@ -0,0 +1,35 @@ |
1 | +<h1><%= _('Virtuoso settings')%></h1> | |
2 | + | |
3 | +<%= form_for(:settings) do |f| %> | |
4 | + | |
5 | + <strong> | |
6 | + <%= labelled_form_field _('Virtuoso URL:'), f.text_field(:virtuoso_uri) %> | |
7 | + <%= labelled_form_field _('Virtuoso Username:'), f.text_field(:virtuoso_username) %> | |
8 | + <%= labelled_form_field _('Virtuoso Password:'), f.password_field(:virtuoso_password) %> | |
9 | + <%= labelled_form_field _('DSpace URL:'), f.text_field(:dspace_uri) %> | |
10 | + </strong> | |
11 | + | |
12 | + <% button_bar do %> | |
13 | + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %> | |
14 | + <% end %> | |
15 | + | |
16 | +<% end %> | |
17 | + | |
18 | +<hr/> | |
19 | +<div class="harvest"> | |
20 | + <% if @settings.last_harvest %> | |
21 | + <div class="date"> | |
22 | + <span class="label"><strong><%= _('Last execution:') %></strong></span> | |
23 | + <span class="value"><%= time_ago_as_sentence @settings.last_harvest %></span> | |
24 | + </div> | |
25 | +<% end %> | |
26 | + <br/> | |
27 | + <div class="actions"> | |
28 | + <% if @harvest_running %> | |
29 | + <%= _('Running...') %> | |
30 | + <% else %> | |
31 | + <%= button :next, _('Force harvest'), :action => :force_harvest %> | |
32 | + <%= button :next, _('Force harvest from start'), :action => :force_harvest, :from_start => true %> | |
33 | + <% end %> | |
34 | + </div> | |
35 | +</div> | ... | ... |