<commit_before># Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Implements a format decision state object that manages whitespace decisions.

Each token is processed one at a time, at which point its whitespace formatting
decisions are made. A graph of potential whitespace formattings is created,
where each node in the graph is a format decision state object. The heuristic
tries formatting the token with and without a newline before it to determine
which one has the least penalty. Therefore, the format decision state object for
each decision needs to be its own unique copy.

Once the heuristic determines the best formatting, it makes a non-dry run pass
through the code to commit the whitespace formatting.

  FormatDecisionState: main class exported by this module.
"""

import copy

from yapf.yapflib import format_token
from yapf.yapflib import split_penalty
from yapf.yapflib import style

class FormatDecisionState(object):
  """The current state when indenting an unwrapped line.

  The FormatDecisionState object is meant to be copied instead of referenced.

  Attributes:
    first_indent: The indent of the first token.
    column: The number of used columns in the current line.
    next_token: The next token to be formatted.
    paren_level: The level of nesting inside (), [], and {}.
    start_of_line_level: The paren_level at the start of this line.
    lowest_level_on_line: The lowest paren_level on the current line.
    newline: Indicates if a newline is added along the edge to this format
      decision state node.
    previous: The previous format decision state in the decision tree.
    stack: A stack (of _ParenState) keeping track of properties applying to
      parenthesis levels.
    ignore_stack_for_comparison: Ignore the stack of _ParenState for state
      comparison.
  """

  def __init__(self, line, first_indent):
    """Initializer.

    Initializes to the state after placing the first token from 'line' at
    'first_indent'.

    Arguments:
      line: (UnwrappedLine) The unwrapped line we're currently processing.
      first_indent: (int) The indent of the first token.
    """
    self.next_token = line.first
    self.column = first_indent
    self.paren_level = 0
    self.start_of_line_level = 0
    self.lowest_level_on_line = 0
    self.ignore_stack_for_comparison = False
    self.stack = [_ParenState(first_indent, first_indent)]
    self.first_indent = first_indent
    self.newline = False
    self.previous = None
    self._MoveStateToNextToken()

  def Clone(self):
    new = copy.copy(self)
    new.stack = copy.deepcopy(self.stack)
    return new

  def __eq__(self, other):
    # Note: 'first_indent' is implicit in the stack. Also, we ignore 'previous',
    # because it shouldn't have a bearing on this comparison. (I.e., it will
    # report equal if 'next_token' does.)
    return (self.next_token == other.next_token and
            self.column == other.column and
            self.paren_level == other.paren_level and
            self.start_of_line_level == other.start_of_line_level and
            self.lowest_level_on_line == other.lowest_level_on_line and
            (self.ignore_stack_for_comparison or
             other.ignore_stack_for_comparison or self.stack == other.stack))

  def __ne__(self, other):
    return not self == other

  def __hash__(self):
    return hash((self.next_token, self.column, self.paren_level,
                 self.start_of_line_level, self.lowest_level_on_line))

  def __repr__(self):
    return ('column::%d, next_token::%s, paren_level::%d, stack::[\n\t%s' %
            (self.column, repr(self.next_token), self.paren_level,
             '\n\t'.join(repr(s) for s in self.stack) + ']'))

  def CanSplit(self):
    """Returns True if the line can be split before the next token."""
    current = self.next_token

    if not current.can_break_before:
      return False

    return True

  def MustSplit(self):
    """Returns True if the line must split before the next token."""
    current = self.next_token
    previous_token = current.previous_token
    next_token = current.next_token

    if current.must_break_before:
      return True

    if (self.stack[-1].split_before_closing_bracket and
        # FIXME(morbo): Use the 'matching_bracket' instead of this.
        # FIXME(morbo): Don't forget about tuples!
        current.value in ']}'):
      # Split if we need to split before the closing bracket and the next
      # token is a closing bracket.
      return True

    if previous_token:
      length = _GetLengthToMatchingParen(previous_token)
      if (previous_token.value == '{' and  # TODO(morbo): List initializers?
          length + self.column > style.Get('COLUMN_LIMIT')):
        return True

      # TODO(morbo): This should be controlled with a knob.
      if (format_token.Subtype.DICTIONARY_KEY in current.subtypes and
          not current.is_comment):
        # Place each dictionary entry on its own line.
        return True

      # TODO(morbo): This should be controlled with a knob.
      if format_token.Subtype.DICT_SET_GENERATOR in current.subtypes:
        return True

      if (next_token and previous_token.value != '(' and
          format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in
              next_token.subtypes and
          next_token.node_split_penalty < split_penalty.UNBREAKABLE):
        return style.Get('SPLIT_BEFORE_NAMED_ASSIGNS')

      if (previous_token.value in '{[(' and
          current.lineno != previous_token.lineno):
        self.stack[-1].split_before_closing_bracket = True
        return True

    return False

  def AddTokenToState(self, newline, dry_run, must_split=False):
    """Add a token to the format decision state.

    Allow the heuristic to try out adding the token with and without a newline.
    Later on, the algorithm will determine which one has the lowest penalty.

    Arguments:
      newline: (bool) Add the token on a new line if True.
      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
        True.
      must_split: (bool) A newline was required before this token.

    Returns:
      The penalty of splitting after the current token.
    """
    penalty = 0
    if newline:
      penalty = self._AddTokenOnNewline(dry_run, must_split)
    else:
      self._AddTokenOnCurrentLine(dry_run)

    return self._MoveStateToNextToken() + penalty

  def _AddTokenOnCurrentLine(self, dry_run):
    """Puts the token on the current line.

    Appends the next token to the state and updates information necessary for
    indentation.

    Arguments:
      dry_run: (bool) Commit whitespace changes to the FormatToken if True.
    """
    current = self.next_token
    previous = current.previous_token

    spaces = current.spaces_required_before
    if not dry_run:
      current.AddWhitespacePrefix(newlines_before=0, spaces=spaces)

    if previous.OpensScope():
      if not current.is_comment:
        # Align closing scopes that are on a newline with the opening scope:
        #
        #     foo = [a,
        #            b,
        #           ]
        self.stack[-1].closing_scope_indent = previous.column
        if style.Get('ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'):
          self.stack[-1].closing_scope_indent += 1
        self.stack[-1].indent = self.column + spaces
      else:
        self.stack[-1].closing_scope_indent = (
            self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH')
        )

    self.column += spaces

  def _AddTokenOnNewline(self, dry_run, must_split):
    """Adds a line break and necessary indentation.

    Appends the next token to the state and updates information necessary for
    indentation.

    Arguments:
      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
        True.
      must_split: (bool) A newline was required before this token.

    Returns:
      The split penalty for splitting after the current state.
    """
    current = self.next_token
    previous = current.previous_token

    self.column = self._GetNewlineColumn()

    if not dry_run:
      current.AddWhitespacePrefix(newlines_before=1, spaces=self.column)

    if not current.is_comment:
      self.stack[-1].last_space = self.column
    self.start_of_line_level = self.paren_level
    self.lowest_level_on_line = self.paren_level

    # Any break on this level means that the parent level has been broken and we
    # need to avoid bin packing there.
    for paren_state in self.stack:
      paren_state.split_before_parameter = True

    if (previous.value != ',' and not previous.is_binary_op and
        not current.is_binary_op and not previous.OpensScope()):
      self.stack[-1].split_before_parameter = True

    if (previous.OpensScope() or
        (previous.is_comment and previous.previous_token is not None and
         previous.previous_token.OpensScope())):
      self.stack[-1].closing_scope_indent = max(
          0, self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
      self.stack[-1].split_before_closing_bracket = True

    # Calculate the split penalty.
    penalty = current.split_penalty

    # Add a penalty for each increasing newline we add.
    last = self.stack[-1]
    penalty += (
        style.Get('SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT') * last.num_line_splits
    )
    if not must_split and current.value not in {'if', 'for'}:
      # Don't penalize for a must split or for splitting before an
      # if-expression or list comprehension.
      last.num_line_splits += 1

    return penalty + 10

  def _GetNewlineColumn(self):
    """Return the new column on the newline."""
    current = self.next_token
    previous = current.previous_token
    top_of_stack = self.stack[-1]

    if current.spaces_required_before > 2:
      return current.spaces_required_before

    if current.OpensScope():
      return self.first_indent if not self.paren_level else top_of_stack.indent

    if current.ClosesScope():
      if (previous.OpensScope() or
          (previous.is_comment and previous.previous_token is not None and
           previous.previous_token.OpensScope())):
        return max(
            0, self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
      return top_of_stack.closing_scope_indent

    if (previous and previous.is_string and current.is_string and
        format_token.Subtype.DICTIONARY_VALUE in current.subtypes):
      return previous.column

    if format_token.Subtype.IF_TEST_EXPR in current.subtypes:
      return top_of_stack.indent + style.Get('INDENT_IF_EXPR_CONTINUATION')

    return top_of_stack.indent

  def _MoveStateToNextToken(self):
    """Calculate format decision state information and move onto the next token.

    Before moving onto the next token, we first calculate the format decision
    state given the current token and its formatting decisions. Then the format
    decision state is set up so that the next token can be added.

    Returns:
      The penalty for the number of characters over the column limit.
    """
    current = self.next_token
    if not current.OpensScope() and not current.ClosesScope():
      self.lowest_level_on_line = min(self.lowest_level_on_line,
                                      self.paren_level)

    # If we encounter an opening bracket, we add a level to our stack to prepare
    # for the subsequent tokens.
    if current.OpensScope():
      last = self.stack[-1]
      new_indent = style.Get('CONTINUATION_INDENT_WIDTH') + last.last_space

      self.stack.append(_ParenState(new_indent, self.stack[-1].last_space))
      self.stack[-1].break_before_paremeter = False
      self.paren_level += 1

    # If we encounter a closing bracket, we can remove a level from our
    # parenthesis stack.
    if len(self.stack) > 1 and current.ClosesScope():
      self.stack[-2].last_space = self.stack[-1].last_space
      self.stack.pop()
      self.paren_level -= 1

    is_multiline_string = current.is_string and '\n' in current.value
    if is_multiline_string:
      # This is a multiline string. Only look at the first line.
      self.column += len(current.value.split('\n')[0])
    else:
      self.column += len(current.value)

    self.next_token = self.next_token.next_token

    # Calculate the penalty for overflowing the column limit.
    penalty = 0
    if self.column > style.Get('COLUMN_LIMIT') and not current.is_comment:
      excess_characters = self.column - style.Get('COLUMN_LIMIT')
      penalty = style.Get('SPLIT_PENALTY_EXCESS_CHARACTER') * excess_characters

    if is_multiline_string:
      # If this is a multiline string, the column is actually the
      # end of the last line in the string.
      self.column = len(current.value.split('\n')[-1])

    return penalty

def _GetLengthToMatchingParen(token):
  """Returns the length from one bracket to the matching bracket.

  Arguments:
    token: (FormatToken) The opening bracket token.

  Returns:
    The length to the closing paren or up to the first point where we can split
    the line. The length includes the brackets.
  """
  if not token.matching_bracket:
    return 0
  end = token.matching_bracket
  while end.next_token and not end.next_token.can_break_before:
    end = end.next_token
  return end.total_length - token.total_length + 1

class _ParenState(object):
  """Maintains the state of the bracket enclosures.

  A stack of _ParenState objects are kept so that we know how to indent relative
  to the brackets.

  Attributes:
    indent: The column position to which a specified parenthesis level needs to
      be indented.
    last_space: The column position of the last space on each level.
    split_before_closing_bracket: Whether a newline needs to be inserted before
      the closing bracket. We only want to insert a newline before the closing
      bracket if there also was a newline after the beginning left bracket.
    split_before_parameter: Split the line after the next comma.
    num_line_splits: Number of line splits this _ParenState contains already.
      Each subsequent line split gets an increasing penalty.
  """

  # TODO(morbo): This doesn't track "bin packing."

  def __init__(self, indent, last_space):
    self.indent = indent
    self.last_space = last_space
    self.closing_scope_indent = 0
    self.split_before_closing_bracket = False
    self.split_before_parameter = False
    self.num_line_splits = 0

  def __repr__(self):
    return '[indent::%d, last_space::%d, closing_scope_indent::%d]' % (
        self.indent, self.last_space, self.closing_scope_indent)
<commit_msg>Fix indentation.<commit_after># Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Implements a format decision state object that manages whitespace decisions.

Each token is processed one at a time, at which point its whitespace formatting
decisions are made. A graph of potential whitespace formattings is created,
where each node in the graph is a format decision state object. The heuristic
tries formatting the token with and without a newline before it to determine
which one has the least penalty. Therefore, the format decision state object for
each decision needs to be its own unique copy.

Once the heuristic determines the best formatting, it makes a non-dry run pass
through the code to commit the whitespace formatting.

  FormatDecisionState: main class exported by this module.
"""

import copy

from yapf.yapflib import format_token
from yapf.yapflib import split_penalty
from yapf.yapflib import style

class FormatDecisionState(object):
  """The current state when indenting an unwrapped line.

  The FormatDecisionState object is meant to be copied instead of referenced.

  Attributes:
    first_indent: The indent of the first token.
    column: The number of used columns in the current line.
    next_token: The next token to be formatted.
    paren_level: The level of nesting inside (), [], and {}.
    start_of_line_level: The paren_level at the start of this line.
    lowest_level_on_line: The lowest paren_level on the current line.
    newline: Indicates if a newline is added along the edge to this format
      decision state node.
    previous: The previous format decision state in the decision tree.
    stack: A stack (of _ParenState) keeping track of properties applying to
      parenthesis levels.
    ignore_stack_for_comparison: Ignore the stack of _ParenState for state
      comparison.
  """

  def __init__(self, line, first_indent):
    """Initializer.

    Initializes to the state after placing the first token from 'line' at
    'first_indent'.

    Arguments:
      line: (UnwrappedLine) The unwrapped line we're currently processing.
      first_indent: (int) The indent of the first token.
    """
    self.next_token = line.first
    self.column = first_indent
    self.paren_level = 0
    self.start_of_line_level = 0
    self.lowest_level_on_line = 0
    self.ignore_stack_for_comparison = False
    self.stack = [_ParenState(first_indent, first_indent)]
    self.first_indent = first_indent
    self.newline = False
    self.previous = None
    self._MoveStateToNextToken()

  def Clone(self):
    new = copy.copy(self)
    new.stack = copy.deepcopy(self.stack)
    return new

  def __eq__(self, other):
    # Note: 'first_indent' is implicit in the stack. Also, we ignore 'previous',
    # because it shouldn't have a bearing on this comparison. (I.e., it will
    # report equal if 'next_token' does.)
    return (self.next_token == other.next_token and
            self.column == other.column and
            self.paren_level == other.paren_level and
            self.start_of_line_level == other.start_of_line_level and
            self.lowest_level_on_line == other.lowest_level_on_line and
            (self.ignore_stack_for_comparison or
             other.ignore_stack_for_comparison or self.stack == other.stack))

  def __ne__(self, other):
    return not self == other

  def __hash__(self):
    return hash((self.next_token, self.column, self.paren_level,
                 self.start_of_line_level, self.lowest_level_on_line))

  def __repr__(self):
    return ('column::%d, next_token::%s, paren_level::%d, stack::[\n\t%s' %
            (self.column, repr(self.next_token), self.paren_level,
             '\n\t'.join(repr(s) for s in self.stack) + ']'))

  def CanSplit(self):
    """Returns True if the line can be split before the next token."""
    current = self.next_token

    if not current.can_break_before:
      return False

    return True

  def MustSplit(self):
    """Returns True if the line must split before the next token."""
    current = self.next_token
    previous_token = current.previous_token
    next_token = current.next_token

    if current.must_break_before:
      return True

    if (self.stack[-1].split_before_closing_bracket and
        # FIXME(morbo): Use the 'matching_bracket' instead of this.
        # FIXME(morbo): Don't forget about tuples!
        current.value in ']}'):
      # Split if we need to split before the closing bracket and the next
      # token is a closing bracket.
      return True

    if previous_token:
      length = _GetLengthToMatchingParen(previous_token)
      if (previous_token.value == '{' and  # TODO(morbo): List initializers?
          length + self.column > style.Get('COLUMN_LIMIT')):
        return True

      # TODO(morbo): This should be controlled with a knob.
      if (format_token.Subtype.DICTIONARY_KEY in current.subtypes and
          not current.is_comment):
        # Place each dictionary entry on its own line.
        return True

      # TODO(morbo): This should be controlled with a knob.
      if format_token.Subtype.DICT_SET_GENERATOR in current.subtypes:
        return True

      if (next_token and previous_token.value != '(' and
          format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN in
          next_token.subtypes and
          next_token.node_split_penalty < split_penalty.UNBREAKABLE):
        return style.Get('SPLIT_BEFORE_NAMED_ASSIGNS')

      if (previous_token.value in '{[(' and
          current.lineno != previous_token.lineno):
        self.stack[-1].split_before_closing_bracket = True
        return True

    return False

  def AddTokenToState(self, newline, dry_run, must_split=False):
    """Add a token to the format decision state.

    Allow the heuristic to try out adding the token with and without a newline.
    Later on, the algorithm will determine which one has the lowest penalty.

    Arguments:
      newline: (bool) Add the token on a new line if True.
      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
        True.
      must_split: (bool) A newline was required before this token.

    Returns:
      The penalty of splitting after the current token.
    """
    penalty = 0
    if newline:
      penalty = self._AddTokenOnNewline(dry_run, must_split)
    else:
      self._AddTokenOnCurrentLine(dry_run)

    return self._MoveStateToNextToken() + penalty

  def _AddTokenOnCurrentLine(self, dry_run):
    """Puts the token on the current line.

    Appends the next token to the state and updates information necessary for
    indentation.

    Arguments:
      dry_run: (bool) Commit whitespace changes to the FormatToken if True.
    """
    current = self.next_token
    previous = current.previous_token

    spaces = current.spaces_required_before
    if not dry_run:
      current.AddWhitespacePrefix(newlines_before=0, spaces=spaces)

    if previous.OpensScope():
      if not current.is_comment:
        # Align closing scopes that are on a newline with the opening scope:
        #
        #     foo = [a,
        #            b,
        #           ]
        self.stack[-1].closing_scope_indent = previous.column
        if style.Get('ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'):
          self.stack[-1].closing_scope_indent += 1
        self.stack[-1].indent = self.column + spaces
      else:
        self.stack[-1].closing_scope_indent = (
            self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH')
        )

    self.column += spaces

  def _AddTokenOnNewline(self, dry_run, must_split):
    """Adds a line break and necessary indentation.

    Appends the next token to the state and updates information necessary for
    indentation.

    Arguments:
      dry_run: (bool) Don't commit whitespace changes to the FormatToken if
        True.
      must_split: (bool) A newline was required before this token.

    Returns:
      The split penalty for splitting after the current state.
    """
    current = self.next_token
    previous = current.previous_token

    self.column = self._GetNewlineColumn()

    if not dry_run:
      current.AddWhitespacePrefix(newlines_before=1, spaces=self.column)

    if not current.is_comment:
      self.stack[-1].last_space = self.column
    self.start_of_line_level = self.paren_level
    self.lowest_level_on_line = self.paren_level

    # Any break on this level means that the parent level has been broken and we
    # need to avoid bin packing there.
    for paren_state in self.stack:
      paren_state.split_before_parameter = True

    if (previous.value != ',' and not previous.is_binary_op and
        not current.is_binary_op and not previous.OpensScope()):
      self.stack[-1].split_before_parameter = True

    if (previous.OpensScope() or
        (previous.is_comment and previous.previous_token is not None and
         previous.previous_token.OpensScope())):
      self.stack[-1].closing_scope_indent = max(
          0, self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
      self.stack[-1].split_before_closing_bracket = True

    # Calculate the split penalty.
    penalty = current.split_penalty

    # Add a penalty for each increasing newline we add.
    last = self.stack[-1]
    penalty += (
        style.Get('SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT') * last.num_line_splits
    )
    if not must_split and current.value not in {'if', 'for'}:
      # Don't penalize for a must split or for splitting before an
      # if-expression or list comprehension.
      last.num_line_splits += 1

    return penalty + 10

  def _GetNewlineColumn(self):
    """Return the new column on the newline."""
    current = self.next_token
    previous = current.previous_token
    top_of_stack = self.stack[-1]

    if current.spaces_required_before > 2:
      return current.spaces_required_before

    if current.OpensScope():
      return self.first_indent if not self.paren_level else top_of_stack.indent

    if current.ClosesScope():
      if (previous.OpensScope() or
          (previous.is_comment and previous.previous_token is not None and
           previous.previous_token.OpensScope())):
        return max(
            0, self.stack[-1].indent - style.Get('CONTINUATION_INDENT_WIDTH'))
      return top_of_stack.closing_scope_indent

    if (previous and previous.is_string and current.is_string and
        format_token.Subtype.DICTIONARY_VALUE in current.subtypes):
      return previous.column

    if format_token.Subtype.IF_TEST_EXPR in current.subtypes:
      return top_of_stack.indent + style.Get('INDENT_IF_EXPR_CONTINUATION')

    return top_of_stack.indent

  def _MoveStateToNextToken(self):
    """Calculate format decision state information and move onto the next token.

    Before moving onto the next token, we first calculate the format decision
    state given the current token and its formatting decisions. Then the format
    decision state is set up so that the next token can be added.

    Returns:
      The penalty for the number of characters over the column limit.
    """
    current = self.next_token
    if not current.OpensScope() and not current.ClosesScope():
      self.lowest_level_on_line = min(self.lowest_level_on_line,
                                      self.paren_level)

    # If we encounter an opening bracket, we add a level to our stack to prepare
    # for the subsequent tokens.
    if current.OpensScope():
      last = self.stack[-1]
      new_indent = style.Get('CONTINUATION_INDENT_WIDTH') + last.last_space

      self.stack.append(_ParenState(new_indent, self.stack[-1].last_space))
      self.stack[-1].break_before_paremeter = False
      self.paren_level += 1

    # If we encounter a closing bracket, we can remove a level from our
    # parenthesis stack.
    if len(self.stack) > 1 and current.ClosesScope():
      self.stack[-2].last_space = self.stack[-1].last_space
      self.stack.pop()
      self.paren_level -= 1

    is_multiline_string = current.is_string and '\n' in current.value
    if is_multiline_string:
      # This is a multiline string. Only look at the first line.
      self.column += len(current.value.split('\n')[0])
    else:
      self.column += len(current.value)

    self.next_token = self.next_token.next_token

    # Calculate the penalty for overflowing the column limit.
    penalty = 0
    if self.column > style.Get('COLUMN_LIMIT') and not current.is_comment:
      excess_characters = self.column - style.Get('COLUMN_LIMIT')
      penalty = style.Get('SPLIT_PENALTY_EXCESS_CHARACTER') * excess_characters

    if is_multiline_string:
      # If this is a multiline string, the column is actually the
      # end of the last line in the string.
      self.column = len(current.value.split('\n')[-1])

    return penalty

def _GetLengthToMatchingParen(token):
  """Returns the length from one bracket to the matching bracket.

  Arguments:
    token: (FormatToken) The opening bracket token.

  Returns:
    The length to the closing paren or up to the first point where we can split
    the line. The length includes the brackets.
  """
  if not token.matching_bracket:
    return 0
  end = token.matching_bracket
  while end.next_token and not end.next_token.can_break_before:
    end = end.next_token
  return end.total_length - token.total_length + 1

class _ParenState(object):
  """Maintains the state of the bracket enclosures.

  A stack of _ParenState objects are kept so that we know how to indent relative
  to the brackets.

  Attributes:
    indent: The column position to which a specified parenthesis level needs to
      be indented.
    last_space: The column position of the last space on each level.
    split_before_closing_bracket: Whether a newline needs to be inserted before
      the closing bracket. We only want to insert a newline before the closing
      bracket if there also was a newline after the beginning left bracket.
    split_before_parameter: Split the line after the next comma.
    num_line_splits: Number of line splits this _ParenState contains already.
      Each subsequent line split gets an increasing penalty.
  """

  # TODO(morbo): This doesn't track "bin packing."

  def __init__(self, indent, last_space):
    self.indent = indent
    self.last_space = last_space
    self.closing_scope_indent = 0
    self.split_before_closing_bracket = False
    self.split_before_parameter = False
    self.num_line_splits = 0

  def __repr__(self):
    return '[indent::%d, last_space::%d, closing_scope_indent::%d]' % (
        self.indent, self.last_space, self.closing_scope_indent)
<|endoftext|>


<commit_before>"""Blog post controller."""
from slugify import slugify

from flask import (
    current_app,
    redirect,
    url_for,
    render_template,
    flash,
    request,
    abort,
    Blueprint
)
from flask_login import login_required, current_user

from yapper.lib.decorators import profile
from ..user.decorators import permission_required
from .forms import PostForm
from .models import Post
from ..user.models import Permission

BP_NM = 'blog'

blog = Blueprint(BP_NM, __name__)

@blog.route('/page/<int:page>')
@blog.route('/')
def index(page=1):
    """Show list of latest blog posts."""
    pagination = Post.query.order_by(
        Post.created_at.desc()
    ).paginate(
        page=page,
        per_page=current_app.config['POSTS_PER_PAGE'],
        error_out=False
    )
    posts = pagination.items
    return render_template(
        'blog/index.html',
        posts=posts,
        pagination=pagination,
        title='Posts' if page < 2 else 'Posts - Page '+str(page)
    )

@blog.route('/<int:pid>', methods=['GET'])
@blog.route('/<int:pid>/<string:slug>', methods=['GET'])
@profile
def get_post(pid=None, slug=None):
    """Display a blog post with given id."""
    post = Post.get(pid)
    if not post:
        return "Not found", 404
    return render_template(
        'blog/post.html',
        post=post,
        title=post.title
    )

@blog.route('/new', methods=['GET', 'POST'])
@login_required
@permission_required(Permission.WRITE_POSTS)
def add():
    """Create a new blog post."""
    form = PostForm()
    status = 200
    if form.validate_on_submit():
        post = Post(
            title=form.title.data,
            slug=slugify(form.title.data),
            description=form.description.data,
            content=form.body.data,
            author=current_user._get_current_object()
        )
        post.save()
        flash(u'Post added')
        return redirect(url_for('.get_post_by_slug', slug=post.slug))
    elif request.method == "POST":
        status = 406
    return render_template(
        'blog/add.html',
        form=form,
        title='Create New Post'), status

@blog.route('/<int:pid>/edit', methods=['GET', 'POST'])
@login_required
@permission_required(Permission.WRITE_POSTS)
def edit_post(pid=None):
    """Edit a blog post with given id."""
    post = Post.query.get_or_404(pid)
    if not (current_user.is_admin() or current_user.id == post.author.id):
        abort(403)
    form = PostForm()
    status = 200
    if form.validate_on_submit():
        post.title = form.title.data
        post.description = form.description.data
        post.content = form.body.data
        post.save()
        flash(u'Post Updated', 'success')
        return redirect(url_for('.get_post', pid=post.id))
    elif request.method == 'POST':
        status = 406
    form.title.data = post.title
    form.description.data = post.description
    form.body.data = post.body
    return render_template(
        'blog/add.html',
        form=form,
        title='Edit Post - '+post.title
    ), status

@blog.route('/<int:pid>', methods=['DELETE', 'POST'])
@login_required
def delete(pid):
    """Delete a blog post with given id."""
    post = Post.query.get_or_404(pid)
    if not (current_user.is_admin() or current_user.id == post.author.id):
        abort(403)
        return
    post.delete()
    flash('Post deleted.', 'success')
    return redirect(url_for('.index'))

# @blog.route('/tag/', methods=['GET', 'POST', 'DELETE', 'PUT'])
# @login_required
# def add_tag():
#     """Tag addition and deletion."""
#     if request.method == 'GET':
#         tag_name = request.args.get('name', '')
#         if tag_name == '':
#             return jsonify({
#                 'type': 'error',
#                 'message': 'Invalid paramater.'
#             }), 400
#         tags = Tag.query.filter_by(name=tag_name.lower()).all()
#         return jsonify({
#             'type': 'success',
#             'message': tags
#         })
#     if request.method == 'POST':
#         taglist = request.form.get('name', '')
#         if taglist == '':
#             return jsonify({
#                 'type': 'error',
#                 'message': 'Invalid paramater.'
#             }), 400
#         taglist = taglist.lower().split(',')
#         tags = []
#         for tag in taglist:
#             try:
#                 Tag.query.filter_by(name=tag).one()
#             except NoResultFound:
#                 tags.append(Tag(name=tag))
#             except MultipleResultsFound:
#                 pass
#         if len(tags) > 0:
#             Tag.save_all(tags)
#         return jsonify({
#             'type': 'success',
#             'message': tags
#         })

@blog.route('/<string:slug>', methods=['GET'])
def get_post_by_slug(slug=None):
    """Display a blog post with given slug."""
    post = Post.query.filter_by(slug=slug).first()
    if not post:
        abort(404)
    return render_template(
        'blog/post.html',
        post=post,
        title=post.title
    )
<commit_msg>Fix failing test case<commit_after>"""Blog post controller."""
from slugify import slugify

from flask import (
    current_app,
    redirect,
    url_for,
    render_template,
    flash,
    request,
    abort,
    Blueprint
)
from flask_login import login_required, current_user

from yapper.lib.decorators import profile
from ..user.decorators import permission_required
from .forms import PostForm
from .models import Post
from ..user.models import Permission

BP_NM = 'blog'

blog = Blueprint(BP_NM, __name__)

@blog.route('/page/<int:page>')
@blog.route('/')
def index(page=1):
    """Show list of latest blog posts."""
    pagination = Post.query.order_by(
        Post.created_at.desc()
    ).paginate(
        page=page,
        per_page=current_app.config['POSTS_PER_PAGE'],
        error_out=False
    )
    posts = pagination.items
    return render_template(
        'blog/index.html',
        posts=posts,
        pagination=pagination,
        title='Posts' if page < 2 else 'Posts - Page '+str(page)
    )

@blog.route('/<int:pid>', methods=['GET'])
@blog.route('/<int:pid>/<string:slug>', methods=['GET'])
@profile
def get_post(pid=None, slug=None):
    """Display a blog post with given id."""
    post = Post.get(pid)
    if not post:
        return "Not found", 404
    return render_template(
        'blog/post.html',
        post=post,
        title=post.title
    )

@blog.route('/new', methods=['GET', 'POST'])
@login_required
@permission_required(Permission.WRITE_POSTS)
def add():
    """Create a new blog post."""
    form = PostForm()
    status = 200
    if form.validate_on_submit():
        post = Post(
            title=form.title.data,
            slug=slugify(form.title.data),
            description=form.description.data,
            content=form.body.data,
            author=current_user._get_current_object()
        )
        post.save()
        flash(u'Post added')
        return redirect(url_for('.get_post_by_slug', slug=post.slug))
    elif request.method == "POST":
        status = 406
    return render_template(
        'blog/add.html',
        form=form,
        title='Create New Post'), status

@blog.route('/<int:pid>/edit', methods=['GET', 'POST'])
@login_required
@permission_required(Permission.WRITE_POSTS)
def edit_post(pid=None):
    """Edit a blog post with given id."""
    post = Post.query.get_or_404(pid)
    if not (current_user.is_admin() or current_user.id == post.author.id):
        abort(403)
    form = PostForm()
    status = 200
    if form.validate_on_submit():
        post.title = form.title.data
        post.description = form.description.data
        post.content = form.body.data
        post.save()
        flash(u'Post Updated', 'success')
        return redirect(url_for('.get_post', pid=post.id))
    elif request.method == 'POST':
        status = 406
    form.title.data = post.title
    form.description.data = post.description
    form.body.data = post.body
    return render_template(
        'blog/add.html',
        form=form,
        title='Edit Post - '+post.title
    ), status

@blog.route('/<int:pid>', methods=['DELETE', 'POST'])
@login_required
def delete(pid):
    """Delete a blog post with given id."""
    post = Post.query.get_or_404(pid)
    if not (current_user.is_admin() or current_user.id == post.author.id):
        abort(403)
        return
    post.delete()
    flash('Post deleted.', 'success')
    return redirect(url_for('.index'))

@blog.route('/<string:slug>', methods=['GET'])
def get_post_by_slug(slug=None):
    """Display a blog post with given slug."""
    post = Post.query.filter_by(slug=slug).first()
    if not post:
        abort(404)
    return render_template(
        'blog/post.html',
        post=post,
        title=post.title
    )
<|endoftext|>


<commit_before># -*- coding: utf8 -*-

import os
import time
import logging
from functools import wraps
from socket import socket, AF_INET, SOCK_DGRAM, error as SocketEror

class MetricTimer(object):
    def __init__(self, metric):
        self.metric = metric
        self.start()

    def start(self):
        """Start measuring execution time"""
        self.running = True
        self.start_time = time.time()

    def stop(self):
        """Stop timer and save execution time"""
        if self.running:
            self.metric.add_ex(time.time() - self.start_time)
            self.running = False
        else:
            raise Exception('Timer is not running')

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.running:
            self.stop()

class Metric(object):
    """
    simple_metric = Metric('foo')
    for i in range(10):
        foo(i)
        simple_metric.add(1)  # increment simple metric value
    print(metric.simple_value)  # 10

    extended_metric = Metric('bar')
    with extended_metric.timer() as timer:
        for i in range(10):
            timer.start()
            bar(i)
            timer.stop()
    print(metric.min, metric.max, metric.avg)
    # here metric.avg is sum of [t(bar(0)), ..., t(bar(9))] / 10
    #      metric.min is min of [t(bar(0)), ..., t(bar(9))]
    #      metric.max is max of [t(bar(0)), ..., t(bar(9))]

    with extended_metric.timer():
        bar(10)
        # stopping the timer with exit
    """
    def __init__(self, name):
        self.name = name
        self.simple_value = None
        self.simple_timestamp = None
        self.min, self.max = float('inf'), float('-inf')
        self.sum, self.len = 0, 0
        self.timestamp = None

    def add(self, value):
        """Add a value to a simple value stored in metric"""
        try:
            self.simple_value += value
        except TypeError:
            self.simple_value = value
        self.simple_timestamp = time.time()

    def add_ex(self, value):
        """
        Add a value to "set" of values stored in metric

        For this "set" of values we can calculate min., max., avg. values.
        """
        self.sum += value
        self.len += 1
        if value < self.min:
            self.min = value
        if value > self.max:
            self.max = value
        self.timestamp = time.time()

    @property
    def avg(self):
        return self.sum / float(self.len)

    def timer(self):
        """Get timer for measuring execution time inside context manager"""
        return MetricTimer(self)

class CarbonMetric(Metric):
    def __init__(self, name, namespace):
        super(CarbonMetric, self).__init__(name)
        self.ns = namespace
        self.name = name

    def __str__(self):
        name = '%s.%s' % (self.ns, self.name) if self.ns else self.name
        ret = ''
        if self.simple_value is not None:
            ret += '%s %s %f\n' % (name, self.simple_value,
                                   self.simple_timestamp)
        if self.len:
            ret += '\n'.join(['%s.%s %s %s' % (name, value_name,
                                               getattr(self, value_name),
                                               self.timestamp)
                              for value_name in ['min', 'avg', 'max']]) + '\n'
        return ret

class CarbonStat(object):
    """
    stat = CarbonStat(host='127.0.0.1', port=2003)

    for i in range(10):
        foo(i)
        stat['simple'].add(1)  # increment simple metric value
    print(stat['simple'].simple_value)  # 10

    with stat.timer('extended') as timer:
        for i in range(10):
            timer.start()
            bar(i)
            timer.stop()
    print(stat['extended'].min, stat['extended'].max, stat['extended'].avg)
    # here stat['extended'].avg is sum of [t(bar(0)), ..., t(bar(9))] / 10
    #      stat['extended'].min is min of [t(bar(0)), ..., t(bar(9))]
    #      stat['extended'].max is max of [t(bar(0)), ..., t(bar(9))]

    with stat.timer('extended'):
        bar(10)
        # stopping the timer with exit

    stat.send()  # send packet to Carbon
    """
    def __init__(self, host='127.0.0.1', port=2003, namespace=''):
        self.host = host
        self.port = port
        self.ns = namespace
        self.metrics = {}
        self.socket = None
        self.__heartbeat = -1
        self.__sending = False

    def __getitem__(self, name):
        """Get metric object with name `name`"""
        return self.metrics.setdefault(name, CarbonMetric(name, self.ns))

    @property
    def heartbeat(self):
        self.__heartbeat = (self.__heartbeat + 1) % 2 ** 32
        return self.__heartbeat

    def set_namespace(self, namespace):
        """Set new namespace"""
        self.ns = namespace

    def timer(self, metric_name):
        """
        Get timer for measuring execution time inside context manager

        with stat.timer('foo') as timer:
            for a in range(10):
                timer.start()
                # some your code
                timer.stop()

        stat.send()  # send info about execution of your code 10 times
        """
        return self[metric_name].timer()

    def timeit(self, metric_name):
        """
        Decorator for measuring execution time of your function

        @stat.timeit('foo')
        def foo(a):
            sleep(a)

        for a in range(10):
            foo(a)

        stat.send()  # send info about execution of `foo` function 10 times
        """
        def decorator(function):
            @wraps(function)
            def wrapped(*args, **kwargs):
                with self.timer(metric_name):
                    return function(*args, **kwargs)
            return wrapped
        return decorator

    def make_header(self):
        header = 'heartbeat {} {}\n'.format(self.heartbeat, time.time())
        if self.ns:
            header = '{}.{}'.format(self.ns, header)
        return header

    def send(self):
        """Send group of collected metrics and clear the group"""
        if self.__sending:
            return
        else:
            self.__sending = True
        if not self.socket:
            try:
                self.socket = socket(AF_INET, SOCK_DGRAM)
            except SocketEror as e:
                logging.warning('Could not open socket: %s', str(e))
                return
        metrics, self.metrics = self.metrics, {}
        packet = self.make_header() + ''.join([str(m) for m in metrics.values()])
        try:
            self.socket.sendto(packet, (self.host, self.port))
        except SocketEror as e:
            logging.error('Could not send packet: %s', str(e))
            self.metrics.update(metrics)
            self.socket = None

host = os.environ.get('CARBON_HOST', '127.0.0.1')
port = int(os.environ.get('CARBON_PORT', '2003'))
stat = CarbonStat(host=host, port=port, namespace='')
<commit_msg>Creating socket in separate function<commit_after># -*- coding: utf8 -*-

import os
import time
import logging
from functools import wraps
from socket import socket, AF_INET, SOCK_DGRAM, error as SocketEror

class MetricTimer(object):
    def __init__(self, metric):
        self.metric = metric
        self.start()

    def start(self):
        """Start measuring execution time"""
        self.running = True
        self.start_time = time.time()

    def stop(self):
        """Stop timer and save execution time"""
        if self.running:
            self.metric.add_ex(time.time() - self.start_time)
            self.running = False
        else:
            raise Exception('Timer is not running')

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.running:
            self.stop()

class Metric(object):
    """
    simple_metric = Metric('foo')
    for i in range(10):
        foo(i)
        simple_metric.add(1)  # increment simple metric value
    print(metric.simple_value)  # 10

    extended_metric = Metric('bar')
    with extended_metric.timer() as timer:
        for i in range(10):
            timer.start()
            bar(i)
            timer.stop()
    print(metric.min, metric.max, metric.avg)
    # here metric.avg is sum of [t(bar(0)), ..., t(bar(9))] / 10
    #      metric.min is min of [t(bar(0)), ..., t(bar(9))]
    #      metric.max is max of [t(bar(0)), ..., t(bar(9))]

    with extended_metric.timer():
        bar(10)
        # stopping the timer with exit
    """
    def __init__(self, name):
        self.name = name
        self.simple_value = None
        self.simple_timestamp = None
        self.min, self.max = float('inf'), float('-inf')
        self.sum, self.len = 0, 0
        self.timestamp = None

    def add(self, value):
        """Add a value to a simple value stored in metric"""
        try:
            self.simple_value += value
        except TypeError:
            self.simple_value = value
        self.simple_timestamp = time.time()

    def add_ex(self, value):
        """
        Add a value to "set" of values stored in metric

        For this "set" of values we can calculate min., max., avg. values.
        """
        self.sum += value
        self.len += 1
        if value < self.min:
            self.min = value
        if value > self.max:
            self.max = value
        self.timestamp = time.time()

    @property
    def avg(self):
        return self.sum / float(self.len)

    def timer(self):
        """Get timer for measuring execution time inside context manager"""
        return MetricTimer(self)

class CarbonMetric(Metric):
    def __init__(self, name, namespace):
        super(CarbonMetric, self).__init__(name)
        self.ns = namespace
        self.name = name

    def __str__(self):
        name = '%s.%s' % (self.ns, self.name) if self.ns else self.name
        ret = ''
        if self.simple_value is not None:
            ret += '%s %s %f\n' % (name, self.simple_value,
                                   self.simple_timestamp)
        if self.len:
            ret += '\n'.join(['%s.%s %s %s' % (name, value_name,
                                               getattr(self, value_name),
                                               self.timestamp)
                              for value_name in ['min', 'avg', 'max']]) + '\n'
        return ret

class CarbonStat(object):
    """
    stat = CarbonStat(host='127.0.0.1', port=2003)

    for i in range(10):
        foo(i)
        stat['simple'].add(1)  # increment simple metric value
    print(stat['simple'].simple_value)  # 10

    with stat.timer('extended') as timer:
        for i in range(10):
            timer.start()
            bar(i)
            timer.stop()
    print(stat['extended'].min, stat['extended'].max, stat['extended'].avg)
    # here stat['extended'].avg is sum of [t(bar(0)), ..., t(bar(9))] / 10
    #      stat['extended'].min is min of [t(bar(0)), ..., t(bar(9))]
    #      stat['extended'].max is max of [t(bar(0)), ..., t(bar(9))]

    with stat.timer('extended'):
        bar(10)
        # stopping the timer with exit

    stat.send()  # send packet to Carbon
    """
    def __init__(self, host='127.0.0.1', port=2003, namespace=''):
        self.host = host
        self.port = port
        self.ns = namespace
        self.metrics = {}
        self.__socket = None
        self.__heartbeat = -1
        self.__sending = False

    def __getitem__(self, name):
        """Get metric object with name `name`"""
        return self.metrics.setdefault(name, CarbonMetric(name, self.ns))

    @property
    def socket(self):
        if not self.__socket:
            try:
                self.__socket = socket(AF_INET, SOCK_DGRAM)
            except SocketEror as e:
                logging.error('Could not create socket: %s', str(e))
                raise

        return self.__socket

    @property
    def heartbeat(self):
        self.__heartbeat = (self.__heartbeat + 1) % 2 ** 32
        return self.__heartbeat

    def set_namespace(self, namespace):
        """Set new namespace"""
        self.ns = namespace

    def timer(self, metric_name):
        """
        Get timer for measuring execution time inside context manager

        with stat.timer('foo') as timer:
            for a in range(10):
                timer.start()
                # some your code
                timer.stop()

        stat.send()  # send info about execution of your code 10 times
        """
        return self[metric_name].timer()

    def timeit(self, metric_name):
        """
        Decorator for measuring execution time of your function

        @stat.timeit('foo')
        def foo(a):
            sleep(a)

        for a in range(10):
            foo(a)

        stat.send()  # send info about execution of `foo` function 10 times
        """
        def decorator(function):
            @wraps(function)
            def wrapped(*args, **kwargs):
                with self.timer(metric_name):
                    return function(*args, **kwargs)
            return wrapped
        return decorator

    def make_header(self):
        header = 'heartbeat {} {}\n'.format(self.heartbeat, time.time())
        if self.ns:
            header = '{}.{}'.format(self.ns, header)
        return header

    def send(self):
        """Send group of collected metrics and clear the group"""
        if self.__sending:
            return
        else:
            self.__sending = True

        metrics, self.metrics = self.metrics, {}
        packet = self.make_header() + ''.join([str(m) for m in metrics.values()])
        try:
            self.socket.sendto(packet, (self.host, self.port))
        except SocketEror as e:
            logging.error('Could not send packet to Carbon %s: %s', self.host, str(e))
            self.metrics.update(metrics)g

host = os.environ.get('CARBON_HOST', '127.0.0.1')
port = int(os.environ.get('CARBON_PORT', '2003'))
stat = CarbonStat(host=host, port=port, namespace='')
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import datetime

from django.contrib import admin
from django.db import models
from django import forms
from django.utils.safestring import mark_safe
from django.contrib.contenttypes.models import ContentType

import helpers
import settings as cart_settings
from models import Order, OrderLine, PaymentAttempt

class PaymentAttemptInline(admin.TabularInline):
    model = PaymentAttempt
    extra = 0

class OrderLineLabelWidget(forms.widgets.HiddenInput):
    def __init__(self, attrs=None, model=None, **kwargs):
        super(OrderLineLabelWidget, self).__init__(attrs)
        self.model = model
        self.is_hidden = False

    def render(self, *args, **kwargs):
        if self.model:
            text_value = getattr(self.model.product, 'order_line_description', "%s: %s" % (self.model.product_content_type.app_label.title(), self.model))
        else:
            text_value = ''

        if self.model and hasattr(self.model.product, 'get_absolute_url'):
            return mark_safe('%s%s' % (self.model.product.get_absolute_url(), text_value, super(OrderLineLabelWidget, self).render(*args, **kwargs)))
        else:
            return mark_safe('
%s
%s' % (text_value, super(OrderLineLabelWidget, self).render(*args, **kwargs)))

class ReadonlyWidget(forms.widgets.HiddenInput):
    def __init__(self, *args, **kwargs):
        super(ReadonlyWidget, self).__init__(*args, **kwargs)
        self.is_hidden = False

    def render(self, name, value, **kwargs):
        original = super(ReadonlyWidget, self).render(name, value, **kwargs)
        try:
            ctype = ContentType.objects.get(pk=value)
        except ContentType.DoesNotExist:
            return original
        else:
            return mark_safe('
%s
%s' % (ctype, original))

class OrderLineForm(forms.ModelForm):
    class Meta:
        model = OrderLine

    def __init__(self, *args, **kwargs):
        super(OrderLineForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.fields['product_object_id'].widget = OrderLineLabelWidget(model=kwargs.get('instance', None))
            self.fields['product_content_type'].widget = ReadonlyWidget()

class OrderLineInline(admin.TabularInline):
    model = OrderLine
    extra = 0
    form = OrderLineForm

class OrderAdmin(admin.ModelAdmin):
    list_display = ('id', 'total_str', 'name', 'status', 'payment_successful', 'created', 'paid', 'shipped', 'products', 'hash', )
    list_display_links = ('id', 'total_str', 'name',)
    list_filter = ('status', 'payment_successful', 'creation_date', 'completion_date')
    search_fields = ('name', 'email',)
    inlines = [OrderLineInline, PaymentAttemptInline]
    actions = ('set_status_to_shipped',)
    save_on_top = True

    def created(self, instance):
        if instance.creation_date:
            return datetime.datetime.strftime(instance.creation_date, '%Y-%m-%d')
        else:
            return 'N/A'

    def paid(self, instance):
        if instance.payment_date:
            return datetime.datetime.strftime(instance.payment_date, '%Y-%m-%d')
        else:
            return 'N/A'

    def products(self, instance):
        products = []
        for order in instance.orderline_set.all():
            if order.product not in products:
                products.append(order.product)

        return ', '.join([unicode(p) for p in products])

    def shipped(self, instance):
        if instance.completion_date:
            return datetime.datetime.strftime(instance.completion_date, '%Y-%m-%d')
        else:
            return 'N/A'
    def set_status_to_shipped(self, request, queryset):
        for item in queryset.all():
            item.status = 'shipped'
            item.save()

order_detail = helpers.get_order_detail()
if order_detail:
    class ExtraDetailInline(admin.StackedInline):
        model = order_detail
        max_num = 1
        extra = 1

    OrderAdmin.inlines = [ExtraDetailInline] + OrderAdmin.inlines

admin.site.register(Order, OrderAdmin)

<commit_msg>admin ordering<commit_after># -*- coding: utf-8 -*-

import datetime

from django.contrib import admin
from django.db import models
from django import forms
from django.utils.safestring import mark_safe
from django.contrib.contenttypes.models import ContentType

import helpers
import settings as cart_settings
from models import Order, OrderLine, PaymentAttempt

class PaymentAttemptInline(admin.TabularInline):
    model = PaymentAttempt
    extra = 0

class OrderLineLabelWidget(forms.widgets.HiddenInput):
    def __init__(self, attrs=None, model=None, **kwargs):
        super(OrderLineLabelWidget, self).__init__(attrs)
        self.model = model
        self.is_hidden = False

    def render(self, *args, **kwargs):
        if self.model:
            text_value = getattr(self.model.product, 'order_line_description', "%s: %s" % (self.model.product_content_type.app_label.title(), self.model))
        else:
            text_value = ''

        if self.model and hasattr(self.model.product, 'get_absolute_url'):
            return mark_safe('%s%s' % (self.model.product.get_absolute_url(), text_value, super(OrderLineLabelWidget, self).render(*args, **kwargs)))
        else:
            return mark_safe('
%s
%s' % (text_value, super(OrderLineLabelWidget, self).render(*args, **kwargs)))

class ReadonlyWidget(forms.widgets.HiddenInput):
    def __init__(self, *args, **kwargs):
        super(ReadonlyWidget, self).__init__(*args, **kwargs)
        self.is_hidden = False

    def render(self, name, value, **kwargs):
        original = super(ReadonlyWidget, self).render(name, value, **kwargs)
        try:
            ctype = ContentType.objects.get(pk=value)
        except ContentType.DoesNotExist:
            return original
        else:
            return mark_safe('
%s
%s' % (ctype, original))

class OrderLineForm(forms.ModelForm):
    class Meta:
        model = OrderLine

    def __init__(self, *args, **kwargs):
        super(OrderLineForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.fields['product_object_id'].widget = OrderLineLabelWidget(model=kwargs.get('instance', None))
            self.fields['product_content_type'].widget = ReadonlyWidget()

class OrderLineInline(admin.TabularInline):
    model = OrderLine
    extra = 0
    form = OrderLineForm

class OrderAdmin(admin.ModelAdmin):
    list_display = ('id', 'total_str', 'name', 'status', 'payment_successful', 'created', 'paid', 'shipped', 'products', 'hash', )
    list_display_links = ('id', 'total_str', 'name',)
    list_filter = ('status', 'payment_successful', 'creation_date', 'completion_date')
    search_fields = ('name', 'email',)
    inlines = [OrderLineInline, PaymentAttemptInline]
    actions = ('set_status_to_shipped',)
    save_on_top = True

    def created(self, instance):
        if instance.creation_date:
            return datetime.datetime.strftime(instance.creation_date, '%Y-%m-%d')
        else:
            return 'N/A'
    created.admin_order_field = 'creation_date'

    def paid(self, instance):
        if instance.payment_date:
            return datetime.datetime.strftime(instance.payment_date, '%Y-%m-%d')
        else:
            return 'N/A'
    paid.admin_order_field = 'payment_date'

    def products(self, instance):
        products = []
        for order in instance.orderline_set.all():
            if order.product not in products:
                products.append(order.product)

        return ', '.join([unicode(p) for p in products])

    def shipped(self, instance):
        if instance.completion_date:
            return datetime.datetime.strftime(instance.completion_date, '%Y-%m-%d')
        else:
            return 'N/A'
    shipped.admin_order_field = 'completion_date'

    def set_status_to_shipped(self, request, queryset):
        for item in queryset.all():
            item.status = 'shipped'
            item.save()

order_detail = helpers.get_order_detail()
if order_detail:
    class ExtraDetailInline(admin.StackedInline):
        model = order_detail
        max_num = 1
        extra = 1

    OrderAdmin.inlines = [ExtraDetailInline] + OrderAdmin.inlines

admin.site.register(Order, OrderAdmin)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from django import forms
from django.shortcuts import render_to_response
from django.template import RequestContext as Context
from django.contrib.formtools.wizard import FormWizard
from models import Order
from datetime import datetime
from decimal import Decimal

from cart import settings as cart_settings
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from api import Cart
from django.forms.formsets import formset_factory
from utils import get_order_detail_class, OrderDetailNotAvailable

product_type_queryset = ContentType.objects.filter(
    reduce(Q.__or__, [Q(app_label=t[0], model=t[1]) for t in cart_settings.PRODUCT_TYPES])
)

#print product_type_queryset, cart_settings.PRODUCT_TYPES

class AddToCartForm(forms.Form):
    product_type = forms.ModelChoiceField(
        queryset=product_type_queryset,
        widget=forms.widgets.HiddenInput()
    )
    product_id = forms.IntegerField(
        min_value=1,
        widget=forms.widgets.HiddenInput()
    )
    quantity = forms.IntegerField(min_value=1)

    def clean_product_id(self):
        if self.get_product():
            return self.cleaned_data['product_id']
        else:
            raise forms.ValidationError('Invalid product')

    def get_product(self):
        product_class = self.cleaned_data['product_type'].model_class()
        try:
            return product_class.objects.get(pk=self.cleaned_data['product_id'])
        except product_class.DoesNotExist:
            return None

    def get_options(self):
        options = {}
        for field in self.cleaned_data:
            if field not in ['product_id', 'product_type', 'quantity']:
                options[field] = self.cleaned_data[field]
        return options

    def get_quantity(self):
        return self.cleaned_data['quantity']

    def add(self, request):
        Cart(request).add(self.get_product(), self.cleaned_data['quantity'], self.get_options())

    def __init__(self, *args, **kwargs):
        single = kwargs.pop('single', False)
        product_instance = kwargs.pop('product_instance', False)
        returnval = super(AddToCartForm, self).__init__(*args, **kwargs)

        if single:
            self.fields['quantity'].initial = 1
            self.fields['quantity'].widget = forms.widgets.HiddenInput()
        if product_instance:
            self.fields['product_id'].initial = product_instance.id
            self.fields['product_type'].initial = ContentType.objects.get_for_model(product_instance).id

        return returnval

def checkout_form_factory():
    try:
        order_detail_cls = get_order_detail_class()
    except OrderDetailNotAvailable:
        order_detail_cls = None

    if order_detail_cls and cart_settings.CHECKOUT_FORM_FIELDS:
        class CheckoutForm(forms.ModelForm):
            class Meta:
                model = order_detail_cls
                fields = cart_settings.CHECKOUT_FORM_FIELDS

            def update(self, cart):
                for name in self.cleaned_data:
                    cart.detail_data[name] = self.cleaned_data[name]
                cart.modified()

        return CheckoutForm
    else:
        class DummyForm(forms.Form):
            def update(self, cart):
                pass
        return DummyForm

def shipping_options_form_factory(cart):
    class ShippingOptionsForm(forms.Form):
        def update(self, cart):
            for name in self.cleaned_data:
                cart.shipping_options[name] = self.cleaned_data[name]
            cart.modified()

    for option_tuple in cart.get_available_shipping_options():
        if option_tuple[2]:
            field = forms.ChoiceField(label=option_tuple[1], choices=option_tuple[2])
        else:
            field = forms.CharField(label=option_tuple[1])
        ShippingOptionsForm.base_fields[option_tuple[0]] = field

    return ShippingOptionsForm

class OrderForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ('name', 'email', 'phone', 'street_address', 'suburb', 'city', 'post_code', 'country')

    def clean(self):
        if not self.cleaned_data.get('email', None) and not self.cleaned_data.get('phone', None):
            self._errors['email'] = forms.util.ErrorList(['Please enter an email address.'])
        return self.cleaned_data

OrderForm.base_fields['suburb'].required = False
OrderForm.base_fields['email'].required = True

def order_detail_form_factory():
    try:
        model_cls = get_order_detail_class()
        class OrderDetailForm(forms.ModelForm):
            class Meta:
                model = model_cls
                exclude = ['order'] + (cart_settings.CHECKOUT_FORM_FIELDS or [])
        return OrderDetailForm
    except OrderDetailNotAvailable:
        # dummy form class with no fields, to simplify the view
        return forms.Form<commit_msg>DummyForm class added as forms.Form was spitting the dummy<commit_after># -*- coding: utf-8 -*-

from django import forms
from django.shortcuts import render_to_response
from django.template import RequestContext as Context
from django.contrib.formtools.wizard import FormWizard
from models import Order
from datetime import datetime
from decimal import Decimal

from cart import settings as cart_settings
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from api import Cart
from django.forms.formsets import formset_factory
from utils import get_order_detail_class, OrderDetailNotAvailable

product_type_queryset = ContentType.objects.filter(
    reduce(Q.__or__, [Q(app_label=t[0], model=t[1]) for t in cart_settings.PRODUCT_TYPES])
)

#print product_type_queryset, cart_settings.PRODUCT_TYPES

class AddToCartForm(forms.Form):
    product_type = forms.ModelChoiceField(
        queryset=product_type_queryset,
        widget=forms.widgets.HiddenInput()
    )
    product_id = forms.IntegerField(
        min_value=1,
        widget=forms.widgets.HiddenInput()
    )
    quantity = forms.IntegerField(min_value=1)

    def clean_product_id(self):
        if self.get_product():
            return self.cleaned_data['product_id']
        else:
            raise forms.ValidationError('Invalid product')

    def get_product(self):
        product_class = self.cleaned_data['product_type'].model_class()
        try:
            return product_class.objects.get(pk=self.cleaned_data['product_id'])
        except product_class.DoesNotExist:
            return None

    def get_options(self):
        options = {}
        for field in self.cleaned_data:
            if field not in ['product_id', 'product_type', 'quantity']:
                options[field] = self.cleaned_data[field]
        return options

    def get_quantity(self):
        return self.cleaned_data['quantity']

    def add(self, request):
        Cart(request).add(self.get_product(), self.cleaned_data['quantity'], self.get_options())

    def __init__(self, *args, **kwargs):
        single = kwargs.pop('single', False)
        product_instance = kwargs.pop('product_instance', False)
        returnval = super(AddToCartForm, self).__init__(*args, **kwargs)

        if single:
            self.fields['quantity'].initial = 1
            self.fields['quantity'].widget = forms.widgets.HiddenInput()
        if product_instance:
            self.fields['product_id'].initial = product_instance.id
            self.fields['product_type'].initial = ContentType.objects.get_for_model(product_instance).id

        return returnval

def checkout_form_factory():
    try:
        order_detail_cls = get_order_detail_class()
    except OrderDetailNotAvailable:
        order_detail_cls = None

    if order_detail_cls and cart_settings.CHECKOUT_FORM_FIELDS:
        class CheckoutForm(forms.ModelForm):
            class Meta:
                model = order_detail_cls
                fields = cart_settings.CHECKOUT_FORM_FIELDS

            def update(self, cart):
                for name in self.cleaned_data:
                    cart.detail_data[name] = self.cleaned_data[name]
                cart.modified()

        return CheckoutForm
    else:
        class DummyForm(forms.Form):
            def update(self, cart):
                pass
        return DummyForm

def shipping_options_form_factory(cart):
    class ShippingOptionsForm(forms.Form):
        def update(self, cart):
            for name in self.cleaned_data:
                cart.shipping_options[name] = self.cleaned_data[name]
            cart.modified()

    for option_tuple in cart.get_available_shipping_options():
        if option_tuple[2]:
            field = forms.ChoiceField(label=option_tuple[1], choices=option_tuple[2])
        else:
            field = forms.CharField(label=option_tuple[1])
        ShippingOptionsForm.base_fields[option_tuple[0]] = field

    return ShippingOptionsForm

class OrderForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ('name', 'email', 'phone', 'street_address', 'suburb', 'city', 'post_code', 'country')

    def clean(self):
        if not self.cleaned_data.get('email', None) and not self.cleaned_data.get('phone', None):
            self._errors['email'] = forms.util.ErrorList(['Please enter an email address.'])
        return self.cleaned_data

OrderForm.base_fields['suburb'].required = False
OrderForm.base_fields['email'].required = True

def order_detail_form_factory():
    try:
        model_cls = get_order_detail_class()
        class OrderDetailForm(forms.ModelForm):
            class Meta:
                model = model_cls
                exclude = ['order'] + (cart_settings.CHECKOUT_FORM_FIELDS or [])
        return OrderDetailForm
    except OrderDetailNotAvailable:
        # dummy form class with no fields, to simplify the view
        class DummyForm(forms.Form):
            def __init__(self, instance, *args, **kwargs):
                super(DummyForm, self).__init__(*args, **kwargs)
        return DummyForm<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import logging

from s3backup.clients import SyncAction

logger = logging.getLogger('s3backup')

class DeferredFunction(object):
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __call__(self):
        self.func(*self.args, **self.kwargs)

    def __repr__(self):
        return 'DeferredFunction<{self.func}, {self.args}, {self.kwargs}>'.format(self)

def update_client(to_client, from_client, key, timestamp):
    logger.info('UPDATING %s on %s to %s version', key, to_client, from_client)
    to_client.put(key, from_client.get(key))
    to_client.set_remote_timestamp(key, timestamp)
    from_client.set_remote_timestamp(key, timestamp)

def delete_client(client, key, remote_timestamp):
    logger.info('DELETING %s on %s at timestamp %s ', key, client, remote_timestamp)
    client.delete(key)
    client.set_remote_timestamp(key, remote_timestamp)

def get_actions(client_1, client_2):
    keys_1 = client_1.get_all_keys()
    keys_2 = client_2.get_all_keys()

    all_keys = set(keys_1) | set(keys_2)
    for key in all_keys:
        action_1 = client_1.get_action(key)
        action_2 = client_2.get_action(key)
        yield key, action_1, action_2

def sync(client_1, client_2):
    # we store a list of deferred calls to make sure we can handle everything before
    # running any updates on the file system and indexes
    deferred_calls = []

    for key, action_1, action_2 in get_actions(client_1, client_2):
        if action_1.action == SyncAction.NONE and action_2.action == SyncAction.NONE:
            if action_1.timestamp == action_2.timestamp:
                continue
            elif action_1.timestamp is None and action_2.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
                )
            elif action_2.timestamp is None and action_1.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
                )
            elif action_1.timestamp > action_2.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
                )
            elif action_2.timestamp > action_1.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
                )

        elif action_1.action == SyncAction.UPDATED and action_2.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
            )

        elif action_2.action == SyncAction.UPDATED and action_1.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
            )
        elif action_1.action == SyncAction.DELETED and action_2.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(delete_client, client_2, key, action_1.timestamp)
            )

        elif action_2.action == SyncAction.DELETED and action_1.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(delete_client, client_1, key, action_2.timestamp)
            )

        elif action_1.action == SyncAction.DELETED and action_2.action == SyncAction.DELETED:
            # nothing to do
            continue

        # TODO: Check DELETE timestamp. if it is older than you should be able to safely ignore it

        else:
            raise ValueError(
                'Unhandled state, aborting before anything is updated',
                key, client_1, action_1, client_2, action_2
            )

    # call everything once we know we can handle all of it
    # TODO: Should probably catch any exception and update the index anyway here
    for deferred_function in deferred_calls:
        deferred_function()

    if len(deferred_calls) > 0:
        logger.info('Updating Index')
        client_1.update_index()
        client_2.update_index()
    else:
        logger.info('Nothing to update')
<commit_msg>Log deferred functions in debug messages<commit_after># -*- coding: utf-8 -*-

import logging

from s3backup.clients import SyncAction

logger = logging.getLogger('s3backup')

class DeferredFunction(object):
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __call__(self):
        self.func(*self.args, **self.kwargs)

    def __repr__(self):
        return 'DeferredFunction<func{func}, args={args}, kwargs={kwargs}>'.format(
            func=self.func,
            args=self.args,
            kwargs=self.kwargs,
        )

def update_client(to_client, from_client, key, timestamp):
    logger.info('UPDATING %s on %s to %s version', key, to_client, from_client)
    to_client.put(key, from_client.get(key))
    to_client.set_remote_timestamp(key, timestamp)
    from_client.set_remote_timestamp(key, timestamp)

def delete_client(client, key, remote_timestamp):
    logger.info('DELETING %s on %s at timestamp %s ', key, client, remote_timestamp)
    client.delete(key)
    client.set_remote_timestamp(key, remote_timestamp)

def get_actions(client_1, client_2):
    keys_1 = client_1.get_all_keys()
    keys_2 = client_2.get_all_keys()

    all_keys = set(keys_1) | set(keys_2)
    for key in all_keys:
        action_1 = client_1.get_action(key)
        action_2 = client_2.get_action(key)
        yield key, action_1, action_2

def sync(client_1, client_2):
    # we store a list of deferred calls to make sure we can handle everything before
    # running any updates on the file system and indexes
    deferred_calls = []

    for key, action_1, action_2 in get_actions(client_1, client_2):
        if action_1.action == SyncAction.NONE and action_2.action == SyncAction.NONE:
            if action_1.timestamp == action_2.timestamp:
                continue
            elif action_1.timestamp is None and action_2.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
                )
            elif action_2.timestamp is None and action_1.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
                )
            elif action_1.timestamp > action_2.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
                )
            elif action_2.timestamp > action_1.timestamp:
                deferred_calls.append(
                    DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
                )

        elif action_1.action == SyncAction.UPDATED and action_2.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(update_client, client_2, client_1, key, action_1.timestamp)
            )

        elif action_2.action == SyncAction.UPDATED and action_1.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(update_client, client_1, client_2, key, action_2.timestamp)
            )
        elif action_1.action == SyncAction.DELETED and action_2.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(delete_client, client_2, key, action_1.timestamp)
            )

        elif action_2.action == SyncAction.DELETED and action_1.action == SyncAction.NONE:
            deferred_calls.append(
                DeferredFunction(delete_client, client_1, key, action_2.timestamp)
            )

        elif action_1.action == SyncAction.DELETED and action_2.action == SyncAction.DELETED:
            # nothing to do
            continue

        # TODO: Check DELETE timestamp. if it is older than you should be able to safely ignore it

        else:
            raise ValueError(
                'Unhandled state, aborting before anything is updated',
                key, client_1, action_1, client_2, action_2
            )

    # call everything once we know we can handle all of it
    # TODO: Should probably catch any exception and update the index anyway here
    logger.debug('Deferred calls: %s', deferred_calls)
    for deferred_function in deferred_calls:
        deferred_function()

    if len(deferred_calls) > 0:
        logger.info('Updating Index')
        client_1.update_index()
        client_2.update_index()
    else:
        logger.info('Nothing to update')
<|endoftext|>


<commit_before>VERSION="0.5.7.5"
<commit_msg>Version bump<commit_after>VERSION="0.5.7.6"
<|endoftext|>


<commit_before>VERSION="0.5.8.2"
<commit_msg>Version bump<commit_after>VERSION="0.5.8.3"
<|endoftext|>


<commit_before>from django.urls import reverse
from tests.BaseTestWithDB import BaseTestWithDB
from tests.resources.ResourcesTestDataGenerator import ResourcesTestDataGenerator
from utils.create_query_string import query_string

class ModuloClockResourceViewTest(BaseTestWithDB):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.test_data = ResourcesTestDataGenerator()
        self.language = "en"

    def test_modulo_clock_resource_form_view(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:resource", kwargs=kwargs)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)

    def test_modulo_clock_resource_generation_valid_configurations(self):
        pass

    def test_modulo_clock_resource_generation_missing_modulo_number_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "paper_size": "a4",
            "header_text": "",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(404, response.status_code)

    def test_modulo_clock_resource_generation_missing_paper_size_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "header_text": "",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(404, response.status_code)

    def test_modulo_clock_resource_generation_missing_header_text_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "paper_size": "a4",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (2 - a4).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )
<commit_msg>Tests valid parameter combinations<commit_after>from django.urls import reverse

from tests.BaseTestWithDB import BaseTestWithDB
from tests.resources.ResourcesTestDataGenerator import ResourcesTestDataGenerator

from utils.create_query_string import query_string

class ModuloClockResourceViewTest(BaseTestWithDB):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.test_data = ResourcesTestDataGenerator()
        self.language = "en"

    def test_modulo_clock_resource_form_view(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:resource", kwargs=kwargs)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)

    def test_modulo_clock_resource_generation_2_a4(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "paper_size": "a4",
            "header_text": ""
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (2 - a4).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )

    def test_modulo_clock_resource_generation_2_letter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "paper_size": "letter",
            "header_text": ""
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (2 - letter).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )

    def test_modulo_clock_resource_generation_10_a4(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "10",
            "paper_size": "a4",
            "header_text": ""
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (10 - a4).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )

    def test_modulo_clock_resource_generation_10_letter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "10",
            "paper_size": "letter",
            "header_text": ""
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (10 - letter).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )

    def test_modulo_clock_resource_generation_missing_modulo_number_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "paper_size": "a4",
            "header_text": "",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(404, response.status_code)

    def test_modulo_clock_resource_generation_missing_paper_size_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "header_text": "",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(404, response.status_code)

    def test_modulo_clock_resource_generation_missing_header_text_parameter(self):
        resource = self.test_data.create_test_resource(
            "modulo-clock",
            "Modulo Clock",
            "resources/modulo-clock.html",
            "modulo_clock.py",
        )
        kwargs = {
            "resource_slug": resource.slug,
        }
        url = reverse("resources:generate", kwargs=kwargs)
        get_parameters = {
            "modulo_number": "2",
            "paper_size": "a4",
        }
        url += query_string(get_parameters)
        response = self.client.get(url)
        self.assertEqual(200, response.status_code)
        filename = "Resource Modulo Clock (2 - a4).pdf"
        self.assertEqual(
            response.get("Content-Disposition"),
            'attachment; filename="{}"'.format(filename)
        )
<|endoftext|>