Пример #1
0
async def register(request: Request) -> ProtobufResponse:
    # todo: validation

    query = select(model.User.email).where(model.User.email == request.parsed.identity.email)
    result = await request.connection.execute(query)

    if result.scalars().first():
        raise HTTPException(409)

    new_user = model.User(
        name=request.parsed.name,
        surname=request.parsed.surname,
        email=request.parsed.identity.email,
        password=sha_hash(request.parsed.identity.password),
        confirmation_id=uuid.uuid4()
    )

    request.connection.add(new_user)

    if 'X-No-Confirmation' in request.headers:
        new_user.confirmed = True
        return ProtobufResponse(empty_pb2.Empty())
    asyncio.create_task(
        MailManager.send_confirmation_email(
            destination=new_user.email,
            confirmation_id=str(new_user.confirmation_id)
        )
    )
    return ProtobufResponse(empty_pb2.Empty())
Пример #2
0
async def check_unique_user(request: Request):
    query = (
        select(model.User.email)
        .filter_by(email=request.parsed.email)
    )

    result = await request.connection.execute(query)
    user = result.scalars().first()

    if user is None:
        return ProtobufResponse(user_pb2.CheckUniqueUserResponse(is_unique=True))
    return ProtobufResponse(user_pb2.CheckUniqueUserResponse(is_unique=False))
Пример #3
0
async def left_queue(request: Request):
    current_ticket_query = (
        select(model.Ticket)
        .where(
            model.Ticket.user_id == request.user.id,
            model.Ticket.state == 'WAITING',
        )
        .options(
            selectinload(model.Ticket.service),
        )
        .with_for_update(nowait=True)
        .limit(1)
    )

    result = await request.connection.execute(current_ticket_query)
    ticket = result.scalars().first()

    if ticket is None:
        raise HTTPException(404)

    ticket.state = 'PROCESSED'
    ticket.resolution = 'GONE'
    ticket.finished_at = now()

    ticket_proto = ticket.to_protobuf()
    event_manager.publish('ticket_state_changed', ticket_proto, service_id=ticket.service_id)

    return ProtobufResponse(ticket_proto)
Пример #4
0
async def remove_user(request: Request):
    object_model = (
        model.Service
        if request.parsed.target_object == permissions_pb2.TargetObject.SERVICE
        else model.Organization
    )

    query_object = (
        select(object_model)
        .where(object_model.id == request.parsed.id)
        .options(selectinload(object_model.admins))
    )

    result = await request.connection.execute(query_object)
    permission_object = result.scalars().first()

    if permission_object is None:
        raise HTTPException(404)

    user_query = (
        select(model.User)
        .where(model.User.email == request.parsed.email)
    )

    result = await request.connection.execute(user_query)
    user = result.scalars().first()

    if user is None:
        raise HTTPException(404)

    permission_filter = (
        model.Permission.user_id == user.id,
        (
            model.Permission.organization_id == request.parsed.id
            if object_model == model.Organization
            else model.Permission.service_id == request.parsed.id
        )
    )

    permission_query = (
        select(exists(model.Permission))
        .where(*permission_filter)
    )

    result = await request.connection.execute(permission_query)
    if not result.scalar():
        raise HTTPException(404)

    delete_query = (
        delete(model.Permission)
        .where(*permission_filter)
    )

    await request.connection.execute(delete_query)

    with contextlib.suppress(Exception):
        await caches.get('redis').delete(f'user_{user.email}', timeout=0.5)

    return ProtobufResponse(empty_pb2.Empty())
Пример #5
0
async def login(request: Request) -> ProtobufResponse:
    query = (
        select(model.User.email)
        .where(
            model.User.email == request.parsed.email,
            model.User.password == sha_hash(request.parsed.password)
        )
    )

    result = await request.connection.execute(query)
    user = result.scalars().first()

    if user is None:
        return ProtobufResponse(user_pb2.AuthResponse(), status_code=401)

    request.session['user'] = user
    return ProtobufResponse(user_pb2.AuthResponse(token=user))  # todo: empty response
Пример #6
0
async def remove_service(request: Request):
    delete_query = (
        delete(model.Service)
        .where(model.Service.id == request.parsed.id)
    )

    await request.connection.execute(delete_query)
    return ProtobufResponse(empty_pb2.Empty())
Пример #7
0
async def get_current_queue_info(request: Request):
    query = (
        select(model.Ticket)
        .where(
            model.Ticket.user_id == request.user.id,
        )
        .order_by(model.Ticket.enqueue_at.desc())
        .options(
            selectinload(model.Ticket.service)
        )
        .limit(1)
    )

    result = await request.connection.execute(query)
    ticket = result.scalars().first()

    if ticket is None:
        raise HTTPException(404)

    if ticket.state == 'PROCESSED':
        return ProtobufResponse(ticket_pb2.TicketInfo(
            ticket=ticket.to_protobuf(),
        ))

    query = (
        select(func.count(model.Ticket.id))
        .where(
            model.Ticket.service_id == ticket.service_id,
            model.Ticket.state != 'PROCESSED',
            model.Ticket.enqueue_at < ticket.enqueue_at,
        )
    )

    result = await request.connection.execute(query)

    people_count = result.scalar()

    ticket_proto = ticket.to_protobuf()
    ticket_proto.user.CopyFrom(request.user)

    return ProtobufResponse(ticket_pb2.TicketInfo(
        ticket=ticket_proto,
        people_in_front_count=people_count,
        remaining_time=ticket.service.average_waiting_time * people_count,
    ))
Пример #8
0
async def notify_ticket_state_changed(request: Request):
    async with session_scope() as session:
        query = (
            select(model.Ticket)
            .join(model.User, model.User.id == model.Ticket.user_id)
            .where(
                model.User.email == request.user.email,
            )
            .options(
                selectinload(model.Ticket.service),
                selectinload(model.Ticket.user),
            )
            .order_by(model.Ticket.enqueue_at.desc())
            .limit(1)
        )

        result = await session.execute(query)
        ticket = result.scalars().first()

        query = (
            select(func.count(model.Ticket.id))
            .where(
                model.Ticket.service_id == ticket.service_id,
                model.Ticket.state != 'PROCESSED',
                model.Ticket.enqueue_at < ticket.enqueue_at,
            )
        )

        result = await session.execute(query)
        order = result.scalar()

        if ticket is None:
            return ProtobufResponse(ticket_pb2.Ticket())

        if ticket.state != ticket_pb2.Ticket.State[request.parsed.ticket.state] or order != request.parsed.people_in_front_count:
            return ProtobufResponse(ticket_pb2.TicketInfo(
                ticket=ticket.to_protobuf(with_user=True),
                people_in_front_count=order,
                remaining_time=ticket.service.average_waiting_time * order,
            ))

        service_id = ticket.service.id

    return ProtobufResponse(await event_manager.listen('ticket_state_changed', service_id=service_id))
Пример #9
0
async def get_organizations_list(request: Request):
    query_organizations = (
        select(model.Organization)
        .where(model.User.id == request.user.id)
        .join(model.Permission, model.Permission.organization_id == model.Organization.id)
        .join(model.User, model.User.id == model.Permission.user_id)
        .options(
            selectinload(model.Organization.services),
            selectinload(model.Organization.admins),
            selectinload(model.Organization.admins, model.Permission.user),
            selectinload(model.Organization.services, model.Service.admins),
            selectinload(model.Organization.services, model.Service.admins, model.Permission.user)
        )
    )

    result = await request.connection.execute(query_organizations)
    organizations = result.scalars().all()

    response: tp.List[organization_pb2.Organization] = []
    for organization in organizations:
        response.append(
            organization.to_protobuf()
        )

    query_service = (
        select(model.Service)
        .where(
            model.User.id == request.user.id,
            model.Organization.id.notin_((organization.info.id for organization in response))
        )
        .join(model.Permission, model.Permission.service_id == model.Service.id)
        .join(model.User, model.User.id == model.Permission.user_id)
        .join(model.Organization, model.Organization.id == model.Service.organization_id)
        .options(
            selectinload(model.Service.organization),
            selectinload(model.Service.organization, model.Organization.admins),
            selectinload(model.Service.organization, model.Organization.admins, model.Permission.user),
            selectinload(model.Service.admins),
            selectinload(model.Service.admins, model.Permission.user),
        )
    )

    result = await request.connection.execute(query_service)
    services = result.scalars().all()

    for service in services:
        organization = service.organization.to_protobuf(with_services=False)
        organization.services.extend((service.to_protobuf(), ))
        response.append(organization)

    return ProtobufResponse(organization_pb2.OrganizationList(organizations=response))
Пример #10
0
async def update_service(request: Request) -> empty_pb2.Empty:
    query = (
        update(model.Service)
        .where(model.Service.id == request.parsed.id)
        .values({
            model.Service.name: request.parsed.name,
            model.Service.data: dict(**request.parsed.data),
            model.Service.default_waiting_time: request.parsed.default_waiting_time or 300,
            model.Service.timetable: request.parsed.timetable.SerializeToString(),
        })
    )

    await request.connection.execute(query)
    return ProtobufResponse(empty_pb2.Empty())
Пример #11
0
async def update_organization(request: Request) -> empty_pb2.Empty:
    query = (
        update(model.Organization)
        .where(model.Organization.id == request.parsed.id)
        .values({
            model.Organization.name: request.parsed.name,
            model.Organization.address: request.parsed.address,
            model.Organization.data: dict(**request.parsed.data),
            model.Organization.timetable: request.parsed.timetable.SerializeToString(),
        })
    )

    await request.connection.execute(query)
    return ProtobufResponse(empty_pb2.Empty())
Пример #12
0
async def service_next_user(request: Request):
    current_ticket_query = (
        select(model.Ticket)
        .where(
            model.Ticket.servicing_by == request.user.id,
            model.Ticket.state == 'PROCESSING',
        )
        .limit(1)
    )

    result = await request.connection.execute(current_ticket_query)
    ticket = result.scalars().first()

    if ticket is not None:
        raise HTTPException(409)

    next_ticket_query = (
        select(model.Ticket)
        .where(
            model.Ticket.service_id.in_(request.parsed.service_ids),
            model.Ticket.state == 'WAITING',
        )
        .order_by(model.Ticket.enqueue_at.asc())
        .options(
            selectinload(model.Ticket.user),
            selectinload(model.Ticket.service),
        )
        .with_for_update(nowait=True)
        .limit(1)
    )

    result = await request.connection.execute(next_ticket_query)
    ticket = result.scalars().first()

    if ticket is None:
        raise HTTPException(404)

    ticket.servicing_by = request.user.id
    ticket.state = 'PROCESSING'
    ticket.accepted_at = now()
    ticket.window = request.parsed.window

    ticket_proto = ticket.to_protobuf(with_user=True)
    event_manager.publish('ticket_state_changed', ticket_proto, service_id=ticket.service_id)

    return ProtobufResponse(ticket_proto)
Пример #13
0
async def update_user(request: Request) -> ProtobufResponse:
    query = (
        update(model.User)
        .where(model.User.id == request.user.id)
        .values({
            model.User.email: request.parsed.email,
            model.User.name: request.parsed.name,
            model.User.surname: request.parsed.surname,
            model.User.data: dict(**request.parsed.data)
        })
    )
    await request.connection.execute(query)

    with contextlib.suppress(Exception):
        await caches.get('redis').delete(f'user_{request.parsed.email}', timeout=0.5)

    return ProtobufResponse(empty_pb2.Empty())
Пример #14
0
async def enter_queue(request: Request):
    query = (
        select(model.Ticket)
        .where(model.Service.id == request.parsed.id)
        .join(model.Service, model.Service.id == model.Ticket.service_id)
        .order_by(model.Ticket.enqueue_at.desc())
        .limit(1)
    )

    result = await request.connection.execute(query)
    last_queue_item = result.scalars().first()

    if last_queue_item is None:
        last_ticket_id = '000'
    else:
        last_ticket_id = last_queue_item.ticket_id

    query = (
        select(model.Ticket)
        .where(model.Ticket.user_id == request.user.id)
        .order_by(model.Ticket.enqueue_at.desc())
        .limit(1)
    )

    last_ticket: model.Ticket = (await request.connection.execute(query)).scalars().first()
    if last_ticket and last_ticket.state != 'PROCESSED':
        raise HTTPException(409)

    query = (
        select(model.Service.index)
        .where(model.Service.id == request.parsed.id)
    )

    service_index = (await request.connection.execute(query)).scalar()

    new_queue_item = model.Ticket(
        user_id=request.user.id,
        service_id=request.parsed.id,
        ticket_id=generate_next_ticket(last_ticket_id, service_index),
    )

    request.connection.add(new_queue_item)
    return ProtobufResponse(ticket_pb2.Ticket(ticket_id=new_queue_item.ticket_id))
Пример #15
0
async def user_tickets_history(request: Request):
    tickets_query = (
        select(model.Ticket)
        .where(
            model.Ticket.state == 'PROCESSED',
            model.Ticket.user_id == request.user.id,
        )
        .options(
            selectinload(model.Ticket.user),
            selectinload(model.Ticket.service),
        )
    )

    result = await request.connection.execute(tickets_query)
    tickets = result.scalars().all()

    return ProtobufResponse(ticket_pb2.TicketList(
        tickets=[ticket.to_protobuf(with_user=True) for ticket in tickets]
    ))
Пример #16
0
async def create_organization(request: Request):
    new_organization = model.Organization(
        name=request.parsed.name,
        address=request.parsed.address,
        data=dict(**request.parsed.data),
        timetable=request.parsed.timetable.SerializeToString(),
    )

    new_permission = model.Permission(
        user_id=request.user.id,
        permission_type='OWNER',
    )

    new_organization.admins.append(new_permission)
    request.connection.add(new_organization)

    with contextlib.suppress(Exception):
        await caches.get('redis').delete(f'user_{request.user.email}', timeout=0.5)

    return ProtobufResponse(empty_pb2.Empty())
Пример #17
0
async def get_current_ticket(request: Request):
    current_ticket_query = (
        select(model.Ticket)
        .where(
            model.Ticket.servicing_by == request.user.id,
            model.Ticket.state == 'PROCESSING',
        )
        .options(
            selectinload(model.Ticket.user),
            selectinload(model.Ticket.service),
        )
        .limit(1)
    )

    result = await request.connection.execute(current_ticket_query)
    ticket = result.scalars().first()

    if ticket is None:
        raise HTTPException(404)

    return ProtobufResponse(ticket.to_protobuf(with_user=True))
Пример #18
0
async def fetch_organization(request: Request):
    query = (
        select(model.Organization)
        .where(model.Organization.id == request.parsed.id)
        .options(
            selectinload(model.Organization.services),
            selectinload(model.Organization.admins),
            selectinload(model.Organization.admins, model.Permission.user),
            selectinload(model.Organization.admins, model.Permission.user),
            selectinload(model.Organization.services, model.Service.admins),
            selectinload(model.Organization.services, model.Service.admins, model.Permission.user)
        )
    )

    result = await request.connection.execute(query)
    organization = result.scalars().first()

    if organization is None:
        raise HTTPException(404)

    return ProtobufResponse(organization.to_protobuf())
Пример #19
0
async def queue_tickets(request: Request):
    query = (
        select(model.Ticket)
        .where(
            model.Organization.id == request.parsed.id,
            model.Ticket.state != 'PROCESSED',
        )
        .join(model.Service, model.Ticket.service_id == model.Service.id)
        .join(model.Organization, model.Organization.id == model.Service.organization_id)
        .order_by(model.Ticket.enqueue_at.desc())
        .options(
            selectinload(model.Ticket.service)
        )
    )

    result = await request.connection.execute(query)
    tickets = result.scalars().all()

    response = []
    for ticket in tickets:
        response.append(ticket.to_protobuf())

    return ProtobufResponse(ticket_pb2.TicketList(tickets=response))
Пример #20
0
async def create_service(request: Request):
    query = (
        select(model.Service)
        .where(
            model.Service.organization_id == request.parsed.organization_id
        )
        .order_by(model.Service.index.desc())
    )
    result = await request.connection.execute(query)
    service = result.scalars().first()

    next_index = 0
    if service is not None:
        next_index = service.index + 1

    new_service = model.Service(
        name=request.parsed.name,
        organization_id=request.parsed.organization_id,
        data=dict(request.parsed.data),
        index=next_index,
        default_waiting_time=request.parsed.default_waiting_time or 300,
        timetable=request.parsed.timetable.SerializeToString(),
    )

    new_permission = model.Permission(
        user_id=request.user.id,
        permission_type='OWNER',
    )

    new_service.admins.append(new_permission)
    request.connection.add(new_service)

    with contextlib.suppress(Exception):
        await caches.get('redis').delete(f'user_{request.user.email}', timeout=0.5)

    return ProtobufResponse(empty_pb2.Empty())
Пример #21
0
async def check_auth(request: Request):
    return ProtobufResponse(user_pb2.User(name=request.user.name, email=request.user.email))
Пример #22
0
async def ping(_: Request):
    return ProtobufResponse(empty_pb2.Empty())
Пример #23
0
async def get_user(request: Request):
    return ProtobufResponse(request.user)