Ejemplo n.º 1
0
def ignored_assignment(laure_data: object, session: object) -> (bool, dict):
    """Perform necessary transition after photo set validation.

    :param laure_data: Python object representing Laure data after assignment validation
    :return: Flag indicating whether operation was successful, empty dict
    """
    with transaction.manager:
        assignment_id = laure_data.assignment.id
        assignment = Assignment.get(assignment_id)

        if not assignment:
            logger.error('Got message for unknown assignment id {0}'.format(
                assignment_id))
            return False, {}

        if assignment.state != 'asset_validation':
            logger.error(
                "Got message to transition assignment '{0}' which is in state '{1}'"
                .format(assignment_id, assignment.state))
            return False, {}

        logger.info(
            "Assignment '{0}' assets validation ignored. Transitioning to 'in_qa'"
            .format(assignment_id))
        assignment.workflow.context = SystemUser
        assignment.workflow.validate_assets(message='Ignored validation.')
        logger.info('Assignment {0} state set to {1}'.format(
            assignment.slug, assignment.state))

        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)

    return True, {}
Ejemplo n.º 2
0
def _notify_late_submissions(assignment: Assignment) -> bool:
    """Create a new comment to let professionals know that 48hs passed after the shoot.

    Task name: leica.task.notify_late_submission
    Task events:

        * leica.task.notify_late_submission.success
        * leica.task.notify_late_submission.failure

    :param assignment: Assignment to be processed
    :return: True if a new notify comment was registered in the Assignment.
    """
    status = False
    has_notify_comment = assignment.comments.filter(
        Comment.content == LATE_SUBMISSION_MSG,
        Comment.internal.is_(True),
    ).all()

    now = timezone_now('UTC')
    delta = now - assignment.scheduled_datetime
    config_delta = timedelta(seconds=int(LATE_SUBMISSION_SECONDS))
    should_notify = delta > config_delta and delta.days <= int(
        LATE_SUBMISSION_MAX_DAYS)
    if assignment.state != 'awaiting_assets' or has_notify_comment or not should_notify:
        return status

    payload = dict(
        entity_id=assignment.id,
        entity_type=assignment.__class__.__name__,
        author_id=SystemUser.id,
        content=LATE_SUBMISSION_MSG,
        author_role='project_manager',
        to_role='professional_user',
        internal=True,
    )
    try:
        comment = Comment(**payload)
        session = assignment.__session__
        session.add(comment)
        assignment.comments.append(comment)
        session.flush()
    except Exception as exc:
        msg = 'Failure to add comment to assignment: {id}. Error: {exc}'
        logger.error(msg.format(id=assignment.id, exc=str(exc)))
    else:
        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)
        status = True

    task_name = 'leica.task.notify_late_submission'
    event = LeicaTaskEvent(task_name=task_name, success=status, obj=assignment)
    event()
    return status
Ejemplo n.º 3
0
def _notify_before_shooting(assignment: Assignment) -> bool:
    """Create a new comment to let professionals about scheduled datetime before shooting.

    Task name: leica.task.notify_before_shooting
    Task events:

        * leica.task.notify_before_shooting.success
        * leica.task.notify_before_shooting.failure

    :param assignment: Assignment to be processed
    :return: True if a new notify comment was registered in the Assignment.
    """
    status = False
    now = timezone_now('UTC')
    has_notify_comment = assignment.comments.filter(
        Comment.content == BEFORE_SHOOTING_MSG,
        Comment.internal.is_(True),
    ).all()
    delta = assignment.scheduled_datetime - now
    config_delta = timedelta(seconds=int(BEFORE_SHOOTING_SECONDS))
    should_notify = delta.days >= 0 and delta <= config_delta
    if assignment.state != 'scheduled' or not should_notify or has_notify_comment:
        return status

    payload = dict(
        entity_id=assignment.id,
        entity_type=assignment.__class__.__name__,
        author_id=SystemUser.id,
        content=BEFORE_SHOOTING_MSG,
        author_role='project_manager',
        to_role='professional_user',
        internal=True,
    )
    try:
        comment = Comment(**payload)
        session = assignment.__session__
        session.add(comment)
        assignment.comments.append(comment)
        session.flush()
    except Exception as exc:
        msg = 'Failure to add comment to assignment: {id}. Error: {exc}'
        logger.error(msg.format(id=assignment.id, exc=str(exc)))
    else:
        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)
        status = True

    task_name = 'leica.task.notify_before_shooting'
    event = LeicaTaskEvent(task_name=task_name, success=status, obj=assignment)
    event()
    return status
Ejemplo n.º 4
0
def _move_order_accepted(order: Order) -> bool:
    """Move Order from delivered to accepted after a certain amount of working days.

    Task name: leica.task.order_accepted
    Task events:

        * leica.task.order_accepted.success
        * leica.task.order_accepted.failure

    :param order: Order to be processed.
    :return: Status of the transition
    """
    task_name = 'leica.task.order_accepted'
    status = False
    state = order.state
    last_deliver_date = order.last_deliver_date
    if state == 'delivered' and last_deliver_date:
        now = datetime_utcnow()
        project = order.project
        approval_window = project.approval_window
        allowed_accept_date = workday(last_deliver_date, approval_window)
        wf = order.workflow
        wf.context = SystemUser

        if now >= allowed_accept_date:
            if not wf.can_accept:
                assignment_states = [a.state for a in order.assignments]
                msg = 'Approval window for Order {id} expired but it can not be completed. ' \
                      'Assignment states: {states}'
                msg = msg.format(id=order.id, states=str(assignment_states))
            else:
                wf.accept(
                    message=
                    'Order automatic accepted after the end of the approval window.'
                )
                msg = 'Order {id} moved to completed. Delivery date: {deliver_date}'
                msg = msg.format(id=order.id, deliver_date=last_deliver_date)
                status = True

            # Trigger task event
            cache_region.invalidate(order)
            for assignment in order.assignments:
                cache_region.invalidate(assignment)
            event = LeicaTaskEvent(task_name=task_name,
                                   success=status,
                                   obj=order)
            event()
            logger.info(msg)
    return status
Ejemplo n.º 5
0
def approve_assignment(laure_data: object,
                       session: object,
                       copy_ignored: bool = False) -> (bool, dict):
    """Perform necessary updates after set was copied to destination folders.

    :param laure_data: Python object representing Laure data after assignment approval
    :param copy_ignored: Set when copying assets was ignored on ms.laure
    :return: Flag indicating if the operation was successful, empty dict
    """
    status = False
    with transaction.manager:
        assignment_id = laure_data.assignment.id
        assignment = Assignment.get(assignment_id)
        if not assignment:
            logger.warn(
                'Got assignment approval message for non existing assignment {0}'
                .format(assignment_id))
            return False, {}

        order = assignment.order
        if not copy_ignored:
            delivery_info = update_delivery(order, laure_data)
            msg = 'Assets copied over on laure - committing delivery URL to order "{order_id}"'
            status = True
        else:
            # We need to get the existing delivery to execute the proper transition
            msg = 'Assets were a result of previous manual review and were not ' \
                  'touched on ms.laure. Order "{order_id}" unchanged!'
            status = True
            delivery_info = order.delivery

        if order.state == 'in_qa' and assignment.state == 'approved':
            # force new instance object to make sure sqlalchemy will detect the change
            delivery = delivery_info.copy()
            fields = dict(delivery=delivery)
            wf = order.workflow
            wf.context = SystemUser
            wf.deliver(fields=fields, message='Assets automatic delivered.')

        cache_region.invalidate(assignment)
        cache_region.invalidate(order)

        logger.info(msg.format(order_id=assignment.order.id))

    return status, {}
Ejemplo n.º 6
0
def asset_copy_malfunction(laure_data: object,
                           session: object) -> (bool, dict):
    """Perform necessary transition and field update after photo set was deemed invalid.

    :param laure_data: Python object representing Laure data after assignment validation
    :return: Flag indicating if the operation was successful, empty dict
    """
    with transaction.manager:
        assignment_id = laure_data.assignment.id
        assignment = Assignment.get(assignment_id)
        if not assignment:
            logger.warn(
                'Got assignment message for non existing assignment {0}'.
                format(assignment_id))
            return False, {}

        text = (
            'This assignment\'s assets could not be copied automatically!\n'
            'Please take manual actions that may be needed.')

        comment = Comment(
            entity_id=assignment_id,
            entity_type=assignment.__class__.__name__,
            author_id=SystemUser.id,
            author_role='system',
            to_role='qa_manager',
            content=text,
            internal=True,
        )
        session.add(comment)

        logger.warn(
            'There was a problem copying assets to delivery or archive folders.'
            'Adding comment to assignment {0}'.format(assignment_id))

        comment.workflow_context = SystemUser
        assignment.workflow_context = SystemUser
        assignment.comments.append(comment)

        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)

    return True, {}
Ejemplo n.º 7
0
def invalidate_assignment(laure_data: object, session: object) -> (bool, dict):
    """Perform necessary transition and field update after photo set was deemed invalid.

    :param laure_data: Python object representing Laure data after assignment validation
    :return: Flag indicating whether operation was successful, empty dict
    """
    with transaction.manager:
        assignment_id = laure_data.assignment.id
        assignment = Assignment.get(assignment_id)

        if not assignment:
            logger.error(
                'Got a message for non-existing assignment id {0}'.format(
                    assignment_id))
            return False, {}

        if assignment.state != 'asset_validation':
            logger.error(
                "Got message to invalidate '{0}' which is in state '{1}'".
                format(assignment_id, assignment.state))
            return False, {}

        feedback_text = '\n'.join(
            [item.complete_feedback for item in laure_data.validation])

        logger.info(
            'Assignment "{0}" assets reported as not sufficient. Transitioning back '
            'to "waiting_assets" and adding comments to assignment.'.format(
                assignment_id))

        assignment.workflow_context = SystemUser

        assignment.workflow.invalidate_assets(message=feedback_text)
        logger.info('Assignment {0} state set to {1}'.format(
            assignment.slug, assignment.state))

        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)

        return True, {}
Ejemplo n.º 8
0
def move_assignments_awaiting_assets():
    """Move Assignments from scheduled to awaiting_assets."""
    query = Assignment.query().filter(Assignment.state == 'scheduled').all()

    assignments = []
    for item in query:
        now = timezone_now('UTC')
        if item.scheduled_datetime and item.scheduled_datetime < now:
            assignments.append(item)

    logger.info(
        'Total assignments to be moved: {size}'.format(size=len(assignments)))
    total_moved = 0

    for assignment in assignments:
        status = _move_assignment_awaiting_assets(assignment)
        total_moved += 1 if status else 0
        cache_region.invalidate(assignment)
        cache_region.invalidate(assignment.order)

    logger.info('Total assignments moved to awaiting assets: {total}'.format(
        total=total_moved))
Ejemplo n.º 9
0
def _move_assignment_awaiting_assets(assignment: Assignment) -> bool:
    """Move Assignments from scheduled to awaiting_assets.

    Task name: leica.task.assignment_awaiting_assets
    Task events:

        * leica.task.assignment_awaiting_assets.success
        * leica.task.assignment_awaiting_assets.failure

    :param assignment: Assignment to be processed
    :return: Status of the transition
    """
    task_name = 'leica.task.assignment_awaiting_assets'
    now = timezone_now('UTC')
    status = False
    if assignment.state == 'scheduled' and assignment.scheduled_datetime < now:
        wf = assignment.workflow
        wf.context = SystemUser
        try:
            wf.ready_for_upload()
        except WorkflowTransitionException as e:
            logger.exception(
                'Assignment {id} not moved to awaiting_assets.'.format(
                    id=assignment.id))
        else:
            status = True
            logger.info(
                'Assignment {id} moved to awaiting_assets. Shoot time: {shoot_time}'
                .format(id=assignment.id,
                        shoot_time=assignment.scheduled_datetime))

        cache_region.invalidate(assignment)

        event = LeicaTaskEvent(task_name=task_name,
                               success=status,
                               obj=assignment)
        event()

    return status
Ejemplo n.º 10
0
def project_after_update(mapper, connection, target):
    """Invalidate Project cache after instance update."""
    project = target
    cache_region.invalidate(project)