def key(self, value: str) -> None: self._key = value if value.startswith('-'): attr_name = value[1:] self._reverse_order = True else: attr_name = value self._reverse_order = False field_info = FieldInfo(self.queryset.model, attr_name) if any(f.many_to_many for f in field_info): raise ValueError( 'Invalid key: ManyToManyFields cannot be used as key.') # TODO: if related_model is not None ? last_field = field_info[-1] if last_field.is_relation: subfield_model = last_field.remote_field.model subfield_ordering = subfield_model._meta.ordering if not subfield_ordering: raise ValueError( f'Invalid key: related field model "{subfield_model}" ' f'should have Meta.ordering') attr_name += '__' + subfield_ordering[0] field_info = FieldInfo(self.queryset.model, attr_name) self._attr_name = attr_name self._key_field_info = field_info
def test_localcache_is_fieldinfo_hidden05(self): "Field in CremeEntity." hidden = 'description' FieldsConfig.objects.create( content_type=FakeImage, descriptions=[(hidden, {FieldsConfig.HIDDEN: True})], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertTrue(is_hidden(FieldInfo(FakeImage, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertTrue(is_hidden(FieldInfo(FakeContact, f'image__{hidden}')))
def test_localcache_is_fieldinfo_hidden04(self): "Sub-field hidden." hidden = 'exif_date' FieldsConfig.objects.create( content_type=FakeImage, descriptions=[ (hidden, {FieldsConfig.HIDDEN: True}), ], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertFalse(is_hidden(FieldInfo(FakeContact, 'first_name'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertTrue(is_hidden(FieldInfo(FakeContact, f'image__{hidden}')))
def __new__(cls, report_field): try: field_info = FieldInfo(report_field.model, report_field.name) except FieldDoesNotExist as e: raise ReportHand.ValueError('Invalid field: "{}" (does not exist)'.format(report_field.name)) from e info_length = len(field_info) if info_length > 1: if info_length > 2: raise ReportHand.ValueError('Invalid field: "{}" (too deep)'.format(report_field.name)) second_part = field_info[1] if (isinstance(second_part, (ForeignKey, ManyToManyField)) and issubclass(second_part.remote_field.model, CremeEntity)): raise ReportHand.ValueError('Invalid field: "{}" (no entity at depth=1)'.format(report_field.name)) first_part = field_info[0] klass = RHForeignKey if isinstance(first_part, ForeignKey) else \ RHManyToManyField if isinstance(first_part, ManyToManyField) else \ RHRegularField instance = ReportHand.__new__(klass) instance._field_info = field_info return instance
def validate_fieldname(graph, field_name): try: field_info = FieldInfo(graph.model, field_name) except FieldDoesNotExist: return 'invalid field "{}"'.format(field_name) if len(field_info) > 1: return 'field "{}" with deep > 1'.format(field_name) field = field_info[0] if not (isinstance(field, ForeignKey) and issubclass(field.remote_field.model, CremeEntity)): return 'field "{}" is not a ForeignKey to CremeEntity'.format(field_name)
def get_q(self, *, model, field_name, values): # As default, set isnull operator (always true, negate is done later) query = Q(**{self.key_pattern.format(field_name): True}) # Add filter for text fields, "isEmpty" should mean null or empty string finfo = FieldInfo(model, field_name) # TODO: what about CustomField ?! if isinstance(finfo[-1], (models.CharField, models.TextField)): query |= Q(**{field_name: ''}) # Negate filter on false value if not values[0]: query.negate() return query
def test_localcache_is_fieldinfo_hidden06(self): "Sub-field with depth > 1." hidden = 'description' FieldsConfig.objects.create( content_type=FakeFolder, descriptions=[(hidden, {FieldsConfig.HIDDEN: True})], ) is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertTrue(is_hidden(FieldInfo(FakeFolder, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, hidden))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, 'linked_folder'))) self.assertTrue(is_hidden(FieldInfo(FakeDocument, f'linked_folder__{hidden}'))) self.assertFalse(is_hidden(FieldInfo(FakeDocument, 'linked_folder__parent'))) self.assertTrue(is_hidden(FieldInfo(FakeDocument, f'linked_folder__parent__{hidden}')))
def test_localcache_is_fieldinfo_hidden01(self): "No field configured." is_hidden = FieldsConfig.LocalCache().is_fieldinfo_hidden self.assertFalse(is_hidden(FieldInfo(FakeContact, 'first_name'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image'))) self.assertFalse(is_hidden(FieldInfo(FakeContact, 'image__exif_date')))
def create_report_n_graphes(self, rt_obj_emit_orga): "Create the report 'Opportunities' and 2 ReportGraphs." from django.contrib.auth import get_user_model from creme import reports from creme.creme_core.utils.meta import FieldInfo # from creme.reports import constants as rep_constants from creme.reports.constants import RFT_FIELD, RFT_RELATION from creme.reports.core.graph.fetcher import SimpleGraphFetcher from creme.reports.models import Field admin = get_user_model().objects.get_admin() if reports.report_model_is_custom(): logger.info( 'Report model is custom => no Opportunity report is created.') return # Create the report ---------------------------------------------------- report = reports.get_report_model().objects.create( name=_('Opportunities'), user=admin, ct=Opportunity, ) # TODO: helper method(s) (see EntityFilterCondition) create_field = partial(Field.objects.create, report=report, type=RFT_FIELD) create_field(name='name', order=1) create_field(name='estimated_sales', order=2) create_field(name='made_sales', order=3) create_field(name='sales_phase__name', order=4) create_field(name=rt_obj_emit_orga.id, order=5, type=RFT_RELATION) # Create 2 graphs ------------------------------------------------------ if reports.rgraph_model_is_custom(): logger.info('ReportGraph model is custom' ' => no Opportunity report-graph is created.') return sales_cell = EntityCellRegularField.build(Opportunity, 'estimated_sales') if sales_cell is None: logger.warning( 'Opportunity seems not having a field "estimated_sales"' ' => no ReportGraph created.') return ReportGraph = reports.get_rgraph_model() # TODO: helper method (range only on DateFields etc...) create_graph = partial( ReportGraph.objects.create, linked_report=report, user=admin, # ordinate_type=rep_constants.RGA_SUM, ordinate_type=ReportGraph.Aggregator.SUM, ordinate_cell_key=sales_cell.key, ) esales_vname = FieldInfo(Opportunity, 'estimated_sales').verbose_name rgraph1 = create_graph( name=_('Sum {estimated_sales} / {sales_phase}').format( estimated_sales=esales_vname, sales_phase=FieldInfo(Opportunity, 'sales_phase').verbose_name, ), # abscissa_type=rep_constants.RGT_FK, abscissa_type=ReportGraph.Group.FK, abscissa_cell_value='sales_phase', ) rgraph2 = create_graph( name=_( 'Sum {estimated_sales} / Quarter (90 days on {closing_date})'). format( estimated_sales=esales_vname, closing_date=FieldInfo(Opportunity, 'closing_date').verbose_name, ), # abscissa_type=rep_constants.RGT_RANGE, abscissa_type=ReportGraph.Group.RANGE, abscissa_cell_value='closing_date', abscissa_parameter='90', ) # Create 2 instance block items for the 2 graphs ----------------------- brick_id1 = SimpleGraphFetcher( rgraph1).create_brick_config_item().brick_id brick_id2 = SimpleGraphFetcher( rgraph2).create_brick_config_item().brick_id create_bdl = partial( BrickDetailviewLocation.objects.create_if_needed, zone=BrickDetailviewLocation.RIGHT, model=Opportunity, ) create_bdl(brick=brick_id1, order=4) create_bdl(brick=brick_id2, order=6) create_hbl = BrickHomeLocation.objects.create create_hbl(brick_id=brick_id1, order=5) create_hbl(brick_id=brick_id2, order=6)
def create_report_n_graphes(self, rt_obj_emit_orga): "Create the report 'Opportunities' and 2 ReportGraphs." from django.contrib.auth import get_user_model from creme.creme_core.utils.meta import FieldInfo from creme import reports from creme.reports import constants as rep_constants from creme.reports.models import Field admin = get_user_model().objects.get_admin() if reports.report_model_is_custom(): logger.info( 'Report model is custom => no Opportunity report is created.') return # Create the report ---------------------------------------------------- report = reports.get_report_model() \ .objects.create(name=_('Opportunities'), user=admin, ct=Opportunity) # TODO: helper method(s) (see EntityFilterCondition) create_field = partial(Field.objects.create, report=report, type=rep_constants.RFT_FIELD) create_field(name='name', order=1) create_field(name='estimated_sales', order=2) create_field(name='made_sales', order=3) create_field(name='sales_phase__name', order=4) create_field(name=rt_obj_emit_orga.id, order=5, type=rep_constants.RFT_RELATION) # Create 2 graphs ----------------------------------------------------- if reports.rgraph_model_is_custom(): logger.info( 'ReportGraph model is custom => no Opportunity report-graph is created.' ) return # TODO: helper method ('sum' => is_count=False, range only on DateFields etc...) create_graph = partial( reports.get_rgraph_model().objects.create, linked_report=report, user=admin, is_count=False, ordinate='estimated_sales__sum', ) esales_vname = FieldInfo(Opportunity, 'estimated_sales').verbose_name rgraph1 = create_graph( name=_('Sum {estimated_sales} / {sales_phase}').format( estimated_sales=esales_vname, sales_phase=FieldInfo(Opportunity, 'sales_phase').verbose_name, ), abscissa='sales_phase', type=rep_constants.RGT_FK, ) rgraph2 = create_graph( name=_( 'Sum {estimated_sales} / Quarter (90 days on {closing_date})'). format( estimated_sales=esales_vname, closing_date=FieldInfo(Opportunity, 'closing_date').verbose_name, ), abscissa='closing_date', type=rep_constants.RGT_RANGE, days=90, ) # Create 2 instance block items for the 2 graphs ---------------------- brick_id1 = rgraph1.create_instance_brick_config_item().brick_id brick_id2 = rgraph2.create_instance_brick_config_item().brick_id create_bdl = partial( BrickDetailviewLocation.objects.create_if_needed, zone=BrickDetailviewLocation.RIGHT, model=Opportunity, ) create_bdl(brick=brick_id1, order=4) create_bdl(brick=brick_id2, order=6) BrickHomeLocation.objects.create(brick_id=brick_id1, order=5) BrickHomeLocation.objects.create(brick_id=brick_id2, order=6)