Esempio n. 1
0
    def _get_fields(self):
        if self._fields is None:
            self._fields = fields = {}
            model = self._model
            non_hiddable_fnames = self._non_hiddable_fnames
            fconfigs = FieldsConfig.LocalCache()
            is_field_hidden = fconfigs.get_4_model(model).is_field_hidden
            excluded = self.excluded_fields

            # TODO: use meta.ModelFieldEnumerator (need to be improved for grouped options)
            for field in model._meta.fields:
                if field.get_tag('viewable') and not is_date_field(field) \
                   and not isinstance(field, excluded):
                    if isinstance(field, ModelForeignKey):
                        self._build_related_fields(field, fields, fconfigs)
                    elif not is_field_hidden(
                            field) or field.name in non_hiddable_fnames:
                        fields[field.name] = [field]

            for field in model._meta.many_to_many:
                # if field.get_tag('viewable'):
                if field.get_tag(FieldTag.VIEWABLE):  # TODO: test not viewable
                    self._build_related_fields(field, fields, fconfigs)

        return self._fields
Esempio n. 2
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        model = self.graph.model
        self._field: Optional['Field'] = None
        self.verbose_name = '??'

        field_name = self.value
        if not field_name:
            logger.warning('Instance block: no given field in block config.')
            self.error = _('No field given.')
        else:
            try:
                field = model._meta.get_field(field_name)
            except FieldDoesNotExist as e:
                logger.warning(
                    'Instance block: invalid field in block config: %s',
                    e,
                )
                self.error = _('The field is invalid.')
            else:
                fconf = FieldsConfig.LocalCache()
                error = self._check_field(field=field, fields_configs=fconf)
                if error:
                    logger.warning(
                        'Instance block: invalid field in block config: %s',
                        error,
                    )
                    self.error = error
                else:
                    self.verbose_name = gettext('{field} (Field)').format(
                        field=field.verbose_name,
                    )
                    self._field = field
    def test_localcache_get_4_models(self):
        model1 = FakeContact
        model2 = FakeOrganisation

        h_field1 = 'phone'
        h_field2 = 'url_site'

        create_fc = FieldsConfig.create
        create_fc(model1,
                  descriptions=[(h_field1, {
                      FieldsConfig.HIDDEN: True
                  })])
        create_fc(model2,
                  descriptions=[(h_field2, {
                      FieldsConfig.HIDDEN: True
                  })])

        lc = FieldsConfig.LocalCache()
        fconfigs = lc.get_4_models([model1, model2])

        fc1 = fconfigs.get(model1)
        self.assertIsInstance(fc1, FieldsConfig)
        self.assertEqual(model1, fc1.content_type.model_class())
        self.assertTrue(fc1.is_fieldname_hidden(h_field1))

        self.assertTrue(fconfigs.get(model2).is_fieldname_hidden(h_field2))

        with self.assertNumQueries(0):
            lc.get_4_model(model1)
Esempio n. 4
0
 def __init__(self,
              instance_to_delete,
              fk_handler_classes=None,
              *args,
              **kwargs):
     super().__init__(*args, **kwargs)
     self.instance_to_delete = instance_to_delete
     self.fields_configs = FieldsConfig.LocalCache()
     self.fk_handler_classes = fk_handler_classes or self.fk_handler_classes
     self.handlers = self._build_handlers()
     self._add_handlers_fields()
Esempio n. 5
0
    def choices(cls, model):
        fconf = FieldsConfig.LocalCache()
        check_field = partial(cls._check_field, fields_configs=fconf)

        yield from ModelFieldEnumerator(
            # model, deep=0, only_leafs=False,
            model, depth=0, only_leaves=False,
        ).filter(
            # lambda f, deep: check_field(field=f) is None,
            lambda model, field, depth: check_field(field=field) is None,
        ).choices()
Esempio n. 6
0
    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}')))
Esempio n. 7
0
    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}')))
Esempio n. 8
0
    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}')))
Esempio n. 9
0
 def __init__(self, model):
     super().__init__(model=model)
     self.fields_configs = FieldsConfig.LocalCache()
Esempio n. 10
0
 def _fields_configs(self) -> FieldsConfig.LocalCache:
     "Protected API. Useful for ReportHands/ReportGraphHand (in order to avoid queries)"
     return FieldsConfig.LocalCache()
Esempio n. 11
0
 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')))
Esempio n. 12
0
    def __init__(self, entity, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.report = entity
        report_ct = entity.ct
        model = report_ct.model_class()

        instance = self.instance
        fields = self.fields

        aggregate_field_f = fields['aggregate_field']
        abscissa_field_f  = fields['abscissa_field']
        is_count_f        = fields['is_count']

        get_fconf = FieldsConfig.LocalCache().get_4_model
        # TODO: split('__', 1) when 'count' is an aggregate operator
        ordinate_field_name, __, aggregate = instance.ordinate.rpartition('__')

        # Abscissa -------------------------------------------------------------
        def absc_field_excluder(field, deep):
            # TODO: set the ForeignKeys to entities as not enumerable automatically ?
            if isinstance(field, RelatedField) and \
               issubclass(field.remote_field.model, CremeEntity):
                return True

            return get_fconf(field.model).is_field_hidden(field) and \
                   field.name != instance.abscissa

        abscissa_model_fields = ModelFieldEnumerator(model, deep=0, only_leafs=False) \
                                    .filter(self._filter_abcissa_field, viewable=True) \
                                    .exclude(absc_field_excluder) \
                                    .choices()

        self.rtypes = rtypes = dict(RelationType.objects
                                                .compatible(report_ct, include_internals=True)
                                                .values_list('id', 'predicate')
                                   )
        abscissa_predicates = [*rtypes.items()]
        sort_key = collator.sort_key
        abscissa_predicates.sort(key=lambda k: sort_key(k[1]))

        abscissa_choices = [
            (_('Fields'),        abscissa_model_fields),
            (_('Relationships'), abscissa_predicates),
        ]

        self.abs_cfields = cfields = {
            cf.id: cf
                for cf in CustomField.objects
                                     .filter(field_type__in=(CustomField.ENUM,
                                                             CustomField.DATETIME,
                                                            ),
                                             content_type=report_ct,
                                            )
        }

        if cfields:
            # TODO: sort ?
            abscissa_choices.append(
                (_('Custom fields'), [(cf.id, cf.name) for cf in cfields.values()])
            )

        # TODO: we could build the complete map fields/allowed_types, instead of doing AJAX queries...
        abscissa_field_f.choices = abscissa_choices
        abscissa_field_f.widget.target_url = reverse('reports__graph_types', args=(report_ct.id,))  # Meh

        # Ordinate -------------------------------------------------------------
        def agg_field_excluder(field, deep):
            return get_fconf(field.model).is_field_hidden(field) and \
                   field.name != ordinate_field_name

        aggfields = [
            field_info[0]
                for field_info in ModelFieldEnumerator(model, deep=0)
                                    .filter((lambda f, depth: isinstance(f, field_aggregation_registry.authorized_fields)),
                                            viewable=True
                                           )
                                    .exclude(agg_field_excluder)
        ]
        aggfield_choices = [(field.name, field.verbose_name) for field in aggfields]
        aggcustom_choices = [
            *CustomField.objects
                        .filter(field_type__in=field_aggregation_registry.authorized_customfields,
                                content_type=report_ct,
                               )
                        .values_list('id', 'name')
        ]
        ordinate_choices = aggfield_choices or aggcustom_choices

        if ordinate_choices:
            self.force_count = False

            money_fields = [field for field in aggfields if isinstance(field, MoneyField)]
            if money_fields:
                # TODO: lazy lazily-translated-string interpolation
                aggregate_field_f.help_text = gettext(
                        'If you use a field related to money, the entities should use the same '
                        'currency or the result will be wrong. Concerned fields are : {}'
                    ).format(', '.join(str(field.verbose_name) for field in money_fields))

            if aggcustom_choices and aggfield_choices:
                ordinate_choices = [
                    (_('Fields'),        aggfield_choices),
                    (_('Custom fields'), aggcustom_choices),
               ]
        else:
            self.force_count = True
            ordinate_choices = [('', _('No field is usable for aggregation'))]

            disabled_attrs = {'disabled': True}
            aggregate_field_f.widget.attrs = disabled_attrs
            fields['aggregate'].widget.attrs = disabled_attrs

            is_count_f.help_text = _('You must make a count because no field is usable for aggregation')
            is_count_f.initial = True
            is_count_f.widget.attrs = disabled_attrs

        aggregate_field_f.choices = ordinate_choices

        # Initial data ---------------------------------------------------------
        data = self.data

        if data:
            get_data = data.get
            widget = abscissa_field_f.widget
            widget.source_val = get_data('abscissa_field')
            widget.target_val = get_data('abscissa_group_by')

            # NB: ugly hack ; see AbscissaGroupBySelect/toggleDaysField.
            #     Should be removed in Creme2.2 with a clean field/widget
            fields['abscissa_group_by'].widget.attrs['data-initial-value'] = get_data('abscissa_group_by')
        elif instance.pk is not None:
            fields['aggregate'].initial = aggregate
            aggregate_field_f.initial   = ordinate_field_name
            abscissa_field_f.initial    = instance.abscissa

            widget = abscissa_field_f.widget
            widget.source_val = instance.abscissa
            widget.target_val = instance.type

            # NB: idem
            fields['abscissa_group_by'].widget.attrs['data-initial-value'] = instance.type

        # TODO: remove this sh*t when is_count is a real widget well initialized (disabling set by JS)
        if is_count_f.initial or instance.is_count or data.get('is_count'):
            disabled_attrs = {'disabled': True}
            aggregate_field_f.widget.attrs = disabled_attrs
            fields['aggregate'].widget.attrs = disabled_attrs