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

SO-5535: MRCM evaluations #1068

Merged
merged 40 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
46def53
[mrcm]: Support evaluation of applicable type/predicates
AAAlinaaa Oct 4, 2022
d6d8095
[mrcm]: Take module scopes into account in finding applicable predicates
AAAlinaaa Oct 7, 2022
99486b8
fix(validation): Fix component type comparisons
AAAlinaaa Oct 12, 2022
b661e21
fix(mrcm): Limit results, by modules, attribute types
AAAlinaaa Oct 12, 2022
7dda5a7
fix(mrcm): Limit results by module scope, skip validation of axiom ...
AAAlinaaa Oct 12, 2022
75edb27
fix(mrcm): Use module scope in evaluation, skip concrete value ranges
AAAlinaaa Oct 12, 2022
3794b07
fix(mrcm): Report issues in domains outside of MRCM domain ranges
AAAlinaaa Oct 12, 2022
02c378d
fix(mrcm): Remove debug messages
AAAlinaaa Oct 13, 2022
304c669
fix(mrcm): Fix condition for skipping concrete value type ranges
AAAlinaaa Oct 13, 2022
cf1e25a
fix(validation): Update validation tests launch configurations
AAAlinaaa Oct 18, 2022
edc4bcd
fix(tests): Ensure MRCM validation tests take refset scope into account
AAAlinaaa Oct 18, 2022
66139ce
test(mrcm): Add MRCM requests tests
AAAlinaaa Oct 18, 2022
b2fb5ca
fix(mrcm): Handle duplicate entries for the same type
AAAlinaaa Oct 19, 2022
d4c2a40
fix(mrcm): Add constant for precoordinated SCT content type
AAAlinaaa Oct 19, 2022
aa29485
fix(mrcm): Add missing license header
AAAlinaaa Oct 26, 2022
4c94712
fix(mrcm): Add missing since tag
AAAlinaaa Oct 26, 2022
02cc2a2
fix(mrcm): Explain handling missing case of flag setting combinations
AAAlinaaa Oct 26, 2022
f8f101b
fix(mrcm): Restrict member fetches to only the most necessary fields
AAAlinaaa Oct 26, 2022
306b26c
fix(mrcm): Take self ids into account in calculating applicable rules
AAAlinaaa Oct 26, 2022
7ea273d
fix(mrcm): Move concrete value type range prefixes to constants
AAAlinaaa Oct 27, 2022
95239e0
fix(mrcm): Move markers of relationshipless axioms to a constant
AAAlinaaa Oct 27, 2022
57615e0
fix(mrcm): Use unsupported axiom type markers constant
AAAlinaaa Oct 27, 2022
c15371a
fix(mrcm): Use helper methods to construct ECL expression
AAAlinaaa Oct 27, 2022
032f87d
fix(mrcm): Create MRCM REST service
AAAlinaaa Oct 27, 2022
a11bcc1
fix(mrcm): Refactor MRCM requests
AAAlinaaa Oct 27, 2022
c4abc6d
Merge branch '8.x' into issue/SO-5535-MRCM_applicable_predicates
AAAlinaaa Nov 14, 2022
9a8522a
fix(mrcm): Use predefined constant
AAAlinaaa Nov 28, 2022
43c655c
fix(mrcm): Make variable final
AAAlinaaa Nov 28, 2022
5a8ff18
fix(mrcm): Correct logical operator in case both object and data ...
AAAlinaaa Nov 28, 2022
0123652
fix(mrcm): Add line breaks after switch cases
AAAlinaaa Nov 28, 2022
ae68384
fix(mrcm): update since tags
AAAlinaaa Nov 28, 2022
67286e8
fix(mrcm): Rename MRCM requests
AAAlinaaa Nov 28, 2022
3e2cc8e
fix(mrcm): Rename enum
AAAlinaaa Nov 28, 2022
2525acc
fix(mrcm): Make visibility of new requests package private
AAAlinaaa Nov 28, 2022
ba2eb0f
fix(mrcm): Fix request visibility
AAAlinaaa Nov 28, 2022
34d079e
fix(mrcm): Move, rename attribute type enum
AAAlinaaa Nov 29, 2022
031f412
fix(mrcm): Correct date in license header
AAAlinaaa Nov 29, 2022
ab252c7
fix(mrcm): Support paging, update endpoint names
AAAlinaaa Nov 30, 2022
666fe2a
fix(mrcm): Update endpoint names
AAAlinaaa Dec 7, 2022
d5914a9
fix(mrcm): Use predefined Ecl constant
AAAlinaaa Dec 7, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ private Concepts() { }
// MRCM Attribute Range
public static final String ATTRIBUTE_TYPE_RANGE_CONSTRAINT = "723575003";
public static final String ATTRIBUTE_TYPE_ATTRIBUTE_RULE = "723576002";
public static final String ALL_PRECOORDINATED_CONTENT = "723594008";

// MRCM Module Scope
public static final String ATTRIBUTE_TYPE_RULE_REFSET = "723577006";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2022 B2i Healthcare Pte Ltd, http://b2i.sg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b2international.snowowl.snomed.core.request;
cmark marked this conversation as resolved.
Show resolved Hide resolved

import static com.b2international.snowowl.snomed.common.SnomedConstants.Concepts.SUBSTANCE;
import static com.b2international.snowowl.snomed.common.SnomedRf2Headers.FIELD_MRCM_RANGE_CONSTRAINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.junit.Test;

import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts;
import com.b2international.snowowl.snomed.core.MrcmAttributeType;
import com.b2international.snowowl.snomed.core.domain.refset.SnomedReferenceSetMember;
import com.b2international.snowowl.snomed.core.domain.refset.SnomedReferenceSetMembers;
import com.b2international.snowowl.snomed.datastore.request.SnomedRequests;
import com.b2international.snowowl.test.commons.Services;
import com.google.common.base.Objects;

/**
* @since 8.8.0
*/
public class SnomedMrcmTest {
cmark marked this conversation as resolved.
Show resolved Hide resolved

private static final String CODESYSTEM = "SNOMEDCT";

@Test
public void applicableTypesTest() {
final String isAModificationOf = "738774007";
final String hasDisposition = "726542003";

SnomedReferenceSetMembers substanceDataTypes = SnomedRequests.prepareGetMrcmTypeRules()
.setAttributeType(MrcmAttributeType.DATA)
.setModuleIds(List.of(Concepts.MODULE_SCT_CORE))
.setParentIds(Set.of(SUBSTANCE))
.build(CODESYSTEM)
.execute(Services.bus())
.getSync(1, TimeUnit.MINUTES);
assertEquals(0, substanceDataTypes.getTotal());

Collection<String> substanceObjectTypes = SnomedRequests.prepareGetMrcmTypeRules()
.setAttributeType(MrcmAttributeType.OBJECT)
.setModuleIds(List.of(Concepts.MODULE_SCT_CORE))
.setParentIds(Set.of(SUBSTANCE))
.build(CODESYSTEM)
.execute(Services.bus())
.getSync(1, TimeUnit.MINUTES)
.stream()
.map(SnomedReferenceSetMember::getReferencedComponentId)
.collect(Collectors.toSet());

assertEquals(2, substanceObjectTypes.size());
assertTrue(substanceObjectTypes.containsAll(List.of(isAModificationOf, hasDisposition)));
}

@Test
public void applicableRangeTest() {
SnomedReferenceSetMembers substanceDataTypeRanges = SnomedRequests.prepareGetMrcmRangeRules()
.setAttributeType(MrcmAttributeType.DATA)
.setModuleIds(List.of(Concepts.MODULE_SCT_CORE))
.setParentIds(Set.of(SUBSTANCE))
.build(CODESYSTEM)
.execute(Services.bus())
.getSync(1, TimeUnit.MINUTES);
assertEquals(0, substanceDataTypeRanges.getTotal());

SnomedReferenceSetMembers substanceObjectTypeRanges = SnomedRequests.prepareGetMrcmRangeRules()
.setAttributeType(MrcmAttributeType.OBJECT)
.setModuleIds(List.of(Concepts.MODULE_SCT_CORE))
.setParentIds(Set.of(SUBSTANCE))
.build(CODESYSTEM)
.execute(Services.bus())
.getSync(1, TimeUnit.MINUTES);
assertEquals(2, substanceObjectTypeRanges.getTotal());
Map<String, String> ranges = Map.of("738774007", "<< 105590001 |Substance (substance)|", //Is A Modification Of
"726542003", "<< 726711005 |Disposition (disposition)|"); //Has Disposition
substanceObjectTypeRanges.forEach(member -> {
final String expectedRange = ranges.get(member.getReferencedComponentId());
final String actualRange = (String) member.getProperties().get(FIELD_MRCM_RANGE_CONSTRAINT);
assertTrue(String.format("Expected range (%s) does not match actual range (%s)", expectedRange, actualRange), Objects.equal(expectedRange, actualRange));
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
SnomedReferenceSetDeletionPerformanceTest.class,
SnomedConceptCreatePerformanceTest.class,
SnomedMergePerformanceTest.class,
SnomedMrcmTest.class
})
public class AllSnomedApiTests {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2022 B2i Healthcare Pte Ltd, http://b2i.sg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b2international.snowowl.snomed.core.rest;

import java.util.List;
import java.util.Set;

import org.springframework.web.bind.annotation.*;

import com.b2international.snowowl.core.events.util.Promise;
import com.b2international.snowowl.core.rest.AbstractRestService;
import com.b2international.snowowl.snomed.core.MrcmAttributeType;
import com.b2international.snowowl.snomed.core.domain.refset.SnomedReferenceSetMembers;
import com.b2international.snowowl.snomed.datastore.request.SnomedRequests;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

/**
* @since 8.8.0
*/
@Tag(description="MRCM Rules", name = "mrcm")
@RestController
public class SnomedMrcmRestService extends AbstractRestService {

@Operation(
summary="Retrieve MRCM attribute domain type refset members filtered by domain and attribute type",
description="Retrieve MRCM relationship type rules ")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "404", description = "Not found"),
})
@GetMapping(value = "/{path:**}/mrcm/types", produces = { AbstractRestService.JSON_MEDIA_TYPE })
public @ResponseBody Promise<SnomedReferenceSetMembers> getApplicableTypes(
@Parameter(description = "The resource path", required = true)
@PathVariable(name ="path")
final String path,

@Parameter(description = "The attribute types to include in the results", schema = @Schema(allowableValues = "all,object,data", defaultValue = "all"))
@RequestParam(name = "attributeType", defaultValue = "all")
final String attributeType,

@Parameter(name = "selfIds", description = "Set of concept ids that might form the domain of the rule", required = false)
@RequestParam(name = "selfIds", required = false)
final Set<String> selfIds,

@Parameter(name = "parentIds", description = "Set of parent/ancestor ids that might form the domain of the rule", required = false)
@RequestParam(name = "parentIds", required = false)
final Set<String> parentIds,

@Parameter(name = "refsetIds", description = "Set of reference set ids that might form the domain of the rule", required = false)
@RequestParam(name = "refsetIds", required = false)
final Set<String> refsetIds,

@Parameter(name = "moduleIds", description = "List of applicable modules refining the scope of returned rules", required = false)
@RequestParam(name = "moduleIds", required = false)
final List<String> moduleIds,

@RequestParam(name = "searchAfter", required = false)
@Parameter(name = "searchAfter", description = "The search key to use for retrieving the next page of results")
final String searchAfter,

@RequestParam(name = "limit", required = false, defaultValue = "50")
@Parameter(name = "limit",description = "The maximum number of items to return")
int limit) {

return SnomedRequests.prepareGetMrcmTypeRules()
.setAttributeType(MrcmAttributeType.getByNameIgnoreCase(attributeType))
.setModuleIds(moduleIds)
.setParentIds(parentIds)
.setRefSetIds(refsetIds)
.setSelfIds(selfIds)
.setLimit(limit)
.setSearchAfter(searchAfter)
.build(path)
.execute(getBus());
}

@Operation(
summary="Retrieve MRCM attribute domain range refset members filtered by domain and attribute type",
description="Retrieve MRCM relationship range rules ")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "404", description = "Not found"),
})
@GetMapping(value = "/{path:**}/mrcm/ranges", produces = { AbstractRestService.JSON_MEDIA_TYPE })
public @ResponseBody Promise<SnomedReferenceSetMembers> getApplicableRanges(
@Parameter(description = "The resource path", required = true)
@PathVariable(name ="path")
final String path,

@Parameter(description = "The attribute types to include in the results", schema = @Schema(allowableValues = "all,object,data", defaultValue = "all"))
@RequestParam(name = "attributeType", defaultValue = "all")
final String attributeType,

@Parameter(name = "selfIds", description = "Set of concept ids that might form the domain of the rule", required = false)
@RequestParam(name = "selfIds", required = false)
final Set<String> selfIds,

@Parameter(name = "parentIds", description = "Set of parent/ancestor ids that might form the domain of the rule", required = false)
@RequestParam(name = "parentIds", required = false)
final Set<String> parentIds,

@Parameter(name = "refsetIds", description = "Set of reference set ids that might form the domain of the rule", required = false)
@RequestParam(name = "refsetIds", required = false)
final Set<String> refsetIds,

@Parameter(name = "moduleIds", description = "List of applicable modules refining the scope of returned rules", required = false)
@RequestParam(name = "moduleIds", required = false)
final List<String> moduleIds,

@RequestParam(name = "searchAfter", required = false)
@Parameter(name = "searchAfter", description = "The search key to use for retrieving the next page of results")
final String searchAfter,

@RequestParam(name = "limit", required = false, defaultValue = "50")
@Parameter(name = "limit",description = "The maximum number of items to return")
int limit) {

return SnomedRequests.prepareGetMrcmRangeRules()
.setAttributeType(MrcmAttributeType.getByNameIgnoreCase(attributeType))
.setModuleIds(moduleIds)
.setParentIds(parentIds)
.setRefSetIds(refsetIds)
.setSelfIds(selfIds)
.setLimit(limit)
.setSearchAfter(searchAfter)
.build(path)
.execute(getBus());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2022 B2i Healthcare Pte Ltd, http://b2i.sg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.b2international.snowowl.snomed.core;

import static com.google.common.base.Strings.nullToEmpty;

import com.b2international.commons.exceptions.BadRequestException;

/**
* @since 8.8.0
*/
public enum MrcmAttributeType {

DATA,
OBJECT,
ALL;

public static MrcmAttributeType getByNameIgnoreCase(String name) {
for (final MrcmAttributeType type : values()) {
if (nullToEmpty(name).equalsIgnoreCase(type.toString())) {
return type;
}
}
throw new BadRequestException("Unknown attribute type: '%s'", name);
}

}
Loading