Package: django-snomed-ct
django-snomed-ctADjango Appfor storing, managing, and querying the SNOMED Clinical Terms (SNOMED CT) system - an international medical terminology system and standard - by itself or as part of Django application.Installationdjango-snomed-ctcan be installed via :pip install django-snomed-ctIt currently works with Django versions 3.2+ but not 4+ and has been tested withSNOMED CT United States Editionversion 20230301
(released on  March 1, 2023).As it is a separate, installable Django app that doesn't come with a Djangoproject, you will either
have to use it with an existing Django project orcreate oneto use its Django management commands (load_snomed_ct_dataandquery_snomed_ct_data).In either case, once you have it working with a Django project you will need to set up a database
(in your project's settings.py) into which the SNOMED-CT
content will be loaded.For performance reasons, it is recommended that you use a database management system capable of handling queries against data
at the size of the current SNOMED-CT distribution.  In addition, you can also usedjango-snomed-ctwith Django’s cache framework
to reduce the cost of repeatedly looking up the fully-specified or preferred names of SNOMED-CT concepts.  This is not
required, but is advised.SeeSetting up the cachefor more information about how to configure Django's cache framework, but you will basically
need the CACHES setting configured to have an entry for 'snomed_ct'.Loading dataOnce your Django project is configured to use a database and the database has been initialized, you can use theload_snomed_ct_datacustom django-admin command to load a release into a configured database.Below is an example of running theload_snomed_ct_datacommand (using the manage.py of a Django project) to
load the 3/1/2023 US release, itsICD10 mappingsandISA transitive closure files,  and assumes you have downloaded theSNOMED-CT distributionfiles into a ~/SCT-distribution/ directory.SnomedCT_ManagedServiceUSTransitiveClosure_PRODUCTION_US1000124_20230301T120000Z.zipSnomedCT_ManagedServiceUS_PRODUCTION_US1000124_20230301T120000Z.zipSnomedCT_ManagedServiceUSTransitiveClosure_PRODUCTION_US1000124_20230301T120000Z.zip$pythonmanage.pyload_snomed_ct_data--snapshot\--icd10_map_location=~/SCT-distribution/SNOMED_CT_to_ICD-10-CM_Resources_20230301.zip\--transitive_closure_location=~/SCT-distribution/SnomedCT_ManagedServiceUSTransitiveClosure_PRODUCTION_US1000124_20230301T120000Z.zip\~/SCT-distribution/SnomedCT_ManagedServiceUS_PRODUCTION_US1000124_20230301T120000Z.zipThe --icd10_map_location option can be excluded if you don't want to load the ICD10 SNOMED-CT mappingsThe --international option should be specified if working with an international distribution (otherwise, it defaults
to assuming it is a US distribution).  The --snapshot option should be used with a snapshot distribution
(otherwise, it defaults toFull)Simple functionalityfromsnomed_ct.modelsimportConcept,ICD10_Mapping,TextDefinition,Description,ISAYou can fetch Concept instances by their SNOMED-CT identifiers:>>>fromsnomed_ct.modelsimportConcept>>>Concept.by_id('59820001')<Concept:59820001|Bloodvesselstructure(bodystructure)>The custom manager for the Concept Model provides aby_idsmethod which takes a list of SNOMED-CT identifiers
and returns a corresponding set of Concepts:>>>Concept.objects.by_ids(['371627004','194984004'])<ConceptQuerySet[<Concept:194984004|Aorticstenosis,non-rheumatic(disorder)>,<Concept:371627004|Angiotensinconvertingenzymeinhibitor-aggravatedangioedema(disorder)>]>In addition to retrieving concepts by their identifiers, you also can fetch Concept instances via various types of string matching against their name:Regular expression matching (case sensitive or insensitive), using Django'sregexandiregexfield lookups respectivelyText substring matching (case sensitive or insensitive), using Django'scontainsandicontainsfield lookups respectivelyPostgreSQL full text search, using Django'ssearchlookup for PostgreSQLThe Concept custom manager also provides aby_fully_specified_namemethod.  This method returns a queryset
of Concepts matched by their fully specified name.  The first argument is a search string pattern and the second optional keyword
argument (search_type)  can be one of the following attributes on theTextSearchTypesclass in snomed_ct.models:CASE_INSENSITIVE_CONTAINSCASE_SENSITIVE_CONTAINSCASE_SENSITIVE_REGEXCASE_INSENSITIVE_REGEXPOSTGRES_FULL_TEXT_SEARCHEach of these corresponds to the 5 ways the string search will be performed.  By default, and if not specified, the
search will be performed using the case insensitive (CASE_INSENSITIVE_CONTAINS) substring matching method:>>>Concept.objects.by_fully_specified_name('vessel structure')<ConceptQuerySet[<Concept:281471001|Abdominopelvicbloodvesselstructure(bodystructure)>,<Concept:42586008|Largebloodvesselstructure(bodystructure)>,<Concept:397018003|Bloodvesselstructureofskin(bodystructure)>,<Concept:59820001|Bloodvesselstructure(bodystructure)>,<Concept:306954006|Regionalbloodvesselstructure(bodystructure)>]>There is also aby_fully_specified_namesmethod defined on the custom manager that can be used in a similar way to
return a query set of Concept objects except that the first argument is a list of search string patterns to match against
using the specified string search method.Similarly, concepts can be fetched via matching definitions (theTextDefinitionmodel) made about them by the
Concept.by_definitionclass method, which has the same method signature and arguments as by_fully_specified_name .forcinConcept.by_definition('aort.+stenosis',search_type=TextSearchTypes.CASE_SENSITIVE_REGEX):..print(c)Concept query sets have ahas_definitionsmethod which filters the concepts to only include those that have textual definitions:>>>Concept.objects.by_fully_specified_name('aortic stenosis').has_definitions()<ConceptQuerySet[<Concept:783096008|Subaorticstenosisandshortstaturesyndrome(disorder)>]>A single Concept can be fetched by theby_idClass method and you can get direct access to the fully specified name
(via thefully_specified_nameproperty) and the name without the parenthesized type suffix (fully_specified_name_no_typeproperty)concept=Concept.by_id(194733006)concept.fully_specified_name_no_typeconcept.fully_specified_nameConcept query sets also have afully_specified_names_no_typeproperty that can be used to return the list of
fully specified names of its concepts:>>>Concept.objects.by_fully_specified_name('vessel structure').fully_specified_names_no_type['Blood vessel structure','Blood vessel structure of skin','Abdominopelvic blood vessel structure','Large blood vessel structure','Regional blood vessel structure']Relationships about a given concept (concept definitions relating another concept to
the one given) can be returned as an iterable of Relationship instances using theoutbound_relationships()method and
the same can be done in the other direction via theinbound_relationships()method on Concept model instances.forrelinsnomed_concept.outbound_relationships():..print("\t-{}->{}".format(rel.type,rel.destination))Hierarchies and DescriptionsYou can navigate the ISA relationships of a given concept (General Concept Inclusion) via theisaConcept property, which returns an iterator of Concept instances related to the starting concept via
ISA:forgeneral_conceptinc.isa:..print("\t",general_concept)Conversely, there is also aspecializationproperty that returns an iterator over other Concept instances that have an ISA relationship with the
given concept:forspecialized_conceptinc.specializations:..print("\t",specialized_concept)Similarly, you can iterate over the finding site concepts (via thefinding_siteproperty) as well as the associated
morphologies (of disorders via themorphologiesproperty) and everything a given concept is 'part of' using thepart_ofproperty, which returns an iterator of concepts related in this way.  Thehas_partproperty returns
everything that is 'part of' a given concept (the opposite relationship).All the definitions for a concept object can also be returned as an iterator of TextDefinition model instances using thedefinitions()method.fordefninc.definitions().values_list('term',flat=True):..print("\t",defn)All the descriptions for a concept object can be returned with thedescriptionsrelation name:fordescriptioninc.descriptions().values_list('term',flat=True):..print("\t",description)There are a few convenience properties available on QuerySets of Description and TextDefinition instances.  In particular,
the terms property will return a list of the term vales for each instance in the query set, thesynonymsproperty
will filter the query set to only include Description or TextDefinition instances that are synonyms:description_text=list(c.descriptions().terms)definition_text=list(c.definitions())synonyms_text=list(c.definitions().synonyms)RelationshipsSNOMED-CT's Relationship relation is implemented in theRelationDjango model.  Every instance of this model has asource,destination, andtypeattribute, each of which corresponds to aConcept.  These correspond,
semantically, toOWL 2 (existential) Property Restrictions, wheretypeis the property being restricted,sourceis the Concept whose definition restricts the use of the property, anddestinationis theConceptthat the object of the property must be an instance of.EachConceptinstance has anoutbound_relationships()andinbound_relationships()method that can be used to
return a query set ofRelationshipmodel instances that have the concept as itssourcein the case of the former
and itsdestinationfor the latter.  Query sets ofRelationmodel instances can be filtered in the usual manner but
also define the following useful methods:sources()destinations()types()They return a query set of Concepts that correspond to thesource,destination, andtyperespectively of
each Relationship instance in the query setTo learn more about the how SNOMED-CT uses the reasoning capabilities of Description Logic and OWL for its semantics, please read
theSNOMED CT OWL Guide.ICD 10 mappingOnce you have loaded ICD 10 <-> SNOMED-CT mappings via the --icd10_map_location option of the load_snomed_ct_data
manage command, you can start finding mappings by ICD10 codes, iterating over just those SNOMED-CT concepts with
ICD 10 mappings, etc.EachICD10_Mappingmodel represents a mapping from SNOMED-CT to ICD 10, where thereference_componentattribute
is theConceptbeing mapped to ICD-10,map_targetis a string representation of the linked ICD 10 code(s), andmap_ruleis a string
representation of a boolean that has a value of "TRUE" for a 'correct' mapping (seeNLM's SNOMED CT to ICD-10-CM MapandICD-10 Mapping Technical Guide.Conceptinstances have anicd10_mappingsattribute which will return arelated managerfor instances ofICD10_Mappingthat can be filtered.  For example:>>>Concept.by_id('712832005').icd10_mappings.filter(map_rule='TRUE')<ICD10_MappingQuerySet[<ICD10_Mapping:I10->712832005|Supinehypertension(disorder)>]>Query sets ofICD10_Mappingcan be filtered to only those that are mapped to SNOMED-CT concepts with textual
definitions with thehas_definition()method and theget_icd_codes()method returns the ICD 10 codes
(or code patterns) mapped by each instance in the query set.  Theirconceptsproperty will return a query set
of eachConceptmapped in the query set.Finally, the custom manager for theICD10_Mappingmodel (ICD10_mapping.objects) has the following methods that each return a query set:by_icd_codes(codes) - Takes a list of ICD 10 codes and returns a query set of mappings whose ICD 10 value begins with any of the given codes (supporting ICD 10 code prefix matching)by_icd_names(search_strings,search_type=TextSearchTypes.CASE_INSENSITIVE_CONTAINS) - Returns mappings where the label of the corresponding ICD 10 code matches an item in the first argument (a list of string patterns) using the search method specified by the second argumentby_fully_specified_name(search_string,search_type=TextSearchTypes.CASE_INSENSITIVE_CONTAINS) - Behaves similarly to theby_fully_specified_namemethod defined on the Concept custom manager except it returns a query set of mappings from concepts whose fully_specified_name matches the given pattern and search methodISA Transitive ClosureOnce the transitive closure file has been loaded (as described above), you can return all the concepts related to a given concept via thetransitive_isamethod which returns a TransitiveClosureQuerySet.TheTransitiveClosuremodel is the transitive closure of the 'subsumption' relationship (the inverse of ISA) materialized
by the SNOMED CT Database Scripts.Each TransitiveClosureQuerySet instance, has ageneral_conceptsproperty that
returns a ConceptQuerySet ofallthe general concepts (those that generalise the given concept) and aspecific_conceptsmethod which returnsallthe specific concepts from the transitive closure.  So the following will return all
the more general SNOMED-CT concepts that a given concept is derived from that also have ICD mappings:icd_mapped_concepts=c.transitive_isa.general_concepts.has_icd10_mappings()Once you have loaded the transitive closure and ICD mappings, given a concept instance, you can use the mehcanism for both
to find all the ICD-10 codes mapped to concepts in the transitive closure of its ISA relation (i.e., all more general
SNOMED-CT concepts)This is a very useful way todiscoverICD-10 codes relevant to a problem identified (possibly from its name or
definition) by leveraging the logical expressiveness of the mathematics used to capture the meaning of SNOMED-CT
terminologyquery_snomed_ct_data Django management commandBelow is the raw documentation of this command (the inherited options have been removed for brevity):$./manage.pyquery_snomed_ct_data--helpusage: manage.py query_snomed_ct_data [-h] [-qt {ICD,ICD_CODE,SNOMED,SNOMED_CODE}] [-rx] [-d] [-r] [-o {relations,english,name}]search_terms [search_terms ...]Query SNOMED CT Release from the database.positional arguments:search_termsoptions:-h, --help            show this help message and exit-qt {ICD,ICD_CODE,SNOMED,SNOMED_CODE}, --query-type {ICD,ICD_CODE,SNOMED,SNOMED_CODE}What the search is matching against: 'ICD' (name), 'ICD_CODE', 'SNOMED_CODE', or 'SNOMED'-rx, --regex          The query is a REGEX, otherwise it is a case-insensitive substring to match-d, --def-only        Ony show concepts with textual definitions-r, --logically-relatedShow concepts logically related to those matched-o {relations,english,name}, --output-type {relations,english,name}How to print the matching concepts: 'relations', 'english', 'name'For example:$./manage.pyquery_snomed_ct_data-qtSNOMED-oenglish"systolic essential hypertension"--------------- 429457004|Systolic essential hypertension (disorder) ------------------------------Systolic essential hypertension is an essential hypertension (59621000) and a systolic hypertension (56218007).  It is an interpretation of blood pressure (75367002) as increased (35105006).  It is located in some systemic circulatory system structure (51840005) and entire cardiovascular system (278198007)ICD 10 Mappings: I10 (Essential (primary) hypertension)Controlled Natural LanguageThe rendering of SNOMED-CT concepts the --output-type option is set to 'english' is done by thesnomed_ct.controlled_natural_language.ControlledEnglishGeneratorclass. This class takes aConceptinstance as
the only argument to its constructor and has aget_controlled_english_definition()method with anembed_idsboolean keyword argument that defaults to False.  It returns a controlled natural language representation of the
concept's SNOMED-CT definition, embedding SNOMED-CT identifiers to referenced concepts when embed_ids is True and
excluding these otherwise.The rendering process is mostly based onAttempto Controlled Englishbut also implements its own syntactic mechanisms
specific to SNOMED-CT's semantics, such as part-whole reasoning to infer that "everything located in a part is located in the whole."