<commit_before>import os, shutil, socket
from datetime import datetime
from urlparse import urljoin
from playoff import sql as p_sql
from playoff import template as p_temp

from playoff.settings import PlayoffSettings
s = PlayoffSettings()
settings = s.get()
teams = settings['teams']
leaderboard = [None] * len(teams)

from playoff.db import PlayoffDB
db = PlayoffDB(settings['database'])

def get_shortname(fullname):
    for team in settings['teams']:
        if team[0] == fullname:
            return team[1]
    return fullname

def get_team_image(fullname):
    for team in settings['teams']:
        if team[0] == fullname and len(team) > 2:
            if team[2] is not None:
                return p_temp.LEADERBOARD_ROW_FLAG % (team[2])
    return ''

def get_match_table(match):
    rows = ''
    for team in match.teams:
        rows += p_temp.MATCH_TEAM_ROW % (
            ' '.join(['winner' if team.name == match.winner else '',
                      'loser' if team.name == match.loser else '']).strip(),
            match.link,
            team.name,
            ' / '.join([get_shortname(name) for name in team.name.split('
')]),
            match.link,
            team.score
        )
    html = p_temp.MATCH_TABLE.decode('utf8') % (
        int(settings['page']['width'] * 0.75),
        int(settings['page']['width'] * 0.25),
        rows
    )
    if match.running > 0:
        html += p_temp.MATCH_RUNNING % (match.link, match.running)
    return html

def get_match_grid(grid, matches, width, height):
    grid_boxes = ''
    col_no = 0
    for column in grid:
        grid_x = col_no * (settings['page']['width'] + settings['page']['margin'])
        grid_header = p_temp.MATCH_GRID_PHASE_RUNNING if len([match for match in column if match is not None and matches[match].running > 0]) > 0 else p_temp.MATCH_GRID_PHASE
        grid_boxes += grid_header % (
            settings['phases'][col_no]['link'],
            settings['page']['width'],
            grid_x,
            settings['phases'][col_no]['title']
        )
        row_no = 0
        column_height = height / len(column)
        for match in column:
            grid_y = int(row_no * column_height + 0.5 * (column_height - settings['page']['height']))
            if match is not None:
                grid_boxes += p_temp.MATCH_BOX % (
                    grid_x, grid_y,
                    match,
                    ' '.join([str(m) for m in matches[match].winner_matches]) if matches[match].winner_matches is not None else '',
                    ' '.join([str(m) for m in matches[match].loser_matches]) if matches[match].loser_matches is not None else '',
                    get_match_table(matches[match])
                )
            row_no += 1
        col_no += 1
    canvas_settings = []
    if 'canvas' in settings:
        for setting, value in settings['canvas'].iteritems():
            canvas_settings.append(
                'data-%s="%s"' % (setting.replace('_', '-'), str(value))
            )
    return p_temp.MATCH_GRID % (width, height, width, height, ' '.join(canvas_settings), grid_boxes)

def get_leaderboard_table(leaderboard):
    if len([t for t in leaderboard if t is not None]) == 0:
        return ''
    position = 1
    rows = ''
    for team in leaderboard:
        rows += p_temp.LEADERBOARD_ROW % (position, get_team_image(team), team or '')
        position +=1
    html = p_temp.LEADERBOARD.decode('utf8') % (rows)
    return html

class Team:
    name = ''
    score = 0.0

class Match:
    teams = None
    running = 0
    link = None
    winner = None
    loser = None
    winner_matches = None
    loser_matches = None

match_info = {}

def get_match_info(match):
    info = Match()
    info.teams = [Team(), Team()]
    info.winner_matches = []
    info.loser_matches = []
    for i in range(0, 2):
        if 'winner' in match['teams'][i]:
            info.winner_matches += match['teams'][i]['winner']
        if 'loser' in match['teams'][i]:
            info.loser_matches += match['teams'][i]['loser']
    info.winner_matches = list(set(info.winner_matches))
    info.loser_matches = list(set(info.loser_matches))
    try:
        row = db.fetch(match['database'], p_sql.PREFIX, ())
        info.link = '%srunda%d.html' % (row[0], match['round'])
    except Exception as e:
        pass
    try:
        row = db.fetch(match['database'], p_sql.MATCH_RESULTS, (match['table'], match['round']))
        info.teams[0].name = row[0]
        info.teams[1].name = row[1]
        info.teams[0].score = row[3] + row[5]
        info.teams[1].score = row[4] + row[6]
        if row[2] > 0:
            info.teams[0].score += row[2]
        else:
            info.teams[1].score -= row[2]
    except Exception as e:
        for i in range(0, 2):
            if isinstance(match['teams'][i], basestring):
                info.teams[i].name = match['teams'][i]
            elif isinstance(match['teams'][i], list):
                info.teams[i].name = '
'.join(match['teams'][i])
            else:
                match_teams = []
                if 'winner' in match['teams'][i]:
                    match_teams += [
                        match_info[winner_match].winner
                        for winner_match in match['teams'][i]['winner']
                    ]
                if 'loser' in match['teams'][i]:
                    match_teams += [
                        match_info[loser_match].loser
                        for loser_match in match['teams'][i]['loser']
                    ]
                if 'place' in match['teams'][i]:
                    match_teams += [
                        teams[place-1][0]
                        for place in match['teams'][i]['place']
                    ]
                info.teams[i].name = '
'.join([
                    team if team is not None else '??'
                    for team in match_teams]
                ) if len([team for team in match_teams if team is not None]) > 0 else ''

    try:
        towels = db.fetch(match['database'], p_sql.TOWEL_COUNT, (match['table'], match['round']))
        row = [0 if r is None else r for r in db.fetch(match['database'], p_sql.BOARD_COUNT, (match['table'], match['round']))]
        if row[1] > 0:
            info.running = int(row[1])
        if row[1] >= row[0] - towels[0]:
            info.running = 0
            info.winner = info.teams[0].name if info.teams[0].score > info.teams[1].score else info.teams[1].name
            info.loser = info.teams[1].name if info.teams[0].score > info.teams[1].score else info.teams[0].name
    except Exception as e:
        pass
    return info

grid = []
for phase in settings['phases']:
    phase_grid = [None] * (len(phase['dummies']) + len(phase['matches']) if 'dummies' in phase else len(phase['matches']))
    phase_pos = 0
    for match in phase['matches']:
        if 'dummies' in phase:
            while phase_pos in phase['dummies']:
                phase_pos += 1
        match_info[match['id']] = get_match_info(match)
        match_info[match['id']].link = phase['link'] if match_info[match['id']].link is None else urljoin(phase['link'], match_info[match['id']].link)
        phase_grid[phase_pos] = match['id']
        phase_pos += 1
    grid.append(phase_grid)

for team in settings['teams']:
    if len(team) > 3:
        leaderboard[team[3]-1] = team[0]

leaderboard_teams = {}
for phase in settings['phases']:
    for match in phase['matches']:
        if 'winner' in match:
            winner_key = tuple(match['winner'])
            if winner_key not in leaderboard_teams:
                leaderboard_teams[winner_key] = []
            leaderboard_teams[winner_key].append(match_info[match['id']].winner)
        if 'loser' in match:
            loser_key = tuple(match['loser'])
            if loser_key not in leaderboard_teams:
                leaderboard_teams[loser_key] = []
            leaderboard_teams[loser_key].append(match_info[match['id']].loser)

for positions, teams in leaderboard_teams.iteritems():
    positions = list(positions)
    if len(positions) == len([team for team in teams if team is not None]):
        for table_team in settings['teams']:
            if table_team[0] in teams:
                position = positions.pop(0)
                leaderboard[position-1] = table_team[0]

grid_columns = len(settings['phases'])
grid_rows = max([len(phase['matches']) + len(phase['dummies']) if 'dummies' in phase else len(phase['matches']) for phase in settings['phases']])
grid_height = grid_rows * (settings['page']['height'] + settings['page']['margin']) - settings['page']['margin']
grid_width = grid_columns * (settings['page']['width'] + settings['page']['margin']) - settings['page']['margin']

content = (
    p_temp.PAGE % (
        p_temp.PAGE_HEAD % (
            p_temp.PAGE_HEAD_REFRESH % (settings['page']['refresh']) if settings['page']['refresh'] > 0 else '',
            settings['page']['title']
        ),
        p_temp.PAGE_BODY % (
            settings['page']['logoh'],
            get_match_grid(grid, match_info, grid_width, grid_height),
            get_leaderboard_table(leaderboard),
            p_temp.PAGE_BODY_FOOTER.decode('utf8') % (datetime.now().strftime('%Y-%m-%d o %H:%M'))
        )
    )).encode('utf8')

output = open(settings['output'], 'w')
output.write(content)
output.close()

output_path = os.path.dirname(settings['output'])
script_output_path = os.path.join(output_path, 'sklady/playoff.js')

shutil.copy(unicode(os.path.join(os.path.dirname(__file__), 'playoff.js')),
            unicode(script_output_path))

if settings['goniec']['enabled']:
    try:
        content_lines = [(output_path.strip('/') + '/').replace('/', '\\')] + [os.path.basename(settings['output']), 'sklady/playoff.js'] + ['bye', '']
        print '\n'.join(content_lines)
        goniec = socket.socket()
        goniec.connect((settings['goniec']['host'], settings['goniec']['port']))
        goniec.sendall('\n'.join(content_lines))
        goniec.close()
    except socket.error:
        pass
<commit_msg>Spliting spaghetti code into functions<commit_after>import os, shutil, socket
from datetime import datetime
from urlparse import urljoin
from playoff import sql as p_sql
from playoff import template as p_temp
from playoff.db import PlayoffDB
from playoff.settings import PlayoffSettings

def get_shortname(fullname, teams):
    for team in teams:
        if team[0] == fullname:
            return team[1]
    return fullname

def get_team_image(fullname, teams):
    for team in teams:
        if team[0] == fullname and len(team) > 2:
            if team[2] is not None:
                return p_temp.LEADERBOARD_ROW_FLAG % (team[2])
    return ''

def get_match_table(match, teams, page_settings):
    rows = ''
    for team in match.teams:
        rows += p_temp.MATCH_TEAM_ROW % (
            ' '.join(['winner' if team.name == match.winner else '',
                      'loser' if team.name == match.loser else '']).strip(),
            match.link,
            team.name,
            ' / '.join([get_shortname(name, teams) for name in team.name.split('
')]),
            match.link,
            team.score
        )
    html = p_temp.MATCH_TABLE.decode('utf8') % (
        int(page_settings['width'] * 0.75),
        int(page_settings['width'] * 0.25),
        rows
    )
    if match.running > 0:
        html += p_temp.MATCH_RUNNING % (match.link, match.running)
    return html

def get_match_grid(grid, phases, matches, page_settings, width, height, teams, canvas_settings):
    grid_boxes = ''
    col_no = 0
    for column in grid:
        grid_x = col_no * (page_settings['width'] + page_settings['margin'])
        grid_header = p_temp.MATCH_GRID_PHASE_RUNNING if len([match for match in column if match is not None and matches[match].running > 0]) > 0 else p_temp.MATCH_GRID_PHASE
        grid_boxes += grid_header % (
            phases[col_no]['link'],
            page_settings['width'],
            grid_x,
            phases[col_no]['title']
        )
        row_no = 0
        column_height = height / len(column)
        for match in column:
            grid_y = int(row_no * column_height + 0.5 * (column_height - page_settings['height']))
            if match is not None:
                grid_boxes += p_temp.MATCH_BOX % (
                    grid_x, grid_y,
                    match,
                    ' '.join([str(m) for m in matches[match].winner_matches]) if matches[match].winner_matches is not None else '',
                    ' '.join([str(m) for m in matches[match].loser_matches]) if matches[match].loser_matches is not None else '',
                    get_match_table(matches[match], teams, page_settings)
                )
            row_no += 1
        col_no += 1
    canvas_attrs = []
    for setting, value in canvas_settings.iteritems():
        canvas_attrs.append(
            'data-%s="%s"' % (setting.replace('_', '-'), str(value))
        )
    return p_temp.MATCH_GRID % (width, height, width, height, ' '.join(canvas_attrs), grid_boxes)

def get_leaderboard_table(leaderboard, teams):
    if len([t for t in leaderboard if t is not None]) == 0:
        return ''
    position = 1
    rows = ''
    for team in leaderboard:
        rows += p_temp.LEADERBOARD_ROW % (position, get_team_image(team, teams), team or '')
        position +=1
    html = p_temp.LEADERBOARD.decode('utf8') % (rows)
    return html

class Team:
    name = ''
    score = 0.0

class Match:
    teams = None
    running = 0
    link = None
    winner = None
    loser = None
    winner_matches = None
    loser_matches = None

def get_match_info(match, match_info, teams, db):
    info = Match()
    info.teams = [Team(), Team()]
    info.winner_matches = []
    info.loser_matches = []
    for i in range(0, 2):
        if 'winner' in match['teams'][i]:
            info.winner_matches += match['teams'][i]['winner']
        if 'loser' in match['teams'][i]:
            info.loser_matches += match['teams'][i]['loser']
    info.winner_matches = list(set(info.winner_matches))
    info.loser_matches = list(set(info.loser_matches))
    try:
        row = db.fetch(match['database'], p_sql.PREFIX, ())
        info.link = '%srunda%d.html' % (row[0], match['round'])
    except Exception as e:
        pass
    try:
        row = db.fetch(match['database'], p_sql.MATCH_RESULTS, (match['table'], match['round']))
        info.teams[0].name = row[0]
        info.teams[1].name = row[1]
        info.teams[0].score = row[3] + row[5]
        info.teams[1].score = row[4] + row[6]
        if row[2] > 0:
            info.teams[0].score += row[2]
        else:
            info.teams[1].score -= row[2]
    except Exception as e:
        for i in range(0, 2):
            if isinstance(match['teams'][i], basestring):
                info.teams[i].name = match['teams'][i]
            elif isinstance(match['teams'][i], list):
                info.teams[i].name = '
'.join(match['teams'][i])
            else:
                match_teams = []
                if 'winner' in match['teams'][i]:
                    match_teams += [
                        match_info[winner_match].winner
                        for winner_match in match['teams'][i]['winner']
                    ]
                if 'loser' in match['teams'][i]:
                    match_teams += [
                        match_info[loser_match].loser
                        for loser_match in match['teams'][i]['loser']
                    ]
                if 'place' in match['teams'][i]:
                    match_teams += [
                        teams[place-1][0]
                        for place in match['teams'][i]['place']
                    ]
                info.teams[i].name = '
'.join([
                    team if team is not None else '??'
                    for team in match_teams]
                ) if len([team for team in match_teams if team is not None]) > 0 else ''

    try:
        towels = db.fetch(match['database'], p_sql.TOWEL_COUNT, (match['table'], match['round']))
        row = [0 if r is None else r for r in db.fetch(match['database'], p_sql.BOARD_COUNT, (match['table'], match['round']))]
        if row[1] > 0:
            info.running = int(row[1])
        if row[1] >= row[0] - towels[0]:
            info.running = 0
            info.winner = info.teams[0].name if info.teams[0].score > info.teams[1].score else info.teams[1].name
            info.loser = info.teams[1].name if info.teams[0].score > info.teams[1].score else info.teams[0].name
    except Exception as e:
        pass
    return info

def generate_phases(phases):
    grid = []
    for phase in phases:
        phase_grid = [None] * (len(phase['dummies']) + len(phase['matches']) if 'dummies' in phase else len(phase['matches']))
        phase_pos = 0
        for match in phase['matches']:
            if 'dummies' in phase:
                while phase_pos in phase['dummies']:
                    phase_pos += 1
            phase_grid[phase_pos] = match['id']
            phase_pos += 1
        grid.append(phase_grid)
    return grid

def fill_match_info(phases, teams, db):
    match_info = {}
    for phase in phases:
        for match in phase['matches']:
            match_info[match['id']] = get_match_info(match, match_info, teams, db)
            match_info[match['id']].link = phase['link'] if match_info[match['id']].link is None else urljoin(phase['link'], match_info[match['id']].link)
    return match_info

def prefill_leaderboard(teams):
    leaderboard = [None] * len(teams)
    for team in teams:
        if len(team) > 3:
            leaderboard[team[3]-1] = team[0]
    return leaderboard

def fill_leaderboard(phases, teams):
    leaderboard_teams = {}
    leaderboard = prefill_leaderboard(teams)
    for phase in phases:
        for match in phase['matches']:
            if 'winner' in match:
                winner_key = tuple(match['winner'])
                if winner_key not in leaderboard_teams:
                    leaderboard_teams[winner_key] = []
                leaderboard_teams[winner_key].append(match_info[match['id']].winner)
            if 'loser' in match:
                loser_key = tuple(match['loser'])
                if loser_key not in leaderboard_teams:
                    leaderboard_teams[loser_key] = []
                leaderboard_teams[loser_key].append(match_info[match['id']].loser)

    for positions, teams in leaderboard_teams.iteritems():
        positions = list(positions)
        if len(positions) == len([team for team in teams if team is not None]):
            for table_team in teams:
                if table_team[0] in teams:
                    position = positions.pop(0)
                    leaderboard[position-1] = table_team[0]
    return leaderboard

def generate_content(grid, phases, match_info, teams, grid_width, grid_height, page_settings, canvas_settings):
    return p_temp.PAGE % (
        p_temp.PAGE_HEAD % (
            p_temp.PAGE_HEAD_REFRESH % (page_settings['refresh']) if page_settings['refresh'] > 0 else '',
            page_settings['title']
        ),
        p_temp.PAGE_BODY % (
            page_settings['logoh'],
            get_match_grid(grid, phases, match_info, page_settings, grid_width, grid_height, teams, canvas_settings),
            get_leaderboard_table(leaderboard, teams),
            p_temp.PAGE_BODY_FOOTER.decode('utf8') % (datetime.now().strftime('%Y-%m-%d o %H:%M'))
        )
    )

def write_content(content, output_file):
    output = open(output_file, 'w')
    output.write(content.encode('utf8'))
    output.close()
    return os.path.dirname(output_file)

def copy_scripts(output_path):
    script_path = 'sklady/playoff.js'
    script_output_path = os.path.join(output_path, script_path)
    shutil.copy(unicode(os.path.join(os.path.dirname(__file__), 'playoff.js')),
                unicode(script_output_path))
    return script_output_path

def send_files(goniec_settings, path, files):
    if goniec_settings['enabled']:
        try:
            base_path = path.strip(os.sep) + os.sep
            content_files = [filename.replace(base_path, '') for filename in files if filename.startswith(base_path)]
            content_lines = [base_path] + content_files + ['bye', '']
            print '\n'.join(content_lines)
            goniec = socket.socket()
            goniec.connect((goniec_settings['host'], goniec_settings['port']))
            goniec.sendall('\n'.join(content_lines))
            goniec.close()
        except socket.error:
            pass

s = PlayoffSettings()
db = PlayoffDB(s.get('database'))

phase_settings = s.get('phases')
grid = generate_phases(phase_settings)
match_info = fill_match_info(phase_settings, s.get('teams'), db)
leaderboard = fill_leaderboard(phase_settings, s.get('teams'))

page_settings = s.get('page')
grid_columns = len(phase_settings)
grid_rows = max([len(phase['matches']) + len(phase['dummies']) if 'dummies' in phase else len(phase['matches']) for phase in phase_settings])
grid_height = grid_rows * (page_settings['height'] + page_settings['margin']) - page_settings['margin']
grid_width = grid_columns * (page_settings['width'] + page_settings['margin']) - page_settings['margin']

content = generate_content(grid, phase_settings, match_info, s.get('teams'), grid_width, grid_height, page_settings, s.get('canvas') if s.has_section('canvas') else {})

output_file = s.get('output')
output_path = write_content(content, output_file)
script_path = copy_scripts(output_path)
send_files(s.get('goniec'), output_path, [output_file, script_path])

<|endoftext|>


<commit_before>
import logging
import os
import unittest

from nose.plugins import Plugin

from jstestnetlib.control import Connection

log = logging.getLogger('nose.plugins.jstests')

class JSTests(Plugin):
    """Run JavaScript tests using a JS TestNet server."""
    name = 'jstests'

    def options(self, parser, env=os.environ):
        super(JSTests, self).options(parser, env=env)
        parser.add_option('--jstests-server', action="store",
                          help='http://jstestnet-server/')
        parser.add_option('--jstests-suite', action="store",
                          help='Name of test suite to run')
        parser.add_option('--jstests-browsers', action="store",
                          help=('Comma separated list of browsers to run '
                                'tests against, see JS TestNet docs for '
                                'details. Example: '
                                'firefox=~3,firefox=~4,chrome'))
        self.parser = parser

    def configure(self, options, conf):
        super(JSTests, self).configure(options, conf)
        if not self.enabled:
            return
        self.options = options
        if not self.options.jstests_server:
            self.parser.error("Missing --jstests-server")
        if not self.options.jstests_suite:
            self.parser.error("Missing --jstests-suite")
        if not self.options.jstests_browsers:
            self.parser.error("Missing --jstests-browsers")
        self.started = False
        self.conn = Connection(self.options.jstests_server)

    def loadTestsFromDir(self, directory):
        if self.started:
            # hijacking loadTestsFromDir to run tests once
            # and only once.
            return
        self.started = True
        log.debug('Starting %r [%s] %s' % (self.options.jstests_suite,
                                           self.options.jstests_server,
                                           self.options.jstests_browsers))

        tests = self.conn.run_tests(self.options.jstests_suite,
                                    self.options.jstests_browsers)
        for test in tests['results']:
            yield JSTestCase(test)
            # TODO(Kumar) check stop_on_error for nosetests -x

class JSTestError(Exception):
    pass

class JSTestCase(unittest.TestCase):
    """A test case that represents a remote test known by the server."""
    __test__ = False # this is not a collectible test

    def __init__(self, test):
        self.test = test
        super(JSTestCase, self).__init__()

    def runTest(self):
        pass

    def run(self, result):
        result.startTest(self)
        try:
            passed = True
            # Since unittest does not log assertions,
            # iterate until the first failure (if there is one).
            for assertion in self.test['assertions']:
                if not assertion['result']:
                    passed = False
                    # log.debug(repr(self.test))
                    msg = assertion['message'] or '<unknown error>'
                    traceback = None # Python
                    # TODO(Kumar) add shortened worker_user_agent here?
                    e = (JSTestError, "[worker=%s] %s %s" % (
                         assertion['worker_id'], msg,
                         assertion['stacktrace'] or ''), traceback)
                    result.addError(self, e)
                    break
            if passed:
                result.addSuccess(self)
        finally:
            result.stopTest(self)

    def address(self):
        return (self.id(), None, None)

    def id(self):
        return repr(self)

    def shortDescription(self):
        return "%r: %s: %s" % (self, self.test['module'], self.test['test'])

    def __repr__(self):
        return "JS"

    __str__ = __repr__
<commit_msg>Supports nosetests --stop now<commit_after>
import logging
import os
import unittest

from nose.plugins import Plugin

from jstestnetlib.control import Connection

log = logging.getLogger('nose.plugins.jstests')

class JSTests(Plugin):
    """Run JavaScript tests using a JS TestNet server."""
    name = 'jstests'

    def options(self, parser, env=os.environ):
        super(JSTests, self).options(parser, env=env)
        parser.add_option('--jstests-server', action="store",
                          help='http://jstestnet-server/')
        parser.add_option('--jstests-suite', action="store",
                          help='Name of test suite to run')
        parser.add_option('--jstests-browsers', action="store",
                          help=('Comma separated list of browsers to run '
                                'tests against, see JS TestNet docs for '
                                'details. Example: '
                                'firefox=~3,firefox=~4,chrome'))
        self.parser = parser

    def configure(self, options, conf):
        super(JSTests, self).configure(options, conf)
        if not self.enabled:
            return
        self.options = options
        if not self.options.jstests_server:
            self.parser.error("Missing --jstests-server")
        if not self.options.jstests_suite:
            self.parser.error("Missing --jstests-suite")
        if not self.options.jstests_browsers:
            self.parser.error("Missing --jstests-browsers")
        self.started = False
        self.conn = Connection(self.options.jstests_server)

    def loadTestsFromDir(self, directory):
        if self.started:
            # hijacking loadTestsFromDir to run tests once
            # and only once.
            return
        self.started = True
        log.debug('Starting %r [%s] %s' % (self.options.jstests_suite,
                                           self.options.jstests_server,
                                           self.options.jstests_browsers))

        tests = self.conn.run_tests(self.options.jstests_suite,
                                    self.options.jstests_browsers)
        for test in tests['results']:
            successful = True
            # TODO(Kumar) find a way to not parse results twice.
            for assertion in test['assertions']:
                if not assertion['result']:
                    successful = False
                    break
            yield JSTestCase(test)
            if self.result.shouldStop and not self.successful:
                break

    def prepareTestResult(self, result):
        self.result = result

class JSTestError(Exception):
    pass

class JSTestCase(unittest.TestCase):
    """A test case that represents a remote test known by the server."""
    __test__ = False # this is not a collectible test

    def __init__(self, test):
        self.test = test
        super(JSTestCase, self).__init__()

    def runTest(self):
        pass

    def run(self, result):
        result.startTest(self)
        try:
            passed = True
            # Since unittest does not log assertions,
            # iterate until the first failure (if there is one).
            for assertion in self.test['assertions']:
                if not assertion['result']:
                    passed = False
                    # log.debug(repr(self.test))
                    msg = assertion['message'] or '<unknown error>'
                    traceback = None # Python
                    # TODO(Kumar) add shortened worker_user_agent here?
                    e = (JSTestError, "[worker=%s] %s %s" % (
                         assertion['worker_id'], msg,
                         assertion['stacktrace'] or ''), traceback)
                    result.addError(self, e)
                    break
            if passed:
                result.addSuccess(self)
        finally:
            result.stopTest(self)

    def address(self):
        return (self.id(), None, None)

    def id(self):
        return repr(self)

    def shortDescription(self):
        return "%r: %s: %s" % (self, self.test['module'], self.test['test'])

    def __repr__(self):
        return "JS"

    __str__ = __repr__
<|endoftext|>


<commit_before># Copyright 2014 Google Inc. All Rights Reserved.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Measurements for OpenHTF.

Measurements in OpenHTF are used to represent values collected during a Test.
They can be numeric or string values, and can be configured such that the
OpenHTF framework will automatically check them against Pass/Fail criteria.
Measurements should not be used for large binary blobs, which are instead best
stored as Attachments (see attachments.py).

Measurements are described by the measurements.Measurement class, a subclass of
data.Descriptor with some additional functionality for Units and Dimensions.
See data.py for more information on data descriptors.  Essentially, the
Measurement class is used by test authors to declare measurements by name, and
to optionally provide unit, type, and validation information.  Measurements
are attached to Test Phases using the @measurements.measures() decorator.

When measurements are output by the OpenHTF framework, the Measurement is
serialized as a measurements.Declaration, and the values themselves are output
as per measurements.Measurement.GetValue().  test_record.PhaseRecord objects
store separate data structures for declarations and values.  See test_record.py
for more information.

Examples:

@measurements.measures(
    measurements.Measurement(
        'number_widgets').Integer().InRange(5, 10).Doc(
        '''This phase parameter tracks the number of widgets.'''))
@measurements.measures(
    [measurements.Measurement(
        'level_%s' % i).Number() for i in ['none', 'some', 'all']])
def WidgetTestPhase(test):
  ...

"""

import collections
import inspect
import itertools

# from openhtf.io import records
from openhtf.io import test_record
from openhtf.util import data

class InvalidDimensionsError(Exception):
  """Raised when there is a problem with measurement dimensions."""

class InvalidType(Exception):
  """Raised when an unexpected measurement type is given."""

class StatusError(Exception):
  """Raised when checking status of a measurement without a status."""

class MeasurementError(Exception):
  """A generic exception for problems with measurements."""

class MeasurementNotSetError(Exception):
  """Raised when a measurement is accessed that hasn't been set."""

class NotAMeasurementError(Exception):
  """Raised if an invalid measurement name is accessed."""

class DuplicateNameError(Exception):
  """An exception which occurs when a measurement name collision occurs."""

class MultipleValidatorsException(Exception):
  """Multiple validators were used when defining a measurement."""

class Measurement(data.Descriptor):
  """Data descriptor specifically for measurements."""

  def __init__(self, name):
    super(Measurement, self).__init__()
    self.name = name
    self.unit_code = None
    self.dimensions = None  # Tuple of unit codes of additional dimensions.

  def WithUnitCode(self, unit_code):
    """Declare the unit for this Descriptor."""
    self.unit_code = unit_code
    return self

  def WithDimensions(self, *dimensions):
    """Declare dimensions for this Mmeasurement."""
    self.dimensions = dimensions
    return self

class Declaration(collections.namedtuple(
    'Declaration',
    'name units dimensions numeric_lower_bound numeric_upper_bound '
    'string_regex validator_code outcome')):
  """
  Describes a measurement as it was declared.

  At most one of the following may be set:
    - Lower and/or upper numeric limits
    - A regular expression for matching against strings
    - The source code of an arbitrary validation function

  Attributes:
    units: UOM code of the units for the measurement being taken.
    dimensions: Tuple of UOM codes for units of dimensions.
    numeric_lower_bound: For numeric measurements, the lower bound.
    numeric_upper_bound: For numeric measurements, the upper bound.
    string_regex: For string measurements, a regular expression to match.
    validator_code: Source code of a custom validator.
    outcome: Either 'PASS' or 'FAIL' if the value was set, otherwise None.
  """

  @classmethod
  def FromMeasurement(cls, descriptor, value=None):
    """Construct a Declaration from a Measurement."""
    numeric_lower_bound = numeric_upper_bound = None
    string_regex = None
    validator_code = None
    outcome = None if value is None else True

    for validator in descriptor.validators:
      if value is not None:
        outcome = outcome and validator.SafeValidate(value)
      if isinstance(validator, data.InRange):
        numeric_lower_bound = validator.minimum
        numeric_upper_bound = validator.maximum
      elif isinstance(validator, data.Equals):
        numeric_lower_bound = numeric_upper_bound = validator.expected
      elif isinstance(validator, data.MatchesRegex):
        string_regex = validator.regex_pattern
      else:
        validator_code = inspect.getsource(validator)

    return cls(
        descriptor.name, descriptor.unit_code, descriptor.dimensions,
        numeric_lower_bound, numeric_upper_bound, string_regex, validator_code,
        {None: None, True: 'PASS', False: 'FAIL'}[outcome])

class MeasuredValue(object):
  """Class encapsulating actual values measured.

  Note that this is really just a value wrapper with some sanity checks.  See
  Declaration for the class that handles the descriptive aspect of the
  measurement.  This class is the type that Collection actually stores in
  its _values attribute.

  Dimensional MeasuredValues can be converted to dicts, but undimensioned
  MeasuredValues will raise InvalidDimensionsError if this is attempted.
  """

  def __init__(self, name, num_dimensions):
    self.name = name
    self.num_dimensions = num_dimensions
    # Only one of these will actually be used; if num_dimensions is 0, then
    # only self.value is used, otherwise only self.values is used.
    self.values = {}
    self.value = None

  def __iter__(self):
    if self.num_dimesions:
      return self.values.iteritems()
    raise InvalidDimensionsError(
        'Cannot iterate over undimensioned measurement.')

  @classmethod
  def ForDeclaration(cls, declaration):
    """Create an unset MeasuredValue for this declaration."""
    if declaration.dimensions:
      return cls(declaration.name, len(declaration.dimensions))
    else:
      return cls(declaration.name, 0)

  def SetValue(self, value):
    if self.num_dimensions:
      raise InvalidDimensionsError(
          'Expected %s-dimensional coordinates, got dimensionless value %s' % (
              self.num_dimensions, value))

    if self.value is not None:
      logging.warning(
          'Overriding previous measurement %s value of %s with %s',
          self.name, self.value, value)
    self.value = value

  def __setitem__(self, coordinates, value):
    coordinates_len = len(coordinates) if hasattr(coordinates, '__len__') else 1
    if coordinates_len != self.num_dimensions:
      raise InvalidDimensionsError(
          'Expected %s-dimensional coordinates, got %s' % (self.num_dimensions,
                                                           coordinates_len))
    if coordinates in self.values:
      logging.warning(
          'Overriding previous measurement %s[%s] value of %s with %s',
          self.name, coordinates, self.values[coordinates], value)
    self.values[coordinates] = value

  def __getitem__(self, coordinates):
    return self.values[coordinates]

  def GetValue(self):
    """Return the value(s) stored in this record.

    If this measurement is dimensioned, the return value will be a list of
    tuples; the last element of each tuple will be the measured value, the
    other elements will be the assocated coordinates.  Otherwise, the return
    value will simply be the value that was most recently set for this
    measurement (or None if it wasn't set).
    """
    if self.num_dimensions > 1:
      # We have dimensions, create the tuples to output
      return [tuple(dimensions) + (value,) for dimensions, value in
              self.values.iteritems()]
    elif self.num_dimensions == 1:
      # With only a single dimension, we don't have to tuple() them.
      return self.values.items()
    else:
      # We have no dimensions, just output our value.
      return self.value

class Collection(object):  # pylint: disable=too-few-public-methods
  """Encapsulates a collection of measurements.

  This collection can have measurement values retrieved and set via getters and
  setters.

  A Collection is created with a list of Descriptor objects (defined above).
  Measurements can't be added after initialization, only accessed and set.

  MeasuredValue values can be set as attributes (see below).  They can also be
  read as attributes, but you get a MeasuredValue object back if the measurement
  accessed is dimensioned (this is how setting of dimensioned measurements
  works, and so is unavoidable).

  Iterating over a Collection results in (key, value) tuples of only set
  measurements and their values.  As such, a Collection can be converted to
  a dict if you want to see all of a dimensioned measurement's values.
  Alternatively, MeasuredValue objects can also be converted to dicts.

  Example:
    from openhtf.util import measurements
    from openhtf.util.units import UOM

    self.measurements = measurements.Collection([
        measurements.Descriptor('widget_height'),
        measurements.Descriptor('widget_freq_response').WithDimensions(
            UOM['HERTZ'])])
    self.measurements.widget_height = 3
    print self.measurements.widget_height            # 3
    self.measurements.widget_freq_response[5] = 10
    print self.measurements.widget_freq_response[5]  # 10
    self.measurements.widget_freq_response[6] = 11
    print dict(self.measurements.widget_freq_response)
    # {5: 10, 6: 11}

    # Not recommended, but you can also do this.
    print dict(self.measurements)['widget_freq_response']
    # [(5, 10), (6, 11)]
  """

  __slots__ = ('_declarations', '_values')

  def __init__(self, measurement_declarations):
    # We have setattr so we have to bypass it to set attributes.
    object.__setattr__(self, '_declarations', measurement_declarations)
    object.__setattr__(self, '_values', {})

  def _AssertValidKey(self, name):
    """Raises if name is not a valid measurement."""
    if name not in self._declarations:
      raise NotAMeasurementError('Not a measurement', name)

  def __iter__(self):
    def _GetMeasValue(item):
      return item[0], item[1].GetValue()
    return itertools.imap(_GetMeasValue, self._values.iteritems())

  def __setattr__(self, name, value):
    self._AssertValidKey(name)
    record = self._values.setdefault(
        name, MeasuredValue.ForDeclaration(self._declarations[name]))
    record.SetValue(value)

  def __getattr__(self, name):  # pylint: disable=invalid-name
    self._AssertValidKey(name)
    if self._declarations[name].dimensions:
      return self._values.setdefault(name, MeasuredValue.ForDeclaration(
          self._declarations[name]))
    if name not in self._values:
      raise MeasurementNotSetError('Measurement not yet set', name)
    return self._values[name].GetValue()

  def __setitem__(self, name, value):
    raise NotImplementedError(
        'Measurement values can only be set via attributes.')

  def __getitem__(self, name):
    """When accessed as a dictionary, get the actual value(s) stored."""
    if name in self._values:
      return self._values[name].GetValue()
    return None

  def __len__(self):  # pylint: disable=invalid-name
    return len(self._declarations)

def measures(measurements):
  """Decorator-maker used to declare measurements for phases.

  See the measurements module docstring for examples of usage.

  Args:
    measurements: List of Measurement objects to declare, or a single
        Measurement object to attach, or a string name from which to
        create a Measurement.
  Returns:
    A decorator that declares the measurement(s) for the decorated phase.
  """
  def _maybe_make(meas):
    """Turn strings into Measurement objects if necessary."""
    if isinstance(meas, Measurement):
      return meas
    elif isinstance(meas, basestring):
      return Measurement(meas)
    raise InvalidType('Invalid measurement type: %s' % meas)

  # If we were passed in an iterable, make sure each element is an
  # instance of Measurement.
  if hasattr(measurements, '__iter__'):
    measurements = [_maybe_make(meas) for meas in measurements]
  else:
    measurements = [_maybe_make(measurements)]

  # 'descriptors' is guaranteed to be a list of Descriptors here.
  def decorate(wrapped_phase):
    """Phase decorator to be returned."""
    phase = wrapped_phase
    while hasattr(phase, 'wraps'):
      phase = phase.wraps

    if not hasattr(phase, 'measurement_descriptors'):
      phase.measurement_descriptors = []
    phase.measurement_descriptors.extend(measurements)
    return wrapped_phase
  return decorate
<commit_msg>typo fix in comment<commit_after># Copyright 2014 Google Inc. All Rights Reserved.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Measurements for OpenHTF.

Measurements in OpenHTF are used to represent values collected during a Test.
They can be numeric or string values, and can be configured such that the
OpenHTF framework will automatically check them against Pass/Fail criteria.
Measurements should not be used for large binary blobs, which are instead best
stored as Attachments (see attachments.py).

Measurements are described by the measurements.Measurement class, a subclass of
data.Descriptor with some additional functionality for Units and Dimensions.
See data.py for more information on data descriptors.  Essentially, the
Measurement class is used by test authors to declare measurements by name, and
to optionally provide unit, type, and validation information.  Measurements
are attached to Test Phases using the @measurements.measures() decorator.

When measurements are output by the OpenHTF framework, the Measurement is
serialized as a measurements.Declaration, and the values themselves are output
as per measurements.Measurement.GetValue().  test_record.PhaseRecord objects
store separate data structures for declarations and values.  See test_record.py
for more information.

Examples:

@measurements.measures(
    measurements.Measurement(
        'number_widgets').Integer().InRange(5, 10).Doc(
        '''This phase parameter tracks the number of widgets.'''))
@measurements.measures(
    [measurements.Measurement(
        'level_%s' % i).Number() for i in ['none', 'some', 'all']])
def WidgetTestPhase(test):
  ...

"""

import collections
import inspect
import itertools

# from openhtf.io import records
from openhtf.io import test_record
from openhtf.util import data

class InvalidDimensionsError(Exception):
  """Raised when there is a problem with measurement dimensions."""

class InvalidType(Exception):
  """Raised when an unexpected measurement type is given."""

class StatusError(Exception):
  """Raised when checking status of a measurement without a status."""

class MeasurementError(Exception):
  """A generic exception for problems with measurements."""

class MeasurementNotSetError(Exception):
  """Raised when a measurement is accessed that hasn't been set."""

class NotAMeasurementError(Exception):
  """Raised if an invalid measurement name is accessed."""

class DuplicateNameError(Exception):
  """An exception which occurs when a measurement name collision occurs."""

class MultipleValidatorsException(Exception):
  """Multiple validators were used when defining a measurement."""

class Measurement(data.Descriptor):
  """Data descriptor specifically for measurements."""

  def __init__(self, name):
    super(Measurement, self).__init__()
    self.name = name
    self.unit_code = None
    self.dimensions = None  # Tuple of unit codes of additional dimensions.

  def WithUnitCode(self, unit_code):
    """Declare the unit for this Descriptor."""
    self.unit_code = unit_code
    return self

  def WithDimensions(self, *dimensions):
    """Declare dimensions for this Measurement."""
    self.dimensions = dimensions
    return self

class Declaration(collections.namedtuple(
    'Declaration',
    'name units dimensions numeric_lower_bound numeric_upper_bound '
    'string_regex validator_code outcome')):
  """
  Describes a measurement as it was declared.

  At most one of the following may be set:
    - Lower and/or upper numeric limits
    - A regular expression for matching against strings
    - The source code of an arbitrary validation function

  Attributes:
    units: UOM code of the units for the measurement being taken.
    dimensions: Tuple of UOM codes for units of dimensions.
    numeric_lower_bound: For numeric measurements, the lower bound.
    numeric_upper_bound: For numeric measurements, the upper bound.
    string_regex: For string measurements, a regular expression to match.
    validator_code: Source code of a custom validator.
    outcome: Either 'PASS' or 'FAIL' if the value was set, otherwise None.
  """

  @classmethod
  def FromMeasurement(cls, descriptor, value=None):
    """Construct a Declaration from a Measurement."""
    numeric_lower_bound = numeric_upper_bound = None
    string_regex = None
    validator_code = None
    outcome = None if value is None else True

    for validator in descriptor.validators:
      if value is not None:
        outcome = outcome and validator.SafeValidate(value)
      if isinstance(validator, data.InRange):
        numeric_lower_bound = validator.minimum
        numeric_upper_bound = validator.maximum
      elif isinstance(validator, data.Equals):
        numeric_lower_bound = numeric_upper_bound = validator.expected
      elif isinstance(validator, data.MatchesRegex):
        string_regex = validator.regex_pattern
      else:
        validator_code = inspect.getsource(validator)

    return cls(
        descriptor.name, descriptor.unit_code, descriptor.dimensions,
        numeric_lower_bound, numeric_upper_bound, string_regex, validator_code,
        {None: None, True: 'PASS', False: 'FAIL'}[outcome])

class MeasuredValue(object):
  """Class encapsulating actual values measured.

  Note that this is really just a value wrapper with some sanity checks.  See
  Declaration for the class that handles the descriptive aspect of the
  measurement.  This class is the type that Collection actually stores in
  its _values attribute.

  Dimensional MeasuredValues can be converted to dicts, but undimensioned
  MeasuredValues will raise InvalidDimensionsError if this is attempted.
  """

  def __init__(self, name, num_dimensions):
    self.name = name
    self.num_dimensions = num_dimensions
    # Only one of these will actually be used; if num_dimensions is 0, then
    # only self.value is used, otherwise only self.values is used.
    self.values = {}
    self.value = None

  def __iter__(self):
    if self.num_dimesions:
      return self.values.iteritems()
    raise InvalidDimensionsError(
        'Cannot iterate over undimensioned measurement.')

  @classmethod
  def ForDeclaration(cls, declaration):
    """Create an unset MeasuredValue for this declaration."""
    if declaration.dimensions:
      return cls(declaration.name, len(declaration.dimensions))
    else:
      return cls(declaration.name, 0)

  def SetValue(self, value):
    if self.num_dimensions:
      raise InvalidDimensionsError(
          'Expected %s-dimensional coordinates, got dimensionless value %s' % (
              self.num_dimensions, value))

    if self.value is not None:
      logging.warning(
          'Overriding previous measurement %s value of %s with %s',
          self.name, self.value, value)
    self.value = value

  def __setitem__(self, coordinates, value):
    coordinates_len = len(coordinates) if hasattr(coordinates, '__len__') else 1
    if coordinates_len != self.num_dimensions:
      raise InvalidDimensionsError(
          'Expected %s-dimensional coordinates, got %s' % (self.num_dimensions,
                                                           coordinates_len))
    if coordinates in self.values:
      logging.warning(
          'Overriding previous measurement %s[%s] value of %s with %s',
          self.name, coordinates, self.values[coordinates], value)
    self.values[coordinates] = value

  def __getitem__(self, coordinates):
    return self.values[coordinates]

  def GetValue(self):
    """Return the value(s) stored in this record.

    If this measurement is dimensioned, the return value will be a list of
    tuples; the last element of each tuple will be the measured value, the
    other elements will be the assocated coordinates.  Otherwise, the return
    value will simply be the value that was most recently set for this
    measurement (or None if it wasn't set).
    """
    if self.num_dimensions > 1:
      # We have dimensions, create the tuples to output
      return [tuple(dimensions) + (value,) for dimensions, value in
              self.values.iteritems()]
    elif self.num_dimensions == 1:
      # With only a single dimension, we don't have to tuple() them.
      return self.values.items()
    else:
      # We have no dimensions, just output our value.
      return self.value

class Collection(object):  # pylint: disable=too-few-public-methods
  """Encapsulates a collection of measurements.

  This collection can have measurement values retrieved and set via getters and
  setters.

  A Collection is created with a list of Descriptor objects (defined above).
  Measurements can't be added after initialization, only accessed and set.

  MeasuredValue values can be set as attributes (see below).  They can also be
  read as attributes, but you get a MeasuredValue object back if the measurement
  accessed is dimensioned (this is how setting of dimensioned measurements
  works, and so is unavoidable).

  Iterating over a Collection results in (key, value) tuples of only set
  measurements and their values.  As such, a Collection can be converted to
  a dict if you want to see all of a dimensioned measurement's values.
  Alternatively, MeasuredValue objects can also be converted to dicts.

  Example:
    from openhtf.util import measurements
    from openhtf.util.units import UOM

    self.measurements = measurements.Collection([
        measurements.Descriptor('widget_height'),
        measurements.Descriptor('widget_freq_response').WithDimensions(
            UOM['HERTZ'])])
    self.measurements.widget_height = 3
    print self.measurements.widget_height            # 3
    self.measurements.widget_freq_response[5] = 10
    print self.measurements.widget_freq_response[5]  # 10
    self.measurements.widget_freq_response[6] = 11
    print dict(self.measurements.widget_freq_response)
    # {5: 10, 6: 11}

    # Not recommended, but you can also do this.
    print dict(self.measurements)['widget_freq_response']
    # [(5, 10), (6, 11)]
  """

  __slots__ = ('_declarations', '_values')

  def __init__(self, measurement_declarations):
    # We have setattr so we have to bypass it to set attributes.
    object.__setattr__(self, '_declarations', measurement_declarations)
    object.__setattr__(self, '_values', {})

  def _AssertValidKey(self, name):
    """Raises if name is not a valid measurement."""
    if name not in self._declarations:
      raise NotAMeasurementError('Not a measurement', name)

  def __iter__(self):
    def _GetMeasValue(item):
      return item[0], item[1].GetValue()
    return itertools.imap(_GetMeasValue, self._values.iteritems())

  def __setattr__(self, name, value):
    self._AssertValidKey(name)
    record = self._values.setdefault(
        name, MeasuredValue.ForDeclaration(self._declarations[name]))
    record.SetValue(value)

  def __getattr__(self, name):  # pylint: disable=invalid-name
    self._AssertValidKey(name)
    if self._declarations[name].dimensions:
      return self._values.setdefault(name, MeasuredValue.ForDeclaration(
          self._declarations[name]))
    if name not in self._values:
      raise MeasurementNotSetError('Measurement not yet set', name)
    return self._values[name].GetValue()

  def __setitem__(self, name, value):
    raise NotImplementedError(
        'Measurement values can only be set via attributes.')

  def __getitem__(self, name):
    """When accessed as a dictionary, get the actual value(s) stored."""
    if name in self._values:
      return self._values[name].GetValue()
    return None

  def __len__(self):  # pylint: disable=invalid-name
    return len(self._declarations)

def measures(measurements):
  """Decorator-maker used to declare measurements for phases.

  See the measurements module docstring for examples of usage.

  Args:
    measurements: List of Measurement objects to declare, or a single
        Measurement object to attach, or a string name from which to
        create a Measurement.
  Returns:
    A decorator that declares the measurement(s) for the decorated phase.
  """
  def _maybe_make(meas):
    """Turn strings into Measurement objects if necessary."""
    if isinstance(meas, Measurement):
      return meas
    elif isinstance(meas, basestring):
      return Measurement(meas)
    raise InvalidType('Invalid measurement type: %s' % meas)

  # If we were passed in an iterable, make sure each element is an
  # instance of Measurement.
  if hasattr(measurements, '__iter__'):
    measurements = [_maybe_make(meas) for meas in measurements]
  else:
    measurements = [_maybe_make(measurements)]

  # 'descriptors' is guaranteed to be a list of Descriptors here.
  def decorate(wrapped_phase):
    """Phase decorator to be returned."""
    phase = wrapped_phase
    while hasattr(phase, 'wraps'):
      phase = phase.wraps

    if not hasattr(phase, 'measurement_descriptors'):
      phase.measurement_descriptors = []
    phase.measurement_descriptors.extend(measurements)
    return wrapped_phase
  return decorate
<|endoftext|>


<commit_before>#
# Copyright (c) 2014 NSONE, Inc.
#
# License under The MIT License (MIT). See LICENSE in project root.
from __future__ import absolute_import

from nsone.rest.transport.base import TransportBase
from nsone.rest.errors import ResourceException, RateLimitException, \
    AuthException
import json
import random
import StringIO

try:
    from twisted.internet import reactor
    from twisted.web.client import Agent, readBody, FileBodyProducer
    from twisted.web.http_headers import Headers
    from twisted.internet.defer import succeed
    have_twisted = True
except Exception as e:
    have_twisted = False

def encodeForm(varname, f, ctype):
    randomChars = [str(random.randrange(10)) for _ in range(28)]
    boundary = "".join(randomChars)

    lines = ['--' + boundary]

    def add(name, content):
        header = 'Content-Disposition: form-data; name="%s"; filename="%s"' % \
                 (name, f.name)
        header += ";\r\nContent-Type: %s" % ctype
        lines.extend([header, "", content])

    add(varname, f.read())

    lines.extend(['--' + boundary + "--", ""])
    return boundary, "\r\n".join(lines)

class StringProducer(object):

    def __init__(self, body):
        self.body = body
        self.length = len(body)

    def startProducing(self, consumer):
        consumer.write(self.body)
        return succeed(None)

    def pauseProducing(self):
        pass

    def stopProducing(self):
        pass

class TwistedTransport(TransportBase):

    def __init__(self, config):
        if not have_twisted:
            raise ImportError('Twisted required for TwistedTransport')
        TransportBase.__init__(self, config, self.__module__)
        self.agent = Agent(reactor)

    def _callback(self, response, user_callback, data, headers):
        d = readBody(response)
        d.addCallback(self._onBody, response, user_callback, data, headers)
        return d

    def _onBody(self, body, response, user_callback, data, headers):
        self._logHeaders(headers)
        self._log.debug("%s %s %s %s" % (response.request.method,
                                         response.request.absoluteURI,
                                         response.code,
                                         data))
        if response.code != 200:
            if response.code == 429:
                raise RateLimitException('rate limit exceeded',
                                         response,
                                         body)
            elif response.code == 401:
                raise AuthException('unauthorized',
                                    response,
                                    body)
            else:
                raise ResourceException('server error', response, body)
        try:
            jsonOut = json.loads(body)
        except:
            raise ResourceException('invalid json in response',
                                    response,
                                    body)
        if user_callback:
            # set these in case callback throws, so we have them for errback
            self.response = response
            self.body = body
            return user_callback(jsonOut, body, response)
        else:
            return jsonOut

    def _errback(self, failure, user_errback):
        # print "failure: %s" % failure.printTraceback()
        if user_errback:
            return user_errback(failure)
        else:
            if failure.check(ResourceException, RateLimitException):
                raise failure.value
            raise ResourceException(failure.getErrorMessage())

    def send(self, method, url, headers=None, data=None, files=None,
             callback=None, errback=None):
        bProducer = None
        if data:
            bProducer = StringProducer(data)
        elif files:
            if len(files) > 1:
                raise Exception('twisted transport currently only accepts one'
                                ' multipart file')
            boundary, body = encodeForm(files[0][0],
                                        files[0][1][1],
                                        files[0][1][2])
            if headers is None:
                headers = {}
            headers['Content-Type'] =\
                "multipart/form-data; boundary={}".format(boundary)
            bProducer = FileBodyProducer(StringIO.StringIO(body))
        theaders = None
        if headers:
            theaders = Headers({str(k): [str(v)]
                                for (k, v) in headers.iteritems()})
        d = self.agent.request(method, str(url), theaders, bProducer)
        d.addCallback(self._callback, callback, data, headers)
        d.addErrback(self._errback, errback)
        return d

TransportBase.REGISTRY['twisted'] = TwistedTransport
<commit_msg>Adds twisted 16 ssl support.<commit_after>#
# Copyright (c) 2014 NSONE, Inc.
#
# License under The MIT License (MIT). See LICENSE in project root.
from __future__ import absolute_import

from nsone.rest.transport.base import TransportBase
from nsone.rest.errors import ResourceException, RateLimitException, \
    AuthException
import json
import random
import StringIO

try:
    from twisted.internet import reactor
    from twisted.web.client import Agent, readBody, FileBodyProducer
    from twisted.web.http_headers import Headers
    from twisted.internet.defer import succeed
    from twisted.internet.ssl import ClientContextFactory
    have_twisted = True
except Exception as e:
    have_twisted = False

class WebClientContextFactory(ClientContextFactory):
    def getContext(self, hostname, port):
        return ClientContextFactory.getContext(self)

def encodeForm(varname, f, ctype):
    randomChars = [str(random.randrange(10)) for _ in range(28)]
    boundary = "".join(randomChars)

    lines = ['--' + boundary]

    def add(name, content):
        header = 'Content-Disposition: form-data; name="%s"; filename="%s"' % \
                 (name, f.name)
        header += ";\r\nContent-Type: %s" % ctype
        lines.extend([header, "", content])

    add(varname, f.read())

    lines.extend(['--' + boundary + "--", ""])
    return boundary, "\r\n".join(lines)

class StringProducer(object):

    def __init__(self, body):
        self.body = body
        self.length = len(body)

    def startProducing(self, consumer):
        consumer.write(self.body)
        return succeed(None)

    def pauseProducing(self):
        pass

    def stopProducing(self):
        pass

class TwistedTransport(TransportBase):

    def __init__(self, config):
        if not have_twisted:
            raise ImportError('Twisted required for TwistedTransport')
        TransportBase.__init__(self, config, self.__module__)
        contextFactory = WebClientContextFactory()
        self.agent = Agent(reactor, contextFactory)

    def _callback(self, response, user_callback, data, headers):
        d = readBody(response)
        d.addCallback(self._onBody, response, user_callback, data, headers)
        return d

    def _onBody(self, body, response, user_callback, data, headers):
        self._logHeaders(headers)
        self._log.debug("%s %s %s %s" % (response.request.method,
                                         response.request.absoluteURI,
                                         response.code,
                                         data))
        if response.code != 200:
            if response.code == 429:
                raise RateLimitException('rate limit exceeded',
                                         response,
                                         body)
            elif response.code == 401:
                raise AuthException('unauthorized',
                                    response,
                                    body)
            else:
                raise ResourceException('server error', response, body)
        try:
            jsonOut = json.loads(body)
        except:
            raise ResourceException('invalid json in response',
                                    response,
                                    body)
        if user_callback:
            # set these in case callback throws, so we have them for errback
            self.response = response
            self.body = body
            return user_callback(jsonOut, body, response)
        else:
            return jsonOut

    def _errback(self, failure, user_errback):
        # print "failure: %s" % failure.printTraceback()
        if user_errback:
            return user_errback(failure)
        else:
            if failure.check(ResourceException, RateLimitException):
                raise failure.value
            raise ResourceException(failure.getErrorMessage())

    def send(self, method, url, headers=None, data=None, files=None,
             callback=None, errback=None):
        bProducer = None
        if data:
            bProducer = StringProducer(data)
        elif files:
            if len(files) > 1:
                raise Exception('twisted transport currently only accepts one'
                                ' multipart file')
            boundary, body = encodeForm(files[0][0],
                                        files[0][1][1],
                                        files[0][1][2])
            if headers is None:
                headers = {}
            headers['Content-Type'] =\
                "multipart/form-data; boundary={}".format(boundary)
            bProducer = FileBodyProducer(StringIO.StringIO(body))
        theaders = None
        if headers:
            theaders = Headers({str(k): [str(v)]
                                for (k, v) in headers.iteritems()})
        d = self.agent.request(method, str(url), theaders, bProducer)
        d.addCallback(self._callback, callback, data, headers)
        d.addErrback(self._errback, errback)
        return d

TransportBase.REGISTRY['twisted'] = TwistedTransport
<|endoftext|>


<commit_before>import datetime
import django
import logging
import warnings

from django.db import models
from django.db.models import signals
from django.template import TemplateSyntaxError
from django.views.debug import ExceptionReporter

from sentry import settings
from sentry.helpers import construct_checksum, transform, varmap

assert not settings.DATABASE_USING or django.VERSION >= (1, 2), 'The `SENTRY_DATABASE_USING` setting requires Django >= 1.2'

logger = logging.getLogger('sentry')

class SentryManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        qs = super(SentryManager, self).get_query_set()
        if settings.DATABASE_USING:
            qs = qs.using(settings.DATABASE_USING)
        return qs

    def from_kwargs(self, **kwargs):
        from sentry.models import Message, GroupedMessage

        URL_MAX_LENGTH = Message._meta.get_field_by_name('url')[0].max_length
        now = datetime.datetime.now()

        view = kwargs.pop('view', None)
        logger_name = kwargs.pop('logger', 'root')
        url = kwargs.pop('url', None)
        server_name = kwargs.pop('server_name', )
        data = kwargs.pop('data', {}) or {}

        if url:
            data['url'] = url
            url = url[:URL_MAX_LENGTH]

        checksum = construct_checksum(**kwargs)

        mail = False
        try:
            group, created = GroupedMessage.objects.get_or_create(
                view=view,
                logger=logger_name,
                checksum=checksum,
                defaults=kwargs
            )
            if not created:
                GroupedMessage.objects.filter(pk=group.pk).update(
                    times_seen=models.F('times_seen') + 1,
                    status=0,
                    last_seen=now,
                )
                # HACK: maintain appeared state
                if group.status == 1:
                    mail = True
                group.status = 0
                group.last_seen = now
                group.times_seen += 1
                signals.post_save.send(sender=GroupedMessage, instance=group, created=False)
            else:
                mail = True
            instance = Message.objects.create(
                view=view,
                logger=logger_name,
                data=data,
                url=url,
                server_name=server_name,
                checksum=checksum,
                group=group,
                **kwargs
            )
        except Exception, exc:
            # TODO: should we mail admins when there are failures?
            try:
                logger.exception(u'Unable to process log entry: %s' % (exc,))
            except Exception, exc:
                warnings.warn(u'Unable to process log entry: %s' % (exc,))
        else:
            if mail:
                group.mail_admins()
            return instance

class GroupedMessageManager(SentryManager):
    def get_by_natural_key(self, logger, view, checksum):
        return self.get(logger=logger, view=view, checksum=checksum)<commit_msg>Somehow slipped with server_name. Fixing passing of settings.CLIENT.<commit_after>import datetime
import django
import logging
import warnings

from django.db import models
from django.db.models import signals
from django.template import TemplateSyntaxError
from django.views.debug import ExceptionReporter

from sentry import settings
from sentry.helpers import construct_checksum, transform, varmap

assert not settings.DATABASE_USING or django.VERSION >= (1, 2), 'The `SENTRY_DATABASE_USING` setting requires Django >= 1.2'

logger = logging.getLogger('sentry')

class SentryManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        qs = super(SentryManager, self).get_query_set()
        if settings.DATABASE_USING:
            qs = qs.using(settings.DATABASE_USING)
        return qs

    def from_kwargs(self, **kwargs):
        from sentry.models import Message, GroupedMessage

        URL_MAX_LENGTH = Message._meta.get_field_by_name('url')[0].max_length
        now = datetime.datetime.now()

        view = kwargs.pop('view', None)
        logger_name = kwargs.pop('logger', 'root')
        url = kwargs.pop('url', None)
        server_name = kwargs.pop('server_name', settings.CLIENT)
        data = kwargs.pop('data', {}) or {}

        if url:
            data['url'] = url
            url = url[:URL_MAX_LENGTH]

        checksum = construct_checksum(**kwargs)

        mail = False
        try:
            group, created = GroupedMessage.objects.get_or_create(
                view=view,
                logger=logger_name,
                checksum=checksum,
                defaults=kwargs
            )
            if not created:
                GroupedMessage.objects.filter(pk=group.pk).update(
                    times_seen=models.F('times_seen') + 1,
                    status=0,
                    last_seen=now,
                )
                # HACK: maintain appeared state
                if group.status == 1:
                    mail = True
                group.status = 0
                group.last_seen = now
                group.times_seen += 1
                signals.post_save.send(sender=GroupedMessage, instance=group, created=False)
            else:
                mail = True
            instance = Message.objects.create(
                view=view,
                logger=logger_name,
                data=data,
                url=url,
                server_name=server_name,
                checksum=checksum,
                group=group,
                **kwargs
            )
        except Exception, exc:
            # TODO: should we mail admins when there are failures?
            try:
                logger.exception(u'Unable to process log entry: %s' % (exc,))
            except Exception, exc:
                warnings.warn(u'Unable to process log entry: %s' % (exc,))
        else:
            if mail:
                group.mail_admins()
            return instance

class GroupedMessageManager(SentryManager):
    def get_by_natural_key(self, logger, view, checksum):
        return self.get(logger=logger, view=view, checksum=checksum)<|endoftext|>