dor-services 5.1.0 → 5.1.1
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 +13 -5
- data/config/dev_console_env.rb +45 -32
- data/lib/dor-services.rb +52 -49
- data/lib/dor/datastreams/administrative_metadata_ds.rb +2 -2
- data/lib/dor/datastreams/content_metadata_ds.rb +114 -144
- data/lib/dor/datastreams/default_object_rights_ds.rb +10 -12
- data/lib/dor/datastreams/desc_metadata_ds.rb +4 -4
- data/lib/dor/datastreams/events_ds.rb +8 -8
- data/lib/dor/datastreams/identity_metadata_ds.rb +40 -23
- data/lib/dor/datastreams/rights_metadata_ds.rb +109 -0
- data/lib/dor/datastreams/role_metadata_ds.rb +4 -4
- data/lib/dor/datastreams/simple_dublin_core_ds.rb +6 -6
- data/lib/dor/datastreams/version_metadata_ds.rb +29 -10
- data/lib/dor/models/admin_policy_object.rb +3 -3
- data/lib/dor/models/describable.rb +5 -4
- data/lib/dor/models/editable.rb +29 -33
- data/lib/dor/models/embargoable.rb +1 -12
- data/lib/dor/models/governable.rb +4 -27
- data/lib/dor/models/identifiable.rb +60 -58
- data/lib/dor/models/item.rb +5 -9
- data/lib/dor/models/presentable.rb +15 -2
- data/lib/dor/models/processable.rb +34 -30
- data/lib/dor/models/publishable.rb +5 -14
- data/lib/dor/models/{releasable.rb → releaseable.rb} +59 -15
- data/lib/dor/models/rightsable.rb +25 -0
- data/lib/dor/models/versionable.rb +6 -3
- data/lib/dor/models/workflow_object.rb +4 -4
- data/lib/dor/services/cleanup_reset_service.rb +1 -2
- data/lib/dor/services/cleanup_service.rb +1 -1
- data/lib/dor/services/registration_service.rb +0 -3
- data/lib/dor/services/sdr_ingest_service.rb +1 -1
- data/lib/dor/services/search_service.rb +1 -1
- data/lib/dor/utils/sdr_client.rb +23 -0
- data/lib/dor/version.rb +1 -1
- data/lib/dor/workflow/document.rb +23 -22
- data/lib/tasks/rdoc.rake +4 -4
- metadata +113 -96
- data/config/environments/development.rb +0 -84
- data/config/environments/development.rb.old +0 -84
- data/config/environments/test.rb +0 -84
- data/lib/tasks/dor.rake +0 -39
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,15 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
             | 
| 2 | 
            +
            !binary "U0hBMQ==":
         | 
| 3 | 
            +
              metadata.gz: !binary |-
         | 
| 4 | 
            +
                OGQ5ZWQ0NWFjYTg0ZDc0YTFiNmMwMjM2ZmEzYzJjYTA4NzA5NmUwOA==
         | 
| 5 | 
            +
              data.tar.gz: !binary |-
         | 
| 6 | 
            +
                MDJmMmQ5ZGNmNzU5OWQxNmMwNjJlOWY2MTgwMTc4NWM4ZmI3YWNhYg==
         | 
| 5 7 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
             | 
| 8 | 
            +
              metadata.gz: !binary |-
         | 
| 9 | 
            +
                MzcxNDU3MjQ4NjQwODgwZTQyM2Q1OGQ4ZGYzYjdhOTI3ZTY1MzA3OGEzZjc2
         | 
| 10 | 
            +
                MWJiYjkzN2VjNzVmYWI1NWRhMDBjMTE4MDZhMjM5Y2QzOTIwNGRlY2IwYWYy
         | 
| 11 | 
            +
                YmQyZDc5YjY4N2I3ODcwMDZkZDE3ODRlOTM3NTY4MDY3ODhmNzI=
         | 
| 12 | 
            +
              data.tar.gz: !binary |-
         | 
| 13 | 
            +
                OTZiMzAzNjg3NDFlZTM3NzYwYmVmNjljMzcxYjA4YzE2Y2MwYmQ3ZjA2Zjdh
         | 
| 14 | 
            +
                ZjY0MWVhNDlmMWE3ZWE2ZWUxNWUwN2JkNWU2NDlhZGNiYWZkZTg3MmUzZWQx
         | 
| 15 | 
            +
                Y2FhM2I3MGFhMjc4OTk2YTk3MGU5N2YwYjkzYTQyOTQxMTIxNjY=
         | 
    
        data/config/dev_console_env.rb
    CHANGED
    
    | @@ -1,18 +1,13 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
            cert_dir = File.join(File.dirname(__FILE__), ".", "certs")
         | 
| 1 | 
            +
            cert_dir = File.join(File.dirname(__FILE__), "certs")
         | 
| 4 2 |  | 
| 5 3 | 
             
            Dor::Config.configure do
         | 
| 6 | 
            -
              fedora do
         | 
| 7 | 
            -
                url 'https://sul-dor-dev.stanford.edu/fedora'
         | 
| 8 | 
            -
              end
         | 
| 9 4 |  | 
| 10 5 | 
             
              ssl do
         | 
| 11 6 | 
             
                cert_file File.join(cert_dir,"robots-dor-dev.crt")
         | 
| 12 | 
            -
                key_file | 
| 13 | 
            -
                key_pass | 
| 7 | 
            +
                key_file  File.join(cert_dir,"robots-dor-dev.key")
         | 
| 8 | 
            +
                key_pass  ''
         | 
| 14 9 | 
             
              end
         | 
| 15 | 
            -
             | 
| 10 | 
            +
              
         | 
| 16 11 | 
             
              suri do
         | 
| 17 12 | 
             
                mint_ids true
         | 
| 18 13 | 
             
                id_namespace 'druid'
         | 
| @@ -20,46 +15,64 @@ Dor::Config.configure do | |
| 20 15 | 
             
                user 'labware'
         | 
| 21 16 | 
             
                pass 'lyberteam'
         | 
| 22 17 | 
             
              end
         | 
| 23 | 
            -
             | 
| 18 | 
            +
              
         | 
| 24 19 | 
             
              metadata do
         | 
| 25 | 
            -
                exist.url | 
| 20 | 
            +
                exist.url   'http://viewer:l3l%40nd@lyberapps-dev.stanford.edu/exist/rest/'
         | 
| 26 21 | 
             
                catalog.url 'http://lyberservices-prod.stanford.edu/catalog/mods'
         | 
| 27 22 | 
             
              end
         | 
| 28 | 
            -
             | 
| 23 | 
            +
              
         | 
| 29 24 | 
             
              stacks do
         | 
| 30 25 | 
             
                document_cache_host 'purl-dev.stanford.edu'
         | 
| 31 | 
            -
                 | 
| 32 | 
            -
                local_workspace_root '/dor/workspace'
         | 
| 33 | 
            -
                storage_root '/stacks'
         | 
| 34 | 
            -
                host 'stacks-dev.stanford.edu'
         | 
| 35 | 
            -
                user 'lyberadmin'
         | 
| 36 | 
            -
                local_stacks_root '/stacks'
         | 
| 26 | 
            +
                local_stacks_root         '/stacks'
         | 
| 37 27 | 
             
                local_document_cache_root '/purl/document_cache'
         | 
| 28 | 
            +
                local_workspace_root      '/dor/workspace'
         | 
| 38 29 | 
             
              end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
               | 
| 41 | 
            -
               | 
| 42 | 
            -
              workflow.url | 
| 43 | 
            -
              dor_services.url 'https://dorAdmin:dorAdmin@sul-lyberservices-dev.stanford.edu/dor | 
| 30 | 
            +
             
         | 
| 31 | 
            +
              solrizer.url     'http://sul-solr.stanford.edu/solr/argo_test'
         | 
| 32 | 
            +
              fedora.url       'https://sul-dor-test.stanford.edu/fedora'
         | 
| 33 | 
            +
              workflow.url     'https://lyberservices-dev.stanford.edu/workflow/'
         | 
| 34 | 
            +
              dor_services.url 'https://dorAdmin:dorAdmin@sul-lyberservices-dev.stanford.edu/dor'
         | 
| 44 35 |  | 
| 45 36 | 
             
              cleanup do
         | 
| 46 37 | 
             
                local_workspace_root '/dor/workspace'
         | 
| 47 | 
            -
                local_export_home | 
| 38 | 
            +
                local_export_home    '/dor/export'
         | 
| 48 39 | 
             
              end
         | 
| 49 40 |  | 
| 50 41 | 
             
              sdr do
         | 
| 42 | 
            +
                url 'https://sdrAdmin:sdrAdmin@sdr-services-test.stanford.edu/sdr/'
         | 
| 51 43 | 
             
                local_workspace_root '/dor/workspace'
         | 
| 52 | 
            -
                local_export_home | 
| 44 | 
            +
                local_export_home    '/dor/export'
         | 
| 53 45 | 
             
                datastreams do
         | 
| 54 | 
            -
                   | 
| 55 | 
            -
                   | 
| 56 | 
            -
                   | 
| 57 | 
            -
                   | 
| 58 | 
            -
                   | 
| 59 | 
            -
                   | 
| 60 | 
            -
                   | 
| 46 | 
            +
                  administrativeMetadata 'optional'
         | 
| 47 | 
            +
                  contentMetadata        'optional'
         | 
| 48 | 
            +
                  descMetadata           'required'
         | 
| 49 | 
            +
                  defaultObjectRights    'optional'
         | 
| 50 | 
            +
                  events                 'optional'
         | 
| 51 | 
            +
                  embargoMetadata        'optional'
         | 
| 52 | 
            +
                  identityMetadata       'required'
         | 
| 53 | 
            +
                  provenanceMetadata     'required'
         | 
| 54 | 
            +
                  relationshipMetadata   'required'
         | 
| 55 | 
            +
                  rightsMetadata         'optional'
         | 
| 56 | 
            +
                  roleMetadata           'optional'
         | 
| 57 | 
            +
                  sourceMetadata         'optional'
         | 
| 58 | 
            +
                  technicalMetadata      'optional'
         | 
| 59 | 
            +
                  versionMetadata        'required'
         | 
| 60 | 
            +
                  workflows              'optional'
         | 
| 61 61 | 
             
                end
         | 
| 62 62 | 
             
              end
         | 
| 63 63 |  | 
| 64 | 
            +
              accessioning_robot_sleep_time 30
         | 
| 65 | 
            +
             | 
| 64 66 | 
             
            end
         | 
| 65 67 |  | 
| 68 | 
            +
             | 
| 69 | 
            +
            #WORKFLOW_URI = 'http://lyberservices-test.stanford.edu/workflow'
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            # Constants for Dor::WorkflowService
         | 
| 72 | 
            +
            #module Dor
         | 
| 73 | 
            +
            #  CREATE_WORKFLOW = DOR_CREATE_WORKFLOW = true
         | 
| 74 | 
            +
            #  WF_URI = 'http://lyberservices-test.stanford.edu/workflow'
         | 
| 75 | 
            +
            #end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            # External application locations
         | 
| 78 | 
            +
            JHOVE_HOME = File.join(ENV['HOME'], 'jhoveToolkit')
         | 
    
        data/lib/dor-services.rb
    CHANGED
    
    | @@ -4,7 +4,7 @@ require 'active_support/core_ext/module/attribute_accessors' | |
| 4 4 | 
             
            module Dor
         | 
| 5 5 | 
             
              @@registered_classes = {}
         | 
| 6 6 | 
             
              mattr_reader :registered_classes
         | 
| 7 | 
            -
              INDEX_VERSION_FIELD = ' | 
| 7 | 
            +
              INDEX_VERSION_FIELD = 'dor_services_version_ssi'
         | 
| 8 8 |  | 
| 9 9 | 
             
              class << self
         | 
| 10 10 |  | 
| @@ -40,7 +40,7 @@ module Dor | |
| 40 40 | 
             
                  resp.docs.collect do |solr_doc|
         | 
| 41 41 | 
             
                    doc_version = solr_doc[INDEX_VERSION_FIELD].first rescue '0.0.0'
         | 
| 42 42 | 
             
                    doc_version = Gem::Version.new(doc_version)
         | 
| 43 | 
            -
                    object_type = Array(solr_doc[ActiveFedora::SolrService.solr_name('objectType' | 
| 43 | 
            +
                    object_type = Array(solr_doc[ActiveFedora::SolrService.solr_name('objectType', :symbol)]).first
         | 
| 44 44 | 
             
                    object_class = registered_classes[object_type] || ActiveFedora::Base
         | 
| 45 45 | 
             
                    if opts[:lightweight] and doc_version >= Gem::Version.new('3.1.0')
         | 
| 46 46 | 
             
                      begin
         | 
| @@ -78,63 +78,66 @@ module Dor | |
| 78 78 | 
             
              require 'druid-tools'
         | 
| 79 79 |  | 
| 80 80 | 
             
              # datastreams
         | 
| 81 | 
            -
              autoload :AdministrativeMetadataDS, | 
| 82 | 
            -
              autoload :ContentMetadataDS, | 
| 83 | 
            -
              autoload :DescMetadataDS, | 
| 84 | 
            -
              autoload :EmbargoMetadataDS, | 
| 85 | 
            -
              autoload :EventsDS, | 
| 86 | 
            -
              autoload :GeoMetadataDS, | 
| 87 | 
            -
              autoload :IdentityMetadataDS, | 
| 88 | 
            -
              autoload : | 
| 89 | 
            -
              autoload : | 
| 90 | 
            -
              autoload : | 
| 91 | 
            -
              autoload : | 
| 92 | 
            -
              autoload : | 
| 93 | 
            -
              autoload : | 
| 81 | 
            +
              autoload :AdministrativeMetadataDS,    'dor/datastreams/administrative_metadata_ds'
         | 
| 82 | 
            +
              autoload :ContentMetadataDS,           'dor/datastreams/content_metadata_ds'
         | 
| 83 | 
            +
              autoload :DescMetadataDS,              'dor/datastreams/desc_metadata_ds'
         | 
| 84 | 
            +
              autoload :EmbargoMetadataDS,           'dor/datastreams/embargo_metadata_ds'
         | 
| 85 | 
            +
              autoload :EventsDS,                    'dor/datastreams/events_ds'
         | 
| 86 | 
            +
              autoload :GeoMetadataDS,               'dor/datastreams/geo_metadata_ds'
         | 
| 87 | 
            +
              autoload :IdentityMetadataDS,          'dor/datastreams/identity_metadata_ds'
         | 
| 88 | 
            +
              autoload :RightsMetadataDS,            'dor/datastreams/rights_metadata_ds'
         | 
| 89 | 
            +
              autoload :RoleMetadataDS,              'dor/datastreams/role_metadata_ds'
         | 
| 90 | 
            +
              autoload :WorkflowDefinitionDs,        'dor/datastreams/workflow_definition_ds'
         | 
| 91 | 
            +
              autoload :WorkflowDs,                  'dor/datastreams/workflow_ds'
         | 
| 92 | 
            +
              autoload :VersionMetadataDS,           'dor/datastreams/version_metadata_ds'
         | 
| 93 | 
            +
              autoload :DefaultObjectRightsDS,       'dor/datastreams/default_object_rights_ds'
         | 
| 94 | 
            +
              autoload :SimpleDublinCoreDs,          'dor/datastreams/simple_dublin_core_ds'
         | 
| 94 95 |  | 
| 95 96 | 
             
              # DOR Concerns
         | 
| 96 97 | 
             
              autoload :Identifiable, 'dor/models/identifiable'
         | 
| 97 | 
            -
              autoload :Itemizable, | 
| 98 | 
            -
              autoload :Processable, | 
| 99 | 
            -
              autoload :Governable, | 
| 100 | 
            -
              autoload :Describable, | 
| 101 | 
            -
              autoload :Publishable, | 
| 102 | 
            -
              autoload :Shelvable, | 
| 103 | 
            -
              autoload :Embargoable, | 
| 104 | 
            -
              autoload :Preservable, | 
| 98 | 
            +
              autoload :Itemizable,   'dor/models/itemizable'
         | 
| 99 | 
            +
              autoload :Processable,  'dor/models/processable'
         | 
| 100 | 
            +
              autoload :Governable,   'dor/models/governable'
         | 
| 101 | 
            +
              autoload :Describable,  'dor/models/describable'
         | 
| 102 | 
            +
              autoload :Publishable,  'dor/models/publishable'
         | 
| 103 | 
            +
              autoload :Shelvable,    'dor/models/shelvable'
         | 
| 104 | 
            +
              autoload :Embargoable,  'dor/models/embargoable'
         | 
| 105 | 
            +
              autoload :Preservable,  'dor/models/preservable'
         | 
| 105 106 | 
             
              autoload :Assembleable, 'dor/models/assembleable'
         | 
| 106 | 
            -
              autoload :Upgradable, | 
| 107 | 
            -
              autoload :Eventable, | 
| 108 | 
            -
              autoload :Versionable, | 
| 109 | 
            -
              autoload :Contentable, | 
| 110 | 
            -
              autoload :Editable, | 
| 111 | 
            -
              autoload : | 
| 112 | 
            -
              autoload : | 
| 113 | 
            -
              autoload : | 
| 107 | 
            +
              autoload :Upgradable,   'dor/models/upgradable'
         | 
| 108 | 
            +
              autoload :Eventable,    'dor/models/eventable'
         | 
| 109 | 
            +
              autoload :Versionable,  'dor/models/versionable'
         | 
| 110 | 
            +
              autoload :Contentable,  'dor/models/contentable'
         | 
| 111 | 
            +
              autoload :Editable,     'dor/models/editable'
         | 
| 112 | 
            +
              autoload :Discoverable, 'dor/models/discoverable'
         | 
| 113 | 
            +
              autoload :Geoable,      'dor/models/geoable'
         | 
| 114 | 
            +
              autoload :Presentable,  'dor/models/presentable'
         | 
| 115 | 
            +
              autoload :Releaseable,   'dor/models/releaseable'
         | 
| 116 | 
            +
              autoload :Rightsable,   'dor/models/rightsable'
         | 
| 114 117 |  | 
| 115 118 |  | 
| 116 119 | 
             
              # ActiveFedora Classes
         | 
| 117 | 
            -
              autoload :Abstract, | 
| 118 | 
            -
              autoload :Item, | 
| 119 | 
            -
              autoload :Set, | 
| 120 | 
            +
              autoload :Abstract,   'dor/models/item'
         | 
| 121 | 
            +
              autoload :Item,       'dor/models/item'
         | 
| 122 | 
            +
              autoload :Set,        'dor/models/set'
         | 
| 120 123 | 
             
              autoload :Collection, 'dor/models/collection'
         | 
| 121 124 | 
             
              autoload :AdminPolicyObject, 'dor/models/admin_policy_object'
         | 
| 122 | 
            -
              autoload :WorkflowObject, | 
| 125 | 
            +
              autoload :WorkflowObject,    'dor/models/workflow_object'
         | 
| 123 126 |  | 
| 124 127 | 
             
              # Services
         | 
| 125 | 
            -
              autoload :SearchService, | 
| 126 | 
            -
              autoload :MetadataService, | 
| 127 | 
            -
              autoload :RegistrationService, | 
| 128 | 
            -
              autoload :SuriService, | 
| 129 | 
            -
              autoload :WorkflowService, | 
| 130 | 
            -
              autoload :DigitalStacksService, | 
| 131 | 
            -
              autoload :SdrIngestService, | 
| 132 | 
            -
              autoload :CleanupService, | 
| 128 | 
            +
              autoload :SearchService,             'dor/services/search_service'
         | 
| 129 | 
            +
              autoload :MetadataService,           'dor/services/metadata_service'
         | 
| 130 | 
            +
              autoload :RegistrationService,       'dor/services/registration_service'
         | 
| 131 | 
            +
              autoload :SuriService,               'dor/services/suri_service'
         | 
| 132 | 
            +
              autoload :WorkflowService,           'dor/services/workflow_service'
         | 
| 133 | 
            +
              autoload :DigitalStacksService,      'dor/services/digital_stacks_service'
         | 
| 134 | 
            +
              autoload :SdrIngestService,          'dor/services/sdr_ingest_service'
         | 
| 135 | 
            +
              autoload :CleanupService,            'dor/services/cleanup_service'
         | 
| 133 136 | 
             
              autoload :ProvenanceMetadataService, 'dor/services/provenance_metadata_service'
         | 
| 134 | 
            -
              autoload :TechnicalMetadataService, | 
| 135 | 
            -
              autoload :MergeService, | 
| 136 | 
            -
              autoload :ResetWorkspaceService, | 
| 137 | 
            -
              autoload :CleanupResetService, | 
| 137 | 
            +
              autoload :TechnicalMetadataService,  'dor/services/technical_metadata_service'
         | 
| 138 | 
            +
              autoload :MergeService,              'dor/services/merge_service'
         | 
| 139 | 
            +
              autoload :ResetWorkspaceService,     'dor/services/reset_workspace_service'
         | 
| 140 | 
            +
              autoload :CleanupResetService,       'dor/services/cleanup_reset_service'
         | 
| 138 141 |  | 
| 139 142 | 
             
              # Versioning Classes
         | 
| 140 143 | 
             
              module Versioning
         | 
| @@ -143,8 +146,8 @@ module Dor | |
| 143 146 |  | 
| 144 147 | 
             
              # Workflow Classes
         | 
| 145 148 | 
             
              module Workflow
         | 
| 146 | 
            -
                autoload :Graph, | 
| 147 | 
            -
                autoload :Process, | 
| 149 | 
            +
                autoload :Graph,    'dor/workflow/graph'
         | 
| 150 | 
            +
                autoload :Process,  'dor/workflow/process'
         | 
| 148 151 | 
             
                autoload :Document, 'dor/workflow/document'
         | 
| 149 152 | 
             
              end
         | 
| 150 153 | 
             
            end
         | 
| @@ -17,14 +17,14 @@ class AdministrativeMetadataDS < ActiveFedora::OmDatastream | |
| 17 17 | 
             
                t.registration :index_as => [:not_searchable] do
         | 
| 18 18 | 
             
                  t.agreementId
         | 
| 19 19 | 
             
                  t.itemTag
         | 
| 20 | 
            -
                  t.workflow_id :path => 'workflow/@id', :index_as => [:symbol | 
| 20 | 
            +
                  t.workflow_id :path => 'workflow/@id', :index_as => [:symbol]
         | 
| 21 21 | 
             
                  t.default_collection :path => 'collection/@id'
         | 
| 22 22 | 
             
                end
         | 
| 23 23 | 
             
                t.workflow :path => 'registration/workflow'
         | 
| 24 24 | 
             
                t.deposit :index_as => [:not_searchable]
         | 
| 25 25 |  | 
| 26 26 | 
             
                t.accessioning :index_as => [:not_searchable] do
         | 
| 27 | 
            -
                  t.workflow_id :path => 'workflow/@id', :index_as => [: | 
| 27 | 
            +
                  t.workflow_id :path => 'workflow/@id', :index_as => [:symbol]
         | 
| 28 28 | 
             
                end
         | 
| 29 29 |  | 
| 30 30 | 
             
                t.preservation :index_as => [:not_searchable]
         | 
| @@ -1,26 +1,26 @@ | |
| 1 1 | 
             
            module Dor
         | 
| 2 | 
            -
              class ContentMetadataDS < ActiveFedora::OmDatastream | 
| 2 | 
            +
              class ContentMetadataDS < ActiveFedora::OmDatastream
         | 
| 3 3 | 
             
                include Upgradable
         | 
| 4 4 | 
             
                include SolrDocHelper
         | 
| 5 5 |  | 
| 6 6 | 
             
                set_terminology do |t|
         | 
| 7 | 
            -
                  t.root | 
| 8 | 
            -
                  t.contentType :path => '/contentMetadata/@type', | 
| 9 | 
            -
                  t.stacks | 
| 7 | 
            +
                  t.root        :path => 'contentMetadata',          :index_as => [:not_searchable]
         | 
| 8 | 
            +
                  t.contentType :path => '/contentMetadata/@type',   :index_as => [:not_searchable]
         | 
| 9 | 
            +
                  t.stacks      :path => '/contentMetadata/@stacks', :index_as => [:not_searchable]
         | 
| 10 10 | 
             
                  t.resource(:index_as => [:not_searchable]) do
         | 
| 11 | 
            -
                    t.id_ | 
| 12 | 
            -
                    t.sequence | 
| 13 | 
            -
                    t.type_ | 
| 11 | 
            +
                    t.id_       :path => { :attribute => 'id' }
         | 
| 12 | 
            +
                    t.sequence  :path => { :attribute => 'sequence' }#, :data_type => :integer
         | 
| 13 | 
            +
                    t.type_     :path => { :attribute => 'type' }, :index_as => [:displayable]
         | 
| 14 14 | 
             
                    t.attribute(:path => 'attr', :index_as => [:not_searchable]) do
         | 
| 15 | 
            -
                      t.name | 
| 15 | 
            +
                      t.name    :path => { :attribute => 'name' }, :index_as => [:not_searchable]
         | 
| 16 16 | 
             
                    end
         | 
| 17 17 | 
             
                    t.file(:index_as => [:not_searchable]) do
         | 
| 18 | 
            -
                      t.id_ | 
| 18 | 
            +
                      t.id_      :path => { :attribute => 'id' }
         | 
| 19 19 | 
             
                      t.mimeType :path => { :attribute => 'mimeType' }, :index_as => [:displayable]
         | 
| 20 20 | 
             
                      t.dataType :path => { :attribute => 'dataType' }, :index_as => [:displayable]
         | 
| 21 | 
            -
                      t.size | 
| 22 | 
            -
                      t.shelve | 
| 23 | 
            -
                      t.publish | 
| 21 | 
            +
                      t.size     :path => { :attribute => 'size'     }, :index_as => [:displayable]#, :data_type => :long
         | 
| 22 | 
            +
                      t.shelve   :path => { :attribute => 'shelve'   }, :index_as => [:not_searchable]#, :data_type => :boolean
         | 
| 23 | 
            +
                      t.publish  :path => { :attribute => 'publish'  }, :index_as => [:not_searchable]#, :data_type => :boolean
         | 
| 24 24 | 
             
                      t.preserve :path => { :attribute => 'preserve' }, :index_as => [:not_searchable]#, :data_type => :boolean
         | 
| 25 25 | 
             
                      t.checksum do
         | 
| 26 26 | 
             
                        t.type_ :path => { :attribute => 'type' }
         | 
| @@ -35,69 +35,50 @@ module Dor | |
| 35 35 |  | 
| 36 36 | 
             
                def public_xml
         | 
| 37 37 | 
             
                  result = self.ng_xml.clone
         | 
| 38 | 
            -
                  result.xpath('/contentMetadata/resource[not(file[(@deliver="yes" or @publish="yes")])]').each { |n| n.remove }
         | 
| 39 | 
            -
                  result.xpath('/contentMetadata/resource/file[not(@deliver="yes" or @publish="yes")]').each { |n| n.remove }
         | 
| 38 | 
            +
                  result.xpath('/contentMetadata/resource[not(file[(@deliver="yes" or @publish="yes")])]'   ).each { |n| n.remove }
         | 
| 39 | 
            +
                  result.xpath('/contentMetadata/resource/file[not(@deliver="yes" or @publish="yes")]'      ).each { |n| n.remove }
         | 
| 40 40 | 
             
                  result.xpath('/contentMetadata/resource/file').xpath('@preserve|@shelve|@publish|@deliver').each { |n| n.remove }
         | 
| 41 | 
            -
                  result.xpath('/contentMetadata/resource/file/checksum').each { |n| n.remove }
         | 
| 41 | 
            +
                  result.xpath('/contentMetadata/resource/file/checksum'                                    ).each { |n| n.remove }
         | 
| 42 42 | 
             
                  result
         | 
| 43 43 | 
             
                end
         | 
| 44 44 | 
             
                def add_file(file, resource_name)
         | 
| 45 45 | 
             
                  xml=self.ng_xml
         | 
| 46 46 | 
             
                  resource_nodes = xml.search('//resource[@id=\''+resource_name+'\']')
         | 
| 47 | 
            -
                  if resource_nodes.length==0
         | 
| 48 | 
            -
                    raise 'resource doesnt exist.'
         | 
| 49 | 
            -
                  end
         | 
| 47 | 
            +
                  raise 'resource doesnt exist.' if resource_nodes.length==0
         | 
| 50 48 | 
             
                  node=resource_nodes.first
         | 
| 51 49 | 
             
                  file_node=Nokogiri::XML::Node.new('file',xml)
         | 
| 52 50 | 
             
                  file_node['id']=file[:name]
         | 
| 53 | 
            -
                  file_node['shelve']=file[:shelve] ? file[:shelve] : ''
         | 
| 54 | 
            -
                  file_node['publish']=file[:publish] ? file[:publish] : ''
         | 
| 55 | 
            -
                  file_node['preserve']=file[:preserve] ? file[:preserve] : ''
         | 
| 51 | 
            +
                  file_node['shelve'  ] = file[:shelve  ] ? file[:shelve  ] : ''
         | 
| 52 | 
            +
                  file_node['publish' ] = file[:publish ] ? file[:publish ] : ''
         | 
| 53 | 
            +
                  file_node['preserve'] = file[:preserve] ? file[:preserve] : ''
         | 
| 56 54 | 
             
                  node.add_child(file_node)
         | 
| 57 55 |  | 
| 58 | 
            -
                   | 
| 59 | 
            -
                     | 
| 60 | 
            -
                    checksum_node | 
| 61 | 
            -
                    checksum_node | 
| 62 | 
            -
                     | 
| 63 | 
            -
                  end
         | 
| 64 | 
            -
                  if file[:sha1]
         | 
| 65 | 
            -
                    checksum_node=Nokogiri::XML::Node.new('checksum',xml)
         | 
| 66 | 
            -
                    checksum_node['type']='sha1'
         | 
| 67 | 
            -
                    checksum_node.content=file[:sha1]
         | 
| 56 | 
            +
                  [:md5, :sha1].each do |algo|
         | 
| 57 | 
            +
                    next unless file[algo]
         | 
| 58 | 
            +
                    checksum_node = Nokogiri::XML::Node.new('checksum',xml)
         | 
| 59 | 
            +
                    checksum_node['type'] = algo.to_s
         | 
| 60 | 
            +
                    checksum_node.content = file[algo]
         | 
| 68 61 | 
             
                    file_node.add_child(checksum_node)
         | 
| 69 62 | 
             
                  end
         | 
| 70 | 
            -
                  if file[:size]
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                  end
         | 
| 73 | 
            -
                  if file[:mime_type]
         | 
| 74 | 
            -
                    file_node['mimetype']=file[:mime_type]
         | 
| 75 | 
            -
                  end
         | 
| 63 | 
            +
                  file_node['size'    ] = file[:size     ] if file[:size     ]
         | 
| 64 | 
            +
                  file_node['mimetype'] = file[:mime_type] if file[:mime_type]
         | 
| 76 65 | 
             
                  self.content=xml.to_s
         | 
| 77 66 | 
             
                  self.save
         | 
| 78 67 | 
             
                end
         | 
| 79 68 |  | 
| 80 | 
            -
                def add_resource(files,resource_name, position,type="file") | 
| 69 | 
            +
                def add_resource(files,resource_name, position,type="file")
         | 
| 81 70 | 
             
                  xml=self.ng_xml
         | 
| 82 71 | 
             
                  if xml.search('//resource[@id=\''+resource_name+'\']').length>0
         | 
| 83 72 | 
             
                    raise 'resource '+resource_name+' already exists'
         | 
| 84 73 | 
             
                  end
         | 
| 85 74 | 
             
                  node=nil
         | 
| 86 75 |  | 
| 87 | 
            -
                  max | 
| 88 | 
            -
                  xml.search('//resource').each do |node|
         | 
| 89 | 
            -
                    if node['sequence'].to_i>max
         | 
| 90 | 
            -
                      max=node['sequence'].to_i
         | 
| 91 | 
            -
                    end
         | 
| 92 | 
            -
                  end
         | 
| 76 | 
            +
                  max = xml.search('//resource').map{ |node| node['sequence'].to_i }.max
         | 
| 93 77 | 
             
                  #renumber all of the resources that will come after the newly added one
         | 
| 94 78 | 
             
                  while max>position do
         | 
| 95 79 | 
             
                    node=xml.search('//resource[@sequence=\'' + position + '\']')
         | 
| 96 | 
            -
                    if node.length>0
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                      node[sequence]=max+1
         | 
| 99 | 
            -
                    end
         | 
| 100 | 
            -
                    max=max-1
         | 
| 80 | 
            +
                    node.first[sequence]=max+1 if node.length>0
         | 
| 81 | 
            +
                    max-=1
         | 
| 101 82 | 
             
                  end
         | 
| 102 83 | 
             
                  node=Nokogiri::XML::Node.new('resource',xml)
         | 
| 103 84 | 
             
                  node['sequence']=position.to_s
         | 
| @@ -117,7 +98,7 @@ module Dor | |
| 117 98 | 
             
                      file_node.add_child(checksum_node)
         | 
| 118 99 | 
             
                    }
         | 
| 119 100 | 
             
                    file_node['size'] = file[:size] if file[:size]
         | 
| 120 | 
            -
                  end | 
| 101 | 
            +
                  end
         | 
| 121 102 | 
             
                  xml.search('//contentMetadata').first.add_child(node)
         | 
| 122 103 | 
             
                  self.content=xml.to_s
         | 
| 123 104 | 
             
                  self.save
         | 
| @@ -125,20 +106,12 @@ module Dor | |
| 125 106 |  | 
| 126 107 | 
             
                def remove_resource resource_name
         | 
| 127 108 | 
             
                  xml=self.ng_xml
         | 
| 128 | 
            -
                   | 
| 129 | 
            -
             | 
| 130 | 
            -
                   | 
| 131 | 
            -
                  if resources.length!=1
         | 
| 132 | 
            -
                    raise 'Resource is missing or duplicated!'
         | 
| 133 | 
            -
                  end
         | 
| 134 | 
            -
                  position=resources.first['sequence']
         | 
| 135 | 
            -
                  resources.first.remove
         | 
| 136 | 
            -
                  position=position.to_i+1
         | 
| 109 | 
            +
                  node = singular_node('//resource[@id=\''+resource_name+'\']')
         | 
| 110 | 
            +
                  position = node['sequence'].to_i+1
         | 
| 111 | 
            +
                  node.remove
         | 
| 137 112 | 
             
                  while true
         | 
| 138 113 | 
             
                    res=xml.search('//resource[@sequence=\''+position.to_s+'\']')
         | 
| 139 | 
            -
                    if | 
| 140 | 
            -
                      break
         | 
| 141 | 
            -
                    end
         | 
| 114 | 
            +
                    break if res.length==0
         | 
| 142 115 | 
             
                    res['sequence']=position.to_s
         | 
| 143 116 | 
             
                    position=position+1
         | 
| 144 117 | 
             
                  end
         | 
| @@ -184,54 +157,50 @@ module Dor | |
| 184 157 | 
             
                  self.content=xml.to_s
         | 
| 185 158 | 
             
                  self.save
         | 
| 186 159 | 
             
                end
         | 
| 160 | 
            +
             | 
| 187 161 | 
             
                # Terminology-based solrization is going to be painfully slow for large
         | 
| 188 162 | 
             
                # contentMetadata streams. Just select the relevant elements instead.
         | 
| 163 | 
            +
                # TODO: Call super()?
         | 
| 189 164 | 
             
                def to_solr(solr_doc=Hash.new, *args)
         | 
| 190 165 | 
             
                  doc = self.ng_xml
         | 
| 191 | 
            -
                   | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 198 | 
            -
             | 
| 199 | 
            -
                     | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 203 | 
            -
             | 
| 204 | 
            -
             | 
| 205 | 
            -
             | 
| 206 | 
            -
                         | 
| 207 | 
            -
             | 
| 208 | 
            -
                      resource.xpath('file').each do |file|
         | 
| 209 | 
            -
                        content_file_count+=1
         | 
| 210 | 
            -
                        if file['shelve'] == 'yes'
         | 
| 211 | 
            -
                          shelved_file_count+=1
         | 
| 212 | 
            -
                          if first_shelved_image.nil? && file['id'].match(/jp2$/)
         | 
| 213 | 
            -
                            first_shelved_image=file['id']
         | 
| 214 | 
            -
                          end
         | 
| 215 | 
            -
                        end
         | 
| 216 | 
            -
                        if file['preserve'] == 'yes'
         | 
| 217 | 
            -
                          preserved_size += file['size'].to_i
         | 
| 166 | 
            +
                  return solr_doc unless doc.root['type']
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                  preserved_size=0
         | 
| 169 | 
            +
                  counts = Hash.new(0)                # default count is zero
         | 
| 170 | 
            +
                  resource_type_counts = Hash.new(0)  # default count is zero
         | 
| 171 | 
            +
                  first_shelved_image=nil
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                  doc.xpath('contentMetadata/resource').sort { |a,b| a['sequence'].to_i <=> b['sequence'].to_i }.each do |resource|
         | 
| 174 | 
            +
                    counts['resource']+=1
         | 
| 175 | 
            +
                    resource_type_counts[resource['type']]+=1 if resource['type']
         | 
| 176 | 
            +
                    resource.xpath('file').each do |file|
         | 
| 177 | 
            +
                      counts['content_file']+=1
         | 
| 178 | 
            +
                      preserved_size += file['size'].to_i if file['preserve'] == 'yes'
         | 
| 179 | 
            +
                      if file['shelve'] == 'yes'
         | 
| 180 | 
            +
                        counts['shelved_file']+=1
         | 
| 181 | 
            +
                        if first_shelved_image.nil? && file['id'].match(/jp2$/)
         | 
| 182 | 
            +
                          first_shelved_image=file['id']
         | 
| 218 183 | 
             
                        end
         | 
| 219 184 | 
             
                      end
         | 
| 220 185 | 
             
                    end
         | 
| 221 | 
            -
                    add_solr_value(solr_doc, "content_file_count", content_file_count.to_s, :string, [:searchable, :displayable])
         | 
| 222 | 
            -
                    add_solr_value(solr_doc, "shelved_content_file_count", shelved_file_count.to_s, :string, [:searchable, :displayable])
         | 
| 223 | 
            -
                    add_solr_value(solr_doc, "resource_count", resource_count.to_s, :string, [:searchable, :displayable])
         | 
| 224 | 
            -
                    add_solr_value(solr_doc, "preserved_size", preserved_size.to_s, :string, [:searchable, :displayable])
         | 
| 225 | 
            -
                    resource_type_counts.each do |key, count|
         | 
| 226 | 
            -
                      add_solr_value(solr_doc, "resource_types", key, :string, [:symbol])
         | 
| 227 | 
            -
                      add_solr_value(solr_doc, key+"_resource_count", count.to_s, :string, [:searchable, :displayable])
         | 
| 228 | 
            -
                    end
         | 
| 229 | 
            -
                    unless first_shelved_image.nil?
         | 
| 230 | 
            -
                      add_solr_value(solr_doc, "first_shelved_image", first_shelved_image, :string, [:displayable])
         | 
| 231 | 
            -
                    end
         | 
| 232 186 | 
             
                  end
         | 
| 187 | 
            +
                  solr_doc["content_type_ssim"              ] = doc.root['type']
         | 
| 188 | 
            +
                  solr_doc["content_file_count_itsi"        ] = counts['content_file']
         | 
| 189 | 
            +
                  solr_doc["shelved_content_file_count_itsi"] = counts['shelved_file']
         | 
| 190 | 
            +
                  solr_doc["resource_count_itsi"            ] = counts['resource']
         | 
| 191 | 
            +
                  solr_doc["preserved_size_dbtsi"           ] = preserved_size        # double (trie) to support very large sizes
         | 
| 192 | 
            +
                  solr_doc["resource_types_ssim"            ] = resource_type_counts.keys if resource_type_counts.size > 0
         | 
| 193 | 
            +
                  resource_type_counts.each do |key, count|
         | 
| 194 | 
            +
                    solr_doc["#{key}_resource_count_itsi"] = count
         | 
| 195 | 
            +
                  end
         | 
| 196 | 
            +
                  # first_shelved_image is neither indexed nor multiple
         | 
| 197 | 
            +
                  solr_doc["first_shelved_image_ss"] = first_shelved_image unless first_shelved_image.nil?
         | 
| 233 198 | 
             
                  solr_doc
         | 
| 234 199 | 
             
                end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                #@param old_name [String] unique id attribute of the file element
         | 
| 202 | 
            +
                #@param new_name [String] new unique id value being assigned
         | 
| 203 | 
            +
                #@return [Nokogiri::XML::Element] the file node
         | 
| 235 204 | 
             
                def rename_file old_name, new_name
         | 
| 236 205 | 
             
                  xml=self.ng_xml
         | 
| 237 206 | 
             
                  file_node=xml.search('//file[@id=\''+old_name+'\']').first
         | 
| @@ -240,63 +209,52 @@ module Dor | |
| 240 209 | 
             
                  self.save
         | 
| 241 210 | 
             
                end
         | 
| 242 211 |  | 
| 212 | 
            +
                #Updates old label OR creates a new one if necessary
         | 
| 213 | 
            +
                #@param resource_name [String] unique id attribute of the resource
         | 
| 214 | 
            +
                #@param new_label [String] label value being assigned
         | 
| 215 | 
            +
                #@return [Nokogiri::XML::Element] the resource node
         | 
| 243 216 | 
             
                def update_resource_label resource_name, new_label
         | 
| 244 | 
            -
                   | 
| 245 | 
            -
                   | 
| 246 | 
            -
                  if(resource_node.length!=1)
         | 
| 247 | 
            -
                    raise 'Resource not found or duplicate found.'
         | 
| 248 | 
            -
                  end
         | 
| 249 | 
            -
                  labels=xml.search('//resource[@id=\''+resource_name+'\']/label')
         | 
| 217 | 
            +
                  node = singular_node('//resource[@id=\''+resource_name+'\']')
         | 
| 218 | 
            +
                  labels = node.xpath('./label')
         | 
| 250 219 | 
             
                  if(labels.length==0)
         | 
| 251 220 | 
             
                    #create a label
         | 
| 252 | 
            -
                    label_node = Nokogiri::XML::Node.new('label', | 
| 221 | 
            +
                    label_node = Nokogiri::XML::Node.new('label',self.ng_xml)
         | 
| 253 222 | 
             
                    label_node.content=new_label
         | 
| 254 | 
            -
                     | 
| 223 | 
            +
                    node.add_child(label_node)
         | 
| 255 224 | 
             
                  else
         | 
| 256 225 | 
             
                    labels.first.content=new_label
         | 
| 257 226 | 
             
                  end
         | 
| 227 | 
            +
                  return node
         | 
| 258 228 | 
             
                end
         | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
                  end
         | 
| 265 | 
            -
                  resource_node.first['type']=new_type
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                #@param resource_name [String] unique id attribute of the resource
         | 
| 231 | 
            +
                #@param new_type [String] type value being assigned
         | 
| 232 | 
            +
                def update_resource_type resource_name, new_type
         | 
| 233 | 
            +
                  singular_node('//resource[@id=\''+resource_name+'\']')['type']=new_type
         | 
| 266 234 | 
             
                end
         | 
| 267 235 |  | 
| 236 | 
            +
                #You just *had* to have ordered lists in XML, didn't you?
         | 
| 237 | 
            +
                #Re-enumerate the sequence numbers affected
         | 
| 238 | 
            +
                #@param resource_name [String] unique id attribute of the resource
         | 
| 239 | 
            +
                #@param new_position [Integer, String] new sequence number of the resource, or a string that looks like one
         | 
| 240 | 
            +
                #@return [Nokogiri::XML::Element] the resource node
         | 
| 268 241 | 
             
                def move_resource resource_name, new_position
         | 
| 269 | 
            -
                   | 
| 270 | 
            -
                   | 
| 271 | 
            -
                   | 
| 272 | 
            -
             | 
| 273 | 
            -
                   | 
| 274 | 
            -
                  position | 
| 275 | 
            -
                   | 
| 276 | 
            -
                   | 
| 277 | 
            -
             | 
| 278 | 
            -
                     | 
| 279 | 
            -
                    while true
         | 
| 280 | 
            -
                      if counter == position
         | 
| 281 | 
            -
                        break
         | 
| 282 | 
            -
                      end
         | 
| 283 | 
            -
                      item=xml.search('/resource[@id=\''+counter.to_s+'\']').first
         | 
| 284 | 
            -
                      counter=counter+1
         | 
| 285 | 
            -
                      item['sequence']=counter.to_s
         | 
| 286 | 
            -
                    end
         | 
| 287 | 
            -
                  else
         | 
| 288 | 
            -
                    counter=position
         | 
| 289 | 
            -
                    while true
         | 
| 290 | 
            -
                      if counter == new_position
         | 
| 291 | 
            -
                        break
         | 
| 292 | 
            -
                      end
         | 
| 293 | 
            -
                      item=xml.search('/resource[@id=\''+counter.to_s+'\']').first
         | 
| 294 | 
            -
                      counter=counter-1
         | 
| 295 | 
            -
                      item['sequence']=counter.to_s
         | 
| 296 | 
            -
                    end
         | 
| 242 | 
            +
                  node = singular_node('//resource[@id=\''+resource_name+'\']')
         | 
| 243 | 
            +
                  position = node['sequence'].to_i
         | 
| 244 | 
            +
                  new_position = new_position.to_i              # tolerate strings as a Legacy behavior
         | 
| 245 | 
            +
                  return node if position == new_position
         | 
| 246 | 
            +
                  #otherwise, is the resource being moved earlier in the sequence or later?
         | 
| 247 | 
            +
                  up = new_position>position
         | 
| 248 | 
            +
                  others = new_position..(up ? position-1 : position+1)  # a range
         | 
| 249 | 
            +
                  others.each do |i|
         | 
| 250 | 
            +
                    item = self.ng_xml.at_xpath('/resource[@sequence=\''+i.to_s+'\']')
         | 
| 251 | 
            +
                    item['sequence'] = (up ? i-1 : i+1).to_s    # if you're going up, everything else comes down and vice versa
         | 
| 297 252 | 
             
                  end
         | 
| 253 | 
            +
                  node['sequence'] = new_position.to_s          # set the node we already had last, so we don't hit it twice!
         | 
| 254 | 
            +
                  return node
         | 
| 298 255 | 
             
                end
         | 
| 299 | 
            -
             | 
| 256 | 
            +
             | 
| 257 | 
            +
                #Set the content type and the resource types for all resources
         | 
| 300 258 | 
             
                #@param new_type [String] the new content type, ex book
         | 
| 301 259 | 
             
                #@param new_resource_type [String] the new type for all resources, ex book
         | 
| 302 260 | 
             
                def set_content_type old_type, old_resource_type, new_type, new_resource_type
         | 
| @@ -309,6 +267,18 @@ module Dor | |
| 309 267 | 
             
                  end
         | 
| 310 268 | 
             
                  self.content=xml.to_s
         | 
| 311 269 | 
             
                end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                # Only use this when you want the behavior of raising an exception if anything besides exactly one matching node
         | 
| 272 | 
            +
                # is found.  Otherwise just use .xpath, .at_xpath or .search.
         | 
| 273 | 
            +
                #@param xpath [String] accessor invocation for Nokogiri xpath
         | 
| 274 | 
            +
                #@return [Nokogiri::XML::Element] the matched element
         | 
| 275 | 
            +
                def singular_node xpath
         | 
| 276 | 
            +
                  node = self.ng_xml.search(xpath)
         | 
| 277 | 
            +
                  len  = node.length
         | 
| 278 | 
            +
                  raise "#{xpath} not found" if len < 1
         | 
| 279 | 
            +
                  raise "#{xpath} duplicated: #{len} found" if len != 1
         | 
| 280 | 
            +
                  node.first
         | 
| 281 | 
            +
                end
         | 
| 312 282 | 
             
              end
         | 
| 313 283 |  | 
| 314 284 | 
             
            end
         |