def add_membership(user_id): user = get_user_or_404(user_id) form = UserAddGroupMembership() if form.validate_on_submit(): if form.begins_at.data is not None: begins_at = datetime.combine(form.begins_at.data, utc.time_min()) else: begins_at = session.utcnow() if not form.ends_at.unlimited.data: ends_at = datetime.combine(form.ends_at.date.data, utc.time_min()) else: ends_at = None make_member_of(user, form.group.data, current_user, closed(begins_at, ends_at)) message = u"Nutzer zur Gruppe '{}' hinzugefügt.".format(form.group.data.name) lib.logging.log_user_event(message, current_user, user) session.session.commit() flash(u'Nutzer wurde der Gruppe hinzugefügt.', 'success') return redirect(url_for(".user_show", user_id=user_id, _anchor='groups')) return render_template('user/add_membership.html', page_title=u"Neue Gruppenmitgliedschaft für Nutzer {}".format(user_id), user_id=user_id, form=form)
def edit_membership(user_id, membership_id): membership = Membership.q.get(membership_id) if membership is None: flash( u"Gruppenmitgliedschaft mit ID {} existiert nicht!".format( membership_id), 'error') abort(404) if membership.group.permission_level > current_user.permission_level: flash( "Eine Bearbeitung von Gruppenmitgliedschaften für Gruppen mit " "höherem Berechtigungslevel ist nicht möglich.", 'error') abort(403) membership_data = {} if request.method == 'GET': membership_data = { "begins_at": None if membership.begins_at is None else membership.begins_at.date(), "ends_at": { "unlimited": membership.ends_at is None, "date": membership.ends_at and membership.ends_at.date() } } form = UserEditGroupMembership(**membership_data) if form.validate_on_submit(): membership.begins_at = datetime.combine(form.begins_at.data, utc.time_min()) if form.ends_at.unlimited.data: membership.ends_at = None else: membership.ends_at = datetime.combine(form.ends_at.date.data, utc.time_min()) message = ( u"Edited the membership of group '{group}'. During: {during}". format(group=membership.group.name, during=closed(membership.begins_at, membership.ends_at))) lib.logging.log_user_event(message, current_user, membership.user) session.session.commit() flash(u'Gruppenmitgliedschaft bearbeitet', 'success') return redirect( url_for('.user_show', user_id=membership.user_id, _anchor='groups')) return render_template('user/user_edit_membership.html', page_title=(u"Mitgliedschaft {} für " u"{} bearbeiten".format( membership.group.name, membership.user.name)), membership_id=membership_id, user=membership.user, form=form)
def edit_membership(user_id, membership_id): membership = Membership.q.get(membership_id) if membership is None: flash(u"Gruppenmitgliedschaft mit ID {} existiert nicht!".format( membership_id), 'error') abort(404) membership_data = {} if request.method == 'GET': membership_data = { "begins_at": None if membership.begins_at is None else membership.begins_at.date(), "ends_at": {"unlimited": membership.ends_at is None, "date": membership.ends_at and membership.ends_at.date()} } form = UserEditGroupMembership(**membership_data) if form.validate_on_submit(): membership.begins_at = datetime.combine(form.begins_at.data, utc.time_min()) if form.ends_at.unlimited.data: membership.ends_at = None else: membership.ends_at = datetime.combine(form.ends_at.date.data, utc.time_min()) message = (u"hat die Mitgliedschaft des Nutzers in der Gruppe '{}' " u"bearbeitet.".format(membership.group.name)) lib.logging.log_user_event(message, current_user, membership.user) session.session.commit() flash(u'Gruppenmitgliedschaft bearbeitet', 'success') return redirect(url_for('.user_show', user_id=membership.user_id, _anchor='groups')) return render_template('user/user_edit_membership.html', page_title=(u"Mitgliedschaft {} für " u"{} bearbeiten".format( membership.group.name, membership.user.name)), membership_id=membership_id, user=membership.user, form=form)
def move_out(user_id): form = UserMoveOutForm() user = get_user_or_404(user_id) if not user.room: flash("Nutzer {} ist aktuell nirgends eingezogen!".format(user_id), 'error') abort(404) if form.validate_on_submit(): when = session.utcnow() if form.now.data else datetime.combine( form.when.data, utc.time_min()) _, success = web_execute( lib.user.move_out, None, user=user, comment=form.comment.data, processor=current_user, when=session.utcnow() if form.now.data else datetime.combine( form.when.data, utc.time_min()), end_membership=form.end_membership.data) if success: session.session.commit() if when > session.utcnow(): flash("Der Auszug wurde vorgemerkt.", "success") else: flash(u'Benutzer ausgezogen.', 'success') return redirect(url_for('.user_show', user_id=user.id)) if not form.is_submitted(): form.end_membership.data = True return render_template('user/user_move_out.html', form=form, user_id=user_id)
def date_format(dt, default=None): """ Format date or datetime objects for `table.dateFormatter`. :param datetime|date|None dt: a date or datetime object or None :param str|None default: formatted value to use if `dt` is None :return: """ if dt is not None: return { 'formatted': date_filter(dt), 'timestamp': int(datetime.combine(dt, utc.time_min()).timestamp()), } else: return { 'formatted': default if default is not None else date_filter(None), 'timestamp': None, }
def move(user_id): user = get_user_or_404(user_id) form = UserMoveForm() refill_form_data = False if form.validate_on_submit(): if user.room == Room.q.filter_by( number=form.room_number.data, level=form.level.data, building_id=form.building.data.id).one(): flash(u"Nutzer muss in anderes Zimmer umgezogen werden!", "error") refill_form_data = True else: when = session.utcnow() if form.now.data else datetime.combine( form.when.data, utc.time_min()) _, success = web_execute(lib.user.move, None, user=user, building_id=form.building.data.id, level=form.level.data, room_number=form.room_number.data, processor=current_user, when=when) if success: session.session.commit() if when > session.utcnow(): flash(u'Der Umzug wurde vorgemerkt.', 'success') else: flash(u'Benutzer umgezogen', 'success') sheet = lib.user.store_user_sheet( user, '********', generation_purpose='user moved') session.session.commit() flask_session['user_sheet'] = sheet.id return redirect(url_for('.user_show', user_id=user.id)) if not form.is_submitted() or refill_form_data: if user.room is not None: refill_room_data(form, user.room) return render_template('user/user_move.html', user_id=user_id, form=form)
def finish_member_request(prm: PreMember, processor: Optional[User], ignore_similar_name: bool = False): if prm.room is None: raise ValueError("Room is None") if prm.move_in_date is not None and prm.move_in_date < session.utcnow( ).date(): prm.move_in_date = session.utcnow().date() check_new_user_data(prm.login, prm.email, prm.name, prm.swdd_person_id, prm.room, prm.move_in_date, ignore_similar_name) user, _ = create_user(prm.name, prm.login, prm.email, prm.birthdate, groups=[], processor=processor, address=prm.room.address, passwd_hash=prm.passwd_hash) processor = processor if processor is not None else user user.swdd_person_id = prm.swdd_person_id user.email_confirmed = prm.email_confirmed move_in_datetime = datetime.combine(prm.move_in_date, utc.time_min()) move_in(user, prm.room.building_id, prm.room.level, prm.room.number, None, processor if processor is not None else user, when=move_in_datetime) message = deferred_gettext("Created from registration {}.").format( str(prm.id)).to_json() log_user_event(message, processor, user) session.session.delete(prm) return user
def move_in(user_id): form = UserMoveInForm() user = get_user_or_404(user_id) if user.room is not None: flash("Nutzer {} ist nicht ausgezogen!".format(user_id), 'error') abort(404) if form.validate_on_submit(): when = session.utcnow() if form.now.data else datetime.combine( form.when.data, utc.time_min()) _, success = web_execute( lib.user.move_in, None, user=user, building_id=form.building.data.id, level=form.level.data, room_number=form.room_number.data, mac=form.mac.data, birthdate=form.birthdate.data, begin_membership=form.begin_membership.data, processor=current_user, when=when, ) if success: session.session.commit() if when > session.utcnow(): flash("Der Einzug wurde vorgemerkt.", 'success') else: flash("Benutzer eingezogen.", 'success') return redirect(url_for('.user_show', user_id=user_id)) if not form.is_submitted(): form.birthdate.data = user.birthdate form.begin_membership.data = True return render_template('user/user_move_in.html', form=form, user_id=user_id)
def suspend(user_id): form = UserSuspendForm() myUser = get_user_or_404(user_id) if form.validate_on_submit(): if form.ends_at.unlimited.data: ends_at = None else: ends_at = datetime.combine(form.ends_at.date.data, utc.time_min()) try: during = closedopen(session.utcnow(), ends_at) blocked_user = lib.user.suspend( user=myUser, reason=form.reason.data, processor=current_user, during=during) session.session.commit() except ValueError as e: flash(str(e), 'error') else: flash(u'Nutzer gesperrt', 'success') return redirect(url_for('.user_show', user_id=user_id)) return render_template('user/user_block.html', form=form, user_id=user_id)
def post(self, user_id): """ Terminate the membership on the given date :param user_id: The ID of the user :return: """ parser = reqparse.RequestParser() parser.add_argument( 'end_date', dest='end_date', required=True, type=lambda x: datetime.strptime(x, '%Y-%m-%d').date()) parser.add_argument('comment', dest='comment', required=False) args = parser.parse_args() user = get_user_or_404(user_id) if membership_ending_task(user) is not None: abort(400, message="The termination of the membership has already" " been scheduled.") if not user.has_property('member'): abort(400, message="User is not a member.") move_out(user=user, comment=args.comment if args.comment is not None else "Move-out over API", processor=user, when=datetime.combine(args.end_date, utc.time_min()), end_membership=True) session.session.commit() return "Membership termination scheduled."
def post_transactions_for_membership_fee(membership_fee, processor, simulate=False): """ Posts transactions (and splits) for users where the specified membership fee was not posted yet. User select: User -> Split (user account) -> Transaction -> Split (fee account) Conditions: User has `membership_fee` property on begins_on + booking_begin - 1 day or begins_on + booking_end - 1 day and no transaction exists on the user account int the fee timespan :param membership_fee: The membership fee which should be posted :param processor: :param simulate: Do not post any transactions, just return the affected users. :return: A list of name of all affected users """ description = membership_fee_description.format(fee_name=membership_fee.name).to_json() split_user_account = Split.__table__.alias() split_fee_account = Split.__table__.alias() rhe_end = RoomHistoryEntry.__table__.alias() rhe_begin = RoomHistoryEntry.__table__.alias() fee_accounts = Account.q.join(Building).distinct(Account.id).all() fee_accounts_ids = set([acc.id for acc in fee_accounts] + [config.membership_fee_account_id]) properties_beginning_timestamp = datetime.combine((membership_fee.begins_on + membership_fee.booking_begin - timedelta(1)), time_min()) properties_end_timestamp = datetime.combine((membership_fee.begins_on + membership_fee.booking_end - timedelta(1)), time_max()) begin_tstz = datetime.combine(membership_fee.begins_on, time_min()) end_tstz = datetime.combine(membership_fee.ends_on, time_max()) # Select all users who fulfill the requirements for the fee in the fee timespan users = (select([User.id.label('id'), User.name.label('name'), User.account_id.label('account_id'), # Select fee_account_id of the building or the default # fee_account_id if user was not living in a room at booking time func.coalesce(Building.fee_account_id, literal(config.membership_fee_account_id)).label('fee_account_id')]) .select_from(User.__table__ # Join the users properties at `booking_begin` .outerjoin(func.evaluate_properties(properties_beginning_timestamp) .alias('properties_beginning'), literal_column('properties_beginning.user_id') == User.id) # Join the users properties at `booking_end` .outerjoin(func.evaluate_properties(properties_end_timestamp) .alias('properties_end'), literal_column('properties_end.user_id') == User.id) # Join RoomHistoryEntry, Room and Building of the user at membership_fee.ends_on .outerjoin(rhe_end, and_(rhe_end.c.user_id == User.id, # Only join RoomHistoryEntry that is relevant # on the fee interval end date literal(end_tstz).op("<@")( func.tstzrange(rhe_end.c.begins_at, func.coalesce(rhe_end.c.ends_at, literal('infinity').cast(DateTime) ) , '[)') ))) # Join RoomHistoryEntry, Room and Building of the user at membership_fee.begins_on # As second option if user moved out within the month .outerjoin(rhe_begin, and_(rhe_begin.c.user_id == User.id, # Only join RoomHistoryEntry that is relevant # on the fee interval end date literal(begin_tstz).op("<@")( func.tstzrange(rhe_begin.c.begins_at, func.coalesce(rhe_begin.c.ends_at, literal('infinity').cast(DateTime) ) , '[)') ))) # Join with Room from membership_fee.ends_on if available, # if not, join with the Room from membership_fee.begins_on .outerjoin(Room, Room.id == func.coalesce(rhe_end.c.room_id, rhe_begin.c.room_id)) .outerjoin(Building, Building.id == Room.building_id) ) # Check if a booking already exists on the user account in the fee timespan .where(not_(exists(select([None]).select_from(split_user_account .join(Transaction, Transaction.id == split_user_account.c.transaction_id) .join(split_fee_account, split_fee_account.c.transaction_id == Transaction.id) ) .where(and_(split_user_account.c.account_id == User.account_id, Transaction.valid_on.between(literal(membership_fee.begins_on), literal(membership_fee.ends_on)), split_fee_account.c.account_id.in_(fee_accounts_ids), split_fee_account.c.amount < 0, split_fee_account.c.id != split_user_account.c.id)) ))) # Only those users who had the `membership_fee` property on `booking_begin` or # `booking_end` .where(or_(and_(literal_column('properties_beginning.property_name') == 'membership_fee', not_(literal_column('properties_beginning.denied'))), and_(literal_column('properties_end.property_name') == 'membership_fee', not_(literal_column('properties_end.denied'))))) .distinct() .cte('membership_fee_users')) affected_users_raw = session.session.execute(select([users.c.id, users.c.name, users.c.fee_account_id])).fetchall() if not simulate: numbered_users = (select([users.c.id, users.c.fee_account_id.label('fee_account_id'), users.c.account_id, func.row_number().over().label('index')]) .select_from(users) .cte("membership_fee_numbered_users")) transactions = (Transaction.__table__.insert() .from_select([Transaction.description, Transaction.author_id, Transaction.posted_at, Transaction.valid_on, Transaction.confirmed], select([literal(description), literal(processor.id), func.current_timestamp(), literal(membership_fee.ends_on), True]).select_from(users)) .returning(Transaction.id) .cte('membership_fee_transactions')) numbered_transactions = (select([transactions.c.id, func.row_number().over().label('index')]) .select_from(transactions) .cte('membership_fee_numbered_transactions')) split_insert_fee_account = (Split.__table__.insert() .from_select([Split.amount, Split.account_id, Split.transaction_id], select([literal(-membership_fee.regular_fee, type_=Money), numbered_users.c.fee_account_id, numbered_transactions.c.id]) .select_from(numbered_users.join(numbered_transactions, numbered_transactions.c.index == numbered_users.c.index)) ) .returning(Split.id) .cte('membership_fee_split_fee_account')) split_insert_user = (Split.__table__.insert().from_select( [Split.amount, Split.account_id, Split.transaction_id], select([literal(membership_fee.regular_fee, type_=Money), numbered_users.c.account_id, numbered_transactions.c.id]) .select_from(numbered_users.join(numbered_transactions, numbered_transactions.c.index == numbered_users.c.index))) .returning(Split.id) .cte('membership_fee_split_user')) session.session.execute(select([]).select_from(split_insert_fee_account .join(split_insert_user, split_insert_user.c.id == split_insert_fee_account.c.id))) affected_users = [dict(user) for user in affected_users_raw] return affected_users
def merge_member_request(user: User, prm: PreMember, merge_name: bool, merge_email: bool, merge_person_id: bool, merge_room: bool, merge_password: bool, merge_birthdate: bool, processor: User): if prm.move_in_date is not None and prm.move_in_date < session.utcnow( ).date(): prm.move_in_date = session.utcnow().date() if merge_name: user = edit_name(user, prm.name, processor) if merge_email: user = edit_email(user, prm.email, user.email_forwarded, processor, is_confirmed=prm.email_confirmed) if merge_person_id: user = edit_person_id(user, prm.swdd_person_id, processor) move_in_datetime = datetime.combine(prm.move_in_date, utc.time_min()) if merge_room: if prm.room: if user.room: move(user, prm.room.building_id, prm.room.level, prm.room.number, processor=processor, when=move_in_datetime) else: move_in(user, prm.room.building_id, prm.room.level, prm.room.number, mac=None, processor=processor, when=move_in_datetime) if not user.member_of(config.member_group): make_member_of(user, config.member_group, processor, closed(move_in_datetime, None)) if merge_birthdate: user = edit_birthdate(user, prm.birthdate, processor) log_msg = "Merged information from registration {}." if merge_password: user.passwd_hash = prm.passwd_hash log_msg += " Password overridden." else: log_msg += " Kept old password." log_user_event( deferred_gettext(log_msg).format(encode_type2_user_id( prm.id)).to_json(), processor, user) session.session.delete(prm)