hydra-pcdm 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +38 -0
- data/Gemfile +6 -3
- data/README.md +31 -38
- data/Rakefile +14 -4
- data/hydra-pcdm.gemspec +10 -10
- data/lib/hydra/pcdm.rb +13 -12
- data/lib/hydra/pcdm/collection_indexer.rb +2 -4
- data/lib/hydra/pcdm/deep_member_iterator.rb +1 -1
- data/lib/hydra/pcdm/models/collection.rb +0 -1
- data/lib/hydra/pcdm/models/concerns/collection_behavior.rb +18 -5
- data/lib/hydra/pcdm/models/concerns/object_behavior.rb +13 -17
- data/lib/hydra/pcdm/models/concerns/pcdm_behavior.rb +50 -9
- data/lib/hydra/pcdm/models/file.rb +7 -7
- data/lib/hydra/pcdm/models/object.rb +0 -1
- data/lib/hydra/pcdm/object_indexer.rb +1 -2
- data/lib/hydra/pcdm/services/file/add_type.rb +1 -3
- data/lib/hydra/pcdm/services/file/get_mime_type.rb +2 -4
- data/lib/hydra/pcdm/validators/ancestor_validator.rb +3 -11
- data/lib/hydra/pcdm/validators/pcdm_object_validator.rb +2 -2
- data/lib/hydra/pcdm/validators/pcdm_validator.rb +2 -2
- data/lib/hydra/pcdm/version.rb +1 -1
- data/lib/hydra/pcdm/vocab/pcdm_terms.rb +81 -80
- data/lib/hydra/pcdm/vocab/sweet_jpl_terms.rb +12 -0
- data/spec/hydra/pcdm/ancestor_checker_spec.rb +7 -7
- data/spec/hydra/pcdm/collection_indexer_spec.rb +12 -13
- data/spec/hydra/pcdm/deep_member_iterator_spec.rb +16 -16
- data/spec/hydra/pcdm/models/collection_spec.rb +375 -313
- data/spec/hydra/pcdm/models/file_spec.rb +38 -38
- data/spec/hydra/pcdm/models/object_spec.rb +270 -256
- data/spec/hydra/pcdm/object_indexer_spec.rb +4 -4
- data/spec/hydra/pcdm/services/file/get_mime_type_spec.rb +10 -12
- data/spec/hydra/pcdm_spec.rb +21 -26
- metadata +19 -5
- data/lib/hydra/pcdm/vocab/ebucore_terms.rb +0 -33
- data/lib/hydra/pcdm/vocab/sweetjpl_terms.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a31e7e8302b6fa6566390c103c94e588199f2d50
|
4
|
+
data.tar.gz: 35d89b527d58013dd4c4aa0477a59ac6a0235d56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95ea6fa959fb711e4ca1e5a32e92e3e834d42987c00232353111da00bd7a5a5fb50dcced29ff87e3f5d3bbd4e4a08373a1337bac8acb4e1ba37ca3134f5bc417
|
7
|
+
data.tar.gz: 3549b065504e7bed1b1b8ecdbe941618b2000c73006b0cfe471e0250f79b5f4ec70ff9ea6dbbfb31d09ea73d32623378f71b13211071c29011eadc8ce89bc60a
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
DisplayCopNames: true
|
5
|
+
Include:
|
6
|
+
- '**/Rakefile'
|
7
|
+
|
8
|
+
Metrics/LineLength:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/CollectionMethods:
|
12
|
+
PreferredMethods:
|
13
|
+
collect: 'map'
|
14
|
+
collect!: 'map!'
|
15
|
+
inject: 'reduce'
|
16
|
+
detect: 'find'
|
17
|
+
find_all: 'select'
|
18
|
+
|
19
|
+
Style/ClassAndModuleChildren:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/Documentation:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
RSpec/ExampleWording:
|
26
|
+
CustomTransform:
|
27
|
+
be: is
|
28
|
+
have: has
|
29
|
+
not: does not
|
30
|
+
NOT: does NOT
|
31
|
+
IgnoredWords:
|
32
|
+
- only
|
33
|
+
|
34
|
+
RSpec/FilePath:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
RSpec/InstanceVariable:
|
38
|
+
Enabled: false
|
data/Gemfile
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
4
|
-
gem '
|
5
|
-
gem '
|
3
|
+
group :development, :test do
|
4
|
+
gem 'rubocop', require: false
|
5
|
+
gem 'haml-lint', require: false
|
6
|
+
gem 'rubocop-rspec', require: false
|
7
|
+
gem 'pry' unless ENV['CI']
|
8
|
+
gem 'pry-byebug' unless ENV['CI']
|
6
9
|
end
|
7
10
|
|
8
11
|
# Specify your gem's dependencies in hydra-pcdm.gemspec
|
data/README.md
CHANGED
@@ -1,68 +1,60 @@
|
|
1
1
|
# Hydra::PCDM
|
2
|
+
|
3
|
+
[![Version](https://badge.fury.io/rb/hydra-pcdm.png)](http://badge.fury.io/rb/hydra-pcdm)
|
2
4
|
[![Build Status](https://travis-ci.org/projecthydra-labs/hydra-pcdm.svg?branch=master)](https://travis-ci.org/projecthydra-labs/hydra-pcdm)
|
3
5
|
[![Coverage Status](https://coveralls.io/repos/projecthydra-labs/hydra-pcdm/badge.svg?branch=master)](https://coveralls.io/r/projecthydra-labs/hydra-pcdm?branch=master)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/projecthydra-labs/hydra-pcdm/badges/gpa.svg)](https://codeclimate.com/github/projecthydra-labs/hydra-pcdm)
|
7
|
+
[![Apache 2.0 License](http://img.shields.io/badge/APACHE2-license-blue.svg)](./LICENSE)
|
8
|
+
[![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md)
|
9
|
+
[![API Docs](http://img.shields.io/badge/API-docs-blue.svg)](http://rubydoc.info/gems/hydra-pcdm)
|
10
|
+
[![Stories in Ready](https://badge.waffle.io/projecthydra-labs/hydra-works.png?source=projecthydra-labs%2Fhydra-pcdm&label=ready&title=Ready)](https://waffle.io/projecthydra-labs/hydra-works?source=projecthydra-labs%2Fhydra-pcdm)
|
4
11
|
|
5
|
-
Hydra implementation of Portland Common Data
|
12
|
+
Hydra implementation of the Portland Common Data Model (PCDM)
|
6
13
|
|
7
14
|
## Installation
|
8
15
|
|
9
16
|
Add these lines to your application's Gemfile:
|
10
17
|
|
11
18
|
```ruby
|
12
|
-
gem 'active-fedora',
|
13
|
-
gem 'hydra-pcdm',
|
19
|
+
gem 'active-fedora', '~> 9.3'
|
20
|
+
gem 'hydra-pcdm', '~> 0.1'
|
14
21
|
```
|
15
22
|
|
16
|
-
Substitute another branch name for 'master' to test out a branch under development.
|
17
|
-
<!-- Temporarily comment out until gem is published.
|
18
|
-
gem 'hydra-pcdm'
|
19
|
-
-->
|
20
|
-
|
21
23
|
And then execute:
|
22
|
-
|
23
|
-
$ bundle
|
24
|
-
|
25
|
-
|
26
|
-
Or install it yourself as:
|
24
|
+
|
25
|
+
$ bundle install
|
26
|
+
|
27
|
+
Or install it yourself:
|
27
28
|
|
28
29
|
$ gem install hydra-pcdm
|
29
|
-
-->
|
30
30
|
|
31
31
|
## Access Controls
|
32
|
-
We are using [Web ACL](http://www.w3.org/wiki/WebAccessControl) as implemented by [hydra-access](https://github.com/projecthydra/hydra-head/tree/master/hydra-access-controls) controls
|
33
32
|
|
34
|
-
|
33
|
+
We are using [Web ACL](http://www.w3.org/wiki/WebAccessControl) as implemented in [hydra-access-controls](https://github.com/projecthydra/hydra-head/tree/master/hydra-access-controls).
|
35
34
|
|
36
|
-
|
35
|
+
## Portland Common Data Model
|
37
36
|
|
37
|
+
Reference: [Portland Common Data Model](https://github.com/duraspace/pcdm/wiki)
|
38
38
|
|
39
39
|
### Model Definition
|
40
40
|
|
41
41
|
![PCDM Model Definition](https://raw.githubusercontent.com/wiki/duraspace/pcdm/images/coll-object-file.png)
|
42
42
|
|
43
|
-
|
44
|
-
### Example
|
45
|
-
|
46
|
-
To test the model and provide clarity we have included a sample mode that exercises the interfaces provided by the gem.
|
47
|
-
The sample model may change over time to reflect the state of the gam and what it supports.
|
48
|
-
|
49
|
-
![Sandwich Objet Model](https://docs.google.com/drawings/d/1wI4H3AH9pdIPllKIMO356c1cFHUN57azDlgIqMVODSw/pub?w=1369&h=727)
|
50
|
-
|
51
43
|
## Usage
|
52
44
|
|
53
|
-
Hydra
|
45
|
+
Hydra::PCDM provides three core classes:
|
46
|
+
|
54
47
|
```
|
55
48
|
Hydra::PCDM::Object
|
56
49
|
Hydra::PCDM::Collection
|
57
50
|
Hydra::PCDM::File
|
58
51
|
```
|
59
52
|
|
60
|
-
A `Hydra::PCDM::File` is a NonRDFSource &emdash; a bitstream.
|
53
|
+
A `Hydra::PCDM::File` is a NonRDFSource (in [LDP](http://www.w3.org/TR/ldp/) parlance) &emdash; a bitstream. You can use this to store content. A PCDM::File is contained by a PCDM::Object. A `File` may have some attached technical metadata, but no descriptive metadata. A `Hydra::PCDM::Object` may contain `File`s, may have descriptive metadata, and may declare other `Object`s as members (for complex object hierarchies). A `Hydra::PCDM::Collection` may contain other `Collection`s or `Object`s but may not directly contain `File`s. A `Collection` may also have descriptive metadata.
|
61
54
|
|
62
|
-
Typically usage involves extending the behavior provided by this gem. In your application you can write something like this:
|
55
|
+
Typically, usage involves extending the behavior provided by this gem. In your application you can write something like this:
|
63
56
|
|
64
57
|
```ruby
|
65
|
-
|
66
58
|
class Book < ActiveFedora::Base
|
67
59
|
include Hydra::PCDM::ObjectBehavior
|
68
60
|
end
|
@@ -71,17 +63,18 @@ class Collection < ActiveFedora::Base
|
|
71
63
|
include Hydra::PCDM::CollectionBehavior
|
72
64
|
end
|
73
65
|
|
74
|
-
|
75
|
-
|
66
|
+
collection = Collection.create
|
67
|
+
book = Book.create
|
76
68
|
|
77
|
-
|
78
|
-
#
|
79
|
-
|
69
|
+
collection.members = [book]
|
70
|
+
# or: collection.members << book
|
71
|
+
collection.save
|
80
72
|
|
81
|
-
|
82
|
-
|
83
|
-
|
73
|
+
file = book.files.build
|
74
|
+
file.content = "The quick brown fox jumped over the lazy dog."
|
75
|
+
book.save
|
84
76
|
```
|
85
77
|
|
86
|
-
##
|
78
|
+
## Contributing
|
79
|
+
|
87
80
|
If you'd like to contribute to this effort, please check out the [Contributing Guide](CONTRIBUTING.md)
|
data/Rakefile
CHANGED
@@ -2,19 +2,29 @@ require 'bundler/gem_tasks'
|
|
2
2
|
require 'jettywrapper'
|
3
3
|
require 'rspec/core'
|
4
4
|
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
desc 'Run style checker'
|
8
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
9
|
+
task.requires << 'rubocop-rspec'
|
10
|
+
task.fail_on_error = true
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Run test suite and style checker'
|
14
|
+
task :spec do
|
15
|
+
Rake::Task['rubocop'].invoke
|
16
|
+
RSpec::Core::RakeTask.new(:spec)
|
17
|
+
end
|
8
18
|
|
9
19
|
desc 'Spin up hydra-jetty and run specs'
|
10
20
|
task ci: ['jetty:clean'] do
|
11
21
|
puts 'running continuous integration'
|
12
22
|
jetty_params = Jettywrapper.load_config
|
13
|
-
jetty_params[:startup_wait]= 90
|
23
|
+
jetty_params[:startup_wait] = 90
|
14
24
|
error = Jettywrapper.wrap(jetty_params) do
|
15
25
|
Rake::Task['spec'].invoke
|
16
26
|
end
|
17
|
-
|
27
|
+
fail "test failures: #{error}" if error
|
18
28
|
end
|
19
29
|
|
20
30
|
task default: :ci
|
data/hydra-pcdm.gemspec
CHANGED
@@ -4,21 +4,22 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'hydra/pcdm/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'hydra-pcdm'
|
8
8
|
spec.version = Hydra::PCDM::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
15
|
-
spec.required_ruby_version
|
9
|
+
spec.authors = ['E. Lynette Rayle']
|
10
|
+
spec.email = ['elr37@cornell.edu']
|
11
|
+
spec.summary = 'Portland Common Data Model (PCDM)'
|
12
|
+
spec.description = 'Portland Common Data Model (PCDM)'
|
13
|
+
spec.homepage = 'https://github.com/projecthydra-labs/hydra-pcdm'
|
14
|
+
spec.license = 'APACHE2'
|
15
|
+
spec.required_ruby_version = '>= 1.9.3'
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0")
|
18
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
21
21
|
|
22
|
+
spec.add_dependency 'active-fedora', '>= 9.4.1'
|
22
23
|
spec.add_dependency 'activefedora-aggregation', '~> 0.4'
|
23
24
|
spec.add_dependency 'mime-types', '>= 1'
|
24
25
|
|
@@ -27,5 +28,4 @@ Gem::Specification.new do |spec|
|
|
27
28
|
spec.add_development_dependency 'rake'
|
28
29
|
spec.add_development_dependency 'coveralls'
|
29
30
|
spec.add_development_dependency 'rspec'
|
30
|
-
|
31
31
|
end
|
data/lib/hydra/pcdm.rb
CHANGED
@@ -2,15 +2,17 @@ require 'active_support'
|
|
2
2
|
require 'mime/types'
|
3
3
|
require 'active_fedora/aggregation'
|
4
4
|
|
5
|
-
|
6
5
|
module Hydra
|
7
6
|
module PCDM
|
8
7
|
extend ActiveSupport::Autoload
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
module Vocab
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
eager_autoload do
|
12
|
+
autoload :PCDMTerms
|
13
|
+
autoload :SweetJPLTerms
|
14
|
+
end
|
15
|
+
end
|
14
16
|
|
15
17
|
# models
|
16
18
|
autoload_under 'models' do
|
@@ -41,20 +43,19 @@ module Hydra
|
|
41
43
|
autoload :Validators, 'hydra/pcdm/validators'
|
42
44
|
|
43
45
|
# model validations
|
44
|
-
def self.collection?
|
46
|
+
def self.collection?(collection)
|
45
47
|
return false unless collection.respond_to? :type
|
46
|
-
Array(collection.type).include?
|
48
|
+
Array(collection.type).include? Vocab::PCDMTerms.Collection
|
47
49
|
end
|
48
50
|
|
49
|
-
def self.object?
|
51
|
+
def self.object?(object)
|
50
52
|
return false unless object.respond_to? :type
|
51
|
-
Array(object.type).include?
|
53
|
+
Array(object.type).include? Vocab::PCDMTerms.Object
|
52
54
|
end
|
53
55
|
|
54
|
-
def self.file?
|
56
|
+
def self.file?(file)
|
55
57
|
return false unless file.respond_to? :metadata_node
|
56
|
-
Array(file.metadata_node.type).include?
|
58
|
+
Array(file.metadata_node.type).include? Vocab::PCDMTerms.File
|
57
59
|
end
|
58
|
-
|
59
60
|
end
|
60
61
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module Hydra::PCDM
|
2
2
|
class CollectionIndexer < ObjectIndexer
|
3
|
-
|
4
3
|
def generate_solr_document
|
5
4
|
super.tap do |solr_doc|
|
6
|
-
solr_doc[
|
7
|
-
solr_doc[
|
5
|
+
solr_doc['member_ids_ssim'] = object.member_ids
|
6
|
+
solr_doc['collection_ids_ssim'] = object.collection_ids
|
8
7
|
end
|
9
8
|
end
|
10
|
-
|
11
9
|
end
|
12
10
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Hydra::PCDM
|
2
|
-
|
3
2
|
# behavior:
|
4
3
|
# 1) Hydra::PCDM::Collection can aggregate (pcdm:hasMember) Hydra::PCDM::Collection (no infinite loop, e.g., A -> B -> C -> A)
|
5
4
|
# 2) Hydra::PCDM::Collection can aggregate (pcdm:hasMember) Hydra::PCDM::Object
|
@@ -15,17 +14,17 @@ module Hydra::PCDM
|
|
15
14
|
extend ActiveSupport::Concern
|
16
15
|
|
17
16
|
included do
|
18
|
-
type
|
17
|
+
type Vocab::PCDMTerms.Collection
|
19
18
|
include ::Hydra::PCDM::PcdmBehavior
|
20
19
|
|
21
|
-
filters_association :members, as: :
|
20
|
+
filters_association :members, as: :collections, condition: :pcdm_collection?
|
22
21
|
end
|
23
22
|
|
24
23
|
module ClassMethods
|
25
24
|
def indexer
|
26
25
|
Hydra::PCDM::CollectionIndexer
|
27
26
|
end
|
28
|
-
|
27
|
+
|
29
28
|
def type_validator
|
30
29
|
end
|
31
30
|
end
|
@@ -37,6 +36,20 @@ module Hydra::PCDM
|
|
37
36
|
def pcdm_collection?
|
38
37
|
true
|
39
38
|
end
|
39
|
+
|
40
|
+
def child_collections
|
41
|
+
warn '[DEPRECATION] `child_collections` is deprecated in Hydra::PCDM. Please use `collections` instead. This has a target date for removal of 10-31-2015'
|
42
|
+
collections
|
43
|
+
end
|
44
|
+
|
45
|
+
def child_collections=(new_collections)
|
46
|
+
warn '[DEPRECATION] `child_collections=` is deprecated in Hydra::PCDM. Please use `collections=` instead. This has a target date for removal of 10-31-2015'
|
47
|
+
self.collections = new_collections
|
48
|
+
end
|
49
|
+
|
50
|
+
def child_collection_ids
|
51
|
+
warn '[DEPRECATION] `child_collection_ids` is deprecated in Hydra::PCDM. Please use `collection_ids` instead. This has a target date for removal of 10-31-2015'
|
52
|
+
collection_ids
|
53
|
+
end
|
40
54
|
end
|
41
55
|
end
|
42
|
-
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Hydra::PCDM
|
2
|
-
|
3
2
|
# behavior:
|
4
3
|
# 1) Hydra::PCDM::Object can aggregate (pcdm:hasMember) Hydra::PCDM::Object
|
5
4
|
# 2) Hydra::PCDM::Object can aggregate (ore:aggregates) Hydra::PCDM::Object (Object related to the Object)
|
@@ -13,11 +12,11 @@ module Hydra::PCDM
|
|
13
12
|
extend ActiveSupport::Concern
|
14
13
|
|
15
14
|
included do
|
16
|
-
type
|
15
|
+
type Vocab::PCDMTerms.Object
|
17
16
|
include ::Hydra::PCDM::PcdmBehavior
|
18
17
|
|
19
|
-
directly_contains :files, has_member_relation:
|
20
|
-
|
18
|
+
directly_contains :files, has_member_relation: Vocab::PCDMTerms.hasFile,
|
19
|
+
class_name: 'Hydra::PCDM::File'
|
21
20
|
end
|
22
21
|
|
23
22
|
module ClassMethods
|
@@ -40,23 +39,21 @@ module Hydra::PCDM
|
|
40
39
|
false
|
41
40
|
end
|
42
41
|
|
43
|
-
def
|
42
|
+
def in_objects
|
44
43
|
aggregated_by.select(&:pcdm_object?)
|
45
44
|
end
|
46
45
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
files.all? { |f| Hydra::PCDM.file? f }
|
51
|
-
super(files)
|
46
|
+
def parent_objects
|
47
|
+
warn '[DEPRECATION] `parent_objects` is deprecated in Hydra::PCDM. Please use `in_objects` instead. This has a target date for removal of 10-31-2015'
|
48
|
+
in_objects
|
52
49
|
end
|
53
50
|
|
54
51
|
# Returns directly contained files that have the requested RDF Type
|
55
52
|
# @param [RDF::URI] uri for the desired Type
|
56
53
|
# @example
|
57
54
|
# filter_files_by_type(::RDF::URI("http://pcdm.org/ExtractedText"))
|
58
|
-
def filter_files_by_type
|
59
|
-
|
55
|
+
def filter_files_by_type(uri)
|
56
|
+
files.reject do |file|
|
60
57
|
!file.metadata_node.type.include?(uri)
|
61
58
|
end
|
62
59
|
end
|
@@ -65,10 +62,10 @@ module Hydra::PCDM
|
|
65
62
|
# @param [RDF::URI] uri for the desired Type
|
66
63
|
# @example
|
67
64
|
# file_of_type(::RDF::URI("http://pcdm.org/ExtractedText"))
|
68
|
-
def file_of_type
|
69
|
-
matching_files =
|
70
|
-
if
|
71
|
-
file =
|
65
|
+
def file_of_type(uri)
|
66
|
+
matching_files = filter_files_by_type(uri)
|
67
|
+
if matching_files.empty?
|
68
|
+
file = files.build
|
72
69
|
Hydra::PCDM::AddTypeToFile.call(file, uri)
|
73
70
|
else
|
74
71
|
return matching_files.first
|
@@ -76,4 +73,3 @@ module Hydra::PCDM
|
|
76
73
|
end
|
77
74
|
end
|
78
75
|
end
|
79
|
-
|