Beispiel #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)
Beispiel #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))
Beispiel #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)
Beispiel #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)
Beispiel #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)
Beispiel #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)
Beispiel #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))
Beispiel #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)]),
     )
Beispiel #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))
Beispiel #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)))
Beispiel #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))))
Beispiel #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())
Beispiel #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)
Beispiel #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)]
Beispiel #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)])
Beispiel #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)
Beispiel #17
0
def _to_date_intervals(intervals):
    """
    :param IntervalSet[datetime] intervals:
    :rtype: IntervalSet[date]
    """
    return IntervalSet(_to_date_interval(i) for i in intervals)
Beispiel #18
0
def test_constructor(one: list[Interval], other: list[Interval]):
    assert IntervalSet(one) == IntervalSet(other)
Beispiel #19
0
def test_complement(intervals: list[Interval], expected: IntervalSet):
    assert IntervalSet(intervals).complement() == IntervalSet(expected)
Beispiel #20
0
def test_union(one: list[Interval], other: list[Interval],
               expected: IntervalSet):
    assert IntervalSet(one).union(IntervalSet(other)) == IntervalSet(expected)
Beispiel #21
0
def test_intersect(one: list[Interval], other: list[Interval],
                   expected: IntervalSet):
    assert IntervalSet(one).intersect(
        IntervalSet(other)) == IntervalSet(expected)
Beispiel #22
0
def test_difference(one: list[Interval], other: list[Interval],
                    expected: IntervalSet):
    assert IntervalSet(one).difference(
        IntervalSet(other)) == IntervalSet(expected)
Beispiel #23
0
def test_length(intervals: list[Interval], expected: IntervalSet):
    assert IntervalSet(intervals).length == expected