44from datetime import datetime
55
66from django .contrib import messages
7+ from django .contrib .contenttypes .models import ContentType
78from data .models import Concept , ConceptRelationship , ConceptAncestor
89
910from mapping .models import ScanReportTable , ScanReportField , ScanReportValue
@@ -445,9 +446,6 @@ def get_mapping_rules_list(structural_mapping_rules):
445446 page and processed to build a json
446447 """
447448
448- # Queryset -> list, makes the calls to the db to get the rules
449- structural_mapping_rules = list (structural_mapping_rules )
450-
451449 # get all scan_report_concepts that are used
452450 # get the ids first so we can make a batch call
453451 scan_report_concepts = list (
@@ -457,11 +455,12 @@ def get_mapping_rules_list(structural_mapping_rules):
457455 nmapped_concepts = len (scan_report_concepts )
458456
459457 # make the batch call
460- scan_report_concepts = {
458+ scan_report_concepts_id_to_obj_map = {
461459 x .id : x
462460 for x in list (ScanReportConcept .objects .filter (pk__in = scan_report_concepts ))
463461 }
464- ntotal_concepts = len (scan_report_concepts .values ())
462+
463+ ntotal_concepts = len (scan_report_concepts_id_to_obj_map .values ())
465464
466465 if nmapped_concepts != ntotal_concepts :
467466 print (
@@ -470,15 +469,22 @@ def get_mapping_rules_list(structural_mapping_rules):
470469 )
471470
472471 # get all the ids for all ScanReportValues that are used (have been mapped with a concept)
473- scan_report_values = [
472+ scanreportvalue_content_type = ContentType .objects .get_for_model (ScanReportValue )
473+ scan_report_values_with_scan_report_concepts = [
474474 obj .object_id
475- for obj in scan_report_concepts .values ()
476- if obj .content_type .model_class () is ScanReportValue
475+ for obj in ScanReportConcept .objects .filter (
476+ pk__in = scan_report_concepts_id_to_obj_map ,
477+ content_type = scanreportvalue_content_type ,
478+ )
477479 ]
478480 # make a batch call to the ORM again..
479- scan_report_values = {
481+ scan_report_values_id_to_value_map = {
480482 obj .id : obj .value
481- for obj in list (ScanReportValue .objects .filter (pk__in = scan_report_values ))
483+ for obj in list (
484+ ScanReportValue .objects .filter (
485+ pk__in = scan_report_values_with_scan_report_concepts
486+ )
487+ )
482488 }
483489
484490 # get all destination field ids
@@ -506,10 +512,41 @@ def get_mapping_rules_list(structural_mapping_rules):
506512 for obj in list (ScanReportTable .objects .filter (pk__in = source_tables ))
507513 }
508514
509- # now looop over the rules to actually create the list version of the rules
515+ # Using select_related() means we can chain together querysets into one database
516+ # query rather than using multiple
517+ structural_mapping_rules_sr_concepts = structural_mapping_rules .select_related (
518+ "concept"
519+ )
520+ # Generate rule.id to SRConcept.id map for all SRConcepts related to these rules.
521+ rule_to_srconcept_id_map = {
522+ obj .id : obj .concept .id for obj in structural_mapping_rules_sr_concepts
523+ }
524+
525+ structural_mapping_rules_sr_concepts_concepts = (
526+ structural_mapping_rules .select_related ("concept__concept" )
527+ )
528+ # Generate MappingRule.id to Concept.id map for all Concepts related to SRConcepts
529+ # related to these MappingRules.
530+ rule_id_to_concept_name_map = {
531+ obj .id : obj .concept .concept .concept_name
532+ for obj in structural_mapping_rules_sr_concepts_concepts
533+ }
534+
535+ # Make a single query to get all ScanReportConcepts associated to
536+ # ScanReportValues. This means we avoid what would be more understandable,
537+ # but extremely slow, code to check whether each object is associated to a
538+ # ScanReportValue.
539+ scan_report_concepts_with_values = [
540+ obj .id
541+ for obj in ScanReportConcept .objects .filter (
542+ pk__in = scan_report_concepts_id_to_obj_map ,
543+ content_type = scanreportvalue_content_type ,
544+ )
545+ ]
546+
547+ # now loop over the rules to actually create the list version of the rules
510548 rules = []
511549 for rule in structural_mapping_rules :
512-
513550 # get the fields/tables from the loop up lists
514551 # the speed up comes from here as we dont need to keep hitting the DB to get this data
515552 # we've already cached it in these dictionaries by making a batch call
@@ -520,28 +557,35 @@ def get_mapping_rules_list(structural_mapping_rules):
520557 source_table = source_tables [source_field .scan_report_table_id ]
521558
522559 # get the concepts again
523- scan_report_concept_id = rule .concept_id
524- if rule .concept_id not in scan_report_concepts :
560+ rule_scan_report_concept_id = rule_to_srconcept_id_map [rule .id ]
561+
562+ if rule .concept_id not in scan_report_concepts_id_to_obj_map :
525563 print (f"WARNING!! scan_report_concept { rule .concept_id } no longer exists" )
526564 continue
527- scan_report_concept = scan_report_concepts [rule .concept_id ]
565+
566+ scan_report_concept = scan_report_concepts_id_to_obj_map [rule .concept_id ]
567+
528568 concept_id = scan_report_concept .concept_id
529- concept_name = scan_report_concept .concept .concept_name
569+
570+ concept_name = rule_id_to_concept_name_map [rule .id ]
530571
531572 # work out if we need term_mapping or not
532573 term_mapping = None
533574 if "concept_id" in destination_field .field :
534- if scan_report_concept .content_type . model_class () is ScanReportValue :
575+ if scan_report_concept .id in scan_report_concepts_with_values :
535576 term_mapping = {
536- scan_report_values [scan_report_concept .object_id ]: concept_id
577+ scan_report_values_id_to_value_map [
578+ scan_report_concept .object_id
579+ ]: concept_id
537580 }
538581 else :
539582 term_mapping = concept_id
540583
541584 creation_type = scan_report_concept .creation_type
585+
542586 rules .append (
543587 {
544- "rule_id" : scan_report_concept_id ,
588+ "rule_id" : rule_scan_report_concept_id ,
545589 "rule_name" : concept_name ,
546590 "destination_table" : destination_table ,
547591 "destination_field" : destination_field ,
0 commit comments