Skip to content

Commit

Permalink
OpenConceptLab/ocl_issues#1495 | collections summary
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Mar 6, 2023
1 parent 3e2c620 commit cabb4f7
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 83 deletions.
59 changes: 59 additions & 0 deletions core/collections/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,65 @@ def last_mapping_update(self):
def expansions_url(self):
return self.uri + 'expansions/'

def get_concepts_queryset(self):
expansion = self.expansion
return expansion.concepts.filter() if expansion else Concept.objects.none()

def get_mappings_queryset(self):
expansion = self.expansion
return expansion.mappings.filter() if expansion else Mapping.objects.none()

@property
def references_distribution(self):
return {
'include': self.references.filter(include=True).count(),
'exclude': self.references.filter(include=False).count(),
'concepts': self.references.filter(reference_type=CONCEPT_REFERENCE_TYPE).count(),
'mappings': self.references.filter(reference_type=MAPPING_REFERENCE_TYPE).count(),
'total': self.active_references,
}

@property
def referenced_sources_distribution(self):
from core.sources.serializers import SourceVersionMinimalSerializer
result = {}
for reference in self.references.filter(include=True):
version = reference.resolve_system_version
if version:
_result = get(result, version.uri)
if not _result:
_result = {
**SourceVersionMinimalSerializer(version).data,
'distribution': {'include_reference': True, 'concepts': 0, 'mappings': 0, 'references': 0},
}
_result['distribution']['concepts'] += reference.concepts.count()
_result['distribution']['mappings'] += reference.mappings.count()
_result['distribution']['references'] += 1
result[version.uri] = _result

return sorted(result.values(), key=lambda summary: get(summary, 'distribution.references'), reverse=True)

@property
def referenced_collections_distribution(self):
from core.collections.serializers import CollectionVersionMinimalSerializer
result = {}
for reference in self.references.filter(include=True):
versions = reference.resolve_valueset_versions
for version in versions:
if version:
_result = get(result, version.uri)
if not _result:
_result = {
**CollectionVersionMinimalSerializer(version).data,
'distribution': {'include_reference': True, 'concepts': 0, 'mappings': 0, 'references': 0},
}
_result['distribution']['concepts'] += reference.concepts.count()
_result['distribution']['mappings'] += reference.mappings.count()
_result['distribution']['references'] += 1
result[version.uri] = _result

return sorted(result.values(), key=lambda summary: get(summary, 'distribution.references'), reverse=True)


class ReferencedConcept(models.Model):
reference = models.ForeignKey('collections.CollectionReference', on_delete=models.CASCADE)
Expand Down
89 changes: 89 additions & 0 deletions core/collections/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ class Meta:
fields = ('id', 'url')


class CollectionVersionMinimalSerializer(ModelSerializer):
id = CharField(source='version')
version_url = CharField(source='uri')
type = CharField(source='resource_version_type')
short_code = CharField(source='mnemonic')

class Meta:
model = Collection
fields = ('id', 'version_url', 'type', 'short_code')


class CollectionListSerializer(ModelSerializer):
type = CharField(source='resource_type')
short_code = CharField(source='mnemonic')
Expand Down Expand Up @@ -240,6 +251,84 @@ class Meta:
)


class CollectionSummaryVerboseSerializer(ModelSerializer):
sources = JSONField(source='referenced_sources_distribution')
collections = JSONField(source='referenced_collections_distribution')
concepts = JSONField(source='concepts_distribution')
mappings = JSONField(source='mappings_distribution')
locales = JSONField(source='concept_names_distribution')
versions = JSONField(source='versions_distribution')
references = JSONField(source='references_distribution')
expansions = IntegerField(source='expansions_count')
uuid = CharField(source='id')
id = CharField(source='mnemonic')

class Meta:
model = Collection
fields = (
'id', 'uuid', 'concepts', 'mappings', 'locales', 'versions', 'references', 'expansions', 'sources',
'collections'
)


class CollectionVersionSummaryVerboseSerializer(ModelSerializer):
concepts = JSONField(source='concepts_distribution')
mappings = JSONField(source='mappings_distribution')
locales = JSONField(source='concept_names_distribution')
references = JSONField(source='references_distribution')
expansions = IntegerField(source='expansions_count')
uuid = CharField(source='id')
id = CharField(source='version')

class Meta:
model = Collection
fields = (
'id', 'uuid', 'concepts', 'mappings', 'locales', 'references', 'expansions'
)


class CollectionSummaryFieldDistributionSerializer(ModelSerializer):
uuid = CharField(source='id')
id = CharField(source='mnemonic')
distribution = SerializerMethodField()

class Meta:
model = Collection
fields = (
'id', 'uuid', 'distribution'
)

def get_distribution(self, obj):
result = {}
fields = (get(self.context, 'request.query_params.distribution') or '').split(',')
for field in fields:
func = get(obj, f"get_{field}_distribution")
if func:
result[field] = func()
return result


class CollectionVersionSummaryFieldDistributionSerializer(ModelSerializer):
uuid = CharField(source='id')
id = CharField(source='version')
distribution = SerializerMethodField()

class Meta:
model = Collection
fields = (
'id', 'uuid', 'distribution'
)

def get_distribution(self, obj):
result = {}
fields = (get(self.context, 'request.query_params.distribution') or '').split(',')
for field in fields:
func = get(obj, f"get_{field}_distribution")
if func:
result[field] = func()
return result


class CollectionVersionSummarySerializer(ModelSerializer):
expansions = IntegerField(source='expansions_count')

Expand Down
156 changes: 156 additions & 0 deletions core/collections/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,162 @@ def test_get_cascaded_mapping_uris_from_concept_expressions(self):
sorted([mapping1.url, mapping2.url])
)

def test_references_distribution(self):
collection = OrganizationCollectionFactory()
reference1 = CollectionReference(expression='/foo/concepts/', collection=collection, reference_type='concepts')
reference2 = CollectionReference(expression='/foo/mappings', collection=collection, reference_type='mappings')
reference3 = CollectionReference(
expression='/bar/concepts', collection=collection, reference_type='mappings', include=False)
reference1.save()
reference2.save()
reference3.save()

self.assertEqual(collection.references.count(), 3)

distribution = collection.references_distribution

self.assertEqual(distribution, {'concepts': 1, 'mappings': 2, 'include': 2, 'exclude': 1, 'total': 3})

def test_referenced_sources_distribution(self):
self.maxDiff = None
collection = OrganizationCollectionFactory()
source1 = OrganizationSourceFactory()
source2 = OrganizationSourceFactory()
source2_v1 = OrganizationSourceFactory(mnemonic=source2.mnemonic, version='v1', organization=source2.parent)
concept1 = ConceptFactory(parent=source1)
concept2 = ConceptFactory(parent=source2)
concept3 = ConceptFactory(parent=source2)
mapping = MappingFactory(parent=source2)
concept2_latest_version = concept2.get_latest_version()
concept2_latest_version.sources.add(source2_v1)
reference1 = CollectionReference(
expression=concept1.uri, collection=collection, system=source1.uri, code=concept1.mnemonic
)
reference2 = CollectionReference(
expression=concept2_latest_version.uri, collection=collection, system=source2_v1.uri,
code=concept2.mnemonic, resource_version=concept2_latest_version.version
)
reference3 = CollectionReference(
expression=concept3.uri, collection=collection, system=source2.uri,
code=concept3.mnemonic
)
reference4 = CollectionReference(
expression=mapping.uri, collection=collection, system=source2.uri, reference_type='mappings'
)
reference1.clean()
reference1.save()
reference2.clean()
reference2.save()
reference3.clean()
reference3.save()
reference4.clean()
reference4.save()

distribution = collection.referenced_sources_distribution

self.assertCountEqual(
distribution,
[{
'id': 'HEAD',
'version_url': source1.uri,
'type': 'Source Version',
'short_code': source1.mnemonic,
'distribution': {
'include_reference': True,
'concepts': 1,
'mappings': 0,
'references': 1
}
}, {
'id': 'v1',
'version_url': source2_v1.uri,
'type': 'Source Version',
'short_code': source2.mnemonic,
'distribution': {
'include_reference': True,
'concepts': 1,
'mappings': 0,
'references': 1
}
}, {
'id': 'HEAD',
'version_url': source2.uri,
'type': 'Source Version',
'short_code': source2.mnemonic,
'distribution': {
'include_reference': True,
'concepts': 1,
'mappings': 1,
'references': 2
}
}]
)

def test_referenced_collections_distribution(self): # pylint: disable=too-many-locals
self.maxDiff = None
collection = OrganizationCollectionFactory()
collection2 = OrganizationCollectionFactory()
source1 = OrganizationSourceFactory()
source2 = OrganizationSourceFactory()
source2_v1 = OrganizationSourceFactory(mnemonic=source2.mnemonic, version='v1', organization=source2.parent)
concept1 = ConceptFactory(parent=source1)
concept2 = ConceptFactory(parent=source2)
concept3 = ConceptFactory(parent=source2)
mapping = MappingFactory(parent=source2)
concept2_latest_version = concept2.get_latest_version()
concept2_latest_version.sources.add(source2_v1)
reference1 = CollectionReference(
expression=concept1.uri, collection=collection, system=source1.uri, code=concept1.mnemonic
)
reference2 = CollectionReference(
expression=concept2_latest_version.uri, collection=collection, system=source2_v1.uri,
code=concept2.mnemonic, resource_version=concept2_latest_version.version
)
reference3 = CollectionReference(
expression=concept3.uri, collection=collection, system=source2.uri,
code=concept3.mnemonic
)
reference4 = CollectionReference(
expression=mapping.uri, collection=collection, system=source2.uri, reference_type='mappings'
)
reference1.clean()
reference1.save()
reference2.clean()
reference2.save()
reference3.clean()
reference3.save()
reference4.clean()
reference4.save()

reference5 = CollectionReference(
expression=collection.uri, collection=collection2, valueset=[collection.uri], reference_type='concepts'
)
reference6 = CollectionReference(
expression=collection.uri, collection=collection2, valueset=[collection.uri], reference_type='mappings'
)
reference5.clean()
reference5.save()
reference6.clean()
reference6.save()

distribution = collection2.referenced_collections_distribution

self.assertCountEqual(
distribution,
[{
'id': 'HEAD',
'version_url': collection.uri,
'type': 'Collection Version',
'short_code': collection.mnemonic,
'distribution': {
'include_reference': True,
'concepts': 0, # no expansion
'mappings': 0,
'references': 2
}
}]
)


class CollectionReferenceTest(OCLTestCase):
def test_uri(self):
Expand Down
25 changes: 24 additions & 1 deletion core/collections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
CollectionCreateSerializer, CollectionReferenceSerializer, CollectionVersionDetailSerializer,
CollectionVersionListSerializer, CollectionVersionExportSerializer, CollectionSummaryDetailSerializer,
CollectionVersionSummaryDetailSerializer, CollectionReferenceDetailSerializer, ExpansionSerializer,
ExpansionDetailSerializer, ReferenceExpressionResolveSerializer, CollectionMinimalSerializer)
ExpansionDetailSerializer, ReferenceExpressionResolveSerializer, CollectionMinimalSerializer,
CollectionSummaryFieldDistributionSerializer, CollectionSummaryVerboseSerializer,
CollectionVersionSummaryFieldDistributionSerializer, CollectionVersionSummaryVerboseSerializer)
from core.collections.utils import is_version_specified
from core.common.constants import (
HEAD, RELEASED_PARAM, PROCESSING_PARAM, OK_MESSAGE,
Expand Down Expand Up @@ -1137,6 +1139,13 @@ class CollectionSummaryView(CollectionBaseView, RetrieveAPIView, CreateAPIView):
serializer_class = CollectionSummaryDetailSerializer
permission_classes = (CanViewConceptDictionary,)

def get_serializer_class(self):
if self.is_verbose():
if self.request.query_params.get('distribution'):
return CollectionSummaryFieldDistributionSerializer
return CollectionSummaryVerboseSerializer
return CollectionSummaryDetailSerializer

def get_object(self, queryset=None):
instance = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, instance)
Expand All @@ -1154,6 +1163,13 @@ class CollectionVersionSummaryView(CollectionBaseView, RetrieveAPIView):
serializer_class = CollectionVersionSummaryDetailSerializer
permission_classes = (CanViewConceptDictionary,)

def get_serializer_class(self):
if self.is_verbose():
if self.request.query_params.get('distribution'):
return CollectionVersionSummaryFieldDistributionSerializer
return CollectionVersionSummaryVerboseSerializer
return CollectionVersionSummaryDetailSerializer

def get_object(self, queryset=None):
instance = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, instance)
Expand All @@ -1171,6 +1187,13 @@ class CollectionLatestVersionSummaryView(CollectionVersionBaseView, RetrieveAPIV
serializer_class = CollectionVersionSummaryDetailSerializer
permission_classes = (CanViewConceptDictionary,)

def get_serializer_class(self):
if self.is_verbose():
if self.request.query_params.get('distribution'):
return CollectionVersionSummaryFieldDistributionSerializer
return CollectionVersionSummaryVerboseSerializer
return CollectionVersionSummaryDetailSerializer

def get_object(self, queryset=None):
obj = self.get_queryset().first()
if not obj:
Expand Down
Loading

0 comments on commit cabb4f7

Please sign in to comment.