<commit_before>"""Layout for columns."""

from math import floor, inf

from .absolute import absolute_layout
from .percent import resolve_percentages

def columns_layout(context, box, bottom_space, skip_stack, containing_block,
                   page_is_empty, absolute_boxes, fixed_boxes,
                   adjoining_margins):
    """Lay out a multi-column ``box``."""
    from .block import (
        block_box_layout, block_level_layout, block_level_width,
        collapse_margin, remove_placeholders)

    # Implementation of the multi-column pseudo-algorithm:
    # https://www.w3.org/TR/css-multicol-1/#pseudo-algorithm
    width = None
    style = box.style
    original_bottom_space = bottom_space
    context.in_column = True

    if box.style['position'] == 'relative':
        # New containing block, use a new absolute list
        absolute_boxes = []

    box = box.copy_with_children(box.children)
    box.position_y += collapse_margin(adjoining_margins) - box.margin_top

    height = box.style['height']
    if height != 'auto' and height.unit != '%':
        assert height.unit == 'px'
        known_height = True
        bottom_space = max(
            bottom_space,
            context.page_bottom - box.content_box_y() - height.value)
    else:
        known_height = False

    # TODO: the available width can be unknown if the containing block needs
    # the size of this block to know its own size.
    block_level_width(box, containing_block)
    available_width = box.width
    if style['column_width'] == 'auto' and style['column_count'] != 'auto':
        count = style['column_count']
        width = max(
            0, available_width - (count - 1) * style['column_gap']) / count
    elif (style['column_width'] != 'auto' and
            style['column_count'] == 'auto'):
        count = max(1, int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])
    else:
        count = min(style['column_count'], int(floor(
            (available_width + style['column_gap']) /
            (style['column_width'] + style['column_gap']))))
        width = (
            (available_width + style['column_gap']) / count -
            style['column_gap'])

    def create_column_box(children):
        column_box = box.anonymous_from(box, children=children)
        resolve_percentages(column_box, containing_block)
        column_box.is_column = True
        column_box.width = width
        column_box.position_x = box.content_box_x()
        column_box.position_y = box.content_box_y()
        return column_box

    # Handle column-span property.
    # We want to get the following structure:
    # columns_and_blocks = [
    #     [column_child_1, column_child_2],
    #     spanning_block,
    #     …
    # ]
    columns_and_blocks = []
    column_children = []

    if skip_stack:
        skip, = skip_stack.keys()
    else:
        skip = 0

    for index, child in enumerate(box.children[skip:], start=skip):
        if child.style['column_span'] == 'all':
            if column_children:
                columns_and_blocks.append(
                    (index - len(column_children), column_children))
            columns_and_blocks.append((index, child.copy()))
            column_children = []
            continue
        column_children.append(child.copy())
    if column_children:
        columns_and_blocks.append(
            (index + 1 - len(column_children), column_children))

    if skip_stack:
        skip_stack = {0: skip_stack[skip]}

    if not box.children:
        next_page = {'break': 'any', 'page': None}
        skip_stack = None

    # Balance.
    #
    # The current algorithm starts from the total available height, to check
    # whether the whole content can fit. If it doesn’t fit, we keep the partial
    # rendering. If it fits, we try to balance the columns starting from the
    # ideal height (the total height divided by the number of columns). We then
    # iterate until the last column is not the highest one. At the end of each
    # loop, we add the minimal height needed to make one direct child at the
    # top of one column go to the end of the previous column.
    #
    # We rely on a real rendering for each loop, and with a stupid algorithm
    # like this it can last minutes…

    adjoining_margins = []
    current_position_y = box.content_box_y()
    new_children = []
    column_skip_stack = None
    forced_end_probing = False
    break_page = False
    for index, column_children_or_block in columns_and_blocks:
        if not isinstance(column_children_or_block, list):
            # We get a spanning block, we display it like other blocks.
            block = column_children_or_block
            resolve_percentages(block, containing_block)
            block.position_x = box.content_box_x()
            block.position_y = current_position_y
            new_child, resume_at, next_page, adjoining_margins, _, _ = (
                block_level_layout(
                    context, block, original_bottom_space, skip_stack,
                    containing_block, page_is_empty, absolute_boxes,
                    fixed_boxes, adjoining_margins))
            skip_stack = None
            if new_child is None:
                forced_end_probing = True
                break_page = True
                break
            new_children.append(new_child)
            current_position_y = (
                new_child.border_height() + new_child.border_box_y())
            adjoining_margins.append(new_child.margin_bottom)
            if resume_at:
                forced_end_probing = True
                break_page = True
                column_skip_stack = resume_at
                break
            page_is_empty = False
            continue

        excluded_shapes = context.excluded_shapes[:]

        # We have a list of children that we have to balance between columns.
        column_children = column_children_or_block

        # Find the total height available for the first run.
        current_position_y += collapse_margin(adjoining_margins)
        adjoining_margins = []
        column_box = create_column_box(column_children)
        column_box.position_y = current_position_y
        max_height = (
            context.page_bottom - current_position_y - original_bottom_space)
        height = max_height

        # Try to render columns until the content fits, increase the column
        # height step by step.
        column_skip_stack = skip_stack
        lost_space = inf
        original_page_is_empty = page_is_empty
        page_is_empty = False
        stop_rendering = False
        balancing = False
        footnote_heights = [
            0 if context.current_footnote_area.height == 'auto'
            else context.current_footnote_area.margin_height()]
        while True:
            column_skip_stack = skip_stack

            # Remove extra excluded shapes introduced during previous loop
            new_excluded_shapes = (
                len(context.excluded_shapes) - len(excluded_shapes))
            for i in range(new_excluded_shapes):
                context.excluded_shapes.pop()

            consumed_heights = []
            new_boxes = []
            for i in range(count):
                # Render the column
                new_box, resume_at, next_page, _, _, _ = block_box_layout(
                    context, column_box,
                    context.page_bottom - current_position_y - height,
                    column_skip_stack, containing_block,
                    page_is_empty or not balancing, [], [], [],
                    discard=False, max_lines=None)
                if new_box is None:
                    # We didn't render anything, retry.
                    column_skip_stack = {0: None}
                    break
                new_boxes.append(new_box)
                column_skip_stack = resume_at

                in_flow_children = [
                    child for child in new_box.children
                    if child.is_in_normal_flow()]

                if in_flow_children:
                    # Get the empty space at the bottom of the column box
                    consumed_height = (
                        in_flow_children[-1].margin_height() +
                        in_flow_children[-1].position_y - current_position_y)
                    empty_space = height - consumed_height

                    if column_skip_stack:
                        # Get the minimum size needed to render the next box
                        next_box, _, _, _, _, _ = block_box_layout(
                            context, column_box,
                            context.page_bottom - box.content_box_y(),
                            column_skip_stack, containing_block, True, [], [],
                            [], discard=False, max_lines=None)
                        for child in next_box.children:
                            if child.is_in_normal_flow():
                                next_box_size = child.margin_height()
                                break
                        remove_placeholders(context, [next_box], [], [])
                    else:
                        next_box_size = 0
                else:
                    consumed_height = empty_space = next_box_size = 0

                consumed_heights.append(consumed_height)

                # Append the size needed to render the next box in this
                # column.
                #
                # The next box size may be smaller than the empty space, for
                # example when the next box can't be separated from its own
                # next box. In this case we don't try to find the real value
                # and let the workaround below fix this for us.
                #
                # We also want to avoid very small values that may have been
                # introduced by rounding errors. As the workaround below at
                # least adds 1 pixel for each loop, we can ignore lost spaces
                # lower than 1px.
                if next_box_size - empty_space > 1:
                    lost_space = min(lost_space, next_box_size - empty_space)

                # Stop if we already rendered the whole content
                if resume_at is None:
                    break

            new_footnotes_height = (
                0 if context.current_footnote_area.height == 'auto'
                else context.current_footnote_area.margin_height())
            remove_placeholders(context, new_boxes, [], [])

            if forced_end_probing:
                break

            if balancing:
                if column_skip_stack is None:
                    # We rendered the whole content, stop
                    break
                else:
                    if lost_space == inf:
                        # We didn't find the extra size needed to render a
                        # child in the previous column, increase height by the
                        # minimal value.
                        add_height = 1
                    else:
                        # Increase the column heights and render them again
                        add_height = lost_space

                    if height + add_height > max_height:
                        height = max_height
                        stop_rendering = True
                        break

                    height += add_height
            else:
                if new_footnotes_height not in footnote_heights:
                    # Footnotes have been rendered, try to re-render with the
                    # new footnote area height.
                    height -= new_footnotes_height - footnote_heights[-1]
                    footnote_heights.append(new_footnotes_height)
                    continue
                if (
                        column_skip_stack or
                        max(consumed_heights) > max_height or
                        len(footnote_heights) > 2):
                    # Even at maximum height, not everything fits. Stop now and
                    # let the columns continue on the next page.
                    height += footnote_heights[-1]
                    if len(footnote_heights) > 2:
                        new_footnotes_height = min(
                            new_footnotes_height, footnote_heights[-1])
                    height -= new_footnotes_height
                    stop_rendering = True
                    break
                else:
                    # Everything fits, start expanding columns at the average
                    # of the column heights.
                    max_height -= new_footnotes_height
                    if style['column_fill'] == 'balance':
                        balancing = True
                        height = sum(consumed_heights) / count
                    else:
                        break

        # TODO: check box.style['max']-height
        bottom_space = max(
            bottom_space, context.page_bottom - current_position_y - height)

        # Replace the current box children with columns
        i = 0
        max_column_height = 0
        columns = []
        while True:
            column_box = create_column_box(column_children)
            column_box.position_y = current_position_y
            if style['direction'] == 'rtl':
                column_box.position_x += (
                    box.width - (i + 1) * width - i * style['column_gap'])
            else:
                column_box.position_x += i * (width + style['column_gap'])
            new_child, column_skip_stack, column_next_page, _, _, _ = (
                block_box_layout(
                    context, column_box, bottom_space, skip_stack,
                    containing_block, original_page_is_empty, absolute_boxes,
                    fixed_boxes, None, discard=False, max_lines=None))
            if new_child is None:
                break_page = True
                break
            next_page = column_next_page
            skip_stack = column_skip_stack
            columns.append(new_child)
            max_column_height = max(
                max_column_height, new_child.margin_height())
            # Drop column’s bottom margin if it overflows the page
            max_column_height = min(max_column_height, max_height)
            if skip_stack is None:
                bottom_space = original_bottom_space
                break
            i += 1
            if i == count and not known_height:
                # [If] a declaration that constrains the column height
                # (e.g., using height or max-height). In this case,
                # additional column boxes are created in the inline
                # direction.
                break

        current_position_y += max_column_height
        for column in columns:
            column.height = max_column_height
            new_children.append(column)

        skip_stack = None
        page_is_empty = False

        if stop_rendering:
            break

    reported_footnotes = 0
    while (
            context.current_page_footnotes and
            context.current_footnote_area.margin_height() >
            new_footnotes_height):
        context.report_footnote(context.current_page_footnotes[-1])
        reported_footnotes += 1
    if reported_footnotes >= 2:
        extra = context.reported_footnotes[-1:-reported_footnotes-1:-1]
        context.reported_footnotes[-reported_footnotes:] = extra

    if box.children and not new_children:
        # The box has children but none can be drawn, let's skip the whole box
        context.in_column = False
        return None, (0, None), {'break': 'any', 'page': None}, [], False

    # Set the height of box and the columns
    box.children = new_children
    current_position_y += collapse_margin(adjoining_margins)
    height = current_position_y - box.content_box_y()
    if box.height == 'auto':
        box.height = height
        height_difference = 0
    else:
        height_difference = box.height - height
    if box.min_height != 'auto' and box.min_height > box.height:
        height_difference += box.min_height - box.height
        box.height = box.min_height
    for child in new_children[::-1]:
        if child.is_column:
            child.height += height_difference
        else:
            break

    if box.style['position'] == 'relative':
        # New containing block, resolve the layout of the absolute descendants
        for absolute_box in absolute_boxes:
            absolute_layout(
                context, absolute_box, box, fixed_boxes, bottom_space,
                skip_stack=None)

    if column_skip_stack:
        skip, = column_skip_stack.keys()
        skip_stack = {index + skip: column_skip_stack[skip]}
    elif break_page:
        skip_stack = {index: None}
    context.in_column = False
    if context.current_footnote_area.height != 'auto':
        context.page_bottom -= context.current_footnote_area.margin_height()
    return box, skip_stack, next_page, [], False
<commit_msg>Clean code for multicolumn layout<commit_after>"""Layout for columns."""

from math import floor, inf

from .absolute import absolute_layout
from .percent import resolve_percentages

def columns_layout(context, box, bottom_space, skip_stack, containing_block,
                   page_is_empty, absolute_boxes, fixed_boxes,
                   adjoining_margins):
    """Lay out a multi-column ``box``."""
    from .block import (
        block_box_layout, block_level_layout, block_level_width,
        collapse_margin, remove_placeholders)

    style = box.style
    width = style['column_width']
    count = style['column_count']
    gap = style['column_gap']
    height = style['height']
    original_bottom_space = bottom_space
    context.in_column = True

    if style['position'] == 'relative':
        # New containing block, use a new absolute list
        absolute_boxes = []

    box = box.copy_with_children(box.children)
    box.position_y += collapse_margin(adjoining_margins) - box.margin_top

    # Set height if defined
    if height != 'auto' and height.unit != '%':
        assert height.unit == 'px'
        height_defined = True
        empty_space = context.page_bottom - box.content_box_y() - height.value
        bottom_space = max(bottom_space, empty_space)
    else:
        height_defined = False

    # TODO: the columns container width can be unknown if the containing block
    # needs the size of this block to know its own size
    block_level_width(box, containing_block)

    # Define the number of columns and their widths
    if width == 'auto' and count != 'auto':
        width = max(0, box.width - (count - 1) * gap) / count
    elif width != 'auto' and count == 'auto':
        count = max(1, int(floor((box.width + gap) / (width + gap))))
        width = (box.width + gap) / count - gap
    else:  # overconstrained, with width != 'auto' and count != 'auto'
        count = min(count, int(floor((box.width + gap) / (width + gap))))
        width = (box.width + gap) / count - gap

    # Handle column-span property with the following structure:
    # columns_and_blocks = [
    #     [column_child_1, column_child_2],
    #     spanning_block,
    #     …
    # ]
    columns_and_blocks = []
    column_children = []
    skip, = skip_stack.keys() if skip_stack else (0,)
    for i, child in enumerate(box.children[skip:], start=skip):
        if child.style['column_span'] == 'all':
            if column_children:
                columns_and_blocks.append(
                    (i - len(column_children), column_children))
            columns_and_blocks.append((i, child.copy()))
            column_children = []
            continue
        column_children.append(child.copy())
    if column_children:
        columns_and_blocks.append(
            (i + 1 - len(column_children), column_children))

    if skip_stack:
        skip_stack = {0: skip_stack[skip]}

    if not box.children:
        next_page = {'break': 'any', 'page': None}
        skip_stack = None

    # Find height and balance.
    #
    # The current algorithm starts from the total available height, to check
    # whether the whole content can fit. If it doesn’t fit, we keep the partial
    # rendering. If it fits, we try to balance the columns starting from the
    # ideal height (the total height divided by the number of columns). We then
    # iterate until the last column is not the highest one. At the end of each
    # loop, we add the minimal height needed to make one direct child at the
    # top of one column go to the end of the previous column.
    #
    # We rely on a real rendering for each loop, and with a stupid algorithm
    # like this it can last minutes…

    adjoining_margins = []
    current_position_y = box.content_box_y()
    new_children = []
    column_skip_stack = None
    last_loop = False
    break_page = False
    footnote_area_heights = [
        0 if context.current_footnote_area.height == 'auto'
        else context.current_footnote_area.margin_height()]
    last_footnotes_height = 0
    for index, column_children_or_block in columns_and_blocks:
        if not isinstance(column_children_or_block, list):
            # We have a spanning block, we display it like other blocks
            block = column_children_or_block
            resolve_percentages(block, containing_block)
            block.position_x = box.content_box_x()
            block.position_y = current_position_y
            new_child, resume_at, next_page, adjoining_margins, _, _ = (
                block_level_layout(
                    context, block, original_bottom_space, skip_stack,
                    containing_block, page_is_empty, absolute_boxes,
                    fixed_boxes, adjoining_margins))
            skip_stack = None
            if new_child is None:
                last_loop = True
                break_page = True
                break
            new_children.append(new_child)
            current_position_y = (
                new_child.border_height() + new_child.border_box_y())
            adjoining_margins.append(new_child.margin_bottom)
            if resume_at:
                last_loop = True
                break_page = True
                column_skip_stack = resume_at
                break
            page_is_empty = False
            continue

        # We have a list of children that we have to balance between columns
        column_children = column_children_or_block

        # Find the total height available for the first run
        current_position_y += collapse_margin(adjoining_margins)
        adjoining_margins = []
        column_box = _create_column_box(
            box, containing_block, column_children, width, current_position_y)
        height = max_height = (
            context.page_bottom - current_position_y - original_bottom_space)

        # Try to render columns until the content fits, increase the column
        # height step by step
        column_skip_stack = skip_stack
        lost_space = inf
        original_excluded_shapes = context.excluded_shapes[:]
        original_page_is_empty = page_is_empty
        page_is_empty = stop_rendering = balancing = False
        while True:
            # Remove extra excluded shapes introduced during the previous loop
            while len(context.excluded_shapes) > len(original_excluded_shapes):
                context.excluded_shapes.pop()

            # Render the columns
            column_skip_stack = skip_stack
            consumed_heights = []
            new_boxes = []
            for i in range(count):
                # Render one column
                new_box, resume_at, next_page, _, _, _ = block_box_layout(
                    context, column_box,
                    context.page_bottom - current_position_y - height,
                    column_skip_stack, containing_block,
                    page_is_empty or not balancing, [], [], [],
                    discard=False, max_lines=None)
                if new_box is None:
                    # We didn't render anything, retry
                    column_skip_stack = {0: None}
                    break
                new_boxes.append(new_box)
                column_skip_stack = resume_at

                # Calculate consumed height, empty space and next box height
                in_flow_children = [
                    child for child in new_box.children
                    if child.is_in_normal_flow()]
                if in_flow_children:
                    # Get the empty space at the bottom of the column box
                    consumed_height = (
                        in_flow_children[-1].margin_height() +
                        in_flow_children[-1].position_y - current_position_y)
                    empty_space = height - consumed_height

                    # Get the minimum size needed to render the next box
                    if column_skip_stack:
                        next_box = block_box_layout(
                            context, column_box, inf, column_skip_stack,
                            containing_block, True, [], [], [],
                            discard=False, max_lines=None)[0]
                        for child in next_box.children:
                            if child.is_in_normal_flow():
                                next_box_height = child.margin_height()
                                break
                        remove_placeholders(context, [next_box], [], [])
                    else:
                        next_box_height = 0
                else:
                    consumed_height = empty_space = next_box_height = 0

                consumed_heights.append(consumed_height)

                # Append the size needed to render the next box in this
                # column.
                #
                # The next box size may be smaller than the empty space, for
                # example when the next box can't be separated from its own
                # next box. In this case we don't try to find the real value
                # and let the workaround below fix this for us.
                #
                # We also want to avoid very small values that may have been
                # introduced by rounding errors. As the workaround below at
                # least adds 1 pixel for each loop, we can ignore lost spaces
                # lower than 1px.
                if next_box_height - empty_space > 1:
                    lost_space = min(lost_space, next_box_height - empty_space)

                # Stop if we already rendered the whole content
                if resume_at is None:
                    break

            # Remove placeholders but keep the current footnote area height
            last_footnotes_height = (
                0 if context.current_footnote_area.height == 'auto'
                else context.current_footnote_area.margin_height())
            remove_placeholders(context, new_boxes, [], [])

            if last_loop:
                break

            if balancing:
                if column_skip_stack is None:
                    # We rendered the whole content, stop
                    break

                # Increase the column heights and render them again
                add_height = 1 if lost_space == inf else lost_space
                height += add_height

                if height > max_height:
                    # We reached max height, stop rendering
                    height = max_height
                    stop_rendering = True
                    break
            else:
                if last_footnotes_height not in footnote_area_heights:
                    # Footnotes have been rendered, try to re-render with the
                    # new footnote area height
                    height -= last_footnotes_height - footnote_area_heights[-1]
                    footnote_area_heights.append(last_footnotes_height)
                    continue

                everything_fits = (
                    not column_skip_stack and
                    max(consumed_heights) <= max_height)
                if everything_fits:
                    # Everything fits, start expanding columns at the average
                    # of the column heights
                    max_height -= last_footnotes_height
                    if style['column_fill'] == 'balance':
                        balancing = True
                        height = sum(consumed_heights) / count
                    else:
                        break
                else:
                    # Content overflows even at maximum height, stop now and
                    # let the columns continue on the next page
                    height += footnote_area_heights[-1]
                    if len(footnote_area_heights) > 2:
                        last_footnotes_height = min(
                            last_footnotes_height, footnote_area_heights[-1])
                    height -= last_footnotes_height
                    stop_rendering = True
                    break

        # TODO: check style['max']-height
        bottom_space = max(
            bottom_space, context.page_bottom - current_position_y - height)

        # Replace the current box children with real columns
        i = 0
        max_column_height = 0
        columns = []
        while True:
            column_box = _create_column_box(
                box, containing_block, column_children, width,
                current_position_y)
            if style['direction'] == 'rtl':
                column_box.position_x += box.width - (i + 1) * width - i * gap
            else:
                column_box.position_x += i * (width + gap)
            new_child, column_skip_stack, column_next_page, _, _, _ = (
                block_box_layout(
                    context, column_box, bottom_space, skip_stack,
                    containing_block, original_page_is_empty, absolute_boxes,
                    fixed_boxes, None, discard=False, max_lines=None))
            if new_child is None:
                break_page = True
                break
            next_page = column_next_page
            skip_stack = column_skip_stack
            columns.append(new_child)
            max_column_height = max(
                max_column_height, new_child.margin_height())
            if skip_stack is None:
                bottom_space = original_bottom_space
                break
            i += 1
            if i == count and not height_defined:
                # [If] a declaration that constrains the column height
                # (e.g., using height or max-height). In this case,
                # additional column boxes are created in the inline
                # direction.
                break

        # Update the current y position and set the columns’ height
        current_position_y += min(max_height, max_column_height)
        for column in columns:
            column.height = max_column_height
            new_children.append(column)

        skip_stack = None
        page_is_empty = False

        if stop_rendering:
            break

    # Report footnotes above the defined footnotes height
    _report_footnotes(context, last_footnotes_height)

    if box.children and not new_children:
        # The box has children but none can be drawn, let's skip the whole box
        context.in_column = False
        return None, (0, None), {'break': 'any', 'page': None}, [], False

    # Set the height of the containing box
    box.children = new_children
    current_position_y += collapse_margin(adjoining_margins)
    height = current_position_y - box.content_box_y()
    if box.height == 'auto':
        box.height = height
        height_difference = 0
    else:
        height_difference = box.height - height

    # Update the latest columns’ height to respect min-height
    if box.min_height != 'auto' and box.min_height > box.height:
        height_difference += box.min_height - box.height
        box.height = box.min_height
    for child in new_children[::-1]:
        if child.is_column:
            child.height += height_difference
        else:
            break

    if style['position'] == 'relative':
        # New containing block, resolve the layout of the absolute descendants
        for absolute_box in absolute_boxes:
            absolute_layout(
                context, absolute_box, box, fixed_boxes, bottom_space,
                skip_stack=None)

    # Calculate skip stack
    if column_skip_stack:
        skip, = column_skip_stack.keys()
        skip_stack = {index + skip: column_skip_stack[skip]}
    elif break_page:
        skip_stack = {index: None}

    # Update page bottom according to the new footnotes
    if context.current_footnote_area.height != 'auto':
        context.page_bottom -= context.current_footnote_area.margin_height()

    context.in_column = False
    return box, skip_stack, next_page, [], False

def _report_footnotes(context, footnotes_height):
    """Report footnotes above the defined footnotes height."""
    if not context.current_page_footnotes:
        return

    # Report and count footnotes
    reported_footnotes = 0
    while context.current_footnote_area.margin_height() > footnotes_height:
        context.report_footnote(context.current_page_footnotes[-1])
        reported_footnotes += 1

    # Revert reported footnotes, as they’ve been reported starting from the
    # last one
    if reported_footnotes >= 2:
        extra = context.reported_footnotes[-1:-reported_footnotes-1:-1]
        context.reported_footnotes[-reported_footnotes:] = extra

def _create_column_box(box, containing_block, children, width, position_y):
    """Create a column box including given children."""
    column_box = box.anonymous_from(box, children=children)
    resolve_percentages(column_box, containing_block)
    column_box.is_column = True
    column_box.width = width
    column_box.position_x = box.content_box_x()
    column_box.position_y = position_y
    return column_box
<|endoftext|>


<commit_before>import random

def sort_questions(list_tiger, list_bear, list_turtle):
	list_question = []

	question_number_tiger = random.randint(1, len(list_tiger))
	question_number_tiger -= 1

	question_number_bear = random.randint(1, len(list_bear))
	question_number_bear -= 1

	question_number_turtle = random.randint(1, len(list_turtle))
	question_number_turtle -= 1

	for index, obj in enumerate(list_tiger):
		print('for tiger')
		if index == question_number_tiger:
			list_question.append(obj)
			list_tiger.remove(obj)
			break

	for index, obj in enumerate(list_bear):
		print('for bear')
		if index == question_number_bear:
			list_question.append(obj)
			list_bear.remove(obj)
			break

	for index, obj in enumerate(list_turtle):
		print('for turtle')
		if index == question_number_turtle:
			list_question.append(obj)
			list_turtle.remove(obj)
			break

	return list_question

def choice_one_question(choice, list_question):
	for index, obj in enumerate(list_question):
		if index == choice:
			return obj
	return None

if __name__ == '__main__':

	list_tiger = [
		'tiger 1',
		'tiger 2',
		'tiger 3',
		'tiger 4',
		'tiger 5',
	]

	list_bear = [
		'bear 1',
		'bear 2',
		'bear 3',
		'bear 4',
		'bear 5',
	]

	list_turtle = [
		'turtle 1',
		'turtle 2',
		'turtle 3',
		'turtle 4',
		'turtle 5',
	]

	result_list = []
	#1
	sorted_list1 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list1)
	choice1 = int(input('Escolha uma questão entre 1 e 3:'))
	choice1 -= 1
	second_answer1 = choice_one_question(choice1, sorted_list1)
	result_list.append(second_answer1)

	#2
	sorted_list2 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list2)
	choice2 = int(input('Escolha uma questão entre 1 e 3:'))
	choice2 -= 1
	second_answer2 = choice_one_question(choice2, sorted_list2)
	result_list.append(second_answer2)

	#3
	sorted_list3 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list3)
	choice3 = int(input('Escolha uma questão entre 1 e 3:'))
	choice3 -= 1
	second_answer3 = choice_one_question(choice3, sorted_list3)
	result_list.append(second_answer3)

	#4
	sorted_list4 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list4)
	choice4 = int(input('Escolha uma questão entre 1 e 3:'))
	choice4 -= 1
	second_answer4 = choice_one_question(choice4, sorted_list4)
	result_list.append(second_answer4)

	#5
	sorted_list5 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list5)
	choice5 = int(input('Escolha uma questão entre 1 e 3:'))
	choice5 -= 1
	second_answer5 = choice_one_question(choice5, sorted_list5)
	result_list.append(second_answer5)

	print(result_list)

second_list_question = choice_one_question(1, ['bla', 'ble', 'bli'])

choice = 1
a_list = ['bla', 'ble', 'bli']

second_list_question = choice_one_question(choice, a_list)

def choice_one_question(choice, a_list):
	second_list_question = []

	for index, obj in enumerate(a_list):
		if index == choice:
			second_list_question.append(obj)

	return second_list_question
<commit_msg>adjust identation, remove extra codes and fix bugs<commit_after>import random

def sort_questions(list_tiger, list_bear, list_turtle):
	list_question = []

	question_number_tiger = random.randint(1, len(list_tiger))
	question_number_tiger -= 1

	question_number_bear = random.randint(1, len(list_bear))
	question_number_bear -= 1

	question_number_turtle = random.randint(1, len(list_turtle))
	question_number_turtle -= 1

	for index, obj in enumerate(list_tiger):
		print('for tiger')
		if index == question_number_tiger:
			list_question.append(obj)
			list_tiger.remove(obj)
			break

	for index, obj in enumerate(list_bear):
		print('for bear')
		if index == question_number_bear:
			list_question.append(obj)
			list_bear.remove(obj)
			break

	for index, obj in enumerate(list_turtle):
		print('for turtle')
		if index == question_number_turtle:
			list_question.append(obj)
			list_turtle.remove(obj)
			break

	return list_question

def choice_one_question(choice, list_question):
	for index, obj in enumerate(list_question):
		if index == choice:
			return obj
	return None

if __name__ == '__main__':

	list_tiger = [
		'tiger 1',
		'tiger 2',
		'tiger 3',
		'tiger 4',
		'tiger 5',
	]

	list_bear = [
		'bear 1',
		'bear 2',
		'bear 3',
		'bear 4',
		'bear 5',
	]

	list_turtle = [
		'turtle 1',
		'turtle 2',
		'turtle 3',
		'turtle 4',
		'turtle 5',
	]

	result_list = []
	#1
	sorted_list1 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list1)
	choice1 = int(input('Escolha uma questão entre 1 e 3:'))
	choice1 -= 1
	second_answer1 = choice_one_question(choice1, sorted_list1)
	result_list.append(second_answer1)

	#2
	sorted_list2 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list2)
	choice2 = int(input('Escolha uma questão entre 1 e 3:'))
	choice2 -= 1
	second_answer2 = choice_one_question(choice2, sorted_list2)
	result_list.append(second_answer2)

	#3
	sorted_list3 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list3)
	choice3 = int(input('Escolha uma questão entre 1 e 3:'))
	choice3 -= 1
	second_answer3 = choice_one_question(choice3, sorted_list3)
	result_list.append(second_answer3)

	#4
	sorted_list4 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list4)
	choice4 = int(input('Escolha uma questão entre 1 e 3:'))
	choice4 -= 1
	second_answer4 = choice_one_question(choice4, sorted_list4)
	result_list.append(second_answer4)

	#5
	sorted_list5 = sort_questions(list_tiger, list_bear, list_turtle)
	print(sorted_list5)
	choice5 = int(input('Escolha uma questão entre 1 e 3:'))
	choice5 -= 1
	second_answer5 = choice_one_question(choice5, sorted_list5)
	result_list.append(second_answer5)

	print(result_list)

second_list_question = choice_one_question(1, ['bla', 'ble', 'bli'])

choice = 1
a_list = ['bla', 'ble', 'bli']

second_list_question = choice_one_question(choice, a_list)
<|endoftext|>


<commit_before>from datetime import datetime, timedelta
from devilry.apps.core.models import Assignment
from devilry.apps.core.testhelper import TestHelper

from .base import SubjectAdminSeleniumTestCase
from .base import RenameBasenodeTestMixin
from .base import DeleteBasenodeTestMixin

class TestAssignment(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_doesnotexists(self):
        self.testhelper.create_user('normal')
        self.login('normal')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_shortcuts_render(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertTrue('Manage students' in self.selenium.page_source)
        self.assertTrue('Manage deadlines' in self.selenium.page_source)

    def test_title(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertEquals(self.selenium.title, 'sub.period1.week2 - Devilry')

    def test_notpublished(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForText('>Not published')

    def test_published(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2)'],
                            assignments=['week2:admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForText('>Published')

class TestAssignmentDangerous(SubjectAdminSeleniumTestCase, RenameBasenodeTestMixin, DeleteBasenodeTestMixin):
    renamebutton_id = 'assignmentRenameButton'
    deletebutton_id = 'assignmentDeleteButton'

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['sub'],
                            periods=['period1:begins(-2):admin(p1admin)'],
                            assignments=['week1:admin(weekadmin)'])

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_rename(self):
        self.login('weekadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.rename_test_helper(self.testhelper.sub_period1_week1)

    def test_rename_failure(self):
        self.login('weekadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.rename_test_failure_helper()

    def test_delete(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2):admin(p1admin)'],
                            assignments=['willbedeleted'])
        self.login('p1admin')
        self._browseToAssignment(self.testhelper.sub_period1_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        assignmenturl = self.selenium.current_url
        self.perform_delete()
        self.waitFor(self.selenium, lambda s: s.current_url != assignmenturl) # Will time out and fail unless the page is changed after delete
        self.assertEquals(Assignment.objects.filter(id=self.testhelper.sub_period1_willbedeleted.id).count(), 0)

    def test_delete_notparentadmin(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2)'],
                            assignments=['willbedeleted:admin(willbedeletedadm)'])
        self.login('willbedeletedadm')
        self._browseToAssignment(self.testhelper.sub_period1_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

    def test_delete_not_empty(self):
        self.testhelper.add_to_path('uni;sub.period1.week1.g1:candidate(goodStud1)')
        self.login('uniadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

class TestEditPublishingTime(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.browseTo('/assignment/1/')
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime')

        editpublishingtime_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime')
        self.datefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_datefield input')
        self.timefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_timefield input')
        self.savebutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editpublishingtime_window = editpublishingtime_window

    def _set_datetime(self, date, time):
        self.datefield.clear()
        self.timefield.clear()
        self.datefield.send_keys(date)
        self.timefield.send_keys(time)

    def test_editpublishingtime(self):
        self.assertTrue('Published' in self.selenium.page_source)
        self.assertTrue('Choose a time when time when students will be able to start adding deliveries on the assignment' in self.selenium.page_source)
        yesterday = datetime.now() - timedelta(days=1)
        isoday_yesterday = yesterday.date().isoformat()
        self._set_datetime(isoday_yesterday, '12:00')
        self.savebutton.click()
        self.waitForText('Publishing time was {isoday_yesterday} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.sub_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), yesterday.date())

    def test_editpublishingtime_notpublished(self):
        tomorrow = datetime.now() + timedelta(days=1)
        isoday_tomorrow = tomorrow.date().isoformat()
        self._set_datetime(isoday_tomorrow, '12:00')
        self.savebutton.click()
        self.waitForText('Will be published {isoday_tomorrow} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.sub_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), tomorrow.date())

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editpublishingtime_window, '.x-tool-close')  # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editpublishingtime' in self.selenium.page_source)

    def test_editpublishingtime_errorhandling(self):
        self._set_datetime('2000-02-01', '12:00')
        self.savebutton.click()
        self.waitForText("The publishing time, 2000-02-01 12:00:00, is invalid")

class TestEditAnonymous(SubjectAdminSeleniumTestCase):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.week1 = self.testhelper.sub_period1_week1

        self.browseTo('/assignment/{0}/'.format(self.testhelper.sub_period1_week1.id))
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous')

        editanonymous_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous')
        self.anonymouscheckbox = editanonymous_window.find_element_by_css_selector('input.x-form-checkbox')
        self.savebutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editanonymous_window = editanonymous_window

    def test_editanonymous(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.assertTrue('Not anonymous' in self.selenium.page_source)
        self.assertTrue('Examiners and students can see each other and communicate.' in self.selenium.page_source)
        self.anonymouscheckbox.click()
        self.savebutton.click()
        self.waitForText('>Anonymous') # If this times out, is has not been updated
        self.waitForText('Examiners and students can not see each other and they can not communicate.') # If this times out, is has not been updated
        self.assertTrue(Assignment.objects.get(pk=self.week1.pk).anonymous)

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editanonymous_window, '.x-tool-close') # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editanonymous' in self.selenium.page_source)

    def test_editanonymous_nochange(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.savebutton.click()
        self.waitForText('Not anonymous') # If this times out, it has not been updated
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
<commit_msg>devilry_subjectadmin.tests.assignment: Update for changes to is_empty.<commit_after>from datetime import datetime, timedelta
from devilry.apps.core.models import Assignment
from devilry.apps.core.testhelper import TestHelper

from .base import SubjectAdminSeleniumTestCase
from .base import RenameBasenodeTestMixin
from .base import DeleteBasenodeTestMixin

class TestAssignment(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_doesnotexists(self):
        self.testhelper.create_user('normal')
        self.login('normal')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self._browseToAssignment(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_shortcuts_render(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertTrue('Manage students' in self.selenium.page_source)
        self.assertTrue('Manage deadlines' in self.selenium.page_source)

    def test_title(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.assertEquals(self.selenium.title, 'sub.period1.week2 - Devilry')

    def test_notpublished(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1'],
                            assignments=['week2:pub(2):admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForText('>Not published')

    def test_published(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2)'],
                            assignments=['week2:admin(week2admin)'])
        self.login('week2admin')
        self._browseToAssignment(self.testhelper.sub_period1_week2.id)
        self.waitForText('>Published')

class TestAssignmentDangerous(SubjectAdminSeleniumTestCase, RenameBasenodeTestMixin, DeleteBasenodeTestMixin):
    renamebutton_id = 'assignmentRenameButton'
    deletebutton_id = 'assignmentDeleteButton'

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['sub'],
                            periods=['period1:begins(-2):admin(p1admin)'],
                            assignments=['week1:admin(weekadmin)'])

    def _browseToAssignment(self, id):
        self.browseTo('/assignment/{id}/'.format(id=id))

    def test_rename(self):
        self.login('weekadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.rename_test_helper(self.testhelper.sub_period1_week1)

    def test_rename_failure(self):
        self.login('weekadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.rename_test_failure_helper()

    def test_delete(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2):admin(p1admin)'],
                            assignments=['willbedeleted'])
        self.login('p1admin')
        self._browseToAssignment(self.testhelper.sub_period1_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        assignmenturl = self.selenium.current_url
        self.perform_delete()
        self.waitFor(self.selenium, lambda s: s.current_url != assignmenturl) # Will time out and fail unless the page is changed after delete
        self.assertEquals(Assignment.objects.filter(id=self.testhelper.sub_period1_willbedeleted.id).count(), 0)

    def test_delete_notparentadmin(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-2)'],
                            assignments=['willbedeleted:admin(willbedeletedadm)'])
        self.login('willbedeletedadm')
        self._browseToAssignment(self.testhelper.sub_period1_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

    def test_delete_not_empty(self):
        self.testhelper.add_to_path('uni;sub.period1.week1.g1:candidate(goodStud1):examiner(exam1).d1')
        self.testhelper.add_delivery('sub.period1.week1.g1', {'bad.py': ['print ', 'bah']})
        self.login('uniadmin')
        self._browseToAssignment(self.testhelper.sub_period1_week1.id)
        self.waitForCssSelector('.devilry_subjectadmin_assignmentoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

class TestEditPublishingTime(SubjectAdminSeleniumTestCase):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.browseTo('/assignment/1/')
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editpublishingtime')

        editpublishingtime_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editpublishingtime')
        self.datefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_datefield input')
        self.timefield = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_timefield input')
        self.savebutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editpublishingtime_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editpublishingtime_window = editpublishingtime_window

    def _set_datetime(self, date, time):
        self.datefield.clear()
        self.timefield.clear()
        self.datefield.send_keys(date)
        self.timefield.send_keys(time)

    def test_editpublishingtime(self):
        self.assertTrue('Published' in self.selenium.page_source)
        self.assertTrue('Choose a time when time when students will be able to start adding deliveries on the assignment' in self.selenium.page_source)
        yesterday = datetime.now() - timedelta(days=1)
        isoday_yesterday = yesterday.date().isoformat()
        self._set_datetime(isoday_yesterday, '12:00')
        self.savebutton.click()
        self.waitForText('Publishing time was {isoday_yesterday} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.sub_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), yesterday.date())

    def test_editpublishingtime_notpublished(self):
        tomorrow = datetime.now() + timedelta(days=1)
        isoday_tomorrow = tomorrow.date().isoformat()
        self._set_datetime(isoday_tomorrow, '12:00')
        self.savebutton.click()
        self.waitForText('Will be published {isoday_tomorrow} 12:00'.format(**vars())) # If this times out, it has not been updated
        week1 = Assignment.objects.get(pk=self.testhelper.sub_period1_week1.pk)
        self.assertEquals(week1.publishing_time.date(), tomorrow.date())

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editpublishingtime_window, '.x-tool-close')  # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editpublishingtime' in self.selenium.page_source)

    def test_editpublishingtime_errorhandling(self):
        self._set_datetime('2000-02-01', '12:00')
        self.savebutton.click()
        self.waitForText("The publishing time, 2000-02-01 12:00:00, is invalid")

class TestEditAnonymous(SubjectAdminSeleniumTestCase):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['period1:begins(-3)'],
                            assignments=['week1:admin(week1admin)'])
        self.login('week1admin')

        self.week1 = self.testhelper.sub_period1_week1

        self.browseTo('/assignment/{0}/'.format(self.testhelper.sub_period1_week1.id))
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous_widget button')
        button = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous_widget button')
        button.click()
        self.waitForCssSelector('.devilry_subjectadmin_editanonymous')

        editanonymous_window = self.selenium.find_element_by_css_selector('.devilry_subjectadmin_editanonymous')
        self.anonymouscheckbox = editanonymous_window.find_element_by_css_selector('input.x-form-checkbox')
        self.savebutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_savebutton button')
        self.cancelbutton = editanonymous_window.find_element_by_css_selector('.devilry_extjsextras_cancelbutton')
        self.editanonymous_window = editanonymous_window

    def test_editanonymous(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.assertTrue('Not anonymous' in self.selenium.page_source)
        self.assertTrue('Examiners and students can see each other and communicate.' in self.selenium.page_source)
        self.anonymouscheckbox.click()
        self.savebutton.click()
        self.waitForText('>Anonymous') # If this times out, is has not been updated
        self.waitForText('Examiners and students can not see each other and they can not communicate.') # If this times out, is has not been updated
        self.assertTrue(Assignment.objects.get(pk=self.week1.pk).anonymous)

    def test_cancel(self):
        self.failIfCssSelectorFound(self.editanonymous_window, '.x-tool-close') # Make sure window does not have closable set to true
        self.cancelbutton.click()
        self.assertFalse('.devilry_subjectadmin_editanonymous' in self.selenium.page_source)

    def test_editanonymous_nochange(self):
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
        self.savebutton.click()
        self.waitForText('Not anonymous') # If this times out, it has not been updated
        self.assertFalse(Assignment.objects.get(pk=self.week1.pk).anonymous)
<|endoftext|>


<commit_before>#!/usr/bin/env python

#########################################################################################################################
#											Simple Backup System - Main Daemon											#
#########################################################################################################################
#																		(C) Manuel Bernal Llinares <mbdebian@gmail.com>	#
#																			Distributed under the Apache License 2.0	#
#########################################################################################################################

import os
import sys
import optparse
import settings
import logger
import json
import time
import subprocess

def getEndPoint(item):
	if item['type'] in "local":
		return item['folder'] + "/"
	elif item['type'] in "remote":
		config = settings.getConfig()
		return config['credentials'][item['credentials']]['user'] \
				+ "@" + config['servers'][item['server']]['address'] \
				+ ":" \
				+ item['folder'] + "/"

def getSignalCommands(item):
	config = settings.getConfig()
	endPoints = []
	if "signalFiles" in item:
		if item['type'] in "local":
			for signal in item['signalFiles']:
				endPoints.append("touch " + signal)
		elif item['type'] in "remote":
			config = settings.getConfig()
			for signal in item['signalFiles']:
				endPoints.append("ssh " + config['credentials'][item['credentials']]['user'] \
										+ "@" + config['servers'][item['server']]['address'] \
										+ " touch " \
										+ signal)
	return endPoints

def launchBackupForItem(bitem, key):
	config = settings.getConfig()
	miLogger = logger.getLogger()
	miLogger.info("Running backup for '" + bitem['name'] + "'")
	sourceEndPoint = getEndPoint(bitem['source'])
	destinationEndPoint = getEndPoint(bitem['destination'])
	command = "rsync -vahzx --compress-level=9 --no-devices --no-p " \
				+ "--progress " \
				+ "--stats " \
				+ "--exclude=.Spotlight-V100 " \
				+ "--exclude=.Trashes " \
				+ "--exclude=.Trash " \
				+ "--exclude=.fseventsd " \
				+ "--exclude=._.Trashes " \
				+ "--exclude=.DS_Store " \
				+ "--exclude=._.DS_Store " \
				+ sourceEndPoint \
				+ " " \
				+ destinationEndPoint \
				+ " >> " + config['logger']['ouputAndErrorFilePath'] + " 2>&1"
	miLogger.debug("Synchronization command: " + command)
	return subprocess.Popen(command, shell=True)

def launchBackups():
	config = settings.getConfig()
	miLogger = logger.getLogger()
	# Run the backups
	ongoingBackups = {}
	for bitem in config['backups']:
		miLogger.debug("Processing backup entry " + bitem)
		ongoingBackups[bitem] = {'process': launchBackupForItem(config['backups'][bitem], bitem), 'tryCounter': 1}
	return ongoingBackups

def waitForBackupsToComplete(ongoingBackups):
	# Wait for them and repeat those with error state
	config = settings.getConfig()
	miLogger = logger.getLogger()
	signalCompletion = []
	ntries = 12
	while len(ongoingBackups) > 0:
		for bitem in list(ongoingBackups):
			result = ongoingBackups[bitem]['process'].wait()
			if result:
				if ongoingBackups[bitem]['tryCounter'] == ntries:
					miLogger.error("I've tried " + str(ongoingBackups[bitem]['tryCounter']) + " times to run backup for " + bitem + ", " + config['backups'][bitem]['name'] + ", resulting in ERROR")
					ongoingBackups.pop(bitem, None)
				else:
					ongoingBackups[bitem]['tryCounter'] += 1
					ongoingBackups[bitem]['process'] = launchBackupForItem(config['backups'][bitem], bitem)
			else:
				miLogger.info("Successful backup for " + config['backups'][bitem]['name'] + ", after trying it " + str(ongoingBackups[bitem]['tryCounter']) + " times")
				ongoingBackups.pop(bitem, None)
				signalCompletion.append(bitem)
	return signalCompletion

def sendCompletionSignals(signalCompletion):
	config = settings.getConfig()
	miLogger = logger.getLogger()
	# Get the signaling commands
	ongoingSignals = {}
	for bitem in signalCompletion:
		commands = getSignalCommands(config['backups'][bitem]['source'])
		commands += getSignalCommands(config['backups'][bitem]['destination'])
		for command in commands:
			command = command + " >> " + config['logger']['ouputAndErrorFilePath'] + " 2>&1"
			ongoingSignals[bitem] = {'command': command, 'tryCounter': 1, 'process': subprocess.Popen(command, shell=True)}
			miLogger.debug("Sending signal: " + command)

	# Wait for signal to complete
	ntries = 12
	while len(ongoingSignals) > 0:
		for bitem in list(ongoingSignals):
			result = ongoingSignals[bitem]['process'].wait()
			if result:
				if ongoingSignals[bitem]['tryCounter'] == ntries:
					miLogger.error("I've tried " + str(ongoingSignals[bitem]['tryCounter']) + " times to send the following signal " + ongoingSignals[bitem]['command'] + " for '" + config['backups'][bitem]['name'] + "', resulting in ERROR")
					ongoingSignals.pop(bitem, None)
				else:
					ongoingSignals[bitem]['tryCounter'] += 1
					ongoingSignals[bitem]['process'] = subprocess.Popen(command, shell=True)
			else:
				miLogger.info("Successful signal sent for " + config['backups'][bitem]['name'] + ", after trying it " + str(ongoingSignals[bitem]['tryCounter']) + " times")
				ongoingSignals.pop(bitem, None)

def main():
	# Get settings from file
	config = settings.getConfig()

	if config == None:
		print("Settings file could not be read from ")
		sys.exit(127)

	# Get logger
	miLogger = logger.getLogger()
	miLogger.info(str("=" * 120))
	miLogger.info("Backup session started")

	# Time the session
	sessionStart = time.time()

	# Do backups
	ongoingBackups 		= launchBackups()
	signalCompletion 	= waitForBackupsToComplete(ongoingBackups)
	sendCompletionSignals(signalCompletion)

	# Stop time
	sessionStop = time.time()
	m, s = divmod((sessionStop - sessionStart), 60)
	h, m = divmod(m, 60)
	miLogger.info("===> Total Time for the Session: " + "%d hours %02d minutes %02d seconds" % (h, m, s))

	# Session END, print resutls
	miLogger.info(str("=" * 120))

if __name__ == "__main__":
	main()<commit_msg>'-z' and compression level config disabled due to a bug in RSYNC 3.1.0 and 3.1.1 protocol version 31.<commit_after>#!/usr/bin/env python

#########################################################################################################################
#											Simple Backup System - Main Daemon											#
#########################################################################################################################
#																		(C) Manuel Bernal Llinares <mbdebian@gmail.com>	#
#																			Distributed under the Apache License 2.0	#
#########################################################################################################################

import os
import sys
import optparse
import settings
import logger
import json
import time
import subprocess

def getEndPoint(item):
	if item['type'] in "local":
		return item['folder'] + "/"
	elif item['type'] in "remote":
		config = settings.getConfig()
		return config['credentials'][item['credentials']]['user'] \
				+ "@" + config['servers'][item['server']]['address'] \
				+ ":" \
				+ item['folder'] + "/"

def getSignalCommands(item):
	config = settings.getConfig()
	endPoints = []
	if "signalFiles" in item:
		if item['type'] in "local":
			for signal in item['signalFiles']:
				endPoints.append("touch " + signal)
		elif item['type'] in "remote":
			config = settings.getConfig()
			for signal in item['signalFiles']:
				endPoints.append("ssh " + config['credentials'][item['credentials']]['user'] \
										+ "@" + config['servers'][item['server']]['address'] \
										+ " touch " \
										+ signal)
	return endPoints

def launchBackupForItem(bitem, key):
	config = settings.getConfig()
	miLogger = logger.getLogger()
	miLogger.info("Running backup for '" + bitem['name'] + "'")
	sourceEndPoint = getEndPoint(bitem['source'])
	destinationEndPoint = getEndPoint(bitem['destination'])
	command = "rsync -vahx --no-devices --no-p " \
				+ "--progress " \
				+ "--stats " \
				+ "--exclude=.Spotlight-V100 " \
				+ "--exclude=.Trashes " \
				+ "--exclude=.Trash " \
				+ "--exclude=.fseventsd " \
				+ "--exclude=._.Trashes " \
				+ "--exclude=.DS_Store " \
				+ "--exclude=._.DS_Store " \
				+ sourceEndPoint \
				+ " " \
				+ destinationEndPoint \
				+ " >> " + config['logger']['ouputAndErrorFilePath'] + " 2>&1"
	miLogger.debug("Synchronization command: " + command)
	return subprocess.Popen(command, shell=True)

def launchBackups():
	config = settings.getConfig()
	miLogger = logger.getLogger()
	# Run the backups
	ongoingBackups = {}
	for bitem in config['backups']:
		miLogger.debug("Processing backup entry " + bitem)
		ongoingBackups[bitem] = {'process': launchBackupForItem(config['backups'][bitem], bitem), 'tryCounter': 1}
	return ongoingBackups

def waitForBackupsToComplete(ongoingBackups):
	# Wait for them and repeat those with error state
	config = settings.getConfig()
	miLogger = logger.getLogger()
	signalCompletion = []
	ntries = 12
	while len(ongoingBackups) > 0:
		for bitem in list(ongoingBackups):
			result = ongoingBackups[bitem]['process'].wait()
			if result:
				if ongoingBackups[bitem]['tryCounter'] == ntries:
					miLogger.error("I've tried " + str(ongoingBackups[bitem]['tryCounter']) + " times to run backup for " + bitem + ", " + config['backups'][bitem]['name'] + ", resulting in ERROR")
					ongoingBackups.pop(bitem, None)
				else:
					ongoingBackups[bitem]['tryCounter'] += 1
					ongoingBackups[bitem]['process'] = launchBackupForItem(config['backups'][bitem], bitem)
			else:
				miLogger.info("Successful backup for " + config['backups'][bitem]['name'] + ", after trying it " + str(ongoingBackups[bitem]['tryCounter']) + " times")
				ongoingBackups.pop(bitem, None)
				signalCompletion.append(bitem)
	return signalCompletion

def sendCompletionSignals(signalCompletion):
	config = settings.getConfig()
	miLogger = logger.getLogger()
	# Get the signaling commands
	ongoingSignals = {}
	for bitem in signalCompletion:
		commands = getSignalCommands(config['backups'][bitem]['source'])
		commands += getSignalCommands(config['backups'][bitem]['destination'])
		for command in commands:
			command = command + " >> " + config['logger']['ouputAndErrorFilePath'] + " 2>&1"
			ongoingSignals[bitem] = {'command': command, 'tryCounter': 1, 'process': subprocess.Popen(command, shell=True)}
			miLogger.debug("Sending signal: " + command)

	# Wait for signal to complete
	ntries = 12
	while len(ongoingSignals) > 0:
		for bitem in list(ongoingSignals):
			result = ongoingSignals[bitem]['process'].wait()
			if result:
				if ongoingSignals[bitem]['tryCounter'] == ntries:
					miLogger.error("I've tried " + str(ongoingSignals[bitem]['tryCounter']) + " times to send the following signal " + ongoingSignals[bitem]['command'] + " for '" + config['backups'][bitem]['name'] + "', resulting in ERROR")
					ongoingSignals.pop(bitem, None)
				else:
					ongoingSignals[bitem]['tryCounter'] += 1
					ongoingSignals[bitem]['process'] = subprocess.Popen(command, shell=True)
			else:
				miLogger.info("Successful signal sent for " + config['backups'][bitem]['name'] + ", after trying it " + str(ongoingSignals[bitem]['tryCounter']) + " times")
				ongoingSignals.pop(bitem, None)

def main():
	# Get settings from file
	config = settings.getConfig()

	if config == None:
		print("Settings file could not be read from ")
		sys.exit(127)

	# Get logger
	miLogger = logger.getLogger()
	miLogger.info(str("=" * 120))
	miLogger.info("Backup session started")

	# Time the session
	sessionStart = time.time()

	# Do backups
	ongoingBackups 		= launchBackups()
	signalCompletion 	= waitForBackupsToComplete(ongoingBackups)
	sendCompletionSignals(signalCompletion)

	# Stop time
	sessionStop = time.time()
	m, s = divmod((sessionStop - sessionStart), 60)
	h, m = divmod(m, 60)
	miLogger.info("===> Total Time for the Session: " + "%d hours %02d minutes %02d seconds" % (h, m, s))

	# Session END, print resutls
	miLogger.info(str("=" * 120))

if __name__ == "__main__":
	main()
<|endoftext|>


<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-

import logging
import togetter

if __name__=='__main__':
    print('TogetterSample')
    # logger setting
    logger = logging.getLogger('TogetterSample')
    logger.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    handler.formatter = logging.Formatter(
                 fmt='%(name)s::%(levelname)s: %(message)s')
    logger.addHandler(handler)
    # get tweet from togetter
    togetter_id = XXXXXXX
    page = togetter.TogetterPage(togetter_id, logger= logger)
    page.loadTweets()
    # save as XML
    xml_file = 'togetter_{0}.xml'.format(togetter_id)
    page.saveAsXML(xml_file)
    # load from XML
    togetter_data = togetter.fromXML(xml_file)
    print(togetter_data.title)
    print(togetter_data.url)
    for tweet in togetter_data.tweet_list:
        print(tweet.user_name)
        print(tweet.tweet)
        print()
<commit_msg>SampleProgramのID指定を対話形式にした<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-

import logging
import togetter

if __name__=='__main__':
    print('TogetterSample')
    # logger setting
    logger = logging.getLogger('TogetterSample')
    logger.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    handler.formatter = logging.Formatter(
                 fmt='%(name)s::%(levelname)s: %(message)s')
    logger.addHandler(handler)
    # input togetter id
    while True:
        input_id = input("input TogetterPage ID >")
        try:
            togetter_id = int(input_id)
            break
        except ValueError as error:
            print("your input<{0}> is invalid".format(repr(input_id)))
            print(str(error))
    # get tweet from togetter
    page = togetter.TogetterPage(togetter_id, logger= logger)
    page.loadTweets()
    # save as XML
    xml_file = 'togetter_{0}.xml'.format(togetter_id)
    page.saveAsXML(xml_file)
    # load from XML
    togetter_data = togetter.fromXML(xml_file)
    print(togetter_data.title)
    print(togetter_data.url)
    for tweet in togetter_data.tweet_list:
        print(tweet.user_name)
        print(tweet.tweet)
        print()
<|endoftext|>