def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # TODO: add an 'exclude' argument in creme_entity_content_types() ?? get_for_model = ContentType.objects.get_for_model is_invalid = gui_bricks.brick_registry.is_model_invalid self.fields['ctype'].ctypes = ( get_for_model(model) for model in creme_registry.iter_entity_models() if not is_invalid(model))
def filtered_entity_ctypes(app_labels): ext_app_labels = { app_config.label for app_config in extended_app_configs(app_labels) } get_ct = ContentType.objects.get_for_model for model in creme_registry.iter_entity_models(): if model._meta.app_label in ext_app_labels: yield get_ct(model)
def filtered_entity_ctypes( app_labels ): # TODO: move to creme_core.utils ?? (improve iter_entity_models() ?) ext_app_labels = { app_config.label for app_config in extended_app_configs(app_labels) } get_ct = ContentType.objects.get_for_model for model in creme_registry.iter_entity_models(): if model._meta.app_label in ext_app_labels: yield get_ct(model)
def detailview_display(self, context): # NB: we wrap the ContentType instances instead of store extra data in # them because the instances are stored in a global cache, so we do # not want to mutate them. class _ContentTypeWrapper: # TODO: move from here ? __slots__ = ('ctype', 'locations_info', 'default_count') def __init__(self, ctype): self.ctype = ctype self.default_count = 0 self.locations_info = ( ) # List of tuples (role_arg, role_label, block_count) # with role_arg == role.id or 'superuser' # TODO: factorise with SearchConfigBlock ? # TODO: factorise with CustomBlockConfigItemCreateForm , add a method in block_registry ? get_ct = ContentType.objects.get_for_model is_invalid = self.brick_registry.is_model_invalid ctypes = [ _ContentTypeWrapper(get_ct(model)) for model in creme_registry.iter_entity_models() if not is_invalid(model) ] sort_key = collator.sort_key ctypes.sort(key=lambda ctw: sort_key(str(ctw.ctype))) btc = self.get_template_context( context, ctypes, max_conf_count=UserRole.objects.count() + 1, # NB: '+ 1' is for super-users config. ) ctypes_wrappers = btc['page'].object_list brick_counts = defaultdict( lambda: defaultdict(int) ) # brick_counts[content_type.id][(role_id, superuser)] -> count role_ids = set() for bdl in BrickDetailviewLocation.objects \ .filter(content_type__in=[ctw.ctype for ctw in ctypes_wrappers])\ .exclude(zone=BrickDetailviewLocation.HAT): if bdl.brick_id: # Do not count the 'place-holder' (empty block IDs which mean "no-block for this zone") role_id = bdl.role_id brick_counts[bdl.content_type_id][(role_id, bdl.superuser)] += 1 role_ids.add(role_id) role_names = dict( UserRole.objects.filter(id__in=role_ids).values_list('id', 'name')) superusers_label = gettext('Superuser') # TODO: cached_lazy_gettext for ctw in ctypes_wrappers: count_per_role = brick_counts[ctw.ctype.id] ctw.default_count = count_per_role.pop((None, False), 0) ctw.locations_info = locations_info = [] for (role_id, superuser), block_count in count_per_role.items(): if superuser: role_arg = 'superuser' role_label = superusers_label else: role_arg = role_id role_label = role_names[role_id] locations_info.append((role_arg, role_label, block_count)) locations_info.sort( key=lambda t: sort_key(t[1])) # Sort by role label btc['default_count'] = BrickDetailviewLocation.objects.filter( content_type=None, role=None, superuser=False, ).count() return self._render(btc)
def advanced_search(models_IDs, research_terms, props_IDs, user): total = 0 results = [] models = [] # We will need to access to model class so we retrieve it with the id if not models_IDs: # Add all the existing models models.extend(creme_registry.iter_entity_models()) else: # Add just the requested models for model_ID in models_IDs: models.append(get_ct_or_404(model_ID).model_class()) if(len(models) > 1): # Useless to sort when we have only one model models.sort(key=lambda m: m._meta.verbose_name) filter_viewable = partial(EntityCredentials.filter, user=user) searcher = Searcher(models, user) for model in models: # To retrieve the entities the user is authorized to see entities = list(filter_viewable \ (queryset=searcher.search(model, research_terms))) # Here we will remove the entities which don't have the # requested properties if props_IDs: # We need to add [:] because some entity will be removed # Otherwise some entities will not be processed # http://stackoverflow.com/a/1352908 for entity in entities[:]: # props is the list of the properties related to the # entity and not the properties the user is looking for props = list(CremeProperty.objects.filter \ (creme_entity = entity.id)) # If the entity have some properties if len(props) > 0: count_properties_found = len(props) # We decrement a number if a requested property is # not found. TODO: Better explain the algorithm... for prop in props: if props_IDs.count(prop.type_id) == 0: count_properties_found -= 1 if count_properties_found < len(props_IDs): entities.remove(entity) else: # We remove it because we are looking for props entities.remove(entity) total += len(entities) results.append({'model': model, 'fields': searcher.get_fields(model), 'entities': entities, } ) models_list = [model._meta.verbose_name for model in models] return total, results, models_list
def advanced_search(models_IDs, research_terms, props_IDs, user): total = 0 results = [] models = [] # We will need to access to model class so we retrieve it with the id if not models_IDs: # Add all the existing models models.extend(creme_registry.iter_entity_models()) else: # Add just the requested models for model_ID in models_IDs: models.append(get_ct_or_404(model_ID).model_class()) if (len(models) > 1): # Useless to sort when we have only one model models.sort(key=lambda m: m._meta.verbose_name) filter_viewable = partial(EntityCredentials.filter, user=user) searcher = Searcher(models, user) for model in models: # To retrieve the entities the user is authorized to see entities = list(filter_viewable \ (queryset=searcher.search(model, research_terms))) # Here we will remove the entities which don't have the # requested properties if props_IDs: # We need to add [:] because some entity will be removed # Otherwise some entities will not be processed # http://stackoverflow.com/a/1352908 for entity in entities[:]: # props is the list of the properties related to the # entity and not the properties the user is looking for props = list(CremeProperty.objects.filter \ (creme_entity = entity.id)) # If the entity have some properties if len(props) > 0: count_properties_found = len(props) # We decrement a number if a requested property is # not found. TODO: Better explain the algorithm... for prop in props: if props_IDs.count(prop.type_id) == 0: count_properties_found -= 1 if count_properties_found < len(props_IDs): entities.remove(entity) else: # We remove it because we are looking for props entities.remove(entity) total += len(entities) results.append({ 'model': model, 'fields': searcher.get_fields(model), 'entities': entities, }) models_list = [model._meta.verbose_name for model in models] return total, results, models_list
def detailview_display(self, context): # NB: we wrap the ContentType instances instead of store extra data in # them because the instances are stored in a global cache, so we do # not want to mutate them. class _ContentTypeWrapper: __slots__ = ('ctype', 'all_users_filters', 'owned_filters') def __init__(this, ctype): this.ctype = ctype this.all_users_filters = () this.owned_filters = () # TODO: factorise with SearchConfigBrick ? get_ct = ContentType.objects.get_for_model user = context['user'] has_perm = user.has_perm_to_access ctypes = [ _ContentTypeWrapper(get_ct(model)) for model in creme_registry.iter_entity_models() if has_perm(model._meta.app_label) ] sort_key = collator.sort_key ctypes.sort(key=lambda ctw: sort_key(str(ctw.ctype))) btc = self.get_template_context(context, ctypes) ctypes_wrappers = btc['page'].object_list # NB: efilters[content_type.id][user.id] -> List[EntityFilter] efilters = defaultdict(lambda: defaultdict(list)) user_ids = set() for efilter in EntityFilter.objects.filter( filter_type=EF_USER, entity_type__in=[ctw.ctype for ctw in ctypes_wrappers], ): # TODO: templatetags instead ? efilter.edition_perm = efilter.can_edit(user)[0] efilter.deletion_perm = efilter.can_delete(user)[0] user_id = efilter.user_id efilters[efilter.entity_type_id][user_id].append(efilter) user_ids.add(user_id) users = get_user_model().objects.in_bulk(user_ids) def efilter_key(efilter): return sort_key(efilter.name) for ctw in ctypes_wrappers: ctype_efilters_per_users = efilters[ctw.ctype.id] all_users_filters = ctype_efilters_per_users.pop(None, None) or [] all_users_filters.sort(key=efilter_key) ctw.all_users_filters = all_users_filters ctw.owned_filters = [( str(users[user_id]), sorted(user_efilters, key=efilter_key), ) for user_id, user_efilters in ctype_efilters_per_users.items()] return self._render(btc)
def populate(self): already_populated = RelationType.objects.filter( pk=constants.REL_SUB_SOLD).exists() Act = commercial.get_act_model() ActObjectivePattern = commercial.get_pattern_model() Strategy = commercial.get_strategy_model() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() RelationType.create( (constants.REL_SUB_SOLD, _('has sold'), [Contact, Organisation]), (constants.REL_OBJ_SOLD, _('has been sold by'), [Product, Service ]), ) complete_goal_models = {*creme_registry.iter_entity_models()} complete_goal_models.discard(Strategy) if apps.is_installed('creme.billing'): from creme import billing from creme.billing.registry import lines_registry complete_goal_models.discard(billing.get_credit_note_model()) complete_goal_models.discard(billing.get_template_base_model()) complete_goal_models.difference_update(lines_registry) RelationType.create( ( constants.REL_SUB_COMPLETE_GOAL, _('completes a goal of the commercial action'), complete_goal_models, ), ( constants.REL_OBJ_COMPLETE_GOAL, _('is completed thanks to'), [Act], ), ) # --------------------------- CremePropertyType.create(constants.PROP_IS_A_SALESMAN, _('is a salesman'), [Contact]) # --------------------------- MarketSegment.objects.get_or_create( property_type=None, defaults={'name': _('All the organisations')}, ) # --------------------------- for i, title in enumerate( [_('Phone calls'), _('Show'), _('Demo')], start=1): create_if_needed(ActType, {'pk': i}, title=title, is_custom=False) # --------------------------- create_hf = HeaderFilter.objects.create_if_needed create_hf( pk=constants.DEFAULT_HFILTER_ACT, model=Act, name=_('Com Action view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), (EntityCellRegularField, { 'name': 'expected_sales' }), (EntityCellRegularField, { 'name': 'due_date' }), ], ) create_hf( pk=constants.DEFAULT_HFILTER_STRATEGY, model=Strategy, name=_('Strategy view'), cells_desc=[(EntityCellRegularField, { 'name': 'name' })], ) create_hf( pk=constants.DEFAULT_HFILTER_PATTERN, model=ActObjectivePattern, name=_('Objective pattern view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), (EntityCellRegularField, { 'name': 'segment' }), ], ) # --------------------------- def build_custom_form_items(creation_descriptor, edition_descriptor, field_names): base_groups_desc = [ { 'name': _('General information'), 'layout': LAYOUT_DUAL_FIRST, 'cells': [ *((EntityCellRegularField, { 'name': fname }) for fname in field_names), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_REGULARFIELDS }, ), ], }, { 'name': _('Description'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ (EntityCellRegularField, { 'name': 'description' }), ], }, { 'name': _('Custom fields'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_CUSTOMFIELDS }, ), ], }, ] CustomFormConfigItem.objects.create_if_needed( descriptor=creation_descriptor, groups_desc=[ *base_groups_desc, { 'name': _('Properties'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. CREME_PROPERTIES }, ), ], }, { 'name': _('Relationships'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.RELATIONS }, ), ], }, ], ) CustomFormConfigItem.objects.create_if_needed( descriptor=edition_descriptor, groups_desc=base_groups_desc, ) build_custom_form_items( creation_descriptor=custom_forms.ACT_CREATION_CFORM, edition_descriptor=custom_forms.ACT_EDITION_CFORM, field_names=[ 'user', 'name', 'expected_sales', 'cost', 'goal', 'start', 'due_date', 'act_type', 'segment', ], ) build_custom_form_items( creation_descriptor=custom_forms.PATTERN_CREATION_CFORM, edition_descriptor=custom_forms.PATTERN_EDITION_CFORM, field_names=[ 'user', 'name', 'average_sales', 'segment', ], ) build_custom_form_items( creation_descriptor=custom_forms.STRATEGY_CREATION_CFORM, edition_descriptor=custom_forms.STRATEGY_EDITION_CFORM, field_names=[ 'user', 'name', ], ) # --------------------------- create_searchconf = SearchConfigItem.objects.create_if_needed create_searchconf(Act, ['name', 'expected_sales', 'cost', 'goal']) create_searchconf(Strategy, ['name']) create_searchconf(ActObjectivePattern, [], disabled=True) # --------------------------- SettingValue.objects.get_or_create( key_id=setting_keys.orga_approaches_key.id, defaults={'value': True}, ) # --------------------------- Job.objects.get_or_create( type_id=creme_jobs.com_approaches_emails_send_type.id, defaults={ 'language': settings.LANGUAGE_CODE, 'periodicity': date_period_registry.get_period('days', 1), 'status': Job.STATUS_OK, # The CommercialApproach field for Activities' CustomForms is not # in the default configuration, so a enabled job would be annoying. 'enabled': False, }, ) # --------------------------- # TODO: move to "not already_populated" section in creme2.4 if not MenuConfigItem.objects.filter( entry_id__startswith='commercial-').exists(): container = MenuConfigItem.objects.get_or_create( entry_id=ContainerEntry.id, entry_data={'label': _('Commercial')}, defaults={'order': 30}, )[0] create_mitem = MenuConfigItem.objects.create create_mitem(entry_id=menu.ActsEntry.id, order=50, parent=container) create_mitem(entry_id=menu.StrategiesEntry.id, order=55, parent=container) create_mitem(entry_id=menu.SegmentsEntry.id, order=60, parent=container) create_mitem(entry_id=menu.PatternsEntry.id, order=70, parent=container) directory = MenuConfigItem.objects.filter( entry_id=ContainerEntry.id, entry_data={ 'label': _('Directory') }, ).first() if directory is not None: create_mitem(entry_id=menu.SalesmenEntry.id, order=100, parent=directory) creations = MenuConfigItem.objects.filter( entry_id=ContainerEntry.id, entry_data={ 'label': _('+ Creation') }, ).first() if creations is not None: create_mitem(entry_id=menu.ActCreationEntry.id, order=40, parent=creations) # --------------------------- if not already_populated: ButtonMenuItem.objects.create_if_needed( button=buttons.CompleteGoalButton, order=60, ) TOP = BrickDetailviewLocation.TOP RIGHT = BrickDetailviewLocation.RIGHT LEFT = BrickDetailviewLocation.LEFT # BrickDetailviewLocation.objects.multi_create( # defaults={'brick': bricks.ApproachesBrick, 'order': 10, 'zone': RIGHT}, # data=[ # {}, # default configuration # {'model': Contact}, # {'model': Organisation}, # ] # ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Act, 'zone': LEFT }, data=[ { 'order': 5 }, # generic information brick { 'brick': bricks.ActObjectivesBrick, 'order': 10 }, { 'brick': bricks.RelatedOpportunitiesBrick, 'order': 20 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': ActObjectivePattern, 'zone': LEFT }, data=[ { 'brick': bricks.PatternComponentsBrick, 'order': 10, 'zone': TOP }, { 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Strategy, 'zone': LEFT }, data=[ { 'brick': bricks.SegmentDescriptionsBrick, 'order': 10, 'zone': TOP }, { 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': bricks.EvaluatedOrgasBrick, 'order': 50 }, { 'brick': bricks.AssetsBrick, 'order': 60 }, { 'brick': bricks.CharmsBrick, 'order': 70 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) if apps.is_installed('creme.assistants'): logger.info('Assistants app is installed ' '=> we use the assistants blocks on detail views') from creme.assistants import bricks as a_bricks for model in (Act, ActObjectivePattern, Strategy): BrickDetailviewLocation.objects.multi_create( defaults={ 'model': model, 'zone': RIGHT }, data=[ { 'brick': a_bricks.TodosBrick, 'order': 100 }, { 'brick': a_bricks.MemosBrick, 'order': 200 }, { 'brick': a_bricks.AlertsBrick, 'order': 300 }, { 'brick': a_bricks.UserMessagesBrick, 'order': 400 }, ], ) if apps.is_installed('creme.documents'): # logger.info("Documents app is installed # => we use the documents blocks on Strategy's detail views") from creme.documents.bricks import LinkedDocsBrick BrickDetailviewLocation.objects.multi_create( defaults={ 'brick': LinkedDocsBrick, 'order': 600, 'zone': RIGHT }, data=[ { 'model': Act }, { 'model': ActObjectivePattern }, { 'model': Strategy }, ], )