__docformat__ = 'restructuredtext en'

from collections import OrderedDict
import six
from six import string_types
from geoid.util import isimplify
from geoid.civick import GVid
from geoid import parse_to_gvid
from dateutil import parser
from sqlalchemy import event
from sqlalchemy import Column as SAColumn, Integer, UniqueConstraint
from sqlalchemy import String, ForeignKey
from sqlalchemy.orm import relationship, object_session, backref
from ambry.identity import ObjectNumber, PartialPartitionName, PartitionIdentity
from ambry.orm.columnstat import ColumnStat
from ambry.orm.dataset import Dataset
from ambry.util import Constant
import logging
from ambry.util import get_logger

logger = get_logger(__name__)
# logger.setLevel(logging.DEBUG)

from . import Base, MutationDict, MutationList, JSONEncodedObj, BigIntegerType

class PartitionDisplay(object):
    """Helper object to select what to display for titles and descriptions"""

    def __init__(self, p):

        self._p = p

        desc_used = False
        self.title = self._p.title
        self.description = ''

        if not self.title:
            self.title = self._p.table.description
            desc_used = True

        if not self.title:
            self.title = self._p.vname

        if not desc_used:
            self.description = self._p.description.strip('.') + '.' if self._p.description else ''

        self.notes = self._p.notes

    @property
    def geo_description(self):
        """Return a description of the geographic extents, using the largest scale
        space and grain coverages"""

        sc = self._p.space_coverage
        gc = self._p.grain_coverage

        if sc and gc:
            if parse_to_gvid(gc[0]).level == 'state' and parse_to_gvid(sc[0]).level == 'state':
                return parse_to_gvid(sc[0]).geo_name
            else:
                return ("{} in {}".format(
                    parse_to_gvid(gc[0]).level_plural.title(),
                    parse_to_gvid(sc[0]).geo_name))
        elif sc:
            return parse_to_gvid(sc[0]).geo_name.title()
        elif sc:
            return parse_to_gvid(gc[0]).level_plural.title()
        else:
            return ''

    @property
    def time_description(self):
        """String description of the year or year range"""

        tc = [t for t in self._p.time_coverage if t]

        if not tc:
            return ''

        mn = min(tc)
        mx = max(tc)

        if not mn and not mx:
            return ''
        elif mn == mx:
            return mn
        else:
            return "{} to {}".format(mn, mx)

    @property
    def sub_description(self):
        """Time and space dscription"""
        gd = self.geo_description
        td = self.time_description

        if gd and td:
            return '{}, {}. {} Rows.'.format(gd, td, self._p.count)
        elif gd:
            return '{}. {} Rows.'.format(gd, self._p.count)
        elif td:
            return '{}. {} Rows.'.format(td, self._p.count)
        else:
            return '{} Rows.'.format(self._p.count)

class Partition(Base):
    __tablename__ = 'partitions'

    STATES = Constant()
    STATES.SYNCED = 'synced'
    STATES.CLEANING = 'cleaning'
    STATES.CLEANED = 'cleaned'
    STATES.PREPARING = 'preparing'
    STATES.PREPARED = 'prepared'
    STATES.BUILDING = 'building'
    STATES.BUILT = 'built'
    STATES.COALESCING = 'coalescing'
    STATES.COALESCED = 'coalesced'
    STATES.ERROR = 'error'
    STATES.FINALIZING = 'finalizing'
    STATES.FINALIZED = 'finalized'
    STATES.INSTALLING = 'installing'
    STATES.INSTALLED = 'installed'

    TYPE = Constant
    TYPE.SEGMENT = 's'
    TYPE.UNION = 'u'

    sequence_id = SAColumn('p_sequence_id', Integer)
    vid = SAColumn('p_vid', String(16), primary_key=True, nullable=False)
    id = SAColumn('p_id', String(13), nullable=False)
    d_vid = SAColumn('p_d_vid', String(13), ForeignKey('datasets.d_vid'), nullable=False, index=True)
    t_vid = SAColumn('p_t_vid', String(15), ForeignKey('tables.t_vid'), nullable=False, index=True)
    name = SAColumn('p_name', String(200), nullable=False, index=True)
    vname = SAColumn('p_vname', String(200), unique=True, nullable=False, index=True)
    fqname = SAColumn('p_fqname', String(200), unique=True, nullable=False, index=True)

    title = SAColumn('p_title', String())
    description = SAColumn('p_description', String())
    notes = SAColumn('p_notes', String())

    cache_key = SAColumn('p_cache_key', String(200), unique=True, nullable=False, index=True)
    parent_vid = SAColumn('p_p_vid', String(16), ForeignKey('partitions.p_vid'), nullable=True, index=True)
    ref = SAColumn('p_ref', String(16), index=True,
                   doc='VID reference to an eariler version to use instead of this one.')
    type = SAColumn('p_type', String(20), default=TYPE.UNION,
                    doc='u - normal partition, s - segment')
    table_name = SAColumn('p_table_name', String(50))
    time = SAColumn('p_time', String(20))  # FIXME: add helptext
    space = SAColumn('p_space', String(50))
    grain = SAColumn('p_grain', String(50))
    variant = SAColumn('p_variant', String(50))
    format = SAColumn('p_format', String(50))
    segment = SAColumn('p_segment', Integer,
                       doc='Part of a larger partition. segment_id is usually also a source ds_id')
    epsg = SAColumn('p_epsg', Integer, doc='EPSG SRID for the reference system of a geographic dataset. ')

    # The partition could hold data that is considered a dimension -- if multiple datasets
    # were joined, that dimension would be a dimension column, but it only has a single
    # value in each partition.
    # That could be part of the name, or it could be declared in a table, with a single value for all of the
    # rows in a partition.

    min_id = SAColumn('p_min_id', BigIntegerType)
    max_id = SAColumn('p_max_id', BigIntegerType)
    count = SAColumn('p_count', Integer)
    state = SAColumn('p_state', String(50))
    data = SAColumn('p_data', MutationDict.as_mutable(JSONEncodedObj))

    space_coverage = SAColumn('p_scov', MutationList.as_mutable(JSONEncodedObj))
    time_coverage = SAColumn('p_tcov', MutationList.as_mutable(JSONEncodedObj))
    grain_coverage = SAColumn('p_gcov', MutationList.as_mutable(JSONEncodedObj))

    installed = SAColumn('p_installed', String(100))
    _location = SAColumn('p_location', String(100))  # Location of the data file

    __table_args__ = (
        # ForeignKeyConstraint( [d_vid, d_location], ['datasets.d_vid','datasets.d_location']),
        UniqueConstraint('p_sequence_id', 'p_d_vid', name='_uc_partitions_1'),
    )

    # For the primary table for the partition. There is one per partition, but a table
    # can be primary in multiple partitions.
    table = relationship('Table', backref='partitions', foreign_keys='Partition.t_vid')

    stats = relationship(ColumnStat, backref='partition', cascade='all, delete, delete-orphan')

    children = relationship('Partition', backref=backref('parent', remote_side=[vid]), cascade='all')

    _bundle = None  # Set when returned from a bundle.
    _datafile = None  # TODO: Unused variable.
    _datafile_writer = None  # TODO: Unused variable.
    _stats_dict = None

    @property
    def identity(self):
        """Return this partition information as a PartitionId."""

        if self.dataset is None:
            # The relationship will be null until the object is committed
            s = object_session(self)

            ds = s.query(Dataset).filter(Dataset.id_ == self.d_id).one()
        else:
            ds = self.dataset

        d = {
            'id': self.id,
            'vid': self.vid,
            'name': self.name,
            'vname': self.vname,
            'ref': self.ref,
            'space': self.space,
            'time': self.time,
            'table': self.table_name,
            'grain': self.grain,
            'variant': self.variant,
            'segment': self.segment,
            'format': self.format if self.format else 'db'
        }

        return PartitionIdentity.from_dict(dict(list(ds.dict.items()) + list(d.items())))

    @property
    def display(self):
        """Return an acessor object to get display titles and descriptions"""
        return PartitionDisplay(self)

    @property
    def bundle(self):
        return self._bundle  # Set externally, such as Bundle.wrap_partition

    @property
    def is_segment(self):
        return self.type == self.TYPE.SEGMENT

    @property
    def headers(self):
        return [c.name for c in self.table.columns]

    def __repr__(self):
        return '<partition: {} {}>'.format(self.vid, self.vname)

    def set_stats(self, stats):

        self.stats[:] = []  # Delete existing stats

        for c in self.table.columns:

            if c.name not in stats:
                continue

            d = stats[c.name].dict

            del d['name']
            del d['flags']
            cs = ColumnStat(p_vid=self.vid, d_vid=self.d_vid, c_vid=c.vid, **d)
            self.stats.append(cs)

    def parse_gvid_or_place(self, gvid_or_place):
        try:
            return parse_to_gvid(gvid_or_place)
        except KeyError:

            places = list(self._bundle._library.search.search_identifiers(gvid_or_place))

            if not places:
                err_msg = "Failed to find space identifier '{}' in full " \
                          "text identifier search  for partition '{}'" \
                    .format(gvid_or_place, str(self.identity))
                self._bundle.error(err_msg)
                return None

            return parse_to_gvid(places[0].vid)

    def set_coverage(self, stats):
        """"Extract time space and grain coverage from the stats and store them in the partition"""
        from ambry.util.datestimes import expand_to_years

        scov = set()
        tcov = set()
        grains = set()

        def summarize_maybe(gvid):
            try:
                return parse_to_gvid(gvid).summarize()
            except:
                return None

        def simplifiy_maybe(values, column):

            parsed = []

            for gvid in values:
                # The gvid should not be a st
                if gvid is None or gvid == 'None':
                    continue
                try:
                    parsed.append(parse_to_gvid(gvid))
                except ValueError as e:
                    if self._bundle:
                        self._bundle.warn("While analyzing geo coverage in final partition stage, " +
                                           "Failed to parse gvid '{}' in {}.{}: {}"
                                           .format(str(gvid), column.table.name, column.name, e))

            try:
                return isimplify(parsed)
            except:
                return None

        def int_maybe(year):
            try:
                return int(year)
            except:
                return None

        for c in self.table.columns:

            if c.name not in stats:
                continue

            try:
                if stats[c.name].is_gvid or stats[c.name].is_geoid:
                    scov |= set(x for x in simplifiy_maybe(stats[c.name].uniques, c))
                    grains |= set(summarize_maybe(gvid) for gvid in stats[c.name].uniques)

                elif stats[c.name].is_year:
                    tcov |= set(int_maybe(x) for x in stats[c.name].uniques)

                elif stats[c.name].is_date:
                    # The fuzzy=True argument allows ignoring the '-' char in dates produced by .isoformat()
                    try:
                        tcov |= set(parser.parse(x, fuzzy=True).year if isinstance(x, string_types) else x.year for x in
                                    stats[c.name].uniques)
                    except ValueError:
                        pass

            except Exception as e:
                self._bundle.error("Failed to set coverage for column '{}', partition '{}': {}"
                                   .format(c.name, self.identity.vname, e))
                raise

        # Space Coverage

        if 'source_data' in self.data:

            for source_name, source in list(self.data['source_data'].items()):
                scov.add(self.parse_gvid_or_place(source['space']))

        if self.identity.space:  # And from the partition name
            try:
                scov.add(self.parse_gvid_or_place(self.identity.space))
            except ValueError:
                # Couldn't parse the space as a GVid
                pass

        # For geo_coverage, only includes the higher level summary levels, counties, states,
        # places and urban areas.
        self.space_coverage = sorted([str(x) for x in scov if bool(x) and x.sl
                                      in (10, 40, 50, 60, 160, 400)])

        #
        # Time Coverage

        # From the source
        # If there was a time value in the source that this partition was created from, then
        # add it to the years.
        if 'source_data' in self.data:
            for source_name, source in list(self.data['source_data'].items()):
                if 'time' in source:
                    for year in expand_to_years(source['time']):
                        if year:
                            tcov.add(year)

        # From the partition name
        if self.identity.name.time:
            for year in expand_to_years(self.identity.name.time):
                if year:
                    tcov.add(year)

        self.time_coverage = [t for t in tcov if t]

        #
        # Grains

        if 'source_data' in self.data:
            for source_name, source in list(self.data['source_data'].items()):
                if 'grain' in source:
                    grains.add(source['grain'])

        self.grain_coverage = sorted(str(g) for g in grains if g)

    @property
    def dict(self):
        """A dict that holds key/values for all of the properties in the
        object.

        :return:

        """

        d = {p.key: getattr(self, p.key) for p in self.__mapper__.attrs
             if p.key not in ('table', 'dataset', '_codes', 'stats', 'data', 'process_records')}

        if self.data:
            # Copy data fields into top level dict, but don't overwrite existind values.
            for k, v in six.iteritems(self.data):
                if k not in d and k not in ('table', 'stats', '_codes', 'data'):
                    d[k] = v

        return d

    @property
    def detail_dict(self):
        """A more detailed dict that includes the descriptions, sub descriptions, table
        and columns."""

        d = self.dict

        def aug_col(c):
            d = c.dict
            d['stats'] = [s.dict for s in c.stats]
            return d

        d['table'] = self.table.dict
        d['table']['columns'] = [aug_col(c) for c in self.table.columns]

        return d

    @property
    def stats_dict(self):

        class Bunch(object):
            """Dict and object access to properties"""

            def __init__(self, o):
                self.__dict__.update(o)

            def __str__(self):
                return str(self.__dict__)

            def __repr__(self):
                return repr(self.__dict__)

            def keys(self):
                return list(self.__dict__.keys())

            def items(self):
                return list(self.__dict__.items())

            def iteritems(self):
                return iter(self.__dict__.items())

            def __getitem__(self, k):
                if k in self.__dict__:
                    return self.__dict__[k]
                else:
                    from . import ColumnStat
                    return ColumnStat(hist=[])

        if not self._stats_dict:
            cols = {s.column.name: Bunch(s.dict) for s in self.stats}

            self._stats_dict = Bunch(cols)

        return self._stats_dict

    def build_sample(self):

        name = self.table.name

        count = int(
            self.database.connection.execute('SELECT count(*) FROM "{}"'.format(name)).fetchone()[0])

        skip = count / 20

        if count > 100:
            sql = 'SELECT * FROM "{}" WHERE id % {} = 0 LIMIT 20'.format(name, skip)
        else:
            sql = 'SELECT * FROM "{}" LIMIT 20'.format(name)

        sample = []

        for j, row in enumerate(self.database.connection.execute(sql)):
            sample.append(list(row.values()))

        self.record.data['sample'] = sample

        s = self.bundle.database.session
        s.merge(self.record)
        s.commit()

    @property
    def row(self):
        # Use an Ordered Dict to make it friendly to creating CSV files.
        SKIP_KEYS = [
            'sequence_id', 'vid', 'id', 'd_vid', 't_vid', 'min_key', 'max_key',
            'installed', 'ref', 'count', 'state', 'data', 'space_coverage',
            'time_coverage', 'grain_coverage', 'name', 'vname', 'fqname', 'cache_key'
        ]

        d = OrderedDict([('table', self.table.name)] +
                        [(p.key, getattr(self, p.key)) for p in self.__mapper__.attrs
                         if p.key not in SKIP_KEYS])
        return d

    def update(self, **kwargs):

        if 'table' in kwargs:
            del kwargs['table']  # In source_schema.csv, this is the name of the table, not the object

        for k, v in list(kwargs.items()):
            if hasattr(self, k):
                setattr(self, k, v)

    def finalize(self, ps=None):

        self.state = self.STATES.FINALIZING

        # Write the stats for this partition back into the partition

        with self.datafile.writer as w:
            for i, c in enumerate(self.table.columns, 1):
                wc = w.column(i)
                assert wc.pos == c.sequence_id, (c.name, wc.pos, c.sequence_id)
                wc.name = c.name
                wc.description = c.description
                wc.type = c.python_type.__name__
                self.count = w.n_rows
            w.finalize()

        if self.type == self.TYPE.UNION:
            ps.update('Running stats ', state='running')
            stats = self.datafile.run_stats()

            self.set_stats(stats)
            self.set_coverage(stats)

        self._location = 'build'

        self.title = PartitionDisplay(self).title
        self.description = PartitionDisplay(self).description

        self.state = self.STATES.FINALIZED

    # =============
    # These methods are a bit non-cohesive, since they require the _bundle value to be set, which is
    # set externally, when the object is retured from a bundle.

    def clean(self):
        """Remove all built files and return the partition to a newly-created state"""
        if self.datafile:
            self.datafile.remove()

    @property
    def location(self):

        base_location = self._location

        if not base_location:
            return None

        if self._bundle.build_fs.exists(base_location):
            if self._bundle.build_fs.hashsyspath(base_location):
                return self._bundle.build_fs.getsyspath(base_location)

        return base_location

    @location.setter
    def location(self, v):
        self._location = v

    @property
    def datafile(self):
        from ambry.exc import NotFoundError

        if self.is_local:
            # Use the local version, if it exists
            logger.debug('datafile: Using local datafile {}'.format(self.vname))
            return self.local_datafile
        else:
            # If it doesn't try to get the remote.
            try:
                logger.debug('datafile: Using remote datafile {}'.format(self.vname))
                return self.remote_datafile
            except NotFoundError:
                # If the remote doesnt exist, return the local, so the caller can call  exists() on it,
                # get its path, etc.
                return self.local_datafile

    @property
    def local_datafile(self):
        """Return the datafile for this partition, from the build directory, the remote, or the warehouse"""
        from ambry_sources import MPRowsFile
        from fs.errors import ResourceNotFoundError
        from ambry.orm.exc import NotFoundError

        try:
            return MPRowsFile(self._bundle.build_fs, self.cache_key)

        except ResourceNotFoundError:
            raise NotFoundError(
                'Could not locate data file for partition {} (local)'.format(self.identity.fqname))

    @property
    def remote(self):
        """
        Return the remote for this partition

        :return:

        """
        from ambry.exc import NotFoundError

        ds = self.dataset

        if 'remote_name' not in ds.data:
            raise NotFoundError('Could not determine remote for partition: {}'.format(self.identity.fqname))

        return self._bundle.library.remote(ds.data['remote_name'])

    @property
    def remote_datafile(self):

        from fs.errors import ResourceNotFoundError
        from ambry.exc import AccessError, NotFoundError
        from boto.exception import S3ResponseError

        try:

            from ambry_sources import MPRowsFile

            remote = self.remote

            datafile = MPRowsFile(remote.fs, self.cache_key)

            if not datafile.exists:
                raise NotFoundError(
                    'Could not locate data file for partition {} from remote {} : file does not exist'
                        .format(self.identity.fqname, remote))

        except ResourceNotFoundError as e:
            raise NotFoundError('Could not locate data file for partition {} (remote): {}'
                                .format(self.identity.fqname, e))
        except S3ResponseError as e:
            # HACK. It looks like we get the response error with an access problem when
            # we have access to S3, but the file doesn't exist.
            raise NotFoundError("Can't access MPR file for {} in remote {}".format(self.cache_key, remote.fs))

        return datafile

    @property
    def is_local(self):
        """Return true is the partition file is local"""
        from ambry.orm.exc import NotFoundError
        try:
            if self.local_datafile.exists:
                return True
        except NotFoundError:
            pass

        return False

    def localize(self, ps=None):
        """Copy a non-local partition file to the local build directory"""
        from filelock import FileLock
        from ambry.util import ensure_dir_exists
        from ambry_sources import MPRowsFile
        from fs.errors import ResourceNotFoundError

        if self.is_local:
            return

        local = self._bundle.build_fs

        b = self._bundle.library.bundle(self.identity.as_dataset().vid)

        remote = self._bundle.library.remote(b)

        lock_path = local.getsyspath(self.cache_key + '.lock')

        ensure_dir_exists(lock_path)

        lock = FileLock(lock_path)

        if ps:
            ps.add_update(message='Localizing {}'.format(self.identity.name),
                          partition=self,
                          item_type='bytes',
                          state='downloading')

        if ps:
            def progress(bts):
                if ps.rec.item_total is None:
                    ps.rec.item_count = 0

                if not ps.rec.data:
                    ps.rec.data = {}  # Should not need to do this.
                    return self

                item_count = ps.rec.item_count + bts
                ps.rec.data['updates'] = ps.rec.data.get('updates', 0) + 1

                if ps.rec.data['updates'] % 32 == 1:
                    ps.update(message='Localizing {}'.format(self.identity.name),
                              item_count=item_count)
        else:
            from ambry.bundle.process import call_interval
            @call_interval(5)
            def progress(bts):
                self._bundle.log("Localizing {}. {} bytes downloaded".format(self.vname, bts))

        def exception_cb(e):
            raise e

        with lock:
            # FIXME! This won't work with remote ( http) API, only FS ( s3:, file:)

            if self.is_local:
                return self

            try:
                with remote.fs.open(self.cache_key + MPRowsFile.EXTENSION, 'rb') as f:
                    event = local.setcontents_async(self.cache_key + MPRowsFile.EXTENSION,
                                                    f,
                                                    progress_callback=progress,
                                                    error_callback=exception_cb)
                    event.wait()
                    if ps:
                        ps.update_done()
            except ResourceNotFoundError as e:
                from ambry.orm.exc import NotFoundError
                raise NotFoundError("Failed to get MPRfile '{}' from {}: {} "
                                    .format(self.cache_key, remote.fs, e))

        return self

    @property
    def reader(self):
        from ambry.orm.exc import NotFoundError
        from fs.errors import ResourceNotFoundError
        """The reader for the datafile"""

        try:
            return self.datafile.reader
        except ResourceNotFoundError:
            raise NotFoundError("Failed to find partition file, '{}' "
                                .format(self.datafile.path))

    def select(self, predicate=None, headers=None):
        """
        Select rows from the reader using a predicate to select rows and and itemgetter to return a
        subset of elements
        :param predicate: If defined, a callable that is called for each row, and if it returns true, the
        row is included in the output.
        :param headers: If defined, a list or tuple of header names to return from each row
        :return: iterable of results

        WARNING: This routine works from the reader iterator, which returns RowProxy objects. RowProxy objects
        are reused, so if you construct a list directly from the output from this method, the list will have
        multiple copies of a single RowProxy, which will have as an inner row the last result row. If you will
        be directly constructing a list, use a getter that extracts the inner row, or which converts the RowProxy
        to a dict:

            list(s.datafile.select(lambda r: r.stusab == 'CA', lambda r: r.dict ))

        """

        # FIXME; in Python 3, use yield from
        with self.reader as r:
            for row in r.select(predicate, headers):
                yield row

    def __iter__(self):
        """ Iterator over the partition, returning RowProxy objects.
        :return: a generator
        """

        with self.reader as r:
            for row in r:
                yield row

    @property
    def analysis(self):
        """Return an AnalysisPartition proxy, which wraps this partition to provide acess to
        dataframes, shapely shapes and other analysis services"""
        if isinstance(self, PartitionProxy):
            return AnalysisPartition(self._obj)
        else:
            return AnalysisPartition(self)

    @property
    def measuredim(self):
        """Return a MeasureDimension proxy, which wraps the partition to provide access to
        columns in terms of measures and dimensions"""

        if isinstance(self, PartitionProxy):
            return MeasureDimensionPartition(self._obj)
        else:
            return MeasureDimensionPartition(self)

    # ============================

    def update_id(self, sequence_id=None):
        """Alter the sequence id, and all of the names and ids derived from it. This
        often needs to be done after an IntegrityError in a multiprocessing run"""

        if sequence_id:
            self.sequence_id = sequence_id

        self._set_ids(force=True)

        if self.dataset:
            self._update_names()

    def _set_ids(self, force=False):

        if not self.sequence_id:
            from .exc import DatabaseError

            raise DatabaseError('Sequence ID must be set before insertion')

        if not self.vid or force:
            assert bool(self.d_vid)
            assert bool(self.sequence_id)
            don = ObjectNumber.parse(self.d_vid)
            assert don.revision
            on = don.as_partition(self.sequence_id)
            self.vid = str(on.rev(don.revision))
            self.id = str(on.rev(None))

        if not self.data:
            self.data = {}

    def _update_names(self):
        """Update the derived names"""

        d = dict(
            table=self.table_name,
            time=self.time,
            space=self.space,
            grain=self.grain,
            variant=self.variant,
            segment=self.segment
        )

        assert self.dataset

        name = PartialPartitionName(**d).promote(self.dataset.identity.name)

        self.name = str(name.name)
        self.vname = str(name.vname)
        self.cache_key = name.cache_key
        self.fqname = str(self.identity.fqname)

    @staticmethod
    def before_insert(mapper, conn, target):
        """event.listen method for Sqlalchemy to set the sequence for this
        object and create an ObjectNumber value for the id_"""

        target._set_ids()

        if target.name and target.vname and target.cache_key and target.fqname and not target.dataset:
            return

        Partition.before_update(mapper, conn, target)

    @staticmethod
    def before_update(mapper, conn, target):
        target._update_names()

    @staticmethod
    def before_delete(mapper, conn, target):
        pass

event.listen(Partition, 'before_insert', Partition.before_insert)
event.listen(Partition, 'before_update', Partition.before_update)
event.listen(Partition, 'before_delete', Partition.before_delete)

class PartitionProxy(object):
    __slots__ = ["_obj", "__weakref__"]

    def __init__(self, obj):
        object.__setattr__(self, "_obj", obj)

    #
    # proxying (special cases)
    #
    def __getattr__(self, name):
        return getattr(object.__getattribute__(self, "_obj"), name)

    def __delattr__(self, name):
        delattr(object.__getattribute__(self, "_obj"), name)

    def __setattr__(self, name, value):
        setattr(object.__getattribute__(self, "_obj"), name, value)

    def __nonzero__(self):
        return bool(object.__getattribute__(self, "_obj"))

    def __str__(self):
        return "<{}: {}>".format(type(self), str(object.__getattribute__(self, "_obj")))

    def __repr__(self):
        return "<{}: {}>".format(type(self), repr(object.__getattribute__(self, "_obj")))

    def __iter__(self):
        return iter(object.__getattribute__(self, "_obj"))

class AnalysisPartition(PartitionProxy):
    """A subclass of Partition with methods designed for analysis with Pandas. It is produced from
    the partitions analysis property"""

    def dataframe(self, predicate=None, filtered_columns=None, columns=None, df_class=None):
        """Return the partition as a Pandas dataframe

        :param predicate: If defined, a callable that is called for each row, and if it returns true, the
        row is included in the output.
        :param filtered_columns: If defined, the value is a dict of column names and
        associated values. Only rows where all of the named columms have the given values will be returned.
        Setting the argument will overwrite any value set for the predicate
        :param columns: A list or tuple of column names to return

        :return: Pandas dataframe

        """

        from operator import itemgetter
        from ambry.pands import AmbryDataFrame

        df_class = df_class or AmbryDataFrame

        if columns:
            ig = itemgetter(*columns)
        else:
            ig = None
            columns = self.table.header

        if filtered_columns:

            def maybe_quote(v):
                from six import string_types
                if isinstance(v, string_types):
                    return '"{}"'.format(v)
                else:
                    return v

            code = ' and '.join("row.{} == {}".format(k, maybe_quote(v))
                                for k, v in filtered_columns.items())

            predicate = eval('lambda row: {}'.format(code))

        if predicate:
            def yielder():
                for row in self.reader:
                    if predicate(row):
                        if ig:
                            yield ig(row)
                        else:
                            yield row.dict

            df = df_class(yielder(), columns=columns, partition=self.measuredim)

            return df

        else:

            def yielder():
                for row in self.reader:
                    yield row.values()

            # Put column names in header order
            columns = [c for c in self.table.header if c in columns]

            return df_class(yielder(), columns=columns, partition=self.measuredim)

    def geoframe(self, simplify=None, predicate=None, crs=None, epsg=None):
        """
        Return geopandas dataframe

        :param simplify: Integer or None. Simplify the geometry to a tolerance, in the units of the geometry.
        :param predicate: A single-argument function to select which records to include in the output.
        :param crs: Coordinate reference system information
        :param epsg: Specifiy the CRS as an EPGS number.
        :return: A Geopandas GeoDataFrame
        """
        import geopandas
        from shapely.wkt import loads
        from fiona.crs import from_epsg

        if crs is None and epsg is None and self.epsg is not None:
            epsg = self.epsg

        if crs is None:
            try:
                crs = from_epsg(epsg)
            except TypeError:
                raise TypeError('Must set either crs or epsg for output.')

        df = self.dataframe(predicate=predicate)
        geometry = df['geometry']

        if simplify:
            s = geometry.apply(lambda x: loads(x).simplify(simplify))
        else:
            s = geometry.apply(lambda x: loads(x))

        df['geometry'] = geopandas.GeoSeries(s)

        return geopandas.GeoDataFrame(df, crs=crs, geometry='geometry')

    def shapes(self, simplify=None, predicate=None):
        """
        Return geodata as a list of Shapely shapes

        :param simplify: Integer or None. Simplify the geometry to a tolerance, in the units of the geometry.
        :param predicate: A single-argument function to select which records to include in the output.

        :return: A list of Shapely objects
        """

        from shapely.wkt import loads

        if not predicate:
            predicate = lambda row: True

        if simplify:
            return [loads(row.geometry).simplify(simplify) for row in self if predicate(row)]
        else:
            return [loads(row.geometry) for row in self if predicate(row)]

    def patches(self, basemap, simplify=None, predicate=None, args_f=None, **kwargs):
        """
        Return geodata as a list of Matplotlib patches

        :param basemap: A mpl_toolkits.basemap.Basemap
        :param simplify: Integer or None. Simplify the geometry to a tolerance, in the units of the geometry.
        :param predicate: A single-argument function to select which records to include in the output.
        :param args_f: A function that takes a row and returns a dict of additional args for the Patch constructor

        :param kwargs: Additional args to be passed to the descartes Path constructor
        :return: A list of patch objects
        """
        from descartes import PolygonPatch
        from shapely.wkt import loads
        from shapely.ops import transform

        if not predicate:
            predicate = lambda row: True

        def map_xform(x, y, z=None):
            return basemap(x, y)

        def make_patch(shape, row):

            args = dict(kwargs.items())

            if args_f:
                args.update(args_f(row))

            return PolygonPatch(transform(map_xform, shape), **args)

        def yield_patches(row):

            if simplify:
                shape = loads(row.geometry).simplify(simplify)
            else:
                shape = loads(row.geometry)

            if shape.geom_type == 'MultiPolygon':
                for subshape in shape.geoms:
                    yield make_patch(subshape, row)
            else:
                yield make_patch(shape, row)

        return [patch for row in self if predicate(row)
                for patch in yield_patches(row)]

class MeasureDimensionPartition(PartitionProxy):
    """A partition proxy for accessing measure and dimensions. When returning a column, it returns
    a PartitionColumn, which proxies the table column while adding partition specific functions. """

    def __init__(self, obj):

        super(MeasureDimensionPartition, self).__init__(obj)

        self.filters = {}

    def column(self, c_name):

        return PartitionColumn(self.table.column(c_name), self)

    @property
    def columns(self):
        """Iterate over all columns"""

        return [PartitionColumn(c, self) for c in self.table.columns]

    @property
    def primary_columns(self):
        """Iterate over the primary columns, columns which do not have a parent"""

        return [c for c in self.columns if not c.parent]

    @property
    def dimensions(self):
        """Iterate over all dimensions"""
        from ambry.valuetype.core import ROLE

        return [c for c in self.columns if c.role == ROLE.DIMENSION]

    @property
    def primary_dimensions(self):
        """Iterate over the primary columns, columns which do not have a parent and have a
        cardinality greater than 1"""
        from ambry.valuetype.core import ROLE

        return [c for c in self.columns
                if not c.parent and c.role == ROLE.DIMENSION and c.pstats.nuniques > 1]

    @property
    def measures(self):
        """Iterate over all measures"""
        from ambry.valuetype.core import ROLE

        return [c for c in self.columns if c.role == ROLE.MEASURE]

    def measure(self, vid):
        """Return a measure, given its vid or another reference"""

        from ambry.orm import Column

        if isinstance(vid, PartitionColumn):
            return vid
        elif isinstance(vid, Column):
            return PartitionColumn(vid)
        else:
            return PartitionColumn(self.table.column(vid), self)

    def dimension(self, vid):
        """Return a dimention, given its vid or another reference"""
        from ambry.orm import Column

        if isinstance(vid, PartitionColumn):
            return vid
        elif isinstance(vid, Column):
            return PartitionColumn(vid)
        else:
            return PartitionColumn(self.table.column(vid), self)

    @property
    def primary_measures(self):
        """Iterate over the primary measures, columns which do not have a parent"""

        return [c for c in self.measures if not c.parent]

    @property
    def dict(self):

        d = self.detail_dict

        d['dimension_sets'] = self.enumerate_dimension_sets()

        return d

    def dataframe(self, measure, p_dim, s_dim=None, filters={}, df_class=None):
        """
        Return a dataframe with a sumse of the columns of the partition, including a measure and one
        or two dimensions. FOr dimensions that have labels, the labels are included

        The returned dataframe will have extra properties to describe the conversion:

        * plot_axes: List of dimension names for the first and second axis
        * labels: THe names of the label columns for the axes
        * filtered: The `filters` dict
        * floating: The names of primary dimensions that are not axes nor filtered

        THere is also an iterator, `rows`, which returns the header and then all of the rows.

        :param measure: The column names of one or more measures
        :param p_dim: The primary dimension. This will be the index of the dataframe.
        :param s_dim: a secondary dimension. The returned frame will be unstacked on this dimension
        :param filters: A dict of column names, mapped to a column value, indicating rows to select. a
        row that passes the filter must have the values for all given rows; the entries are ANDED
        :param df_class:
        :return: a Dataframe, with extra properties
        """

        import numpy as np

        measure = self.measure(measure)

        p_dim = self.dimension(p_dim)

        assert p_dim

        if s_dim:
            s_dim = self.dimension(s_dim)

        columns = set([measure.name, p_dim.name])

        if p_dim.label:

            # For geographic datasets, also need the gvid
            if p_dim.geoid:
                columns.add(p_dim.geoid.name)

            columns.add(p_dim.label.name)

        if s_dim:

            columns.add(s_dim.name)

            if s_dim.label:
                columns.add(s_dim.label.name)

        def maybe_quote(v):
            from six import string_types
            if isinstance(v, string_types):
                return '"{}"'.format(v)
            else:
                return v

        # Create the predicate to filter out the filtered dimensions
        if filters:

            selected_filters = []

            for k, v in filters.items():
                if isinstance(v, dict):
                    # The filter is actually the whole set of possible options, so
                    # just select the first one
                    v = v.keys()[0]

                selected_filters.append("row.{} == {}".format(k, maybe_quote(v)))

            code = ' and '.join(selected_filters)

            predicate = eval('lambda row: {}'.format(code))
        else:
            code = None

            def predicate(row):
                return True

        df = self.analysis.dataframe(predicate, columns=columns, df_class=df_class)

        if df is None or df.empty or len(df) == 0:
            return None

        # So we can track how many records were aggregated into each output row
        df['_count'] = 1

        def aggregate_string(x):
            return ', '.join(set(str(e) for e in x))

        agg = {
            '_count': 'count',

        }

        for col_name in columns:
            c = self.column(col_name)

            # The primary and secondary dimensions are put into the index by groupby
            if c.name == p_dim.name or (s_dim and c.name == s_dim.name):
                continue

            # FIXME! This will only work if the child is only level from the parent. Should
            # have an acessor for the top level.
            if c.parent and (c.parent == p_dim.name or (s_dim and c.parent == s_dim.name)):
                continue

            if c.is_measure:
                agg[c.name] = np.mean

            if c.is_dimension:
                agg[c.name] = aggregate_string

        plot_axes = [p_dim.name]

        if s_dim:
            plot_axes.append(s_dim.name)

        df = df.groupby(list(columns - set([measure.name]))).agg(agg).reset_index()

        df._metadata = ['plot_axes', 'filtered', 'floating', 'labels', 'dimension_set', 'measure']

        df.plot_axes = [c for c in plot_axes]
        df.filtered = filters

        # Dimensions that are not specified as axes nor filtered
        df.floating = list(set(c.name for c in self.primary_dimensions) -
                           set(df.filtered.keys()) -
                           set(df.plot_axes))

        df.labels = [self.column(c).label.name if self.column(c).label else c for c in df.plot_axes]

        df.dimension_set = self.dimension_set(p_dim, s_dim=s_dim)

        df.measure = measure.name

        def rows(self):
            yield ['id'] + list(df.columns)

            for t in df.itertuples():
                yield list(t)

        # Really should not do this, but I don't want to re-build the dataframe with another
        # class
        df.__class__.rows = property(rows)

        return df

    def dimension_set(self, p_dim, s_dim=None, dimensions=None, extant=set()):
        """
        Return a dict that describes the combination of one or two dimensions, for a plot

        :param p_dim:
        :param s_dim:
        :param dimensions:
        :param extant:
        :return:
        """

        if not dimensions:
            dimensions = self.primary_dimensions

        key = p_dim.name

        if s_dim:
            key += '/' + s_dim.name

        # Ignore if the key already exists or the primary and secondary dims are the same
        if key in extant or p_dim == s_dim:
            return

        # Don't allow geography to be a secondary dimension. It must either be a primary dimension
        # ( to make a map ) or a filter, or a small-multiple
        if s_dim and s_dim.valuetype_class.is_geo():
            return

        extant.add(key)

        filtered = {}

        for d in dimensions:
            if d != p_dim and d != s_dim:
                filtered[d.name] = d.pstats.uvalues.keys()

        if p_dim.valuetype_class.is_time():
            value_type = 'time'
            chart_type = 'line'
        elif p_dim.valuetype_class.is_geo():
            value_type = 'geo'
            chart_type = 'map'
        else:
            value_type = 'general'
            chart_type = 'bar'

        return dict(
            key=key,
            p_dim=p_dim.name,
            p_dim_type=value_type,
            p_label=p_dim.label_or_self.name,
            s_dim=s_dim.name if s_dim else None,
            s_label=s_dim.label_or_self.name if s_dim else None,
            filters=filtered,
            chart_type=chart_type
        )

    def enumerate_dimension_sets(self):

        dimension_sets = {}

        dimensions = self.primary_dimensions

        extant = set()

        for d1 in dimensions:

            ds = self.dimension_set(d1, None, dimensions, extant)

            if ds:
                dimension_sets[ds['key']] = ds

        for d1 in dimensions:
            for d2 in dimensions:

                if d2.cardinality >= d1.cardinality:
                    d1, d2 = d2, d1

                ds = self.dimension_set(d1, d2, dimensions, extant)

                if ds:
                    dimension_sets[ds['key']] = ds

        return dimension_sets

class ColumnProxy(PartitionProxy):
    def __init__(self, obj, partition):
        object.__setattr__(self, "_obj", obj)
        object.__setattr__(self, "_partition", partition)

MAX_LABELS = 75  # Maximum number of uniques records before it's assume that the values aren't valid labels

class PartitionColumn(ColumnProxy):
    """A proxy on the Column that links a Column to a Partition, for direct access to the stats
    and column labels"""

    def __init__(self, obj, partition):
        super(PartitionColumn, self).__init__(obj, partition)
        object.__setattr__(self, "pstats", partition.stats_dict[obj.name])

    @property
    def children(self):
        """"Return the table's other column that have this column as a parent, excluding labels"""
        for child in self.children:
            yield PartitionColumn(child, self._partition)

    @property
    def label(self):
        """"Return first child that of the column that is marked as a label"""
        for c in self.table.columns:
            if c.parent == self.name and 'label' in c.valuetype:
                return PartitionColumn(c, self._partition)

    @property
    def value_labels(self):
        """Return a map of column code values mapped to labels, for columns that have a label column

        If the column is not assocaited with a label column, it returns an identity map.

        WARNING! This reads the whole partition, so it is really slow

        """

        from operator import itemgetter

        card = self.pstats.nuniques

        if self.label:
            ig = itemgetter(self.name, self.label.name)
        elif self.pstats.nuniques < MAX_LABELS:
            ig = itemgetter(self.name, self.name)
        else:
            return {}

        label_set = set()
        for row in self._partition:
            label_set.add(ig(row))

            if len(label_set) >= card:
                break

        d = dict(label_set)

        assert len(d) == len(label_set)  # Else the label set has multiple values per key

        return d

    @property
    def cardinality(self):
        """Returns the bymber of unique elements"""

        return self.pstats.nuniques

    def __repr__(self):
        return "<{} {}>".format(self.__class__.__name__, self.name)


# from multiprocessing import Process, Queue
from queue import Queue
import threading
from crawler.reviewCrawler import ReviewCrawler
from crawler.userCrawler import UserCrawler
import json
from GameListCrawler import getGameList

import time
from utils.redisUtis import RedisUtil
from utils.sqlUtils import dbconnector
from gameCrawler import GameCrawler
import requests
import properties

game_queue = Queue()
user_queue = Queue()
review_queue = Queue()

def game_consumer(game_queue,user_queue,review_queue):
    while True:
        game_info_str = game_queue.get(block=True)
        try:
            game_info = json.loads(game_info_str)
            game_helper(game_queue = game_queue,user_queue = user_queue,review_queue = review_queue,id = game_info['id'], url = game_info['url'])
        except Exception as e:
            print("game_consumer_error:",game_info_str)
        time.sleep(1)

def game_helper(game_queue,user_queue,review_queue,id, url):
    # crawler review
    review_queue.put(id)
    redisUtil = RedisUtil()
    if redisUtil.checkGameExist(id):
        print("exist game"+str(id))
        return
    gameCrawler = GameCrawler()
    gameCrawler.infoSave(id,url)
    redisUtil.setGameExist(id)

def review_consumer(game_queue,user_queue,review_queue):
    while True:
        appid = review_queue.get(block=True)
        try:
            review_helper(game_queue = game_queue,user_queue = user_queue,review_queue = review_queue,appid = appid)
        except Exception as e:
            print("review_consumer_error:",appid)
        time.sleep(1)

def review_helper(game_queue,user_queue,review_queue,appid):
    rc = ReviewCrawler(appid)
    reviews = rc.requestReview()
    rc.saveReview()
    for review in reviews:
        steamid = review['steamid']
        user_queue.put(steamid)

def user_consumer(game_queue,user_queue,review_queue):
    while True:
        steamid = user_queue.get(block=True)
        try:
            user_helper(game_queue = game_queue,user_queue = user_queue,review_queue = review_queue, steamid = steamid)
        except Exception as e:
            print("user_consumer_error:",steamid)
        time.sleep(1)

def user_helper(game_queue,user_queue,review_queue,steamid):
    uc = UserCrawler(steamid)
    friendList = uc.requestFriendList()
    uc.saveFriendList()
    if friendList != None:
        for friend in friendList:
            user_queue.put(friend['steamid'])
    ownedGameList = uc.requestOwnedGames()
    uc.saveOwnedGames()
    # put game task
    if ownedGameList != None:
        for game in ownedGameList:
            url = "https://store.steampowered.com/app/" + str(game['appid'])
            try:
                response = requests.get(url, headers=properties.headers, timeout=10)
            except Exception as e:
                print("add owned game to gamelist error: no response and",e)
            game_queue.put(json.dumps({"id": game['appid'], "url": url}))

def provider(game_queue):
    sql = dbconnector()
    start_games =[{"id":"10","url":"https://store.steampowered.com/app/10/CounterStrike/"},{"id":"20","url":"https://store.steampowered.com/app/20/Team_Fortress_Classic/"}]
    for item in start_games:
        game_info_str = json.dumps(item)
        game_queue.put(game_info_str)

if __name__ == '__main__':

    # redisUtil = RedisUtil()
    game_consumer_num = 5
    review_consumer_num = 5
    user_consumer_num = 5

    game_consumer_list = []
    review_consumer_list = []
    user_consumer_list = []

    game_list_provider_threading = threading.Thread(target=getGameList, args=(game_queue,))
    game_list_provider_threading.start()

    print("start allocating threading")
    for i in range(game_consumer_num):
        game_consumer_process = threading.Thread(target=game_consumer, args=(game_queue, user_queue, review_queue,))
        game_consumer_list.append(game_consumer_process)
        game_consumer_process.start()
    for i in range(user_consumer_num):
        user_consumer_process = threading.Thread(target=user_consumer, args=(game_queue, user_queue, review_queue,))
        user_consumer_list.append(user_consumer_process)
        user_consumer_process.start()
    for i in range(review_consumer_num):
        reveiw_consumer_process = threading.Thread(target=review_consumer, args=(game_queue, user_queue, review_queue,))
        review_consumer_list.append(reveiw_consumer_process)
        reveiw_consumer_process.start()


def reverse(L, a):
	n = len(L)
	if a < n//2:
		L[a], L[-1-a] = L[-1-a], L[a]
		reverse(L, a+1)

L = list(input())  # 문자열을 입력받아 리스트로 변환
reverse(L, 0)
print(''.join(str(x) for x in L))

#재귀적으로 리스트 뒤집기를 한다면 양끝단 -> 그다음 -> 그다음 -> ... -> 가운데 순으로
#reverse를 호출하고, 결과값은 역순으로 나온다.
#재귀적 리스트 뒤집기의 바닥 조건은 a < n//2 이다. a가 n//2보다 크거나 같아지면 더 이상 reverse가 호출되지 않는다.


num = int(input())

odd_sum = 0
max_odd = -999999999999999
min_odd = 999999999999999

even_sum = 0
max_even = -999999999999999
min_even = 999999999999999

for i in range(1, num + 1):
    in_num = float(input())
    if i % 2 != 0:
        odd_sum += in_num
        if in_num > max_odd:
            max_odd = in_num
        if in_num < min_odd:
            min_odd = in_num
    elif i % 2 == 0:
        even_sum += in_num
        if in_num > max_even:
            max_even = in_num
        if in_num < min_even:
            min_even = in_num

print(f"OddSum={odd_sum:.2f},")
if min_odd == 999999999999999:
    print("OddMin=No,")
else:
    print(f"OddMin={min_odd:.2f},")
if max_odd == -999999999999999:
    print("OddMax=No,")
else:
    print(f"OddMax={max_odd:.2f},")

print(f"EvenSum={even_sum:.2f},")
if min_even == 999999999999999:
    print("EvenMin=No,")
else:
    print(f"EvenMin={min_even:.2f},")
if max_even == -999999999999999:
    print("EvenMax=No")
else:
    print(f"EvenMax={max_even:.2f}")

SoftUni Lessons/Python Development/Python Basics April 2019/Lessons and Problems/11 - For Loop Exercise/03. Odd Even Position .py


import os
import sqlite3
from datetime import datetime, timedelta

import telebot

bot = telebot.TeleBot(os.getenv("BOT_TOKEN"))
memes_chat_id = int(os.getenv("MEMES_CHAT_ID"))
flood_thread_id = int(os.getenv("FLOOD_THREAD_ID", 1))
memes_thread_id = int(os.getenv("MEMES_THREAD_ID", 1))

conn = sqlite3.connect("memes.db", check_same_thread=False)

def main():
    seven_days_ago = datetime.now() - timedelta(days=7)
    query = "SELECT user_id, MAX(username), count(*) FROM memes_posts_v2 WHERE created_at > ? GROUP BY user_id ORDER BY 3 DESC, 3 DESC LIMIT 3"
    rows = conn.execute(query, (seven_days_ago,)).fetchall()
    msg = ["Количество сброшенных мемов\n"]
    stack = ["🥉", "🥈", "🥇"]
    for row in rows:
        user_id, username, memes_count = row
        message = "[{username}](tg://user?id={user_id}) {memes_count} - {medal}".format(
            username=username,
            user_id=user_id,
            memes_count=memes_count,
            medal=stack.pop(),
        )
        msg.append(message)
    bot.send_message(
        memes_chat_id,
        "\n".join(msg),
        message_thread_id=flood_thread_id,
        parse_mode="Markdown",
    )

if __name__ == "__main__":
    main()


# is unique:  Implement an algorithm to determine
# if a string has all unique characters. What if you
# cannot use additional data structures?

# since we check if characters in a string are not duplicated
# we can use a boolean hash map to check if that character
# already exists

def is_unique(string):
    # ASCII -> we have base case: string length > 128 => return false
    if len(string) > 128:
        return False

    # else, we first initialize a hash_map
    # -> space complexity: O(N)
    # then go through the string
    string_hash_map = {}

    # go through the string
    for c in string:
        # check if c is already in hash map
        if c in string_hash_map:
            return False
        else:
            string_hash_map[c] = True

    return True

# Time Complexity: O(N) - N: length of string
# Space Complexity: O(128) - O(1)
print(is_unique('abn'))

# ------------- Better solution with O(1) space complexity -------------
# ------------ Hint: Bit Manipulation --------------


def based(n, b, k):
    a = [0] * k
    if n < 0:
        return 0
    if b <= 1:
        return 1
    x = n
    counter = 0
    while n >= b:
        q = n / b
        t = n - q * b
        a[counter] = t
        n = q
        counter += 1
    a[counter] = n
    final_num = ""
    for i in range(counter):
        h = a[counter-i]
        final_num += str(h)
        #print(h)
    #print(a[0])
    #print(b, x)
    #return(counter+1)
    return final_num + str(a[0])
"""
def to_decimal(number, base):
    return sum([int(character) * base ** index for index,character in enumerate(str(number)[::-1])])
"""
def answer(n,b):
    if n == 0 or n == 1:
        return 1
    if int(max(n)) > (b-1):
        return 0
    k = len(n)
    print("N is: " + n)
    print("B is: " + str(b))
    print("K: " + str(k))
    counter = 0
    container = []
    print("Started compution")
    while counter < 50:
        new_n = [int(i) for i in n]
        x = list(new_n)
        y = list(new_n)
        x.sort(reverse=True)
        y.sort()
        x = ''.join(str(e) for e in x)
        y = ''.join(str(e) for e in y)
        print(x,y)
        x = int(str(x),b)
        y = int(str(y),b)
        print("X | Y")
        print(x,y)
        z = x - y
        z = int(str(z), 10)
        print("Z: " + str(z))
        z = based(z,b, k)
        if len(str(z)) == k:
            n = str(z)
        else:
            z = list(str(z))
            while len(z) < k:
                z.insert(0,0)
            z = ''.join(str(e) for e in z)
            n = str(z)
        n = str(z)
        print(n)
        counter += 1
        if n not in container:
            container.append(str(n))
        else:
            last_num = container.index(str(n))
            return len(container) - last_num
        print("Counter: " + str(counter))
    print(container)
    #return len(set(container))

print(answer("6050", 3))


from abc import ABCMeta
from json import dumps
from logging import getLogger

from uchicagoldrtoolsuite import log_aware
from ..materialsuite import MaterialSuite

__author__ = "Brian Balsamo, Tyler Danstrom"
__email__ = "balsamo@uchicago.edu, tdanstrom@uchicago.edu"
__company__ = "The University of Chicago Library"
__copyright__ = "Copyright University of Chicago, 2016"
__publication__ = ""
__version__ = "0.0.1dev"

log = getLogger(__name__)

class AccessionContainer(metaclass=ABCMeta):
    """
    A Stage is a structure which holds an aggregates contents
    as they are being processed for ingestion into long term storage
    """
    @log_aware(log)
    def __init__(self, identifier):
        """
        Creates a new Stage

        __Args__

        param1 (str): The identifier that will be assigned to the Stage
        """
        log.debug("Entering ABC init")
        self._identifier = None
        self._materialsuite_list = []
        self._accessionrecord = []
        self._adminnote = []
        self._legalnote = []
        self.set_identifier(identifier)
        log.debug("Exiting ABC init")

    @log_aware(log)
    def __repr__(self):
        attr_dict = {
            'identifier': self.identifier,
            'materialsuite_list': [str(x) for x in self.materialsuite_list],
            'accessionrecord_list': [str(x) for x in self.accessionrecord_list],
            'adminnote_list': [str(x) for x in self.adminnote_list],
            'legalnote_list': [str(x) for x in self.legalnote_list]
        }
        return "<{} {}>".format(str(type(self)),
                                dumps(attr_dict, sort_keys=True))

    @log_aware(log)
    def get_identifier(self):
        return self._identifier

    @log_aware(log)
    def set_identifier(self, identifier):
        log.debug("{}({}) identifier being set to {}".format(
            str(type(self)),
            str(self.identifier), identifier)
        )
        self._identifier = identifier
        log.debug(
            "{} identifier set to {}".format(str(type(self)), identifier)
        )

    @log_aware(log)
    def get_materialsuite_list(self):
        return self._materialsuite_list

    @log_aware(log)
    def set_materialsuite_list(self, x):
        self.del_materialsuite_list()
        for y in x:
            self.add_materialsuite(y)

    @log_aware(log)
    def del_materialsuite_list(self):
        while self.materialsuite_list:
            self.pop_materialsuite()

    @log_aware(log)
    def add_materialsuite(self, x):
        if not isinstance(x, MaterialSuite):
            raise ValueError()
        self._materialsuite_list.append(x)

    @log_aware(log)
    def get_materialsuite(self, index):
        return self.materialsuite_list[index]

    @log_aware(log)
    def pop_materialsuite(self, index=None):
        if index is None:
            self.materialsuite_list.pop()
        else:
            self.materialsuite_list.pop(index)

    @log_aware(log)
    def get_accessionrecord_list(self):
        return self._accessionrecord

    @log_aware(log)
    def set_accessionrecord_list(self, acc_rec_list):
        self.del_accessionrecord_list()
        for x in acc_rec_list:
            self.add_accessionrecord(x)

    @log_aware(log)
    def del_accessionrecord_list(self):
        while self.get_accessionrecord_list():
            self.pop_accessionrecord()

    @log_aware(log)
    def add_accessionrecord(self, accrec):
        self._accessionrecord.append(accrec)
        log.debug("Added accession record to {}({}): ({})".format(
            str(type(self)),
            self.identifier,
            str(accrec))
        )

    @log_aware(log)
    def get_accessionrecord(self, index):
        return self.get_accessionrecord_list()[index]

    @log_aware(log)
    def pop_accessionrecord(self, index=None):
        if index is None:
            x = self.get_accessionrecord_list.pop()
        else:
            x = self.get_accessionrecord_list.pop(index)
        log.debug("Popped accession record from {}({}): {}".format(
            str(type(self)),
            self.identifier,
            str(x))
        )
        return x

    @log_aware(log)
    def get_adminnote_list(self):
        return self._adminnote

    @log_aware(log)
    def set_adminnote_list(self, adminnotelist):
        self.del_adminnote_list()
        for x in adminnotelist:
            self.add_adminnote(x)

    @log_aware(log)
    def del_adminnote_list(self):
        while self.get_adminnote_list():
            self.pop_adminnote()

    @log_aware(log)
    def add_adminnote(self, adminnote):
        self.get_adminnote_list().append(adminnote)
        log.debug("Added adminnote to {}({}): {}".format(
            str(type(self)),
            self.identifier,
            str(adminnote))
        )

    @log_aware(log)
    def get_adminnote(self, index):
        return self.get_adminnote_list()[index]

    @log_aware(log)
    def pop_adminnote(self, index=None):
        if index is None:
            x = self.get_adminnote_list().pop()
        else:
            x = self.get_adminnote_list().pop(index)
        log.debug("Popped adminnote from {}({}): {}".format(
            str(type(self)),
            self.identifier,
            str(x))
        )
        return x

    @log_aware(log)
    def get_legalnote_list(self):
        return self._legalnote

    @log_aware(log)
    def set_legalnote_list(self, legalnote_list):
        self.del_legalnote_list()
        for x in legalnote_list:
            self.add_legalnote(x)

    @log_aware(log)
    def del_legalnote_list(self):
        while self.get_legalnote_list():
            self.pop_legalnote()

    @log_aware(log)
    def add_legalnote(self, legalnote):
        self.get_legalnote_list().append(legalnote)
        log.debug("Added legalnote to {}: {}".format(
            str(type(self)),
            str(legalnote))
        )

    @log_aware(log)
    def get_legalnote(self, index):
        return self.get_legalnote_list()[index]

    @log_aware(log)
    def pop_legalnote(self, index=None):
        if index is None:
            return self.get_legalnote_list().pop()
        else:
            return self.get_legalnote_list().pop(index)

    identifier = property(get_identifier,
                          set_identifier)
    materialsuite_list = property(get_materialsuite_list,
                                  set_materialsuite_list,
                                  del_materialsuite_list)
    accessionrecord_list = property(get_accessionrecord_list,
                                    set_accessionrecord_list,
                                    del_accessionrecord_list)
    adminnote_list = property(get_adminnote_list,
                              set_adminnote_list,
                              del_adminnote_list)
    legalnote_list = property(get_legalnote_list,
                              set_legalnote_list,
                              del_legalnote_list)


# -*- coding: utf-8 -*-

from odoo import models, fields, api, _
from odoo.exceptions import AccessError

class OFSaleConfiguration(models.TransientModel):
    _inherit = 'sale.config.settings'

    of_deposit_product_categ_id_setting = fields.Many2one(
        'product.category',
        string=u"(OF) Catégorie des acomptes",
        help=u"Catégorie des articles utilisés pour les acomptes"
    )

    stock_warning_setting = fields.Boolean(
        string="(OF) Stock", required=True, default=False,
        help=u"Afficher les messages d'avertissement de stock ?"
    )

    of_position_fiscale = fields.Boolean(string="(OF) Position fiscale")
    of_allow_quote_addition = fields.Boolean(string=u"(OF) Devis complémentaires")

    group_of_afficher_total_ttc = fields.Boolean(
        string=u"(OF) Afficher les sous-totaux TTC par ligne de commande", default=False,
        help=u"Affiche les sous-totaux TTC par ligne de commande. Uniquement dans le formulaire et non dans les "
             u"rapports.", implied_group='of_sale.group_of_afficher_total_ttc', group='base.group_user')

    group_of_order_line_option = fields.Boolean(
        string=u"(OF) Options de ligne de commande", implied_group='of_sale.group_of_order_line_option',
        group='base.group_portal,base.group_user,base.group_public')

    group_of_sale_multiimage = fields.Selection([
        (0, 'One image per product'),
        (1, 'Several images per product')],
        string='(OF) Multi Images', implied_group='of_sale.group_of_sale_multiimage',
        group='base.group_portal,base.group_user,base.group_public')

    of_sale_print_multiimage_level = fields.Selection([
        (0, 'Do not print'),
        (1, 'Print on each line'),
        (2, 'Print on appendix')], string='(OF) Print product images on Sale Order')
    group_of_sale_print_one_image = fields.Boolean(
        'Print on each line', implied_group='of_sale.group_of_sale_print_one_image',
        group='base.group_portal,base.group_user,base.group_public')
    group_of_sale_print_multiimage = fields.Boolean(
        'Print on appendix', implied_group='of_sale.group_of_sale_print_multiimage',
        group='base.group_portal,base.group_user,base.group_public')

    group_of_sale_print_attachment = fields.Selection([
        (0, 'Do not print'),
        (1, 'Print on appendix')], string='(OF) Print product attachments on Sale Order',
        implied_group='of_sale.group_of_sale_print_attachment',
        group='base.group_portal,base.group_user,base.group_public')

    of_invoice_grouped = fields.Selection(selection=[
        (0, 'Groupement par partenaire + devise'),
        (1, 'Groupement par commande'), ], string=u"(OF) Facturation groupée")

    sale_show_tax = fields.Selection(selection_add=[('both', 'Afficher les sous-totaux HT (B2B) et TTC (B2C)')])

    of_propagate_payment_term = fields.Boolean(
        string=u"(OF) Terms of payment",
        help=u"Si décoché, les conditions de règlement ne sont pas propagées aux factures")

    of_sale_order_margin_control = fields.Boolean(
        string=u"(OF) Contrôle de marge", help=u"Activer le contrôle de marge à la validation des commandes")

    group_product_variant_specific_price = fields.Selection(selection=[
        (0, u"Handle pricing by attribute"),
        (1, u"Handle pricing by variant")], string=u"(OF) Product variant pricing",
        implied_group='of_product.group_product_variant_specific_price',
        group='base.group_portal,base.group_user,base.group_public')

    @api.multi
    def set_stock_warning_defaults(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'stock_warning_setting', self.stock_warning_setting)

    @api.multi
    def set_of_deposit_product_categ_id_defaults(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_deposit_product_categ_id_setting', self.of_deposit_product_categ_id_setting.id)

    @api.multi
    def set_of_position_fiscale(self):
        view = self.env.ref('of_sale.of_sale_order_form_fiscal_position_required')
        if view:
            view.write({'active': self.of_position_fiscale})
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_position_fiscale',
            self.of_position_fiscale)

    @api.multi
    def set_of_allow_quote_addition_defaults(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_allow_quote_addition', self.of_allow_quote_addition)

    @api.multi
    def set_of_invoice_grouped_defaults(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_invoice_grouped', self.of_invoice_grouped)

    @api.multi
    def set_of_sale_print_multiimage_level_defaults(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_sale_print_multiimage_level', self.of_sale_print_multiimage_level)

    @api.onchange('of_sale_print_multiimage_level')
    def onchange_of_sale_print_multiimage_level(self):
        self.group_of_sale_print_one_image = self.of_sale_print_multiimage_level == 1
        self.group_of_sale_print_multiimage = self.of_sale_print_multiimage_level == 2

    @api.onchange('sale_show_tax')
    def _onchange_sale_tax(self):
        # Erase and replace parent function
        if self.sale_show_tax == "subtotal":
            self.update({
                'group_show_price_total': False,
                'group_show_price_subtotal': True,
            })
        elif self.sale_show_tax == "total":
            self.update({
                'group_show_price_total': True,
                'group_show_price_subtotal': False,
            })
        else:
            self.update({
                'group_show_price_total': True,
                'group_show_price_subtotal': True,
            })

    @api.multi
    def set_of_propagate_payment_term(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_propagate_payment_term', self.of_propagate_payment_term)

    @api.multi
    def set_of_sale_order_margin_control(self):
        return self.env['ir.values'].sudo().set_default(
            'sale.config.settings', 'of_sale_order_margin_control', self.of_sale_order_margin_control)

    @api.multi
    def execute(self):
        """This function is called when the user validate the settings.
        We overrided it to add the check of modified groups to allow the recompute only for groups thoses has been
        modified and not for all.
        """
        self.ensure_one()
        if not self.env.user._is_superuser() and not self.env.user.has_group('base.group_system'):
            raise AccessError(_("This setting can only be enabled by the administrator, "
                                "please contact support to enable this option."))

        # Get the default values of the groups and check if the value has been changed
        groups_fields = [field_name for field_name in self.fields_get().keys() if field_name.startswith('group_')]
        salesettings_groups_cache = {
            field_name: default_value
            for field_name, default_value in self.default_get(self.fields_get().keys()).iteritems()
            if field_name.startswith('group_')}
        salesettings_groups_has_changed = [
            field_name
            for field_name in groups_fields
            if getattr(self, field_name) != salesettings_groups_cache[field_name]]

        self = self.with_context(active_test=False)
        classified = self._get_classified_fields()

        # default values fields
        IrValues = self.env['ir.values'].sudo()
        for name, model, field in classified['default']:
            if isinstance(self[name], models.BaseModel):
                if self._fields[name].type == 'many2one':
                    value = self[name].id
                else:
                    value = self[name].ids
            else:
                value = self[name]
            IrValues.set_default(model, field, value)

        # To avoid a very long time of computation (for database with a lot a Users/Groups), we don't want to recompute
        # the groups if they haven't been changed in the settings.
        if salesettings_groups_has_changed:
            # filter groups to recompute only modified ones
            only_changed_values = filter(
                lambda gval: gval and gval[0] in salesettings_groups_has_changed, classified['group'])
            if only_changed_values:
                with self.env.norecompute():
                    for name, groups, implied_group in only_changed_values:
                        if self[name]:
                            groups.write({'implied_ids': [(4, implied_group.id)]})
                        else:
                            groups.write({'implied_ids': [(3, implied_group.id)]})
                            implied_group.write({'users': [(3, user.id) for user in groups.mapped('users')]})
                self.recompute()

        # other fields: execute all methods that start with 'set_'
        for method in dir(self):
            if method.startswith('set_'):
                getattr(self, method)()

        # module fields: install/uninstall the selected modules
        to_install = []
        to_uninstall_modules = self.env['ir.module.module']
        lm = len('module_')
        for name, module in classified['module']:
            if self[name]:
                to_install.append((name[lm:], module))
            else:
                if module and module.state in ('installed', 'to upgrade'):
                    to_uninstall_modules += module

        if to_uninstall_modules:
            to_uninstall_modules.button_immediate_uninstall()

        action = self._install_modules(to_install)
        if action:
            return action

        if to_install or to_uninstall_modules:
            # After the uninstall/install calls, the registry and environments
            # are no longer valid. So we reset the environment.
            self.env.reset()
            self = self.env()[self._name]
        config = self.env['res.config'].next() or {}
        if config.get('type') not in ('ir.actions.act_window_close',):
            return config

        # force client-side reload (update user menu and current view)
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }

    @api.multi
    def action_printings_params(self):
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'of.sale.wizard.set.printing.params',
            'view_mode': 'form',
            'view_type': 'form',
            'target': 'new'
        }


import numpy as np

from scipy import spatial

import matplotlib.pyplot as plt

def fft_smoothing(coords):

    #TODO: More relevant procedure required
    signal = coords[:,0] + 1j*coords[:,1]

    # FFT and frequencies
    fft = np.fft.fft(signal)
    freq = np.fft.fftfreq(signal.shape[-1])

    # filter
    cutoff = 0.1
    fft[np.abs(freq) > cutoff] = 0

    # IFFT
    signal_filt = np.fft.ifft(fft)

    coords[:,0] = signal_filt.real
    coords[:,1] = signal_filt.imag

    return coords

def pl_cytopath_alignment(adata, basis="umap", smoothing=False, figsize=(15,4), size = 3,
                          show=True, save=False,save_type='png', folder=""):

    map_state = adata.obsm['X_'+basis]
    av_allign_score_glob=[]
    std_allign_score_glob=[]

    step_time = adata.uns['trajectories']['step_time']
    fate_prob = adata.uns['trajectories']['cell_fate_probability']

    sequence=0

    # TODO: Separate per step average alignment score calculation from plotting
    for end_point_cluster in adata.uns['run_info']["end_point_clusters"]:
        trajectories = adata.uns['trajectories']["cells_along_trajectories_each_step"]\
                       [np.where(adata.uns['trajectories']["cells_along_trajectories_each_step"]["End point"]==end_point_cluster)[0]]
        for i in range(adata.uns['run_info']['trajectory_count'][end_point_cluster]):

            av_trajectories=trajectories[np.where(trajectories["Trajectory"]==i)[0]]
            av_allign_score=np.zeros((len(np.unique(av_trajectories["Step"]))))
            std_allign_score=np.zeros((len(np.unique(av_trajectories["Step"]))))
            for l in range(len(np.unique(av_trajectories["Step"]))):
                av_allign_score[l]=np.average((av_trajectories[np.where(av_trajectories["Step"]==l)[0]]["Allignment Score"]))
                std_allign_score[l]=np.std((av_trajectories[np.where(av_trajectories["Step"]==l)[0]]["Allignment Score"]))

            # Plotting
            path = folder+"_end_point_"+end_point_cluster+"_cytopath_"+str(i)+\
                 "occurance"+str(adata.uns['run_info']["trajectories_sample_counts"][end_point_cluster][i])+"."+save_type

            fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, figsize=figsize)
            ax1.plot(range(len(np.unique(av_trajectories["Step"]))), av_allign_score, color='black')
            ax1.fill_between(range(len(np.unique(av_trajectories["Step"]))),
                             av_allign_score+std_allign_score, av_allign_score-std_allign_score, facecolor='grey', alpha=0.6)
            ax1.set_ylabel('Mean/std. of alignment scores per step')
            ax1.set_xlabel('Steps')

            # Plot step size for aligned cells
            sc_step = ax2.scatter(map_state[:,0], map_state[:,1], alpha=0.6, s=size, color="whitesmoke")
            sc_step = ax2.scatter(map_state[:,0], map_state[:,1], alpha=0.9, s=size,
                                  vmin=0, vmax=np.nanmax(step_time), c=step_time[sequence,:], cmap='YlGnBu')
            fig.colorbar(sc_step, ax=ax2, label='Step time')

            ax2.set_ylabel(basis.upper()+' 2')
            ax2.set_xlabel(basis.upper()+' 1')

            ax2.set_title('End point: {}-{} Support: {}/{}'.format(end_point_cluster, i,
                                                                adata.uns['run_info']['trajectories_sample_counts'][end_point_cluster][i],
                                                                int(adata.uns['samples']['cell_sequences'].shape[0]/\
                                                                adata.uns['run_info']['end_point_clusters'].shape[0])))

            # Plot alignment score
            sc_score = ax3.scatter(map_state[:,0], map_state[:,1], alpha=0.6, s=size, color="whitesmoke")
            sc_score = ax3.scatter(map_state[:,0], map_state[:,1], alpha=0.9, s=size,
                                   vmin=0, vmax=1, c=fate_prob[sequence,:], cmap='Reds')
            fig.colorbar(sc_score, ax=ax3, label='Cell fate probability')

            ax3.set_ylabel(basis.upper()+' 2')
            ax3.set_xlabel(basis.upper()+' 1')

            # Plot trajectory
            if basis in adata.uns['run_info']['projection_basis']:
                coords = np.array(adata.uns['trajectories']['trajectories_coordinates'][end_point_cluster]['trajectory_'+str(i)+'_coordinates'])

            elif ('pca' in adata.uns['run_info']['projection_basis']) and (basis != 'pca'):
                coords_ = np.array(adata.uns['trajectories']['trajectories_coordinates'][end_point_cluster]['trajectory_'+str(i)+'_coordinates'])

                cell_sequences=[]
                for j in range(len(coords_)):
                    cell_sequences.append(spatial.KDTree(adata.obsm['X_pca']).query(coords_[j])[1])

                coords = map_state[cell_sequences]

            if smoothing == True:
                coords = fft_smoothing(coords)

            ax2.plot(coords[:, 0], coords[:, 1], color='black')
            ax3.plot(coords[:, 0], coords[:, 1], color='black')

            plt.tight_layout()

            if save:
                fig.savefig(path, bbox_inches='tight', dpi=300)
            if show:
                plt.show()
            # End plotting

            sequence+=1

            av_allign_score_glob.append(av_allign_score)
            std_allign_score_glob.append(std_allign_score)


from django import forms
from .models import UserProfile

class UserProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        exclude = ['user']

    def __init__(self, *args, **kwargs):
        """
        Add placeholders and classes, remove auto-generated
        labels and set autofocus on first field
        """
        super().__init__(*args, **kwargs)
        placeholders = {
            'user_phone_number': 'Phone Number',
            'user_zip': 'ZIP',
            'user_city': 'City',
            'user_address_line_1': 'Address line 1',
            'user_address_line_2': 'Address line 2',
            'user_state': 'State',
        }

        self.fields['user_phone_number'].widget.attrs['autofocus'] = True
        for field in self.fields:
            if field != 'user_country':
                if self.fields[field]:
                    placeholder = f'{placeholders[field]}'
                else:
                    placeholder = placeholders[field]
                self.fields[field].widget.attrs['placeholder'] = placeholder
            self.fields[field].label = False


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

import yaml
from yaml.loader import SafeLoader
import subprocess
import netifaces
import argparse
import os
import time
import fcntl

'''yaml
if_list:
   - ipaddr: 10.90.3.37
     prefix: 24
     mac: 52:54:84:11:00:00
     gateway: 10.90.3.1
   - ipaddr: 192.168.100.254
     prefix: 24
     mac: 52:54:84:00:08:38
eip_list:
  - eip: 10.90.2.252
    vm-ip: 192.168.100.192
  - eip: 10.90.2.253
    vm-ip: 192.168.100.193
port_forward_list:
  # master1.kcp5-arm.iefcu.cn
  - eip: 10.90.2.254
    protocal: udp
    port: 80
    end_port: 82
    vm-port: 80
    vm-ip: 192.168.100.190
'''

# https://blog.csdn.net/sunny_day_day/article/details/119893768
def load_interface():
  """获取接口mac地址对应名称"""
  macMap = {}
  for interface in netifaces.interfaces():
    macAddr = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]['addr']
    macMap[macAddr] = interface
  # print(macMap)
  return macMap

# 测试:
# 没有文件的情况; 文件为空内容的情况; 语法异常的情况; 配置缺失的情况;
# 读取配置文件
def load_config():
  # Open the file and load the file
  with open('/etc/kylin-vr/kylin-vr.yaml') as f:
    data = yaml.load(f, Loader=SafeLoader)
    # print(data)
    return data
  return nil

# XXX: 优化, 增量更新配置文件?
def one_interface_conf(ifname, ifconf, eip_list):
  """docstring for one_interface"""
  # filename = '/etc/sysconfig/network-scripts/ifcfg-' + ifname
  filename = '/var/run/kylin-vr/ifcfg-' + ifname
  # print(filename)

  fp = open(filename, 'w')

  fp.write('NAME=%s\nDEVICE="%s"\n' % (ifname, ifname))
  fp.write('''BOOTPROTO="none"
ONBOOT="yes"
TYPE="Ethernet"
IPV6INIT="no"
''')
  fp.write('''
IPADDR=%s
PREFIX=%s
''' % (ifconf['ipaddr'], ifconf['prefix']))
  if 'gateway' in ifconf:
    fp.write('GATEWAY=%s\n' % ifconf['gateway'])

    for i, eip in enumerate(eip_list):
      fp.write('''IPADDR%d=%s\nPREFIX%d=32\n''' % (i+1, eip, i+1))
  fp.close()

def get_eip_list(data):
  eip_set = set()

  for eip in data['eip_list']:
    eip_set.add(eip['eip'])

  for port_forward in data['port_forward_list']:
    eip_set.add(port_forward['eip'])

  return eip_set

# XXX: 处理参数异常情况!
def gen_network_conf(data):
  macMap = load_interface()
  eip_list = []
  for i, if_conf in enumerate(data['if_list']):
    mac = if_conf['mac']
    if mac not in macMap:
      # debug log
      continue
    interface = macMap[mac]
    data['if_list'][i]['ifname'] = interface

    if 'gateway' in if_conf: # 网关接口为公网物理出口
      data['ifname'] = interface
      eip_list = get_eip_list(data)

    # print(mac)
    # print(interface)
    one_interface_conf(interface, if_conf, eip_list)

  # 最后, 替换成新的ifcfg-xxx配置
  subprocess.call("rm -f /etc/sysconfig/network-scripts/ifcfg-eth*", shell=True)
  subprocess.call("mv /var/run/kylin-vr/ifcfg-eth* /etc/sysconfig/network-scripts", shell=True)

# 生成eip规则
def gen_eip_iptable_conf(f, data):
  # 1. 通过网关地址获取到公网接口名称
  # ip route | head -1 | grep default | awk '{print $5}'
  # 2. 或者通过mac地址获取公网接口名称
  if 'ifname' not in data:
    return

  ifname = data['ifname']

  for eip_item in data['eip_list']:
    extern_ip=eip_item['eip']
    vm_ip=eip_item['vm-ip']
    f.write("-A POSTROUTING -s %s/32 -o %s -j SNAT --to-source %s\n" % (vm_ip, ifname, extern_ip))
    f.write("-A PREROUTING -i %s -d %s/32 -j DNAT --to-destination %s\n" % (ifname, extern_ip, vm_ip))

# 生成snat规则
def gen_snat_iptable_conf(f, data):
  if 'ifname' not in data:
    return

  ifname = data['ifname']

  # 默认网关接口开启snat
  f.write('-A POSTROUTING -o %s -j MASQUERADE\n' % ifname)

# 生成端口转发iptable规则表
def gen_port_forward_iptable_conf(f, data):
  for port_forward in data['port_forward_list']:
    extern_ip = port_forward['eip']
    vm_ip = port_forward['vm-ip']
    protocal = port_forward['protocal']
    port = port_forward['port']
    vm_port = port_forward['vm-port']

    if 'end_port' not in port_forward: # 单端口映射
      f.write("-A PREROUTING -p %s -d %s --dport %d -j DNAT --to %s:%d\n" % (protocal, extern_ip, port, vm_ip, vm_port))
      f.write("-A POSTROUTING -p %s -s %s --sport %d -j SNAT --to %s:%d\n" % (protocal, vm_ip, vm_port, extern_ip, port))

    else: # 端口范围映射
      end_port = port_forward['end_port']
      f.write("-A PREROUTING -p %s -d %s --dport %d:%d -j DNAT --to %s:%d-%d\n" % (protocal, extern_ip, port, end_port, vm_ip, port, end_port))
      f.write("-A POSTROUTING -p %s -s %s --sport %d:%d -j SNAT --to %s:%d-%d\n" % (protocal, vm_ip, port, end_port, extern_ip, port, end_port))

# eip, snat, port forward的iptable规则配置
def gen_iptable_conf(data):
  f = open("/var/run/kylin-vr/iptable.txt", 'w')
  f.write('''
*filter
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
COMMIT

*mangle
:PREROUTING ACCEPT
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
COMMIT

*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT
''')

  gen_eip_iptable_conf(f, data)
  gen_port_forward_iptable_conf(f, data)
  gen_snat_iptable_conf(f, data)

  f.write('\nCOMMIT\n')
  f.close()

# 恢复iptable配置
def reload_iptable():
  return_code = subprocess.call(["iptables-restore","/var/run/kylin-vr/iptable.txt"])
  print('iptable reload return %d' % return_code)

# 重置network配置
def reload_network(data):
  """docstring for reload_network"""
  return_code = subprocess.call("nmcli c reload", shell=True)
  print('nmcli c reload return %d' % return_code)

  for if_conf in data['if_list']:
    if 'ifname' not in if_conf:
      continue
    cmd = 'nmcli c up %s' % if_conf['ifname']
    return_code = subprocess.call(cmd, shell=True)
    print('up connection `%s` return %d' % (cmd, return_code))

def check_flag():
  return os.path.exists('/var/run/kylin-vr')

def gen_flag():
  os.makedirs('/var/run/kylin-vr')

def config_init():
  gen_flag()
  data = load_config()
  if not data:
    print('load config failed!')
    return
  gen_network_conf(data)
  gen_iptable_conf(data)
  reload_iptable()
  pass

# 系统起来之后的配置更新
def config_reload(device):
  if not check_flag():
    print('kylin-vr service is not started, can not reload config!')
    return
  data = load_config()
  if not data:
    print('load config failed!')
    return
  gen_network_conf(data)
  gen_iptable_conf(data)
  reload_network(data)
  reload_iptable()
  pass

def is_running(file):
  fd = open(file, "w")
  try:
    fcntl.lockf(fd, fcntl.LOCK_EX|fcntl.LOCK_NB)
  except :
    return None
  return fd

def get_lock():
  lockfile = "/var/run/kylin-vr-running"
  while True:
    fd = is_running(lockfile)
    if fd:
      return fd
    time.sleep(1)

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('-c', '--command', help='sub command, Note: the allocate command needs to be used with -d parameters', \
        choices=['init', 'reload', 'subnet'], \
        default='init')
  parser.add_argument('-d', '--device', help='the subnet command needs to specify interface name. Example: -c subnet -d eth2')
  args = parser.parse_args()

  # 加锁保证单例执行
  a = get_lock()

  cmd = args.command if args.command else 'init'
  if 'reload' == cmd:
    config_reload(args.device)
  # elif 'subnet' == cmd:
    # config_subnet(args.device)
  else: # init
    config_init()

if __name__ == '__main__':
  main()


import time
import warnings

import mmcv
import torch

from mmcv.runner import RUNNERS, IterBasedRunner, IterLoader, get_host_info

@RUNNERS.register_module()
class MultiTaskIterBasedRunner(IterBasedRunner):

    def train(self, data_loader, **kwargs):
        self.model.train()
        self.mode = 'train'
        self.data_loader = data_loader[0]
        self._epoch = data_loader[0].epoch
        data_batch = []
        for dl in data_loader:
            data_batch.append(next(dl))

        self.call_hook('before_train_iter')
        outputs = self.model.train_step(data_batch, self.optimizer, **kwargs)
        if not isinstance(outputs, dict):
            raise TypeError('model.train_step() must return a dict')
        if 'log_vars' in outputs:
            self.log_buffer.update(outputs['log_vars'], outputs['num_samples'])
        self.outputs = outputs
        self.call_hook('after_train_iter')
        self._inner_iter += 1
        self._iter += 1

    def run(self, data_loaders, workflow, max_iters=None, **kwargs):
        """Start running.

        Args:
            data_loaders (list[:obj:`DataLoader`]): Dataloaders for training
                and validation.
            workflow (list[tuple]): A list of (phase, iters) to specify the
                running order and iterations. E.g, [('train', 10000),
                ('val', 1000)] means running 10000 iterations for training and
                1000 iterations for validation, iteratively.
        """
        assert isinstance(data_loaders, list)
        assert mmcv.is_list_of(workflow, tuple)
        # assert len(data_loaders) == len(workflow)
        if max_iters is not None:
            warnings.warn(
                'setting max_iters in run is deprecated, '
                'please set max_iters in runner_config', DeprecationWarning)
            self._max_iters = max_iters
        assert self._max_iters is not None, (
            'max_iters must be specified during instantiation')

        work_dir = self.work_dir if self.work_dir is not None else 'NONE'
        self.logger.info('Start running, host: %s, work_dir: %s',
                         get_host_info(), work_dir)
        self.logger.info('Hooks will be executed in the following order:\n%s',
                         self.get_hook_info())
        self.logger.info('workflow: %s, max: %d iters', workflow,
                         self._max_iters)
        self.call_hook('before_run')

        iter_loaders = [IterLoader(x) for x in data_loaders]

        self.call_hook('before_epoch')

        while self.iter < self._max_iters:
            for i, flow in enumerate(workflow):
                self._inner_iter = 0
                mode, iters = flow
                if not isinstance(mode, str) or not hasattr(self, mode):
                    raise ValueError(
                        'runner has no method named "{}" to run a workflow'.
                        format(mode))
                iter_runner = getattr(self, mode)
                for _ in range(iters):
                    if mode == 'train' and self.iter >= self._max_iters:
                        break
                    iter_runner(iter_loaders, **kwargs)

        time.sleep(1)  # wait for some hooks like loggers to finish
        self.call_hook('after_epoch')
        self.call_hook('after_run')


test_cases = int(input())

all_times = []
displayed_time = 0

for test in range(test_cases):
    current_time = int(input())
    all_times.append(current_time)

# even number of presses means watch is still running
if test_cases % 2 != 0:
    print("still running")

# odd number means we have to add up all the times
# take the difference bw every other  press and add it to the total time
# manually tracking index...maybe theres a better way?
else:
    index = 0
    for time in all_times:
        if index % 2 != 0:
            displayed_time += time - all_times[index-1]
            # print(time)

        index += 1
    print(displayed_time)


import imp
from ecs import World, Entity
from coolClasses import *

def entityAtPos(world : World, x, y, *Components) -> list[Entity]:
    testPos = Posistion(x,y)
    entitys = []
    for i in world.getView(Posistion, *Components):
        pos = i.getComponent(Posistion)
        if pos == testPos:
            entitys.append(i)
    return entitys


# -*- coding: utf-8 -*-
"""
Created on Thu Mar 12 16:12:53 2020

@author: Monik
"""

import os, tifffile
import numpy as np
import matplotlib.pyplot as plt
import SOFI2_0_fromMatlab as sofi2

#%% helper functions
def where_max(a):
    print(a.shape)
    return np.unravel_index(np.argmax(a, axis=None), a.shape)

#%% read data and show mean
data_dir='SOFI2-demo-data/'
T=20

data_timelapse=[np.array(tifffile.imread(os.path.join(data_dir, 'Block'+str(k)+'.tif')), dtype=np.float32) for k in range(1, T+1)]
data_mean_series=np.array([np.mean(data_timelapse[k], axis=0) for k in range(T)])

plt.imshow(data_mean_series[-1])
plt.colorbar()

#%% calculate m6 for all data

m6_series=np.array([sofi2.M6(data_timelapse[k], verbose=True, comment=str(k)) for k in range(T)])

plt.imshow(m6_series[-1])

#%% here I need a better deconvolution!
m6_f=sofi2.filter_timelapse(sofi2.kill_outliers(m6_series))
m6_dcnv=np.array([sofi2.deconvolution(m6_f[k], verbose=True, comment=str(k)) for k in range(T)], dtype=np.float32)
m6_dcnv_f=sofi2.filter_timelapse(m6_dcnv)

#plt.imshow(m6_dcnv_f[-1])
#plt.colorbar()

plt.imshow(m6_dcnv_f[-1])

#%% do ldrc
m6_ldrc_series=np.array([sofi2.ldrc(m6_dcnv_f[k], data_mean_series[k], 25) for k in range(T)])
plt.imshow(m6_ldrc_series[-1])
plt.colorbar()

#%% alternative: ldrc without deconv
m6_ldrc_nodeconv=np.array([sofi2.ldrc(m6_f[k], data_mean_series[k], 25) for k in range(T)])
plt.imshow(m6_ldrc_series[-1])
plt.colorbar()

#%%
tifffile.imsave('demo_means'+'.tif', np.uint16(65500*data_mean_series/data_mean_series.max()))
tifffile.imsave('demo_M6_Deconv_ldrc'+'.tif', np.uint16(65500*m6_ldrc_series/m6_ldrc_series.max()))
tifffile.imsave('demo_M6_noDeconv_ldrc'+'.tif', np.uint16(65500*m6_ldrc_nodeconv/m6_ldrc_nodeconv.max()))


import unittest
import sys
sys.path.insert(1, '..')
import easy_gui

class GUI(easy_gui.EasyGUI):
    def __init__(self):
        self.geometry('300x300')
        self.date = self.add_widget('date')
        self.add_widget(type='button', text='Print Date', command_func=self.print_date)

    def print_date(self, *args):
        print(self.date.get())

class TestEasyGUI(unittest.TestCase):
    def test_gui_creation(self):
        gui = GUI()

if __name__ == '__main__':
    unittest.main() #buffer=True)