Пример #1
0
 def assertMembershipIntervalsEqual(self, expected):
     memberships = session.session.query(Membership).filter_by(
         user=self.user, group=self.group)
     got = IntervalSet(closed(m.begins_at, m.ends_at) for m in memberships)
     assert expected == got, "IntervalSets differ: " \
                             "expected {0!r}" \
                             "got      {1!r}".format(expected, got)
Пример #2
0
    def test_adding_single_membership(self):
        begins_at = session.utcnow()
        ends_at = begins_at + timedelta(hours=1)
        during = closed(begins_at, ends_at)

        self.add_membership(during)
        self.assertMembershipIntervalsEqual(IntervalSet(during))
Пример #3
0
 def assertMembershipIntervalsEqual(self, expected):
     memberships = session.session.query(Membership).filter_by(
         user=self.user, group=self.group)
     got = IntervalSet(m.active_during.closure for m in memberships)
     assert expected == got, "IntervalSets differ: " \
                             "expected {!r}" \
                             "got      {!r}".format(expected, got)
Пример #4
0
def make_member_of(user, group, processor, during=UnboundedInterval):
    """
    Makes a user member of a group in a given interval. If the given interval
    overlaps with an existing membership, this method will join the overlapping
    intervals together, so that there will be at most one membership for
    particular user in particular group at any given point in time.

    :param User user: the user
    :param Group group: the group
    :param User processor: User issuing the addition
    :param Interval during:
    """
    memberships = session.session.query(Membership).filter(
        Membership.user == user, Membership.group == group,
        Membership.active(during)).all()
    intervals = IntervalSet(
        closed(m.begins_at, m.ends_at) for m in memberships).union(during)
    for m in memberships:
        session.session.delete(m)
    session.session.add_all(
        Membership(begins_at=i.begin, ends_at=i.end, user=user, group=group)
        for i in intervals)
    message = deferred_gettext(u"Added to group {group} during {during}.")
    log_user_event(message=message.format(group=group.name,
                                          during=during).to_json(),
                   user=user,
                   author=processor)
Пример #5
0
def remove_member_of(user, group, processor, during=UnboundedInterval):
    """Remove a user from a group in a given interval.

    The interval defaults to the unbounded interval, so that the user
    will be removed from the group at any point in time, **removing
    all memberships** in this group retroactively.

    However, a common use case is terminating a membership by setting
    ``during=closedopen(now, None)``.

    :param User user: the user
    :param Group group: the group
    :param User processor: User issuing the removal
    :param Interval during:
    """
    memberships = session.session.query(Membership).filter(
        Membership.user == user, Membership.group == group,
        Membership.active(during)).all()
    intervals = IntervalSet(
        closed(m.begins_at, m.ends_at) for m in memberships).difference(during)
    for m in memberships:
        session.session.delete(m)
    session.session.add_all(
        Membership(begins_at=i.begin, ends_at=i.end, user=user, group=group)
        for i in intervals)
    message = deferred_gettext(u"Removed from group {group} during {during}.")
    log_user_event(message=message.format(group=group.name,
                                          during=during).to_json(),
                   user=user,
                   author=processor)
Пример #6
0
def make_member_of(user, group, processor, during=UnboundedInterval):
    """
    Makes a user member of a group in a given interval. If the given interval
    overlaps with an existing membership, this method will join the overlapping
    intervals together, so that there will be at most one membership for
    particular user in particular group at any given point in time.

    :param User user: the user
    :param Group group: the group
    :param User processor: User issuing the addition
    :param Interval during:
    """

    if group.permission_level > processor.permission_level:
        raise PermissionError("cannot create a membership for a group with a"
                              " higher permission level")

    memberships: list[Membership] = [
        m for m in user.active_memberships(when=during) if m.group == group
    ]
    intervals = IntervalSet(m.active_during.closure
                            for m in memberships).union(during)
    for m in memberships:
        session.session.delete(m)
    session.session.flush()
    session.session.add_all(
        Membership(active_during=i, user=user, group=group) for i in intervals)
    message = deferred_gettext("Added to group {group} during {during}.")
    log_user_event(message=message.format(group=group.name,
                                          during=during).to_json(),
                   user=user,
                   author=processor)
Пример #7
0
 def assertIntervalSetOperationEquals(cls, operation, args_and_expected):
     """
     :param callable operation:
     :param iterable[iterable[IntervalSet], unknown)] args_and_expected:
     """
     cls.assertIntervalSetMethodEquals(
         operation, ((args, IntervalSet(expected))
                     for args, expected in args_and_expected))
Пример #8
0
 def test_sort_join(self):
     self.assertEqual(
         IntervalSet([
             closed(2, 3),
             closed(2, None),
             closed(None, 1),
             closed(1, 3),
             closed(2, 3),
             closed(-10, None)
         ]), IntervalSet([closed(None, None)]))
     self.assertEqual(
         IntervalSet([
             empty(6),
             closedopen(1, 2),
             empty(0),
             closedopen(2, 3),
             open(4, 5)
         ]),
         IntervalSet([closedopen(1, 3), open(4, 5)]),
     )
Пример #9
0
 def assertIntervalSetMethodEquals(cls, method, args_and_expected):
     """
     :param callable method:
     :param iterable[iterable[IntervalSet], unknown)] args_and_expected:
     """
     for args, expected in args_and_expected:
         args = [IntervalSet(intervals) for intervals in args]
         got = method(*args)
         assert got == expected, (
             "Evaluating {0}({1}) failed: expected {2}, got {3}".format(
                 method.__name__, ', '.join(map(str, args)), expected, got))
Пример #10
0
    def test_join_overlapping_memberships(self):
        begins_at1 = session.utcnow()
        ends_at1 = begins_at1 + timedelta(hours=2)
        during1 = closed(begins_at1, ends_at1)
        begins_at2 = begins_at1 + timedelta(hours=1)
        ends_at2 = begins_at1 + timedelta(hours=3)
        during2 = closed(begins_at2, ends_at2)

        self.add_membership(during1)
        self.add_membership(during2)
        self.assertMembershipIntervalsEqual(IntervalSet(closed(begins_at1, ends_at2)))
Пример #11
0
 def test_removing_memberships(self):
     t0 = session.utcnow()
     t1 = t0 + timedelta(hours=1)
     t2 = t0 + timedelta(hours=2)
     t3 = t0 + timedelta(hours=3)
     t4 = t0 + timedelta(hours=4)
     t5 = t0 + timedelta(hours=5)
     self.add_membership(closed(t0, t2))
     self.add_membership(closed(t3, t5))
     self.remove_membership(closed(t1, t4))
     self.assertMembershipIntervalsEqual(IntervalSet(
         (closed(t0, t1), closed(t4, t5))))
Пример #12
0
    def test_removing_all_memberships(self):
        begins_at1 = session.utcnow()
        ends_at1 = begins_at1 + timedelta(hours=1)
        during1 = closed(begins_at1, ends_at1)
        begins_at2 = begins_at1 + timedelta(hours=2)
        ends_at2 = begins_at1 + timedelta(hours=3)
        during2 = closed(begins_at2, ends_at2)

        self.add_membership(during1)
        self.add_membership(during2)
        self.remove_membership()
        self.assertMembershipIntervalsEqual(IntervalSet())
Пример #13
0
 def property_intervals(self, name, when=UnboundedInterval):
     """
     Get the set of intervals in which the user was granted a given property
     :param str name:
     :param Interval when:
     :returns: The set of intervals in which the user was granted the
     property
     :rtype: IntervalSet
     """
     property_assignments = object_session(self).query(
         Property.granted, Membership.begins_at, Membership.ends_at).filter(
             Property.name == name,
             Property.property_group_id == PropertyGroup.id,
             PropertyGroup.id == Membership.group_id,
             Membership.user_id == self.id).all()
     granted_intervals = IntervalSet(
         closed(begins_at, ends_at)
         for granted, begins_at, ends_at in property_assignments if granted)
     denied_intervals = IntervalSet(
         closed(begins_at, ends_at)
         for granted, begins_at, ends_at in property_assignments
         if not granted)
     return (granted_intervals - denied_intervals).intersect(when)
Пример #14
0
 def test_type_mangling(self):
     # TODO one test per assertion and `target` / `base` as fixtures
     target = IntervalSet([closed(0, 1)])
     # Creation
     assert target == IntervalSet(closed(0, 1))
     assert target == IntervalSet([closed(0, 1)])
     with pytest.raises(TypeError):
         IntervalSet(0)
     # Union
     base = IntervalSet(())
     assert target == base | IntervalSet(closed(0, 1))
     assert target == base | closed(0, 1)
     assert target == base | [closed(0, 1)]
     # Intersection
     base = target | closed(1, 2)
     assert target == base & IntervalSet(openclosed(0, 1))
     assert target == base & openclosed(0, 1)
     assert target == base & [openclosed(0, 1)]
     # Difference
     assert target == base - IntervalSet(openclosed(1, 2))
     assert target == base - openclosed(1, 2)
     assert target == base - [openclosed(1, 2)]
Пример #15
0
 def test_type_mangling(self):
     target = IntervalSet([closed(0, 1)])
     # Creation
     self.assertEqual(target, IntervalSet(closed(0, 1)))
     self.assertEqual(target, IntervalSet([closed(0, 1)]))
     self.assertRaises(TypeError, IntervalSet, 0)
     # Union
     base = IntervalSet(())
     self.assertEqual(target, base | IntervalSet(closed(0, 1)))
     self.assertEqual(target, base | closed(0, 1))
     self.assertEqual(target, base | [closed(0, 1)])
     # Intersection
     base = target | closed(1, 2)
     self.assertEqual(target, base & IntervalSet(openclosed(0, 1)))
     self.assertEqual(target, base & openclosed(0, 1))
     self.assertEqual(target, base & [openclosed(0, 1)])
     # Difference
     self.assertEqual(target, base - IntervalSet(openclosed(1, 2)))
     self.assertEqual(target, base - openclosed(1, 2))
     self.assertEqual(target, base - [openclosed(1, 2)])
Пример #16
0
def remove_member_of(user, group, processor, during=UnboundedInterval):
    """Remove a user from a group in a given interval.

    The interval defaults to the unbounded interval, so that the user
    will be removed from the group at any point in time, **removing
    all memberships** in this group retroactively.

    However, a common use case is terminating a membership by setting
    ``during=closedopen(now, None)``.

    :param User user: the user
    :param Group group: the group
    :param User processor: User issuing the removal
    :param Interval during:
    """

    if group.permission_level > processor.permission_level:
        raise PermissionError("cannot delete a membership for a group with a"
                              " higher permission level")

    memberships: list[Membership] = [
        m for m in user.active_memberships(when=during) if m.group == group
    ]
    intervals = IntervalSet(m.active_during.closure
                            for m in memberships).difference(during)
    for m in memberships:
        session.session.delete(m)
    # flush necessary because we otherwise don't have any control
    # over the order of deletion vs. addition
    session.session.flush()
    session.session.add_all(
        Membership(active_during=i, user=user, group=group) for i in intervals)

    message = deferred_gettext("Removed from group {group} during {during}.")
    log_user_event(message=message.format(group=group.name,
                                          during=during).to_json(),
                   user=user,
                   author=processor)
Пример #17
0
def _to_date_intervals(intervals):
    """
    :param IntervalSet[datetime] intervals:
    :rtype: IntervalSet[date]
    """
    return IntervalSet(_to_date_interval(i) for i in intervals)
Пример #18
0
def test_constructor(one: list[Interval], other: list[Interval]):
    assert IntervalSet(one) == IntervalSet(other)
Пример #19
0
def test_complement(intervals: list[Interval], expected: IntervalSet):
    assert IntervalSet(intervals).complement() == IntervalSet(expected)
Пример #20
0
def test_union(one: list[Interval], other: list[Interval],
               expected: IntervalSet):
    assert IntervalSet(one).union(IntervalSet(other)) == IntervalSet(expected)
Пример #21
0
def test_intersect(one: list[Interval], other: list[Interval],
                   expected: IntervalSet):
    assert IntervalSet(one).intersect(
        IntervalSet(other)) == IntervalSet(expected)
Пример #22
0
def test_difference(one: list[Interval], other: list[Interval],
                    expected: IntervalSet):
    assert IntervalSet(one).difference(
        IntervalSet(other)) == IntervalSet(expected)
Пример #23
0
def test_length(intervals: list[Interval], expected: IntervalSet):
    assert IntervalSet(intervals).length == expected