def _update_linked_docs(self): # TODO: factorise (Relation.get_real_objects() ??) relations = Relation.objects.filter(subject_entity=self.id, type=REL_SUB_CREDIT_NOTE_APPLIED, ) \ .select_related('object_entity') Relation.populate_real_object_entities(relations) for rel in relations: rel.object_entity.get_real_entity().save()
def detailview_display(self, context): activity = context['object'] btc = self.get_template_context( context, activity.relations.filter(type=constants.REL_OBJ_ACTIVITY_SUBJECT) .select_related('type', 'object_entity'), ) Relation.populate_real_object_entities(btc['page'].object_list) return self._render(btc)
def _set_orga_as_subject(sender, instance, **kwargs): if instance.type_id != REL_SUB_PART_2_ACTIVITY: return # NB: when a Relation is created, it is saved twice in order to set the link # with its symmetric instance if instance.symmetric_relation_id is None: return activity = instance.object_entity.get_real_entity() if not is_auto_orga_subject_enabled(): return user = instance.user Relation.objects.safe_multi_save( Relation( subject_entity=orga, type_id=REL_SUB_ACTIVITY_SUBJECT, object_entity=activity, user=user, ) for orga in Organisation.objects.filter( relations__type__in=( persons_constants.REL_OBJ_EMPLOYED_BY, persons_constants.REL_OBJ_MANAGES, ), relations__object_entity=instance.subject_entity_id, ).exclude(is_deleted=False, is_managed=True))
def post_save_relation_opp_subject_activity(sender, instance, **kwargs): if instance.type_id == REL_OBJ_ACTIVITY_SUBJECT: object_entity = instance.object_entity get_ct = ContentType.objects.get_for_model if object_entity.entity_type == get_ct(get_opportunity_model()): # relations = Relation.objects.filter(subject_entity=object_entity, # type=REL_SUB_COMPLETE_GOAL, # object_entity__entity_type=get_ct(get_act_model()), # ) # # create_relation = partial(Relation.objects.create, # subject_entity=instance.subject_entity, # type_id=REL_SUB_COMPLETE_GOAL, # user=instance.user, # ) # # for relation in relations: # create_relation(object_entity=relation.object_entity) activity = instance.subject_entity user = instance.user Relation.objects.safe_multi_save( Relation( subject_entity=activity, type_id=REL_SUB_COMPLETE_GOAL, object_entity=relation.object_entity, user=user, ) for relation in Relation.objects.filter( subject_entity_id=object_entity.id, type=REL_SUB_COMPLETE_GOAL, object_entity__entity_type=get_ct(get_act_model()), ))
def get_credit_notes(self): credit_notes = self._creditnotes_cache if credit_notes is None: self._creditnotes_cache = credit_notes = [] if self.id: relations = Relation.objects.filter(subject_entity=self.id, type=REL_OBJ_CREDIT_NOTE_APPLIED, ) \ .select_related('object_entity') Relation.populate_real_object_entities(relations) credit_notes.extend(rel.object_entity.get_real_entity() for rel in relations if not rel.object_entity.is_deleted) return credit_notes
def _get_relations_to_create(self): instance = self.instance return super()._get_relations_to_create().append( Relation( user=instance.user, subject_entity=instance, type_id=constants.REL_SUB_GEN_BY_EVENT, object_entity=self.event, ))
def _get_relations_to_create(this): instance = this.instance return super()._get_relations_to_create().append( Relation( subject_entity=this.related_entity.get_real_entity(), type_id=constants.REL_SUB_RELATED_2_DOC, object_entity=instance, user=instance.user, ), )
def get_form_kwargs(self): kwargs = super().get_form_kwargs() rtype = self.get_rtype() if rtype: kwargs['forced_relations'] = [ Relation(object_entity=self.linked_orga, type=rtype), ] return kwargs
def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['forced_relations'] = [ Relation( type_id=constants.REL_SUB_COMPLETE_GOAL, object_entity=self.related_entity, ), ] return kwargs
def _get_relations_to_create(self): instance = self.instance return super()._get_relations_to_create().append( Relation(subject_entity=instance, type_id=REL_SUB_LINKED_2_PTASK, object_entity=self._task, user=instance.user, ) )
def _get_relations_to_create(this): instance = this.instance cdata = this.cleaned_data return super()._get_relations_to_create().extend( Relation( user=instance.user, subject_entity=instance, type_id=rtype_id, object_entity=cdata['customers_managed_orga'], ) for rtype_id in cdata['customers_rtypes'])
def test_relations_credentials06(self): "Link credentials on the created entity + forced relationships." user = self.login(is_superuser=False, creatable_models=[FakeContact]) create_creds = partial(SetCredentials.objects.create, role=self.role) create_creds( value=EntityCredentials.VIEW | EntityCredentials.LINK, set_type=SetCredentials.ESET_OWN, ) create_creds(value=EntityCredentials.VIEW, set_type=SetCredentials.ESET_ALL) orga = FakeOrganisation.objects.create(user=user, name='Oshino corp.') rtype = RelationType.create( ('test-subject_heals', 'heals'), ('test-object_heals', 'is healed by'), )[1] data = { 'first_name': 'Kanbaru', 'last_name': 'Suruga', } forced_relations = [Relation(type=rtype, object_entity=orga)] # KO --- form1 = FakeContactForm( user=user, data={ **data, 'user': self.other_user.id }, forced_relations=forced_relations, ) self.assertFormInstanceErrors( form1, ( 'user', _('You are not allowed to link with the «{models}» of this user.' ).format(models='Test Contacts', ), ), ) # OK --- form2 = FakeContactForm( user=user, data={ **data, 'user': user.id }, forced_relations=forced_relations, ) self.assertFalse(form2.errors) subject = form2.save() self.assertRelationCount(1, subject, rtype, orga)
def test_disable02(self): "Relationship creation & deletion" user = self.user hayao = FakeContact.objects.create(user=user, first_name='Hayao', last_name='Miyazaki') ghibli = FakeOrganisation.objects.create(user=user, name='Ghibli') rtype = RelationType.create(('test-subject_employed', 'is employed'), ('test-object_employed', 'employs'))[0] old_count = HistoryLine.objects.count() rel = Relation(user=user, subject_entity=hayao, object_entity=ghibli, type=rtype) HistoryLine.disable(rel) rel.save() self.assertEqual(old_count, HistoryLine.objects.count()) # ----------------------- rel = self.refresh(rel) HistoryLine.disable(rel) rel.delete() self.assertEqual(old_count, HistoryLine.objects.count())
def test_manager_safe_multi_save01(self): "Create several relation" rtype1, srtype1 = RelationType.create( ('test-subject_challenge', 'challenges'), ('test-object_challenge', 'is challenged by')) rtype2, srtype2 = RelationType.create( ('test-subject_foobar', 'loves'), ('test-object_foobar', 'is loved by')) user = self.user create_contact = partial(FakeContact.objects.create, user=user) ryuko = create_contact(first_name='Ryuko', last_name='Matoi') satsuki = create_contact(first_name='Satsuki', last_name='Kiryuin') count = Relation.objects.safe_multi_save([ Relation(user=user, subject_entity=ryuko, type=rtype1, object_entity=satsuki), Relation(user=user, subject_entity=ryuko, type=rtype2, object_entity=satsuki), ]) self.assertEqual(2, count) rel1 = self.get_object_or_fail(Relation, type=rtype1) self.assertEqual(ryuko.id, rel1.subject_entity_id) self.assertEqual(satsuki.id, rel1.object_entity_id) self.assertEqual(user.id, rel1.user_id) self.assertEqual(srtype1, rel1.symmetric_relation.type) rel2 = self.get_object_or_fail(Relation, type=rtype2) self.assertEqual(ryuko.id, rel2.subject_entity_id) self.assertEqual(satsuki.id, rel2.object_entity_id) self.assertEqual(user.id, rel2.user_id) self.assertEqual(srtype2, rel2.symmetric_relation.type)
def test_relations05(self): "Forced Relations (no <relations> block)." user = self.login() orga = FakeOrganisation.objects.create(user=user, name='Oshino corp.') rtype = RelationType.create(('test-subject_heals', 'has healed'), ('test-object_heals', 'healed by'), )[1] form = FakeContactForm( user=user, forced_relations=[Relation(type=rtype, object_entity=orga)], ) self.assertNotIn('rtypes_info', form.fields)
def _get_relations_to_create(this): relations = super()._get_relations_to_create() rtype = this.cleaned_data.get('rtype_for_organisation') instance = this.instance if rtype: relations.append(Relation( subject_entity=instance, type=rtype, object_entity=self.linked_orga, user=instance.user, )) return relations
def _set_orga_as_subject(sender, instance, **kwargs): if instance.type_id != REL_SUB_PART_2_ACTIVITY: return # NB: when a Relation is created, it is saved twice in order to set the link # with its symmetric instance if instance.symmetric_relation_id is None: return activity = instance.object_entity.get_real_entity() if not activity.is_auto_orga_subject_enabled(): return # create_rel = partial(Relation.objects.get_or_create, # type_id=REL_SUB_ACTIVITY_SUBJECT, # object_entity=activity, # defaults={'user': instance.user}, # ) # # for orga in Organisation.objects.filter(relations__type__in=(persons_constants.REL_OBJ_EMPLOYED_BY, # persons_constants.REL_OBJ_MANAGES, # ), # relations__object_entity=instance.subject_entity_id, # ) \ # .exclude(is_deleted=False, is_managed=True): # try: # create_rel(subject_entity=orga) # except Relation.MultipleObjectsReturned: # logger.warning('_set_orga_as_subject(): duplicated ' # 'Relation <subject=%s type=%s object=%s>', # orga.id, REL_SUB_ACTIVITY_SUBJECT, activity.id, # ) user = instance.user Relation.objects.safe_multi_save( Relation( subject_entity=orga, type_id=REL_SUB_ACTIVITY_SUBJECT, object_entity=activity, user=user, ) for orga in Organisation.objects .filter(relations__type__in=( persons_constants.REL_OBJ_EMPLOYED_BY, persons_constants.REL_OBJ_MANAGES, ), relations__object_entity=instance.subject_entity_id, ) .exclude(is_deleted=False, is_managed=True) )
def test_manager_safe_multi_save03(self): "Avoid creating existing relations" rtype1 = RelationType.create( ('test-subject_challenge', 'challenges'), ('test-object_challenge', 'is challenged by'))[0] rtype2 = RelationType.create(('test-subject_foobar', 'loves'), ('test-object_foobar', 'is loved by'))[0] user = self.user create_contact = partial(FakeContact.objects.create, user=user) ryuko = create_contact(first_name='Ryuko', last_name='Matoi') satsuki = create_contact(first_name='Satsuki', last_name='Kiryuin') def build_rel1(): return Relation(user=user, subject_entity=ryuko, type=rtype1, object_entity=satsuki) rel1 = build_rel1() rel1.save() with self.assertNoException(): Relation.objects.safe_multi_save([ build_rel1(), Relation(user=user, subject_entity=ryuko, type=rtype2, object_entity=satsuki), build_rel1(), ]) self.assertStillExists(rel1) rel2 = self.get_object_or_fail(Relation, type=rtype2) self.assertEqual(ryuko.id, rel2.subject_entity_id) self.assertEqual(satsuki.id, rel2.object_entity_id) self.assertEqual(user.id, rel2.user_id)
def save(self, *args, **kwargs): instance = self.instance instance.floating_type = self.floating_type instance.type, instance.sub_type = self._get_activity_type_n_subtype() # super(_ActivityForm, self).save(*args, **kwargs) super().save(*args, **kwargs) # create_relation = partial(Relation.objects.create, object_entity=instance, # type_id=constants.REL_SUB_PART_2_ACTIVITY, user=instance.user, # ) # # for participant in self.participants: # create_relation(subject_entity=participant) Relation.objects.safe_multi_save( Relation( subject_entity=participant, type_id=constants.REL_SUB_PART_2_ACTIVITY, object_entity=instance, user=instance.user, ) for participant in self.participants) return instance
def post(self, request, *args, **kwargs): klass = self.get_ctype().model_class() try: rtype_id, set_as_current, workflow_action = self.behaviours[klass] except KeyError as e: raise Http404('Bad billing document type') from e user = request.user user.has_perm_to_create_or_die(klass) # TODO: check in template too (must upgrade 'has_perm' to use owner!=None) user.has_perm_to_link_or_die(klass, owner=user) opp = self.get_related_entity() b_document = klass.objects.create( user=user, issuing_date=now(), status_id=1, currency=opp.currency, source=opp.emitter, target=opp.target, ) create_relation = partial( Relation.objects.create, subject_entity=b_document, user=user, ) create_relation(type_id=rtype_id, object_entity=opp) b_document.generate_number( ) # Need the relationship with emitter organisation b_document.name = self.generated_name.format(document=b_document, opportunity=opp) b_document.save() relations = Relation.objects.filter( subject_entity=opp.id, type__in=[ constants.REL_OBJ_LINKED_PRODUCT, constants.REL_OBJ_LINKED_SERVICE, ], ).select_related('object_entity') # TODO: Missing test case if relations: Relation.populate_real_object_entities(relations) vat_value = Vat.get_default_vat() Product = get_product_model() for relation in relations: item = relation.object_entity.get_real_entity() line_klass = ProductLine if isinstance( item, Product) else ServiceLine line_klass.objects.create( related_item=item, related_document=b_document, unit_price=item.unit_price, unit=item.unit, vat_value=vat_value, ) if set_as_current: create_relation(type_id=constants.REL_SUB_CURRENT_DOC, object_entity=opp) if workflow_action: workflow_action(opp.emitter, opp.target, user) # if request.is_ajax(): if is_ajax(request): return HttpResponse() return redirect(opp)
def _post_instance_creation(self, instance, line, updated): # super(ActivityMassImportForm, self)._post_instance_creation(instance, line, updated) super()._post_instance_creation(instance, line, updated) cdata = self.cleaned_data user = instance.user participant_ids = set() if updated: # TODO: improve get_participant_relations() (not retrieve real entities) participant_ids.update( Relation.objects.filter( type=constants.REL_SUB_PART_2_ACTIVITY, object_entity=instance.id, ).values_list('subject_entity', flat=True)) # create_sub_rel = partial(Relation.objects.get_or_create, object_entity=instance, # type_id=constants.REL_SUB_ACTIVITY_SUBJECT, # defaults={'user': user}, # ) # else: # create_sub_rel = partial(Relation.objects.create, object_entity=instance, # type_id=constants.REL_SUB_ACTIVITY_SUBJECT, user=user, # ) def add_participant(participant): if participant.id not in participant_ids: # Relation.objects.create( Relation.objects.safe_create( subject_entity=participant, type_id=constants.REL_SUB_PART_2_ACTIVITY, object_entity=instance, user=user, ) participant_ids.add(participant.id) # We could create a cache in self (or even put a cache-per-request in Calendar.get_user_default_calendar() # but the import can take a long time, & the default Calendar could change => TODO: use a time based cache ? default_calendars_cache = {} def add_to_default_calendar(part_user): calendar = default_calendars_cache.get(part_user.id) if calendar is None: default_calendars_cache[part_user.id] = calendar = \ Calendar.get_user_default_calendar(part_user) instance.calendars.add(calendar) i_participate, my_calendar = cdata['my_participation'] if i_participate: add_participant(user.linked_contact) instance.calendars.add(my_calendar) for participant in self.user_participants: add_participant(participant) add_to_default_calendar(participant.is_user) dyn_participants, err_messages = cdata[ 'participants'].extract_value(line, self.user) for err_msg in err_messages: self.append_error(err_msg) for participant in dyn_participants: add_participant(participant) part_user = participant.is_user if part_user is not None: instance.calendars.add( Calendar.get_user_default_calendar(part_user)) # Subjects ---- subjects, err_messages = cdata['subjects'].extract_value( line, self.user) for err_msg in err_messages: self.append_error(err_msg) # create_sub_rel = partial(Relation.objects.get_or_create, object_entity=instance, # type_id=constants.REL_SUB_ACTIVITY_SUBJECT, # defaults={'user': user}, # ) # # for subject in subjects: # try: # create_sub_rel(subject_entity=subject) # except Relation.MultipleObjectsReturned: # pass Relation.objects.safe_multi_save( Relation( subject_entity=subject, type_id=constants.REL_SUB_ACTIVITY_SUBJECT, object_entity=instance, user=user, ) for subject in subjects)
def generate_png(self, user): from os.path import join import pygraphviz as pgv # NB: to work with utf8 label in node: all node must be added explicitly with # unicode label, and when edges are a created, nodes identified by their # labels encoded as string graph = pgv.AGraph(directed=True) # NB: "self.roots.all()" causes a strange additional query (retrieving of the base CremeEntity !).... has_perm_to_view = user.has_perm_to_view roots = [ root for root in RootNode.objects.filter( graph=self.id).select_related('entity') if not root.entity.is_deleted and has_perm_to_view(root.entity) ] add_node = graph.add_node add_edge = graph.add_edge # TODO: entity cache ? regroups relations by type ? ... CremeEntity.populate_real_entities([root.entity for root in roots ]) #small optimisation for root in roots: add_node(str(root.entity), shape='box') # add_node('filled box', shape='box', style='filled', color='#FF00FF') # add_node('filled box v2', shape='box', style='filled', fillcolor='#FF0000', color='#0000FF', penwidth='2.0') #default pensize="1.0" orbital_nodes = {} #cache for root in roots: subject = root.entity str_subject = str(subject) relations = subject.relations.filter(type__in=root.relation_types.all())\ .select_related('object_entity', 'type') Relation.populate_real_object_entities( relations) # Small optimisation for relation in relations: object_ = relation.object_entity if not user.has_perm_to_view(object_): continue uni_object = str(object_) str_object = uni_object orbital_node = orbital_nodes.get(object_.id) if not orbital_node: add_node(uni_object) orbital_nodes[object_.id] = str_object add_edge(str_subject, str_object, label=str(relation.type.predicate)) # add_edge('b', 'd', color='#FF0000', fontcolor='#00FF00', label='foobar', style='dashed') orbital_rtypes = self.orbital_relation_types.all() if orbital_rtypes: orbital_ids = orbital_nodes.keys() for relation in Relation.objects.filter( subject_entity__in=orbital_ids, object_entity__in=orbital_ids, type__in=orbital_rtypes).select_related('type'): add_edge(orbital_nodes[relation.subject_entity_id], orbital_nodes[relation.object_entity_id], label=str(relation.type.predicate), style='dashed') # print graph.string() graph.layout(prog='dot') # Algo: neato dot twopi circo fdp nop img_format = 'png' # Format: pdf svg img_basename = 'graph_{}.{}'.format(self.id, img_format) try: path = FileCreator(join(settings.MEDIA_ROOT, 'upload', 'graphs'), img_basename).create() except FileCreator.Error as e: raise self.GraphException(e) from e try: # graph.draw(join(dir_path, filename), format='png') # Format: pdf svg graph.draw(path, format=img_format) # Format: pdf svg except IOError as e: delete_file(path) raise self.GraphException(str(e)) from e fileref = FileRef.objects.create( # user=request.user, TODO filedata='upload/graphs/' + basename(path), basename=img_basename, ) return HttpResponseRedirect( reverse('creme_core__dl_file', args=(fileref.filedata, )))
def test_relations04(self): "Forced Relations." user = self.login() create_contact = partial(FakeContact.objects.create, user=user) contact1 = create_contact(first_name='Hitagi', last_name='Senjyogahara') contact2 = create_contact(first_name='Koyomi', last_name='Araragi') orga = FakeOrganisation.objects.create(user=user, name='Oshino corp.') create_rtype = RelationType.create rtype1 = create_rtype( ('test-subject_loves', 'loves'), ('test-object_loves', 'is loved'), )[0] rtype2 = create_rtype( ('test-subject_heals', 'has healed', [FakeOrganisation]), ('test-object_heals', 'healed by', [FakeContact]), )[1] fields1 = FakeContactForm( user=user, forced_relations=[Relation(type=rtype2, object_entity=orga)], ).fields with self.assertNoException(): info_field = fields1['rtypes_info'] self.assertIn('relation_types', fields1) self.assertHTMLEqual( _('This relationship will be added: {predicate} «{entity}»'). format( predicate=rtype2.predicate, entity=orga, ), info_field.initial) # --- forced_relations = [ Relation(type=rtype2, object_entity=orga), Relation(type=rtype1, object_entity=contact1), ] fields2 = FakeContactForm(user=user, forced_relations=forced_relations).fields self.assertIn('relation_types', fields2) self.assertHTMLEqual( _('These relationships will be added: {}').format( '<ul><li>{} «{}»</li><li>{} «{}»</li></ul>'.format( rtype2.predicate, orga, rtype1.predicate, contact1, )), fields2['rtypes_info'].initial) # --- form = FakeContactForm( user=user, forced_relations=forced_relations, data={ 'user': user.id, 'first_name': 'Kanbaru', 'last_name': 'Suruga', 'relation_types': self.formfield_value_multi_relation_entity( (rtype1.id, contact2), ), }, ) self.assertFalse(form.errors) subject = form.save() self.assertRelationCount(1, subject, rtype1, contact1) self.assertRelationCount(1, subject, rtype1, contact2) self.assertRelationCount(1, subject, rtype2, orga)
def __init__(self, linked_orga, rtype=None, *args, **kwargs): if rtype: kwargs['forced_relations'] = [ Relation(type=rtype, object_entity=linked_orga), ] super().__init__(*args, **kwargs) # if not linked_orga: # linked_orga = self.initial.get('linked_orga') # if linked_orga: # warnings.warn('RelatedContactForm: the use of initial for "linked_orga" is deprecated ; ' # 'use constructor argument "linked_orga" instead.', # DeprecationWarning # ) self.linked_orga = linked_orga # if not self.linked_orga: # warnings.warn('RelatedContactForm: empty "linked_orga" argument is deprecated ;.', # DeprecationWarning # ) # return # fields = self.fields # fields['orga_overview'].initial = self.linked_orga # if not rtype: # rtype = self.initial.get('relation_type') # if rtype: # warnings.warn('RelatedContactForm: the use of initial for "rtype" is deprecated ; ', # 'use constructor argument "rtype" instead.', # DeprecationWarning # ) self.relation_type = rtype # if rtype: # relation_field = CharField(label=gettext('Relation type'), # widget=TextInput(attrs={'readonly': 'readonly'}), # initial=self.relation_type, # todo: required=False ?? # ) # else: # get_ct = ContentType.objects.get_for_model # relation_field = ModelChoiceField(label=gettext('Status in the organisation'), # queryset=RelationType.objects.filter( # subject_ctypes=get_ct(Contact), # object_ctypes=get_ct(Organisation), # is_internal=False, # ), # ) # # fields['relation'] = relation_field if rtype: del self.fields['rtype_for_organisation'] else: rtype_f = self.fields['rtype_for_organisation'] rtype_f.label = gettext('Status in «{organisation}»').format( organisation=linked_orga, ) # TODO: factorise (see User form hooking) get_ct = ContentType.objects.get_for_model rtype_f.queryset = RelationType.objects.filter( subject_ctypes=get_ct(Contact), object_ctypes=get_ct(Organisation), is_internal=False, )
def build_rel1(): return Relation(user=user, subject_entity=ryuko, type=rtype1, object_entity=satsuki)