require 'equivalent-xml' module Dor module Processable extend ActiveSupport::Concern include SolrDocHelper include Upgradable included do has_metadata :name => 'workflows', :type => Dor::WorkflowDs, :label => 'Workflows', :control_group => 'E' after_initialize :set_workflows_datastream_location end def set_workflows_datastream_location # This is a work-around for some strange logic in ActiveFedora that # don't allow self.workflows.new? to work if we load the object using # .load_instance_from_solr. return if self.respond_to? :inner_object and self.inner_object.is_a? ActiveFedora::SolrDigitalObject if self.workflows.new? workflows.mimeType = 'application/xml' workflows.dsLocation = File.join(Dor::Config.workflow.url,"dor/objects/#{self.pid}/workflows") end end def empty_datastream?(datastream) if datastream.new? true elsif datastream.class.respond_to?(:xml_template) datastream.content.to_s.empty? or EquivalentXml.equivalent?(datastream.content, datastream.class.xml_template) else datastream.content.to_s.empty? end end # Takes the name of a datastream, as a string. # Tries to find a file for the datastream. # Returns the path to it or nil. def find_metadata_file(datastream) druid = DruidTools::Druid.new(pid, Dor::Config.stacks.local_workspace_root) return druid.find_metadata("#{datastream}.xml") end # Takes the name of a datastream, as a string (fooMetadata). # Builds that datastream using the content of a file if such a file # exists and is newer than the object's current datastream; otherwise, # builds the datastream by calling build_fooMetadata_datastream. def build_datastream(datastream, force = false, is_required = false) # See if the datastream exists as a file and if the file's # timestamp is newer than the datastream's timestamp. ds = datastreams[datastream] filename = find_metadata_file(datastream) use_file = filename && (ds.createDate.nil? || File.mtime(filename) >= ds.createDate) # Build datastream. if use_file content = File.read(filename) ds.content = content ds.ng_xml = Nokogiri::XML(content) if ds.respond_to?(:ng_xml) ds.save unless ds.digital_object.new? elsif force or empty_datastream?(ds) meth = "build_#{datastream}_datastream".to_sym if respond_to?(meth) content = self.send(meth, ds) ds.save unless ds.digital_object.new? end end # Check for success. if is_required && empty_datastream?(ds) raise "Required datastream #{datastream} could not be populated!" end return ds end def cleanup() CleanupService.cleanup(self) end def milestones Dor::WorkflowService.get_milestones('dor',self.pid) end def status(include_time=false) current_version='1' begin current_version = self.versionMetadata.current_version_id rescue end status = 0 version = '' #verbage we want to use to describe an item when it has completed a particular step status_hash={ 0 => 'Unknown Status', #if there are no milestones for the current version, someone likely messed up the versioning process. 1 => 'Registered', 2 => 'In accessioning', 3 => 'In accessioning (described)', 4 => 'In accessioning (described, published)', 5 => 'In accessioning (described, published, deposited)', 6 => 'Accessioned', 7 => 'Accessioned (indexed)', 8 => 'Accessioned (indexed, ingested)', 9 => 'Opened' } #milestones from accesioning and the order they happen in steps={ 'registered' => 1, 'submitted' => 2, 'described' => 3, 'published' => 4, 'deposited' => 5, 'accessioned' => 6, 'indexed' => 7, 'shelved' => 8, 'opened' => 1 } status_time=nil current=false versions=[] result="" current_milestones = [] #only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version number, or they might be active (no version number) milestones.each do |m| if steps.keys.include?(m[:milestone]) and (m[:version].nil? or m[:version] == current_version) current_milestones << m unless m[:milestone] == 'registered' and current_version.to_i > 1 end end status = 0 status_time = '' #for each milestone in the current version, see if it comes after the current 'last' step, if so, make it the last and record the date/time current_milestones.each do |m| name=m[:milestone] time=m[:at].utc.xmlschema if steps.keys.include? name if steps[name] > status status = steps[name] status_time=time end end end #use the translation table to get the appropriate verbage for the latest step result='v'+current_version.to_s+' '+status_hash[status].to_s result +=" #{format_date(status_time)}" if include_time result end def to_solr(solr_doc=Hash.new, *args) super(solr_doc, *args) sortable_milestones = {} current_version='1' begin current_version = self.versionMetadata.current_version_id rescue end current_version_num=current_version.to_i if self.respond_to?('versionMetadata') #add an entry with version id, tag and description for each version while current_version_num > 0 add_solr_value(solr_doc, 'versions', current_version_num.to_s + ';' + self.versionMetadata.tag_for_version(current_version_num.to_s) + ';' + self.versionMetadata.description_for_version(current_version_num.to_s), :string, [:displayable]) current_version_num -= 1 end end self.milestones.each do |milestone| timestamp = milestone[:at].utc.xmlschema sortable_milestones[milestone[:milestone]] ||= [] sortable_milestones[milestone[:milestone]] << timestamp add_solr_value(solr_doc, 'lifecycle', milestone[:milestone], :string, [:searchable, :facetable]) unless milestone[:version] milestone[:version]=current_version end add_solr_value(solr_doc, 'lifecycle', "#{milestone[:milestone]}:#{timestamp};#{milestone[:version]}", :string, [:displayable]) end sortable_milestones.each do |milestone, unordered_dates| dates = unordered_dates.sort #create the published_dt and published_day fields and the like add_solr_value(solr_doc, milestone+'_day', DateTime.parse(dates.last).beginning_of_day.utc.xmlschema.split('T').first, :string, [:searchable, :facetable]) add_solr_value(solr_doc, milestone, dates.first, :date, [:searchable, :facetable]) #fields for OAI havester to sort on add_solr_value(solr_doc, "#{milestone}_earliest_dt", dates.first, :date, [:sortable]) add_solr_value(solr_doc, "#{milestone}_latest_dt", dates.last, :date, [:sortable]) #for future faceting add_solr_value(solr_doc, "#{milestone}_earliest", dates.first, :date, [:searchable, :facetable]) add_solr_value(solr_doc, "#{milestone}_latest", dates.last, :date, [ :searchable, :facetable]) end add_solr_value(solr_doc,"status",status,:string, [:displayable]) if sortable_milestones['opened'] #add a facetable field for the date when the open version was opened opened_date=sortable_milestones['opened'].sort.last add_solr_value(solr_doc, "version_opened", DateTime.parse(opened_date).beginning_of_day.utc.xmlschema.split('T').first, :string, [ :searchable, :facetable]) end add_solr_value(solr_doc, "current_version", current_version.to_s, :string, [ :displayable , :facetable]) add_solr_value(solr_doc, "last_modified_day", self.modified_date.to_s.split('T').first, :string, [ :facetable ]) add_solr_value(solr_doc, "rights", rights, :string, [:facetable]) if self.respond_to? :rights solr_doc end # Initilizes workflow for the object in the workflow service # It will set the priorty of the new workflow to the current_priority if it is > 0 # @param [String] name of the workflow to be initialized # @param [String] repo name of the repository to create workflow for # @param [Boolean] create_ds create a 'workflows' datastream in Fedora for the object def initialize_workflow(name, repo='dor', create_ds=true, priority=0) priority = workflows.current_priority if priority == 0 opts = { :create_ds => create_ds } opts[:priority] = priority if(priority > 0) Dor::WorkflowService.create_workflow(repo, self.pid, name, Dor::WorkflowObject.initial_workflow(name), opts) end private #handles formating utc date/time to human readable def format_date datetime begin zone = ActiveSupport::TimeZone.new("Pacific Time (US & Canada)") d = datetime.is_a?(Time) ? datetime : DateTime.parse(datetime).in_time_zone(zone) I18n.l(d) rescue d = datetime.is_a?(Time) ? datetime : Time.parse(datetime.to_s) d.strftime('%Y-%m-%d %I:%M%p') end end end end