class IndicatorDataElementBase(aristotleComponent): class Meta: abstract = True ordering = ['order'] indicator = ConceptForeignKey(Indicator, on_delete=models.CASCADE) order = models.PositiveSmallIntegerField( "Order", help_text=_("The position of this data element in the indicator")) guide_for_use = MDR.RichTextField(blank=True) data_element = ConceptForeignKey(MDR.DataElement, blank=True, null=True, on_delete=models.SET_NULL) data_set_specification = ConceptForeignKey( aristotle_dse.DataSetSpecification, blank=True, null=True, on_delete=models.SET_NULL) data_set = ConceptForeignKey(aristotle_dse.Dataset, blank=True, null=True, on_delete=models.SET_NULL) description = MDR.RichTextField(blank=True) inline_field_layout = 'list' parent_field_name = 'indicator' # Provide a specific field ordering for the advanced metadata editor. inline_field_order: List[str] = [ "data_element", "data_set_specification", "data_set", "description", "guide_for_use", "order", ] @property def inline_editor_description(self): fields = [] if self.data_element: fields.append(f'Data element: {self.data_element.name}') if self.data_set_specification: fields.append( f'Data set specification: {self.data_set_specification}') if self.data_set: fields.append(f'Data set: {self.data_set}') return fields
class RelationRole(MDR.aristotleComponent): # 9.1.2.5 name = models.TextField(help_text=_( "The primary name used for human identification purposes.")) definition = models.TextField( _('definition'), help_text=_( "Representation of a concept by a descriptive statement " "which serves to differentiate it from related concepts. (3.2.39)") ) multiplicity = models.PositiveIntegerField( # 9.1.2.5.3.1 # a.k.a the number of times it can appear in a link :( help_text=_( 'number of links which must (logically) be members of the source ' 'relation of this role, differing only by an end with this role as ' 'an end_role.'), null=True, blank=True, ) ordinal = models.PositiveIntegerField( # 9.1.2.5.3.2 help_text= _('order of the relation role among other relation roles in the relation.' )) relation = ConceptForeignKey(Relation) @property def parentItem(self): return self.relation def __str__(self): return "{0.name}".format(self)
class IndicatorInclusion(aristotleComponent): order = models.PositiveSmallIntegerField( "Order", help_text=_("The position of this indicator in the set")) indicator_set = ConceptForeignKey(IndicatorSet, on_delete=models.CASCADE) indicator = ConceptForeignKey(Indicator, blank=True, null=True, on_delete=models.SET_NULL) name = models.CharField( max_length=1024, blank=True, help_text=_("The name identifying this indicator in the set")) parent_field_name = 'indicator_set' class Meta: ordering = ['order']
class Link(TimeStampedModel): """ Link is a class each instance of which models a link (3.2.69). A link is a member of a relation (3.2.119) (not an instance of a relation). In relational database parlance, a link would be a tuple (row) in a relation (table). Link is a subclass of Assertion (9.1.2.3), and as such is included in one or more Concept_Systems (9.1.2.2) through the assertion_inclusion (9.1.3.5) association. """ relation = ConceptForeignKey(Relation) root_item = ConceptForeignKey(MDR._concept, related_name='owned_links') def concepts(self): return MDR._concept.objects.filter(linkend__link=self).all().distinct() def add_link_end(self, role, concept): return LinkEnd.objects.create(link=self, role=role, concept=concept)
class UserViewHistory(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="recently_viewed_metadata") concept = ConceptForeignKey(MDR._concept, related_name='user_view_history') view_date = models.DateTimeField( default=now, help_text=_("When the item was viewed") )
class DSSClusterInclusion(DSSInclusion): """ The child in this relationship is considered to be a child of the parent DSS as specified by the `dss` property. """ child = ConceptForeignKey(DataSetSpecification, related_name='parent_dss', on_delete=models.CASCADE) inline_field_layout = 'list' inline_field_order = [ "order", "dss", "child", "reference", "inclusion", "maximum_occurrences", "conditional_inclusion", "specific_information" ] class Meta(DSSInclusion.Meta): verbose_name = "DSS Cluster" @property def include(self): return self.child def inline_editor_description(self): if self.order: return "Cluster '{cls}' at position {pos}".format( cls=self.child.name, pos=self.order) return "Cluster '{}'".format(self.child.name) def __str__(self): return "Cluster {cls} at position {pos}".format(cls=self.child_id, pos=self.order)
class DSSGrouping(aristotle.models.aristotleComponent): class Meta: ordering = ['order'] verbose_name = 'DSS Grouping' inline_field_layout = 'list' parent_field_name = 'dss' dss = ConceptForeignKey(DataSetSpecification, related_name="groups", on_delete=models.CASCADE) name = ShortTextField(help_text=_("The name applied to the grouping.")) definition = RichTextField( _('definition'), blank=True, ) linked_group = models.ManyToManyField('self', blank=True, symmetrical=False) order = models.PositiveSmallIntegerField( "Position", null=True, blank=True, ) parent = 'dss' def __str__(self): return self.name
class DistributionDataElementPath(aristotle.models.aristotleComponent): class Meta: ordering = ['order'] parent_field_name = 'distribution' # TODO: Set this to NOT NULL distribution = models.ForeignKey( Distribution, blank=True, null=True, on_delete=models.CASCADE, help_text=_('A relation to the DCAT Distribution Record.'), ) data_element = ConceptForeignKey( aristotle.models.DataElement, blank=True, null=True, help_text=_('An entity responsible for making the dataset available.'), verbose_name='Data Element', on_delete=models.SET_NULL, ) logical_path = models.CharField( max_length=256, help_text= _("A text expression that specifies how to identify which series of data in the distribution maps to this data element" )) order = models.PositiveSmallIntegerField( "Position", null=True, blank=True, help_text=_("Column position within a dataset.")) specialisation_classes = ConceptManyToManyField( aristotle.models.ObjectClass, help_text=_(""), blank=True)
class DistributionDataElementPath(aristotle.models.aristotleComponent): class Meta: ordering = ['order'] @property def parentItem(self): return self.distribution distribution = models.ForeignKey( Distribution, blank=True, null=True, help_text=_('A relation to the DCAT Distribution Record.'), ) data_element = ConceptForeignKey( aristotle.models.DataElement, blank=True, null=True, help_text=_('An entity responsible for making the dataset available.'), ) logical_path = models.CharField( max_length=256, help_text= _("A text expression that specifies how to identify which series of data in the distribution maps to this data element" )) order = models.PositiveSmallIntegerField( "Position", null=True, blank=True, help_text=_("Column position within a dataset.")) specialisation_classes = ConceptManyToManyField( aristotle.models.ObjectClass, help_text=_(""), blank=True)
class Slot(TimeStampedModel): # on save confirm the concept and model are correct, otherwise reject # on save confirm the cardinality name = models.CharField(max_length=256) # Or some other sane length type = models.CharField(max_length=256, blank=True) # Or some other sane length concept = ConceptForeignKey(MDR._concept, related_name='slots', on_delete=models.CASCADE) value = models.TextField() order = models.PositiveSmallIntegerField("Position", default=0) permission = models.IntegerField(choices=permission_choices, default=permission_choices.public) objects = SlotsManager() @property def hr_permission(self): """Human readable permission""" return permission_choices[self.permission] def __str__(self): return u"{0} - {1}".format(self.name, self.value) class Meta: ordering = ['order']
class ScopedIdentifier(TimeStampedModel, MDR.aristotleComponent): namespace = models.ForeignKey(Namespace) concept = ConceptForeignKey(MDR._concept, related_name='identifiers') identifier = models.CharField( # 7.2.2.2.2.1 max_length=512, help_text= _('String used to unambiguously denote an Item within the scope of a specified Namespace.' )) version = models.CharField( # 7.2.2.2.2.2 max_length=512, help_text= _('unique version identifier of the Scoped_Identifier which identifies an Item' ), blank=True, default="") order = models.PositiveSmallIntegerField("Position", default=0) class Meta: unique_together = ("namespace", "identifier", "version") ordering = ['order'] def __str__(self): return u"{0}:{1}:{2}".format(self.namespace.naming_authority.name, self.identifier, self.version) @property def prefix(self): return self.namespace.shorthand_prefix @property def parentItem(self): return self.concept
class Framework(aristotle.models.concept): template = "comet/framework.html" parentFramework = ConceptForeignKey('Framework', blank=True, null=True, related_name="childFrameworks") indicators = ConceptManyToManyField(Indicator, related_name="frameworks", blank=True)
class LinkEnd(TimeStampedModel): # 9.1.2.7 link = models.ForeignKey(Link) role = models.ForeignKey(RelationRole) concept = ConceptForeignKey(MDR._concept) def clean(self): if self.role.relation != self.link.relation: raise ValidationError( _('A link ends role relation must be from the set of roles on the links relation' ))
class Slot(TimeStampedModel): # on save confirm the concept and model are correct, otherwise reject # on save confirm the cardinality name = models.CharField(max_length=256) # Or some other sane length type = models.CharField(max_length=256, blank=True) # Or some other sane length concept = ConceptForeignKey(MDR._concept, related_name='slots') value = models.TextField() def __str__(self): return u"{0} - {1}".format(self.name, self.value)
class DSSClusterInclusion(DSSInclusion): """ The child in this relationship is considered to be a child of the parent DSS as specified by the `dss` property. """ child = ConceptForeignKey(DataSetSpecification, related_name='parent_dss') class Meta(DSSInclusion.Meta): verbose_name = "DSS Cluster Inclusion" @property def include(self): return self.child
class DSSDEInclusion(DSSInclusion): data_element = ConceptForeignKey(aristotle.models.DataElement, related_name="dssInclusions") specialisation_classes = ConceptManyToManyField( aristotle.models.ObjectClass, help_text=_("")) class Meta(DSSInclusion.Meta): verbose_name = "DSS Data Element Inclusion" @property def include(self): return self.data_element
class CustomValue(TimeStampedModel): field = models.ForeignKey(CustomField, related_name='values') content = models.TextField() concept = ConceptForeignKey(_concept) objects = CustomValueManager() class Meta: ordering = ['field__order'] unique_together = ('field', 'concept') @property def is_html(self): return self.field.type == 'html'
class DSSDEInclusion(DSSInclusion): data_element = ConceptForeignKey(aristotle.models.DataElement, related_name="dssInclusions", on_delete=models.CASCADE) group = models.ForeignKey(DSSGrouping, blank=True, null=True, on_delete=models.SET_NULL) specialisation_classes = ConceptManyToManyField( aristotle.models.ObjectClass, help_text=_(""), blank=True, ) inline_field_layout = 'list' inline_field_order = [ "order", "dss", "data_element", "reference", "inclusion", "maximum_occurrences", "conditional_inclusion", "specific_information", "group", "specialisation_classes" ] class Meta(DSSInclusion.Meta): verbose_name = "DSS Data Element" @property def include(self): return self.data_element @property def inline_editor_description(self): if self.group: msg = [ "Data element: '{de}' in group '{group}'".format( de=self.data_element.name, group=self.group.name) ] else: msg = ['Data element: {de}'.format(de=self.data_element.name)] return msg def __str__(self): has_reference = self.reference is not None and self.reference != '' if has_reference: return 'Data element {} at position {} with reference: {}.'.format( self.data_element_id, self.order, self.reference) return 'Data element {} at position {}'.format(self.data_element_id, self.order) def get_absolute_url(self): pass
class AbstractValue(aristotleComponent): """ Implementation note: Not the best name, but there will be times to subclass a "value" when its not just a permissible value. """ class Meta: abstract = True ordering = ['order'] value = ShortTextField( # 11.3.2.7.2.1 - Renamed from permitted value for abstracts help_text=_("the actual value of the Value")) meaning = ShortTextField( # 11.3.2.7.1 help_text= _("A textual designation of a value, where a relation to a Value meaning doesn't exist" ), blank=True) value_meaning = models.ForeignKey( # 11.3.2.7.1 'ValueMeaning', blank=True, null=True, on_delete=models.SET_NULL, help_text=_( 'A reference to the value meaning that this designation relates to' )) # Below will generate exactly the same related name as django, but reversion-compare # needs an explicit related_name for some actions. valueDomain = ConceptForeignKey( 'ValueDomain', related_name="%(class)s_set", help_text=_( "Enumerated Value Domain that this value meaning relates to"), verbose_name='Value Domain', on_delete=models.CASCADE, ) order = models.PositiveSmallIntegerField("Position") start_date = models.DateField( blank=True, null=True, help_text=_('Date at which the value became valid')) end_date = models.DateField( blank=True, null=True, help_text=_('Date at which the value ceased to be valid')) parent_field_name = 'valueDomain' def __str__(self): return "%s: %s - %s" % (self.valueDomain.name, self.value, self.meaning)
class Link(TimeStampedModel): """ Link is a class each instance of which models a link (3.2.69). A link is a member of a relation (3.2.119) (not an instance of a relation). In relational database parlance, a link would be a tuple (row) in a relation (table). Link is a subclass of Assertion (9.1.2.3), and as such is included in one or more Concept_Systems (9.1.2.2) through the assertion_inclusion (9.1.3.5) association. """ relation = ConceptForeignKey(Relation, on_delete=models.CASCADE) root_item = ConceptForeignKey(MDR._concept, related_name='owned_links', on_delete=models.CASCADE) def concepts(self): return MDR._concept.objects.filter(linkend__link=self).all().distinct() def add_link_end(self, role, concept): return LinkEnd.objects.create(link=self, role=role, concept=concept) def get_readable_concepts(self) -> str: """ Get a "readable" version of the concepts connected by this Link object. e.g. "MyLinkObject 1, MyLinkObject 2 and MyLinkObject 3" """ return get_text_list(list(MDR._concept.objects.filter(linkend__link=self).all(). distinct().values_list('name', flat=True)), 'and')
class Indicator(aristotle.models.concept): """ An indicator is a single measure that is reported on regularly and that provides relevant and actionable information about population or system performance. """ template = "comet/indicator.html" dataElementConcept = ConceptForeignKey(aristotle.models.DataElementConcept, verbose_name="Data Element Concept", blank=True, null=True) valueDomain = ConceptForeignKey(aristotle.models.ValueDomain, verbose_name="Value Domain", blank=True, null=True) outcome_areas = ConceptManyToManyField('OutcomeArea', related_name="indicators", blank=True) indicatorType = ConceptForeignKey(IndicatorType, blank=True, null=True) numerators = ConceptManyToManyField(aristotle.models.DataElement, related_name="as_numerator", blank=True) denominators = ConceptManyToManyField(aristotle.models.DataElement, related_name="as_denominator", blank=True) disaggregators = ConceptManyToManyField(aristotle.models.DataElement, related_name="as_disaggregator", blank=True) numerator_description = models.TextField(blank=True) numerator_computation = models.TextField(blank=True) denominator_description = models.TextField(blank=True) denominator_computation = models.TextField(blank=True) computationDescription = RichTextField(blank=True) rationale = RichTextField(blank=True) disaggregation_description = RichTextField(blank=True)
class DSSInclusion(aristotle.models.aristotleComponent): class Meta: abstract = True ordering = ['order'] inline_field_layout = 'list' parent_field_name = 'dss' reference = models.CharField( max_length=512, blank=True, help_text=_( "Optional field for refering to this item within the DSS."), default='') dss = ConceptForeignKey(DataSetSpecification, on_delete=models.CASCADE) maximum_occurrences = models.PositiveIntegerField( default=1, verbose_name=_("Maximum Occurrences"), help_text=_( "The maximum number of times a item can be included in a dataset")) inclusion = models.CharField( "Inclusion", choices=CARDINALITY, default=CARDINALITY.conditional, max_length=20, help_text= _("Specifies if a field is required, optional or conditional within a dataset based on this specification." )) specific_information = RichTextField( blank=True, help_text= _("Any additional information on the inclusion of a data element or cluster in a dataset." )) conditional_inclusion = RichTextField( "Conditional Inclusion", blank=True, help_text= _("If an item is present conditionally, this field defines the conditions under which an item will appear." )) order = models.PositiveSmallIntegerField( "Position", null=True, blank=True, help_text= _("If a dataset is ordered, this indicates which position this item is in a dataset." ))
class Issue(TimeStampedModel): name = models.CharField(max_length=1000) description = models.TextField(blank=True) item = ConceptForeignKey(_concept, related_name='issues') submitter = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='issues') isopen = models.BooleanField(default=True) def can_edit(self, user): return user.id == self.submitter.id def can_view(self, user): return self.item.can_view(user) def can_alter_open(self, user): return self.can_edit(user) or self.item.can_edit(user)
class DSSInclusion(aristotle.models.aristotleComponent): class Meta: abstract = True ordering = ['order'] dss = ConceptForeignKey(DataSetSpecification) maximum_occurances = models.PositiveIntegerField( default=1, help_text=_( "The maximum number of times a item can be included in a dataset")) cardinality = models.CharField( choices=CARDINALITY, default=CARDINALITY.conditional, max_length=20, help_text= _("Specifies if a field is required, optional or conditional within a dataset based on this specification." )) specific_information = RichTextField( blank=True, help_text= _("Any additional information on the inclusion of a data element or cluster in a dataset." )) # may need to become HTML field. conditional_obligation = models.TextField( blank=True, help_text= _("If an item is present conditionally, this field defines the conditions under which an item will appear." )) order = models.PositiveSmallIntegerField( "Position", null=True, blank=True, help_text= _("If a dataset is ordered, this indicates which position this item is in a dataset." )) @property def parentItem(self): return self.dss @property def parentItemId(self): return self.dss_id
class CustomValue(TimeStampedModel): field = models.ForeignKey(CustomField, related_name='values', on_delete=models.CASCADE) content = models.TextField(blank=True) concept = ConceptForeignKey(_concept, on_delete=models.CASCADE) objects = CustomValueManager() class Meta: ordering = ['field__order'] unique_together = ('field', 'concept') @property def is_html(self): return self.field.type == 'html' def __str__(self): return 'CustomValue with field "{}" for concept "{}"'.format( self.field, self.concept)
class Slot(TimeStampedModel): # on save confirm the concept and model are correct, otherwise reject # on save confirm the cardinality name = models.CharField(max_length=256) # Or some other sane length type = models.CharField(max_length=256, blank=True) # Or some other sane length concept = ConceptForeignKey(MDR._concept, related_name='slots') value = models.TextField() order = models.PositiveSmallIntegerField("Position", default=0) permission = models.IntegerField( choices=( (0, 'Public'), (1, 'Authenticated'), (2, 'Workgroup'), ), default=0 ) def __str__(self): return u"{0} - {1}".format(self.name, self.value) class Meta: ordering = ['order']
class FrameworkDimension(MPTTModel, TimeStampedModel, aristotleComponent): parent_field_name = 'framework' objects = FrameworkDimensionManager() framework = ConceptForeignKey('Framework', on_delete=models.CASCADE) name = models.CharField(max_length=2048) description = MDR.RichTextField(blank=True) parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='child_dimensions') class MPTTMeta: order_insertion_by = ['name'] @property def parentItem(self): return self.framework def __str__(self): return self.name
class DataSetSpecification(aristotle.models.concept): """ A collection of :model:`aristotle_mdr.DataElement`\s specifying the order and fields required for a standardised :model:`aristotle_dse.DataSource`. """ edit_page_excludes = ['clusters', 'data_elements'] serialize_weak_entities = [ ('clusters', 'dssclusterinclusion_set'), ('data_elements', 'dssdeinclusion_set'), ] template = "aristotle_dse/concepts/dataSetSpecification.html" ordered = models.BooleanField( default=False, help_text= _("Indicates if the ordering for a dataset is must match exactly the order laid out in the specification." )) statistical_unit = ConceptForeignKey( aristotle.models._concept, related_name='statistical_unit_of', blank=True, null=True, help_text= _("Indiciates if the ordering for a dataset is must match exactly the order laid out in the specification." )) data_elements = ConceptManyToManyField(aristotle.models.DataElement, blank=True, through='DSSDEInclusion') clusters = ConceptManyToManyField('self', through='DSSClusterInclusion', blank=True, null=True, symmetrical=False) collection_method = aristotle.models.RichTextField(blank=True, help_text=_('')) implementation_start_date = models.DateField(blank=True, null=True, help_text=_('')) implementation_end_date = models.DateField(blank=True, null=True, help_text=_('')) def addDataElement(self, data_element, **kwargs): inc = DSSDEInclusion.objects.get_or_create(data_element=data_element, dss=self, defaults=kwargs) def addCluster(self, child, **kwargs): inc = DSSClusterInclusion.objects.get_or_create(child=child, dss=self, defaults=kwargs) @property def registry_cascade_items(self): return list(self.clusters.all()) + list(self.data_elements.all()) def get_download_items(self): return [ (DataSetSpecification, self.clusters.all()), (aristotle.models.DataElement, self.data_elements.all()), (aristotle.models.ObjectClass, aristotle.models.ObjectClass.objects.filter( dataelementconcept__dataelement__datasetspecification=self)), (aristotle.models.Property, aristotle.models.Property.objects.filter( dataelementconcept__dataelement__datasetspecification=self)), (aristotle.models.ValueDomain, aristotle.models.ValueDomain.objects.filter( dataelement__datasetspecification=self)), ]
class Issue(TimeStampedModel): # Fields on a concept that are proposable (must be text) proposable_fields = Choices( ('name', _('Name')), ('definition', _('Definition')), ('references', _('References')), ('origin', _('Origin')), ('comments', _('Comments')), ) name = models.CharField(max_length=1000) description = models.TextField(blank=True) item = ConceptForeignKey(_concept, related_name='issues', on_delete=models.CASCADE) submitter = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='issues', on_delete=models.PROTECT) isopen = models.BooleanField(default=True) proposal_field = models.TextField(choices=proposable_fields, blank=True) proposal_value = models.TextField(blank=True) labels = models.ManyToManyField( 'IssueLabel', blank=True, ) def can_edit(self, user): return user.id == self.submitter.id def can_view(self, user): return self.item.can_view(user) def can_alter_open(self, user): return self.can_edit(user) or self.item.can_edit(user) def apply(self): """Apply proposed changes to an item""" if self.proposal_field and self.proposal_value: item = self.item setattr(item, self.proposal_field, self.proposal_value) return item.save() return None def close(self): """Closes an issue, applying changes to item aswell""" self.isopen = False self.apply() return self.save() def get_absolute_url(self): return url_slugify_issue(self) @classmethod def get_propose_fields(cls): """ Return list of field names and whether fields are html for proposable fields """ fields = [] for fname, uname in cls.proposable_fields: try: field = _concept._meta.get_field(fname) except FieldDoesNotExist: field = None if field: html = False if issubclass(type(field), RichTextField): html = True fields.append({'name': fname, 'html': html}) return fields def __str__(self): return self.name
class Indicator(MDR.concept): """ An indicator is a single measure that is reported on regularly and that provides relevant and actionable information about population or system performance. """ # Subclassing from DataElement causes indicators to present as DataElements, which isn't quite right. backwards_compatible_fields = ['representation_class'] template = "comet/indicator.html" outcome_areas = ConceptManyToManyField('OutcomeArea', related_name="indicators", blank=True) computation_description = MDR.RichTextField(blank=True) computation = MDR.RichTextField(blank=True) numerator_description = MDR.RichTextField(blank=True) denominator_description = MDR.RichTextField(blank=True) disaggregation_description = MDR.RichTextField(blank=True) quality_statement = ConceptForeignKey( "QualityStatement", null=True, blank=True, on_delete=models.SET_NULL, help_text= _("A statement of multiple quality dimensions for the purpose of assessing the quality of the data for reporting against this Indicator." )) rationale = MDR.RichTextField(blank=True) benchmark = MDR.RichTextField(blank=True) reporting_information = MDR.RichTextField(blank=True) dimensions = ConceptManyToManyField("FrameworkDimension", related_name="indicators", blank=True) serialize_weak_entities = [ ('numerators', 'indicatornumeratordefinition_set'), ('denominators', 'indicatordenominatordefinition_set'), ('disaggregators', 'indicatordisaggregationdefinition_set'), ] clone_fields = [ 'indicatornumeratordefinition', 'indicatordenominatordefinition', 'indicatordisaggregationdefinition' ] def add_component(self, model_class, **kwargs): kwargs.pop('indicator', None) from django.db.models import Max max_order = list( model_class.objects.filter(indicator=self).annotate( latest=Max('order')).values_list('order', flat=True)) if not max_order: order = 1 else: order = max_order[0] + 1 return model_class.objects.create(indicator=self, order=order, **kwargs) @property def numerators(self): return MDR.DataElement.objects.filter( indicatornumeratordefinition__indicator=self) def add_numerator(self, **kwargs): self.add_component(model_class=IndicatorNumeratorDefinition, **kwargs) @property def denominators(self): return MDR.DataElement.objects.filter( indicatordenominatordefinition__indicator=self) def add_denominator(self, **kwargs): self.add_component(model_class=IndicatorDenominatorDefinition, **kwargs) @property def disaggregators(self): return MDR.DataElement.objects.filter( indicatordisaggregationdefinition__indicator=self) def add_disaggregator(self, **kwargs): self.add_component(model_class=IndicatorDisaggregationDefinition, **kwargs)