Skip to content

Commit

Permalink
Merge pull request #597 from t3-innovation-network/staging
Browse files Browse the repository at this point in the history
Release 09/25/24
  • Loading branch information
shlag3n authored Sep 25, 2024
2 parents 70c3ccc + c834259 commit 48ac36a
Show file tree
Hide file tree
Showing 26 changed files with 311 additions and 71 deletions.
2 changes: 1 addition & 1 deletion app/controllers/api/v1/alignments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def authorize_with_policy
# @return [ActionController::Parameters]
###
def permitted_params
params.require(:alignment).permit(:comment, :predicate_id)
params.require(:alignment).permit(:comment, :predicate_id, transformation: {})
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const AlignAndFineTune = (props) => {
compactDomains={mapping.specification.compact_domains}
onPredicateSelected={onPredicateSelected}
onUpdateAlignmentComment={actions.updateAlignmentComment}
onUpdateAlignmentTransformation={actions.updateAlignmentTransformation}
onRevertMapping={(mappedTerm) =>
actions.handleRevertMapping({
termId: term.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { useLocalStore } from 'easy-peasy';
import Modal from 'react-modal';
import updateAlignment from '../../services/updateAlignment';
import AlertNotice from '../shared/AlertNotice';
import ModalStyles from '../shared/ModalStyles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faLeftRight, faTimes } from '@fortawesome/free-solid-svg-icons';
import { transformationAlignmentStore } from './stores/transformationAlignmentStore';
import useDidMountEffect from '../../helpers/useDidMountEffect';
import { i18n } from 'utils/i18n';

const AlignmentTransformation = (props) => {
Modal.setAppElement('body');

const { alignment, isOpen, onUpdate, onClose } = props;
const [state, actions] = useLocalStore(() =>
transformationAlignmentStore({
transformation: alignment?.transformation || {},
initialData: alignment?.transformation || {},
})
);
const { transformation, withChanges } = state;

const handleToChange = (e) => actions.handleChange({ to: e.target.value });
const handleFromChange = (e) => actions.handleChange({ from: e.target.value });

const handleSave = async () => {
actions.setLoading(true);
try {
let response = await updateAlignment({
id: alignment.id,
transformation,
});

if (response.error) {
actions.setError(response.error);
return;
}

onUpdate({ saved: true, transformation });
} finally {
actions.setLoading(false);
}
};

useDidMountEffect(() => {
if (isOpen) actions.setTransformation(alignment?.transformation);
}, [isOpen]);

return (
<Modal
isOpen={isOpen}
onRequestClose={onClose}
contentLabel={i18n.t('ui.mapping.transformation.title')}
style={ModalStyles}
shouldCloseOnEsc={true}
shouldCloseOnOverlayClick={true}
>
<div className="card">
<div className="card-header">
<FontAwesomeIcon icon={faLeftRight} className="col-primary" />
<a className="float-right cursor-pointer" onClick={onClose}>
<FontAwesomeIcon icon={faTimes} />
</a>
</div>
<div className="card-body">
{state.hasErrors && <AlertNotice message={state.errors} onClose={actions.clearErrors} />}
<div className="row">
<div className="col col-12 form-group">
<label>{i18n.t('ui.mapping.transformation.form.to.label')}</label>
<input
type="text"
className="form-control"
placeholder={i18n.t('ui.mapping.transformation.form.to.placeholder')}
value={transformation.to || ''}
onChange={handleToChange}
autoFocus
/>
</div>
<div className="col col-12 form-group">
<label>{i18n.t('ui.mapping.transformation.form.from.label')}</label>
<input
type="text"
className="form-control"
placeholder={i18n.t('ui.mapping.transformation.form.from.placeholder')}
value={transformation.from || ''}
onChange={handleFromChange}
/>
</div>
</div>
<div className="row">
<div className="col">
<button
className="btn btn-dark mt-3 mr-3"
onClick={handleSave}
disabled={!withChanges || state.loading}
>
{i18n.t('ui.mapping.transformation.form.save')}
</button>
<button
className="btn btn-link mt-3 col-primary"
onClick={onClose}
disabled={state.loading}
>
{i18n.t('ui.buttons.cancel')}
</button>
</div>
</div>
</div>
</div>
</Modal>
);
};

export default AlignmentTransformation;
55 changes: 49 additions & 6 deletions app/javascript/components/align-and-fine-tune/SpineTermRow.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { useLocalStore } from 'easy-peasy';
import AlignmentTransformation from './AlignmentTransformation';
import EditAlignment from './EditAlignment';
import Collapsible from '../shared/Collapsible';
import MatchVocabulary from './match-vocabulary/MatchVocabulary';
Expand Down Expand Up @@ -31,6 +32,7 @@ const SpineTermRow = (props) => {
const {
alignment,
onUpdateAlignmentComment,
onUpdateAlignmentTransformation,
mappedTermsToSpineTerm,
origin,
compactDomains,
Expand All @@ -54,6 +56,7 @@ const SpineTermRow = (props) => {
predicateDefinition,
mappedTermMatching,
editing,
transforming,
matchingVocab,
spineTermExpanded,
mappedTermExpanded,
Expand Down Expand Up @@ -115,10 +118,22 @@ const SpineTermRow = (props) => {
actions.setEditing(false);
};

const handleOnTransformationUpdated = (result) => {
if (result.saved) {
showSuccess('Changes saved!');
// Update the mapping term in state (if there's a transformation, we need to
// redraw in order to let the orange dot to appear)
onUpdateAlignmentTransformation({ id: alignment.id, transformation: result.transformation });
}
actions.setTransforming(false);
};

// Closes the modal window for editing the alignment and cancel editing the alignment
const onRequestEditClose = () => actions.setEditing(false);
// Closes the modal window for matching vocabularies
const onRequestVocabsClose = () => actions.setMatchingVocab(false);
// Closes the modal window for transformation and cancel adding alignment transformation
const onRequestTransformationClose = () => actions.setTransforming(false);

const mappedTerms = mappedTermsToSpineTerm(term);
const withPredicate = predicateOption && !noMatchPredicate(predicateOption);
Expand All @@ -137,6 +152,12 @@ const SpineTermRow = (props) => {
predicate={findPredicate()}
onRequestClose={onRequestEditClose}
/>
<AlignmentTransformation
isOpen={transforming}
onUpdate={handleOnTransformationUpdated}
alignment={alignment}
onClose={onRequestTransformationClose}
/>

{alignmentHasVocabulary() ? (
<MatchVocabulary
Expand Down Expand Up @@ -165,6 +186,11 @@ const SpineTermRow = (props) => {
</h6>
)}
<p className="card-text">{term.property.comment}</p>
{alignment.transformation?.from && (
<h6 className="card-subtitle">
Data Transformation: {alignment.transformation.from}
</h6>
)}
<button
className="btn btn-link p-0"
onClick={() => actions.setSpineTermExpanded(!spineTermExpanded)}
Expand Down Expand Up @@ -215,12 +241,24 @@ const SpineTermRow = (props) => {
<small>{predicateDefinition}</small>
</div>
)}
<label
className="non-selectable float-right mt-1 mb-0 col-primary cursor-pointer"
onClick={() => actions.setEditing(true)}
>
{alignment?.comment ? 'Edit Comment' : 'Add Comment'}
</label>
<div className="w-100 mt-1 text-right">
<button
className="btn btn-link p-0 col-primary"
onClick={() => actions.setEditing(true)}
>
{alignment?.comment ? 'Edit Comment' : 'Add Comment'}
</button>
</div>
<div className="w-100 mt-1 text-right">
<label
className="btn btn-link p-0 col-primary"
onClick={() => actions.setTransforming(true)}
>
{alignment?.transformation?.to || alignment?.transformation?.from
? 'Edit Transformation'
: 'Add Transformation'}
</label>
</div>
</div>

<div className="col-4">
Expand Down Expand Up @@ -264,6 +302,11 @@ const SpineTermRow = (props) => {
ID:
<span>{' ' + mTerm.sourceUri}</span>
</p>
{alignment.transformation?.to && (
<h6 className="card-subtitle">
Data Transformation: {alignment.transformation.to}
</h6>
)}
<button
className="btn btn-link p-0"
onClick={() => actions.setMappedTermExpanded(!mappedTermExpanded)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export const mappingStore = (initialData = {}) => ({
mappedTerms: [],
spineTermId: syntheticTermId,
predicateId: strongestMatchPredicate?.id,
transformation: {},
comment: null,
});
state.changesPerformed++;
Expand Down Expand Up @@ -361,6 +362,10 @@ export const mappingStore = (initialData = {}) => ({
let alignment = state.alignments.find((alg) => alg.id === id);
alignment.comment = comment;
}),
updateAlignmentTransformation: action((state, { id, transformation }) => {
let alignment = state.alignments.find((alg) => alg.id === id);
alignment.transformation = transformation;
}),
// updated alignment changes after save
updateAlignmentsChanges: action((state, { adds }) => {
// remove not persisted alignments that are completed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const defaultState = {
// Whether we are adding comment to the alignment or not. Set to true when
// the user selects an option from the alignment dropdown after selecting a predicate
editing: false,
// Whether tranformation modal is open or not
transforming: false,
// Whether we are matching vocabulary for the alignment or not. Set to true when
// the user clicks on the vocabulary link on the mapped term of this alignment
matchingVocab: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { action, computed } from 'easy-peasy';
import { baseModel } from '../../stores/baseModel';
import { easyStateSetters } from '../../stores/easyState';
import { assign, isEqual } from 'lodash';

export const defaultState = {
// status
loading: false,
// options
// data
initialData: {},
transformation: {},
};

export const transformationAlignmentStore = (initialData = {}) => ({
...baseModel(initialData),
...easyStateSetters(defaultState, initialData),

// computed
withChanges: computed((state) => !isEqual(state.transformation, state.initialData)),

// actions
handleChange: action((state, payload) => {
assign(state.transformation, payload);
}),
// thunks
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,35 +80,20 @@ const PropertyAlignments = (props) => {
const AlignmentCard = ({ alignment, term, isLast = false }) => {
const [showingAlignmentComment, setShowingAlignmentComment] = useState(false);

const alignmentTermClasses = term.selectedClasses.map((c) => <li key={c}>{c}</li>);
const alignmentTermRanges = term.compactRanges.map((r) => <li key={r}>{r}</li>);
const alignmentTermClasses = term.selectedClasses.join(', ');
const alignmentTermRanges = term.compactRanges.join(', ');

return (
<div className={`card borderless ${isLast ? '' : 'mb-3'}`}>
<div className="card-header desm-rounded bottom-borderless bg-col-secondary">
<div className="row">
<div className="col-2">
<small className="mt-1 col-on-primary-light">Organization</small>
<p className="mb-1">{alignment.origin}</p>

<small className="mt-1 col-on-primary-light">Schema</small>
<p className="mb-1">{alignment.schemaName}</p>
</div>
<div className="col-2 px-1">
<small className="mt-1 col-on-primary-light">Property name</small>
<p className="mb-1">{term.name}</p>

<small className="mt-1 col-on-primary-light">Class/Type</small>
<ul className="list-unstyled mb-1">{alignmentTermClasses}</ul>

<small className="mt-1 col-on-primary-light">Ranges</small>
<ul className="list-unstyled mb-1">{alignmentTermRanges}</ul>
</div>
<div className="col-6">
<small className="mt-1 col-on-primary-light">Definition</small>
<div className="mb-1">{<PropertyComments term={term} />}</div>
<div className="col-12 mb-1">
<span className="fw-bold fs-5">Schema:&nbsp;</span>
<span className="fs-5">{alignment.schemaName}. </span>
<span className="fw-bold fs-5">Property:&nbsp;</span>
<span className="fs-5">{term.name}</span>
</div>
<div className="col-2 ps-1">
<div className="col-2">
<div
className="text-center desm-rounded p-3"
style={{
Expand All @@ -123,6 +108,32 @@ const AlignmentCard = ({ alignment, term, isLast = false }) => {
<small>{alignment.predicate.definition}</small>
</div>
)}
</div>
<div className="col">
<p className="mb-2">
<span className="fw-bold">Definition:&nbsp;</span> {<PropertyComments term={term} />}
</p>
<p className="mb-2">
<span className="fw-bold">Relevant class(es):&nbsp;</span>
{alignmentTermClasses}
</p>
<p className="mb-2">
<span className="fw-bold">Expected value type:&nbsp;</span>
{alignmentTermRanges}
</p>
{(alignment.transformation?.to || alignment.transformation?.from) && (
<>
<div className="mb-2">
<p className="fw-bold mb-0">Data Transformation:</p>
{alignment.transformation?.to ? (
<p className="mb-0">To Spine: {alignment.transformation.to}</p>
) : null}
{alignment.transformation?.from ? (
<p className="mb-0">From Spine: {alignment.transformation.from}</p>
) : null}
</div>
</>
)}
{alignment.comment && (
<label
className="non-selectable float-right mt-1 mb-0 col-primary cursor-pointer"
Expand All @@ -133,13 +144,12 @@ const AlignmentCard = ({ alignment, term, isLast = false }) => {
)}
</div>
</div>

{showingAlignmentComment && (
<div className="row">
<div className="col">
<div className="card borderless">
<div className="card-body">
<h6 className="col-on-primary">Alignment Note</h6>
<h6>Alignment Note</h6>
{alignment.comment}
</div>
</div>
Expand Down
Loading

0 comments on commit 48ac36a

Please sign in to comment.