<commit_before>import os
import importlib
from datetime import datetime
from threading import Thread
from pathlib import Path

from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.app.cta_strategy import (
    CtaTemplate,
    BacktestingEngine,
    OptimizationSetting
)

APP_NAME = "CtaBacktester"

EVENT_BACKTESTER_LOG = "eBacktesterLog"
EVENT_BACKTESTER_BACKTESTING_FINISHED = "eBacktesterBacktestingFinished"
EVENT_BACKTESTER_OPTIMIZATION_FINISHED = "eBacktesterOptimizationFinished"

class BacktesterEngine(BaseEngine):
    """
    For running CTA strategy backtesting.
    """

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__(main_engine, event_engine, APP_NAME)

        self.classes = {}
        self.backtesting_engine = None
        self.thread = None

        self.result_df = None
        self.result_statistics = None

        self.load_strategy_class()

    def init_engine(self):
        """"""
        self.write_log("初始化CTA回测引擎")

        self.backtesting_engine = BacktestingEngine()
        # Redirect log from backtesting engine outside.
        self.backtesting_engine.output = self.write_log

        self.write_log("策略文件加载完成")

    def write_log(self, msg: str):
        """"""
        event = Event(EVENT_BACKTESTER_LOG)
        event.data = msg
        self.event_engine.put(event)

    def load_strategy_class(self):
        """
        Load strategy class from source code.
        """
        app_path = Path(__file__).parent.parent
        path1 = app_path.joinpath("cta_strategy", "strategies")
        self.load_strategy_class_from_folder(
            path1, "vnpy.app.cta_strategy.strategies")

        path2 = Path.cwd().joinpath("strategies")
        self.load_strategy_class_from_folder(path2, "strategies")

    def load_strategy_class_from_folder(self, path: Path, module_name: str = ""):
        """
        Load strategy class from certain folder.
        """
        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                if filename.endswith(".py"):
                    strategy_module_name = ".".join(
                        [module_name, filename.replace(".py", "")])
                    self.load_strategy_class_from_module(strategy_module_name)

    def load_strategy_class_from_module(self, module_name: str):
        """
        Load strategy class from module file.
        """
        try:
            module = importlib.import_module(module_name)

            for name in dir(module):
                value = getattr(module, name)
                if (isinstance(value, type) and issubclass(value, CtaTemplate) and value is not CtaTemplate):
                    self.classes[value.__name__] = value
        except:  # noqa
            msg = f"策略文件{module_name}加载失败，触发异常：\n{traceback.format_exc()}"
            self.write_log(msg)

    def get_strategy_class_names(self):
        """"""
        return list(self.classes.keys())

    def run_backtesting(
        self,
        class_name: str,
        vt_symbol: str,
        interval: str,
        start: datetime,
        end: datetime,
        rate: float,
        slippage: float,
        size: int,
        pricetick: float,
        capital: int,
        setting: dict
    ):
        """"""
        self.result_df = None
        self.result_statistics = None

        engine = self.backtesting_engine
        engine.clear_data()

        engine.set_parameters(
            vt_symbol=vt_symbol,
            interval=interval,
            start=start,
            end=end,
            rate=rate,
            slippage=slippage,
            size=size,
            pricetick=pricetick,
            capital=capital
        )

        strategy_class = self.classes[class_name]
        engine.add_strategy(
            strategy_class,
            setting
        )

        engine.load_data()
        engine.run_backtesting()
        self.result_df = engine.calculate_result()
        self.result_statistics = engine.calculate_statistics(output=False)

        # Clear thread object handler.
        self.thread = None

        # Put backtesting done event
        event = Event(EVENT_BACKTESTER_BACKTESTING_FINISHED)
        self.event_engine.put(event)

    def start_backtesting(
        self,
        class_name: str,
        vt_symbol: str,
        interval: str,
        start: datetime,
        end: datetime,
        rate: float,
        slippage: float,
        size: int,
        pricetick: float,
        capital: int,
        setting: dict
    ):
        if self.thread:
            self.write_log("已有回测在运行中，请等待完成")
            return False

        self.thread = Thread(
            target=self.run_backtesting,
            args=(
                class_name,
                vt_symbol,
                interval,
                start,
                end,
                rate,
                slippage,
                size,
                pricetick,
                capital,
                setting
            )
        )
        self.thread.start()

        return True

    def get_result_df(self):
        """"""
        return self.result_df

    def get_result_statistics(self):
        """"""
        return self.result_statistics

    def get_default_setting(self, class_name: str):
        """"""
        strategy_class = self.classes[class_name]
        return strategy_class.get_class_parameters()
<commit_msg>[Fix]add traceback import<commit_after>import os
import importlib
import traceback
from datetime import datetime
from threading import Thread
from pathlib import Path

from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.app.cta_strategy import (
    CtaTemplate,
    BacktestingEngine
)

APP_NAME = "CtaBacktester"

EVENT_BACKTESTER_LOG = "eBacktesterLog"
EVENT_BACKTESTER_BACKTESTING_FINISHED = "eBacktesterBacktestingFinished"
EVENT_BACKTESTER_OPTIMIZATION_FINISHED = "eBacktesterOptimizationFinished"

class BacktesterEngine(BaseEngine):
    """
    For running CTA strategy backtesting.
    """

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__(main_engine, event_engine, APP_NAME)

        self.classes = {}
        self.backtesting_engine = None
        self.thread = None

        self.result_df = None
        self.result_statistics = None

        self.load_strategy_class()

    def init_engine(self):
        """"""
        self.write_log("初始化CTA回测引擎")

        self.backtesting_engine = BacktestingEngine()
        # Redirect log from backtesting engine outside.
        self.backtesting_engine.output = self.write_log

        self.write_log("策略文件加载完成")

    def write_log(self, msg: str):
        """"""
        event = Event(EVENT_BACKTESTER_LOG)
        event.data = msg
        self.event_engine.put(event)

    def load_strategy_class(self):
        """
        Load strategy class from source code.
        """
        app_path = Path(__file__).parent.parent
        path1 = app_path.joinpath("cta_strategy", "strategies")
        self.load_strategy_class_from_folder(
            path1, "vnpy.app.cta_strategy.strategies")

        path2 = Path.cwd().joinpath("strategies")
        self.load_strategy_class_from_folder(path2, "strategies")

    def load_strategy_class_from_folder(self, path: Path, module_name: str = ""):
        """
        Load strategy class from certain folder.
        """
        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                if filename.endswith(".py"):
                    strategy_module_name = ".".join(
                        [module_name, filename.replace(".py", "")])
                    self.load_strategy_class_from_module(strategy_module_name)

    def load_strategy_class_from_module(self, module_name: str):
        """
        Load strategy class from module file.
        """
        try:
            module = importlib.import_module(module_name)

            for name in dir(module):
                value = getattr(module, name)
                if (isinstance(value, type) and issubclass(value, CtaTemplate) and value is not CtaTemplate):
                    self.classes[value.__name__] = value
        except:  # noqa
            msg = f"策略文件{module_name}加载失败，触发异常：\n{traceback.format_exc()}"
            self.write_log(msg)

    def get_strategy_class_names(self):
        """"""
        return list(self.classes.keys())

    def run_backtesting(
        self,
        class_name: str,
        vt_symbol: str,
        interval: str,
        start: datetime,
        end: datetime,
        rate: float,
        slippage: float,
        size: int,
        pricetick: float,
        capital: int,
        setting: dict
    ):
        """"""
        self.result_df = None
        self.result_statistics = None

        engine = self.backtesting_engine
        engine.clear_data()

        engine.set_parameters(
            vt_symbol=vt_symbol,
            interval=interval,
            start=start,
            end=end,
            rate=rate,
            slippage=slippage,
            size=size,
            pricetick=pricetick,
            capital=capital
        )

        strategy_class = self.classes[class_name]
        engine.add_strategy(
            strategy_class,
            setting
        )

        engine.load_data()
        engine.run_backtesting()
        self.result_df = engine.calculate_result()
        self.result_statistics = engine.calculate_statistics(output=False)

        # Clear thread object handler.
        self.thread = None

        # Put backtesting done event
        event = Event(EVENT_BACKTESTER_BACKTESTING_FINISHED)
        self.event_engine.put(event)

    def start_backtesting(
        self,
        class_name: str,
        vt_symbol: str,
        interval: str,
        start: datetime,
        end: datetime,
        rate: float,
        slippage: float,
        size: int,
        pricetick: float,
        capital: int,
        setting: dict
    ):
        if self.thread:
            self.write_log("已有回测在运行中，请等待完成")
            return False

        self.thread = Thread(
            target=self.run_backtesting,
            args=(
                class_name,
                vt_symbol,
                interval,
                start,
                end,
                rate,
                slippage,
                size,
                pricetick,
                capital,
                setting
            )
        )
        self.thread.start()

        return True

    def get_result_df(self):
        """"""
        return self.result_df

    def get_result_statistics(self):
        """"""
        return self.result_statistics

    def get_default_setting(self, class_name: str):
        """"""
        strategy_class = self.classes[class_name]
        return strategy_class.get_class_parameters()
<|endoftext|>


<commit_before>"""Handle exceptions in CGI scripts by formatting tracebacks into nice HTML.

To enable this module, do:

    import cgitb; cgitb.enable()

at the top of your CGI script.  The optional arguments to enable() are:

    display     - if true, tracebacks are displayed in the web browser
    logdir      - if set, tracebacks are written to files in this directory
    context     - number of lines of source code to show for each stack frame

By default, tracebacks are displayed but not saved, and context is 5.

Alternatively, if you have caught an exception and want cgitb to display it
for you, call cgitb.handle().  The optional argument to handle() is a 3-item
tuple (etype, evalue, etb) just like the value of sys.exc_info()."""

__author__ = 'Ka-Ping Yee'
__version__ = '$Revision$'

def reset():
    """Return a string that resets the CGI and browser to a known state."""
    return '''
 --> -->
   </script> </object>

   '''

__UNDEF__ = []                          # a special sentinel object
def small(text): return '' + text + ''
def strong(text): return '' + text + ''
def grey(text): return '' + text + ''

def lookup(name, frame, locals):
    """Find the value for a given name in the given environment."""
    if name in locals:
        return 'local', locals[name]
    if name in frame.f_globals:
        return 'global', frame.f_globals[name]
    return None, __UNDEF__

def scanvars(reader, frame, locals):
    """Scan one logical line of Python and look up values of variables used."""
    import tokenize, keyword
    vars, lasttoken, parent, prefix = [], None, None, ''
    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
        if ttype == tokenize.NEWLINE: break
        if ttype == tokenize.NAME and token not in keyword.kwlist:
            if lasttoken == '.':
                if parent is not __UNDEF__:
                    value = getattr(parent, token, __UNDEF__)
                    vars.append((prefix + token, prefix, value))
            else:
                where, value = lookup(token, frame, locals)
                vars.append((token, where, value))
        elif token == '.':
            prefix += lasttoken + '.'
            parent = value
        else:
            parent, prefix = None, ''
        lasttoken = token
    return vars

def html((etype, evalue, etb), context=5):
    """Return a nice HTML document describing a given traceback."""
    import sys, os, types, time, traceback, linecache, inspect, pydoc

    if type(etype) is types.ClassType:
        etype = etype.__name__
    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
    date = time.ctime(time.time())
    head = '' + pydoc.html.heading(
        '<big><big>%s</big></big>' % str(etype),
        '#ffffff', '#6622aa', pyver + '
' + date) + '''

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.'''

    indent = '<tt>' + small(' ' * 5) + ' </tt>'
    frames = []
    records = inspect.getinnerframes(etb, context)
    for frame, file, lnum, func, lines, index in records:
        file = file and os.path.abspath(file) or '?'
        link = '%s' % (file, pydoc.html.escape(file))
        args, varargs, varkw, locals = inspect.getargvalues(frame)
        call = ''
        if func != '?':
            call = 'in ' + strong(func) + \
                inspect.formatargvalues(args, varargs, varkw, locals,
                    formatvalue=lambda value: '=' + pydoc.html.repr(value))

        highlight = {}
        def reader(lnum=[lnum]):
            highlight[lnum[0]] = 1
            try: return linecache.getline(file, lnum[0])
            finally: lnum[0] += 1
        vars = scanvars(reader, frame, locals)

        rows = ['
%s%s %s
' %
                ('<big> </big>', link, call)]
        if index is not None:
            i = lnum - index
            for line in lines:
                num = small(' ' * (5-len(str(i))) + str(i)) + ' '
                line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
                if i in highlight:
                    rows.append('
%s
' % line)
                else:
                    rows.append('
%s
' % grey(line))
                i += 1

        done, dump = {}, []
        for name, where, value in vars:
            if name in done: continue
            done[name] = 1
            if value is not __UNDEF__:
                if where == 'global': name = 'global ' + strong(name)
                elif where == 'local': name = strong(name)
                else: name = where + strong(name.split('.')[-1])
                dump.append('%s = %s' % (name, pydoc.html.repr(value)))
            else:
                dump.append(name + ' undefined')

        rows.append('
%s
' % small(grey(', '.join(dump))))
        frames.append('''

%s
''' % '\n'.join(rows))

    exception = ['
%s: %s' % (strong(str(etype)), str(evalue))]
    if type(evalue) is types.InstanceType:
        for name in dir(evalue):
            value = pydoc.html.repr(getattr(evalue, name))
            exception.append('\n
%s%s =\n%s' % (indent, name, value))

    import traceback
    return head + ''.join(frames) + ''.join(exception) + '''

''' % ''.join(traceback.format_exception(etype, evalue, etb))

class Hook:
    """A hook to replace sys.excepthook that shows tracebacks in HTML."""

    def __init__(self, display=1, logdir=None, context=5):
        self.display = display          # send tracebacks to browser if true
        self.logdir = logdir            # log tracebacks to files if not None
        self.context = context          # number of source code lines per frame

    def __call__(self, etype, evalue, etb):
        self.handle((etype, evalue, etb))

    def handle(self, info=None):
        import sys
        info = info or sys.exc_info()
        print reset()

        try:
            text, doc = 0, html(info, self.context)
        except:                         # just in case something goes wrong
            import traceback
            text, doc = 1, ''.join(traceback.format_exception(*info))

        if self.display:
            if text:
                doc = doc.replace('&', '&').replace('<', '<')
                print '
' + doc + '
'
            else:
                print doc
        else:
            print '
A problem occurred in a Python script.'

        if self.logdir is not None:
            import os, tempfile
            name = tempfile.mktemp(['.html', '.txt'][text])
            path = os.path.join(self.logdir, os.path.basename(name))
            try:
                file = open(path, 'w')
                file.write(doc)
                file.close()
                print '
 %s contains the description of this error.' % path
            except:
                print '
 Tried to save traceback to %s, but failed.' % path

handler = Hook().handle
def enable(display=1, logdir=None, context=5):
    """Install an exception handler that formats tracebacks as HTML.

    The optional argument 'display' can be set to 0 to suppress sending the
    traceback to the browser, and 'logdir' can be set to a directory to cause
    tracebacks to be written to files there."""
    import sys
    sys.excepthook = Hook(display, logdir, context)
<commit_msg>Add "file" argument to Hook constructor. By default, save sys.stdout in self.file when a Hook instance is created     (e.g. when cgitb.enable() is called).<commit_after>"""Handle exceptions in CGI scripts by formatting tracebacks into nice HTML.

To enable this module, do:

    import cgitb; cgitb.enable()

at the top of your CGI script.  The optional arguments to enable() are:

    display     - if true, tracebacks are displayed in the web browser
    logdir      - if set, tracebacks are written to files in this directory
    context     - number of lines of source code to show for each stack frame

By default, tracebacks are displayed but not saved, and context is 5.

Alternatively, if you have caught an exception and want cgitb to display it
for you, call cgitb.handle().  The optional argument to handle() is a 3-item
tuple (etype, evalue, etb) just like the value of sys.exc_info()."""

__author__ = 'Ka-Ping Yee'
__version__ = '$Revision$'

import sys

def reset():
    """Return a string that resets the CGI and browser to a known state."""
    return '''
 --> -->
   </script> </object>

   '''

__UNDEF__ = []                          # a special sentinel object
def small(text): return '' + text + ''
def strong(text): return '' + text + ''
def grey(text): return '' + text + ''

def lookup(name, frame, locals):
    """Find the value for a given name in the given environment."""
    if name in locals:
        return 'local', locals[name]
    if name in frame.f_globals:
        return 'global', frame.f_globals[name]
    return None, __UNDEF__

def scanvars(reader, frame, locals):
    """Scan one logical line of Python and look up values of variables used."""
    import tokenize, keyword
    vars, lasttoken, parent, prefix = [], None, None, ''
    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
        if ttype == tokenize.NEWLINE: break
        if ttype == tokenize.NAME and token not in keyword.kwlist:
            if lasttoken == '.':
                if parent is not __UNDEF__:
                    value = getattr(parent, token, __UNDEF__)
                    vars.append((prefix + token, prefix, value))
            else:
                where, value = lookup(token, frame, locals)
                vars.append((token, where, value))
        elif token == '.':
            prefix += lasttoken + '.'
            parent = value
        else:
            parent, prefix = None, ''
        lasttoken = token
    return vars

def html((etype, evalue, etb), context=5):
    """Return a nice HTML document describing a given traceback."""
    import os, types, time, traceback, linecache, inspect, pydoc

    if type(etype) is types.ClassType:
        etype = etype.__name__
    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
    date = time.ctime(time.time())
    head = '' + pydoc.html.heading(
        '<big><big>%s</big></big>' % str(etype),
        '#ffffff', '#6622aa', pyver + '
' + date) + '''

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.'''

    indent = '<tt>' + small(' ' * 5) + ' </tt>'
    frames = []
    records = inspect.getinnerframes(etb, context)
    for frame, file, lnum, func, lines, index in records:
        file = file and os.path.abspath(file) or '?'
        link = '%s' % (file, pydoc.html.escape(file))
        args, varargs, varkw, locals = inspect.getargvalues(frame)
        call = ''
        if func != '?':
            call = 'in ' + strong(func) + \
                inspect.formatargvalues(args, varargs, varkw, locals,
                    formatvalue=lambda value: '=' + pydoc.html.repr(value))

        highlight = {}
        def reader(lnum=[lnum]):
            highlight[lnum[0]] = 1
            try: return linecache.getline(file, lnum[0])
            finally: lnum[0] += 1
        vars = scanvars(reader, frame, locals)

        rows = ['
%s%s %s
' %
                ('<big> </big>', link, call)]
        if index is not None:
            i = lnum - index
            for line in lines:
                num = small(' ' * (5-len(str(i))) + str(i)) + ' '
                line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
                if i in highlight:
                    rows.append('
%s
' % line)
                else:
                    rows.append('
%s
' % grey(line))
                i += 1

        done, dump = {}, []
        for name, where, value in vars:
            if name in done: continue
            done[name] = 1
            if value is not __UNDEF__:
                if where == 'global': name = 'global ' + strong(name)
                elif where == 'local': name = strong(name)
                else: name = where + strong(name.split('.')[-1])
                dump.append('%s = %s' % (name, pydoc.html.repr(value)))
            else:
                dump.append(name + ' undefined')

        rows.append('
%s
' % small(grey(', '.join(dump))))
        frames.append('''

%s
''' % '\n'.join(rows))

    exception = ['
%s: %s' % (strong(str(etype)), str(evalue))]
    if type(evalue) is types.InstanceType:
        for name in dir(evalue):
            value = pydoc.html.repr(getattr(evalue, name))
            exception.append('\n
%s%s =\n%s' % (indent, name, value))

    import traceback
    return head + ''.join(frames) + ''.join(exception) + '''

''' % ''.join(traceback.format_exception(etype, evalue, etb))

class Hook:
    """A hook to replace sys.excepthook that shows tracebacks in HTML."""

    def __init__(self, display=1, logdir=None, context=5, file=None):
        self.display = display          # send tracebacks to browser if true
        self.logdir = logdir            # log tracebacks to files if not None
        self.context = context          # number of source code lines per frame
        self.file = file or sys.stdout  # place to send the output

    def __call__(self, etype, evalue, etb):
        self.handle((etype, evalue, etb))

    def handle(self, info=None):
        info = info or sys.exc_info()
        self.file.write(reset())

        try:
            text, doc = 0, html(info, self.context)
        except:                         # just in case something goes wrong
            import traceback
            text, doc = 1, ''.join(traceback.format_exception(*info))

        if self.display:
            if text:
                doc = doc.replace('&', '&').replace('<', '<')
                self.file.write('
' + doc + '
\n')
            else:
                self.file.write(doc + '\n')
        else:
            self.file.write('
A problem occurred in a Python script.\n')

        if self.logdir is not None:
            import os, tempfile
            name = tempfile.mktemp(['.html', '.txt'][text])
            path = os.path.join(self.logdir, os.path.basename(name))
            try:
                file = open(path, 'w')
                file.write(doc)
                file.close()
                msg = '
 %s contains the description of this error.' % path
            except:
                msg = '
 Tried to save traceback to %s, but failed.' % path
            self.file.write(msg + '\n')
        try:
            self.file.flush()
        except: pass

handler = Hook().handle
def enable(display=1, logdir=None, context=5):
    """Install an exception handler that formats tracebacks as HTML.

    The optional argument 'display' can be set to 0 to suppress sending the
    traceback to the browser, and 'logdir' can be set to a directory to cause
    tracebacks to be written to files there."""
    sys.excepthook = Hook(display, logdir, context)
<|endoftext|>


<commit_before>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import eventlet

from progress.bar import ShadyBar

from warehouse import create_app, db, script
from warehouse.packages import store
from warehouse.synchronize.fetchers import PyPIFetcher

eventlet.monkey_patch()

class DummyBar(object):
    def iter(self, iterable):
        for x in iterable:
            yield x

def synchronize_project(app, project, fetcher):
    with app.test_request_context():
        project = store.project(project)

        versions = fetcher.versions(project.name)

        for v in versions:
            version = store.version(project, fetcher.release(project.name, v))
            distributions = fetcher.distributions(project.name, version.version)

            for dist in distributions:
                distribution = store.distribution(project, version, dist)

                # Check if the stored hash matches what the fetcher says
                if (distribution.hashes is None or
                        dist["md5_digest"] != distribution.hashes.get("md5")):
                    # The fetcher has a different file
                    # TODO(dstufft): Verify that this url is HTTPS
                    store.distribution_file(project, version, distribution,
                                            fetcher.file(dist["url"]))

        # Commit our changes
        db.session.commit()

def syncer(projects=None, fetcher=None, app=None, pool=None, progress=True):
    if pool is None:
        pool = eventlet.GreenPool(10)

    if app is None:
        app = create_app()

    if fetcher is None:
        fetcher = PyPIFetcher()

    # Sync the Classifiers
    for classifier in fetcher.classifiers():
        store.classifier(classifier)

    # Commit the classifiers
    db.session.commit()

    # Sync the Projects/Versions/Files
    if not projects:
        # TODO(dstufft): Determine how to make this do the "since last sync"
        projects = fetcher.projects()

    if progress:
        bar = ShadyBar("Synchronizing", max=len(projects))
    else:
        bar = DummyBar()

    with app.app_context():
        for project in bar.iter(projects):
            pool.spawn_n(synchronize_project, app, project, fetcher)

@script.option("--concurrency", dest="concurrency", type=int, default=10)
@script.option("--no-progress", action="store_false", dest="progress")
@script.option("projects", nargs="*", metavar="project")
def synchronize(projects=None, concurrency=10, progress=True):
    # This is a hack to normalize the incoming projects to unicode
    projects = [x.decode("utf-8") for x in projects]

    # Create the Pool that Synchronization will use
    pool = eventlet.GreenPool(concurrency)

    # Run the actual Synchronization
    syncer(projects, pool=pool, progress=progress)
<commit_msg>Provide a flag to allow forcing to download the file<commit_after>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import eventlet

from progress.bar import ShadyBar

from warehouse import create_app, db, script
from warehouse.packages import store
from warehouse.synchronize.fetchers import PyPIFetcher

eventlet.monkey_patch()

class DummyBar(object):
    def iter(self, iterable):
        for x in iterable:
            yield x

def synchronize_project(app, project, fetcher, force=False):
    with app.test_request_context():
        project = store.project(project)

        versions = fetcher.versions(project.name)

        for v in versions:
            version = store.version(project, fetcher.release(project.name, v))
            distributions = fetcher.distributions(project.name, version.version)

            for dist in distributions:
                distribution = store.distribution(project, version, dist)

                # Check if the stored hash matches what the fetcher says
                if (force or
                        distribution.hashes is None or
                        dist["md5_digest"] != distribution.hashes.get("md5")):
                    # The fetcher has a different file
                    store.distribution_file(project, version, distribution,
                                            fetcher.file(dist["url"]))

        # Commit our changes
        db.session.commit()

def syncer(projects=None, fetcher=None, pool=None, progress=True, force=False):
    if pool is None:
        pool = eventlet.GreenPool(10)

    if fetcher is None:
        fetcher = PyPIFetcher()

    # Sync the Classifiers
    for classifier in fetcher.classifiers():
        store.classifier(classifier)

    # Commit the classifiers
    db.session.commit()

    # Sync the Projects/Versions/Files
    if not projects:
        # TODO(dstufft): Determine how to make this do the "since last sync"
        projects = fetcher.projects()

    if progress:
        bar = ShadyBar("Synchronizing", max=len(projects))
    else:
        bar = DummyBar()

    app = create_app()

    with app.app_context():
        for project in bar.iter(projects):
            pool.spawn_n(synchronize_project, app, project, fetcher, force)

@script.option("--force-download", action="store_true", dest="force")
@script.option("--concurrency", dest="concurrency", type=int, default=10)
@script.option("--no-progress", action="store_false", dest="progress")
@script.option("projects", nargs="*", metavar="project")
def synchronize(projects=None, concurrency=10, progress=True, force=False):
    # This is a hack to normalize the incoming projects to unicode
    projects = [x.decode("utf-8") for x in projects]

    # Create the Pool that Synchronization will use
    pool = eventlet.GreenPool(concurrency)

    # Run the actual Synchronization
    syncer(projects, pool=pool, progress=progress, force=force)
<|endoftext|>


<commit_before># coding: utf8
"""
    weasyprint.css.computed_values
    ------------------------------

    Convert *specified* property values (the result of the cascade and
    inhertance) into *computed* values (that are inherited).

    :copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from __future__ import division, unicode_literals

import math

from .properties import INITIAL_VALUES, Dimension

ZERO_PIXELS = Dimension(0, 'px')

# How many CSS pixels is one <unit>?
# http://www.w3.org/TR/CSS21/syndata.html#length-units
LENGTHS_TO_PIXELS = {
    'px': 1,
    'pt': 1. / 0.75,
    'pc': 16.,  # LENGTHS_TO_PIXELS['pt'] * 12
    'in': 96.,  # LENGTHS_TO_PIXELS['pt'] * 72
    'cm': 96. / 2.54,  # LENGTHS_TO_PIXELS['in'] / 2.54
    'mm': 96. / 25.4,  # LENGTHS_TO_PIXELS['in'] / 25.4
}

# http://dev.w3.org/csswg/css3-values/#angles
# How many radians is one <unit>?
ANGLE_TO_RADIANS = {
    'rad': 1,
    'turn': 2 * math.pi,
    'deg': math.pi / 180,
    'grad': math.pi / 200,
}

# Value in pixels of font-size for <absolute-size> keywords: 12pt (16px) for
# medium, and scaling factors given in CSS3 for others:
# http://www.w3.org/TR/css3-fonts/#font-size-prop
# TODO: this will need to be ordered to implement 'smaller' and 'larger'
FONT_SIZE_KEYWORDS = dict(
    # medium is 16px, others are a ratio of medium
    (name, INITIAL_VALUES['font_size'] * a / b)
    for name, a, b in [
        ('xx-small', 3, 5),
        ('x-small', 3, 4),
        ('small', 8, 9),
        ('medium', 1, 1),
        ('large', 6, 5),
        ('x-large', 3, 2),
        ('xx-large', 2, 1),
    ]
)

# These are unspecified, other than 'thin' <='medium' <= 'thick'.
# Values are in pixels.
BORDER_WIDTH_KEYWORDS = {
    'thin': 1,
    'medium': 3,
    'thick': 5,
}
assert INITIAL_VALUES['border_top_width'] == BORDER_WIDTH_KEYWORDS['medium']

# http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
FONT_WEIGHT_RELATIVE = dict(
    bolder={
        100: 400,
        200: 400,
        300: 400,
        400: 700,
        500: 700,
        600: 900,
        700: 900,
        800: 900,
        900: 900,
    },
    lighter={
        100: 100,
        200: 100,
        300: 100,
        400: 100,
        500: 100,
        600: 400,
        700: 400,
        800: 700,
        900: 700,
    },
)

# http://www.w3.org/TR/css3-page/#size
# name=(width in pixels, height in pixels)
PAGE_SIZES = dict(
    a5=(
        Dimension(148, 'mm'),
        Dimension(210, 'mm'),
    ),
    a4=(
        Dimension(210, 'mm'),
        Dimension(297, 'mm'),
    ),
    a3=(
        Dimension(297, 'mm'),
        Dimension(420, 'mm'),
    ),
    b5=(
        Dimension(176, 'mm'),
        Dimension(250, 'mm'),
    ),
    b4=(
        Dimension(250, 'mm'),
        Dimension(353, 'mm'),
    ),
    letter=(
        Dimension(8.5, 'in'),
        Dimension(11, 'in'),
    ),
    legal=(
        Dimension(8.5, 'in'),
        Dimension(14, 'in'),
    ),
    ledger=(
        Dimension(11, 'in'),
        Dimension(17, 'in'),
    ),
)
# In "portrait" orientation.
for w, h in PAGE_SIZES.values():
    assert w.value < h.value

INITIAL_PAGE_SIZE = PAGE_SIZES['a4']
INITIAL_VALUES['size'] = tuple(
    d.value * LENGTHS_TO_PIXELS[d.unit] for d in INITIAL_PAGE_SIZE)

def _computing_order():
    """Some computed values are required by others, so order matters."""
    first = ['font_size', 'line_height', 'color']
    order = sorted(INITIAL_VALUES)
    for name in first:
        order.remove(name)
    return tuple(first + order)
COMPUTING_ORDER = _computing_order()

# Maps property names to functions returning the computed values
COMPUTER_FUNCTIONS = {}

def register_computer(name):
    """Decorator registering a property ``name`` for a function."""
    name = name.replace('-', '_')
    def decorator(function):
        """Register the property ``name`` for ``function``."""
        COMPUTER_FUNCTIONS[name] = function
        return function
    return decorator

def compute(element, pseudo_type, specified, computed, parent_style):
    """
    Return a StyleDict of computed values.

    :param element: The HTML element these style apply to
    :param pseudo_type: The type of pseudo-element, eg 'before', None
    :param specified: a :class:`StyleDict` of specified values. Should contain
                      values for all properties.
    :param computed: a :class:`StyleDict` of already known computed values.
                     Only contains some properties (or none).
    :param parent_values: a :class:`StyleDict` of computed values of the parent
                          element (should contain values for all properties),
                          or ``None`` if ``element`` is the root element.
    """
    if parent_style is None:
        parent_style = INITIAL_VALUES

    computer = lambda: 0  # Dummy object that holds attributes
    computer.element = element
    computer.pseudo_type = pseudo_type
    computer.specified = specified
    computer.computed = computed
    computer.parent_style = parent_style

    getter = COMPUTER_FUNCTIONS.get

    for name in COMPUTING_ORDER:
        if name in computed:
            # Already computed
            continue

        value = specified[name]
        function = getter(name)
        if function is not None:
            value = function(computer, name, value)
        # else: same as specified

        assert value is not None
        computed[name] = value

    return computed

# Let's be consistent, always use ``name`` as an argument even when
# it is useless.
# pylint: disable=W0613

@register_computer('background-color')
@register_computer('border-top-color')
@register_computer('border-right-color')
@register_computer('border-bottom-color')
@register_computer('border-left-color')
def other_color(computer, name, value):
    """Compute the ``*-color`` properties."""
    if value == 'currentColor':
        return computer.computed.color
    else:
        # As specified
        return value

@register_computer('background-position')
@register_computer('transform-origin')
def length_or_percentage_tuple(computer, name, values):
    """Compute the lists of lengths that can be percentages."""
    return tuple(length(computer, name, value) for value in values)

@register_computer('border-spacing')
@register_computer('size')
@register_computer('clip')
def length_tuple(computer, name, values):
    """Compute the properties with a list of lengths."""
    return tuple(length(computer, name, value, pixels_only=True)
                 for value in values)

@register_computer('top')
@register_computer('right')
@register_computer('left')
@register_computer('bottom')
@register_computer('margin-top')
@register_computer('margin-right')
@register_computer('margin-bottom')
@register_computer('margin-left')
@register_computer('height')
@register_computer('width')
@register_computer('min-width')
@register_computer('min-height')
@register_computer('max-width')
@register_computer('max-height')
@register_computer('padding-top')
@register_computer('padding-right')
@register_computer('padding-bottom')
@register_computer('padding-left')
@register_computer('text-indent')
def length(computer, name, value, font_size=None, pixels_only=False):
    """Compute a length ``value``."""
    if value == 'auto':
        return value
    if value.value == 0:
        return 0 if pixels_only else ZERO_PIXELS

    unit = value.unit
    if unit == 'px':
        return value.value if pixels_only else value
    elif unit in LENGTHS_TO_PIXELS:
        # Convert absolute lengths to pixels
        factor = LENGTHS_TO_PIXELS[unit]
    elif unit in ('em', 'ex'):
        if font_size is None:
            factor = computer.computed.font_size
        else:
            factor = font_size
        if unit == 'ex':
            # TODO: find a better way to measure ex, see
            # http://www.w3.org/TR/CSS21/syndata.html#length-units
            factor *= 0.5
    else:
        # A percentage or 'auto': no conversion needed.
        return value

    result = value.value * factor
    return result if pixels_only else Dimension(result, 'px')

@register_computer('letter-spacing')
def pixel_length(computer, name, value):
    return length(computer, name, value, pixels_only=True)

@register_computer('background-size')
def background_size(computer, name, value):
    """Compute the ``background-size`` properties."""
    if value in ('contain', 'cover'):
        return value
    else:
        return length_or_percentage_tuple(computer, name, value)

@register_computer('border-top-width')
@register_computer('border-right-width')
@register_computer('border-left-width')
@register_computer('border-bottom-width')
def border_width(computer, name, value):
    """Compute the ``border-*-width`` properties."""
    style = computer.computed[name.replace('width', 'style')]
    if style in ('none', 'hidden'):
        return 0

    if value in BORDER_WIDTH_KEYWORDS:
        return BORDER_WIDTH_KEYWORDS[value]

    if isinstance(value, int):
        # The initial value can get here, but length() would fail as
        # it does not have a 'unit' attribute.
        return value

    return length(computer, name, value, pixels_only=True)

@register_computer('content')
def content(computer, name, values):
    """Compute the ``content`` property."""
    if values in ('normal', 'none'):
        return values
    else:
        return [('STRING', computer.element.get(value, ''))
                if type_ == 'attr' else (type_, value)
                for type_, value in values]

@register_computer('display')
def display(computer, name, value):
    """Compute the ``display`` property.

    See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo

    """
    float_ = computer.specified.float
    position = computer.specified.position
    if position in ('absolute', 'fixed') or float_ != 'none' or \
            getattr(computer.element, 'getparent', lambda: None)() is None:
        if value == 'inline-table':
            return'table'
        elif value in ('inline', 'table-row-group', 'table-column',
                       'table-column-group', 'table-header-group',
                       'table-footer-group', 'table-row', 'table-cell',
                       'table-caption', 'inline-block'):
            return 'block'
    return value

@register_computer('float')
def compute_float(computer, name, value):
    """Compute the ``float`` property.

    See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo

    """
    if computer.specified.position in ('absolute', 'fixed'):
        return 'none'
    else:
        return value

@register_computer('font-size')
def font_size(computer, name, value):
    """Compute the ``font-size`` property."""
    if value in FONT_SIZE_KEYWORDS:
        return FONT_SIZE_KEYWORDS[value]
    # TODO: support 'larger' and 'smaller'

    parent_font_size = computer.parent_style['font_size']
    if value.unit == '%':
        return value.value * parent_font_size / 100.
    else:
        return length(computer, name, value, pixels_only=True,
                      font_size=parent_font_size)

@register_computer('font-weight')
def font_weight(computer, name, value):
    """Compute the ``font-weight`` property."""
    if value == 'normal':
        return 400
    elif value == 'bold':
        return 700
    elif value in ('bolder', 'lighter'):
        parent_value = computer.parent_style['font_weight']
        # Use a string here as StyleDict.__setattr__ turns integers into pixel
        # lengths. This is a number without unit.
        return FONT_WEIGHT_RELATIVE[value][parent_value]
    else:
        return value

@register_computer('line-height')
def line_height(computer, name, value):
    """Compute the ``line-height`` property."""
    if value == 'normal':
        return value
    elif not value.unit:
        return ('NUMBER', value.value)
    elif value.unit == '%':
        factor = value.value / 100.
        font_size_value = computer.computed.font_size
        pixels = factor * font_size_value
    else:
        pixels = length(computer, name, value, pixels_only=True)
    return ('PIXELS', pixels)

@register_computer('transform')
def transform(computer, name, value):
    """Compute the ``transform`` property."""
    result = []
    for function, args in value:
        if function in ('rotate', 'skewx', 'skewy'):
            args = args.value * ANGLE_TO_RADIANS[args.unit]
        elif function == 'translate':
            args = length_or_percentage_tuple(computer, name, args)
        result.append((function, args))
    return result

@register_computer('vertical-align')
def vertical_align(computer, name, value):
    """Compute the ``vertical-align`` property."""
    # Use +/- half an em for super and sub, same as Pango.
    # (See the SUPERSUB_RISE constant in pango-markup.c)
    if value in ('baseline', 'middle', 'text-top', 'text-bottom',
                 'top', 'bottom'):
        return value
    elif value == 'super':
        return computer.computed.font_size * 0.5
    elif value == 'sub':
        return computer.computed.font_size * -0.5
    elif value.unit == '%':
        height = used_line_height({
            'line_height': computer.computed.line_height,
            'font_size': computer.computed.font_size
        })
        return height * value.value / 100.
    else:
        return length(computer, name, value, pixels_only=True)

@register_computer('word-spacing')
def word_spacing(computer, name, value):
    """Compute the ``word-spacing`` property."""
    if value == 'normal':
        return 0
    else:
        return length(computer, name, value, pixels_only=True)

def used_line_height(style):
    """Return the used value for the ``line-height`` property."""
    height = style['line_height']
    if height == 'normal':
        # a "reasonable" value
        # http://www.w3.org/TR/CSS21/visudet.html#line-height
        # TODO: use font metrics?
        height = ('NUMBER', 1.2)
    type_, value = height
    if type_ == 'NUMBER':
        return value * style['font_size']
    else:
        return value
<commit_msg>Do not crash on letter-spacing: normal<commit_after># coding: utf8
"""
    weasyprint.css.computed_values
    ------------------------------

    Convert *specified* property values (the result of the cascade and
    inhertance) into *computed* values (that are inherited).

    :copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
    :license: BSD, see LICENSE for details.

"""

from __future__ import division, unicode_literals

import math

from .properties import INITIAL_VALUES, Dimension

ZERO_PIXELS = Dimension(0, 'px')

# How many CSS pixels is one <unit>?
# http://www.w3.org/TR/CSS21/syndata.html#length-units
LENGTHS_TO_PIXELS = {
    'px': 1,
    'pt': 1. / 0.75,
    'pc': 16.,  # LENGTHS_TO_PIXELS['pt'] * 12
    'in': 96.,  # LENGTHS_TO_PIXELS['pt'] * 72
    'cm': 96. / 2.54,  # LENGTHS_TO_PIXELS['in'] / 2.54
    'mm': 96. / 25.4,  # LENGTHS_TO_PIXELS['in'] / 25.4
}

# http://dev.w3.org/csswg/css3-values/#angles
# How many radians is one <unit>?
ANGLE_TO_RADIANS = {
    'rad': 1,
    'turn': 2 * math.pi,
    'deg': math.pi / 180,
    'grad': math.pi / 200,
}

# Value in pixels of font-size for <absolute-size> keywords: 12pt (16px) for
# medium, and scaling factors given in CSS3 for others:
# http://www.w3.org/TR/css3-fonts/#font-size-prop
# TODO: this will need to be ordered to implement 'smaller' and 'larger'
FONT_SIZE_KEYWORDS = dict(
    # medium is 16px, others are a ratio of medium
    (name, INITIAL_VALUES['font_size'] * a / b)
    for name, a, b in [
        ('xx-small', 3, 5),
        ('x-small', 3, 4),
        ('small', 8, 9),
        ('medium', 1, 1),
        ('large', 6, 5),
        ('x-large', 3, 2),
        ('xx-large', 2, 1),
    ]
)

# These are unspecified, other than 'thin' <='medium' <= 'thick'.
# Values are in pixels.
BORDER_WIDTH_KEYWORDS = {
    'thin': 1,
    'medium': 3,
    'thick': 5,
}
assert INITIAL_VALUES['border_top_width'] == BORDER_WIDTH_KEYWORDS['medium']

# http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
FONT_WEIGHT_RELATIVE = dict(
    bolder={
        100: 400,
        200: 400,
        300: 400,
        400: 700,
        500: 700,
        600: 900,
        700: 900,
        800: 900,
        900: 900,
    },
    lighter={
        100: 100,
        200: 100,
        300: 100,
        400: 100,
        500: 100,
        600: 400,
        700: 400,
        800: 700,
        900: 700,
    },
)

# http://www.w3.org/TR/css3-page/#size
# name=(width in pixels, height in pixels)
PAGE_SIZES = dict(
    a5=(
        Dimension(148, 'mm'),
        Dimension(210, 'mm'),
    ),
    a4=(
        Dimension(210, 'mm'),
        Dimension(297, 'mm'),
    ),
    a3=(
        Dimension(297, 'mm'),
        Dimension(420, 'mm'),
    ),
    b5=(
        Dimension(176, 'mm'),
        Dimension(250, 'mm'),
    ),
    b4=(
        Dimension(250, 'mm'),
        Dimension(353, 'mm'),
    ),
    letter=(
        Dimension(8.5, 'in'),
        Dimension(11, 'in'),
    ),
    legal=(
        Dimension(8.5, 'in'),
        Dimension(14, 'in'),
    ),
    ledger=(
        Dimension(11, 'in'),
        Dimension(17, 'in'),
    ),
)
# In "portrait" orientation.
for w, h in PAGE_SIZES.values():
    assert w.value < h.value

INITIAL_PAGE_SIZE = PAGE_SIZES['a4']
INITIAL_VALUES['size'] = tuple(
    d.value * LENGTHS_TO_PIXELS[d.unit] for d in INITIAL_PAGE_SIZE)

def _computing_order():
    """Some computed values are required by others, so order matters."""
    first = ['font_size', 'line_height', 'color']
    order = sorted(INITIAL_VALUES)
    for name in first:
        order.remove(name)
    return tuple(first + order)
COMPUTING_ORDER = _computing_order()

# Maps property names to functions returning the computed values
COMPUTER_FUNCTIONS = {}

def register_computer(name):
    """Decorator registering a property ``name`` for a function."""
    name = name.replace('-', '_')
    def decorator(function):
        """Register the property ``name`` for ``function``."""
        COMPUTER_FUNCTIONS[name] = function
        return function
    return decorator

def compute(element, pseudo_type, specified, computed, parent_style):
    """
    Return a StyleDict of computed values.

    :param element: The HTML element these style apply to
    :param pseudo_type: The type of pseudo-element, eg 'before', None
    :param specified: a :class:`StyleDict` of specified values. Should contain
                      values for all properties.
    :param computed: a :class:`StyleDict` of already known computed values.
                     Only contains some properties (or none).
    :param parent_values: a :class:`StyleDict` of computed values of the parent
                          element (should contain values for all properties),
                          or ``None`` if ``element`` is the root element.
    """
    if parent_style is None:
        parent_style = INITIAL_VALUES

    computer = lambda: 0  # Dummy object that holds attributes
    computer.element = element
    computer.pseudo_type = pseudo_type
    computer.specified = specified
    computer.computed = computed
    computer.parent_style = parent_style

    getter = COMPUTER_FUNCTIONS.get

    for name in COMPUTING_ORDER:
        if name in computed:
            # Already computed
            continue

        value = specified[name]
        function = getter(name)
        if function is not None:
            value = function(computer, name, value)
        # else: same as specified

        assert value is not None
        computed[name] = value

    return computed

# Let's be consistent, always use ``name`` as an argument even when
# it is useless.
# pylint: disable=W0613

@register_computer('background-color')
@register_computer('border-top-color')
@register_computer('border-right-color')
@register_computer('border-bottom-color')
@register_computer('border-left-color')
def other_color(computer, name, value):
    """Compute the ``*-color`` properties."""
    if value == 'currentColor':
        return computer.computed.color
    else:
        # As specified
        return value

@register_computer('background-position')
@register_computer('transform-origin')
def length_or_percentage_tuple(computer, name, values):
    """Compute the lists of lengths that can be percentages."""
    return tuple(length(computer, name, value) for value in values)

@register_computer('border-spacing')
@register_computer('size')
@register_computer('clip')
def length_tuple(computer, name, values):
    """Compute the properties with a list of lengths."""
    return tuple(length(computer, name, value, pixels_only=True)
                 for value in values)

@register_computer('top')
@register_computer('right')
@register_computer('left')
@register_computer('bottom')
@register_computer('margin-top')
@register_computer('margin-right')
@register_computer('margin-bottom')
@register_computer('margin-left')
@register_computer('height')
@register_computer('width')
@register_computer('min-width')
@register_computer('min-height')
@register_computer('max-width')
@register_computer('max-height')
@register_computer('padding-top')
@register_computer('padding-right')
@register_computer('padding-bottom')
@register_computer('padding-left')
@register_computer('text-indent')
def length(computer, name, value, font_size=None, pixels_only=False):
    """Compute a length ``value``."""
    if value == 'auto':
        return value
    if value.value == 0:
        return 0 if pixels_only else ZERO_PIXELS

    unit = value.unit
    if unit == 'px':
        return value.value if pixels_only else value
    elif unit in LENGTHS_TO_PIXELS:
        # Convert absolute lengths to pixels
        factor = LENGTHS_TO_PIXELS[unit]
    elif unit in ('em', 'ex'):
        if font_size is None:
            factor = computer.computed.font_size
        else:
            factor = font_size
        if unit == 'ex':
            # TODO: find a better way to measure ex, see
            # http://www.w3.org/TR/CSS21/syndata.html#length-units
            factor *= 0.5
    else:
        # A percentage or 'auto': no conversion needed.
        return value

    result = value.value * factor
    return result if pixels_only else Dimension(result, 'px')

@register_computer('letter-spacing')
def pixel_length(computer, name, value):
    if value == 'normal':
        return value
    else:
        return length(computer, name, value, pixels_only=True)

@register_computer('background-size')
def background_size(computer, name, value):
    """Compute the ``background-size`` properties."""
    if value in ('contain', 'cover'):
        return value
    else:
        return length_or_percentage_tuple(computer, name, value)

@register_computer('border-top-width')
@register_computer('border-right-width')
@register_computer('border-left-width')
@register_computer('border-bottom-width')
def border_width(computer, name, value):
    """Compute the ``border-*-width`` properties."""
    style = computer.computed[name.replace('width', 'style')]
    if style in ('none', 'hidden'):
        return 0

    if value in BORDER_WIDTH_KEYWORDS:
        return BORDER_WIDTH_KEYWORDS[value]

    if isinstance(value, int):
        # The initial value can get here, but length() would fail as
        # it does not have a 'unit' attribute.
        return value

    return length(computer, name, value, pixels_only=True)

@register_computer('content')
def content(computer, name, values):
    """Compute the ``content`` property."""
    if values in ('normal', 'none'):
        return values
    else:
        return [('STRING', computer.element.get(value, ''))
                if type_ == 'attr' else (type_, value)
                for type_, value in values]

@register_computer('display')
def display(computer, name, value):
    """Compute the ``display`` property.

    See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo

    """
    float_ = computer.specified.float
    position = computer.specified.position
    if position in ('absolute', 'fixed') or float_ != 'none' or \
            getattr(computer.element, 'getparent', lambda: None)() is None:
        if value == 'inline-table':
            return'table'
        elif value in ('inline', 'table-row-group', 'table-column',
                       'table-column-group', 'table-header-group',
                       'table-footer-group', 'table-row', 'table-cell',
                       'table-caption', 'inline-block'):
            return 'block'
    return value

@register_computer('float')
def compute_float(computer, name, value):
    """Compute the ``float`` property.

    See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo

    """
    if computer.specified.position in ('absolute', 'fixed'):
        return 'none'
    else:
        return value

@register_computer('font-size')
def font_size(computer, name, value):
    """Compute the ``font-size`` property."""
    if value in FONT_SIZE_KEYWORDS:
        return FONT_SIZE_KEYWORDS[value]
    # TODO: support 'larger' and 'smaller'

    parent_font_size = computer.parent_style['font_size']
    if value.unit == '%':
        return value.value * parent_font_size / 100.
    else:
        return length(computer, name, value, pixels_only=True,
                      font_size=parent_font_size)

@register_computer('font-weight')
def font_weight(computer, name, value):
    """Compute the ``font-weight`` property."""
    if value == 'normal':
        return 400
    elif value == 'bold':
        return 700
    elif value in ('bolder', 'lighter'):
        parent_value = computer.parent_style['font_weight']
        # Use a string here as StyleDict.__setattr__ turns integers into pixel
        # lengths. This is a number without unit.
        return FONT_WEIGHT_RELATIVE[value][parent_value]
    else:
        return value

@register_computer('line-height')
def line_height(computer, name, value):
    """Compute the ``line-height`` property."""
    if value == 'normal':
        return value
    elif not value.unit:
        return ('NUMBER', value.value)
    elif value.unit == '%':
        factor = value.value / 100.
        font_size_value = computer.computed.font_size
        pixels = factor * font_size_value
    else:
        pixels = length(computer, name, value, pixels_only=True)
    return ('PIXELS', pixels)

@register_computer('transform')
def transform(computer, name, value):
    """Compute the ``transform`` property."""
    result = []
    for function, args in value:
        if function in ('rotate', 'skewx', 'skewy'):
            args = args.value * ANGLE_TO_RADIANS[args.unit]
        elif function == 'translate':
            args = length_or_percentage_tuple(computer, name, args)
        result.append((function, args))
    return result

@register_computer('vertical-align')
def vertical_align(computer, name, value):
    """Compute the ``vertical-align`` property."""
    # Use +/- half an em for super and sub, same as Pango.
    # (See the SUPERSUB_RISE constant in pango-markup.c)
    if value in ('baseline', 'middle', 'text-top', 'text-bottom',
                 'top', 'bottom'):
        return value
    elif value == 'super':
        return computer.computed.font_size * 0.5
    elif value == 'sub':
        return computer.computed.font_size * -0.5
    elif value.unit == '%':
        height = used_line_height({
            'line_height': computer.computed.line_height,
            'font_size': computer.computed.font_size
        })
        return height * value.value / 100.
    else:
        return length(computer, name, value, pixels_only=True)

@register_computer('word-spacing')
def word_spacing(computer, name, value):
    """Compute the ``word-spacing`` property."""
    if value == 'normal':
        return 0
    else:
        return length(computer, name, value, pixels_only=True)

def used_line_height(style):
    """Return the used value for the ``line-height`` property."""
    height = style['line_height']
    if height == 'normal':
        # a "reasonable" value
        # http://www.w3.org/TR/CSS21/visudet.html#line-height
        # TODO: use font metrics?
        height = ('NUMBER', 1.2)
    type_, value = height
    if type_ == 'NUMBER':
        return value * style['font_size']
    else:
        return value
<|endoftext|>


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

# libavg - Media Playback Engine.
# Copyright (C) 2003-2013 Ulrich von Zadow
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Current versions can be found at www.libavg.de
#
# Original author of this file is Robert Parcus <betoparcus@gmail.com>

from libavg import *

import random

def parseCmdLine():
    parser = optparse.OptionParser(usage=
"""%prog [option].
Checks libavg performance by creating lots of nodes. Displays a frame time graph and executes for 20 secs.""")
    parser.add_option('--use-fx', '-f', dest='useFX', action='store_true', default=False,
            help='Display everything using a NullFX to test FX overhead.')
    parser.add_option('--video', '-v', dest='video',  action='store_true', default=False,
            help='Show videos instead of images.')
    parser.add_option('--audio', '-a', dest='audio',  action='store_true', default=False,
            help='When showing videos, use videos with an audio channel')
    parser.add_option('--create-nodes', '-c', dest='createNodes', action='store_true',
            default=False,
            help='Destroy and recreate all nodes every 400 ms.')
    parser.add_option('--move', '-m', dest='move', action='store_true',
            default=False,
            help='Move nodes every frame.')
    parser.add_option('--blur', '-b', dest='blur', action='store_true',
            default=False,
            help='Applies a BlurFXNode to the nodes.')
    parser.add_option('--color', dest='color', action='store_true',
            default=False,
            help='Applies gamma to the nodes, causing the color correction shader to activate.')
    parser.add_option('--vsync', '-s', dest='vsync', action='store_true',
            default=False,
            help='Sync output to vertical refresh.')
    parser.add_option('--profile', '-p', dest='profile', action='store_true',
            default=False,
            help='Enable profiling output. Note that profiling makes things slower.')
    parser.add_option('--num-objs', '-n', dest='numObjs', type='int', default=-1,
            help='Number of objects to create. Default is 200 images or 40 videos.')

    (options, args) = parser.parse_args()

    return options

class SpeedDiv(app.MainDiv):
    def onArgvExtenderCreated(self, argvExtender):
        usage = '%prog [options]\n' \
                'Checks libavg performance by creating lots of nodes. ' \
                'Displays a frame time graph and executes for 20 secs.'
        argvExtender.parser.set_usage(usage)

        argvExtender.parser.add_option('--use-fx', '-f', dest='useFX',
                action='store_true', default=False,
                help='display everything using a NullFX to test FX overhead')
        argvExtender.parser.add_option('--video', '-i', dest='video',
                action='store_true', default=False,
                help='show videos instead of images')
        argvExtender.parser.add_option('--audio', '-a', dest='audio',
                action='store_true', default=False,
                help='when showing videos, use videos with an audio channel')
        argvExtender.parser.add_option('--create-nodes', '-c', dest='create',
                action='store_true', default=False,
                help='destroy and recreate all nodes every 400 ms')
        argvExtender.parser.add_option('--move', '-m', dest='move',
                action='store_true', default=False,
                help='move nodes every frame')
        argvExtender.parser.add_option('--blur', '-b', dest='blur',
                action='store_true', default=False,
                help='apply a BlurFXNode to the nodes')
        argvExtender.parser.add_option('--color', '-o', dest='color',
                action='store_true', default=False,
                help='apply gamma to the nodes, causing the color correction shader to activate')
        argvExtender.parser.add_option('--vsync', '-s', dest='vsync',
                action='store_true', default=False,
                help='sync output to vertical refresh')
        argvExtender.parser.add_option('--num-objs', '-n', dest='numObjs',
                type='int', default=-1,
                help='number of objects to create [Default: 200 images or 40 videos]')
        argvExtender.parser.add_option('--profile', '-p', dest='profile',
                action='store_true', default=False,
                help='enable profiling output, note that profiling makes things slower')

    def onArgvExtenderApplied(self, argvExtender):
        opts, args = argvExtender.parsedArgs

        self.__optUseFX = opts.useFX
        self.__optVideo = opts.video
        self.__optAudio = opts.audio
        self.__optCreate = opts.create
        self.__optMove = opts.move
        self.__optBlur = opts.blur
        self.__optColor = opts.color
        self.__optVsync = opts.vsync
        self.__optNumObjs = opts.numObjs
        if self.__optNumObjs < 1:
            if self.__optVideo:
                self.__optNumObjs = 40
            else:
                self.__optNumObjs = 200

        log = avg.logger
        if opts.profile:
            log.setCategories(log.PROFILE | log.CONFIG | log.WARNING | log.ERROR)
        else:
            log.setCategories(log.CONFIG | log.WARNING | log.ERROR)

    def onInit(self):
        if not self.__optVsync:
            player.setFramerate(1000)

        self.mediadir = utils.getMediaDir(None, 'data')
        self.__createNodes()
        app.instance.debugPanel.toggleWidget(app.debugpanel.FrametimeGraphWidget)
        if self.__optCreate:
            player.setInterval(400, self.__createNodes)
        # Ignore the first frame for the 20 sec-limit so long startup times don't
        # break things.
        player.setTimeout(0, lambda: player.setTimeout(20000, player.stop))

    def onFrame(self, dt):
         if self.__optMove:
            self.__moveNodes()

    def __createNodes(self):
        self.__nodes = []
        for i in xrange(self.__optNumObjs):
            pos = (random.randrange(800-64), random.randrange(600-64))
            if self.__optVideo:
                if self.__optAudio:
                    fname = "mpeg1-48x48-sound.avi"
                else:
                    fname = "mpeg1-48x48.mpg"
                node = avg.VideoNode(pos=pos, href=fname, loop=True, parent=self)
                node.play()
            else:
                node = avg.ImageNode(pos=pos, href="rgb24alpha-64x64.png", parent=self)
            if self.__optUseFX:
                node.setEffect(avg.NullFXNode())
            if self.__optBlur:
                node.setEffect(avg.BlurFXNode(10))
            if self.__optColor:
                node.gamma = (1.1, 1.1, 1.1)
            self.__nodes.append(node)
        if self.__optCreate:
            player.setTimeout(300, self.__deleteNodes)

    def __deleteNodes(self):
        for node in self.__nodes:
            node.unlink(True)
        self.__nodes = []

    def __moveNodes(self):
        for node in self.__nodes:
            node.pos = (random.randrange(800-64), random.randrange(600-64))

if __name__ == '__main__':
    app.App().run(SpeedDiv(), app_resolution='800x600')

<commit_msg>libavg_app: Removed obsolete optparse code in utils/checkspeed.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# libavg - Media Playback Engine.
# Copyright (C) 2003-2013 Ulrich von Zadow
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Current versions can be found at www.libavg.de
#
# Original author of this file is Robert Parcus <betoparcus@gmail.com>

from libavg import *

import random

class SpeedDiv(app.MainDiv):
    def onArgvExtenderCreated(self, argvExtender):
        usage = '%prog [options]\n' \
                'Checks libavg performance by creating lots of nodes. ' \
                'Displays a frame time graph and executes for 20 secs.'
        argvExtender.parser.set_usage(usage)

        argvExtender.parser.add_option('--use-fx', '-f', dest='useFX',
                action='store_true', default=False,
                help='display everything using a NullFX to test FX overhead')
        argvExtender.parser.add_option('--video', '-i', dest='video',
                action='store_true', default=False,
                help='show videos instead of images')
        argvExtender.parser.add_option('--audio', '-a', dest='audio',
                action='store_true', default=False,
                help='when showing videos, use videos with an audio channel')
        argvExtender.parser.add_option('--create-nodes', '-c', dest='create',
                action='store_true', default=False,
                help='destroy and recreate all nodes every 400 ms')
        argvExtender.parser.add_option('--move', '-m', dest='move',
                action='store_true', default=False,
                help='move nodes every frame')
        argvExtender.parser.add_option('--blur', '-b', dest='blur',
                action='store_true', default=False,
                help='apply a BlurFXNode to the nodes')
        argvExtender.parser.add_option('--color', '-o', dest='color',
                action='store_true', default=False,
                help='apply gamma to the nodes, causing the color correction shader to activate')
        argvExtender.parser.add_option('--vsync', '-s', dest='vsync',
                action='store_true', default=False,
                help='sync output to vertical refresh')
        argvExtender.parser.add_option('--num-objs', '-n', dest='numObjs',
                type='int', default=-1,
                help='number of objects to create [Default: 200 images or 40 videos]')
        argvExtender.parser.add_option('--profile', '-p', dest='profile',
                action='store_true', default=False,
                help='enable profiling output, note that profiling makes things slower')

    def onArgvExtenderApplied(self, argvExtender):
        opts, args = argvExtender.parsedArgs

        self.__optUseFX = opts.useFX
        self.__optVideo = opts.video
        self.__optAudio = opts.audio
        self.__optCreate = opts.create
        self.__optMove = opts.move
        self.__optBlur = opts.blur
        self.__optColor = opts.color
        self.__optVsync = opts.vsync
        self.__optNumObjs = opts.numObjs
        if self.__optNumObjs < 1:
            if self.__optVideo:
                self.__optNumObjs = 40
            else:
                self.__optNumObjs = 200

        log = avg.logger
        if opts.profile:
            log.setCategories(log.PROFILE | log.CONFIG | log.WARNING | log.ERROR)
        else:
            log.setCategories(log.CONFIG | log.WARNING | log.ERROR)

    def onInit(self):
        if not self.__optVsync:
            player.setFramerate(1000)

        self.mediadir = utils.getMediaDir(None, 'data')
        self.__createNodes()
        app.instance.debugPanel.toggleWidget(app.debugpanel.FrametimeGraphWidget)
        if self.__optCreate:
            player.setInterval(400, self.__createNodes)
        # Ignore the first frame for the 20 sec-limit so long startup times don't
        # break things.
        player.setTimeout(0, lambda: player.setTimeout(20000, player.stop))

    def onFrame(self, dt):
         if self.__optMove:
            self.__moveNodes()

    def __createNodes(self):
        self.__nodes = []
        for i in xrange(self.__optNumObjs):
            pos = (random.randrange(800-64), random.randrange(600-64))
            if self.__optVideo:
                if self.__optAudio:
                    fname = "mpeg1-48x48-sound.avi"
                else:
                    fname = "mpeg1-48x48.mpg"
                node = avg.VideoNode(pos=pos, href=fname, loop=True, parent=self)
                node.play()
            else:
                node = avg.ImageNode(pos=pos, href="rgb24alpha-64x64.png", parent=self)
            if self.__optUseFX:
                node.setEffect(avg.NullFXNode())
            if self.__optBlur:
                node.setEffect(avg.BlurFXNode(10))
            if self.__optColor:
                node.gamma = (1.1, 1.1, 1.1)
            self.__nodes.append(node)
        if self.__optCreate:
            player.setTimeout(300, self.__deleteNodes)

    def __deleteNodes(self):
        for node in self.__nodes:
            node.unlink(True)
        self.__nodes = []

    def __moveNodes(self):
        for node in self.__nodes:
            node.pos = (random.randrange(800-64), random.randrange(600-64))

if __name__ == '__main__':
    app.App().run(SpeedDiv(), app_resolution='800x600')

<|endoftext|>


<commit_before>"""
Copyright 2013 Shine Wang

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.
"""

from webParser import WebParser
from matcher import Matcher
from profParser import RateMyProfParser
from user import User

def getUserInfo(userCourses):
    print "Hello! I am Classy, and I'll optimize your course selection."
    print "The current optimizations are:"
    print "1.\tno time conflicts."
    print "2.\tsorting by ratemyprofessor.com ratings.\n"
    print "Please follow prompts VERY CLOSELY!"

    sessionString = raw_input("Which session you are in? (e.g. 'fall 2013' "
                              "without quotes): ").strip().lower()

    print "\nPlease add courses in decreasing importance."
    print "Format: 'cs 145' (without quotes)"

    cour = ""
    while cour.strip() != "0":
        cour = raw_input("Enter '0' to stop adding: ").strip().upper()
        userCourses.append(cour)
    userCourses.pop()  # the last '0'
    return sessionString

def queryUniversity(userCourses, courses, user, sessionString):
    print "Currently querying adm.uwaterloo.ca..."
    for courseName in userCourses:
        newCourse = WebParser().run(courseName, sessionString)
        courses.append(newCourse)
        if type(newCourse) is str:
            print "An error has occured with {0}: " \
                  "{1}. {0} will be ignored.".format(courseName, newCourse)
            courses.pop()
    print "Done!\n"

    print "Which reservation categories apply to you?"

    askedNames = set()
    for course in courses:
        for lec in course.lectures:
            if lec.thisUserCanAdd or len(lec.reserves) == 0:
                continue
            for res in lec.reserves:
                if res.enrlCap - res.enrlTotal <= 0:
                    # this reservation has no spots left anyway,
                    # so don't ask
                    continue
                for name in res.names:
                    if name in askedNames:
                        # don't want to re-ask
                        continue

                    askedNames.add(name)
                    inp = raw_input("Enter 'y' if you fufill the "
                                    "reservation of \"{}\". If not, "
                                    "enter 'n': ".format(name))
                    if inp.lower() == 'y':
                        user.userTypes.add(name)

            lec.postProcess(user.userTypes)

def queryRateMyProfessors(courses):
    print "Currently querying ratemyprofessors.com..."

    for i, course in enumerate(courses):
        print "Process: Course {}/{}".format(str(i+1), len(courses))

        for j, slot in enumerate(course.lectures):
            #print "Lecture {}/{}".format(str(j+1), len(course.lectures))
            ret = RateMyProfParser(slot.instructor).getInfo()
            if ret:
                slot.numRatings, slot.quality, slot.easiness = ret

        for j, slot in enumerate(course.tutorials):
            #print "Tutorial {}/{}".format(str(j+1), len(course.tutorials))
            ret = RateMyProfParser(slot.instructor).getInfo()
            if ret:
                slot.numRatings, slot.quality, slot.easiness = ret
    print "Done!\n"

def postProcessing(courses):
    """sort by ease, then quality, then number of ratings"""
    for course in courses:
        course.lectures.sort(key=lambda x: (x.easiness, x.quality,
                                            x.numRatings), reverse=True)
        course.tutorials.sort(key=lambda x: (x.easiness, x.quality,
                                             x.numRatings), reverse=True)

def scheduleGeneration(courses):
    """generates a schedule"""

    print "All calculations complete!"
    print "You will be given a chance to save your selected schedule " \
          "later."
    print "Format is: "
    attrs = ["Class#", "CompSec", "Place", "Start", "End", "Days",
             "Building Room", "Instructor"]
    print "\t".join(attrs)

    generator = Matcher(courses).matching()

    for schedule in generator:
        for slot in schedule:
            print slot
        print
        inp = raw_input("enter 's' to save the last schedule to a file; "
                        "enter 'n' to see another possible schedule: ")
        if inp.lower() == 's':
            return schedule
    print "\nSorry! No more schedules left! :(\n"
    return schedule

def saveToFile(schedule):
    """saves to file"""

    print "Would you like to save the last schedule to a file?"

    ans = raw_input("Enter 'y' if you do; 'n' if you don't: ").lower()
    if ans != 'y':
        print "Okay. Have a nice day then!"

    else:
        outputFile = raw_input("\nPlease enter a file name: ")
        f = open(outputFile+".txt", "w")
        for slot in schedule:
            f.write(str(slot) + "\n")
        f.close()

        print "\nOkay! The file is saved at {}. Have a nice " \
              "day.".format(outputFile+".txt")

def main():
    # variables
    userCourses = []
    courses = []
    user = User()
    sessionString = ""

    # program starts here
    sessionString = getUserInfo(userCourses)
    queryUniversity(userCourses, courses, user, sessionString)
    queryRateMyProfessors(courses)
    postProcessing(courses)
    lastSchedule = scheduleGeneration(courses)
    saveToFile(lastSchedule)

if __name__ == "__main__":
    main()
<commit_msg>"enter anything to exit:" for main.py<commit_after>"""
Copyright 2013 Shine Wang

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.
"""

from webParser import WebParser
from matcher import Matcher
from profParser import RateMyProfParser
from user import User

def getUserInfo(userCourses):
    print "Hello! I am Classy, and I'll optimize your course selection."
    print "The current optimizations are:"
    print "1.\tno time conflicts."
    print "2.\tsorting by ratemyprofessor.com ratings.\n"
    print "Please follow prompts VERY CLOSELY!"

    sessionString = raw_input("Which session you are in? (e.g. 'fall 2013' "
                              "without quotes): ").strip().lower()

    print "\nPlease add courses in decreasing importance."
    print "Format: 'cs 145' (without quotes)"

    cour = ""
    while cour.strip() != "0":
        cour = raw_input("Enter '0' to stop adding: ").strip().upper()
        userCourses.append(cour)
    userCourses.pop()  # the last '0'
    return sessionString

def queryUniversity(userCourses, courses, user, sessionString):
    print "Currently querying adm.uwaterloo.ca..."
    for courseName in userCourses:
        newCourse = WebParser().run(courseName, sessionString)
        courses.append(newCourse)
        if type(newCourse) is str:
            print "An error has occured with {0}: " \
                  "{1}. {0} will be ignored.".format(courseName, newCourse)
            courses.pop()
    print "Done!\n"

    print "Which reservation categories apply to you?"

    askedNames = set()
    for course in courses:
        for lec in course.lectures:
            if lec.thisUserCanAdd or len(lec.reserves) == 0:
                continue
            for res in lec.reserves:
                if res.enrlCap - res.enrlTotal <= 0:
                    # this reservation has no spots left anyway,
                    # so don't ask
                    continue
                for name in res.names:
                    if name in askedNames:
                        # don't want to re-ask
                        continue

                    askedNames.add(name)
                    inp = raw_input("Enter 'y' if you fufill the "
                                    "reservation of \"{}\". If not, "
                                    "enter 'n': ".format(name))
                    if inp.lower() == 'y':
                        user.userTypes.add(name)

            lec.postProcess(user.userTypes)

def queryRateMyProfessors(courses):
    print "Currently querying ratemyprofessors.com..."

    for i, course in enumerate(courses):
        print "Process: Course {}/{}".format(str(i+1), len(courses))

        for j, slot in enumerate(course.lectures):
            #print "Lecture {}/{}".format(str(j+1), len(course.lectures))
            ret = RateMyProfParser(slot.instructor).getInfo()
            if ret:
                slot.numRatings, slot.quality, slot.easiness = ret

        for j, slot in enumerate(course.tutorials):
            #print "Tutorial {}/{}".format(str(j+1), len(course.tutorials))
            ret = RateMyProfParser(slot.instructor).getInfo()
            if ret:
                slot.numRatings, slot.quality, slot.easiness = ret
    print "Done!\n"

def postProcessing(courses):
    """sort by ease, then quality, then number of ratings"""
    for course in courses:
        course.lectures.sort(key=lambda x: (x.easiness, x.quality,
                                            x.numRatings), reverse=True)
        course.tutorials.sort(key=lambda x: (x.easiness, x.quality,
                                             x.numRatings), reverse=True)

def scheduleGeneration(courses):
    """generates a schedule"""

    print "All calculations complete!"
    print "You will be given a chance to save your selected schedule " \
          "later."
    print "Format is: "
    attrs = ["Class#", "CompSec", "Place", "Start", "End", "Days",
             "Building Room", "Instructor"]
    print "\t".join(attrs)

    generator = Matcher(courses).matching()

    for schedule in generator:
        for slot in schedule:
            print slot
        print
        inp = raw_input("enter 's' to save the last schedule to a file; "
                        "enter 'n' to see another possible schedule: ")
        if inp.lower() == 's':
            return schedule
    print "\nSorry! No more schedules left! :(\n"
    return schedule

def saveToFile(schedule):
    """saves to file"""

    print "Would you like to save the last schedule to a file?"

    ans = raw_input("Enter 'y' if you do; 'n' if you don't: ").lower()
    if ans != 'y':
        print "Okay. Have a nice day then!"

    else:
        outputFile = raw_input("\nPlease enter a file name: ")
        f = open(outputFile+".txt", "w")
        for slot in schedule:
            f.write(str(slot) + "\n")
        f.close()

        print "\nOkay! The file is saved at {}. Have a nice " \
              "day.".format(outputFile+".txt")

    raw_input("Enter anything to exit: )

def main():
    # variables
    userCourses = []
    courses = []
    user = User()
    sessionString = ""

    # program starts here
    sessionString = getUserInfo(userCourses)
    queryUniversity(userCourses, courses, user, sessionString)
    queryRateMyProfessors(courses)
    postProcessing(courses)
    lastSchedule = scheduleGeneration(courses)
    saveToFile(lastSchedule)

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