Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: update concept mappings table UI #798

Merged
merged 17 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions app/assets/stylesheets/mappings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,28 @@ div#map_from_concept_details_table, div#map_to_concept_details_table {

#concept_mappings_table {
width: 100%;
word-break: break-word;
font-size: 13px;
}
.mappings-table-mapping-to{
width: 45%;
}
.mappings-table-mapping-to .chip-button-component-container{
text-wrap: wrap !important;
}
.mappings-table-mapping-to a{
background-color: unset;
line-height: unset;
padding: unset;
font-weight: unset;
font-size: unset;
}
.mappings-table-icon{
width: 18px;
height: 18px;
}
.mappings-table-icon path{
fill: var(--gray-color);
}

.summary-mappings-tab-table {
Expand All @@ -341,4 +363,7 @@ div#map_from_concept_details_table, div#map_to_concept_details_table {
padding: 10px;
outline: none;
margin-left: 0 !important;
}
.mappings-table-actions svg path{
fill: var(--primary-color);
}
2 changes: 1 addition & 1 deletion app/components/link_text_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def initialize(text:, icon: nil, target: nil)
end

def call
svg_icon = !@icon&.empty? ? inline_svg(@icon) : ''
svg_icon = !@icon&.empty? ? inline_svg(@icon, width: '14px', height: '14px') : ''
extra_span = @text == t('mappings.upload_mappings') ? '' : "<span class='mx-1'>#{svg_icon}</span>"
"#{@text}#{extra_span}".html_safe
end
Expand Down
54 changes: 53 additions & 1 deletion app/controllers/mappings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,56 @@ def valid_values?(values)
end
errors
end
end

def set_mapping_target(concept_to_id:, ontology_to:, mapping_type: )
case mapping_type
when 'interportal'
@map_to_interportal, @map_to_interportal_ontology = ontology_to.match(%r{(.*)/ontologies/(.*)}).to_a[1..]
@map_to_interportal_class = concept_to_id
when 'external'
@map_to_external_ontology = ontology_to
@map_to_external_class = concept_to_id
else
@map_to_bioportal_ontology_id = ontology_to
@map_to_bioportal_full_id = concept_to_id
end
end

def get_mappings_target_params
mapping_type = Array(params[:mapping_type]).first
external = true
case mapping_type
when 'interportal'
ontology_to = "#{params[:map_to_interportal]}/ontologies/#{params[:map_to_interportal_ontology]}"
concept_to_id = params[:map_to_interportal_class]
when 'external'
ontology_to = params[:map_to_external_ontology]
concept_to_id = params[:map_to_external_class]
else
ontology_to = params[:map_to_bioportal_ontology_id]
concept_to_id = params[:map_to_bioportal_full_id]
external = false
end
[ontology_to, concept_to_id, external]
end

def get_mappings_target
ontology_to, concept_to_id, external_mapping = get_mappings_target_params
target = ''
if external_mapping
target_ontology = ontology_to
target = concept_to_id
else
if helpers.link?(ontology_to)
target_ontology = LinkedData::Client::Models::Ontology.find(ontology_to)
else
target_ontology = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_to).first
end
if target_ontology
target = target_ontology.explore.single_class(concept_to_id).id
target_ontology = target_ontology.id
end
end
[target_ontology, target, external_mapping]
end
end
174 changes: 83 additions & 91 deletions app/helpers/mappings_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,85 @@ module MappingsHelper

# Used to replace the full URI by the prefixed URI
RELATIONSHIP_PREFIX = {
"http://www.w3.org/2004/02/skos/core#" => "skos:",
"http://www.w3.org/2000/01/rdf-schema#" => "rdfs:",
"http://www.w3.org/2002/07/owl#" => "owl:",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" => "rdf:",
"http://purl.org/linguistics/gold/" => "gold:",
"http://lemon-model.net/lemon#" => "lemon:"
'http://www.w3.org/2004/02/skos/core#' => 'skos:',
'http://www.w3.org/2000/01/rdf-schema#' => 'rdfs:',
'http://www.w3.org/2002/07/owl#' => 'owl:',
'http://www.w3.org/1999/02/22-rdf-syntax-ns#' => 'rdf:',
'http://purl.org/linguistics/gold/' => 'gold:',
'http://lemon-model.net/lemon#' => 'lemon:'
}

INTERPORTAL_HASH = $INTERPORTAL_HASH
Bilelkihal marked this conversation as resolved.
Show resolved Hide resolved


# a little method that returns true if the URIs array contain a gold:translation or gold:freeTranslation
def translation?(relation_array)
if relation_array.kind_of?(Array)
relation_array.map!(&:downcase)
if relation_array.include? "http://purl.org/linguistics/gold/translation"
true
elsif relation_array.include? "http://purl.org/linguistics/gold/freetranslation"
true
else
false
def mapping_links(mapping, concept)
target_concept = mapping.classes.select do |c|
c.id != concept.id && c.links['ontology'] != concept.links['ontology']
end.first
target_concept ||= mapping.classes.last
process = mapping.process || {}

if inter_portal_mapping?(target_concept)
cls_link = ajax_to_inter_portal_cls(target_concept)
ont_name = target_concept.links['ontology']
ont_link = link_to ont_name, get_inter_portal_ui_link(ont_name, process['name']), target: '_blank'
source_tooltip = 'Internal-portal'
elsif internal_mapping?(target_concept)
begin
ont = target_concept.explore.ontology
ont_name = ont.acronym
ont_link = link_to ont_name, ontology_path(ont_name), 'data-turbo-frame': '_top'
rescue
ont_name = target_concept.links['ontology'] || target_concept.id
ont_link = ont_name
end
cls_link = raw(get_link_for_cls_ajax(target_concept.id, ont_name, '_top'))
source_tooltip = 'Internal'
else
false
cls_label = ExternalLinkTextComponent.new(text: target_concept.links['self']).call
cls_link = raw("<a href='#{target_concept.links["self"]}' target='_blank'>#{cls_label}</a>")
ont_name = target_concept.links['ontology']
ont_link = link_to ExternalLinkTextComponent.new(text: ont_name).call, target_concept.links['ontology'],
target: '_blank'
source_tooltip = 'External'
end

[cls_link, ont_link, source_tooltip]
end

def mapping_prefixed_relations(mapping)
process = mapping.process || {}
Array(process[:relation]).each { |relation| get_prefixed_uri(relation) }
end

def mapping_type_tooltip(map)
relations = mapping_prefixed_relations(map)
process = map.process || {}
type = if map.source.to_s.include? 'SKOS'
'SKOS'
else
map.source
end
types_description = {
Bilelkihal marked this conversation as resolved.
Show resolved Hide resolved
'CUI' => t('mappings.types_description.cui'),
'LOOM' => t('mappings.types_description.loom'),
'REST' => t('mappings.types_description.rest'),
'SAME_URI' => t('mappings.types_description.same_uri'),
'SKOS' => t('mappings.types_description.skos')
}
type_tooltip = content_tag(:div, "#{map.source} #{relations.join(', ')} : #{types_description[type]} #{process[:source_name]}".strip, style: 'width: 300px')
[type, type_tooltip]
end

# a little method that returns the uri with a prefix : http://purl.org/linguistics/gold/translation become gold:translation
def get_prefixed_uri(uri)
RELATIONSHIP_PREFIX.each { |k, v| uri.sub!(k, v) }
return uri
uri
end

# method to get (using http) prefLabel for interportal classes
# Using bp_ajax_controller.ajax_process_interportal_cls will try to resolve class labels.
def ajax_to_inter_portal_cls(cls)
inter_portal_acronym = get_inter_portal_acronym(cls.links["ui"])
inter_portal_acronym = get_inter_portal_acronym(cls.links['ui'])
href_cls = " href='#{cls.links["ui"]}' "
if inter_portal_acronym
data_cls = " data-cls='#{cls.links["self"]}?apikey=' "
Expand All @@ -52,15 +94,15 @@ def ajax_to_inter_portal_cls(cls)

def ajax_to_internal_cls(cls)
link_to("#{cls.id}<span href='/ajax/classes/label?ontology=#{cls.links["ontology"]}&concept=#{escape(cls.id)}' class='get_via_ajax'></span>".html_safe,
ontology_path(cls.explore.ontology.acronym, p: 'classes', conceptid: cls.id), target: "_blank")
ontology_path(cls.explore.ontology.acronym, p: 'classes', conceptid: cls.id), target: '_blank')
end

# to get the apikey from the interportal instance of the interportal class.
# The best way to know from which interportal instance the class came is to compare the UI url
def get_inter_portal_acronym(class_ui_url)
if !INTERPORTAL_HASH.nil?
INTERPORTAL_HASH.each do |key, value|
if class_ui_url.start_with?(value["ui"])
if class_ui_url.start_with?(value['ui'])
return key
else
return nil
Expand All @@ -71,11 +113,11 @@ def get_inter_portal_acronym(class_ui_url)

# method to extract the prefLabel from the external class URI
def get_label_for_external_cls(class_uri)
if class_uri.include? "#"
prefLabel = class_uri.split("#")[-1]
else
prefLabel = class_uri.split("/")[-1]
end
prefLabel = if class_uri.include? '#'
class_uri.split('#')[-1]
else
class_uri.split('/')[-1]
end
return prefLabel
end

Expand All @@ -86,11 +128,11 @@ def ajax_to_external_cls(cls)
# Replace the inter_portal mapping ontology URI (that link to the API) by the link to the ontology in the UI
def get_inter_portal_ui_link(uri, process_name)
process_name = '' if process_name.nil?
interportal_acronym = process_name.split(" ")[2]
if interportal_acronym.nil? || interportal_acronym.empty?
interportal_acronym = process_name.split(' ')[2]
if interportal_acronym.nil? || interportal_acronym.empty? || INTERPORTAL_HASH[interportal_acronym].nil?
uri
else
uri.sub!(INTERPORTAL_HASH[interportal_acronym]["api"], INTERPORTAL_HASH[interportal_acronym]["ui"])
uri.sub!(INTERPORTAL_HASH[interportal_acronym]['api'], INTERPORTAL_HASH[interportal_acronym]['ui'])
end
end

Expand All @@ -99,66 +141,14 @@ def internal_mapping?(cls)
end

def inter_portal_mapping?(cls)
!internal_mapping?(cls) && cls.links.has_key?("ui")
end

def get_mappings_target_params
mapping_type = Array(params[:mapping_type]).first
external = true
case mapping_type
when 'interportal'
ontology_to = "#{params[:map_to_interportal]}/ontologies/#{params[:map_to_interportal_ontology]}"
concept_to_id = params[:map_to_interportal_class]
when 'external'
ontology_to = params[:map_to_external_ontology]
concept_to_id = params[:map_to_external_class]
else
ontology_to = params[:map_to_bioportal_ontology_id]
concept_to_id = params[:map_to_bioportal_full_id]
external = false
end
[ontology_to, concept_to_id, external]
end

def set_mapping_target(concept_to_id:, ontology_to:, mapping_type: )
case mapping_type
when 'interportal'
@map_to_interportal, @map_to_interportal_ontology = ontology_to.match(%r{(.*)/ontologies/(.*)}).to_a[1..]
@map_to_interportal_class = concept_to_id
when 'external'
@map_to_external_ontology = ontology_to
@map_to_external_class = concept_to_id
else
@map_to_bioportal_ontology_id = ontology_to
@map_to_bioportal_full_id = concept_to_id
end
end

def get_mappings_target
ontology_to, concept_to_id, external_mapping = get_mappings_target_params
target = ''
if external_mapping
target_ontology = ontology_to
target = concept_to_id
else
if helpers.link?(ontology_to)
target_ontology = LinkedData::Client::Models::Ontology.find(ontology_to)
else
target_ontology = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_to).first
end
if target_ontology
target = target_ontology.explore.single_class(concept_to_id).id
target_ontology = target_ontology.id
end
end
[target_ontology, target, external_mapping]
!internal_mapping?(cls) && cls.links.has_key?('ui')
end

def type?(type)
@mapping_type.nil? && type.eql?('internal') || @mapping_type.eql?(type)
end

def concept_mappings_loader(ontology_acronym: ,concept_id: )
def concept_mappings_loader(ontology_acronym:, concept_id:)
content_tag(:span, id: 'mapping_count') do
concat(content_tag(:div, class: 'concepts-mapping-count ml-1 mr-1') do
render(TurboFrameComponent.new(
Expand All @@ -173,21 +163,23 @@ def concept_mappings_loader(ontology_acronym: ,concept_id: )
end

def client_filled_modal
link_to_modal "", ""
link_to_modal '', ''
end

def mappings_bubble_view_legend
content_tag(:div, class: 'mappings-bubble-view-legend') do
mappings_legend_section(t('mappings.bubble_view_legend.bubble_size'), t('mappings.bubble_view_legend.bubble_size_desc'), 'mappings-bubble-size-legend') +
mappings_legend_section(t('mappings.bubble_view_legend.bubble_size'),
t('mappings.bubble_view_legend.bubble_size_desc'), 'mappings-bubble-size-legend') +
mappings_legend_section(
t('mappings.bubble_view_legend.color_degree'),t('mappings.bubble_view_legend.color_degree_desc'),'mappings-bubble-color-legend') +
t('mappings.bubble_view_legend.color_degree'), t('mappings.bubble_view_legend.color_degree_desc'), 'mappings-bubble-color-legend') +
content_tag(:div, class: 'content-container') do
content_tag(:div, class: 'bubble-view-legend-item') do
content_tag(:div, class: 'title') do
content_tag(:div, t('mappings.bubble_view_legend.yellow_bubble'), class: 'd-inline') + content_tag(:span, t('mappings.bubble_view_legend.selected_bubble'))
content_tag(:div, t('mappings.bubble_view_legend.yellow_bubble'),
class: 'd-inline') + content_tag(:span, t('mappings.bubble_view_legend.selected_bubble'))
end +
content_tag(:div, class: "mappings-bubble-size-legend d-flex justify-content-center") do
content_tag(:div, '', class: "bubble yellow")
content_tag(:div, class: 'mappings-bubble-size-legend d-flex justify-content-center') do
content_tag(:div, '', class: 'bubble yellow')
end
end
end
Expand All @@ -209,7 +201,7 @@ def mappings_legend_section(title_text, description_text, css_class)
def mappings_legend(css_class)
content_tag(:div, class: css_class) do
content_tag(:div, t('mappings.bubble_view_legend.less_mappings'), class: 'mappings-legend-text') +
(1..6).map { |i| content_tag(:div, "", class: "bubble bubble#{i}") }.join.html_safe +
(1..6).map { |i| content_tag(:div, '', class: "bubble bubble#{i}") }.join.html_safe +
content_tag(:div, t('mappings.bubble_view_legend.more_mappings'), class: 'mappings-legend-text')
end
end
Expand Down
17 changes: 8 additions & 9 deletions app/views/mappings/_concept_mappings.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
= "#{@mappings.size}"

= turbo_frame_tag @type.eql?('modal') ? 'application_modal_content' : 'concept_mappings' do
%div{:style => "padding: 1%; width: 98%"}
%div.p-1
- if session[:user].nil?
= link_to "Create New Mapping", "/login?redirect=/ontologies/#{@ontology.acronym}/?p=classes&t=mappings&conceptid=#{escape(@concept.id)}", :method => :get, :class => "btn btn-default mb-3"
- else
= link_to_modal("Create New Mapping",
new_mapping_path(ontology_from: "#{@ontology.id}", conceptid_from: "#{@concept.id}"),
id: "new_mapping_btn",
role: "button",
class: "btn btn-default mb-3",
data: { show_modal_title_value: "Create a new mapping for #{@concept.prefLabel}" },
)
%div{style: 'width: 250px; margin: 0 0 10px 0;'}
= link_to_modal(nil,
new_mapping_path(ontology_from: "#{@ontology.id}", conceptid_from: "#{@concept.id}"),
data: { show_modal_title_value: "Create a new mapping for #{@concept.prefLabel}" },
) do
= render Buttons::RegularButtonComponent.new(id:'new_mapping_btn' , value:t('mappings.create_new_mapping'), variant: 'secondary', size: 'slim', state: 'no-anim')

#mapping_details
= render :partial => '/mappings/mapping_table'
= render :partial => '/mappings/mapping_table'
Loading
Loading