SysMO-DB/seek

View on GitHub
lib/seek/rdf/rdf_generation.rb

Summary

Maintainability
C
7 hrs
Test Coverage
require 'csv'


module Seek
  module Rdf
    module RdfGeneration
      include RdfRepositoryStorage
      include RightField

      def self.included(base)
        base.after_save :create_rdf_generation_job
        base.before_destroy :remove_rdf
      end

      def to_rdf
        rdf_graph = to_rdf_graph
        RDF::Writer.for(:rdfxml).buffer(:prefixes=>ns_prefixes) do |writer|
          rdf_graph.each_statement do |statement|
            writer << statement
          end
        end
      end

      def to_rdf_graph
        rdf_graph = handle_rightfield_contents self
        rdf_graph = describe_type(rdf_graph)
        rdf_graph = generate_from_csv_definitions rdf_graph
        rdf_graph = additional_triples rdf_graph
        rdf_graph
      end

      def handle_rightfield_contents object
        graph = nil
        if (object.respond_to?(:contains_extractable_spreadsheet?) && object.contains_extractable_spreadsheet?)
          begin
            graph = generate_rightfield_rdf_graph(self)
          rescue Exception=>e
            Rails.logger.error "Error generating RightField part of rdf for #{object} - #{e.message}"
          end
        end
        graph || RDF::Graph.new
      end

      def rdf_resource
        uri = Seek::Config.site_base_host+"/#{self.class.name.tableize}/#{self.id}"
        RDF::Resource.new(uri)
      end


      #extra steps that cannot be easily handled by the csv template
      def additional_triples rdf_graph
        if self.is_a?(Model) && self.contains_sbml?
          rdf_graph << [self.rdf_resource,JERMVocab.hasFormat,JERMVocab.SBML_format]
        end
        rdf_graph
      end

      def generate_from_csv_definitions rdf_graph
        #load template
        path_to_template=File.join(File.dirname(__FILE__), "rdf_mappings.csv")
        rows = Rails.cache.fetch("rdf_definitions",:expires_in=>1.hour) do
          CSV.read(path_to_template)
        end
        rows.each do |row|
          unless row[0].downcase=="class"
            klass=row[0].strip
            method=row[1]
            property=row[2]
            uri_or_literal=row[3].downcase
            transform=row[4]
            collection_transform=row[5]
            if (klass=="*" || self.class.name==klass) && self.respond_to?(method)
              rdf_graph = generate_triples(self,method,property,uri_or_literal,transform,collection_transform,rdf_graph)
            elsif self.class.name==klass #matched the class but the method isnt found
              puts "WARNING: Expected to find method #{method} for class #{klass}"
            end
          end
        end
        rdf_graph
      end

      def generate_triples subject, method, property,uri_or_literal,transformation,collection_transform,rdf_graph
        resource = subject.rdf_resource
        transform = transformation.strip unless transformation.nil?
        collection_transform = collection_transform.strip unless collection_transform.nil?
        items = subject.send(method)
        items = [items] unless items.kind_of?(Array) #may be an array of items or a single item. Cant use Array(item) here cos it screws up timezones and strips out nils
        unless collection_transform.blank?
          items = eval("items.#{collection_transform}")
        end
        items.each do |item|
          property_uri = eval(property)
          if !transformation.blank?
            item = eval(transformation)
          end
          o = if uri_or_literal.downcase=="u"
                if item.respond_to?(:rdf_resource)
                  item.rdf_resource
                else
                  uri = RDF::URI.new(item)
                  begin
                    uri.validate!
                  rescue
                    nil
                  end
                end
          else
            item.nil? ? "" : item
          end
          unless o.nil?
            rdf_graph << [resource,property_uri,o]
          end

        end
        rdf_graph
      end

      def describe_type rdf_graph
       it_is = JERMVocab.for_type self
       unless it_is.nil?
         resource = self.rdf_resource
         rdf_graph <<  [resource,RDF.type,it_is]
       end
       rdf_graph
      end

      #the hash of namespace prefixes to pass to the RDF::Writer when generating the RDF
      def ns_prefixes
        {
            "jerm"=>JERMVocab.to_uri.to_s,
            "dcterms"=>RDF::DC.to_uri.to_s,
            "owl"=>RDF::OWL.to_uri.to_s,
            "foaf"=>RDF::FOAF.to_uri.to_s,
            "sioc"=>RDF::SIOC.to_uri.to_s,
            "owl"=>RDF::OWL.to_uri.to_s,
        }
      end

      def create_rdf_generation_job force=false,refresh_dependents=true
        unless !force && (self.changed - ["updated_at","last_used_at"]).empty?
          RdfGenerationJob.create_job self,refresh_dependents
        end
      end

      def remove_rdf
        self.remove_rdf_from_repository if self.rdf_repository_configured?
        self.delete_rdf_file
        refresh_dependents_rdf
      end

      def refresh_dependents_rdf
        dependent_items.each do |item|
          item.refresh_rdf if item.respond_to?(:refresh_rdf)
        end
      end

      def dependent_items
        items = []
        #FIXME: this should go into a seperate mixin for active-record
        methods=[:data_files,:sops,:models,:publications,
                 :data_file_masters, :sop_masters, :model_masters,
                 :assets,
                 :assays, :studies, :investigations,
                 :institutions, :creators, :owners,:owner, :contributors, :contributor,:projects, :events, :presentations,
                 :samples, :specimens, :compounds, :organisms, :strains,
                ]
        methods.each do |method|
          if self.respond_to?(method)
            deps = Array(self.send(method))
            #resolve User back to Person
            deps = deps.collect{|dep| dep.is_a?(User) ? [dep, dep.person] : dep }.flatten.compact
            items = items | deps
          end
        end

        items.compact.uniq

        if (self.rdf_repository_configured?)
          items = items | related_items_from_sparql
        end

        items.compact.uniq
      end

      def refresh_rdf
        create_rdf_generation_job(true, false)
      end

    end
  end
end