예제 #1
0
파일: __init__.py 프로젝트: JuKu/pycroft
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)
예제 #2
0
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)
예제 #3
0
파일: __init__.py 프로젝트: JuKu/pycroft
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)
예제 #4
0
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)
예제 #5
0
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,
        }
예제 #6
0
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)
예제 #7
0
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
예제 #8
0
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)
예제 #9
0
파일: __init__.py 프로젝트: JuKu/pycroft
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)
예제 #10
0
    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."
예제 #11
0
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
예제 #12
0
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)