mei 0.0.1 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a696a787a23dcbf11035442725f178f55e108522
4
- data.tar.gz: a623ba74eb922f194ae079038f8b24d77a24f3b9
3
+ metadata.gz: 231eb832fec0dc120fa890741165d2f5121eadac
4
+ data.tar.gz: 34cf84d2cea0f9b062b172f504a1879d899db261
5
5
  SHA512:
6
- metadata.gz: c6a6b15e947675ff12b4a336a2bb90cb3d46c712725d397a6afe1130bbec5300ea3d31bcd398b637a2c3dbc99fd7ecb0d1d689d64be0d70f37f06e351bf7c462
7
- data.tar.gz: 96b7ab7dadd44f82e54430e65bcb66018968bf880cf0825a8b62c531c0e22196287c7ade93ee76dcc531cc741f91e7420fb5eb32291198349a79eb4fac673802
6
+ metadata.gz: 1fd06ca113c6f3f8ec2f12da19cd04c592f0b671fecbf378628499ea78f2b887c7ce026e454e1f2fc8b0cfa307f165ab98973203ecefa30479b8b5175bef8654
7
+ data.tar.gz: ac2e034520b2c2baf260ca8fe261d84f59cc5590c0507c43fa81a9a445ec93faf0f9530eabdd5076a5dc2d68b0d8b62e7f6a473cf5814c98130d3abc61882064
@@ -1,3 +1,21 @@
1
1
  = Mei
2
2
 
3
- This project rocks and uses MIT-LICENSE.
3
+ This adds a Metadata Enrichment Interface to a Hydra / ActiveFedora based project and uses Linked Data Fragments to resolve uris.
4
+ Currently in an alpha state
5
+
6
+ ## Installation
7
+
8
+ Add the following to your Gemfile:
9
+
10
+ ```
11
+ gem 'mei'
12
+ ```
13
+
14
+ The run:
15
+
16
+ $ rails generate mei:install
17
+
18
+
19
+ ## Further configuration
20
+
21
+ See slides on this setup at https://goo.gl/BP2inf .
@@ -0,0 +1,110 @@
1
+
2
+
3
+ Blacklight.onLoad(function() {
4
+ function get_autocomplete_opts(field) {
5
+ var autocomplete_opts = {
6
+ minLength: 2,
7
+ source: function( request, response ) {
8
+ $.getJSON( "/authorities/generic_files/" + field, {
9
+ q: request.term
10
+ }, response );
11
+ },
12
+ focus: function() {
13
+ // prevent value inserted on focus
14
+ return false;
15
+ },
16
+ complete: function(event) {
17
+ $('.ui-autocomplete-loading').removeClass("ui-autocomplete-loading");
18
+ }
19
+ };
20
+ return autocomplete_opts;
21
+ }
22
+
23
+
24
+ /* var autocomplete_vocab = new Object();
25
+
26
+ autocomplete_vocab.url_var = ['other_subject', 'language', 'creator', 'contributor']; // the url variable to pass to determine the vocab to attach to
27
+ autocomplete_vocab.field_name = new Array(); // the form name to attach the event for autocomplete
28
+
29
+ // loop over the autocomplete fields and attach the
30
+ // events for autocomplete and create other array values for autocomplete
31
+ for (var i=0; i < autocomplete_vocab.url_var.length; i++) {
32
+ autocomplete_vocab.field_name.push('generic_file_' + autocomplete_vocab.url_var[i]);
33
+ // autocompletes
34
+ $("input." + autocomplete_vocab.field_name[i])
35
+ // don't navigate away from the field on tab when selecting an item
36
+ .bind( "keydown", function( event ) {
37
+ if ( event.keyCode === $.ui.keyCode.TAB &&
38
+ $( this ).data( "autocomplete" ).menu.active ) {
39
+ event.preventDefault();
40
+ }
41
+ })
42
+ .autocomplete( get_autocomplete_opts(autocomplete_vocab.url_var[i]) );
43
+ }*/
44
+
45
+ function duplicate_field_click(event) {
46
+ original_element = $(event.target).parent().parent().parent().children().children();
47
+ original_id = original_element.attr("id");
48
+
49
+ /* if(original_id == 'generic_file_language' || original_id == 'generic_file_other_subject' || original_id == 'generic_file_creator' || original_id == 'generic_file_contributor' ) {
50
+ cloned_element = $(event.target).parent().parent().parent().clone()
51
+ } else {
52
+ cloned_element = $(event.target).parent().parent().parent().clone(true, true);
53
+ }*/
54
+
55
+
56
+
57
+ cloned_element.find("input").val("");
58
+ cloned_element.find("textarea").val("");
59
+ cloned_element.find("select").val("");
60
+
61
+ $(event.target).parent().parent().parent().after(cloned_element);
62
+
63
+
64
+ /* if(original_id == 'generic_file_language' || original_id == 'generic_file_other_subject' || original_id == 'generic_file_creator' || original_id == 'generic_file_contributor' ) {
65
+ added_element = $(event.target).parent().parent().parent().next().children().children();
66
+ added_add_button = $(event.target).parent().parent().parent().next().children().children('.regular_dta_duplicate_span').children('.regular_dta_duplicate_field');
67
+ remove_add_button = $(event.target).parent().parent().parent().next().children().children('.regular_dta_delete_span').children('.regular_dta_delete_field');
68
+ //added_id = added_element.attr("id");
69
+ if(original_id == 'generic_file_language') {
70
+ $(added_element).autocomplete(get_autocomplete_opts('language'));
71
+ } else if(original_id == 'generic_file_other_subject') {
72
+ $(added_element).autocomplete(get_autocomplete_opts('other_subject'));
73
+ } else if(original_id == 'generic_file_creator') {
74
+ $(added_element).autocomplete(get_autocomplete_opts('creator'));
75
+ } else if(original_id == 'generic_file_contributor') {
76
+ $(added_element).autocomplete(get_autocomplete_opts('contributor'));
77
+ }
78
+
79
+ added_add_button.click(duplicate_field_click);
80
+ remove_add_button.click(delete_field_click);
81
+ }*/
82
+
83
+ }
84
+
85
+ function delete_field_click(event) {
86
+ //parent().parent().children().length
87
+ local_field_name = $(event.target).parent().prev().prev().attr('name');
88
+
89
+ //Current hack for lookup fields... may need more when I add hidden fields...
90
+ if(local_field_name == undefined) {
91
+ local_field_name = $(event.target).parent().prev().prev().prev().attr('name');
92
+ }
93
+ if ($('input[name*="' + local_field_name + '"]').length == 1) {
94
+ //$(event.target).parent().parent().parent().parent().find("input").val("");
95
+ $(event.target).parent().parent().parent().find("input").val("");
96
+ } else if($('select[name*="' + local_field_name + '"]').length == 1) {
97
+ //$(event.target).parent().parent().parent().parent().find("select").val("");
98
+ $(event.target).parent().parent().parent().find("select").val("");
99
+ } else {
100
+ //$(event.target).parent().parent().parent().parent().remove();
101
+ $(event.target).parent().parent().parent().remove();
102
+ }
103
+ }
104
+
105
+ $(".regular_dta_duplicate_field").click(duplicate_field_click);
106
+
107
+ $(".regular_dta_delete_field").click(delete_field_click);
108
+
109
+ });
110
+
@@ -0,0 +1,80 @@
1
+ .mei_alt_table_row {
2
+ background-color: #D3D3D3;
3
+ }
4
+
5
+ .mei_table_style {
6
+ padding: 10px;
7
+ width:100%;
8
+ }
9
+
10
+ .mei_table_style tr td {
11
+ padding-left:10px;
12
+ padding-top:5px;
13
+ padding-bottom: 5px;
14
+ }
15
+
16
+ /*.modal.modal-wide {
17
+ width: 90%;
18
+ }*/
19
+
20
+ .regular_dta_duplicate_span {
21
+ width:35px;
22
+ }
23
+
24
+ .repeat_field_value, .no_repeat_field_value {
25
+ .btn.add, .btn.remove {
26
+ width: 6em;
27
+ }
28
+
29
+ .field-wrapper {
30
+ list-style-type:none;
31
+ width:100%;
32
+ }
33
+
34
+ .listing {
35
+ margin-left: 0;
36
+ max-width: 50em;
37
+ padding-left: 0px;
38
+ .input-group {
39
+ margin-bottom: 1px;
40
+ }
41
+ }
42
+
43
+ .field-controls span {
44
+ margin-left:.2em;
45
+ }
46
+
47
+ .field-controls {
48
+ margin-left: 2em;
49
+ }
50
+
51
+ .message{
52
+ background-size: 40px 40px;
53
+ background-image: linear-gradient(135deg, rgba(255, 255, 255, .05) 25%, transparent 25%,
54
+ transparent 50%, rgba(255, 255, 255, .05) 50%, rgba(255, 255, 255, .05) 75%,
55
+ transparent 75%, transparent);
56
+ box-shadow: inset 0 -1px 0 rgba(255,255,255,.4);
57
+ width: 100%;
58
+ border: 1px solid;
59
+ color: #fff;
60
+ padding: 10px;
61
+ text-shadow: 0 1px 0 rgba(0,0,0,.5);
62
+ animation: animate-bg 5s linear infinite;
63
+ //border-radius: $border-radius-base;
64
+ }
65
+
66
+ .has-error{
67
+ background-color: #de4343;
68
+ border-color: #c43d3d;
69
+ }
70
+
71
+ .has-warning{
72
+ background-color: #eaaf51;
73
+ border-color: #d99a36;
74
+ }
75
+ }
76
+
77
+ // The contributor listing needs some normalization
78
+ #contributors .listing {
79
+ max-width:20em;
80
+ }
@@ -0,0 +1,4 @@
1
+ module Mei
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,55 @@
1
+ require 'rdf'
2
+ require 'cgi'
3
+
4
+ # This controller is used for all requests to all authorities. It will verify params and figure out
5
+ # which class to instantiate based on the "vocab" param. All the authotirty classes inherit from a
6
+ # super class so they implement the same methods.
7
+
8
+ class Mei::TermsController < ApplicationController
9
+ def query
10
+ s = params.fetch("q", "")
11
+ e = params.fetch("e", "")
12
+ field = params[:term]
13
+
14
+ ldf_server = Mei::TermsController.mei_config[:ldf_server]
15
+ selected_config = Mei::TermsController.mei_config[:form_fields].select { |item| item["id"] == field}
16
+ return [] if selected_config.blank?
17
+
18
+ hits = case selected_config.first["adapter"].to_sym
19
+ when :lcsh
20
+ Mei::LcshSubjectResource.find(s,e,selected_config.first["solr_field"])
21
+ when :geonames
22
+ Mei::GeoNamesResource.find(s,e,selected_config.first["solr_field"])
23
+ when :homosaurus
24
+ Mei::HomosaurusSubjectResource.find(s,e,selected_config.first["solr_field"])
25
+ else
26
+ []
27
+ end
28
+
29
+ render json: hits
30
+ end
31
+
32
+ def self.mei_config
33
+ @config ||= YAML::load(File.open(config_path))[env]
34
+ .with_indifferent_access
35
+ end
36
+
37
+ def self.app_root
38
+ return @app_root if @app_root
39
+ @app_root = Rails.root if defined?(Rails) and defined?(Rails.root)
40
+ @app_root ||= APP_ROOT if defined?(APP_ROOT)
41
+ @app_root ||= '.'
42
+ end
43
+
44
+ def self.env
45
+ return @env if @env
46
+ #The following commented line always returns "test" in a rails c production console. Unsure of how to fix this yet...
47
+ #@env = ENV["RAILS_ENV"] = "test" if ENV
48
+ @env ||= Rails.env if defined?(Rails) and defined?(Rails.root)
49
+ @env ||= 'development'
50
+ end
51
+
52
+ def self.config_path
53
+ File.join(app_root, 'config', 'mei.yml')
54
+ end
55
+ end
@@ -0,0 +1,2 @@
1
+ module Mei::ApplicationHelper
2
+ end
@@ -0,0 +1,40 @@
1
+ class LcshLookupInput < MeiMultiLookupInput
2
+ #include WithHelpIcon
3
+
4
+ def buffer_each(collection)
5
+ collection.each_with_object('').with_index do |(value, buffer), index|
6
+ #buffer << yield(value, index) if value.match(/http:\/\/id.loc.gov\/authorities\/subjects\//) || value.blank?
7
+ if value.blank?
8
+ buffer << yield(value, index)
9
+ elsif value.match(/http:\/\/id.loc.gov\/authorities\/subjects\//)
10
+ english_label = nil
11
+ default_label = nil
12
+ any_match = nil
13
+
14
+ repo = Mei::Loc.pop_graph(value)
15
+
16
+ if repo.query(:subject=>::RDF::URI.new(value), :predicate=>Mei::Loc.qskos('prefLabel')).count > 0
17
+ repo.query(:subject=>::RDF::URI.new(value), :predicate=>Mei::Loc.qskos('prefLabel')).each_statement do |result_statement|
18
+ #LoC has blank nodes... see alts of http://id.loc.gov/authorities/subjects/sh85102696 ... these aren't literals.
19
+ #LoC's blank node representation.... alt: to_s == "_:t829213" or check .resource? or check .node? or .id == 't829213'
20
+ if result_statement.object.literal?
21
+ if result_statement.object.language == :en
22
+ english_label ||= result_statement.object.value
23
+ elsif result_statement.object.language.blank?
24
+ default_label ||= result_statement.object.value
25
+ else
26
+ any_match ||= result_statement.object.value
27
+ end
28
+ end
29
+ end
30
+
31
+ default_label ||= any_match
32
+ english_label ||= default_label
33
+
34
+ buffer << yield("#{english_label} (#{value})", index)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,60 @@
1
+ class MeiMultiLookupInput < MultiValueInput
2
+ # Overriding this so that the class is correct and the javascript for multivalue will work on this.
3
+ def input_type
4
+ 'multi_value'.freeze
5
+ end
6
+
7
+ def inner_wrapper
8
+ <<-HTML
9
+ <li class="field-wrapper">
10
+ <div class="input-group col-sm-12">
11
+ #{yield}
12
+ <button style="width:auto;" type="button" class="btn btn-default" data-toggle="modal" data-target="#meiLookupModal_#{attribute_name}">Lookup</button>
13
+ </div>
14
+ </li>
15
+ HTML
16
+ end
17
+
18
+ # Although the 'index' parameter is not used in this implementation it is useful in an
19
+ # an overridden version of this method, especially when the field is a complex object and
20
+ # the override defines nested fields.
21
+ def build_field_options(value, index)
22
+ options = input_html_options.dup
23
+
24
+ options[:value] = value
25
+ if @rendered_first_element
26
+ options[:id] = nil
27
+ options[:required] = nil
28
+ else
29
+ options[:id] ||= input_dom_id
30
+ end
31
+ options[:class] ||= []
32
+ options[:class] += ["#{input_dom_id} form-control multi-text-field"]
33
+ options[:style] ||= []
34
+ options[:style] += ["width:80%"]
35
+ options[:'aria-labelledby'] = label_id
36
+ @rendered_first_element = true
37
+
38
+ options
39
+ end
40
+
41
+ =begin
42
+ def inner_wrapper
43
+ <<-HTML
44
+ <li class="field-wrapper">
45
+ <div class="input-group col-sm-12">
46
+ #{yield}
47
+
48
+ <button style="width:auto;" type="button" class="btn btn-default" data-toggle="modal" data-target="#meiLookupModal_#{attribute_name}">Lookup</button>
49
+ <span class="input-group-btn regular_dta_duplicate_span">
50
+ <button class="btn btn-success regular_dta_duplicate_field" type="button">+</button>
51
+ </span>
52
+ <span class="input-group-btn regular_dta_delete_span">
53
+ <button class="btn btn-danger regular_dta_delete_field" type="button">-</button>
54
+ </span>
55
+ </div>
56
+ </li>
57
+ HTML
58
+ end
59
+ =end
60
+ end
@@ -0,0 +1,32 @@
1
+ module WithHelpIcon
2
+ def label(wrapper_options = nil)
3
+ "#{super} #{link_to_help}"
4
+ end
5
+
6
+ protected
7
+
8
+ def link_to_help
9
+ template.link_to '#', id: "#{input_class}_help", rel: 'popover'.freeze,
10
+ 'data-content' => metadata_help, 'data-original-title' => raw_label_text,
11
+ 'aria-label' => aria_label, 'data-trigger' => 'focus' do
12
+ help_icon
13
+ end
14
+ end
15
+
16
+ def help_icon
17
+ template.content_tag 'i', nil, "aria-hidden" => true, class: "help-icon"
18
+ end
19
+
20
+ def metadata_help
21
+ translate_from_namespace(:metadata_help) || attribute_name.to_s.humanize
22
+ end
23
+
24
+ def aria_label
25
+ translate_from_namespace(:aria_label) || default_aria_label
26
+ end
27
+
28
+ def default_aria_label
29
+ I18n.t("#{i18n_scope}.aria_label.#{lookup_model_names.join('.')}.default",
30
+ title: attribute_name.to_s.humanize)
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Mei
2
+ class GeoNamesResource
3
+ def self.find(subject, type, solr_field)
4
+ authority_check = Mei::Geonames.new(type)
5
+ authority_result = authority_check.search(subject) #URI escaping doesn't work for Baseball fields?
6
+ if authority_result.present?
7
+ return authority_result
8
+ else
9
+ return []
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,132 @@
1
+ module Mei
2
+ class HomosaurusSubjectResource
3
+
4
+ def self.find(subject, type, solr_field)
5
+ #solr_response = ActiveFedora::Base.find_with_conditions({subject_tesim: "test"} , rows: '10', fl: 'subject_tesim' )
6
+ #ActiveFedora::Base.find_each("subject_tesim:tes*") { |x| puts 'hi' }
7
+ matches = []
8
+ dup_checker = []
9
+ subject = subject.downcase #FIXME?
10
+
11
+ solr_response = Homosaurus.find_with_conditions("dta_homosaurus_lcase_prefLabel_ssi:*#{solr_clean(subject)}*", rows: '25', fl: 'identifier_ssi, prefLabel_ssim, altLabel_ssim, narrower_ssim, broader_ssim, related_ssim' )
12
+
13
+ #FIXME - A result for "http" gives back the entire array of values...
14
+ if solr_response.present?
15
+
16
+ solr_response.each do |indv_response|
17
+ indv_subj = indv_response["identifier_ssi"]
18
+ if dup_checker.length < 20 && !dup_checker.include?(indv_subj)
19
+ matches << indv_response
20
+ dup_checker << indv_subj
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ if dup_checker.length < 20
27
+ solr_response = Homosaurus.find_with_conditions("dta_homosaurus_lcase_altLabel_ssim:*#{solr_clean(subject)}*", rows: '25', fl: 'identifier_ssi, prefLabel_ssim, altLabel_ssim, narrower_ssim, broader_ssim, related_ssim' )
28
+
29
+ #FIXME - A result for "http" gives back the entire array of values...
30
+ if solr_response.present?
31
+
32
+ solr_response.each do |indv_response|
33
+ indv_subj = indv_response["identifier_ssi"]
34
+ if dup_checker.length < 20 && !dup_checker.include?(indv_subj)
35
+ matches << indv_response
36
+ dup_checker << indv_subj
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ if dup_checker.length < 20
43
+ solr_response = Homosaurus.find_with_conditions("dta_homosaurus_lcase_comment_tesi:#{solr_clean(subject)}", rows: '25', fl: 'identifier_ssi, prefLabel_ssim, altLabel_ssim, comment_ssim, narrower_ssim, broader_ssim, related_ssim' )
44
+
45
+ #FIXME - A result for "http" gives back the entire array of values...
46
+ if solr_response.present?
47
+
48
+ solr_response.each do |indv_response|
49
+ indv_subj = indv_response["identifier_ssi"]
50
+ if dup_checker.length < 20 && !dup_checker.include?(indv_subj)
51
+ matches << indv_response
52
+ dup_checker << indv_subj
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ if matches.present?
59
+
60
+ return matches.map! { |item|
61
+ ##{URI.escape(item)}
62
+ full_uri = 'http://homosaurus.org/terms/' + item['identifier_ssi']
63
+
64
+ count = ActiveFedora::Base.find_with_conditions("homosaurus_subject_ssim:#{solr_clean(full_uri)}", rows: '100', fl: 'subject_tesim' ).length
65
+ if count >= 99
66
+ count = "99+"
67
+ else
68
+ count = count.to_s
69
+ end
70
+
71
+ variants = item['altLabel_ssim']
72
+
73
+ {
74
+ "uri_link" => full_uri,
75
+ "label" => item['prefLabel_ssim'].first,
76
+ "broader" => getBroader(item['broader_ssim']),
77
+ "narrower" => getNarrower(item['narrower_ssim']),
78
+ "related" => getRelated(item['related_ssim']),
79
+ "variants" => variants,
80
+ "count" => count
81
+ }
82
+ }
83
+ else
84
+ return []
85
+ end
86
+ end
87
+
88
+ def self.getBroader(broader_uris)
89
+ broader_list = []
90
+
91
+ if broader_uris.present?
92
+ broader_uris.each do |broader_single_uri|
93
+ broader_label = Homosaurus.find_with_conditions("id:#{solr_clean(broader_single_uri)}", rows: '1', fl: 'prefLabel_ssim' )
94
+ broader_list << {:uri_link=>"http://homosaurus.org/terms/#{broader_single_uri.split('/').last}", :label=>broader_label[0]["prefLabel_ssim"][0]}
95
+ end
96
+ end
97
+
98
+ return broader_list
99
+ end
100
+
101
+ def self.getNarrower(narrower_uris)
102
+ narrower_list = []
103
+
104
+ if narrower_uris.present?
105
+ narrower_uris.each do |narrower_single_uri|
106
+ narrower_label = Homosaurus.find_with_conditions("id:#{solr_clean(narrower_single_uri)}", rows: '1', fl: 'prefLabel_ssim' )
107
+ narrower_list << {:uri_link=>"http://homosaurus.org/terms/#{narrower_single_uri.split('/').last}", :label=>narrower_label[0]["prefLabel_ssim"][0]}
108
+ end
109
+ end
110
+
111
+ return narrower_list
112
+ end
113
+
114
+ def self.getRelated(related_uris)
115
+ related_list = []
116
+
117
+ if related_uris.present?
118
+ related_uris.each do |related_single_uri|
119
+ related_label = Homosaurus.find_with_conditions("id:#{solr_clean(related_single_uri)}", rows: '1', fl: 'prefLabel_ssim' )
120
+ related_list << {:uri_link=>"http://homosaurus.org/terms/#{related_single_uri.split('/').last}", :label=>related_label[0]["prefLabel_ssim"][0]}
121
+ end
122
+ end
123
+
124
+ return related_list
125
+ end
126
+
127
+ def self.solr_clean(term)
128
+ return term.gsub('\\', '\\\\').gsub(':', '\\:').gsub(' ', '\ ')
129
+ end
130
+
131
+ end
132
+ end