def build(self, context): int_ids = getUtility(IIntIds) schedule_calendars = set() calendar = getScheduleCalendar(self.calendar.__parent__) if calendar is None: return schedule_calendars.add(calendar) for event in self.events: schedule = context.shared.schedule_map.get(event['timetable_key']) if schedule is None: continue period = context.shared.period_map[event['period_key']] owner_int_id = int(schedule.__parent__.__name__) owner = int_ids.getObject(owner_int_id) calendar = getScheduleCalendar(owner) if calendar is None: continue if calendar not in schedule_calendars: schedule_calendars.add(calendar) new_event = self.findEvent(calendar, period, event['dtstart'], event['duration']) if new_event is not None: new_event.description = event['description'] new_event.location = event['location'] for resource in event['resources']: if resource not in new_event.resources: new_event.bookResource(resource) # XXX: maybe copy over old "free" section events here schedule_cal_relationships = [(cal, IRelationshipLinks(cal)) for cal in schedule_calendars] old_relationships = IRelationshipLinks(self.calendar) old_subscriptions = list( old_relationships.getLinksByRole(URICalendarSubscriber)) for link in old_subscriptions: old_info = link.extra_info for schedule_cal, relationships in schedule_cal_relationships: info = CalendarOverlayInfo(schedule_cal, old_info.show, old_info.color1, old_info.color2) info.__parent__ = old_info.__parent__ try: relationships.find(link.my_role, link.target, link.role, link.rel_type) except ValueError: relate(link.rel_type, (schedule_cal, link.my_role), (link.target, link.role), extra_info=info) for link in old_subscriptions: unrelate(link.rel_type, (self.calendar, link.my_role), (link.target, link.role))
def duplicate(link, obj): """Create duplicate link from obj to link.target.""" obj_links = IRelationshipLinks(obj) for olink in obj_links: if (olink.target is link.target and olink.role_hash == link.role_hash and olink.rel_type_hash == link.rel_type_hash): raise DuplicateRelationship shared = OOBTree() for key, val in link.shared.items(): shared[key] = val zope.event.notify( BeforeRelationshipEvent(link.rel_type, (obj, link.my_role), (link.target, link.role), shared)) uri_cache = getURICache() uri_cache.cache(link.rel_type) uri_cache.cache(link.my_role) uri_cache.cache(link.role) link_a = Link(link.my_role, link.target, link.role, link.rel_type, shared) IRelationshipLinks(obj).add(link_a) link_b = Link(link.role, obj, link.my_role, link.rel_type, shared) IRelationshipLinks(link.target).add(link_b) zope.event.notify( RelationshipAddedEvent(link.rel_type, (obj, link.my_role), (link.target, link.role), shared))
def getLinks(self): links_1 = IRelationshipLinks(self.participant1) links_2 = IRelationshipLinks(self.participant2) try: link_1_to_2 = links_1.find(self.role1, self.participant2, self.role2, self.rel_type) except ValueError: raise NoSuchRelationship try: link_2_to_1 = links_2.find(self.role2, self.participant1, self.role1, self.rel_type) except ValueError: raise NoSuchRelationship return link_1_to_2, link_2_to_1
def evolveRelationships(date, target, rel_type, other_role, new_type): int_ids = getUtility(IIntIds) links = IRelationshipLinks(target).iterLinksByRole(other_role) for link in links: link = int_ids.getObject(link.lid) if link.rel_type != rel_type: continue backlink = IRelationshipLinks(link.target).find( link.role, target, link.my_role, link.rel_type) link.rel_type = backlink.rel_type = new_type if 'tmp' not in link.shared: link.shared['tmp'] = () link.state.set(date) notify(ObjectModifiedEvent(link)) notify(ObjectModifiedEvent(backlink))
def relationships(self, **party): other_role = self.getPartyRole(**party) obj = party.values()[0] links = IRelationshipLinks(obj).iterLinksByRole(other_role, rel_type=self.rel_type) for link in links: yield RelationshipInfo(obj, link)
def unrelateOnDeletion(event): """Remove all relationships when an object is deleted.""" if not IObjectRemovedEvent.providedBy(event): return linkset = IRelationshipLinks(event.object, None) if linkset is not None: # event.object may be a ContainedProxy unrelateAll(getProxiedObject(event.object))
def state(self, other): links = IRelationshipLinks(self.this) try: link = links.find(self.my_role, other, self.other_role, self.rel_type) except ValueError: return None return link.state
def __nonzero__(self): linkset = IRelationshipLinks(self.this) iterator = iter(linkset.iterLinksByRole(self.other_role, self.rel_type)) try: iterator.next() except StopIteration: return False else: return True
def __contains__(self, other): if other is None: return False linkset = IRelationshipLinks(self.this) for link in linkset.getCachedLinksByTarget(other): if (link.rel_type_hash == hash(self.rel_type) and link.my_role_hash == hash(self.my_role) and link.role_hash == hash(self.other_role) and self._filter(link)): return True return False
def __contains__(self, other): if other is None: return False other = removeSecurityProxy(other) linkset = IRelationshipLinks(self.this) filter = self.rel_type.filter for link in linkset.getCachedLinksByTarget(other): if (link.my_role_hash == hash(self.my_role) and link.role_hash == hash(self.other_role) and filter(link)): return True return False
def unrelateCalendarOnDeletion(event): """When you delete an object, relationships of it's calendar should be removed >>> from schooltool.relationship.tests import setUp, tearDown >>> from schooltool.testing.setup import setUpCalendaring >>> setUp() >>> setUpCalendaring() >>> import zope.event >>> old_subscribers = zope.event.subscribers[:] >>> from schooltool.app.overlay import unrelateCalendarOnDeletion >>> zope.event.subscribers.append(unrelateCalendarOnDeletion) We will need some object that implements IHaveCalendar for that: >>> from zope.container.btree import BTreeContainer >>> container = BTreeContainer() >>> from schooltool.person.person import Person >>> container = BTreeContainer() >>> container['jonas'] = jonas = Person(username="******") >>> container['petras'] = petras = Person(username="******") Let's add calendar of Petras to the list of overlaid calendars: >>> jonas.overlaid_calendars.add(ISchoolToolCalendar(petras)) <...CalendarOverlayInfo object at ...> >>> list(jonas.overlaid_calendars) [<schooltool.app.overlay.CalendarOverlayInfo object at ...>] If we delete Petras - Jonas should have no calendars in his overlay list: >>> del container['petras'] >>> list(jonas.overlaid_calendars) [] Restore old subscribers: >>> zope.event.subscribers[:] = old_subscribers >>> tearDown() """ if not IObjectRemovedEvent.providedBy(event): return # event.object may be a ContainedProxy obj = getProxiedObject(event.object) if not IHaveCalendar.providedBy(obj): return calendar = ISchoolToolCalendar(obj) linkset = IRelationshipLinks(calendar, None) if linkset is not None: unrelateAll(calendar)
def unrelateOnCopy(event): """Remove all relationships when an object is copied.""" if not IObjectCopiedEvent.providedBy(event): return # event.object may be a ContainedProxy obj = getProxiedObject(event.object) linkset = IRelationshipLinks(obj, None) if linkset is not None: links_to_remove = [] for link in linkset: other_linkset = IRelationshipLinks(link.target) try: other_linkset.find(link.role, obj, link.my_role, link.rel_type) except ValueError: # The corresponding other link was not copied, so we have a # degenerate one-sided relationship. Let's remove it # altogether. It would not difficult to have a different # function, cloneRelationshipsOnCopy, that would create # a corresponding link in other_linkset. links_to_remove.append(link) for link in links_to_remove: linkset.remove(link)
def relate(self, other, meaning=ACTIVE, code=ACTIVE_CODE): links = IRelationshipLinks(self.this) try: link = links.find(self.my_role, other, self.other_role, self.rel_type) except ValueError: relate(self.rel_type, (self.this, self.my_role), (other, self.other_role)) link = links.find(self.my_role, other, self.other_role, self.rel_type) link.state.set(self.filter_date, meaning=meaning, code=code) notify( LinkStateModifiedEvent(link, self.this, other, self.filter_date, meaning, code))
def unrelateAll(obj): """Break all relationships of `obj`. Note that this operation is not atomic: if an event subscriber catches a BeforeRemovingRelationshipEvent and vetoes the operation, some relationships may have been removed, while others may still be there. """ links_of_a = IRelationshipLinks(obj) relationships = [(link.rel_type, (obj, link.my_role), (link.target, link.role)) for link in links_of_a] for args in relationships: try: unrelate(*args) except NoSuchRelationship: pass # it was a loop, so we tried to delete it twice return
def unrelate(self, other): """Delete state on filtered date or unrelate completely if no states left or filtered date is .all() """ links = IRelationshipLinks(self.this) link = links.find(self.my_role, other, self.other_role, self.rel_type) if self.filter_date is None: unrelate(self.rel_type, (self.this, self.my_role), (other, self.other_role)) return state = link.state date = state.closest(self.filter_date) if date is None: raise KeyError(self.filter_date) del state[date] try: iter(state).next() except StopIteration: unrelate(self.rel_type, (self.this, self.my_role), (other, self.other_role))
def __iter__(self): for link in IRelationshipLinks(self.this): if link.role == self.other_role and link.rel_type == self.rel_type: yield link.extra_info
def __contains__(self, key): return (self.lid, key) in self.catalog['shared'] def __getitem__(self, key): return self.catalog['shared'].get(self.lid, key) def __setitem__(self, key, value): link = getUtility(IIntIds).getObject(self.lid) link.shared[key] = value notify(ObjectModifiedEvent(link)) def relate(rel_type, (a, role_of_a), (b, role_of_b), extra_info=None): """Establish a relationship between objects `a` and `b`.""" for link in IRelationshipLinks(a): if (link.target is b and link.role_hash == hash(role_of_b) and link.rel_type_hash == hash(rel_type)): raise DuplicateRelationship shared = OOBTree() shared['X'] = extra_info zope.event.notify( BeforeRelationshipEvent(rel_type, (a, role_of_a), (b, role_of_b), shared)) uri_cache = getURICache() uri_cache.cache(rel_type) uri_cache.cache(role_of_a) uri_cache.cache(role_of_b) link_a = Link(role_of_a, b, role_of_b, rel_type, shared) IRelationshipLinks(a).add(link_a) link_b = Link(role_of_b, a, role_of_a, rel_type, shared)
def relationships(self): links = IRelationshipLinks(self.this).iterLinksByRole( self.other_role, rel_type=self.rel_type) for link in links: yield RelationshipInfo(self.this, link)
def int_ids(self): int_ids = getUtility(IIntIds) linkset = IRelationshipLinks(self.this) for link in linkset.iterLinksByRole(self.other_role, self.rel_type): yield int_ids.getId(LinkTargetKeyReference(link))
def __len__(self): count = 0 linkset = IRelationshipLinks(self.this) for i in linkset.iterLinksByRole(self.other_role, self.rel_type): count += 1 return count
def _iter_filtered_links(self): links = IRelationshipLinks(self.this).getCachedLinksByRole( self.other_role) for link in links: if self._filter(link): yield link
def iterRelatedObjects(obj, role, rel_type=None, catalog=None): """Return all objects related to `obj` with a given role.""" return IRelationshipLinks(obj).iterTargetsByRole(role, rel_type, catalog=catalog)