mei 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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