-
Notifications
You must be signed in to change notification settings - Fork 45
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
OCLM-78 - Enable importing single concept exports from OCL #84
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I'm in favour of these changes, especially looking up concepts by mappings before assuming they don't exist, but I think we need some additional discussion around this point:
instead set the import URL to the path to the saved file within the imports directory
The problem here is that this seems to break the "happy path" flow for OCL import. As I understand things, the idea with file import wasn't "this is a quick way to get concepts into your dictionary", but rather "our site can't connect to the OCL server; here's a way we can load the dictionary offline."
In other words, the subscription URL used would be the same regardless of whether the concept came from an export from OCL or was uploaded via a zip file. This change breaks that flow as concepts imported via a zip file will no longer have the same "identity" as concepts imported via a download from OCL.
Finally, I think there's a slightly more philosophical question: does this module exist to get content from OCL into OpenMRS or does it exist to allow OCL to be used to manage OpenMRS dictionaries, because those are slightly different outlooks that lead to somewhat different conclusions. I.e., if the purpose of this module is just to get content from OCL into OpenMRS, then this is a logical bit of functionality to have. However, if the purpose is to have collection in OCL and be able to say "this collection reflects what's on my server", this functionality kind of breaks that since what is actually on the server depends on the order in which things were processed (e.g., if there's a conflict between what was loaded locally from a file and what OCL says).
api/src/main/java/org/openmrs/module/openconceptlab/importer/Importer.java
Outdated
Show resolved
Hide resolved
private void closeInputStream() { | ||
IOUtils.closeQuietly(in); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably rather refactor things to use try-with-resources, e.g., by returning the input stream from this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll need to look back at the code but there was a reason I needed to do it this way. I think it was because there are 2 paths here - one in which an Input Stream is coming from a File, and another in which an Input Stream is coming from the subscription. If we can make this work with a try-with-resources, I'm all for it, but we'd need to look at why this is like this.
else if (oclMapping.getToSourceName().equals(oclConcept.getSource()) && oclMapping.getToConceptCode().equals(oclConcept.getId())) { | ||
log.debug("Skipping SAME-AS Mapping for OCL ID as this is added in the ImportTask, and would result in duplicate mappings"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be honest: this probably a guard we should have in the ImportTask
rather than here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's strange that this is even needed. Shouldn't we just add mappings from mappings? If the CIEL mapping will always be present under mappings, then we should just add it there and not have that other custom code that adds the CIEL mapping when the concept is created. Right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right... the custom code was added to work around the fact that the import of CIEL wasn't including those SAME-AS maps (and to deal with the fact that dictionaries don't necessarily have the SAME-AS mapping to their concept id.
// We do not import Q-AND-A mappings for which the imported concept is an answer | ||
boolean isQuestionAnswer = "Q-AND-A".equals(oclMapping.getMapType()); | ||
if (isQuestionAnswer && oclMapping.getToConceptUrl().equals(oclConcept.getUrl())) { | ||
log.debug("Skipping Q-AND-A mapping for which the imported concept is a mapped answer"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a common occurrence? Also, isn't this something that should be fixed outside of this module, i.e., at the concept source itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is common - anytime you are exporting a concept that is the answer to a question. Look at the export for "TRUE" in OCL - it has 710 "associations", most of which are Q-AND-A mappings for which it is an answer:
https://app.openconceptlab.org/#/orgs/CIEL/sources/CIEL/concepts/1065/
The assumption / decision I made was to not worry about these associations when exporting a concept that is an answer, only worry about those when exporting a question that has this answer. Make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that associations like this are common, but they aren't generally included in the file, depending on how it's generated. E.g., when I download that concept as JSON from the OCL Term Browser, it actually has no mappings in it at all.
If I download the concept from the API, I can either get the concept with no mappings at:
https://api.openconceptlab.org/orgs/CIEL/sources/CIEL/concepts/1065/
Or with mappings where the concept is the from
concept:
https://api.openconceptlab.org/orgs/CIEL/sources/CIEL/concepts/1065/?includeMappings=true
Or with mappings where the concept is the to
concept:
https://api.openconceptlab.org/orgs/CIEL/sources/CIEL/concepts/1065/?includeInverseMappings=true
I can also combine those filter. The point was that, in general, I wouldn't expect someone to end up with a bunch of mappings where the concept is the to concept and if we do we need to guard against it more generally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's cool that there is that flexibility in the API. Unfortunately, that same flexibility isn't in the term browser, and the term browser that I see does contain all of those mappings. This is what I was using to download a Concept.
My experience with OCL is that there is a lot of inconsistency in each of the various pages you might go to in terms of what is included in an export file. For a Concept like this you get either JSON or ZIP. For other Collections or Dictionaries, you sometimes get JSON, sometimes CSV, sometimes ZIP formats, and these formats are not necessarily consistent with each other (as you say, sometimes they contain mappings and other details, sometimes they do not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah... that's fair. Actually this would be a good thing to put more centrally in mapping exclusions because I suppose the export format could change whether intentionally or accidentally to include inverse mappings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see... I didn't quite get that the JSON and Zip file there give you very different versions of the concept. Well, different in respect to mappings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ibacher I created this issue in OCL (and it was already resolved) as I worked on this. But I don't think this is the extent of the discrepencies that exist in all of the different exports across the UI and API. If you are in close contact with the OCL team, this is an area I'd like to advocate gets:
a) work done to make exports more consistent overall, across the application
b) work done to document the export formats and what one can expect from each.
If this is already done, I may just need to be pointed to the docs.
owa/app/js/home/home.controller.js
Outdated
} | ||
|
||
function uploadZip(file) { | ||
function uploadZip(file, importType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SHould probably rename this to uploadFile()
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good catch. I'm happy to go in and make various changes due to feedback like this, but let's agree first that this is even something we want in this module in the first place, and if that get's agreed I'll tidy things up. @ibacher ?
I admit to not really understanding yet the nature of the importance of this import URL, and it was not clear to me that this had anything to do with the "identity" of a concept. It isn't clear to me that the current implementation - which just sets the import URL to the location specified in the global property "ocl/configuration/loadAtStartup" - was actually done meaningfully, as this a) doesn't point to a specific file, and b) any file placed in there is immediately deleted once it is processed at startup.
This is certainly a fair point. I don't honestly know. I approached this as follows:
Personally I'd like to see this module be about how to usefully and practically manage (or simply find shared) Concepts defined in OCL and import those into one's dictionary. Subscription is one possible approach to this, but to be honest, in terms of getting comfortable adopting OCL, I'm much more comfortable starting out with a single concept, whose impact I can clearly understand and test on my dictionary, than starting with a subscription. If we want to make this purely about subscription, that's fine, but we might find it useful to develop a small shared library (i.e. like the hapi APIs) that can be shared and used for reading OCL export files and converting these to OpenMRS concepts. |
…mporter.java Co-authored-by: Ian <[email protected]>
@mseaton Yeah, I was probably a bit over-the-top with that. I think, though, we need to distinguish between uploading Zip files (which should use the global property-stored API URL if any) and uploading JSON files (which can just use the file URL). That way we get the "load single concept" functionality without losing the offline load part. |
Also, I missed the fact that OCL now has SAME-AS mappings for CIEL... another thing to worry about. |
At the end of the day, I'm not planning to push hard on getting this merged in. If it's too controversial or inconsistent with the plan for the module, that's fine to let it sit here in a branch. I do think that working through this PR has uncovered some issues that need thought and review overall. We might need a design discussion to work through next steps here. @ibacher ? |
I'm actually working through cleaning this up a bit right now because I agree this has uncovered some issues and the idea of being able to just download a concept from OCL and import it into the UI is pretty compelling. |
This adds the ability to import a single concept export from OCL into an OpenMRS instance via the existing import page. See demo below of the code within this PR in action:
What this PR does:
** Copies any uploaded file into this "imports" directory once it has an import attempt, with a timestamp differentiator
** Changes the existing logic that set the import URL to the .OpenMRS/ocl/configuration/loadAtStartup for file imports, and instead set the import URL to the path to the saved file within the imports directory
** Importing either zip or json
** Importing a single concept in addition to a collection