<commit_before>import logging

from bumper import BumperDriver

from workspace.commands import AbstractCommand
from workspace.commands.helpers import expand_product_groups
from workspace.commands.review import Review
from workspace.config import config
from workspace.scm import repo_check, is_git_repo, product_name, current_branch

log = logging.getLogger(__name__)

class Bump(AbstractCommand):
  """
    Bump dependency versions in requirements.txt, pinned.txt, or any specified file.

    :param str names: Only bump dependencies that match the name.
                      Name can be a product group name defined in workspace.cfg.
                      To bump to a specific version instead of latest, append version to name
                      (i.e. requests==1.2.3 or 'requests>=1.2.3'). When > or < is used, be sure to quote.
    :param int test: Run tests and include results in RB.
    :param bool push: Wait for 'Ship It' from RB (unless --skip-rb is used) and push the bump (git only)
    :param bool skip_rb: Skip creating or updating RB
    :param bool add: Add the `names` to the requirements file if they don't exist.
    :param str msg: Summary commit message
    :param str/list file: Requirement file to bump. Defaults to requirements.txt or pinned.txt
                          that are set by bump.requirement_files in workspace.cfg.
    :param dict bumper_models: List of classes that implements :class:`bumper.cars.AbstractBumper`
                               Defaults to :class:`bumper.cars.RequirementsBumper`
    :param bool force: Force a bump even when certain bump requirements are not met.
    :param bool dry_run: Performs a dry run by printing out the changes instead of committing/creating an rb
    :param dict kwargs: Additional args from argparse
  """

  def __init__(self, *args, **kwargs):
    kwargs.setdefault('show_filter', True)
    super(Bump, self).__init__(*args, **kwargs)

  @classmethod
  def arguments(cls):
    _, docs = cls.docs()
    return ([
        cls.make_args('names', nargs='*', help=docs['names']),
        cls.make_args('-s', '--skip-rb', action='store_true', help=docs['skip_rb']),
        cls.make_args('--add', action='store_true', help=docs['add']),
        cls.make_args('--force', action='store_true', help=docs['force']),
        cls.make_args('-m', '--msg', help=docs['msg']),
        cls.make_args('--file', help=docs['file']),
        cls.make_args('-n', '--dry-run', action='store_true', help=docs['dry_run'])
      ], [
        cls.make_args('-t', '--test', action='store_true', help=docs['test']),
        cls.make_args('-p', '--push', action='store_true', help=docs['push']),
      ])

  def run(self):
    """
      :return: Tuple with 3 elements: A map of file to bump message, commit message, and list of :class:`Bump`
    """
    repo_check()

    self.commander.run('update', raises=True)

    config.commit.auto_branch_from_commit_words = 1

    if not self.names:
      self.names = []

    filter_requirements = expand_product_groups(self.names)

    if self.show_filter and filter_requirements:
      log.info('Only bumping: %s', ' '.join(filter_requirements))

    if isinstance(self.file, list):
      requirement_files = self.file
    elif self.file:
      requirement_files = [self.file]
    else:
      requirement_files = config.bump.requirement_files.strip().split()

    bumper = BumperDriver(requirement_files, bumper_models=self.bumper_models, full_throttle=self.force, detail=True, test_drive=self.dry_run)
    messages, bumps = bumper.bump(filter_requirements, required=self.add, show_summary=not is_git_repo())
    commit_msg = None

    try:
      if messages:
        summary_msgs = []
        detail_msgs = []
        for m in sorted(messages.values()):
          splits = m.split('\n', 1)
          summary_msgs.append(splits[0])
          if len(splits) == 2:
            detail_msgs.append(splits[1])

        commit_msg = '\n\n'.join(summary_msgs + detail_msgs)

        if self.msg:
          commit_msg = self.msg + '\n\n' + commit_msg

        if not self.dry_run and is_git_repo():
          self.commander.run('commit', msg=commit_msg, files=messages.keys())

    except Exception:
      bumper.reverse()
      raise

    if bumps:
      tests = {}

      if self.test:
        log.info('Running tests')
        tests[product_name()] = self.commander.run('test', return_output=not self.skip_rb, skip_editable_install=True)

      if not self.dry_run:
        if not self.skip_rb and commit_msg and self.commander.command('review') != Review:
          reviewer_groups = set()
          reviewers = set()

          for lib in bumps.keys():
            g, r = self.commander.command('review').reviewers_for_product(lib)
            if g:
              reviewer_groups.update(g)
            if r:
              reviewers.update(r)
            log.debug('Reviewers for %s: %s %s', lib, g, r)

          self.commander.run('review', publish=self.push, files=messages.keys(), description=commit_msg, test=tests,
                             skip_prereview=True, reviewer_groups=reviewer_groups, reviewers=reviewers)

        if self.push and is_git_repo():
          if not self.skip_rb:
            branch = current_branch()
            self.commander.run('wait', review=True, in_background=True)
          self.commander.run('push', branch=branch)

    return messages, commit_msg, bumps
<commit_msg>Ensure branch is assigned before use<commit_after>import logging

from bumper import BumperDriver

from workspace.commands import AbstractCommand
from workspace.commands.helpers import expand_product_groups
from workspace.commands.review import Review
from workspace.config import config
from workspace.scm import repo_check, is_git_repo, product_name, current_branch

log = logging.getLogger(__name__)

class Bump(AbstractCommand):
  """
    Bump dependency versions in requirements.txt, pinned.txt, or any specified file.

    :param str names: Only bump dependencies that match the name.
                      Name can be a product group name defined in workspace.cfg.
                      To bump to a specific version instead of latest, append version to name
                      (i.e. requests==1.2.3 or 'requests>=1.2.3'). When > or < is used, be sure to quote.
    :param int test: Run tests and include results in RB.
    :param bool push: Wait for 'Ship It' from RB (unless --skip-rb is used) and push the bump (git only)
    :param bool skip_rb: Skip creating or updating RB
    :param bool add: Add the `names` to the requirements file if they don't exist.
    :param str msg: Summary commit message
    :param str/list file: Requirement file to bump. Defaults to requirements.txt or pinned.txt
                          that are set by bump.requirement_files in workspace.cfg.
    :param dict bumper_models: List of classes that implements :class:`bumper.cars.AbstractBumper`
                               Defaults to :class:`bumper.cars.RequirementsBumper`
    :param bool force: Force a bump even when certain bump requirements are not met.
    :param bool dry_run: Performs a dry run by printing out the changes instead of committing/creating an rb
    :param dict kwargs: Additional args from argparse
  """

  def __init__(self, *args, **kwargs):
    kwargs.setdefault('show_filter', True)
    super(Bump, self).__init__(*args, **kwargs)

  @classmethod
  def arguments(cls):
    _, docs = cls.docs()
    return ([
        cls.make_args('names', nargs='*', help=docs['names']),
        cls.make_args('-s', '--skip-rb', action='store_true', help=docs['skip_rb']),
        cls.make_args('--add', action='store_true', help=docs['add']),
        cls.make_args('--force', action='store_true', help=docs['force']),
        cls.make_args('-m', '--msg', help=docs['msg']),
        cls.make_args('--file', help=docs['file']),
        cls.make_args('-n', '--dry-run', action='store_true', help=docs['dry_run'])
      ], [
        cls.make_args('-t', '--test', action='store_true', help=docs['test']),
        cls.make_args('-p', '--push', action='store_true', help=docs['push']),
      ])

  def run(self):
    """
      :return: Tuple with 3 elements: A map of file to bump message, commit message, and list of :class:`Bump`
    """
    repo_check()

    self.commander.run('update', raises=True)

    config.commit.auto_branch_from_commit_words = 1

    if not self.names:
      self.names = []

    filter_requirements = expand_product_groups(self.names)

    if self.show_filter and filter_requirements:
      log.info('Only bumping: %s', ' '.join(filter_requirements))

    if isinstance(self.file, list):
      requirement_files = self.file
    elif self.file:
      requirement_files = [self.file]
    else:
      requirement_files = config.bump.requirement_files.strip().split()

    bumper = BumperDriver(requirement_files, bumper_models=self.bumper_models, full_throttle=self.force, detail=True, test_drive=self.dry_run)
    messages, bumps = bumper.bump(filter_requirements, required=self.add, show_summary=not is_git_repo())
    commit_msg = None

    try:
      if messages:
        summary_msgs = []
        detail_msgs = []
        for m in sorted(messages.values()):
          splits = m.split('\n', 1)
          summary_msgs.append(splits[0])
          if len(splits) == 2:
            detail_msgs.append(splits[1])

        commit_msg = '\n\n'.join(summary_msgs + detail_msgs)

        if self.msg:
          commit_msg = self.msg + '\n\n' + commit_msg

        if not self.dry_run and is_git_repo():
          self.commander.run('commit', msg=commit_msg, files=messages.keys())

    except Exception:
      bumper.reverse()
      raise

    if bumps:
      tests = {}

      if self.test:
        log.info('Running tests')
        tests[product_name()] = self.commander.run('test', return_output=not self.skip_rb, skip_editable_install=True)

      if not self.dry_run:
        if not self.skip_rb and commit_msg and self.commander.command('review') != Review:
          reviewer_groups = set()
          reviewers = set()

          for lib in bumps.keys():
            g, r = self.commander.command('review').reviewers_for_product(lib)
            if g:
              reviewer_groups.update(g)
            if r:
              reviewers.update(r)
            log.debug('Reviewers for %s: %s %s', lib, g, r)

          self.commander.run('review', publish=self.push, files=messages.keys(), description=commit_msg, test=tests,
                             skip_prereview=True, reviewer_groups=reviewer_groups, reviewers=reviewers)

        if self.push and is_git_repo():
          branch = current_branch()
          if not self.skip_rb:
            self.commander.run('wait', review=True, in_background=True)
          self.commander.run('push', branch=branch)

    return messages, commit_msg, bumps
<|endoftext|>


<commit_before>"""
DIMACS

For more information on the input formats,
see "Satisfiability Suggested Format",
published May 1993 by the Rutgers Center for Discrete Mathematics (DIMACS).

Also, see the proceedings of the International SAT Competition
(http://www.satcompetition.org) for information and CNF examples.

Exceptions:
    DIMACSError

Interface Functions:
    parse_cnf
    parse_sat
"""

# pylint: disable=C0103

from pyeda.parsing.lex import RegexLexer, action
from pyeda.parsing.token import (
    KeywordToken, IntegerToken, OperatorToken, PunctuationToken,
)

class DIMACSError(Exception):
    """An error happened during parsing a DIMACS file"""
    def __init__(self, msg):
        super(DIMACSError, self).__init__(msg)

# Keywords
class KW_p(KeywordToken):
    """DIMACS 'p' preamble token"""

class KW_cnf(KeywordToken):
    """DIMACS 'cnf' token"""

class KW_sat(KeywordToken):
    """DIMACS 'sat' token"""

class KW_satx(KeywordToken):
    """DIMACS 'satx' token"""

class KW_sate(KeywordToken):
    """DIMACS 'sate' token"""

class KW_satex(KeywordToken):
    """DIMACS 'satex' token"""

# Operators
class OP_not(OperatorToken):
    """DIMACS '-' operator"""
    ASTOP = 'not'

class OP_or(OperatorToken):
    """DIMACS '+' operator"""
    ASTOP = 'or'

class OP_and(OperatorToken):
    """DIMACS '*' operator"""
    ASTOP = 'and'

class OP_xor(OperatorToken):
    """DIMACS 'xor' operator"""
    ASTOP = 'xor'

class OP_equal(OperatorToken):
    """DIMACS '=' operator"""
    ASTOP = 'equal'

# Punctuation
class LPAREN(PunctuationToken):
    """DIMACS '(' token"""

class RPAREN(PunctuationToken):
    """DIMACS ')' token"""

class CNFLexer(RegexLexer):
    """Lexical analysis of CNF strings"""

    def ignore(self, text):
        """Ignore this text."""

    def keyword(self, text):
        """Push a keyword onto the token queue."""
        cls = self.KEYWORDS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def operator(self, text):
        """Push an operator onto the token queue."""
        cls = self.OPERATORS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    @action(IntegerToken)
    def integer(self, text):
        """Push an integer onto the token queue."""
        return int(text)

    RULES = {
        'root': [
            (r"c.*\n", ignore),
            (r"\bp\b", keyword, 'preamble'),
        ],
        'preamble': [
            (r"[ \t]+", ignore),
            (r"\bcnf\b", keyword),
            (r"\d+", integer),
            (r"\n", ignore, 'formula'),
        ],
        'formula': [
            (r"\s+", ignore),
            (r"-", operator),
            (r"\d+", integer),
        ],
    }

    KEYWORDS = {
        'p': KW_p,
        'cnf': KW_cnf,
    }

    OPERATORS = {
        '-': OP_not,
    }

def _expect_token(lex, types):
    """Return the next token, or raise an exception."""
    tok = next(lex)
    if any(type(tok) is t for t in types):
        return tok
    else:
        raise DIMACSError("unexpected token: " + str(tok))

def parse_cnf(s, varname='x'):
    """
    Parse an input string in DIMACS CNF format,
    and return an expression.
    """
    lex = iter(CNFLexer(s))

    _expect_token(lex, {KW_p})
    _expect_token(lex, {KW_cnf})
    nvars = _expect_token(lex, {IntegerToken}).value
    nclauses = _expect_token(lex, {IntegerToken}).value

    return _cnf_formula(lex, varname, nvars, nclauses)

def _cnf_formula(lex, varname, nvars, nclauses):
    """Return a DIMACS CNF formula."""
    clauses = list()
    while True:
        try:
            tok = _expect_token(lex, {OP_not, IntegerToken})
        except StopIteration:
            break
        lex.unpop_token(tok)
        clauses.append(_cnf_clause(lex, varname, nvars))

    if len(clauses) < nclauses:
        fstr = "formula has fewer than {} clauses"
        raise DIMACSError(fstr.format(nclauses))
    elif len(clauses) > nclauses:
        fstr = "formula has more than {} clauses"
        raise DIMACSError(fstr.format(nclauses))

    return ('and', ) + tuple(clauses)

def _cnf_clause(lex, varname, nvars):
    """Return a DIMACS CNF clause."""
    clause = list()
    tok = _expect_token(lex, {OP_not, IntegerToken})
    while not (type(tok) is IntegerToken and tok.value == 0):
        if type(tok) is OP_not:
            neg = True
            tok = _expect_token(lex, {IntegerToken})
            index = tok.value
        else:
            neg = False
            index = tok.value
        if index > nvars:
            fstr = "formula literal {} is greater than {}"
            raise DIMACSError(fstr.format(index, nvars))
        if neg:
            clause.append((OP_not.ASTOP, ('var', (varname, ), (index, ))))
        else:
            clause.append(('var', (varname, ), (index, )))
        try:
            tok = _expect_token(lex, {OP_not, IntegerToken})
        except StopIteration:
            raise DIMACSError("incomplete clause")

    return ('or', ) + tuple(clause)

class SATLexer(RegexLexer):
    """Lexical analysis of SAT strings"""

    def ignore(self, text):
        """Ignore this text."""

    def keyword(self, text):
        """Push a keyword onto the token queue."""
        cls = self.KEYWORDS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def operator(self, text):
        """Push an operator onto the token queue."""
        cls = self.OPERATORS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def punct(self, text):
        """Push punctuation onto the token queue."""
        cls = self.PUNCTUATION[text]
        self.push_token(cls(text, self.lineno, self.offset))

    @action(IntegerToken)
    def integer(self, text):
        """Push an integer onto the token queue."""
        return int(text)

    RULES = {
        'root': [
            (r"c.*\n", ignore),
            (r"\bp\b", keyword, 'preamble'),
        ],
        'preamble': [
            (r"[ \t]+", ignore),
            (r"\bsat\b", keyword),
            (r"\bsatx\b", keyword),
            (r"\bsate\b", keyword),
            (r"\bsatex\b", keyword),
            (r"\d+", integer),
            (r"\n", ignore, 'formula'),
        ],
        'formula': [
            (r"\s+", ignore),
            (r"\+", operator),
            (r"\-", operator),
            (r"\*", operator),
            (r"\bxor\b", operator),
            (r"=", operator),
            (r"\(", punct),
            (r"\)", punct),
            (r"\d+", integer),
        ],
    }

    KEYWORDS = {
        'p': KW_p,
        'sat': KW_sat,
        'satx': KW_satx,
        'sate': KW_sate,
        'satex': KW_satex,
    }

    OPERATORS = {
        '-': OP_not,
        '+': OP_or,
        '*': OP_and,
        'xor': OP_xor,
        '=': OP_equal,
    }

    PUNCTUATION = {
        '(': LPAREN,
        ')': RPAREN,
    }

# Grammar for a SAT file
#
# SAT := COMMENT* PREAMBLE FORMULA
#
# COMMENT := 'c' .* '\n'
#
# PREAMBLE := 'p' FORMAT VARIABLES '\n'
#
# FORMAT := 'sat' | 'satx' | 'sate' | 'satex'
#
# VARIABLES := INT
#
# FORMULA := INT
#          | '-' INT
#          | '(' FORMULA ')'
#          | '-' '(' FORMULA ')'
#          | '+' '(' FORMULA* ')'
#          | '*' '(' FORMULA* ')'
#          | 'xor' '(' FORMULA* ')'
#          | '=' '(' FORMULA* ')'

_SAT_TOKS = {
    'sat': {OP_not, OP_or, OP_and},
    'satx': {OP_not, OP_or, OP_and, OP_xor},
    'sate': {OP_not, OP_or, OP_and, OP_equal},
    'satex': {OP_not, OP_or, OP_and, OP_xor, OP_equal},
}

def parse_sat(s, varname='x'):
    """
    Parse an input string in DIMACS SAT format,
    and return an expression.
    """
    lex = iter(SATLexer(s))

    _expect_token(lex, {KW_p})
    fmt = _expect_token(lex, {KW_sat, KW_satx, KW_sate, KW_satex}).value
    nvars = _expect_token(lex, {IntegerToken}).value

    try:
        types = {IntegerToken, LPAREN} | _SAT_TOKS[fmt]
        tok = _expect_token(lex, types)
        lex.unpop_token(tok)
        return _sat_formula(lex, fmt, varname, nvars)
    except StopIteration:
        raise DIMACSError("incomplete formula")

def _sat_formula(lex, fmt, varname, nvars):
    """Return a DIMACS SAT formula."""
    types = {IntegerToken, LPAREN} | _SAT_TOKS[fmt]
    tok = _expect_token(lex, types)
    if type(tok) is IntegerToken:
        index = tok.value
        if not 0 < index <= nvars:
            fstr = "formula literal {} outside valid range: (0, {}]"
            raise DIMACSError(fstr.format(index, nvars))
        return ('var', (varname, ), (index, ))
    elif type(tok) is OP_not:
        tok = _expect_token(lex, {IntegerToken, LPAREN})
        if type(tok) is IntegerToken:
            index = tok.value
            if not 0 < index <= nvars:
                fstr = "formula literal {} outside valid range: (0, {}]"
                raise DIMACSError(fstr.format(index, nvars))
            return (OP_not.ASTOP, ('var', (varname, ), (index, )))
        else:
            return (OP_not.ASTOP, _one_formula(lex, fmt, varname, nvars))
    elif type(tok) is LPAREN:
        return _one_formula(lex, fmt, varname, nvars)
    # OR/AND/XOR/EQUAL
    else:
        _expect_token(lex, {LPAREN})
        return (tok.ASTOP, ) + _zom_formulas(lex, fmt, varname, nvars)

def _one_formula(lex, fmt, varname, nvars):
    """Return one DIMACS SAT formula."""
    f = _sat_formula(lex, fmt, varname, nvars)
    _expect_token(lex, {RPAREN})
    return f

def _zom_formulas(lex, fmt, varname, nvars):
    """Return zero or more DIMACS SAT formulas."""
    fs = list()
    types = {IntegerToken, LPAREN, RPAREN} | _SAT_TOKS[fmt]
    tok = _expect_token(lex, types)
    while type(tok) is not RPAREN:
        lex.unpop_token(tok)
        fs.append(_sat_formula(lex, fmt, varname, nvars))
        tok = _expect_token(lex, types)
    return tuple(fs)

<commit_msg>Improve parse_cnf docstring and error handling<commit_after>"""
DIMACS

For more information on the input formats,
see "Satisfiability Suggested Format",
published May 1993 by the Rutgers Center for Discrete Mathematics (DIMACS).

Also, see the proceedings of the International SAT Competition
(http://www.satcompetition.org) for information and CNF examples.

Exceptions:
    DIMACSError

Interface Functions:
    parse_cnf
    parse_sat
"""

# pylint: disable=C0103

from pyeda.parsing.lex import LexRunError, RegexLexer, action
from pyeda.parsing.token import (
    KeywordToken, IntegerToken, OperatorToken, PunctuationToken,
)

class DIMACSError(Exception):
    """An error happened during parsing a DIMACS file"""
    def __init__(self, msg):
        super(DIMACSError, self).__init__(msg)

# Keywords
class KW_p(KeywordToken):
    """DIMACS 'p' preamble token"""

class KW_cnf(KeywordToken):
    """DIMACS 'cnf' token"""

class KW_sat(KeywordToken):
    """DIMACS 'sat' token"""

class KW_satx(KeywordToken):
    """DIMACS 'satx' token"""

class KW_sate(KeywordToken):
    """DIMACS 'sate' token"""

class KW_satex(KeywordToken):
    """DIMACS 'satex' token"""

# Operators
class OP_not(OperatorToken):
    """DIMACS '-' operator"""
    ASTOP = 'not'

class OP_or(OperatorToken):
    """DIMACS '+' operator"""
    ASTOP = 'or'

class OP_and(OperatorToken):
    """DIMACS '*' operator"""
    ASTOP = 'and'

class OP_xor(OperatorToken):
    """DIMACS 'xor' operator"""
    ASTOP = 'xor'

class OP_equal(OperatorToken):
    """DIMACS '=' operator"""
    ASTOP = 'equal'

# Punctuation
class LPAREN(PunctuationToken):
    """DIMACS '(' token"""

class RPAREN(PunctuationToken):
    """DIMACS ')' token"""

class CNFLexer(RegexLexer):
    """Lexical analysis of CNF strings"""

    def ignore(self, text):
        """Ignore this text."""

    def keyword(self, text):
        """Push a keyword onto the token queue."""
        cls = self.KEYWORDS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def operator(self, text):
        """Push an operator onto the token queue."""
        cls = self.OPERATORS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    @action(IntegerToken)
    def integer(self, text):
        """Push an integer onto the token queue."""
        return int(text)

    RULES = {
        'root': [
            (r"c.*\n", ignore),
            (r"\bp\b", keyword, 'preamble'),
        ],
        'preamble': [
            (r"[ \t]+", ignore),
            (r"\bcnf\b", keyword),
            (r"\d+", integer),
            (r"\n", ignore, 'formula'),
        ],
        'formula': [
            (r"\s+", ignore),
            (r"-", operator),
            (r"\d+", integer),
        ],
    }

    KEYWORDS = {
        'p': KW_p,
        'cnf': KW_cnf,
    }

    OPERATORS = {
        '-': OP_not,
    }

def _expect_token(lex, types):
    """Return the next token, or raise an exception."""
    tok = next(lex)
    if any(type(tok) is t for t in types):
        return tok
    else:
        raise DIMACSError("unexpected token: " + str(tok))

def parse_cnf(s, varname='x'):
    """
    Parse an input string in DIMACS CNF format,
    and return an expression abstract syntax tree.

    Parameters
    ----------
    s : str
        String containing a DIMACS CNF.

    varname : str, optional
        The variable name used for creating literals.
        Defaults to 'x'.

    Returns
    -------
    An ast tuple, defined recursively:

    ast := ('var', names, indices)
         | ('not', ast)
         | ('or', ast, ...)
         | ('and', ast, ...)

    names := (name, ...)

    indices := (index, ...)
    """
    lex = iter(CNFLexer(s))
    try:
        _expect_token(lex, {KW_p})
        _expect_token(lex, {KW_cnf})
        nvars = _expect_token(lex, {IntegerToken}).value
        nclauses = _expect_token(lex, {IntegerToken}).value
        ast = _cnf_formula(lex, varname, nvars, nclauses)
    except LexRunError as exc:
        fstr = ("{0.args[0]}: "
                "(line: {0.lineno}, offset: {0.offset}, text: {0.text})")
        raise DIMACSError(fstr.format(exc))

    return ast

def _cnf_formula(lex, varname, nvars, nclauses):
    """Return a DIMACS CNF formula."""
    clauses = list()
    while True:
        try:
            tok = _expect_token(lex, {OP_not, IntegerToken})
        except StopIteration:
            break
        lex.unpop_token(tok)
        clauses.append(_cnf_clause(lex, varname, nvars))

    if len(clauses) < nclauses:
        fstr = "formula has fewer than {} clauses"
        raise DIMACSError(fstr.format(nclauses))
    elif len(clauses) > nclauses:
        fstr = "formula has more than {} clauses"
        raise DIMACSError(fstr.format(nclauses))

    return ('and', ) + tuple(clauses)

def _cnf_clause(lex, varname, nvars):
    """Return a DIMACS CNF clause."""
    clause = list()
    tok = _expect_token(lex, {OP_not, IntegerToken})
    while not (type(tok) is IntegerToken and tok.value == 0):
        if type(tok) is OP_not:
            neg = True
            tok = _expect_token(lex, {IntegerToken})
            index = tok.value
        else:
            neg = False
            index = tok.value
        if index > nvars:
            fstr = "formula literal {} is greater than {}"
            raise DIMACSError(fstr.format(index, nvars))
        if neg:
            clause.append((OP_not.ASTOP, ('var', (varname, ), (index, ))))
        else:
            clause.append(('var', (varname, ), (index, )))
        try:
            tok = _expect_token(lex, {OP_not, IntegerToken})
        except StopIteration:
            raise DIMACSError("incomplete clause")

    return ('or', ) + tuple(clause)

class SATLexer(RegexLexer):
    """Lexical analysis of SAT strings"""

    def ignore(self, text):
        """Ignore this text."""

    def keyword(self, text):
        """Push a keyword onto the token queue."""
        cls = self.KEYWORDS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def operator(self, text):
        """Push an operator onto the token queue."""
        cls = self.OPERATORS[text]
        self.push_token(cls(text, self.lineno, self.offset))

    def punct(self, text):
        """Push punctuation onto the token queue."""
        cls = self.PUNCTUATION[text]
        self.push_token(cls(text, self.lineno, self.offset))

    @action(IntegerToken)
    def integer(self, text):
        """Push an integer onto the token queue."""
        return int(text)

    RULES = {
        'root': [
            (r"c.*\n", ignore),
            (r"\bp\b", keyword, 'preamble'),
        ],
        'preamble': [
            (r"[ \t]+", ignore),
            (r"\bsat\b", keyword),
            (r"\bsatx\b", keyword),
            (r"\bsate\b", keyword),
            (r"\bsatex\b", keyword),
            (r"\d+", integer),
            (r"\n", ignore, 'formula'),
        ],
        'formula': [
            (r"\s+", ignore),
            (r"\+", operator),
            (r"\-", operator),
            (r"\*", operator),
            (r"\bxor\b", operator),
            (r"=", operator),
            (r"\(", punct),
            (r"\)", punct),
            (r"\d+", integer),
        ],
    }

    KEYWORDS = {
        'p': KW_p,
        'sat': KW_sat,
        'satx': KW_satx,
        'sate': KW_sate,
        'satex': KW_satex,
    }

    OPERATORS = {
        '-': OP_not,
        '+': OP_or,
        '*': OP_and,
        'xor': OP_xor,
        '=': OP_equal,
    }

    PUNCTUATION = {
        '(': LPAREN,
        ')': RPAREN,
    }

# Grammar for a SAT file
#
# SAT := COMMENT* PREAMBLE FORMULA
#
# COMMENT := 'c' .* '\n'
#
# PREAMBLE := 'p' FORMAT VARIABLES '\n'
#
# FORMAT := 'sat' | 'satx' | 'sate' | 'satex'
#
# VARIABLES := INT
#
# FORMULA := INT
#          | '-' INT
#          | '(' FORMULA ')'
#          | '-' '(' FORMULA ')'
#          | '+' '(' FORMULA* ')'
#          | '*' '(' FORMULA* ')'
#          | 'xor' '(' FORMULA* ')'
#          | '=' '(' FORMULA* ')'

_SAT_TOKS = {
    'sat': {OP_not, OP_or, OP_and},
    'satx': {OP_not, OP_or, OP_and, OP_xor},
    'sate': {OP_not, OP_or, OP_and, OP_equal},
    'satex': {OP_not, OP_or, OP_and, OP_xor, OP_equal},
}

def parse_sat(s, varname='x'):
    """
    Parse an input string in DIMACS SAT format,
    and return an expression.
    """
    lex = iter(SATLexer(s))

    _expect_token(lex, {KW_p})
    fmt = _expect_token(lex, {KW_sat, KW_satx, KW_sate, KW_satex}).value
    nvars = _expect_token(lex, {IntegerToken}).value

    try:
        types = {IntegerToken, LPAREN} | _SAT_TOKS[fmt]
        tok = _expect_token(lex, types)
        lex.unpop_token(tok)
        return _sat_formula(lex, fmt, varname, nvars)
    except StopIteration:
        raise DIMACSError("incomplete formula")

def _sat_formula(lex, fmt, varname, nvars):
    """Return a DIMACS SAT formula."""
    types = {IntegerToken, LPAREN} | _SAT_TOKS[fmt]
    tok = _expect_token(lex, types)
    if type(tok) is IntegerToken:
        index = tok.value
        if not 0 < index <= nvars:
            fstr = "formula literal {} outside valid range: (0, {}]"
            raise DIMACSError(fstr.format(index, nvars))
        return ('var', (varname, ), (index, ))
    elif type(tok) is OP_not:
        tok = _expect_token(lex, {IntegerToken, LPAREN})
        if type(tok) is IntegerToken:
            index = tok.value
            if not 0 < index <= nvars:
                fstr = "formula literal {} outside valid range: (0, {}]"
                raise DIMACSError(fstr.format(index, nvars))
            return (OP_not.ASTOP, ('var', (varname, ), (index, )))
        else:
            return (OP_not.ASTOP, _one_formula(lex, fmt, varname, nvars))
    elif type(tok) is LPAREN:
        return _one_formula(lex, fmt, varname, nvars)
    # OR/AND/XOR/EQUAL
    else:
        _expect_token(lex, {LPAREN})
        return (tok.ASTOP, ) + _zom_formulas(lex, fmt, varname, nvars)

def _one_formula(lex, fmt, varname, nvars):
    """Return one DIMACS SAT formula."""
    f = _sat_formula(lex, fmt, varname, nvars)
    _expect_token(lex, {RPAREN})
    return f

def _zom_formulas(lex, fmt, varname, nvars):
    """Return zero or more DIMACS SAT formulas."""
    fs = list()
    types = {IntegerToken, LPAREN, RPAREN} | _SAT_TOKS[fmt]
    tok = _expect_token(lex, types)
    while type(tok) is not RPAREN:
        lex.unpop_token(tok)
        fs.append(_sat_formula(lex, fmt, varname, nvars))
        tok = _expect_token(lex, types)
    return tuple(fs)

<|endoftext|>


<commit_before>import os
import sqlite3
import datetime

last_location_time_key = "last_location_time"

class BuffData:
    def __init__(self):
        buff_folder = os.path.join(os.environ['HOME'], '.hmsoft')
        if not os.path.isdir(buff_folder):
            os.makedirs(buff_folder)

        db_filename = os.path.join(buff_folder, 'picture-data.db')
        self._connection = sqlite3.connect(db_filename, timeout=30.0)
        self._cursor = self._connection.cursor()
        self._connection.execute(
            "CREATE TABLE IF NOT EXISTS uploaded (file_name TEXT, photo_id TEXT UNIQUE, md5sum TEXT UNIQUE, "
            "upload_date TEXT )")
        self._connection.execute(
            "CREATE TABLE IF NOT EXISTS location (timestamp INTEGER PRIMARY KEY, latitude REAL, longitude REAL, "
            "altitude REAL )")
        self._connection.execute("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)")

    def set_setting(self, key, value):
        with self._connection:
            self._connection.execute("INSERT OR REPLACE INTO settings (key, value) VALUES(?, ?)",
                                     (key, value))

    def get_setting(self, key):
        self._cursor.execute("SELECT value FROM settings WHERE key = ?", (key,))
        result = self._cursor.fetchall()
        if len(result) == 1:
            return result[0][0]
        return None

    def add_locations(self, locations):

        c = 0
        last_time = 0
        with self._connection:
            for location in locations:
                if location is None:
                    continue

                self._connection.execute(
                    "INSERT OR IGNORE INTO location (timestamp, latitude, longitude, altitude) VALUES(?, ?, ?, ?) ",
                    location)
                c += 1
                if location[0] > last_time:
                    last_time = location[0]

            if c > 0:
                self._connection.execute("INSERT OR REPLACE INTO settings (key, value) VALUES(?, ?)",
                                         (last_location_time_key, last_time))

        return c

    def get_location_from_timestamp(self, timestamp, offset=None):
        if offset is None:
            offset = 1000 * 60 * 15
        self._cursor.execute("SELECT latitude, longitude FROM location WHERE timestamp <= ? AND "
                             "timestamp >= ? ORDER BY ABS(? - timestamp) LIMIT 1",
                             (timestamp + offset, timestamp - offset, timestamp,))
        result = self._cursor.fetchall()
        if len(result) == 1:
            return result[0]

        return None

    def add_location(self, location):
        with self._connection:
            self._connection.execute(
                "INSERT OR IGNORE INTO location (timestamp, latitude, longitude, altitude)VALUES(?, ?, ?, ?) ",
                location)

    def file_already_uploaded(self, md5sum):
        self._cursor.execute("SELECT photo_id FROM uploaded WHERE md5sum = ? AND photo_id <> ?", (md5sum, "0",))
        uploaded = len(self._cursor.fetchall()) > 0
        return uploaded

    def set_file_uploaded(self, file_name, photo_id, md5sum):
        date = datetime.datetime.now().isoformat()
        with self._connection:
            self._connection.execute(
                "INSERT OR IGNORE INTO uploaded (file_name, photo_id, md5sum, upload_date)VALUES(?, ?, ?, ?) ",
                (file_name, photo_id, md5sum, date,))<commit_msg>Added address field to location table and reverse geocoder<commit_after>import os
import sqlite3
import datetime

last_location_time_key = "last_location_time"

class BuffData:
    def __init__(self):
        buff_folder = os.path.join(os.environ['HOME'], '.hmsoft')
        if not os.path.isdir(buff_folder):
            os.makedirs(buff_folder)

        db_filename = os.path.join(buff_folder, 'picture-data.db')
        self._connection = sqlite3.connect(db_filename, timeout=30.0)
        self._cursor = self._connection.cursor()
        self._connection.execute(
            "CREATE TABLE IF NOT EXISTS uploaded (file_name TEXT, photo_id TEXT UNIQUE, md5sum TEXT UNIQUE, "
            "upload_date TEXT )")
        self._connection.execute(
            "CREATE TABLE IF NOT EXISTS location (timestamp INTEGER PRIMARY KEY, latitude REAL, longitude REAL, "
            "altitude REAL, address TEXT )")
        self._connection.execute("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)")

    def set_setting(self, key, value):
        with self._connection:
            self._connection.execute("INSERT OR REPLACE INTO settings (key, value) VALUES(?, ?)",
                                     (key, value))

    def get_setting(self, key):
        self._cursor.execute("SELECT value FROM settings WHERE key = ?", (key,))
        result = self._cursor.fetchall()
        if len(result) == 1:
            return result[0][0]
        return None

    def add_locations(self, locations):

        c = 0
        last_time = 0
        with self._connection:
            for location in locations:
                if location is None:
                    continue

                self._connection.execute(
                    "INSERT OR IGNORE INTO location (timestamp, latitude, longitude, altitude) VALUES(?, ?, ?, ?) ",
                    location)
                c += 1
                if location[0] > last_time:
                    last_time = location[0]

            if c > 0:
                self._connection.execute("INSERT OR REPLACE INTO settings (key, value) VALUES(?, ?)",
                                         (last_location_time_key, last_time))

        return c

    def get_location_from_timestamp(self, timestamp, offset=None):
        if offset is None:
            offset = 15

        offset *= 1000 * 60
        print offset
        self._cursor.execute("SELECT latitude, longitude, address, timestamp FROM location WHERE timestamp <= ? AND "
                             "timestamp >= ? ORDER BY ABS(? - timestamp) LIMIT 1",
                             (timestamp + offset, timestamp - offset, timestamp,))
        result = self._cursor.fetchall()
        if len(result) == 1:
            location = result[0]
            lat, lon, add, time = location
            if not add:
                try:
                    from geopy.geocoders import Nominatim
                    geolocator = Nominatim()
                    address = geolocator.reverse(str(lat) + ", " + str(lon), exactly_one=True).address
                    if address:
                        location = lat, lon, address, time
                        with self._connection:
                            self._connection.execute("UPDATE location SET address=? WHERE timestamp = ?",
                                                     (address, time))
                except Exception as e:
                    print e
                    pass

            return location

        return None

    def add_location(self, location):
        with self._connection:
            self._connection.execute(
                "INSERT OR IGNORE INTO location (timestamp, latitude, longitude, altitude)VALUES(?, ?, ?, ?) ",
                location)

    def file_already_uploaded(self, md5sum):
        self._cursor.execute("SELECT photo_id FROM uploaded WHERE md5sum = ? AND photo_id <> ?", (md5sum, "0",))
        uploaded = len(self._cursor.fetchall()) > 0
        return uploaded

    def set_file_uploaded(self, file_name, photo_id, md5sum):
        date = datetime.datetime.now().isoformat()
        with self._connection:
            self._connection.execute(
                "INSERT OR IGNORE INTO uploaded (file_name, photo_id, md5sum, upload_date)VALUES(?, ?, ?, ?) ",
                (file_name, photo_id, md5sum, date,))<|endoftext|>


<commit_before># Copyright 2017 Taylor DeHaan
#
# 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.

"""This modules contains tests for the DirectoryTree and DirectoryNode class.

Example:
    These tests can be run with the command::

        $ python -m dir_ascii.tests.test_directory_tree
"""

import unittest
from ..directory_tree import DirectoryTree, DirectoryNode

class TestDirectoryNode(unittest.TestCase):
    """Unittest class containing tests for DirectoryNode."""

    def setUp(self):
        self.node_name = "node"
        self.node = DirectoryNode(self.node_name)

    def test_get_name(self):
        """Test the get_name() method."""
        self.assertEqual(self.node_name, self.node.get_name())

    def test_add_child_get_children(self):
        """Test add_child and get_children methods."""
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        child_dirs = self.node.get_children()
        for i in range(n_children):
            self.assertEqual(child_dirs[i].get_name(), name_template % i)

    def test_add_file_get_files(self):
        """Test add_file and get_files methods."""
        n_files = 50
        name_template = "file%i"

        for i in range(n_files):
            self.node.add_file(name_template % i)

        files = self.node.get_files()
        for i in range(n_files):
            self.assertEqual(files[i], name_template % i)

    def test_add_files_get_files(self):
        """Test add_files and get_files methods."""
        n_files = 50
        file_list = []
        name_template = "file%i"

        for i in range(n_files):
            file_list.append(name_template % i)

        self.node.add_files(file_list)

        files = self.node.get_files()
        for i in range(n_files):
            self.assertEqual(files[i], name_template % i)

    def test_children_generator(self):
        """Test the get_children generator method."""
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        i = 0
        for child in self.node.children():
            self.assertEqual(child.get_name(), name_template % i)
            i += 1

    def test_files_generator(self):
        """Test the get_files generator method."""
        n_files = 50
        name_template = "file%i"

        for i in range(n_files):
            self.node.add_file(name_template % i)

        i = 0
        for filename in self.node.get_files():
            self.assertEqual(filename, name_template % i)
            i += 1

    def test_get_last_child(self):
        """Test the get_last_child method."""
        # Test on empty list
        self.assertIsNone(self.node.get_last_child())

        # Test on populated list
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        self.assertEqual(self.node.get_last_child().get_name(),
                         name_template % (n_children - 1))

class TestDirectoryTree(unittest.TestCase):
    """Unittest class containing tests for DirectoryTree."""

    def test_get_root(self):
        """Test the get_root() method."""
        root_name = "root"
        tree = DirectoryTree(root_name)
        self.assertEqual(root_name, tree.get_root().get_name())

if __name__ == "__main__":
    unittest.main()
<commit_msg>Created test case for symlink methods<commit_after># Copyright 2017 Taylor DeHaan
#
# 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.

"""This modules contains tests for the DirectoryTree and DirectoryNode class.

Example:
    These tests can be run with the command::

        $ python -m dir_ascii.tests.test_directory_tree
"""

import unittest
from ..directory_tree import DirectoryTree, DirectoryNode

class TestDirectoryNode(unittest.TestCase):
    """Unittest class containing tests for DirectoryNode."""

    def setUp(self):
        self.node_name = "node"
        self.node = DirectoryNode(self.node_name)

    def test_get_name(self):
        """Test the get_name() method."""
        self.assertEqual(self.node_name, self.node.get_name())

    def test_add_child_get_children(self):
        """Test add_child and get_children methods."""
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        child_dirs = self.node.get_children()
        for i in range(n_children):
            self.assertEqual(child_dirs[i].get_name(), name_template % i)

    def test_add_file_get_files(self):
        """Test add_file and get_files methods."""
        n_files = 50
        name_template = "file%i"

        for i in range(n_files):
            self.node.add_file(name_template % i)

        files = self.node.get_files()
        for i in range(n_files):
            self.assertEqual(files[i], name_template % i)

    def test_add_files_get_files(self):
        """Test add_files and get_files methods."""
        n_files = 50
        file_list = []
        name_template = "file%i"

        for i in range(n_files):
            file_list.append(name_template % i)

        self.node.add_files(file_list)

        files = self.node.get_files()
        for i in range(n_files):
            self.assertEqual(files[i], name_template % i)

    def test_add_symlinks_get_symlinks(self):
        """Test add_symlinks and get_symlinks methods."""
        n_symlinks = 50
        symlink_list = []
        name_template = "link%i"

        for i in range(n_symlinks):
            symlink_list.append(name_template % i)

        self.node.add_files(symlink_list)

        files = self.node.get_files()
        for i in range(n_symlinks):
            self.assertEqual(files[i], name_template % i)

    def test_children_generator(self):
        """Test the get_children generator method."""
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        i = 0
        for child in self.node.children():
            self.assertEqual(child.get_name(), name_template % i)
            i += 1

    def test_files_generator(self):
        """Test the get_files generator method."""
        n_files = 50
        name_template = "file%i"

        for i in range(n_files):
            self.node.add_file(name_template % i)

        i = 0
        for filename in self.node.get_files():
            self.assertEqual(filename, name_template % i)
            i += 1

    def test_get_last_child(self):
        """Test the get_last_child method."""
        # Test on empty list
        self.assertIsNone(self.node.get_last_child())

        # Test on populated list
        n_children = 50
        name_template = "child%i"

        for i in range(n_children):
            self.node.add_child(name_template % i)

        self.assertEqual(self.node.get_last_child().get_name(),
                         name_template % (n_children - 1))

class TestDirectoryTree(unittest.TestCase):
    """Unittest class containing tests for DirectoryTree."""

    def test_get_root(self):
        """Test the get_root() method."""
        root_name = "root"
        tree = DirectoryTree(root_name)
        self.assertEqual(root_name, tree.get_root().get_name())

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


<commit_before>import gspread
import config
import datetime
import uuid
import httplib2

from oauth2client.file import Storage

def refresh_access_token():
    creds.refresh(httplib2.Http())
    storage.put(creds)

print "Loading DB..."

storage = Storage('credentials-nhshd.dat')
creds = storage.get()
refresh_access_token()
gs = gspread.authorize(creds)
sheet = gs.open_by_key(config.google_sheet_key)
log_worksheet = sheet.worksheet("log")
student_worksheet = sheet.worksheet("students")
procedures_worksheet = sheet.worksheet("procedures")
timeframes_worksheet = sheet.worksheet("timeframes")
locations_worksheet = sheet.worksheet("locations")
doctors_worksheet = sheet.worksheet("doctors")

print "Complete"

def to_timestamp(dt):
    return (dt - datetime.datetime(1970, 1, 1)).total_seconds()

def get_all_students():
    refresh_access_token()
    return student_worksheet.get_all_records()

def get_all_opportunities():
    refresh_access_token()
    rs = log_worksheet.get_all_records()

    for r in rs:
        if r["outcome"] == "COMPLETED":
            r["status"] = "Completed"
        elif r["student"]:
            r["status"] = "Accepted"
        elif to_timestamp(datetime.datetime.now()) > int(r["expiry_time"]):
            r["status"] = "Expired"
        else:
            r["status"] = "Offered"

        r["time"] = datetime.datetime.fromtimestamp(r["time_sent"])
    return rs

## Call this function with a new opportunity:
# db.add_opportunity({
#        "doctor": "Dr Thing",
#        "procedure":"do thing",
#        "location": "the ward",
#        "duration": 20
#    })
#
# Returns the GUID of the created opportunity
def add_opportunity(op):
    refresh_access_token()

    vs = [uuid.uuid4()]

    now = to_timestamp(datetime.datetime.now())
    vs.append(op["doctor"])
    vs.append(now)
    vs.append(op["procedure"])
    vs.append(int(now + int(op["duration"]) * 60))
    vs.append(op["location"])
    log_worksheet.append_row(vs)

    return vs[0]

def get_opportunity(guid):
    refresh_access_token()

    ops = get_all_opportunities()

    for op in ops:
        if op["id"] == guid:
            return op

def update_opportunity(guid, student_name):
    refresh_access_token()

    ops = get_all_opportunities()

    i = 1
    x = None
    for op in ops:
        i += 1
        if op["id"] == guid:
            x = op
            break

    if x["student"]:
        return False

    log_worksheet.update_cell(i, 7, student_name)
    log_worksheet.update_cell(i, 8, to_timestamp(datetime.datetime.now()))

    return True

def complete_opportunity(guid):
    refresh_access_token()

    ops = get_all_opportunities()

    i = 1
    x = None
    for op in ops:
        i += 1
        if op["id"] == guid:
            x = op
            break

    log_worksheet.update_cell(i, 9, "COMPLETED")

    return True

def get_procedures():
    refresh_access_token()
    return [p['procedure'] for p in procedures_worksheet.get_all_records()]

def get_locations():
    refresh_access_token()
    return [l['location'] for l in locations_worksheet.get_all_records()]

def get_timeframes():
    refresh_access_token()
    return [t['timeframe'] for t in timeframes_worksheet.get_all_records()]

def get_doctors():
    refresh_access_token()
    return [d['doctor'] for d in doctors_worksheet.get_all_records()]<commit_msg>Load google credentials from environment if possible.<commit_after>import gspread
import config
import datetime
import uuid
import httplib2
import os

from oauth2client.file import Storage

def refresh_access_token():
    creds.refresh(httplib2.Http())
    storage.put(creds)

print "Loading DB..."

storage = os.environ.get("GOOGLE_CREDS", Storage('credentials-nhshd.dat'))
creds = storage.get()
refresh_access_token()
gs = gspread.authorize(creds)
sheet = gs.open_by_key(config.google_sheet_key)
log_worksheet = sheet.worksheet("log")
student_worksheet = sheet.worksheet("students")
procedures_worksheet = sheet.worksheet("procedures")
timeframes_worksheet = sheet.worksheet("timeframes")
locations_worksheet = sheet.worksheet("locations")
doctors_worksheet = sheet.worksheet("doctors")

print "Complete"

def to_timestamp(dt):
    return (dt - datetime.datetime(1970, 1, 1)).total_seconds()

def get_all_students():
    refresh_access_token()
    return student_worksheet.get_all_records()

def get_all_opportunities():
    refresh_access_token()
    rs = log_worksheet.get_all_records()

    for r in rs:
        if r["outcome"] == "COMPLETED":
            r["status"] = "Completed"
        elif r["student"]:
            r["status"] = "Accepted"
        elif to_timestamp(datetime.datetime.now()) > int(r["expiry_time"]):
            r["status"] = "Expired"
        else:
            r["status"] = "Offered"

        r["time"] = datetime.datetime.fromtimestamp(r["time_sent"])
    return rs

## Call this function with a new opportunity:
# db.add_opportunity({
#        "doctor": "Dr Thing",
#        "procedure":"do thing",
#        "location": "the ward",
#        "duration": 20
#    })
#
# Returns the GUID of the created opportunity
def add_opportunity(op):
    refresh_access_token()

    vs = [uuid.uuid4()]

    now = to_timestamp(datetime.datetime.now())
    vs.append(op["doctor"])
    vs.append(now)
    vs.append(op["procedure"])
    vs.append(int(now + int(op["duration"]) * 60))
    vs.append(op["location"])
    log_worksheet.append_row(vs)

    return vs[0]

def get_opportunity(guid):
    refresh_access_token()

    ops = get_all_opportunities()

    for op in ops:
        if op["id"] == guid:
            return op

def update_opportunity(guid, student_name):
    refresh_access_token()

    ops = get_all_opportunities()

    i = 1
    x = None
    for op in ops:
        i += 1
        if op["id"] == guid:
            x = op
            break

    if x["student"]:
        return False

    log_worksheet.update_cell(i, 7, student_name)
    log_worksheet.update_cell(i, 8, to_timestamp(datetime.datetime.now()))

    return True

def complete_opportunity(guid):
    refresh_access_token()

    ops = get_all_opportunities()

    i = 1
    x = None
    for op in ops:
        i += 1
        if op["id"] == guid:
            x = op
            break

    log_worksheet.update_cell(i, 9, "COMPLETED")

    return True

def get_procedures():
    refresh_access_token()
    return [p['procedure'] for p in procedures_worksheet.get_all_records()]

def get_locations():
    refresh_access_token()
    return [l['location'] for l in locations_worksheet.get_all_records()]

def get_timeframes():
    refresh_access_token()
    return [t['timeframe'] for t in timeframes_worksheet.get_all_records()]

def get_doctors():
    refresh_access_token()
    return [d['doctor'] for d in doctors_worksheet.get_all_records()]<|endoftext|>


<commit_before>"""
    .. module:: OakParkDatasetAdapter
    :platform: Unix
    :synopsis: Contains methods for importing data from Oak Park
    dataset.

    .. moduleauthor:: Phil Ngo <ngo.phil@gmail.com>
    .. moduleauthor:: Miguel Perez <miguel@invalid.com>
    .. moduleauthor:: Stephen Suffian <steve@invalid.com>
    .. moduleauthor:: Sabina Tomkins <sabina.tomkins@gmail.com>

"""

from appliance import ApplianceTrace
from appliance import ApplianceInstance
from appliance import ApplianceSet
from appliance import ApplianceType
import utils
import pymongo
import pandas as pd

def get_db_connection():
    connection = pymongo.MongoClient('ds059938.mongolab.com',59938)
    db = connection['oakparkhistoric']
    db.authenticate('anonymouseenergy', 'KaTard3i3')
    return db

def get_homes(db):
    """
    Returns a dictionary where each key is a home id and each value is a home.
    A home as meta_data, and a list of dates and power values in kwH.
    """
    houses =[]
    ids=[]
    c = db.usage.find()
    for co in c:
        ids.append(str(co['meta']['dataid']))
        houses.append(co)
    return {i:h for i,h in zip (ids,houses)}

def generate_trace_by_dataid(homes,dataid):
    '''
    Returns a trace.
    '''
    house = homes[dataid]
    dates = [x[0] for x in house['interval_readings']]
    values = [x[1]['value'] for x in house['interval_readings']]

    return ApplianceTrace(pd.Series(values, index = dates),house['meta'])

def resample_trace_by_month(trace,month):
    '''
    Returns a set of houses for a given month.
    '''
    return utils.resample_trace(trace,'MS')

def check_complete(home,year,month):
    dt_start = datetime(year,month,1)
    dt_end = datetime(year,month,calendar.monthrange(year,month)[1])
    index_start = home.series.index.searchsorted(dt_start)
    index_end = home.series.index.searchsorted(dt_end)
    return index_end-index_start==((dt_end-dt_start)*48).days

def get_home_series_by_year_month(year,month,home):
    '''
        Returns the home's series sliced by year and month.
        '''
    if check_complete(home,year,month):
        return  home.trace.series[index_start:index_end]

def get_list_of_homes_with_certain_month_year(homes,year,month):
    '''
        Returns a list of homes which have complete trace info for given year and month.
        Assumes that homes is dict where keys are dataids and values are traces.
        '''
    complete_homes = []
    for h in homes.keys():
        if check_complete(homes[h],year,month):
            complete_homes.append(h)
    return complete_homes

<commit_msg>added homes : traces dict<commit_after>"""
    .. module:: OakParkDatasetAdapter
    :platform: Unix
    :synopsis: Contains methods for importing data from Oak Park
    dataset.

    .. moduleauthor:: Phil Ngo <ngo.phil@gmail.com>
    .. moduleauthor:: Miguel Perez <miguel@invalid.com>
    .. moduleauthor:: Stephen Suffian <steve@invalid.com>
    .. moduleauthor:: Sabina Tomkins <sabina.tomkins@gmail.com>

"""

from appliance import ApplianceTrace
from appliance import ApplianceInstance
from appliance import ApplianceSet
from appliance import ApplianceType
import utils
import pymongo
import pandas as pd

def get_db_connection():
    connection = pymongo.MongoClient('ds059938.mongolab.com',59938)
    db = connection['oakparkhistoric']
    db.authenticate('anonymouseenergy', 'KaTard3i3')
    return db

def get_homes(db):
    """
    Returns a dictionary where each key is a home id and each value is a home.
    A home as meta_data, and a list of dates and power values in kwH.
    """
    houses =[]
    ids=[]
    c = db.usage.find()
    for co in c:
        ids.append(str(co['meta']['dataid']))
        houses.append(co)
    return {i:h for i,h in zip (ids,houses)}

def homes_to_traces(homes):
    '''
       Returns a dictionary where the key is a home id and the value is an
       Appliance Trace.
    '''
    homes = {}
    for di in homes.keys():
        homes[di]=generate_trace_by_dataid(homes,di)
    return homes

def generate_trace_by_dataid(homes,dataid):
    '''
    Returns a trace.
    '''
    house = homes[dataid]
    dates = [x[0] for x in house['interval_readings']]
    values = [x[1]['value'] for x in house['interval_readings']]

    return ApplianceTrace(pd.Series(values, index = dates),house['meta'])

def resample_trace_by_month(trace,month):
    '''
    Returns a set of houses for a given month.
    '''
    return utils.resample_trace(trace,'MS')

def check_complete(home,year,month):
    dt_start = datetime(year,month,1)
    dt_end = datetime(year,month,calendar.monthrange(year,month)[1])
    index_start = home.series.index.searchsorted(dt_start)
    index_end = home.series.index.searchsorted(dt_end)
    return index_end-index_start==((dt_end-dt_start)*48).days

def get_home_series_by_year_month(year,month,home):
    '''
        Returns the home's series sliced by year and month.
        '''
    if check_complete(home,year,month):
        return  home.trace.series[index_start:index_end]

def get_list_of_homes_with_certain_month_year(homes,year,month):
    '''
        Returns a list of homes which have complete trace info for given year and month.
        Assumes that homes is dict where keys are dataids and values are traces.
        '''
    complete_homes = []
    for h in homes.keys():
        if check_complete(homes[h],year,month):
            complete_homes.append(h)
    return complete_homes

<|endoftext|>


<commit_before>"""
    pyexcel.sheets
    ~~~~~~~~~~~~~~~~~~~

    Representation of data sheets

    :copyright: (c) 2014 by C. W.
    :license: GPL v3
"""
import datetime
from ..io import load_file
from .nominablesheet import NominableSheet

def load(file, sheetname=None,
         name_columns_by_row=-1,
         name_rows_by_column=-1,
         colnames=None,
         rownames=None,
         **keywords):
    """Constructs an instance :class:`Sheet` from a sheet of an excel file

    except csv, most excel files has more than one sheet.
    Hence sheetname is required here to indicate from which sheet the instance
    should be constructed. If this parameter is omitted, the first sheet, which
    is indexed at 0, is used. For csv, sheetname is always omitted because csv
    file contains always one sheet.
    :param str sheetname: which sheet to be used for construction
    :param int name_colmns_by_row: which row to give column names
    :param int name_rows_by_column: which column to give row names
    :param dict keywords: other parameters
    """
    book = load_file(file, **keywords)
    sheets = book.sheets()
    if sheetname:
        if sheetname not in sheets:
            raise KeyError("%s is not found" % sheetname)
    else:
        keys = list(sheets.keys())
        sheetname = keys[0]
    return Sheet(sheets[sheetname],
                 sheetname,
                 name_columns_by_row=name_columns_by_row,
                 name_rows_by_column=name_rows_by_column,
                 colnames=colnames,
                 rownames=rownames
             )

def load_from_memory(file_type,
                     file_content,
                     sheetname=None,
                     **keywords):
    """Constructs an instance :class:`Sheet` from memory

    :param str file_type: one value of these: 'csv', 'tsv', 'csvz',
    'tsvz', 'xls', 'xlsm', 'xslm', 'ods'
    :param iostream file_content: file content
    :param str sheetname: which sheet to be used for construction
    :param dict keywords: any other parameters
    """
    return load((file_type, file_content), sheetname, **keywords)

def load_from_query_sets(column_names, querysets, sheet_name=None):
    array = []
    array.append(column_names)
    for o in querysets:
        new_array = []
        for column in column_names:
            value = getattr(o, column)
            if isinstance(value, (datetime.date, datetime.time)):
                value = value.isoformat()
            new_array.append(value)
        array.append(new_array)
    return Sheet(array, name=sheet_name, name_columns_by_row=0)

def load_from_sql(session, table):
    """Constructs an instance :class:`Sheet` from database table

    :param session: SQLAlchemy session object
    :param table: SQLAlchemy database table
    :returns: :class:`Sheet`
    """
    sheet_name = getattr(table, '__tablename__', None)
    objects = session.query(table).all()
    if len(objects) == 0:
        return None
    else:
        column_names = sorted([column for column in objects[0].__dict__
                               if column != '_sa_instance_state'])
        return load_from_query_sets(column_names, objects, sheet_name)

def load_from_django_model(model):
    """Constructs an instance :class:`Sheet` from a django model

    :param model: Django model
    :returns: :class:`Sheet`
    """
    sheet_name = model._meta.model_name
    objects = model.objects.all()
    column_names = sorted([field.attname for field in model._meta.concrete_fields])
    return load_from_query_sets(column_names, objects, sheet_name)

def load_from_dict(the_dict, with_keys=True):
    """Return a sheet from a dictionary of one dimensional arrays

    :param dict the_dict: its value should be one dimensional array
    :param bool with_keys: indicate if dictionary keys should be appended or not
    """
    from ..utils import dict_to_array
    tmp_array = dict_to_array(the_dict, with_keys)
    sheet = Sheet(tmp_array)
    if with_keys:
        sheet.name_columns_by_row(0)
    return sheet

def load_from_records(records):
    """Return a sheet from a list of records

    Sheet.to_records() would produce a list of dictionaries. All dictionaries
    share the same keys.
    :params list records: records are likely to be produced by Sheet.to_records()
    method.
    """
    from ..utils import from_records
    tmp_array = from_records(records)
    sheet = Sheet(tmp_array, name_columns_by_row=0)
    return sheet

def get_sheet(file_name=None, content=None, file_type=None,
              session=None, table=None,
              model=None,
              adict=None, with_keys=True,
              records=None,
              array=None,
              sheet_name=None,
              **keywords):
    """Get an instance of :class:`Sheet` from an excel source

    :param file_name: a file with supported file extension
    :param content: the file content
    :param file_type: the file type in *content*
    :param session: database session
    :param table: database table
    :param model: a django model
    :param adict: a dictionary of one dimensional arrays
    :param with_keys: load with previous dictionary's keys, default is True
    :param records: a list of dictionaries that have the same keys
    :param array: a two dimensional array, a list of lists
    :param keywords: additional parameters, see :meth:`Sheet.__init__`
    :param sheet_name: sheet name

    Not all parameters are needed. Here is a table

    ========================== =========================================
    loading from file          file_name, sheet_name, keywords
    loading from memory        file_type, content, sheet_name, keywords
    loading from sql           session ,table
    loading from sql in django django model
    loading from dictionary    adict, with_keys
    loading from records       records
    loading from array         array
    ========================== =========================================

    see also :ref:`a-list-of-data-structures`
    """
    sheet = None
    if file_name:
        sheet = load(file_name, sheet_name, **keywords)
    elif content and file_type:
        sheet = load_from_memory(file_type, content, sheet_name, **keywords)
    elif session and table:
        sheet = load_from_sql(session, table)
    elif model:
        sheet = load_from_django_model(model)
    elif adict:
        sheet = load_from_dict(adict, with_keys)
    elif records:
        sheet = load_from_records(records)
    elif array:
        sheet = Sheet(array, **keywords)
    return sheet

class Sheet(NominableSheet):
    """Two dimensional data container for filtering, formatting and iteration

    :class:`Sheet` is a container for a two dimensional array, where individual
    cell can be any Python types. Other than numbers, value of thsee
    types: string, date, time and boolean can be mixed in the array. This
    differs from Numpy's matrix where each cell are of the same number type.

    In order to prepare two dimensional data for your computation, formatting
    functions help convert array cells to required types. Formatting can be
    applied not only to the whole sheet but also to selected rows or columns.
    Custom conversion function can be passed to these formatting functions. For
    example, to remove extra spaces surrounding the content of a cell, a custom
    function is required.

    Filtering functions are used to reduce the information contained in the
    array.
    """
    def save_as(self, filename, **keywords):
        """Save the content to a named file"""
        from ..writers import Writer
        w = Writer(filename, sheet_name=self.name, **keywords)
        w.write_reader(self)
        w.close()

    def save_to_memory(self, file_type, stream, **keywords):
        """Save the content to memory

        :param str file_type: any value of 'csv', 'tsv', 'csvz',
        'tsvz', 'xls', 'xlsm', 'xslm', 'ods'
        :param iostream stream: the memory stream to be written to
        """
        self.save_as((file_type, stream), **keywords)

    def save_to_django_model(self, model, batch_size=None):
        """Save to database table through django model

        :param table: a database model or a tuple of (model, column_names, name_columns_by_row, name_rows_by_column).
                      table_init_func is needed when the supplied table had a custom initialization function.
                      mapdict is needed when the column headers of your sheet does not match the column names of the supplied table.
                      name_column_by_row indicates which row has column headers and by default it is the first row of the supplied sheet
        """
        mymodel = None
        column_names = None
        mapdict = None
        data_wrapper = None
        name_columns_by_row = -1
        name_rows_by_column = -1
        objs = None

        if isinstance(model, tuple):
            if len(model) == 1:
                mymodel = model[0]
            elif len(model) == 2:
                mymodel, mapdict = model
            elif len(model) == 3:
                mymodel, mapdict, data_wrapper = model
            elif len(model) == 4:
                mymodel, mapdict, data_wrapper, name_columns_by_row = model
            else:
                mymodel, mapdict, data_wrapper, name_columns_by_row, name_rows_by_column = model
        else:
            mymodel = model

        if data_wrapper is None:
            data_wrapper = lambda row: row
        if name_columns_by_row != -1:
            self.name_columns_by_row(name_columns_by_row)
        if name_rows_by_column != -1:
            self.name_rows_by_column(name_rows_by_column)

        if isinstance(mapdict, list):
            column_names = mapdict
        elif isinstance(mapdict, dict):
            if len(self.colnames) > 0:
                column_names = [mapdict[name] for name in self.colnames]
            elif len(self.rownames) > 0:
                column_names = [mapdict[name] for name in self.rownames]
        elif mapdict is None:
            if len(self.colnames) > 0:
                column_names = self.colnames
            elif len(self.rownames) > 0:
                column_names = self.rownames

        if column_names is not None:
            # by default, assume column_names for rows.
            objs = [ mymodel(**dict(zip(column_names, data_wrapper(row)))) for row in self.rows()]
            mymodel.objects.bulk_create(objs, batch_size=batch_size)
        else:
            raise NameError("No column names or row names found!")

    def save_to_database(self, session, table):
        """Save data in sheet to database table

        :param session: database session
        :param table: a database table or a tuple of (table, table_init_func, mapdict, name_columns_by_row, name_rows_by_column).
                      table_init_func is needed when the supplied table had a custom initialization function.
                      mapdict is needed when the column headers of your sheet does not match the column names of the supplied table.
                      name_column_by_row indicates which row has column headers and by default it is the first row of the supplied sheet
        """
        mytable = None
        table_init_func = None
        mapdict = None
        name_columns_by_row = -1
        name_rows_by_column = -1
        if isinstance(table, tuple):
            if len(table) == 1:
                mytable = table[0]
            elif len(table) == 2:
                mytable, table_init_func = table
            elif len(table) == 3:
                mytable, table_init_func, mapdict = table
            elif len(table) == 4:
                mytable, table_init_func, mapdict, name_columns_by_row = table
            else:
                mytable, table_init_func, mapdict, name_columns_by_row, name_rows_by_column = table
        else:
            mytable = table
        if name_columns_by_row != -1:
            self.name_columns_by_row(name_columns_by_row)
        if name_rows_by_column != -1:
            self.name_rows_by_column(name_rows_by_column)
        if len(self.colnames) > 0:
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_rows(), self.colnames)
        elif len(self.rownames) > 0:
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_columns(), self.rownames)
        else:
            self.name_columns_by_row(0)
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_rows(), self.colnames)

    def _save_to_database(self, session, table, table_init_func,
                          mapdict, iterator, keys):
        for row in iterator:
            if table_init_func:
                o = table_init_func(row)
            else:
                o = table()
                for name in keys:
                    if mapdict:
                        key = mapdict[name]
                    else:
                        key = name
                    setattr(o, key, row[name])
            session.add(o)
        session.commit()

def Reader(file=None, sheetname=None, **keywords):
    """
    A single sheet excel file reader

    Default is the sheet at index 0. Or you specify one using sheet index
    or sheet name. The short coming of this reader is: column filter is
    applied first then row filter is applied next

    use as class would fail though
    changed since 0.0.7
    """
    return load(file, sheetname, **keywords)

def SeriesReader(file=None, sheetname=None, series=0, **keywords):
    """A single sheet excel file reader and it has column headers in a selected row

    use as class would fail
    changed since 0.0.7
    """
    return load(file, sheetname, name_columns_by_row=series, **keywords)

def ColumnSeriesReader(file=None, sheetname=None, series=0, **keywords):
    """A single sheet excel file reader and it has row headers in a selected column

    use as class would fail
    changed since 0.0.7
    """
    return load(file, sheetname, name_rows_by_column=series, **keywords)
<commit_msg>bug fix: transposed sheet failed to get proper dictionary hence not able to be imported into django model<commit_after>"""
    pyexcel.sheets
    ~~~~~~~~~~~~~~~~~~~

    Representation of data sheets

    :copyright: (c) 2014 by C. W.
    :license: GPL v3
"""
import datetime
from ..io import load_file
from .nominablesheet import NominableSheet

def load(file, sheetname=None,
         name_columns_by_row=-1,
         name_rows_by_column=-1,
         colnames=None,
         rownames=None,
         **keywords):
    """Constructs an instance :class:`Sheet` from a sheet of an excel file

    except csv, most excel files has more than one sheet.
    Hence sheetname is required here to indicate from which sheet the instance
    should be constructed. If this parameter is omitted, the first sheet, which
    is indexed at 0, is used. For csv, sheetname is always omitted because csv
    file contains always one sheet.
    :param str sheetname: which sheet to be used for construction
    :param int name_colmns_by_row: which row to give column names
    :param int name_rows_by_column: which column to give row names
    :param dict keywords: other parameters
    """
    book = load_file(file, **keywords)
    sheets = book.sheets()
    if sheetname:
        if sheetname not in sheets:
            raise KeyError("%s is not found" % sheetname)
    else:
        keys = list(sheets.keys())
        sheetname = keys[0]
    return Sheet(sheets[sheetname],
                 sheetname,
                 name_columns_by_row=name_columns_by_row,
                 name_rows_by_column=name_rows_by_column,
                 colnames=colnames,
                 rownames=rownames
             )

def load_from_memory(file_type,
                     file_content,
                     sheetname=None,
                     **keywords):
    """Constructs an instance :class:`Sheet` from memory

    :param str file_type: one value of these: 'csv', 'tsv', 'csvz',
    'tsvz', 'xls', 'xlsm', 'xslm', 'ods'
    :param iostream file_content: file content
    :param str sheetname: which sheet to be used for construction
    :param dict keywords: any other parameters
    """
    return load((file_type, file_content), sheetname, **keywords)

def load_from_query_sets(column_names, querysets, sheet_name=None):
    array = []
    array.append(column_names)
    for o in querysets:
        new_array = []
        for column in column_names:
            value = getattr(o, column)
            if isinstance(value, (datetime.date, datetime.time)):
                value = value.isoformat()
            new_array.append(value)
        array.append(new_array)
    return Sheet(array, name=sheet_name, name_columns_by_row=0)

def load_from_sql(session, table):
    """Constructs an instance :class:`Sheet` from database table

    :param session: SQLAlchemy session object
    :param table: SQLAlchemy database table
    :returns: :class:`Sheet`
    """
    sheet_name = getattr(table, '__tablename__', None)
    objects = session.query(table).all()
    if len(objects) == 0:
        return None
    else:
        column_names = sorted([column for column in objects[0].__dict__
                               if column != '_sa_instance_state'])
        return load_from_query_sets(column_names, objects, sheet_name)

def load_from_django_model(model):
    """Constructs an instance :class:`Sheet` from a django model

    :param model: Django model
    :returns: :class:`Sheet`
    """
    sheet_name = model._meta.model_name
    objects = model.objects.all()
    column_names = sorted([field.attname for field in model._meta.concrete_fields])
    return load_from_query_sets(column_names, objects, sheet_name)

def load_from_dict(the_dict, with_keys=True):
    """Return a sheet from a dictionary of one dimensional arrays

    :param dict the_dict: its value should be one dimensional array
    :param bool with_keys: indicate if dictionary keys should be appended or not
    """
    from ..utils import dict_to_array
    tmp_array = dict_to_array(the_dict, with_keys)
    sheet = Sheet(tmp_array)
    if with_keys:
        sheet.name_columns_by_row(0)
    return sheet

def load_from_records(records):
    """Return a sheet from a list of records

    Sheet.to_records() would produce a list of dictionaries. All dictionaries
    share the same keys.
    :params list records: records are likely to be produced by Sheet.to_records()
    method.
    """
    from ..utils import from_records
    tmp_array = from_records(records)
    sheet = Sheet(tmp_array, name_columns_by_row=0)
    return sheet

def get_sheet(file_name=None, content=None, file_type=None,
              session=None, table=None,
              model=None,
              adict=None, with_keys=True,
              records=None,
              array=None,
              sheet_name=None,
              **keywords):
    """Get an instance of :class:`Sheet` from an excel source

    :param file_name: a file with supported file extension
    :param content: the file content
    :param file_type: the file type in *content*
    :param session: database session
    :param table: database table
    :param model: a django model
    :param adict: a dictionary of one dimensional arrays
    :param with_keys: load with previous dictionary's keys, default is True
    :param records: a list of dictionaries that have the same keys
    :param array: a two dimensional array, a list of lists
    :param keywords: additional parameters, see :meth:`Sheet.__init__`
    :param sheet_name: sheet name

    Not all parameters are needed. Here is a table

    ========================== =========================================
    loading from file          file_name, sheet_name, keywords
    loading from memory        file_type, content, sheet_name, keywords
    loading from sql           session ,table
    loading from sql in django django model
    loading from dictionary    adict, with_keys
    loading from records       records
    loading from array         array
    ========================== =========================================

    see also :ref:`a-list-of-data-structures`
    """
    sheet = None
    if file_name:
        sheet = load(file_name, sheet_name, **keywords)
    elif content and file_type:
        sheet = load_from_memory(file_type, content, sheet_name, **keywords)
    elif session and table:
        sheet = load_from_sql(session, table)
    elif model:
        sheet = load_from_django_model(model)
    elif adict:
        sheet = load_from_dict(adict, with_keys)
    elif records:
        sheet = load_from_records(records)
    elif array:
        sheet = Sheet(array, **keywords)
    return sheet

class Sheet(NominableSheet):
    """Two dimensional data container for filtering, formatting and iteration

    :class:`Sheet` is a container for a two dimensional array, where individual
    cell can be any Python types. Other than numbers, value of thsee
    types: string, date, time and boolean can be mixed in the array. This
    differs from Numpy's matrix where each cell are of the same number type.

    In order to prepare two dimensional data for your computation, formatting
    functions help convert array cells to required types. Formatting can be
    applied not only to the whole sheet but also to selected rows or columns.
    Custom conversion function can be passed to these formatting functions. For
    example, to remove extra spaces surrounding the content of a cell, a custom
    function is required.

    Filtering functions are used to reduce the information contained in the
    array.
    """
    def save_as(self, filename, **keywords):
        """Save the content to a named file"""
        from ..writers import Writer
        w = Writer(filename, sheet_name=self.name, **keywords)
        w.write_reader(self)
        w.close()

    def save_to_memory(self, file_type, stream, **keywords):
        """Save the content to memory

        :param str file_type: any value of 'csv', 'tsv', 'csvz',
        'tsvz', 'xls', 'xlsm', 'xslm', 'ods'
        :param iostream stream: the memory stream to be written to
        """
        self.save_as((file_type, stream), **keywords)

    def save_to_django_model(self, model, batch_size=None):
        """Save to database table through django model

        :param table: a database model or a tuple of (model, column_names, name_columns_by_row, name_rows_by_column).
                      table_init_func is needed when the supplied table had a custom initialization function.
                      mapdict is needed when the column headers of your sheet does not match the column names of the supplied table.
                      name_column_by_row indicates which row has column headers and by default it is the first row of the supplied sheet
        """
        mymodel = None
        column_names = None
        mapdict = None
        data_wrapper = None
        name_columns_by_row = -1
        name_rows_by_column = -1
        objs = None
        using_rows = False

        if isinstance(model, tuple):
            if len(model) == 1:
                mymodel = model[0]
            elif len(model) == 2:
                mymodel, mapdict = model
            elif len(model) == 3:
                mymodel, mapdict, data_wrapper = model
            elif len(model) == 4:
                mymodel, mapdict, data_wrapper, name_columns_by_row = model
            else:
                mymodel, mapdict, data_wrapper, name_columns_by_row, name_rows_by_column = model
        else:
            mymodel = model

        if data_wrapper is None:
            data_wrapper = lambda row: row
        if name_columns_by_row != -1:
            self.name_columns_by_row(name_columns_by_row)
        if name_rows_by_column != -1:
            self.name_rows_by_column(name_rows_by_column)

        if isinstance(mapdict, list):
            if len(self.colnames) == 0 and len(self.rownames) > 0:
                using_rows = True
            column_names = mapdict
        elif isinstance(mapdict, dict):
            if len(self.colnames) > 0:
                column_names = [mapdict[name] for name in self.colnames]
            elif len(self.rownames) > 0:
                column_names = [mapdict[name] for name in self.rownames]
                using_rows = True
        elif mapdict is None:
            if len(self.colnames) > 0:
                column_names = self.colnames
            elif len(self.rownames) > 0:
                column_names = self.rownames
                using_rows = True

        if column_names is not None:
            # by default, assume column_names for rows.
            if using_rows:
                objs = [ mymodel(**dict(zip(column_names, data_wrapper(row)))) for row in self.columns()]
            else:
                objs = [ mymodel(**dict(zip(column_names, data_wrapper(row)))) for row in self.rows()]
            mymodel.objects.bulk_create(objs, batch_size=batch_size)
        else:
            raise NameError("No column names or row names found!")

    def save_to_database(self, session, table):
        """Save data in sheet to database table

        :param session: database session
        :param table: a database table or a tuple of (table, table_init_func, mapdict, name_columns_by_row, name_rows_by_column).
                      table_init_func is needed when the supplied table had a custom initialization function.
                      mapdict is needed when the column headers of your sheet does not match the column names of the supplied table.
                      name_column_by_row indicates which row has column headers and by default it is the first row of the supplied sheet
        """
        mytable = None
        table_init_func = None
        mapdict = None
        name_columns_by_row = -1
        name_rows_by_column = -1
        if isinstance(table, tuple):
            if len(table) == 1:
                mytable = table[0]
            elif len(table) == 2:
                mytable, table_init_func = table
            elif len(table) == 3:
                mytable, table_init_func, mapdict = table
            elif len(table) == 4:
                mytable, table_init_func, mapdict, name_columns_by_row = table
            else:
                mytable, table_init_func, mapdict, name_columns_by_row, name_rows_by_column = table
        else:
            mytable = table
        if name_columns_by_row != -1:
            self.name_columns_by_row(name_columns_by_row)
        if name_rows_by_column != -1:
            self.name_rows_by_column(name_rows_by_column)
        if len(self.colnames) > 0:
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_rows(), self.colnames)
        elif len(self.rownames) > 0:
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_columns(), self.rownames)
        else:
            self.name_columns_by_row(0)
            self._save_to_database(session, mytable, table_init_func, mapdict,
                                   self.named_rows(), self.colnames)

    def _save_to_database(self, session, table, table_init_func,
                          mapdict, iterator, keys):
        for row in iterator:
            if table_init_func:
                o = table_init_func(row)
            else:
                o = table()
                for name in keys:
                    if mapdict:
                        key = mapdict[name]
                    else:
                        key = name
                    setattr(o, key, row[name])
            session.add(o)
        session.commit()

def Reader(file=None, sheetname=None, **keywords):
    """
    A single sheet excel file reader

    Default is the sheet at index 0. Or you specify one using sheet index
    or sheet name. The short coming of this reader is: column filter is
    applied first then row filter is applied next

    use as class would fail though
    changed since 0.0.7
    """
    return load(file, sheetname, **keywords)

def SeriesReader(file=None, sheetname=None, series=0, **keywords):
    """A single sheet excel file reader and it has column headers in a selected row

    use as class would fail
    changed since 0.0.7
    """
    return load(file, sheetname, name_columns_by_row=series, **keywords)

def ColumnSeriesReader(file=None, sheetname=None, series=0, **keywords):
    """A single sheet excel file reader and it has row headers in a selected column

    use as class would fail
    changed since 0.0.7
    """
    return load(file, sheetname, name_rows_by_column=series, **keywords)
<|endoftext|>


<commit_before>"""
(c) 2013 LinkedIn Corp. 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.
"""
import logging

from lxml import etree
from datetime import datetime
from pytz import utc

from ..exceptions import FailedExchangeException

SOAP_NS = u'http://schemas.xmlsoap.org/soap/envelope/'

SOAP_NAMESPACES = {u's': SOAP_NS}

log = logging.getLogger('pyexchange')

class ExchangeServiceSOAP(object):

  EXCHANGE_DATE_FORMAT = u"%Y-%m-%dT%H:%M:%SZ"

  def __init__(self, connection):
    self.connection = connection

  def send(self, xml, headers=None, retries=4, timeout=30, encoding="utf-8"):
    request_xml = self._wrap_soap_xml_request(xml)
    log.info(etree.tostring(request_xml, encoding=encoding, pretty_print=True))
    response = self._send_soap_request(request_xml, headers=headers, retries=retries, timeout=timeout, encoding=encoding)
    return self._parse(response, encoding=encoding)

  def _parse(self, response, encoding="utf-8"):

    try:
      tree = etree.XML(response.encode(encoding))
    except (etree.XMLSyntaxError, TypeError) as err:
      raise FailedExchangeException(u"Unable to parse response from Exchange - check your login information. Error: %s" % err)

    self._check_for_errors(tree)

    log.info(etree.tostring(tree, encoding=encoding, pretty_print=True))
    return tree

  def _check_for_errors(self, xml_tree):
    self._check_for_SOAP_fault(xml_tree)

  def _check_for_SOAP_fault(self, xml_tree):
    # Check for SOAP errors. if <soap:Fault> is anywhere in the response, flip out

    fault_nodes = xml_tree.xpath(u'//s:Fault', namespaces=SOAP_NAMESPACES)

    if fault_nodes:
      fault = fault_nodes[0]
      log.debug(etree.tostring(fault, pretty_print=True))
      raise FailedExchangeException(u"SOAP Fault from Exchange server", fault.text)

  def _send_soap_request(self, xml, headers=None, retries=2, timeout=30, encoding="utf-8"):
    body = etree.tostring(xml, encoding=encoding)

    response = self.connection.send(body, headers, retries, timeout)
    return response

  def _wrap_soap_xml_request(self, exchange_xml):
    root = etree.Element(u"{%s}Envelope" % SOAP_NS)
    body = etree.SubElement(root, u"{%s}Body" % SOAP_NS)
    body.append(exchange_xml)

    return root

  def _parse_date(self, date_string):
    date = datetime.strptime(date_string, self.EXCHANGE_DATE_FORMAT)
    date = date.replace(tzinfo=utc)

    return date

  def _parse_date_only_naive(self, date_string):
    date = datetime.strptime(date_string[0:10], self.EXCHANGE_DATE_FORMAT[0:8])

    return date.date()

  def _xpath_to_dict(self, element, property_map, namespace_map):
    """
    property_map = {
      u'name'         : { u'xpath' : u't:Mailbox/t:Name'},
      u'email'        : { u'xpath' : u't:Mailbox/t:EmailAddress'},
      u'response'     : { u'xpath' : u't:ResponseType'},
      u'last_response': { u'xpath' : u't:LastResponseTime', u'cast': u'datetime'},
    }

    This runs the given xpath on the node and returns a dictionary

    """

    result = {}

    log.info(etree.tostring(element, pretty_print=True))

    for key in property_map:
      item = property_map[key]
      log.info(u'Pulling xpath {xpath} into key {key}'.format(key=key, xpath=item[u'xpath']))
      nodes = element.xpath(item[u'xpath'], namespaces=namespace_map)

      if nodes:
        result_for_node = []

        for node in nodes:
          cast_as = item.get(u'cast', None)

          if cast_as == u'datetime':
            result_for_node.append(self._parse_date(node.text))
          elif cast_as == u'date_only_naive':
            result_for_node.append(self._parse_date_only_naive(node.text))
          elif cast_as == u'int':
            result_for_node.append(int(node.text))
          elif cast_as == u'bool':
            if node.text.lower() == u'true':
              result_for_node.append(True)
            else:
              result_for_node.append(False)
          else:
            result_for_node.append(node.text)

        if not result_for_node:
          result[key] = None
        elif len(result_for_node) == 1:
          result[key] = result_for_node[0]
        else:
          result[key] = result_for_node

    return result
<commit_msg>Ensure we have the right 's' prefix for the SOAP elements.<commit_after>"""
(c) 2013 LinkedIn Corp. 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.
"""
import logging

from lxml import etree
from lxml.builder import ElementMaker
from datetime import datetime
from pytz import utc

from ..exceptions import FailedExchangeException

SOAP_NS = u'http://schemas.xmlsoap.org/soap/envelope/'

SOAP_NAMESPACES = {u's': SOAP_NS}
S = ElementMaker(namespace=SOAP_NS, nsmap=SOAP_NAMESPACES)

log = logging.getLogger('pyexchange')

class ExchangeServiceSOAP(object):

  EXCHANGE_DATE_FORMAT = u"%Y-%m-%dT%H:%M:%SZ"

  def __init__(self, connection):
    self.connection = connection

  def send(self, xml, headers=None, retries=4, timeout=30, encoding="utf-8"):
    request_xml = self._wrap_soap_xml_request(xml)
    log.info(etree.tostring(request_xml, encoding=encoding, pretty_print=True))
    response = self._send_soap_request(request_xml, headers=headers, retries=retries, timeout=timeout, encoding=encoding)
    return self._parse(response, encoding=encoding)

  def _parse(self, response, encoding="utf-8"):

    try:
      tree = etree.XML(response.encode(encoding))
    except (etree.XMLSyntaxError, TypeError) as err:
      raise FailedExchangeException(u"Unable to parse response from Exchange - check your login information. Error: %s" % err)

    self._check_for_errors(tree)

    log.info(etree.tostring(tree, encoding=encoding, pretty_print=True))
    return tree

  def _check_for_errors(self, xml_tree):
    self._check_for_SOAP_fault(xml_tree)

  def _check_for_SOAP_fault(self, xml_tree):
    # Check for SOAP errors. if <soap:Fault> is anywhere in the response, flip out

    fault_nodes = xml_tree.xpath(u'//s:Fault', namespaces=SOAP_NAMESPACES)

    if fault_nodes:
      fault = fault_nodes[0]
      log.debug(etree.tostring(fault, pretty_print=True))
      raise FailedExchangeException(u"SOAP Fault from Exchange server", fault.text)

  def _send_soap_request(self, xml, headers=None, retries=2, timeout=30, encoding="utf-8"):
    body = etree.tostring(xml, encoding=encoding)

    response = self.connection.send(body, headers, retries, timeout)
    return response

  def _wrap_soap_xml_request(self, exchange_xml):
    root = S.Envelope(S.Body(exchange_xml))
    return root

  def _parse_date(self, date_string):
    date = datetime.strptime(date_string, self.EXCHANGE_DATE_FORMAT)
    date = date.replace(tzinfo=utc)

    return date

  def _parse_date_only_naive(self, date_string):
    date = datetime.strptime(date_string[0:10], self.EXCHANGE_DATE_FORMAT[0:8])

    return date.date()

  def _xpath_to_dict(self, element, property_map, namespace_map):
    """
    property_map = {
      u'name'         : { u'xpath' : u't:Mailbox/t:Name'},
      u'email'        : { u'xpath' : u't:Mailbox/t:EmailAddress'},
      u'response'     : { u'xpath' : u't:ResponseType'},
      u'last_response': { u'xpath' : u't:LastResponseTime', u'cast': u'datetime'},
    }

    This runs the given xpath on the node and returns a dictionary

    """

    result = {}

    log.info(etree.tostring(element, pretty_print=True))

    for key in property_map:
      item = property_map[key]
      log.info(u'Pulling xpath {xpath} into key {key}'.format(key=key, xpath=item[u'xpath']))
      nodes = element.xpath(item[u'xpath'], namespaces=namespace_map)

      if nodes:
        result_for_node = []

        for node in nodes:
          cast_as = item.get(u'cast', None)

          if cast_as == u'datetime':
            result_for_node.append(self._parse_date(node.text))
          elif cast_as == u'date_only_naive':
            result_for_node.append(self._parse_date_only_naive(node.text))
          elif cast_as == u'int':
            result_for_node.append(int(node.text))
          elif cast_as == u'bool':
            if node.text.lower() == u'true':
              result_for_node.append(True)
            else:
              result_for_node.append(False)
          else:
            result_for_node.append(node.text)

        if not result_for_node:
          result[key] = None
        elif len(result_for_node) == 1:
          result[key] = result_for_node[0]
        else:
          result[key] = result_for_node

    return result
<|endoftext|>


<commit_before>"""Defines the version of pyexperiment
"""

__version__ = '0.2.18'
<commit_msg>Bump version...<commit_after>"""Defines the version of pyexperiment
"""

__version__ = '0.2.19'
<|endoftext|>