<commit_before>import geojson, datetime, pytz, json, os, importlib, datetime
from housepy import server, config, log, util, strings
from pymongo import ASCENDING, DESCENDING

"""
    Basically, it's like this: /api/<view>/<output>?<query>

    The view is what kind of thing you want back (eg, a FeatureCollection (features), or a list of expeditions)
    The output is json if it's missing, otherwise, how about a map? or HTML? a chart?
    See templates/api/map.html for an example of how to subsequently load the JSON data asyncronously

    The query defines the filter. This might be any property at all, but keyed ones are:
    - Expedition (eg okavango_14)
    - Member (eg Jer)
    - startDate and endDate (endDate is one day later if omitted and startDate is present)
    - geoBounds (upper left (NW), lower right (SE): lon_1,lat_1,lon_2,lat_2. So Okavango is something like 20,-17,26,-22

    Can also do expeditionDay=N for the 24 hour period N days after the expedition start date specified in the config

    By default, returns the first 100 results. limit=N for more.
    Sorted in ascending order by t_utc. To reverse, use order=descending.
    Returns only one feature for every resolution seconds.

"""

class Api(server.Handler):

    def post(self, nop=None):
        return self.not_found()

    def get(self, view_name=None, output=None):

        # add a header for unrestricted access
        self.set_header("Access-Control-Allow-Origin", "*")
        csv = False

        # do the routing and load view module
        if not len(view_name):
            log.info("Listing views...")
            views = ["/api/%s" % filename.split('.')[0] for filename in os.listdir(os.path.abspath(os.path.dirname(__file__))) if filename[0] != "_" and filename[-3:] == ".py"]
            response = {'description': "API view endpoints", "views": views}
            return self.json(response)
        module_name = "api.%s" % view_name
        try:
            view = importlib.import_module(module_name)
            log.info("Loaded %s module" % module_name)
        except ImportError as e:
            log.error(log.exc(e))
            return self.error("View \"%s\" not recognized" % view_name)
        if len(output):
            if output == "csv":
                csv = True
            else:
                feature_type = self.get_argument('FeatureType', None)
                try:
                    return self.render("api/%s.html" % output, query=(self.request.uri).replace("/%s" % output, ""), feature_type=feature_type)
                except Exception as e:
                    return self.error("Could not render %s" % output)

        # time to build our search filter
        search = {}

        # special parsing for startDate and endDate
        start_string = self.get_argument('startDate', None)
        if start_string is not None:
            try:
                start_dt = util.parse_date(start_string, tz=config['local_tz'])
                start_t = util.timestamp(start_dt)
                end_string = self.get_argument('endDate', (start_dt + datetime.timedelta(days=1)).strftime("%Y-%m-%d"))
                end_dt = util.parse_date(end_string, tz=config['local_tz'])
                end_t = util.timestamp(end_dt)
                log.debug("startDate %s" % start_dt)
                log.debug("endDate %s" % end_dt)
                search['t_utc'] = {'$gt': start_t, '$lt': end_t}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad dates")

        # special parsing for location
        # expecting bounds (upper left (NW), lower right (SE)): lon_1,lat_1,lon_2,lat_2
        # oka: 20,-17,26,-22 nyc: -75,41,-71,40
        geo_bounds = self.get_argument('geoBounds', None)
        if geo_bounds is not None:
            try:
                lon_1, lat_1, lon_2, lat_2 = [float(coord) for coord in geo_bounds.split(',')]
                log.debug("geo_bounds %f,%f %f,%f" % (lon_1, lat_1, lon_2, lat_2))
                search['geometry'] = {'$geoWithin': {'$geometry': {'type': "Polygon", 'coordinates': [[ [lon_1, lat_1], [lon_2, lat_1], [lon_2, lat_2], [lon_1, lat_2], [lon_1, lat_1] ]]}}}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad geometry")

        # special parsing for expeditionDay (overrides startDate / endDate)
        expedition_day = self.get_argument('expeditionDay', None)
        if expedition_day is not None:
            try:
                expedition = self.get_argument('expedition', config['expedition'])
                expedition = self.get_argument('Expedition', expedition)
                start_dt = util.parse_date(str(config['start_date'][expedition]), tz=config['local_tz'])
                expedition_day = int(expedition_day) - 1
                log.debug("%s days after %s" % (expedition_day, start_dt))
                gt_t = util.timestamp(start_dt + datetime.timedelta(days=expedition_day))
                lt_t = util.timestamp(start_dt + datetime.timedelta(days=expedition_day + 1))
                search['t_utc'] = {'$gt': gt_t, '$lt': lt_t}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad day")

        # special parsing for resolution
        resolution = strings.as_numeric(self.request.arguments['resolution'][0]) if 'resolution' in self.request.arguments else 0

        # special parsing for SpeciesSearch
        species_search = self.get_argument('speciesSearch', None)
        if species_search is not None:
            search['$text'] = {'$search': species_search}

        # get limit and order
        # limit = self.get_argument('limit', 100) # this fails on int arguments, which I think is a tornado bug
        limit = strings.as_numeric(self.request.arguments['limit'][0]) if 'limit' in self.request.arguments else 100
        order = self.request.arguments['order'][0].lower() if 'order' in self.request.arguments else 'ascending'
        order = ASCENDING if order == "ascending" else DESCENDING

        # get all the rest of the arguments and format as properties
        try:
            for param, value in self.request.arguments.items():
                for i, item in enumerate(value):
                    item = item.decode('utf-8')
                    item = strings.as_numeric(item)
                    item = True if type(item) == str and item.lower() == "true" else item
                    item = False if type(item) == str and item.lower() == "false" else item
                    item = {'$exists': True} if item == '*' else item
                    value[i] = item
                search[param] = value[0] if len(value) == 1 else value
            search = {('properties.%s' % (strings.camelcase(param) if param != 't_utc' else 't_utc') if param != 'geometry' and param != '$text' else param): value for (param, value) in search.items() if param not in ['geoBounds', 'startDate', 'endDate', 'expeditionDay', 'limit', 'order', 'resolution', 'speciesSearch']}
        except Exception as e:
            log.error(log.exc(e))
            return self.error("bad parameters")

        # http://localhost:7777/api?geoBounds=20,-17,26,-22&startDate=2014-08-01&endDate=2014-09-01&Member=Jer
        log.info("FILTER %s" % search)

        # pass our search to the view module for execution and formatting
        # try:
        result = view.assemble(self, search, limit, order, resolution)
        if result is None:
            return
        if csv:
            return self.csv(format_csv(result), "data.csv")
        results, total, returned = result
        search = {key.replace('properties.', ''): value for (key, value) in search.items()}
        return self.json({'order': order, 'limit': limit, 'total': total, 'returned': len(results) if returned is None else returned, 'filter': search, 'results': results, 'resolution': resolution if resolution != 0 else "full"})
        # except Exception as e:
        #     return self.error(log.exc(e))

def format_csv(data):
    import csv
    features = data[0]['features']

    # build header
    header = []
    for feature in features:
        feature.update(feature['properties'])
        if feature['geometry'] is not None:
            feature.update({"Longitude": feature['geometry']['coordinates'][0], "Latitude": feature['geometry']['coordinates'][1]})
        del feature['properties']
        del feature['geometry']
        for key in feature:
            if key not in header:
                header.append(key)
    header.sort()
    log.debug(header)

    # populate rows
    csv = []
    csv.append(','.join(header))
    with open('data.csv', 'w', newline='') as csvfile:
        for feature in features:
            row = []
            for column in header:
                if column in feature:
                    value = feature[column]
                    if type(value) == str:
                        value = strings.singlespace(value)
                        value.replace('"', "'")
                        value = "%s" % value
                    row.append(str(value).replace(",", ""))
                else:
                    row.append("None")
            csv.append(','.join(row))
    return '\n'.join(csv)

    # print(json.dumps(features, indent=4, default=lambda x: str(x)))<commit_msg>experimental csv output<commit_after>import geojson, datetime, pytz, json, os, importlib, datetime
from housepy import server, config, log, util, strings
from pymongo import ASCENDING, DESCENDING

"""
    Basically, it's like this: /api/<view>/<output>?<query>

    The view is what kind of thing you want back (eg, a FeatureCollection (features), or a list of expeditions)
    The output is json if it's missing, otherwise, how about a map? or HTML? a chart?
    See templates/api/map.html for an example of how to subsequently load the JSON data asyncronously

    The query defines the filter. This might be any property at all, but keyed ones are:
    - Expedition (eg okavango_14)
    - Member (eg Jer)
    - startDate and endDate (endDate is one day later if omitted and startDate is present)
    - geoBounds (upper left (NW), lower right (SE): lon_1,lat_1,lon_2,lat_2. So Okavango is something like 20,-17,26,-22

    Can also do expeditionDay=N for the 24 hour period N days after the expedition start date specified in the config

    By default, returns the first 100 results. limit=N for more.
    Sorted in ascending order by t_utc. To reverse, use order=descending.
    Returns only one feature for every resolution seconds.

"""

class Api(server.Handler):

    def post(self, nop=None):
        return self.not_found()

    def get(self, view_name=None, output=None):

        # add a header for unrestricted access
        self.set_header("Access-Control-Allow-Origin", "*")
        csv = False

        # do the routing and load view module
        if not len(view_name):
            log.info("Listing views...")
            views = ["/api/%s" % filename.split('.')[0] for filename in os.listdir(os.path.abspath(os.path.dirname(__file__))) if filename[0] != "_" and filename[-3:] == ".py"]
            response = {'description': "API view endpoints", "views": views}
            return self.json(response)
        module_name = "api.%s" % view_name
        try:
            view = importlib.import_module(module_name)
            log.info("Loaded %s module" % module_name)
        except ImportError as e:
            log.error(log.exc(e))
            return self.error("View \"%s\" not recognized" % view_name)
        if len(output):
            if output == "csv":
                csv = True
            else:
                feature_type = self.get_argument('FeatureType', None)
                try:
                    return self.render("api/%s.html" % output, query=(self.request.uri).replace("/%s" % output, ""), feature_type=feature_type)
                except Exception as e:
                    return self.error("Could not render %s" % output)

        # time to build our search filter
        search = {}

        # special parsing for startDate and endDate
        start_string = self.get_argument('startDate', None)
        if start_string is not None:
            try:
                start_dt = util.parse_date(start_string, tz=config['local_tz'])
                start_t = util.timestamp(start_dt)
                end_string = self.get_argument('endDate', (start_dt + datetime.timedelta(days=1)).strftime("%Y-%m-%d"))
                end_dt = util.parse_date(end_string, tz=config['local_tz'])
                end_t = util.timestamp(end_dt)
                log.debug("startDate %s" % start_dt)
                log.debug("endDate %s" % end_dt)
                search['t_utc'] = {'$gt': start_t, '$lt': end_t}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad dates")

        # special parsing for location
        # expecting bounds (upper left (NW), lower right (SE)): lon_1,lat_1,lon_2,lat_2
        # oka: 20,-17,26,-22 nyc: -75,41,-71,40
        geo_bounds = self.get_argument('geoBounds', None)
        if geo_bounds is not None:
            try:
                lon_1, lat_1, lon_2, lat_2 = [float(coord) for coord in geo_bounds.split(',')]
                log.debug("geo_bounds %f,%f %f,%f" % (lon_1, lat_1, lon_2, lat_2))
                search['geometry'] = {'$geoWithin': {'$geometry': {'type': "Polygon", 'coordinates': [[ [lon_1, lat_1], [lon_2, lat_1], [lon_2, lat_2], [lon_1, lat_2], [lon_1, lat_1] ]]}}}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad geometry")

        # special parsing for expeditionDay (overrides startDate / endDate)
        expedition_day = self.get_argument('expeditionDay', None)
        if expedition_day is not None:
            try:
                expedition = self.get_argument('expedition', config['expedition'])
                expedition = self.get_argument('Expedition', expedition)
                start_dt = util.parse_date(str(config['start_date'][expedition]), tz=config['local_tz'])
                expedition_day = int(expedition_day) - 1
                log.debug("%s days after %s" % (expedition_day, start_dt))
                gt_t = util.timestamp(start_dt + datetime.timedelta(days=expedition_day))
                lt_t = util.timestamp(start_dt + datetime.timedelta(days=expedition_day + 1))
                search['t_utc'] = {'$gt': gt_t, '$lt': lt_t}
            except Exception as e:
                log.error(log.exc(e))
                return self.error("Bad day")

        # special parsing for resolution
        resolution = strings.as_numeric(self.request.arguments['resolution'][0]) if 'resolution' in self.request.arguments else 0

        # special parsing for SpeciesSearch
        species_search = self.get_argument('speciesSearch', None)
        if species_search is not None:
            search['$text'] = {'$search': species_search}

        # get limit and order
        # limit = self.get_argument('limit', 100) # this fails on int arguments, which I think is a tornado bug
        limit = strings.as_numeric(self.request.arguments['limit'][0]) if 'limit' in self.request.arguments else 100
        order = self.request.arguments['order'][0].lower() if 'order' in self.request.arguments else 'ascending'
        order = ASCENDING if order == "ascending" else DESCENDING

        # get all the rest of the arguments and format as properties
        try:
            for param, value in self.request.arguments.items():
                for i, item in enumerate(value):
                    item = item.decode('utf-8')
                    item = strings.as_numeric(item)
                    item = True if type(item) == str and item.lower() == "true" else item
                    item = False if type(item) == str and item.lower() == "false" else item
                    item = {'$exists': True} if item == '*' else item
                    value[i] = item
                search[param] = value[0] if len(value) == 1 else value
            search = {('properties.%s' % (strings.camelcase(param) if param != 't_utc' else 't_utc') if param != 'geometry' and param != '$text' else param): value for (param, value) in search.items() if param not in ['geoBounds', 'startDate', 'endDate', 'expeditionDay', 'limit', 'order', 'resolution', 'speciesSearch']}
        except Exception as e:
            log.error(log.exc(e))
            return self.error("bad parameters")

        # http://localhost:7777/api?geoBounds=20,-17,26,-22&startDate=2014-08-01&endDate=2014-09-01&Member=Jer
        log.info("FILTER %s" % search)

        # pass our search to the view module for execution and formatting
        # try:
        result = view.assemble(self, search, limit, order, resolution)
        if result is None:
            return
        if csv:
            return self.csv(format_csv(result), "data.csv")
        results, total, returned = result
        search = {key.replace('properties.', ''): value for (key, value) in search.items()}
        return self.json({'order': order, 'limit': limit, 'total': total, 'returned': len(results) if returned is None else returned, 'filter': search, 'results': results, 'resolution': resolution if resolution != 0 else "full"})
        # except Exception as e:
        #     return self.error(log.exc(e))

def format_csv(data):
    import csv
    features = data[0]['features']

    # build header
    header = []
    for feature in features:
        feature.update(feature['properties'])
        if 'taxonomy' in feature and feature['taxonomy'] is not None:
            feature.update(feature['taxonomy'])
        if feature['geometry'] is not None:
            feature.update({"Longitude": feature['geometry']['coordinates'][0], "Latitude": feature['geometry']['coordinates'][1]})
        del feature['properties']
        del feature['geometry']
        del feature['taxonomy']
        for key in feature:
            if key not in header:
                header.append(key)
    header.sort()
    log.debug(header)

    # populate rows
    csv = []
    csv.append(','.join(header))
    with open('data.csv', 'w', newline='') as csvfile:
        for feature in features:
            row = []
            for column in header:
                if column in feature:
                    value = feature[column]
                    if type(value) == str:
                        value = strings.singlespace(value)
                        value.replace('"', "'")
                        value = "%s" % value
                    row.append(str(value).replace(",", ""))
                else:
                    row.append("None")
            csv.append(','.join(row))
    return '\n'.join(csv)

    # print(json.dumps(features, indent=4, default=lambda x: str(x)))<|endoftext|>


<commit_before>from typing import Union

from hwt.code import If
from hwt.doc_markers import internal
from hwt.hdl.assignment import Assignment
from hwt.hdl.operator import Operator
from hwt.hdl.operatorDefs import AllOps
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BOOL, INT
from hwt.hdl.value import Value
from hwt.serializer.generic.context import SerializerCtx
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal

@internal
def isResultOfTypeConversion(sig):
    if len(sig.drivers) != 1:
        return False

    if sig.hidden:
        d = sig.singleDriver()
        return d.operator != AllOps.INDEX

    return False

class VhdlSerializer_ops():
    # keep in mind that there is no such a thing in vhdl itself
    opPrecedence = {
        AllOps.DOWNTO: 8,
        AllOps.TO: 8,
        AllOps.TERNARY: 8,

        AllOps.XOR: 7,
        AllOps.AND: 7,
        AllOps.OR: 7,

        AllOps.EQ: 6,
        AllOps.NEQ: 6,
        AllOps.GT: 6,
        AllOps.LT: 6,
        AllOps.GE: 6,
        AllOps.LE: 6,

        AllOps.CONCAT: 5,
        AllOps.ADD: 5,
        AllOps.SUB: 5,

        AllOps.NEG: 4,

        AllOps.DIV: 3,
        AllOps.MUL: 3,
        AllOps.MOD: 3,

        AllOps.NOT: 2,
        AllOps.POW: 2,

        AllOps.RISING_EDGE: 1,
        AllOps.FALLING_EDGE: 1,
        AllOps.INDEX: 1,
        AllOps.CALL: 1,
        AllOps.BitsAsSigned: 1,
        AllOps.BitsAsUnsigned: 1,
        AllOps.BitsAsVec: 1,
    }
    _binOps = {
        AllOps.AND: '%s AND %s',
        AllOps.OR: '%s OR %s',
        AllOps.XOR: '%s XOR %s',
        AllOps.CONCAT: '%s & %s',
        AllOps.DIV: '%s / %s',
        AllOps.DOWNTO: '%s-1 DOWNTO %s',
        AllOps.TO: '%s-1 TO %s',
        AllOps.EQ: '%s = %s',
        AllOps.GT: '%s > %s',
        AllOps.GE: '%s >= %s',
        AllOps.LE: '%s <= %s',
        AllOps.POW: '%s ** %s',
        AllOps.LT: '%s < %s',
        AllOps.SUB: '%s - %s',
        AllOps.MUL: '%s * %s',
        AllOps.NEQ: '%s /= %s',
        AllOps.ADD: '%s + %s',
    }
    _unaryOps = {
        AllOps.NOT: "NOT %s",
        AllOps.NEG: "-(%s)",
        AllOps.RISING_EDGE: "RISING_EDGE(%s)",
        AllOps.FALLING_EDGE: "FALLING_EDGE(%s)",
        AllOps.BitsAsSigned: "SIGNED(%s)",
        AllOps.BitsAsUnsigned: "UNSIGNED(%s)",
        AllOps.BitsAsVec: "STD_LOGIC_VECTOR(%s)",
    }

    @internal
    @classmethod
    def _tmp_var_for_ternary(cls, val: RtlSignal, ctx: SerializerCtx):
        """
        Optionaly convert boolean to std_logic_vector
        """
        o = ctx.createTmpVarFn("tmpTernary", val._dtype)
        cond, ifTrue, ifFalse = val.drivers[0].operands
        if_ = If(cond)
        if_.ifTrue.append(Assignment(ifTrue, o,
                                     virtual_only=True,
                                     parentStm=if_))
        if_.ifFalse = []
        if_.ifFalse.append(Assignment(ifFalse, o,
                                      virtual_only=True,
                                      parentStm=if_))
        if_._outputs.append(o)
        for obj in (cond, ifTrue, ifFalse):
            if isinstance(obj, RtlSignalBase):
                if_._inputs.append(obj)
        o.drivers.append(if_)
        return o

    @classmethod
    def _as_Bits(cls, val: Union[RtlSignal, Value], ctx: SerializerCtx):
        if val._dtype == BOOL:
            bit1_t = Bits(1)
            o = ctx.createTmpVarFn("tmpBool2std_logic", bit1_t)
            ifTrue, ifFalse = bit1_t.from_py(1), bit1_t.from_py(0)
            if_ = If(val)
            if_.ifTrue.append(Assignment(ifTrue, o, virtual_only=True, parentStm=if_))
            if_.ifFalse = []
            if_.ifFalse.append(Assignment(ifFalse, o, virtual_only=True, parentStm=if_))
            if_._outputs.append(o)
            o.drivers.append(if_)
            return o
        else:
            assert isinstance(val._dtype, Bits), val._dtype
            return val

    @internal
    @classmethod
    def _operand(cls, operand: Union[RtlSignal, Value], i: int,
                 oper: Operator,
                 expr_requires_parenthesis: bool,
                 cancel_parenthesis: bool,
                 ctx: SerializerCtx):
        # no nested ternary in expressions like
        # ( '1'  WHEN r = f ELSE  '0' ) & "0"
        try:
            isTernaryOp = operand.hidden\
                and operand.drivers[0].operator == AllOps.TERNARY
        except (AttributeError, IndexError):
            isTernaryOp = False

        if isTernaryOp:
            # rewrite ternary operator as if
            operand = cls._tmp_var_for_ternary(operand, ctx)

        return super(VhdlSerializer_ops, cls)._operand(
                operand,
                i,
                oper,
                not isTernaryOp and expr_requires_parenthesis,
                isTernaryOp or cancel_parenthesis,
                ctx)

    @classmethod
    def Operator(cls, op: Operator, ctx: SerializerCtx):
        ops = op.operands
        o = op.operator

        op_str = cls._unaryOps.get(o, None)
        if op_str is not None:
            cancel_parenthesis = op_str[-1] == ")"
            return op_str % (cls._operand(ops[0], 0, op, False, cancel_parenthesis, ctx))

        op_str = cls._binOps.get(o, None)
        if op_str is not None:
            res_t = op.result._dtype
            if isinstance(res_t, Bits) and res_t != BOOL:
                op0 = cls._as_Bits(ops[0], ctx)
                op1 = cls._as_Bits(ops[1], ctx)
                op = op.operator._evalFn(op0, op1).drivers[0]
            return cls._bin_op(op, op_str, ctx, expr_requires_parenthesis=True)

        if o == AllOps.CALL:
            return "%s(%s)" % (
                cls.FunctionContainer(ops[0]),
                ", ".join(
                    # operand i does not matter as thy are all in ()
                    map(lambda op: cls._operand(op, 1, o, False, True, ctx), ops[1:])
                    )
                )
        elif o == AllOps.INDEX:
            op0, op1 = ops
            if isinstance(op0, RtlSignalBase) and isResultOfTypeConversion(op0):
                op0 = ctx.createTmpVarFn("tmpTypeConv", op0._dtype)
                op0.def_val = ops[0]

            # if the op0 is not signal or other index index operator it is extracted
            # as tmp variable
            op0_str = cls._operand(op0, 0, op, False, False, ctx)
            op1_str = cls._operand(ops[1], 1, op, False, True, ctx)

            if isinstance(op1._dtype, Bits) and op1._dtype != INT:
                sig = op1._dtype.signed
                if sig is None:
                    op1_str = "UNSIGNED(%s)" % op1_str
                op1_str = "TO_INTEGER(%s)" % op1_str

            return "%s(%s)" % (op0_str, op1_str)
        elif o == AllOps.TERNARY:
            op0 = cls.condAsHdl([ops[0]], True, ctx)
            op1 = cls._operand(ops[1], 1, op, False, False, ctx)
            op2 = cls._operand(ops[2], 2, op, False, False, ctx)
            return "%s WHEN %s ELSE %s" % (op0, op1, op2)
        else:
            raise NotImplementedError(
                "Do not know how to convert %s to vhdl" % (o))
<commit_msg>vhdl parenthesis alg: fix for nester expressions without precedence care<commit_after>from typing import Union

from hwt.code import If
from hwt.doc_markers import internal
from hwt.hdl.assignment import Assignment
from hwt.hdl.operator import Operator
from hwt.hdl.operatorDefs import AllOps
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BOOL, INT
from hwt.hdl.value import Value
from hwt.serializer.generic.context import SerializerCtx
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal

@internal
def isResultOfTypeConversion(sig):
    if len(sig.drivers) != 1:
        return False

    if sig.hidden:
        d = sig.singleDriver()
        return d.operator != AllOps.INDEX

    return False

class VhdlSerializer_ops():
    # keep in mind that there is no such a thing in vhdl itself
    opPrecedence = {
        AllOps.DOWNTO: 8,
        AllOps.TO: 8,
        AllOps.TERNARY: 8,

        AllOps.XOR: 7,
        AllOps.AND: 7,
        AllOps.OR: 7,

        AllOps.EQ: 6,
        AllOps.NEQ: 6,
        AllOps.GT: 6,
        AllOps.LT: 6,
        AllOps.GE: 6,
        AllOps.LE: 6,

        AllOps.CONCAT: 5,
        AllOps.ADD: 5,
        AllOps.SUB: 5,

        AllOps.NEG: 4,

        AllOps.DIV: 3,
        AllOps.MUL: 3,
        AllOps.MOD: 3,

        AllOps.NOT: 2,
        AllOps.POW: 2,

        AllOps.RISING_EDGE: 1,
        AllOps.FALLING_EDGE: 1,
        AllOps.INDEX: 1,
        AllOps.CALL: 1,
        AllOps.BitsAsSigned: 1,
        AllOps.BitsAsUnsigned: 1,
        AllOps.BitsAsVec: 1,
    }
    _binOps = {
        AllOps.AND: '%s AND %s',
        AllOps.OR: '%s OR %s',
        AllOps.XOR: '%s XOR %s',
        AllOps.CONCAT: '%s & %s',
        AllOps.DIV: '%s / %s',
        AllOps.DOWNTO: '%s-1 DOWNTO %s',
        AllOps.TO: '%s-1 TO %s',
        AllOps.EQ: '%s = %s',
        AllOps.GT: '%s > %s',
        AllOps.GE: '%s >= %s',
        AllOps.LE: '%s <= %s',
        AllOps.POW: '%s ** %s',
        AllOps.LT: '%s < %s',
        AllOps.SUB: '%s - %s',
        AllOps.MUL: '%s * %s',
        AllOps.NEQ: '%s /= %s',
        AllOps.ADD: '%s + %s',
    }
    _unaryOps = {
        AllOps.NOT: "NOT %s",
        AllOps.NEG: "-(%s)",
        AllOps.RISING_EDGE: "RISING_EDGE(%s)",
        AllOps.FALLING_EDGE: "FALLING_EDGE(%s)",
        AllOps.BitsAsSigned: "SIGNED(%s)",
        AllOps.BitsAsUnsigned: "UNSIGNED(%s)",
        AllOps.BitsAsVec: "STD_LOGIC_VECTOR(%s)",
    }
    OPERANDS_WITH_PARENTHESIS = {
        AllOps.RISING_EDGE,
        AllOps.FALLING_EDGE,
        AllOps.BitsAsSigned,
        AllOps.BitsAsUnsigned,
        AllOps.BitsAsVec,
        AllOps.RISING_EDGE,
        AllOps.FALLING_EDGE,
        AllOps.INDEX,
        AllOps.CALL,
        AllOps.BitsAsSigned,
        AllOps.BitsAsUnsigned,
        AllOps.BitsAsVec,
    }

    @internal
    @classmethod
    def _tmp_var_for_ternary(cls, val: RtlSignal, ctx: SerializerCtx):
        """
        Optionaly convert boolean to std_logic_vector
        """
        o = ctx.createTmpVarFn("tmpTernary", val._dtype)
        cond, ifTrue, ifFalse = val.drivers[0].operands
        if_ = If(cond)
        if_.ifTrue.append(Assignment(ifTrue, o,
                                     virtual_only=True,
                                     parentStm=if_))
        if_.ifFalse = []
        if_.ifFalse.append(Assignment(ifFalse, o,
                                      virtual_only=True,
                                      parentStm=if_))
        if_._outputs.append(o)
        for obj in (cond, ifTrue, ifFalse):
            if isinstance(obj, RtlSignalBase):
                if_._inputs.append(obj)
        o.drivers.append(if_)
        return o

    @classmethod
    def _as_Bits(cls, val: Union[RtlSignal, Value], ctx: SerializerCtx):
        if val._dtype == BOOL:
            bit1_t = Bits(1)
            o = ctx.createTmpVarFn("tmpBool2std_logic", bit1_t)
            ifTrue, ifFalse = bit1_t.from_py(1), bit1_t.from_py(0)
            if_ = If(val)
            if_.ifTrue.append(Assignment(ifTrue, o, virtual_only=True, parentStm=if_))
            if_.ifFalse = []
            if_.ifFalse.append(Assignment(ifFalse, o, virtual_only=True, parentStm=if_))
            if_._outputs.append(o)
            o.drivers.append(if_)
            return o
        else:
            assert isinstance(val._dtype, Bits), val._dtype
            return val

    @internal
    @classmethod
    def _operand(cls, operand: Union[RtlSignal, Value], i: int,
                 oper: Operator,
                 expr_requires_parenthesis: bool,
                 cancel_parenthesis: bool,
                 ctx: SerializerCtx):
        # no nested ternary in expressions like
        # ( '1'  WHEN r = f ELSE  '0' ) & "0"
        try:
            isTernaryOp = operand.hidden\
                and operand.drivers[0].operator == AllOps.TERNARY
        except (AttributeError, IndexError):
            isTernaryOp = False

        if isTernaryOp:
            # rewrite ternary operator as if
            operand = cls._tmp_var_for_ternary(operand, ctx)
        else:
            try:
                already_have_parenthesis = operand.hidden\
                    and operand.drivers[0].operator in cls.OPERANDS_WITH_PARENTHESIS
            except (AttributeError, IndexError):
                already_have_parenthesis = False

        return super(VhdlSerializer_ops, cls)._operand(
                operand,
                i,
                oper,
                not isTernaryOp and not already_have_parenthesis\
                    and expr_requires_parenthesis,
                isTernaryOp or cancel_parenthesis,
                ctx)

    @classmethod
    def Operator(cls, op: Operator, ctx: SerializerCtx):
        ops = op.operands
        o = op.operator

        op_str = cls._unaryOps.get(o, None)
        if op_str is not None:
            cancel_parenthesis = op_str[-1] == ")"
            return op_str % (cls._operand(ops[0], 0, op, False, cancel_parenthesis, ctx))

        op_str = cls._binOps.get(o, None)
        if op_str is not None:
            res_t = op.result._dtype
            if isinstance(res_t, Bits) and res_t != BOOL:
                op0 = cls._as_Bits(ops[0], ctx)
                op1 = cls._as_Bits(ops[1], ctx)
                op = op.operator._evalFn(op0, op1).drivers[0]
            return cls._bin_op(op, op_str, ctx, expr_requires_parenthesis=op.operator != AllOps.CONCAT)

        if o == AllOps.CALL:
            return "%s(%s)" % (
                cls.FunctionContainer(ops[0]),
                ", ".join(
                    # operand i does not matter as thy are all in ()
                    map(lambda op: cls._operand(op, 1, o, False, True, ctx), ops[1:])
                    )
                )
        elif o == AllOps.INDEX:
            op0, op1 = ops
            if isinstance(op0, RtlSignalBase) and isResultOfTypeConversion(op0):
                op0 = ctx.createTmpVarFn("tmpTypeConv", op0._dtype)
                op0.def_val = ops[0]

            # if the op0 is not signal or other index index operator it is extracted
            # as tmp variable
            op0_str = cls._operand(op0, 0, op, False, False, ctx)
            op1_str = cls._operand(ops[1], 1, op, False, True, ctx)

            if isinstance(op1._dtype, Bits) and op1._dtype != INT:
                sig = op1._dtype.signed
                if sig is None:
                    op1_str = "UNSIGNED(%s)" % op1_str
                op1_str = "TO_INTEGER(%s)" % op1_str

            return "%s(%s)" % (op0_str, op1_str)
        elif o == AllOps.TERNARY:
            op0 = cls.condAsHdl([ops[0]], True, ctx)
            op1 = cls._operand(ops[1], 1, op, False, False, ctx)
            op2 = cls._operand(ops[2], 2, op, False, False, ctx)
            return "%s WHEN %s ELSE %s" % (op0, op1, op2)
        else:
            raise NotImplementedError(
                "Do not know how to convert %s to vhdl" % (o))
<|endoftext|>


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

from hwt.bitmask import mask
from hwt.hdl.constants import Time
from hwt.interfaces.std import VectSignal
from hwt.simulator.simTestCase import SimTestCase
from hwt.synthesizer.unit import Unit
from hwtHls.hls import Hls
from hwtHls.platform.virtual import VirtualHlsPlatform

class HlsConnection(Unit):
    def _declr(self):
        self.a = VectSignal(32, signed=False)
        self.b = VectSignal(32, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b)(a)

class HlsSlice(Unit):
    def _declr(self):
        self.a = VectSignal(32, signed=False)
        self.b = VectSignal(16, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(a[16:])(self.b)

class HlsSliceB(HlsSlice):
    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(a)[16:](self.b)

class HlsSlice2(Unit):
    def _declr(self):
        self.a = VectSignal(16, signed=False)
        self.b = VectSignal(32, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b[16:])(a)
            hls.io(self.b[:16])(16)

class HlsSlice2B(HlsSlice2):
    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b)[16:](a)
            hls.io(self.b)[:16](16)

class HlsSlice2C(HlsSlice2):
    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            b = hls.io(self.b)
            b[16:](a)
            b[:16](16)

class HlsSlicingTC(SimTestCase):
    def _test(self, unit, data_in, data_out):
        self.prepareUnit(unit, targetPlatform=VirtualHlsPlatform())
        unit.a._ag.data.extend(data_in)
        self.doSim(len(data_in) * 10 * Time.ns)
        self.assertValSequenceEqual(unit.b._ag.data, data_out)

    def test_connection(self):
        u = HlsConnection()
        data = [0, 1, 2, 3, 1 << 16]
        self._test(u, data, data)

    def _test_slice(self, cls):
        u = cls()
        data_in = [0, 1, 2, 3, 1 << 16, 768 << 20]
        data_out = [d & mask(16) for d in data_in]
        self._test(u, data_in, data_out)

    def test_slice(self):
        self._test_slice(HlsSlice)

    def test_sliceB(self):
        self._test_slice(HlsSliceB)

    def _test_slice2(self, cls):
        u = HlsSlice2()
        data_in = [0, 1, 2, 3]
        data_out = [d + (16 << 16) for d in data_in]
        self._test(u, data_in, data_out)

    def test_slice2(self, cls):
        self._test_slice2(HlsSlice2)

    def test_slice2B(self, cls):
        self._test_slice2(HlsSlice2B)

    def test_slice2C(self, cls):
        self._test_slice2(HlsSlice2C)

if __name__ == "__main__":
    import unittest

    from hwt.synthesizer.utils import toRtl

    suite = unittest.TestSuite()
    # suite.addTest(FrameTmplTC('test_frameHeader'))
    suite.addTest(unittest.makeSuite(HlsSlicingTC))
    runner = unittest.TextTestRunner(verbosity=3)
    runner.run(suite)

    u = HlsConnection()
    print(toRtl(u, targetPlatform=VirtualHlsPlatform()) + "\n")

    u = HlsSlice()
    print(toRtl(u, targetPlatform=VirtualHlsPlatform()))

    u = HlsSlice2()
    print(toRtl(u, targetPlatform=VirtualHlsPlatform()) + "\n")
<commit_msg>more HlsSlice tests<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hwt.bitmask import mask
from hwt.hdl.constants import Time
from hwt.interfaces.std import VectSignal
from hwt.simulator.simTestCase import SimTestCase
from hwt.synthesizer.unit import Unit
from hwtHls.hls import Hls
from hwtHls.platform.virtual import VirtualHlsPlatform

class HlsConnection(Unit):
    def _declr(self):
        self.a = VectSignal(32, signed=False)
        self.b = VectSignal(32, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b)(a)

class HlsSlice(Unit):
    def _declr(self):
        self.a = VectSignal(32, signed=False)
        self.b = VectSignal(16, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b)(a[16:])

class HlsSlice2(Unit):
    def _declr(self):
        self.a = VectSignal(16, signed=False)
        self.b = VectSignal(32, signed=False)

    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b[16:])(a)
            hls.io(self.b[:16])(16)

class HlsSlice2B(HlsSlice2):
    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            hls.io(self.b)[16:](a)
            hls.io(self.b)[:16](16)

class HlsSlice2C(HlsSlice2):
    def _impl(self):
        with Hls(self, freq=int(100e6)) as hls:
            a = hls.io(self.a)
            b = hls.io(self.b)
            b[16:](a)
            b[:16](16)

class HlsSlicingTC(SimTestCase):
    def _test(self, unit, data_in, data_out):
        self.prepareUnit(unit, targetPlatform=VirtualHlsPlatform())
        unit.a._ag.data.extend(data_in)
        self.doSim(len(data_in) * 10 * Time.ns)
        self.assertValSequenceEqual(unit.b._ag.data, data_out)

    def test_connection(self):
        u = HlsConnection()
        data = [0, 1, 2, 3, 1 << 16]
        self._test(u, data, data)

    def _test_slice(self, cls):
        u = cls()
        data_in = [0, 1, 2, 3, 1 << 16, 768 << 20]
        data_out = [d & mask(16) for d in data_in]
        self._test(u, data_in, data_out)

    def test_slice(self):
        self._test_slice(HlsSlice)

    def _test_slice2(self, cls):
        u = HlsSlice2()
        data_in = [0, 1, 2, 3]
        data_out = [d + (16 << 16) for d in data_in]
        self._test(u, data_in, data_out)

    def test_slice2(self):
        self._test_slice2(HlsSlice2)

    def test_slice2B(self):
        self._test_slice2(HlsSlice2B)

    def test_slice2C(self):
        self._test_slice2(HlsSlice2C)

if __name__ == "__main__":
    import unittest

    from hwt.synthesizer.utils import toRtl

    suite = unittest.TestSuite()
    # suite.addTest(FrameTmplTC('test_frameHeader'))
    suite.addTest(unittest.makeSuite(HlsSlicingTC))
    runner = unittest.TextTestRunner(verbosity=3)
    runner.run(suite)

    u = HlsConnection()
    print(toRtl(u, targetPlatform=VirtualHlsPlatform()) + "\n")

    u = HlsSlice2()
    print(toRtl(u, targetPlatform=VirtualHlsPlatform()))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
hyper/http20/exceptions
~~~~~~~~~~~~~~~~~~~~~~~

This defines exceptions used in the HTTP/2 portion of hyper.
"""
class HTTP20Error(Exception):
    """
    The base class for all of ``hyper``'s HTTP/2-related exceptions.
    """
    pass

class HPACKEncodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK encoding.
    """
    pass

class HPACKDecodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK decoding.
    """
    pass

class ConnectionError(HTTP20Error):
    """
    The remote party signalled an error affecting the entire HTTP/2
    connection, and the connection has been closed.
    """
    pass

class ProtocolError(HTTP20Error):
    """
    The remote party violated the HTTP/2 protocol.
    """
    pass
<commit_msg>Fix exception class spacing.<commit_after># -*- coding: utf-8 -*-
"""
hyper/http20/exceptions
~~~~~~~~~~~~~~~~~~~~~~~

This defines exceptions used in the HTTP/2 portion of hyper.
"""
class HTTP20Error(Exception):
    """
    The base class for all of ``hyper``'s HTTP/2-related exceptions.
    """
    pass

class HPACKEncodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK encoding.
    """
    pass

class HPACKDecodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK decoding.
    """
    pass

class ConnectionError(HTTP20Error):
    """
    The remote party signalled an error affecting the entire HTTP/2
    connection, and the connection has been closed.
    """
    pass

class ProtocolError(HTTP20Error):
    """
    The remote party violated the HTTP/2 protocol.
    """
    pass
<|endoftext|>


<commit_before># The MIT License (MIT)
# Copyright (c) 2014-2017 University of Bristol
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
"""
Main HyperStream class
"""

from . import ChannelManager, HyperStreamConfig, PlateManager, WorkflowManager, Client, Workflow
from version import __version__
from utils import HyperStreamLogger

import logging

class HyperStream(object):
    """
    HyperStream class: can be instantiated simply with hyperstream = HyperStream() for default operation
    """
    def __init__(self, loglevel=logging.DEBUG, file_logger=True, console_logger=True, mqtt_logger=None):
        """
        Initialise the HyperStream class. This starts the logger, loads the config files, connects to the main mongodb,
        and initialises the managers (channels, plates, workflows).

        :type console_logger: bool | dict | None
        :type file_logger: bool | dict | None
        :type mqtt_logger: dict | None
        :param loglevel: The default logging level
        :param file_logger: Whether to use a file logger. Either specify "True" in which case defaults are used,
        otherwise a dict optionally containing path, filename, loglevel
        :param console_logger: The console logger. Either specify "True" in which case defaults are used,
        otherwise a dict optionally containing loglevel
        :param mqtt_logger: Dict containing mqtt server, topic, and optionally loglevel
        """
        self.logger = HyperStreamLogger(
            default_loglevel=loglevel, file_logger=file_logger, console_logger=console_logger, mqtt_logger=mqtt_logger)
        self.config = HyperStreamConfig()
        self.client = Client(self.config.mongo)

        # Define some managers
        self.channel_manager = ChannelManager(self.config.plugins)
        self.plate_manager = PlateManager()
        self.workflow_manager = WorkflowManager(channel_manager=self.channel_manager, plate_manager=self.plate_manager)

    def __repr__(self):
        name = self.__class__.__name__
        values = ", ".join("{}={}".format(k, repr(v)) for k, v in sorted(self.__dict__.items())
                           if k[0] != "_" and not k.endswith('manager'))
        return "{}({})".format(name, values)

    def __str__(self):
        return "HyperStream version {version}, connected to mongodb://{host}:{port}/{db}".format(
            version=__version__,
            host=self.config.mongo['host'],
            port=self.config.mongo['port'],
            db=self.config.mongo['db']
        )

    def __del__(self):
        self._cleanup()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self._cleanup()
        return self

    def _cleanup(self):
        """
        Clean-up operations
        """
        for handler in list(self.logger.root_logger.handlers):
            self.logger.root_logger.removeHandler(handler)
            handler.flush()
            handler.close()

    def create_workflow(self, workflow_id, name, owner, description, online=False, monitor=False):
        """
        Create a new workflow. Simple wrapper for creating a workflow and adding it to the workflow manager.

        :param workflow_id: The workflow id
        :param name: The workflow name
        :param owner: The owner/creator of the workflow
        :param description: A human readable description
        :param online: Whether this workflow should be executed by the online engine
        :param monitor:
        :return: The workflow
        """
        w = Workflow(
            channels=self.channel_manager,
            plate_manager=self.plate_manager,
            workflow_id=workflow_id,
            name=name,
            owner=owner,
            description=description,
            online=online,
            monitor=monitor
        )

        self.workflow_manager.add_workflow(w)

        return w
<commit_msg>Made code pep8 compliant<commit_after># The MIT License (MIT)
# Copyright (c) 2014-2017 University of Bristol
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
"""
Main HyperStream class
"""

from . import ChannelManager, HyperStreamConfig, PlateManager, WorkflowManager
from . import Client, Workflow
from version import __version__
from utils import HyperStreamLogger

import logging

class HyperStream(object):
    """ HyperStream class

    This class can be instantiated simply with hyperstream = HyperStream() for
    default operation
    """
    def __init__(self, loglevel=logging.DEBUG, file_logger=True,
                 console_logger=True, mqtt_logger=None):
        """Initialise the HyperStream class.

        This starts the logger, loads the config files, connects to the main
        mongodb, and initialises the managers (channels, plates, workflows).

        :type console_logger: bool | dict | None
        :type file_logger: bool | dict | None
        :type mqtt_logger: dict | None
        :param loglevel: The default logging level
        :param file_logger: Whether to use a file logger. Either specify "True"
        in which case defaults are used, otherwise a dict optionally containing
        path, filename, loglevel
        :param console_logger: The console logger. Either specify "True" in
        which case defaults are used, otherwise a dict optionally containing
        loglevel
        :param mqtt_logger: Dict containing mqtt server, topic, and optionally
        loglevel
        """
        self.logger = HyperStreamLogger(default_loglevel=loglevel,
                                        file_logger=file_logger,
                                        console_logger=console_logger,
                                        mqtt_logger=mqtt_logger)
        self.config = HyperStreamConfig()
        self.client = Client(self.config.mongo)

        # Define some managers
        self.channel_manager = ChannelManager(self.config.plugins)
        self.plate_manager = PlateManager()
        self.workflow_manager = WorkflowManager(
                channel_manager=self.channel_manager,
                plate_manager=self.plate_manager)

    def __repr__(self):
        name = self.__class__.__name__
        values = ", ".join("{}={}".format(k, repr(v)) for k, v in sorted(self.__dict__.items())
                           if k[0] != "_" and not k.endswith('manager'))
        return "{}({})".format(name, values)

    def __str__(self):
        return ("HyperStream version {version}, connected to "
                "mongodb://{host}:{port}/{db}").format(
                    version=__version__,
                    host=self.config.mongo['host'],
                    port=self.config.mongo['port'],
                    db=self.config.mongo['db']
        )

    def __del__(self):
        self._cleanup()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self._cleanup()
        return self

    def _cleanup(self):
        """
        Clean-up operations
        """
        for handler in list(self.logger.root_logger.handlers):
            self.logger.root_logger.removeHandler(handler)
            handler.flush()
            handler.close()

    def create_workflow(self, workflow_id, name, owner, description,
                        online=False, monitor=False):
        """Create a new workflow.

        Simple wrapper for creating a workflow and adding it to the workflow
        manager.

        :param workflow_id: The workflow id
        :param name: The workflow name
        :param owner: The owner/creator of the workflow
        :param description: A human readable description
        :param online: Whether this workflow should be executed by the online
        engine
        :param monitor:
        :return: The workflow
        """
        w = Workflow(
            channels=self.channel_manager,
            plate_manager=self.plate_manager,
            workflow_id=workflow_id,
            name=name,
            owner=owner,
            description=description,
            online=online,
            monitor=monitor
        )

        self.workflow_manager.add_workflow(w)

        return w
<|endoftext|>


<commit_before>""" process a targeted experiment"""

import getpass
import logging
import shutil

from typing import Optional

from IPython.display import display

import metatlas.datastructures.analysis_identifiers as analysis_ids
import metatlas.plots.dill2plots as dp

from metatlas.datastructures.id_types import Experiment
from metatlas.datastructures.metatlas_dataset import MetatlasDataset
from metatlas.datastructures.utils import Username
from metatlas.io.gdrive import copy_outputs_to_google_drive
from metatlas.io.targeted_output import generate_all_outputs
from metatlas.tools.config import Config, Workflow, Analysis
from metatlas.tools.notebook import in_papermill
from metatlas.io.targeted_output import generate_qc_outputs

logger = logging.getLogger(__name__)

# pylint: disable=too-many-arguments,too-many-locals
def pre_annotation(
    experiment: Experiment,
    rt_alignment_number: int,
    analysis_number: int,
    source_atlas: str,
    configuration: Config,
    workflow: Workflow,
    analysis: Analysis,
    clear_cache: bool = False,
    username: Optional[Username] = None,
) -> MetatlasDataset:
    """All data processing that needs to occur before the annotation GUI in Targeted notebook"""
    params = analysis.parameters
    ids = analysis_ids.AnalysisIdentifiers(
        workflow=workflow.name,
        analysis=analysis.name,
        source_atlas=source_atlas,
        copy_atlas=params.copy_atlas,
        polarity=params.polarity,
        analysis_number=analysis_number,
        experiment=experiment,
        include_groups=params.include_groups,
        exclude_groups=params.exclude_groups,
        groups_controlled_vocab=params.groups_controlled_vocab,
        exclude_files=params.exclude_files,
        rt_alignment_number=rt_alignment_number,
        project_directory=params.project_directory,
        google_folder=params.google_folder,
        username=getpass.getuser() if username is None else username,
        configuration=configuration,
    )
    if clear_cache:
        logger.info("Clearing cache.")
        shutil.rmtree(ids.cache_dir)
    metatlas_dataset = MetatlasDataset(ids=ids, max_cpus=params.max_cpus)
    metatlas_dataset.filter_compounds_by_signal(params.num_points, params.peak_height, params.msms_score)
    return metatlas_dataset

def annotation_gui(
    data: MetatlasDataset,
    compound_idx: int = 0,
    width: float = 15,
    height: float = 3,
    alpha: float = 0.5,
    colors="",
    adjustable_rt_peak=False,
) -> Optional[dp.adjust_rt_for_selected_compound]:
    """
    Opens the interactive GUI for setting RT bounds and annotating peaks
    inputs:
        compound_idx: number of compound-adduct pair to start at
        width: width of interface in inches
        height: height of each plot in inches
        alpha: (0-1] controls transparency of lines on EIC plot
        colors: list (color_id, search_string) for coloring lines on EIC plot
                based on search_string occuring in LCMS run filename
    """
    if in_papermill():
        logger.info("Non-interactive execution of notebook detected. Skipping annotation GUI.")
        return None
    display(dp.LOGGING_WIDGET)  # surface event handler error messages in UI
    return dp.adjust_rt_for_selected_compound(
        data,
        msms_hits=data.hits,
        color_me=colors,
        compound_idx=compound_idx,
        alpha=alpha,
        width=width,
        height=height,
        adjustable_rt_peak=adjustable_rt_peak,
    )

# pylint:disable=unused-argument
def post_annotation(
    data: MetatlasDataset, configuration: Config, workflow: Workflow, analysis: Analysis
) -> None:
    """All data processing that needs to occur after the annotation GUI in Targeted notebook"""
    if analysis.parameters.require_all_evaluated and not in_papermill():
        data.error_if_not_all_evaluated()
    if analysis.parameters.filter_removed:
        data.filter_compounds_ms1_notes_remove()
    data.extra_time = 0.5
    logger.info("extra_time set to 0.5 minutes for output generation.")
    data.update()  # update hits and data if they no longer are based on current rt bounds
    if analysis.parameters.generate_qc_outputs:
        generate_qc_outputs(data)
    if analysis.parameters.generate_analysis_outputs:
        logger.info(
            "Setting exclude_groups to %s.",
            str(analysis.parameters.exclude_groups_for_analysis_outputs),
        )
        data.ids.set_trait("exclude_groups", analysis.parameters.exclude_groups_for_analysis_outputs)
        generate_all_outputs(data, workflow, analysis)
    if not in_papermill():
        copy_outputs_to_google_drive(data.ids)
    logger.info("DONE - execution of notebook %s is complete.", "in draft mode" if in_papermill() else " ")
<commit_msg>ci(gui): revert to displaying GUI under papermill<commit_after>""" process a targeted experiment"""

import getpass
import logging
import shutil

from typing import Optional

from IPython.display import display

import metatlas.datastructures.analysis_identifiers as analysis_ids
import metatlas.plots.dill2plots as dp

from metatlas.datastructures.id_types import Experiment
from metatlas.datastructures.metatlas_dataset import MetatlasDataset
from metatlas.datastructures.utils import Username
from metatlas.io.gdrive import copy_outputs_to_google_drive
from metatlas.io.targeted_output import generate_all_outputs
from metatlas.tools.config import Config, Workflow, Analysis
from metatlas.tools.notebook import in_papermill
from metatlas.io.targeted_output import generate_qc_outputs

logger = logging.getLogger(__name__)

# pylint: disable=too-many-arguments,too-many-locals
def pre_annotation(
    experiment: Experiment,
    rt_alignment_number: int,
    analysis_number: int,
    source_atlas: str,
    configuration: Config,
    workflow: Workflow,
    analysis: Analysis,
    clear_cache: bool = False,
    username: Optional[Username] = None,
) -> MetatlasDataset:
    """All data processing that needs to occur before the annotation GUI in Targeted notebook"""
    params = analysis.parameters
    ids = analysis_ids.AnalysisIdentifiers(
        workflow=workflow.name,
        analysis=analysis.name,
        source_atlas=source_atlas,
        copy_atlas=params.copy_atlas,
        polarity=params.polarity,
        analysis_number=analysis_number,
        experiment=experiment,
        include_groups=params.include_groups,
        exclude_groups=params.exclude_groups,
        groups_controlled_vocab=params.groups_controlled_vocab,
        exclude_files=params.exclude_files,
        rt_alignment_number=rt_alignment_number,
        project_directory=params.project_directory,
        google_folder=params.google_folder,
        username=getpass.getuser() if username is None else username,
        configuration=configuration,
    )
    if clear_cache:
        logger.info("Clearing cache.")
        shutil.rmtree(ids.cache_dir)
    metatlas_dataset = MetatlasDataset(ids=ids, max_cpus=params.max_cpus)
    metatlas_dataset.filter_compounds_by_signal(params.num_points, params.peak_height, params.msms_score)
    return metatlas_dataset

def annotation_gui(
    data: MetatlasDataset,
    compound_idx: int = 0,
    width: float = 15,
    height: float = 3,
    alpha: float = 0.5,
    colors="",
    adjustable_rt_peak=False,
) -> Optional[dp.adjust_rt_for_selected_compound]:
    """
    Opens the interactive GUI for setting RT bounds and annotating peaks
    inputs:
        compound_idx: number of compound-adduct pair to start at
        width: width of interface in inches
        height: height of each plot in inches
        alpha: (0-1] controls transparency of lines on EIC plot
        colors: list (color_id, search_string) for coloring lines on EIC plot
                based on search_string occuring in LCMS run filename
    """
    display(dp.LOGGING_WIDGET)  # surface event handler error messages in UI
    return dp.adjust_rt_for_selected_compound(
        data,
        msms_hits=data.hits,
        color_me=colors,
        compound_idx=compound_idx,
        alpha=alpha,
        width=width,
        height=height,
        adjustable_rt_peak=adjustable_rt_peak,
    )

# pylint:disable=unused-argument
def post_annotation(
    data: MetatlasDataset, configuration: Config, workflow: Workflow, analysis: Analysis
) -> None:
    """All data processing that needs to occur after the annotation GUI in Targeted notebook"""
    if analysis.parameters.require_all_evaluated and not in_papermill():
        data.error_if_not_all_evaluated()
    if analysis.parameters.filter_removed:
        data.filter_compounds_ms1_notes_remove()
    data.extra_time = 0.5
    logger.info("extra_time set to 0.5 minutes for output generation.")
    data.update()  # update hits and data if they no longer are based on current rt bounds
    if analysis.parameters.generate_qc_outputs:
        generate_qc_outputs(data)
    if analysis.parameters.generate_analysis_outputs:
        logger.info(
            "Setting exclude_groups to %s.",
            str(analysis.parameters.exclude_groups_for_analysis_outputs),
        )
        data.ids.set_trait("exclude_groups", analysis.parameters.exclude_groups_for_analysis_outputs)
        generate_all_outputs(data, workflow, analysis)
    if not in_papermill():
        copy_outputs_to_google_drive(data.ids)
    logger.info("DONE - execution of notebook %s is complete.", "in draft mode" if in_papermill() else " ")
<|endoftext|>


<commit_before>

import os
import sys
import argparse as ap

# ------------------------------------
# Main function
# ------------------------------------

__version__ = '0.3.0'

def main():
    """The Main function/pipeline for GSEAPY.

    """
    # Parse options...
    argparser = prepare_argparser()
    args = argparser.parse_args()

    if args.outdir:
        # use a output directory to store GSEAPY output
        if not os.path.exists( args.outdir ):
            try:
                os.makedirs( args.outdir )
            except:
                sys.exit( "Output directory (%s) could not be created. Terminating program." % args.outdir )

    subcommand  = args.subcommand_name

    if subcommand == "replot":
        # reproduce plots using GSEAPY
        from .gsea import replot

        replot(indir=args.indir,outdir=args.outdir,weight=args.weight,figsize=args.figsize,format=args.format,)

    elif subcommand == "call":
        # compute using GSEAPY
        from .gsea import run

        run(args.data, args.gmt,args.cls, args.mins, args.maxs, args.n, args.weight,
            args.type, args.method,args.ascending, args.outdir,args.figsize,args.format,)

def prepare_argparser ():
    """Prepare argparser object. New options will be added in this
    function first.

    """
    description = "%(prog)s -- GSEAPY for Gene Set Enrichment Analysis"
    epilog = "For command line options of each command, type: %(prog)s COMMAND -h"

    # top-level parser
    argparser = ap.ArgumentParser( description = description, epilog = epilog )
    argparser.add_argument("--version", action="version", version="%(prog)s "+ __version__)
    subparsers = argparser.add_subparsers( dest = 'subcommand_name' ) #help="sub-command help")

    # command for 'call'
    add_call_parser( subparsers )

    # command for 'plot'
    add_plot_parser( subparsers )

    return argparser

def add_output_option ( parser ):
    parser.add_argument("-o", "--outdir", dest = "outdir", type = str, default = 'gseapy_out',
                        metavar='',action="store",
                        help = "The gseapy output directory. Default: the current working directory")

    parser.add_argument( "-f", "--format", dest = "format", type = str, metavar='',action="store",
                              choices = ("pdf", "png", "jpeg", "eps"),default = "pdf",
                              help = "Format of output figures, choose from {'pdf', 'png', 'jpeg', 'eps'}. Default: 'pdf'." )
    parser.add_argument("--figsize",action='store',nargs=2,dest='figsize',
                        metavar=('width', 'height'),type=float,default=[6.5,6],
                        help="The figsize keyword argument need two parameter to define. Default: [6.5,6]")

def add_output_group ( parser, required = True ):
    output_group = parser.add_mutually_exclusive_group( required = required )
    output_group.add_argument( "--ofile", dest = "ofile", type = str,
                               help = "Output file name. Mutually exclusive with --o-prefix." )
    output_group.add_argument( "--o-prefix", dest = "oprefix", type = str,
                               help = "Output file prefix. Mutually exclusive with -o/--ofile." )

def add_call_parser( subparsers ):
    """Add main function 'call' argument parsers.
    """
    argparser_call = subparsers.add_parser("call", help="Main GSEAPY Function: run GSEAPY instead of GSEA.")

    # group for input files
    group_input = argparser_call.add_argument_group( "Input files arguments" )
    group_input.add_argument( "-d", "--datab", dest = "data",  action="store",type = str, required = True,
                              help = "Expression table of phenotypes. Expected a txt file.Same with GSEA." )
    group_input.add_argument( "-c", "--cls", dest = "cls",  action="store", type = str, required = True,
                                    help = ".cls files. Same with GSEA.")
    group_input.add_argument( "-g", "--gmt", dest = "gmt",  action="store", type = str, required = True,
                              help = "Gene Sets in gmt format. Same with GSEA." )
    group_input.add_argument( "-p", "--permu-type",  action="store",dest = "type", type = str,metavar='',
                              choices = ("gene_set", "phnotype"),default = "gene_set",
                              help = "Gene Sets in gmt format. Same with GSEA, choose from {'gene_set', 'phenotype'}")

    # group for output files
    group_output = argparser_call.add_argument_group( "Output arguments" )
    add_output_option( group_output )

     # group for General options.
    group_opt = argparser_call.add_argument_group( "GSEA advance arguments" )
    group_opt.add_argument( "--min-size",  dest = "mins",  action="store",type = int, default =15,metavar='',
                            help = "Min size of gene sets. Default: 15")
    group_opt.add_argument( "--max-size", dest = "maxs",  action="store",type = int, default = 1000,metavar='',
                            help = "Max size of gene sets. Default: 1000")
    group_opt.add_argument( "-n", "--permu-num", dest = "n",  action="store",type = int, default = 1000, metavar='',
                            help = "Permutation number. Default: 1000" )
    group_opt.add_argument("-w","--weight",action='store',dest='weight',default= 1, type= float,metavar='',
                            help='Weighted_score type of rank_metrics.Choose from {0, 1, 1.5, 2},default: 1',)

    group_opt.add_argument( "-m", "--method",  action="store",dest = "method", type = str, metavar='',
                            choices = ("signal_to_noise", "t_test", "ratio_of_classes", "diff_of_classes","log2_ratio_of_classes"),
                            default = "log2_ratio_of_classes",
                            help = "Methods to calculate correlations of ranking metrics. \
                            Choose from {'signal_to_noise', 't_test', 'ratio_of_classes', 'diff_of_classes','log2_ratio_of_classes'}.\
                            Default: 'log2_ratio_of_classes'" )
    group_opt.add_argument("-a","--ascending",action='store_true',dest='ascending',default= False ,
                            help='Rank metrice sorting order. If the -a flag was chosen, then ascending equals to True. Default: False.')

    return

def add_plot_parser( subparsers ):
    """Add function 'plot' argument parsers.
    """
    argparser_replot = subparsers.add_parser( "replot",help = "Reproduce GSEA desktop figures." )

    group_replot = argparser_replot.add_argument_group( "Positional arguments" )

    group_replot.add_argument("-i","--indir", action="store", dest="indir", required=True, metavar='',
                        help="The GSEA desktop results directroy that you want to reproduce the figure ")
    add_output_option( group_replot)
    #add_output_group( argparser_plot )
    group_replot.add_argument("-w","--weight",action='store',dest='weight',default= 1, type= float,metavar='',
                        help='Weighted_score type of rank_metrics.Choose from (0, 1, 1.5, 2),default: 1',)

    return

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        sys.stderr.write("User interrupted me! ;-) Bye!\n")
        sys.exit(0)<commit_msg>Typo fix<commit_after>

import os
import sys
import argparse as ap

# ------------------------------------
# Main function
# ------------------------------------

__version__ = '0.3.0'

def main():
    """The Main function/pipeline for GSEAPY.

    """
    # Parse options...
    argparser = prepare_argparser()
    args = argparser.parse_args()

    if args.outdir:
        # use a output directory to store GSEAPY output
        if not os.path.exists( args.outdir ):
            try:
                os.makedirs( args.outdir )
            except:
                sys.exit( "Output directory (%s) could not be created. Terminating program." % args.outdir )

    subcommand  = args.subcommand_name

    if subcommand == "replot":
        # reproduce plots using GSEAPY
        from .gsea import replot

        replot(indir=args.indir,outdir=args.outdir,weight=args.weight,figsize=args.figsize,format=args.format,)

    elif subcommand == "call":
        # compute using GSEAPY
        from .gsea import run

        run(args.data, args.gmt,args.cls, args.mins, args.maxs, args.n, args.weight,
            args.type, args.method,args.ascending, args.outdir,args.figsize,args.format,)

def prepare_argparser ():
    """Prepare argparser object. New options will be added in this
    function first.

    """
    description = "%(prog)s -- Gene Set Enrichment Analysis in Python"
    epilog = "For command line options of each command, type: %(prog)s COMMAND -h"

    # top-level parser
    argparser = ap.ArgumentParser( description = description, epilog = epilog )
    argparser.add_argument("--version", action="version", version="%(prog)s "+ __version__)
    subparsers = argparser.add_subparsers( dest = 'subcommand_name' ) #help="sub-command help")

    # command for 'call'
    add_call_parser( subparsers )

    # command for 'plot'
    add_plot_parser( subparsers )

    return argparser

def add_output_option ( parser ):
    parser.add_argument("-o", "--outdir", dest = "outdir", type = str, default = 'gseapy_out',
                        metavar='',action="store",
                        help = "The GSEAPY output directory. Default: the current working directory")

    parser.add_argument( "-f", "--format", dest = "format", type = str, metavar='',action="store",
                              choices = ("pdf", "png", "jpeg", "eps"),default = "pdf",
                              help = "Format of output figures, choose from {'pdf', 'png', 'jpeg', 'eps'}. Default: 'pdf'." )
    parser.add_argument("--figsize",action='store',nargs=2,dest='figsize',
                        metavar=('width', 'height'),type=float,default=[6.5,6],
                        help="The figsize keyword argument need two parameter to define. Default: [6.5,6]")

def add_output_group ( parser, required = True ):
    output_group = parser.add_mutually_exclusive_group( required = required )
    output_group.add_argument( "--ofile", dest = "ofile", type = str,
                               help = "Output file name. Mutually exclusive with --o-prefix." )
    output_group.add_argument( "--o-prefix", dest = "oprefix", type = str,
                               help = "Output file prefix. Mutually exclusive with -o/--ofile." )

def add_call_parser( subparsers ):
    """Add main function 'call' argument parsers.
    """
    argparser_call = subparsers.add_parser("call", help="Main GSEAPY Function: run GSEAPY instead of GSEA.")

    # group for input files
    group_input = argparser_call.add_argument_group( "Input files arguments" )
    group_input.add_argument( "-d", "--datab", dest = "data",  action="store",type = str, required = True,
                              help = "Input gene expression Affymetrix dataset file in txt format.Same with GSEA." )
    group_input.add_argument( "-c", "--cls", dest = "cls",  action="store", type = str, required = True,
                                    help = "Input class vector (phenotype) file in CLS format. Same with GSEA.")
    group_input.add_argument( "-g", "--gmt", dest = "gmt",  action="store", type = str, required = True,
                              help = "Gene set database in GMT format. Same with GSEA." )
    group_input.add_argument( "-p", "--permu-type",  action="store",dest = "type", type = str,metavar='',
                              choices = ("gene_set", "phenotype"),default = "gene_set",
                              help = "Permutation type. Same with GSEA, choose from {'gene_set', 'phenotype'}")

    # group for output files
    group_output = argparser_call.add_argument_group( "Output arguments" )
    add_output_option( group_output )

     # group for General options.
    group_opt = argparser_call.add_argument_group( "GSEA advanced arguments" )
    group_opt.add_argument( "--min-size",  dest = "mins",  action="store",type = int, default =15,metavar='',
                            help = "Min size of input genes presented in Gene Sets. Default: 15")
    group_opt.add_argument( "--max-size", dest = "maxs",  action="store",type = int, default = 1000,metavar='',
                            help = "Max size of input genes presented in Gene Sets. Default: 1000")
    group_opt.add_argument( "-n", "--permu-num", dest = "n",  action="store",type = int, default = 1000, metavar='',
                            help = "Number of random permutations. For calculating esnulls. Default: 1000" )
    group_opt.add_argument("-w","--weight",action='store',dest='weight',default= 1, type= float,metavar='',
                            help='Weighted_score of rank_metrics.For weighting input genes. Choose from {0, 1, 1.5, 2},default: 1',)

    group_opt.add_argument( "-m", "--method",  action="store",dest = "method", type = str, metavar='',
                            choices = ("signal_to_noise", "t_test", "ratio_of_classes", "diff_of_classes","log2_ratio_of_classes"),
                            default = "log2_ratio_of_classes",
                            help = "Methods to calculate correlations of ranking metrics. \
                            Choose from {'signal_to_noise', 't_test', 'ratio_of_classes', 'diff_of_classes','log2_ratio_of_classes'}.\
                            Default: 'log2_ratio_of_classes'" )
    group_opt.add_argument("-a","--ascending",action='store_true',dest='ascending',default= False ,
                            help='Rank metric sorting order. If the -a flag was chosen, then ascending equals to True. Default: False.')

    return

def add_plot_parser( subparsers ):
    """Add function 'plot' argument parsers.
    """
    argparser_replot = subparsers.add_parser( "replot",help = "Reproduce GSEA desktop figures." )

    group_replot = argparser_replot.add_argument_group( "Positional arguments" )

    group_replot.add_argument("-i","--indir", action="store", dest="indir", required=True, metavar='',
                        help="The GSEA desktop results directroy that you want to reproduce the figure ")
    add_output_option( group_replot)
    #add_output_group( argparser_plot )
    group_replot.add_argument("-w","--weight",action='store',dest='weight',default= 1, type= float,metavar='',
                        help='Weighted_score of rank_metrics. Please Use the same value in GSEA. Choose from (0, 1, 1.5, 2),default: 1',)

    return

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        sys.stderr.write("User interrupted me! ;-) Bye!\n")
        sys.exit(0)<|endoftext|>


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

##PACKAGES##
from __future__ import division
from builtins import zip
import numpy as np
import matplotlib
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from .._shared.helpers import *
from ..tools.reduce import reduce as reduceD

##MAIN FUNCTION##
def animated_plot(x, *args, **kwargs):
    """
    Implements animated trajectory plot

    INPUTS:
    -numpy array(s)
    -list of numpy arrays

    OUTPUTS:
    -returns fig, ax, data, line_ani. fig, ax and line_ani are matplotlib figure, axis, and animation handles,
    respectively. Data is a numpy array of (reduced) data. (Optional, to use set return_data=True)
    """

    assert x[0].shape[1]>2, "Hypertools currently only supports animation for data with > 2 dims."

    ## HYPERTOOLS-SPECIFIC ARG PARSING ##

    if 'zoom' in kwargs:
        zoom=kwargs['zoom']
        del kwargs['zoom']
    else:
        zoom=0

    if 'chemtrails' in kwargs:
        chemtrails= kwargs['chemtrails']
        del kwargs['chemtrails']
    else:
        chemtrails=False

    if 'rotations' in kwargs:
        rotations=kwargs['rotations']
        del kwargs['rotations']
    else:
        rotations=2

    if 'duration' in kwargs:
        duration=kwargs['duration']
        del kwargs['duration']
    else:
        duration=30

    if 'frame_rate' in kwargs:
        frame_rate=kwargs['frame_rate']
        del kwargs['frame_rate']
    else:
        frame_rate=50

    if 'tail_duration' in kwargs:
        tail_duration=kwargs['tail_duration']
        del kwargs['tail_duration']
    else:
        tail_duration=2

    if 'return_data' in kwargs:
        return_data = kwargs['return_data']
        del kwargs['return_data']
    else:
        return_data=False

    if 'legend' in kwargs:
        legend=True
        legend_data = kwargs['legend']
        del kwargs['legend']
    else:
        legend=False

    if 'color_palette' in kwargs:
        palette = kwargs['color_palette']
        del kwargs['color_palette']

    if 'save_path' in kwargs:
        save=True
        save_path = kwargs['save_path']
        del kwargs['save_path']
    else:
        save=False

    # handle show flag
    if 'show' in kwargs:
        show=kwargs['show']
        del kwargs['show']
        matplotlib.use('Agg')
        import matplotlib.pyplot as plt
    else:
        show=True
        import matplotlib.pyplot as plt

    ##SUB FUNCTIONS##
    def plot_cube(scale, const=1):
        cube = {
            "top"    : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[1,1],[1,1]] ),
            "bottom" : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[-1,-1],[-1,-1]] ),
            "left"   : ( [[-1,-1],[-1,-1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "right"  : ( [[1,1],[1,1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "front"  : ( [[-1,1],[-1,1]], [[-1,-1],[-1,-1]], [[-1,-1],[1,1]] ),
            "back"   : ( [[-1,1],[-1,1]], [[1,1],[1,1]], [[-1,-1],[1,1]] )
            }

        plane_list = []
        for side in cube:
            (Xs, Ys, Zs) = (
                np.asarray(cube[side][0])*scale*const,
                np.asarray(cube[side][1])*scale*const,
                np.asarray(cube[side][2])*scale*const
                )
            plane_list.append(ax.plot_wireframe(Xs, Ys, Zs, rstride=1, cstride=1, color='black', linewidth=2))
        return plane_list

    def update_lines(num, data_lines, lines, trail_lines, cube_scale, tail_duration):

        if hasattr(update_lines, 'planes'):
            for plane in update_lines.planes:
                plane.remove()

        update_lines.planes = plot_cube(cube_scale)
        ax.view_init(elev=10, azim=rotations*(360*(num/data_lines[0].shape[0])))
        ax.dist=8-zoom

        for line, data, trail in zip(lines, data_lines, trail_lines):
            if num<=tail_duration:
                    line.set_data(data[0:num+1, 0:2].T)
                    line.set_3d_properties(data[0:num+1, 2])
            else:
                line.set_data(data[num-tail_duration:num+1, 0:2].T)
                line.set_3d_properties(data[num-tail_duration:num+1, 2])
            if chemtrails:
                trail.set_data(data[0:num + 1, 0:2].T)
                trail.set_3d_properties(data[0:num + 1, 2])
        return lines,trail_lines

    args_list = parse_args(x,args)
    kwargs_list = parse_kwargs(x,kwargs)

    # Attaching 3D axis to the figure
    fig = plt.figure()
    ax = p3.Axes3D(fig)

    if type(x) is not list:
        x = [x]

    interp_val = frame_rate*duration/(x[0].shape[0] - 1)
    x = interp_array_list(x, interp_val=interp_val)
    x = center(x)
    x = scale(x)

    if tail_duration==0:
        tail_duration=1
    else:
        tail_duration = frame_rate*tail_duration

    lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], linewidth=3, *args_list[idx], **kwargs_list[idx])[0] for idx,dat in enumerate(x)]
    trail = [
        ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], alpha=.3, linewidth=3, *args_list[idx], **kwargs_list[idx])[0]
        for idx, dat in enumerate(x)]

    ax.set_axis_off()

    # Get cube scale from data
    cube_scale = 1

    # Setting the axes properties
    ax.set_xlim3d([-cube_scale, cube_scale])
    ax.set_ylim3d([-cube_scale, cube_scale])
    ax.set_zlim3d([-cube_scale, cube_scale])

    #add legend
    if legend:
        proxies = [plt.Rectangle((0, 0), 1, 1, fc=palette[idx]) for idx,label in enumerate(legend_data)]
        ax.legend(proxies,legend_data)

    # Creating the Animation object
    line_ani = animation.FuncAnimation(fig, update_lines, x[0].shape[0], fargs=(x, lines, trail, cube_scale, tail_duration),
                                   interval=1000/frame_rate, blit=False, repeat=False)
    if save:
        Writer = animation.writers['ffmpeg']
        writer = Writer(fps=frame_rate, bitrate=1800)
        line_ani.save(save_path, writer=writer)

    if show:
        plt.show()

    if return_data:
        return fig,ax,x,line_ani
<commit_msg>patched bug where tail_duration would crash if not an integer value<commit_after>#!/usr/bin/env python

##PACKAGES##
from __future__ import division
from builtins import zip
import numpy as np
import matplotlib
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from .._shared.helpers import *
from ..tools.reduce import reduce as reduceD

##MAIN FUNCTION##
def animated_plot(x, *args, **kwargs):
    """
    Implements animated trajectory plot

    INPUTS:
    -numpy array(s)
    -list of numpy arrays

    OUTPUTS:
    -returns fig, ax, data, line_ani. fig, ax and line_ani are matplotlib figure, axis, and animation handles,
    respectively. Data is a numpy array of (reduced) data. (Optional, to use set return_data=True)
    """

    assert x[0].shape[1]>2, "Hypertools currently only supports animation for data with > 2 dims."

    ## HYPERTOOLS-SPECIFIC ARG PARSING ##

    if 'zoom' in kwargs:
        zoom=kwargs['zoom']
        del kwargs['zoom']
    else:
        zoom=0

    if 'chemtrails' in kwargs:
        chemtrails= kwargs['chemtrails']
        del kwargs['chemtrails']
    else:
        chemtrails=False

    if 'rotations' in kwargs:
        rotations=kwargs['rotations']
        del kwargs['rotations']
    else:
        rotations=2

    if 'duration' in kwargs:
        duration=kwargs['duration']
        del kwargs['duration']
    else:
        duration=30

    if 'frame_rate' in kwargs:
        frame_rate=kwargs['frame_rate']
        del kwargs['frame_rate']
    else:
        frame_rate=50

    if 'tail_duration' in kwargs:
        tail_duration=kwargs['tail_duration']
        del kwargs['tail_duration']
    else:
        tail_duration=2

    if 'return_data' in kwargs:
        return_data = kwargs['return_data']
        del kwargs['return_data']
    else:
        return_data=False

    if 'legend' in kwargs:
        legend=True
        legend_data = kwargs['legend']
        del kwargs['legend']
    else:
        legend=False

    if 'color_palette' in kwargs:
        palette = kwargs['color_palette']
        del kwargs['color_palette']

    if 'save_path' in kwargs:
        save=True
        save_path = kwargs['save_path']
        del kwargs['save_path']
    else:
        save=False

    # handle show flag
    if 'show' in kwargs:
        show=kwargs['show']
        del kwargs['show']
        matplotlib.use('Agg')
        import matplotlib.pyplot as plt
    else:
        show=True
        import matplotlib.pyplot as plt

    ##SUB FUNCTIONS##
    def plot_cube(scale, const=1):
        cube = {
            "top"    : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[1,1],[1,1]] ),
            "bottom" : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[-1,-1],[-1,-1]] ),
            "left"   : ( [[-1,-1],[-1,-1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "right"  : ( [[1,1],[1,1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "front"  : ( [[-1,1],[-1,1]], [[-1,-1],[-1,-1]], [[-1,-1],[1,1]] ),
            "back"   : ( [[-1,1],[-1,1]], [[1,1],[1,1]], [[-1,-1],[1,1]] )
            }

        plane_list = []
        for side in cube:
            (Xs, Ys, Zs) = (
                np.asarray(cube[side][0])*scale*const,
                np.asarray(cube[side][1])*scale*const,
                np.asarray(cube[side][2])*scale*const
                )
            plane_list.append(ax.plot_wireframe(Xs, Ys, Zs, rstride=1, cstride=1, color='black', linewidth=2))
        return plane_list

    def update_lines(num, data_lines, lines, trail_lines, cube_scale, tail_duration):

        if hasattr(update_lines, 'planes'):
            for plane in update_lines.planes:
                plane.remove()

        update_lines.planes = plot_cube(cube_scale)
        ax.view_init(elev=10, azim=rotations*(360*(num/data_lines[0].shape[0])))
        ax.dist=8-zoom

        for line, data, trail in zip(lines, data_lines, trail_lines):
            if num<=tail_duration:
                    line.set_data(data[0:num+1, 0:2].T)
                    line.set_3d_properties(data[0:num+1, 2])
            else:
                line.set_data(data[num-tail_duration:num+1, 0:2].T)
                line.set_3d_properties(data[num-tail_duration:num+1, 2])
            if chemtrails:
                trail.set_data(data[0:num + 1, 0:2].T)
                trail.set_3d_properties(data[0:num + 1, 2])
        return lines,trail_lines

    args_list = parse_args(x,args)
    kwargs_list = parse_kwargs(x,kwargs)

    # Attaching 3D axis to the figure
    fig = plt.figure()
    ax = p3.Axes3D(fig)

    if type(x) is not list:
        x = [x]

    interp_val = frame_rate*duration/(x[0].shape[0] - 1)
    x = interp_array_list(x, interp_val=interp_val)
    x = center(x)
    x = scale(x)

    if tail_duration==0:
        tail_duration=1
    else:
        tail_duration = int(frame_rate*tail_duration)

    lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], linewidth=3, *args_list[idx], **kwargs_list[idx])[0] for idx,dat in enumerate(x)]
    trail = [
        ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], alpha=.3, linewidth=3, *args_list[idx], **kwargs_list[idx])[0]
        for idx, dat in enumerate(x)]

    ax.set_axis_off()

    # Get cube scale from data
    cube_scale = 1

    # Setting the axes properties
    ax.set_xlim3d([-cube_scale, cube_scale])
    ax.set_ylim3d([-cube_scale, cube_scale])
    ax.set_zlim3d([-cube_scale, cube_scale])

    #add legend
    if legend:
        proxies = [plt.Rectangle((0, 0), 1, 1, fc=palette[idx]) for idx,label in enumerate(legend_data)]
        ax.legend(proxies,legend_data)

    # Creating the Animation object
    line_ani = animation.FuncAnimation(fig, update_lines, x[0].shape[0], fargs=(x, lines, trail, cube_scale, tail_duration),
                                   interval=1000/frame_rate, blit=False, repeat=False)
    if save:
        Writer = animation.writers['ffmpeg']
        writer = Writer(fps=frame_rate, bitrate=1800)
        line_ani.save(save_path, writer=writer)

    if show:
        plt.show()

    if return_data:
        return fig,ax,x,line_ani
<|endoftext|>