def test_active_memberships(self): self.assertEqual(self.user.active_memberships(), []) m = self.add_membership(self.property_group) self.assertEqual(self.user.active_memberships(), [m]) when = single(session.utcnow() - timedelta(hours=1)) self.assertEqual(self.user.active_memberships(when), []) when = single(session.utcnow() + timedelta(hours=1)) self.assertEqual(self.user.active_memberships(when), [m])
def test_active_property_groups(self, session, utcnow, user, property_group, add_membership): assert user.active_property_groups() == [] add_membership(property_group) assert user.active_property_groups() == [property_group] when = single(utcnow - timedelta(hours=1)) assert user.active_property_groups(when) == [] when = single(utcnow + timedelta(hours=1)) assert user.active_property_groups(when) == [property_group]
def test_active_traffic_groups(self): self.assertEqual(self.user.active_traffic_groups(), []) self.add_membership(self.traffic_group) self.assertEqual(self.user.active_traffic_groups(), [self.traffic_group]) when = single(session.utcnow() - timedelta(hours=1)) self.assertEqual(self.user.active_traffic_groups(when), []) when = single(session.utcnow() + timedelta(hours=1)) self.assertEqual(self.user.active_traffic_groups(when), [self.traffic_group])
def test_active_traffic_groups_expression(self): query = self.create_active_traffic_groups_query() self.assertEqual(query.all(), []) self.add_membership(self.traffic_group) query = self.create_active_traffic_groups_query() self.assertEqual(query.all(), [self.traffic_group]) when = single(session.utcnow() - timedelta(hours=1)) query = self.create_active_traffic_groups_query(when) self.assertEqual(query.all(), []) when = single(session.utcnow() + timedelta(hours=1)) query = self.create_active_traffic_groups_query(when) self.assertEqual(query.all(), [self.traffic_group])
def test_active_property_groups_expression(self): query = self.create_active_property_groups_query() self.assertEqual(query.all(), []) self.add_membership(self.property_group) query = self.create_active_property_groups_query() self.assertEqual(query.all(), [self.property_group]) when = single(session.utcnow() - timedelta(hours=1)) query = self.create_active_property_groups_query(when) self.assertEqual(query.all(), []) when = single(session.utcnow() + timedelta(hours=1)) query = self.create_active_property_groups_query(when) self.assertEqual(query.all(), [self.property_group])
def estimate_balance(user, end_date): """ :param user: The member :param end_date: Date of the end of the membership :return: Estimated balance at the end_date """ now = session.utcnow().date() # Use tomorrow in case that it is the last of the month, the fee for the # current month will be added later tomorrow = now + timedelta(1) last_fee = MembershipFee.q.order_by(MembershipFee.ends_on.desc()).first() if last_fee is None: raise ValueError("no fee information available") # Bring end_date to previous month if the end_date is in grace period end_date_justified = end_date - timedelta(last_fee.booking_begin.days - 1) months_to_pay = diff_month(end_date_justified, tomorrow) # If the user has to pay a fee for the current month if user.has_property('membership_fee', single(tomorrow.replace(day=last_fee.booking_end.days))): months_to_pay += 1 # If there was no fee booked yet for the last month and the user has to pay # a fee for the last month, increment months_to_pay last_month_last = tomorrow.replace(day=1) - timedelta(1) last_month_fee_outstanding = ( fee_from_valid_date(last_month_last, user.account) is None ) if last_month_fee_outstanding: had_to_pay_last_month = user.has_property( 'membership_fee', single(last_month_last.replace(day=last_fee.booking_end.days)) ) if had_to_pay_last_month: months_to_pay += 1 # If there is already a fee booked for this month, decrement months_to_pay this_month_fee_outstanding = ( fee_from_valid_date(last_day_of_month(tomorrow), user.account) is None ) if not this_month_fee_outstanding: months_to_pay -= 1 return (-user.account.balance) - (months_to_pay * last_fee.regular_fee)
def test_active_memberships(self, session, utcnow, user, property_group, add_membership): assert user.active_memberships() == [] assert user.current_memberships == [] m = add_membership(property_group) session.refresh(user) assert user.active_memberships() == [m] assert user.current_memberships == [m] when = single(utcnow - timedelta(hours=1)) assert user.active_memberships(when) == [] when = single(utcnow + timedelta(hours=1)) assert user.active_memberships(when) == [m]
def active_memberships(self, when=None): if when is None: now = session.utcnow() when = single(now) return [ m for m in self.memberships if when.overlaps(closed(m.begins_at, m.ends_at)) ]
def test_move_out_keeps_address(self): assert not self.user.has_custom_address old_address = self.user.address self.move_out(self.user) assert self.user.active_memberships(when=single(datetime.now(timezone.utc))) == [] assert self.user.room is None assert self.user.address == old_address
def test_active_memberships_expression( self, session, utcnow, property_group, add_membership, create_active_memberships_query, ): query = create_active_memberships_query() assert query.all() == [] m = add_membership(property_group) query = create_active_memberships_query() assert query.all() == [m] when = single(utcnow - timedelta(hours=1)) query = create_active_memberships_query(when) assert query.all() == [] when = single(utcnow + timedelta(hours=1)) query = create_active_memberships_query(when) assert query.all() == [m]
def active_memberships(self, when: Optional[Interval] = None ) -> List[Membership]: if when is None: now = session.utcnow() when = single(now) return [ m for m in self.memberships if when.overlaps(closed(m.begins_at, m.ends_at)) ]
def active_property_groups(self, when: Interval | None = None) -> list[PropertyGroup]: sess = object_session(self) when = when or single(session.utcnow()) return sess.query( PropertyGroup ).join( Membership ).filter( Membership.active_during & when, Membership.user_id == self.id ).all()
def test_move_out_keeps_address(self): self.assertFalse(self.user.has_custom_address) old_address = self.user.address self.move_out(self.user) self.assertEqual( self.user.active_memberships(when=single(datetime.now(timezone.utc))), [] ) self.assertIsNone(self.user.room) self.assertEqual(self.user.address, old_address)
def status_query(): now = single(session.utcnow()) return session.session.query( User, User.member_of(config.member_group, now).label('member'), (Account.balance <= 0).label('account_balanced'), # a User.properties hybrid attribute would be preferrable (User.has_property('network_access', now)).label('network_access'), (User.has_property('violation', now)).label('violation'), (User.has_property('ldap', now)).label('ldap'), or_(*(User.has_property(prop, now) for prop in admin_properties)).label('admin') ).join(Account)
def active(self, when=None): """ Tests if the membership overlaps with a given interval. If no interval is given, it tests if the membership is active right now. :param Interval when: interval in which the membership :rtype: bool """ if when is None: now = object_session(self).query(func.current_timestamp()).scalar() when = single(now) return when.overlaps(closed(self.begins_at, self.ends_at))
def has_property(self, property_name, when=None): """ :param str property_name: name of a property :param Interval when: """ if when is None: now = session.utcnow() when = single(now) prop_granted_flags = [ group.property_grants[property_name] for group in self.active_property_groups(when) if property_name in group.property_grants ] return all(prop_granted_flags) and any(prop_granted_flags)
def test_empty(self): self.assertCallTrue(operator.attrgetter("empty"), [ [empty(0)], [closedopen(0, 0)], [openclosed(0, 0)], [open(0, 0)], ]) self.assertCallFalse(operator.attrgetter("empty"), [ [single(0)], [closed(0, 0)], [closed(0, 1)], [closedopen(0, 1)], [openclosed(0, 1)], [open(0, 1)], ])
def has_property(self, property_name: str, when: Optional[Interval] = None) -> bool: if when is None: now = session.utcnow() when = single(now) prop_granted_flags = [ group.property_grants[property_name] for group in self.active_property_groups(when) if property_name in group.property_grants ] # In case of prop_granted_flags = []: Return False # Else: Return True if all elements of prop_granted_flags are True return all(prop_granted_flags) and any(prop_granted_flags)
def active(cls, when=None): """ Tests if memberships overlap with a given interval. If no interval is given, it tests if the memberships are active right now. :param Interval when: :return: """ if when is None: now = session.utcnow() when = single(now) return and_( or_(cls.begins_at == null(), literal(when.end) == null(), cls.begins_at <= literal(when.end)), or_(literal(when.begin) == null(), cls.ends_at == null(), literal(when.begin) <= cls.ends_at) ).label("active")
def has_property(self, property_name: str, when: datetime | None = None) -> bool: if when is None: return property_name in self.current_properties_set if isinstance(when, Interval): raise PycroftModelException("`has_property` does not accept intervals!") # usages in this branch: only wrt `membership_fee` in `estimate_balance` for finance stuff prop_granted_flags = [ group.property_grants[property_name] for group in self.active_property_groups(single(when)) if property_name in group.property_grants ] # In case of prop_granted_flags = []: Return False # Else: Return True if all elements of prop_granted_flags are True return all(prop_granted_flags) and any(prop_granted_flags)
def has_property(self, property_name, when=None): """ :param str property_name: name of a property :param Interval when: """ if when is None: now = session.utcnow() when = single(now) prop_granted_flags = [ group.property_grants[property_name] for group in self.active_property_groups(when) if property_name in group.property_grants ] # In case of prop_granted_flags = []: Return False # Else: Return True if all elements of prop_granted_flags are True return all(prop_granted_flags) and any(prop_granted_flags)
def test_0030_transferred_value(self): amount = Decimal(90) today = session.utcnow().date() simple_transaction("transaction", self.fee_account, self.user_account, amount, self.author, today - timedelta(1)) simple_transaction("transaction", self.fee_account, self.user_account, amount, self.author, today) simple_transaction("transaction", self.fee_account, self.user_account, amount, self.author, today + timedelta(1)) assert (transferred_amount(self.fee_account, self.user_account, single(today)) == amount) assert (transferred_amount(self.fee_account, self.user_account, closedopen(today, None)) == 2 * amount) assert (transferred_amount(self.fee_account, self.user_account, openclosed(None, today)) == 2 * amount) assert (transferred_amount(self.fee_account, self.user_account) == 3 * amount) Transaction.q.delete() session.session.commit()
def test_0030_transferred_value(self): amount = Decimal(90) today = session.utcnow().date() simple_transaction( u"transaction", self.fee_account, self.user_account, amount, self.author, today - timedelta(1) ) simple_transaction( u"transaction", self.fee_account, self.user_account, amount, self.author, today ) simple_transaction( u"transaction", self.fee_account, self.user_account, amount, self.author, today + timedelta(1) ) self.assertEqual( transferred_amount( self.fee_account, self.user_account, single(today) ), amount ) self.assertEqual( transferred_amount( self.fee_account, self.user_account, closedopen(today, None) ), 2*amount ) self.assertEqual( transferred_amount( self.fee_account, self.user_account, openclosed(None, today) ), 2*amount ) self.assertEqual( transferred_amount( self.fee_account, self.user_account ), 3*amount ) Transaction.q.delete() session.session.commit()
def active_memberships(self, when=None): if when is None: now = session.utcnow() when = single(now) return [m for m in self.memberships if when.overlaps(closed(m.begins_at, m.ends_at))]
(open(0, 1), 1), (closed(0, 1), 1), (closed(0, None), None), (closed(None, 0), None), (closed(None, None), None), ]) def test_length(interval: Interval, expected): assert interval.length == expected @pytest.mark.parametrize('interval, should_be_empty', [ (empty(0), True), (closedopen(0, 0), True), (openclosed(0, 0), True), (open(0, 0), True), (single(0), False), (closed(0, 0), False), (closed(0, 1), False), (closedopen(0, 1), False), (openclosed(0, 1), False), (open(0, 1), False), ]) def test_empty(interval, should_be_empty): assert interval.empty == should_be_empty @pytest.mark.parametrize('one, other, expected', [ (closed(0, 0), closed(0, 0), closed(0, 0)), (closed(0, 0), open(0, 0), None), (closed(1, 1), closed(0, 2), closed(1, 1)), (closed(0, 2), closed(1, 3), closed(1, 2)),
open(NOW, NOW + timedelta(days=1)), ]) def test_literal_select(session, interval): stmt = select(cast(literal(interval, TsTzRange), TsTzRange)) assert session.scalar(stmt) == interval @pytest.fixture def table_with_interval(session): interval = closedopen(NOW, NOW + timedelta(days=1)) with session.begin_nested(): session.add(TableWithInterval(value=interval)) @pytest.mark.parametrize('value, expected', [ (literal(NOW, DateTimeTz), True), (NOW, True), (func.current_timestamp(), True), (NOW + timedelta(hours=6), True), (single(NOW), True), (closedopen(NOW, NOW + timedelta(days=1)), True), (open(NOW, NOW + timedelta(days=1)), True), (NOW - timedelta(days=1), False), (NOW - timedelta(seconds=1), False), (literal(NOW - timedelta(seconds=1), DateTimeTz), False), (closed(NOW, NOW + timedelta(days=1)), False), ]) def test_containmment(session, table_with_interval, value, expected): stmt = select(TableWithInterval.value.contains(value)) assert session.execute(stmt).scalar() is expected
def active_memberships(self, when: Interval | None = None) -> list[Membership]: if when is None: now = session.utcnow() when = single(now) return [m for m in self.memberships if when.overlaps(m.active_during.closure)]