import base64

from gcloud._helpers import _datetime_to_rfc3339
from gcloud._helpers import _NOW
from gcloud.exceptions import NotFound
from gcloud.pubsub._helpers import subscription_name_from_path
from gcloud.pubsub._helpers import topic_name_from_path
from gcloud.pubsub.iam import Policy
from gcloud.pubsub.subscription import Subscription

class Topic(object):
    """Topics are targets to which messages can be published.

    Subscribers then receive those messages.

    See:
    https://cloud.google.com/pubsub/reference/rest/v1/projects.topics

    :type name: string
    :param name: the name of the topic

    :type client: :class:`gcloud.pubsub.client.Client`
    :param client: A client which holds credentials and project configuration
                   for the topic (which requires a project).

    :type timestamp_messages: boolean
    :param timestamp_messages: If true, the topic will add a ``timestamp`` key
                               to the attributes of each published message:
                               the value will be an RFC 3339 timestamp.
    """
    def __init__(self, name, client, timestamp_messages=False):
        self.name = name
        self._client = client
        self.timestamp_messages = timestamp_messages

    def subscription(self, name, ack_deadline=None, push_endpoint=None):
        """Creates a subscription bound to the current topic.

        Example:  pull-mode subcription, default paramter values

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_subscription_defaults]
           :end-before: [END topic_subscription_defaults]

        Example:  pull-mode subcription, override ``ack_deadline`` default

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_subscription_ack90]
           :end-before: [END topic_subscription_ack90]

        Example:  push-mode subcription

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_subscription_push]
           :end-before: [END topic_subscription_push]

        :type name: string
        :param name: the name of the subscription

        :type ack_deadline: int
        :param ack_deadline: the deadline (in seconds) by which messages pulled
                             from the back-end must be acknowledged.

        :type push_endpoint: string
        :param push_endpoint: URL to which messages will be pushed by the
                              back-end. If not set, the application must pull
                              messages.
        """
        return Subscription(name, self, ack_deadline=ack_deadline,
                            push_endpoint=push_endpoint)

    @classmethod
    def from_api_repr(cls, resource, client):
        """Factory:  construct a topic given its API representation

        :type resource: dict
        :param resource: topic resource representation returned from the API

        :type client: :class:`gcloud.pubsub.client.Client`
        :param client: Client which holds credentials and project
                       configuration for the topic.

        :rtype: :class:`gcloud.pubsub.topic.Topic`
        :returns: Topic parsed from ``resource``.
        :raises: :class:`ValueError` if ``client`` is not ``None`` and the
                 project from the resource does not agree with the project
                 from the client.
        """
        topic_name = topic_name_from_path(resource['name'], client.project)
        return cls(topic_name, client=client)

    @property
    def project(self):
        """Project bound to the topic."""
        return self._client.project

    @property
    def full_name(self):
        """Fully-qualified name used in topic / subscription APIs"""
        return 'projects/%s/topics/%s' % (self.project, self.name)

    def _require_client(self, client):
        """Check client or verify over-ride.

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.

        :rtype: :class:`gcloud.pubsub.client.Client`
        :returns: The client passed in or the currently bound client.
        """
        if client is None:
            client = self._client
        return client

    def create(self, client=None):
        """API call:  create the topic via a PUT request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/create

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_create]
           :end-before: [END topic_create]

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.
        """
        client = self._require_client(client)
        api = client.publisher_api
        api.topic_create(topic_path=self.full_name)

    def exists(self, client=None):
        """API call:  test for the existence of the topic via a GET request

        See
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/get

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_exists]
           :end-before: [END topic_exists]

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.
        """
        client = self._require_client(client)
        api = client.publisher_api

        try:
            api.topic_get(topic_path=self.full_name)
        except NotFound:
            return False
        else:
            return True

    def delete(self, client=None):
        """API call:  delete the topic via a DELETE request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/delete

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_delete]
           :end-before: [END topic_delete]

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.
        """
        client = self._require_client(client)
        api = client.publisher_api
        api.topic_delete(topic_path=self.full_name)

    def _timestamp_message(self, attrs):
        """Add a timestamp to ``attrs``, if the topic is so configured.

        If ``attrs`` already has the key, do nothing.

        Helper method for ``publish``/``Batch.publish``.
        """
        if self.timestamp_messages and 'timestamp' not in attrs:
            attrs['timestamp'] = _datetime_to_rfc3339(_NOW())

    def publish(self, message, client=None, **attrs):
        """API call:  publish a message to a topic via a POST request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/publish

        Example without message attributes:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_publish_simple_message]
           :end-before: [END topic_publish_simple_message]

        With message attributes:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_publish_message_with_attrs]
           :end-before: [END topic_publish_message_with_attrs]

        :type message: bytes
        :param message: the message payload

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.

        :type attrs: dict (string -> string)
        :param attrs: key-value pairs to send as message attributes

        :rtype: str
        :returns: message ID assigned by the server to the published message
        """
        client = self._require_client(client)
        api = client.publisher_api

        self._timestamp_message(attrs)
        message_b = base64.b64encode(message).decode('ascii')
        message_data = {'data': message_b, 'attributes': attrs}
        message_ids = api.topic_publish(self.full_name, [message_data])
        return message_ids[0]

    def batch(self, client=None):
        """Return a batch to use as a context manager.

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_batch]
           :end-before: [END topic_batch]

        .. note::

           The only API request happens during the ``__exit__()`` of the topic
           used as a context manager, and only if the block exits without
           raising an exception.

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.

        :rtype: :class:`Batch`
        :returns: A batch to use as a context manager.
        """
        client = self._require_client(client)
        return Batch(self, client)

    def list_subscriptions(self, page_size=None, page_token=None, client=None):
        """List subscriptions for the project associated with this client.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics.subscriptions/list

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_list_subscriptions]
           :end-before: [END topic_list_subscriptions]

        :type page_size: int
        :param page_size: maximum number of topics to return, If not passed,
                          defaults to a value set by the API.

        :type page_token: string
        :param page_token: opaque marker for the next "page" of topics. If not
                           passed, the API will return the first page of
                           topics.

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current topic.

        :rtype: tuple, (list, str)
        :returns: list of :class:`gcloud.pubsub.subscription.Subscription`,
                  plus a "next page token" string:  if not None, indicates that
                  more topics can be retrieved with another call (pass that
                  value as ``page_token``).
        """
        client = self._require_client(client)
        api = client.publisher_api
        sub_paths, next_token = api.topic_list_subscriptions(
            self.full_name, page_size, page_token)
        subscriptions = []
        for sub_path in sub_paths:
            sub_name = subscription_name_from_path(sub_path, self.project)
            subscriptions.append(Subscription(sub_name, self))
        return subscriptions, next_token

    def get_iam_policy(self, client=None):
        """Fetch the IAM policy for the topic.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/getIamPolicy

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_get_iam_policy]
           :end-before: [END topic_get_iam_policy]

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current batch.

        :rtype: :class:`gcloud.pubsub.iam.Policy`
        :returns: policy created from the resource returned by the
                  ``getIamPolicy`` API request.
        """
        client = self._require_client(client)
        api = client.iam_policy_api
        resp = api.get_iam_policy(self.full_name)
        return Policy.from_api_repr(resp)

    def set_iam_policy(self, policy, client=None):
        """Update the IAM policy for the topic.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/setIamPolicy

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_set_iam_policy]
           :end-before: [END topic_set_iam_policy]

        :type policy: :class:`gcloud.pubsub.iam.Policy`
        :param policy: the new policy, typically fetched via
                       :meth:`get_iam_policy` and updated in place.

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current batch.

        :rtype: :class:`gcloud.pubsub.iam.Policy`
        :returns: updated policy created from the resource returned by the
                  ``setIamPolicy`` API request.
        """
        client = self._require_client(client)
        api = client.iam_policy_api
        resource = policy.to_api_repr()
        resp = api.set_iam_policy(self.full_name, resource)
        return Policy.from_api_repr(resp)

    def check_iam_permissions(self, permissions, client=None):
        """Verify permissions allowed for the current user.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/testIamPermissions

        Example:

        .. literalinclude:: pubsub_snippets.py
           :start-after: [START topic_check_iam_permissions]
           :end-before: [END topic_check_iam_permissions]

        :type permissions: list of string
        :param permissions: list of permissions to be tested

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current batch.

        :rtype: sequence of string
        :returns: subset of ``permissions`` allowed by current IAM policy.
        """
        client = self._require_client(client)
        api = client.iam_policy_api
        return api.test_iam_permissions(
            self.full_name, list(permissions))

class Batch(object):
    """Context manager:  collect messages to publish via a single API call.

    Helper returned by :meth:Topic.batch

    :type topic: :class:`gcloud.pubsub.topic.Topic`
    :param topic: the topic being published

    :type client: :class:`gcloud.pubsub.client.Client`
    :param client: The client to use.
    """
    def __init__(self, topic, client):
        self.topic = topic
        self.messages = []
        self.message_ids = []
        self.client = client

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            self.commit()

    def __iter__(self):
        return iter(self.message_ids)

    def publish(self, message, **attrs):
        """Emulate publishing a message, but save it.

        :type message: bytes
        :param message: the message payload

        :type attrs: dict (string -> string)
        :param attrs: key-value pairs to send as message attributes
        """
        self.topic._timestamp_message(attrs)
        self.messages.append(
            {'data': base64.b64encode(message).decode('ascii'),
             'attributes': attrs})

    def commit(self, client=None):
        """Send saved messages as a single API call.

        :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType``
        :param client: the client to use.  If not passed, falls back to the
                       ``client`` stored on the current batch.
        """
        if client is None:
            client = self.client
        api = client.publisher_api
        message_ids = api.topic_publish(self.topic.full_name, self.messages[:])
        self.message_ids.extend(message_ids)
        del self.messages[:]


# if this doesn't work then probabaly fuzzywuzzy is not installed so for that do this:
# pip install fuzzywuzzy

from fuzzywuzzy import process
str2Match = "apple inc"
strOptions = ["Apple Inc.","apple park","apple incorporated","iphone"]
Ratios = process.extract(str2Match,strOptions)
print(Ratios)
# You can also select the string with the highest matching percentage
highest = process.extractOne(str2Match,strOptions)
print(highest)


from django.urls import path
from . import views

app_name = 'movies'

urlpatterns = [
    path('movies/', views.movies_list, name='movies_list'),
    path('genres/', views.genres_list, name='genres_list'),
    path('genres/<int:genre_pk>/', views.genres_detail, name='genres_detail'),
    path('movies/<int:movie_pk>/', views.movies_detail, name='movies_detail'),
    path('movies/<int:movie_pk>/reviews/', views.reviews_create, name='reviews_create'),
    path('reviews/<int:review_pk>/', views.reviews_update_delete, name='reviews_udpate_delete'),
]


################################Deprecated (changed data source)########################
import numpy as np
import pandas as pd
from datetime import datetime

###############################################################################
#-------------------------stop data--------------------------------------------
# tring to identify DUI for stop data and collision data.
DUI_lst = ['CONTRABAND VISIBLE', 'ODOR OF CONTRABAND', 'DUI']

rename_dict = {
    'perceived_age':'subject_age',\
    'perceived_gender':'subject_sex',\
    'result':'arrested',\
    'reason_for_stop_detail':'stop_cause',\
    'basis_for_search':'searched',\
    'race':'subject_race',\
    'type_of_property_seized':'property_seized',\
    'contraband':'contraband_found',\
    # below is for filtering out DUI records
    # I rename the old versions DUI col to the new versions's name
    'search_details_description':'reason_for_stop_explanation'}

# return subset of the new stop data with only DUI related record
def filter_to_dui(stop):
    copy = stop.copy(deep=True)
    copy = copy[~copy.reason_for_stop_explanation.isna()]
    copy.reason_for_stop_explanation = copy.reason_for_stop_explanation.str.upper()
    return copy[copy.reason_for_stop_explanation.str.contains("DUI")]

# standardize df according to the full columns
def standardize(df,cols):
    result = df.rename(rename_dict,axis=1)
    # find cols that are not in df
    cols_series = pd.Series(cols)
    not_in = cols_series[~cols_series.isin(result.columns)]
    for col in not_in:
        result[col] = np.NaN
    result = result[cols]
    return result

# clean race by Asian,pacific, black, hispanic, white, other/nan  A,P,B,H,W,O
def clean_old_race(x):
    if not isinstance(x,str):
        return np.NaN
    elif x.upper() in ['B','O','W','H']:
        return x.upper()
    elif x.upper() in 'A C D F I J K L V Z'.split():
        return 'A'
    elif x.upper() in  'G P S U'.split():
        return 'P'
    else:
        return 'O'

def clean_new_race(x):
    if x.upper() == "White".upper():
        return 'W'
    elif x.upper() == "Hispanic/Latino/a".upper():
        return 'H'
    elif x.upper() == "Black/African American".upper():
        return 'B'
    elif x.upper() == "Native American".upper():
        return 'O'
    elif x.upper() == "Middle Eastern or South Asian".upper():
        return 'A'
    elif x.upper() == "Asian".upper():
        return 'A'
    elif x.upper() == "Pacific Islander".upper():
        return 'P'
    elif not isinstance(x,str):
        return np.NaN
    else:
        return 'O'

# derive the new version service area from beat
def new_to_service(x):
    if np.isnan(x):
        return np.NaN
    else:
        return int(x//10*10)

def clean_old_service(x):
    if isinstance(x,str) and x.upper() == 'Unknown'.upper():
        return np.NaN
    else:
        return int(x)

# clean subject_age may contain NaN, so type becomes float
def clean_age(x):
    try:
        return float(x)
    except:
        return np.NaN

# clean sex based on the old version 'M','F','X',nan
def clean_sex(x):
    if not isinstance(x,str):
        return np.NaN
    elif x.upper() in ['F','FEMALE']:
        return 'F'
    elif x.upper() in ['M','MALE']:
        return 'M'
    else:
        return 'X'

def clean_time(df):
    temp = df[~pd.isnull(df.time_stop)]
    temp.time_stop = temp.time_stop.str.extract('([0-9]+:[0-9]+)')
    temp.time_stop = pd.to_datetime(temp.time_stop,errors='coerce')
    return temp

# clean old verison searched, arrested, contraband_found, and property_seized
def clean_old_post(x):
    if not isinstance(x,str):
        return np.NaN
    else:
        return x.upper()

# clean new version searched, contraband_found,property_seized based on
# https://oag.ca.gov/sites/all/files/agweb/pdfs/ripa/stop-data-reg-final-text-110717.pdf?
def clean_new_post(x):
    if not isinstance(x,str):
        return 'N'
    else:
        if x.upper() == 'NONE':
            return 'N'
        else:
            return 'Y'

def clean_new_arrest(x):
    arrest_lst = ['Custodial Arrest without warrant'.upper(),'Custodial Arrest pursuant to outstanding warrant'.upper()]
    if not isinstance(x,str):
        return np.NaN
    elif x.upper() in arrest_lst:
        return 'Y'
    else:
        return 'N'

old_clean = {"service_area":clean_old_service,"subject_race":clean_old_race\
,"subject_age":clean_age,"subject_sex":clean_sex,"searched":clean_old_post,\
"arrested":clean_old_post,"contraband_found":clean_old_post,"property_seized":clean_old_post}

new_clean = {"subject_race":clean_new_race\
,"subject_age":clean_age,"subject_sex":clean_sex,"searched":clean_new_post,\
"arrested":clean_new_arrest,"contraband_found":clean_new_post,"property_seized":clean_new_post}

###############################################################################
#-------------------------collision data--------------------------------------------
# return subset of the collision data with only DUI related record
def collision_dui(collision_df):
    copy = stop.copy(deep=True)
    copy = copy[copy['PCF_VIOL_CATEGORY'] == '01']
    return copy

def clean_longitude(x):
    if x:
        return (-1)*float(x)
    else:
        return x

def clean_PCF(x):
    if x == '01':
        return 'DUI'
    else:
        return x

def clean_date(x):
    return datetime.strptime(str(x), '%Y%m%d')

def get_hour(x):
    #collision time is non zero padded
    x = str(int(x)).zfill(4)[0:2]+':00'
    x = datetime.strptime(x,'%H:%M')
    return x

def type_collision(x):
    dic = {'A': 'Head-On','B':'Sideswipe','C': 'Rear End',\
          'D':'Broadside','E':'Hit Object','F':'Overturned',\
          'G':'Vehicle/Pedstrian','H':'Other','-':'Not Stated'}
    return dic[x]

def type_injury(x):
    dic = {0:'property damage only',1:'fatal',2:'severe injury',3:'other visible injury',\
          4:'complaint of pain'}
    return dic[x]

collision_clean = {'PCF_VIOL_CATEGORY': clean_PCF, 'LONGITUDE': clean_longitude,\
'COLLISION_DATE': clean_date}


import numpy as np
import sympy
from sympy import RealNumber as Real, symbols as sym, diff as d

DIMENSION = 2
x = sym('x y')

CONTRAV = True
COV = False

class Index:
    def __init__(self, name):
        self.name = name.strip('-')
        self.contrv = (name[0] != '-')

    @staticmethod
    def new(names):
        return tuple([Index(i) for i in names.split()])

    def __repr__(self):
        return self.name if self.contrv else '-' + self.name

    def __neg__(self):
        if self.contrv:
            return Index('-' + self.name)
        else:
            return Index(self.name)

    def __eq__(self, other):
        return self.name == other.name and self.contrv == other.contrv

class IndexHandle:
    """
    The art of representing a whole tensor with a component...
      comprising coordinate transform, internal spaces etc.
    Tensor T := T_{ij}^{mn}
    In PyGR as T[-i,-j,m,n],where i/j/m/n is of type IndexHandle.
    Contraction: T_{ij}*U^{j}
     as contr(T[-i,-j]*U[j],j)? To avoid certain ambiguities...
    """

    def __init__(self, T, i, do_not_contract=False):
        """
        Creates an index handle
        :param T: Tensor handled
        :param i: tuple of indices
        """
        self.ind = i
        # Contraction
        if not do_not_contract:
            rten = T.T
            itype = []
            icopy = []
            i = list(i)
            inum = 0
            while True:
                curind = i[inum]
                try:
                    cind = i.index(-curind, inum + 1)
                    rten = np.array(np.sum(np.diagonal(rten, axis1=inum, axis2=cind), axis=-1))
                    i.remove(curind)
                    i.remove(-curind)
                except ValueError:
                    itype.append(curind.contrv)
                    icopy.append(curind)
                    inum += 1
                finally:
                    if inum >= len(i):
                        break
            self.ind = icopy
            self.tensor = Tensor(ind=tuple(itype), arr=rten.copy())
        else:
            self.tensor = T

    def __add__(self, other):
        if self.ind != other.ind:
            raise ValueError(f'Indices do not match: {self.ind} and {other.ind}')
        else:
            return IndexHandle(self.tensor + other.tensor, self.ind)

    def __mul__(self, other):
        if type(other) == float:
            return IndexHandle(self.tensor * other, self.ind)
        return IndexHandle(self.tensor * other.tensor, (*self.ind, *other.ind))

    __rmul__ = __mul__
    __radd__ = __add__

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

    def __sub__(self, other):
        if self.ind != other.ind:
            raise ValueError(f'Indices do not match: {self.ind} and {other.ind}')
        else:
            return IndexHandle(self.tensor - other.tensor, self.ind)

    def __truediv__(self, other):
        if type(other) == float:
            return IndexHandle(self.tensor / other, self.ind)
        return IndexHandle(self.tensor / other.tensor, (*self.ind, *[-i for i in other.ind]))

    def __rsub__(self, other):
        if self.ind != other.ind:
            raise ValueError(f'Indices do not match: {self.ind} and {other.ind}')
        else:
            return IndexHandle(other.tensor - self.tensor, self.ind)

    def __rtruediv__(self, other):
        if type(other) == float:
            return IndexHandle(other / self.tensor, self.ind)
        return IndexHandle(other.tensor / self.tensor, (*other.ind, *[-i for i in self.ind]))

vec_exp = np.frompyfunc(sympy.expand, 1, 1)
vec_sym = np.frompyfunc(sympy.simplify, 1, 1)

class Tensor:
    IMPLICIT_INDEX_RAISING_LOWERING = False
    METRIC = None
    INVERSE_METRIC = None

    def __init__(self, ind, arr=None):
        if type(ind) == Index:
            ind = (ind,)
        if arr is None:
            self.T = np.zeros((DIMENSION,) * len(ind), dtype=Real)
        else:
            self.T = arr.reshape((DIMENSION,) * len(ind))
        self.IndexType = tuple(ind)  # Should be like (True, False,...); True for contravariant
        self.expand()

    def __getitem__(self, index):
        if type(index) == Index:
            index = (index,)
        it = tuple([i.contrv for i in index])
        if self.IndexType != it:
            if Tensor.IMPLICIT_INDEX_RAISING_LOWERING:
                raise NotImplementedError
                if len(self.IndexType) != len(it):
                    raise ValueError('Index number does not match.')
                for i in range(len(it)):
                    if(self.IndexType[i], it[i]) == (COV, CONTRAV):
                        pass  # TODO
            raise ValueError('Index type does not match, perhaps you want to enable automatic index raising/lowering?')
        return IndexHandle(self, index)

    def __setitem__(self, ind, other: IndexHandle):
        axes = []
        for i in ind:
            axes.append(other.ind.index(i))
        self.T = other.tensor.T.transpose(axes)
        self.expand()

    def __add__(self, other):
        if self.IndexType != other.IndexType:
            raise ValueError('Index type does not match.')
        return Tensor(arr=self.T + other.T, ind=self.IndexType)

    def __mul__(self, other):
        if type(other) == float:
            return Tensor(arr=self.T * other, ind=self.IndexType)
        return Tensor(arr=np.tensordot(self.T, other.T, axes=0), ind=(*self.IndexType, *other.IndexType))

    def __sub__(self, other):
        if self.IndexType != other.IndexType:
            raise ValueError('Index type does not match.')
        return Tensor(arr=self.T - other.T, ind=self.IndexType)

    def __neg__(self):
        return Tensor(arr=-self.T, ind=self.IndexType)

    def __truediv__(self, other):
        if type(other) == float:
            return Tensor(arr=self.T / other, ind=self.IndexType)
        return Tensor(arr=np.tensordot(other.T, 1 / self.T, axes=0),
                      ind=(*other.IndexType, *[not i for i in self.IndexType]))

    __rmul__ = __mul__
    __radd__ = __add__

    def __rsub__(self, other):
        if self.IndexType != other.IndexType:
            raise ValueError('Index type does not match.')
        return Tensor(arr=other.T - self.T, ind=self.IndexType)

    def __rtruediv__(self, other):
        if type(other) == float:
            return Tensor(arr=other / self.T, ind=self.IndexType)
        return Tensor(arr=np.tensordot(self.T, 1 / other.T, axes=0), ind=(*self.IndexType,
                                                                          *[not i for i in other.IndexType]))

    def __repr__(self):
        return str(self.T)

    def simplify(self):
        self.T = vec_sym(self.T)

    def expand(self):
        self.T = vec_exp(self.T)

Tensor.METRIC = Tensor(arr=np.array([[Real(1), Real(0)], [Real(0), Real(1)]]), ind=(COV, COV))
Tensor.INVERSE_METRIC = Tensor(arr=np.array([[Real(1), Real(0)], [Real(0), Real(1)]]), ind=(CONTRAV, CONTRAV))
Tensor.IMPLICIT_INDEX_RAISING_LOWERING = False

class D:
    """Implements derivative.
    Usage: D(index)()
    """
    _d = np.frompyfunc(d, 2, 1)

    def __init__(self, index):
        if index.contrv and not Tensor.IMPLICIT_INDEX_RAISING_LOWERING:
            raise ValueError('The derivative must be covariant.')
        elif index.contrv:
            raise NotImplementedError('Not Implemented: implicit index raising/lowering')
        self.index = index

    def __call__(self, op: IndexHandle):
        return IndexHandle(np.array([self._d(op.tensor.T, i) for i in x]), i=(self.index, *op.ind))

if __name__ == '__main__':
    # UNIT TEST
    arr = np.array([x[0], x[1]])
    V: Tensor = Tensor(arr=arr, ind=(CONTRAV,))
    a, b, c, d, e, f = Index.new('a b c d e f')
    print(V[a] * V[b], V[a] + V[a], V[a] - V[a], V[b] / V[a], 0.3 / V[a], sep='\n')
    R = Tensor(arr=np.array([[[1, 0], [0, x[0]]], [[x[1] + 1, 0], [x[0] * x[1] - 2, 0]]]), ind=(CONTRAV, CONTRAV, COV))
    T = Tensor(ind=(CONTRAV, CONTRAV, CONTRAV, COV))
    print(V)
    T[a, c, d, -e] = R[a, b, -e] * R[c, d, -b]
    print(T)
    print()
    print(D(-a)(V[a]))


import json
from flaskr.tests.fixtures.album_fixture import AlbumFixture
from flaskr.tests.fixtures.user_fixtures import UsuarioFixture
from flaskr.modelos import Acceso, Medio
from flaskr.tests import BaseCase, fixtures

class TestAlbum(BaseCase):
    def test_given_existing_user_that_creates_new_album_when_login_and_retrieve_album_list_then_success(self):
        """
        Asserts un usuario puede ver su lista de albumes
        """
        # Arrange
        user = UsuarioFixture().create()
        album_data = {
            'titulo': 'Test',
            'descripcion': 'Test',
            'medio': Medio.CASETE.name,
            'anio': 2020
        }

        # Act
        crear_album_response = self.run_authenticated(user, 'post', f'/usuario/{user.id}/albumes', data=json.dumps(album_data))
        response = self.run_authenticated(user, 'get', f'/usuario/{user.id}/albumes')

        new_album = crear_album_response.json
        albums = response.json

        # Assert
        self.assertEqual(200, crear_album_response.status_code)
        self.assertEqual(200, response.status_code)
        self.assertEqual(len(albums), 1)
        self.assertEqual(new_album['acceso']['llave'], Acceso.PRIVADO.name)
        self.assertEqual(new_album['titulo'], album_data['titulo'])
        self.assertTrue(any(album['id'] is new_album['id'] for album in albums))

    def test_given_existing_user_that_have_albums_when_login_and_retrieve_album_list_then_success(self):
        """
        Asserts un usuario puede ver su lista de albumes
        """
        # Arrange
        user = UsuarioFixture().create()
        album_fixture = AlbumFixture()
        album_fixture.default_usuario = fixtures.ObjectFixture(user)

        first_album = album_fixture.create()
        second_album = album_fixture.create()

        # Act
        response = self.run_authenticated(user, 'get', f'/usuario/{user.id}/albumes')
        albums = response.json
        # Assert
        self.assertEqual(200, response.status_code)
        self.assertEqual(len(albums), 2)
        self.assertTrue(any(album['id'] is first_album.id for album in albums))
        self.assertTrue(any(album['id'] is second_album.id for album in albums))

    def test_given_existing_user_that_have_albums_and_public_albums_exists_when_login_and_retrieve_album_list_then_success(self):
        """
        Asserts que en la lista de albumes de un usuario puede ver sus albumes y los albumes publicos
        """
        # Arrange
        user_fixture = UsuarioFixture()
        first_user = user_fixture.create()
        album_fixture = AlbumFixture()
        album_fixture.default_usuario = fixtures.ObjectFixture(first_user)

        private_first_album = album_fixture.create()
        private_second_album = album_fixture.create()

        # otro usuario que tendrá albumes publicos
        second_user = user_fixture.create()
        album_fixture.default_usuario = fixtures.ObjectFixture(second_user)

        private_album = album_fixture.create()

        album_fixture.default_acceso = fixtures.ObjectFixture(Acceso.PUBLICO)
        public_first_album = album_fixture.create()

        # Act
        response = self.run_authenticated(first_user, 'get', f'/usuario/{first_user.id}/albumes')
        albums = response.json
        # Assert
        self.assertEqual(200, response.status_code)
        self.assertEqual(len(albums), 3)
        self.assertTrue(any(album['id'] is private_first_album.id for album in albums))
        self.assertTrue(any(album['id'] is private_second_album.id for album in albums))
        self.assertTrue(any(album['id'] is public_first_album.id and album['pertenece'] == False for album in albums)) # album publico que pertenece a otro coleccionista

    def test_given_existing_user_that_makes_an_existing_album_to_public_then_success(self):
        """
        Asserts un usuario puede ver su lista de albumes, incluidos sus albumes publicos
        """
        # Arrange
        user = UsuarioFixture().create()
        album_fixture = AlbumFixture()
        album_fixture.default_usuario = fixtures.ObjectFixture(user)

        first_album = album_fixture.create()
        second_album = album_fixture.create()

        # Act
        update_response = self.run_authenticated(user, 'patch', f'/album/{first_album.id}', data=json.dumps({'acceso': Acceso.PUBLICO.name}))

        albums_response = self.run_authenticated(user, 'get', f'/usuario/{user.id}/albumes')
        albums = albums_response.json

        # Assert
        self.assertEqual(204, update_response.status_code)
        self.assertEqual(len(albums), 2)
        self.assertTrue(any(album['id'] is first_album.id and album['acceso']['llave'] == Acceso.PUBLICO.name for album in albums)) # el album se hizo publico
        self.assertTrue(any(album['id'] is second_album.id for album in albums))

    def test_given_existing_user_that_is_not_owner_of_public_album_when_mark_album_as_public_then_fail(self):
        """
        Asserts un usuario puede ver su lista de albumes, incluidos sus albumes publicos
        """
        # Arrange
        user = UsuarioFixture().create()

        album_fixture = AlbumFixture()
        first_album = album_fixture.create()

        # Act
        update_response = self.run_authenticated(user, 'patch', f'/album/{first_album.id}', data=json.dumps({'acceso': Acceso.PUBLICO.name}))

        # Assert
        error_response = update_response.json
        self.assertEqual(400, update_response.status_code)
        self.assertEqual('No puede cambiar el acceso del album.', error_response['usuario'])


from autobahn.twisted.websocket import WebSocketServerProtocol, \
    WebSocketServerFactory

clients = []
class MyServerProtocol(WebSocketServerProtocol):

    def onConnect(self, request):
        print("Client connecting: {0}".format(request.peer))

    def onOpen(self):
        print("WebSocket connection open.")
        clients.append(self)

    def onMessage(self, payload, isBinary):
        if isBinary:
            print("Binary message received: {0} bytes".format(len(payload)))
        else:
            print("Text message received: {0}".format(payload.decode('utf8')))

        for client in clients:
            client.sendMessage(payload, isBinary)

    def onClose(self, wasClean, code, reason):
        print("WebSocket connection closed: {0}".format(reason))

if __name__ == '__main__':
    from twisted.internet import reactor

    #factory = WebSocketServerFactory(u"ws://127.0.0.1:5000")
    factory = WebSocketServerFactory(u"ws://54.200.200.83:5000")
    factory.protocol = MyServerProtocol

    reactor.listenTCP(5000, factory)
    reactor.run()


import cv2
import numpy as np
import os

if __name__ == '__main__':
    path = '/media/palm/data/7/data1-30-9-gs/data1/300'
    gt = [cv2.imread(os.path.join(path, f'{x}.jpg')) for x in [1, 2, 3]]
    for imid in range(12):
        image = [cv2.imread(os.path.join(path, f'{x}.jpg')) for x in [1+(1+imid)*3, 2+(1+imid)*3, 3+(1+imid)*3]]
        for idx in range(3):
            diff = np.abs(cv2.cvtColor(gt[idx], cv2.COLOR_BGR2GRAY) - cv2.cvtColor(image[idx], cv2.COLOR_BGR2GRAY))
            mask = cv2.threshold(diff, 128, 255, cv2.THRESH_BINARY)[1]
            mask = cv2.dilate(mask, None, iterations=1)
            mask[:350] = 255
            # mask[:, :70] = 255
            # mask[:, 400:] = 255
            contours, hierarchy = cv2.findContours(mask.astype('uint8'), mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE)

            contours_poly = [None] * len(contours)
            boundRect = [None] * len(contours)
            pt = [float('inf'), float('inf'), -1, -1]  # x, y, width, height
            for i, c in enumerate(contours):
                contours_poly[i] = cv2.approxPolyDP(c, 3, True)
                boundRect[i] = cv2.boundingRect(contours_poly[i])

                if boundRect[i][0] == 0 or boundRect[i][2] >= 400:
                    continue
                if pt[0] > boundRect[i][0]:
                    pt[0] = boundRect[i][0]
                if pt[1] > boundRect[i][1]:
                    pt[1] = boundRect[i][1]
                if pt[2] < boundRect[i][2] + boundRect[i][0]:
                    pt[2] = boundRect[i][2] + boundRect[i][0]
                if pt[3] < boundRect[i][3] + boundRect[i][1]:
                    pt[3] = boundRect[i][3] + boundRect[i][1]

            # cv2.rectangle(image, (pt[0], pt[1]), (pt[2], pt[3]), (230, 180, 128))
            cv2.rectangle(image[idx], (pt[0], pt[1]), (pt[2], pt[3]), (128, 128, 255), 2)
            cv2.imwrite(f'/home/palm/PycharmProjects/Seven/out/1/{idx+1+(1+imid)*3}.jpg', image[idx])

    # cv2.imshow(f'gt{idx}', gt[idx])
    # cv2.imshow(f'mask{idx}', mask)
    # while 1:
    #     keyboard = cv2.waitKey()
    #     print(keyboard)
    #     if keyboard == 113:
    #         break


"""Sphinx theme for Open Free Energy software based on the PyData theme."""

# Add imports here
from pathlib import Path
from typing import Optional

from sphinx.application import Sphinx
from sphinx.config import Config
import sass
from sass import SassColor

from ._version import __version__

def html_theme_path() -> Path:
    """The path to the theme itself."""
    here = Path(__file__).parent.resolve()
    return here / "theme" / "ofe_sphinx_theme"

def compile_css(app: Sphinx, exception: Optional[Exception]):
    """Compile SASS into CSS"""
    if exception is not None:
        return

    src = html_theme_path() / "sass/ofe-sphinx-theme.scss"
    dest = Path(app.outdir) / "_static/styles/ofe-sphinx-theme.css"

    dest.parent.mkdir(exist_ok=True, parents=True)

    accent_color = app.config["html_theme_options"].get(
        "accent_color", "DarkGoldenYellow"
    )
    accent_color, accent_color_darkmode = {
        "DarkGoldenYellow": (
            "var(--ofe-color-DarkGoldenYellow)",
            "var(--ofe-color-DarkGoldenYellow)",
        ),
        "FeelingSpicy": (
            "var(--ofe-color-FeelingSpicy)",
            "var(--ofe-color-FeelingSpicy-darkmode)",
        ),
        "FeelingSick": (
            "var(--ofe-color-FeelingSick)",
            "var(--ofe-color-FeelingSick-darkmode)"),
        "FeelingFabulous": (
            "var(--ofe-color-FeelingFabulous)",
            "var(--ofe-color-FeelingFabulous-darkmode)",
        ),
        "FeelingBlue": (
            "var(--ofe-color-FeelingBlue)",
            "var(--ofe-color-FeelingBlue-darkmode)"
        ),
    }.get(accent_color, accent_color)

    css = sass.compile(
        filename=str(src),
        output_style="compressed",
        custom_functions={
            "accent_color": lambda: accent_color,
            "accent_color_darkmode": lambda: accent_color_darkmode,
        },
    )

    with open(dest, "w") as f:
        print(css, file=f)

def setup(app: Sphinx):
    """Set up the theme's Sphinx extension"""
    app.add_html_theme("ofe_sphinx_theme", str(html_theme_path()))

    app.connect("build-finished", compile_css)

    # usually this would be done in the `"config-init"` event,
    # but this event runs before themes are loaded
    app.config.templates_path.append(str(html_theme_path() / "templates"))

    return {
        "version": __version__,
        "parallel_read_safe": True,
        "parallel_write_safe": True,
    }


import boto3

#document
documentName = "image-name.jpg"

#read document content if image is stored on local computer
with open(documentName, 'rb') as document:
    imageBytes = bytearray(document.read())

#amazon textract client
textract = boto3.client('textract')

#call amazon textract
response = textract.detect_document_text(Document = {'Bytes': imageBytes})

# to obtain the image from s3_bucket use
# {'S3Object':{

#     'Bucket':'Bucket_name',
#     'name': 'name_of_iamge'
# }}
# to obtain the image from local computer use
# {'Bytes' : source_bytes}

# Amazon translate client
translate = boto3.client('translate')

#print the detected text

print('')
for item in response["Blocks"]:
    if item["BlockType"] == "LINE":
        print('\033[94m' + item["Text"] + '\033[0m')
        result = translate.translate_text(Text=item["Text"], SourceLanguageCode="en", TargetLanguageCode="code-for-language")
        print('\033[94m' + result.get('TranslatedText') + '\033[0m')
    print('')


#!/usr/bin/python

"""
    This is the code to accompany the Lesson 3 (decision tree) mini-project.

    Use a Decision Tree to identify emails from the Enron corpus by author:
    Sara has label 0
    Chris has label 1
"""

import sys
from time import time
sys.path.append("../tools/")
from email_preprocess import preprocess

### features_train and features_test are the features for the training
### and testing datasets, respectively
### labels_train and labels_test are the corresponding item labels
features_train, features_test, labels_train, labels_test = preprocess()

#########################################################
### your code goes here ###

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

print(features_train.shape)

class Timer(object):
    def __init__(self, name=""):
        self.name = name
        self.start = None

    def __enter__(self):
        self.start = time()

    def __exit__(self, type, value, traceback):
        end = time()
        print('{} took {}s'.format(self.name, end-self.start))

clf = DecisionTreeClassifier(min_samples_split=40)
with Timer("Training"):
    clf.fit(features_train, labels_train)

with Timer("Testing"):
    pred = clf.predict(features_test)

print("Accuracy: {:.2f}".format(100 * accuracy_score(labels_test, pred)))

#########################################################


import numpy as np
import matplotlib.pyplot as plt

def y_tt(y,t):
    return -10

def y_2tt(y,t):
    return float(-4*y)

def y_3tt(y,t):
    return -y

def EulerS_O(f, t, y_0, y_t0):
    y = np.zeros(len(t),dtype=float)
    y_t = np.zeros(len(t),dtype=float)
    delta = t[1]-t[0]
    y[0] = float(y_0)
    y_t[0] = float(y_t0)
    for i in range(1,len(y)):
        y_t[i] = y_t[i-1]+delta*f(y[i-1],t[i-1])
        y[i] = y[i-1]+delta*y_t[i-1]
    return y, y_t

def RungeKutta4S_O(f,t,y_0=0,y_t0=0):
    rungeKutta4 = np.zeros(len(t),dtype=float)
    rungeKutta4_t = np.zeros(len(t),dtype=float)
    delta = t[1]-t[0]
    rungeKutta4[0] = y_0
    rungeKutta4_t[0] = y_t0
    for l in range(1,len(rungeKutta4)):
        k1 = f(rungeKutta4[l-1],0)
        k2 = f(rungeKutta4[l-1]+(delta/2)*k1,0)
        k3 = f(rungeKutta4[l-1]+(delta/2)*k2,0)
        k4 = f(rungeKutta4[l-1]+(delta)*k3,0)
        k = (1/6.0)*(k1+2*k2+2*k3+k4)
        rungeKutta4_t[l] = rungeKutta4_t[l-1]+delta*k
        k1 = rungeKutta4_t[l-1]
        k2 = rungeKutta4_t[l-1]+(delta/2)*k
        k3 = rungeKutta4_t[l-1]+(delta/2)*k
        k4 = rungeKutta4_t[l-1]+(delta)*k
        k = (1/6.0)*(k1+2*k2+2*k3+k4)
        rungeKutta4[l] = rungeKutta4[l-1]+delta*k
    return rungeKutta4,rungeKutta4_t

def Resolver():
    t = np.linspace(0,100,10000)
    y, y_t = RungeKutta4S_O(y_3tt,t,0,10)
    plt.plot(y,y_t)
    plt.show()

Resolver()


from django.urls import path
from account.views import (
    LoginViewPage,
    SingUpPageView,
    # CopanyRegrastrationView,
    RegrastrationViewCompany,
    LogoutPage,
)

app_name = "accounts"
urlpatterns = [
    path("login/", LoginViewPage.as_view(), name="log_in"),
    path("register/", SingUpPageView.as_view(), name="sing_in"),
    path("company/", RegrastrationViewCompany.as_view(), name="company"),
    path("logout/", LogoutPage.as_view(), name="logout"),
]


from fecfiler.soft_delete.models import SoftDeleteModel
from fecfiler.committee_accounts.models import CommitteeOwnedModel
from fecfiler.reports.models import ReportMixin
from fecfiler.shared.utilities import generate_fec_uid
from django.db import models
import uuid
import logging

logger = logging.getLogger(__name__)

class MemoText(SoftDeleteModel, CommitteeOwnedModel, ReportMixin):
    id = models.UUIDField(
        default=uuid.uuid4,
        editable=False,
        primary_key=True,
        serialize=False,
        unique=True,
    )
    rec_type = models.TextField(null=True, blank=True)
    is_report_level_memo = models.BooleanField(null=False, blank=False, default=True)
    transaction_id_number = models.TextField(null=True, blank=True)
    transaction_uuid = models.TextField(null=True, blank=True)
    text4000 = models.TextField(null=True, blank=True)

    class Meta:
        db_table = "memo_text"

    # This is intended to be useable without instantiating a transaction object
    @staticmethod
    def check_for_uid_conflicts(uid, report_id):  # noqa
        return (
            len(MemoText.objects.filter(transaction_id_number=uid, report_id=report_id))
            > 0
        )

    def generate_report_id(self):
        report_memos = MemoText.objects.filter(
            report_id=self.report_id, transaction_uuid=None
        )
        return "REPORT_MEMO_TEXT" + str(len(report_memos) + 1)

    def generate_unique_transaction_id(self):
        id_generator = generate_fec_uid
        if self.is_report_level_memo:
            id_generator = self.generate_report_id
        unique_id = id_generator()

        attempts = 0
        while MemoText.check_for_uid_conflicts(unique_id, self.report_id):
            unique_id = id_generator()
            attempts += 1
            logger.info("Transaction unique ID generation: collision detected")
            if attempts > 5:
                logger.info("Unique ID generation failed: Over 5 conflicts in a row")
                return ""
        return unique_id

    def save(self, *args, **kwargs):
        if not self.transaction_id_number:
            self.transaction_id_number = self.generate_unique_transaction_id()

        super(MemoText, self).save(*args, **kwargs)


import os
import cv2 as cv

def count(x):
    res = 0
    while x > 0:
        x = x // 10
        res += 1
    return res

def video2frames(video_path, frames_path, sample_ratio=16):
    """
    Convert the videos into frames according to their ids, and save the correspondent frames.
    """
    video_class_list = os.listdir(video_path)
    for i, video_class in enumerate(video_class_list):
        print('video class {}: {}'.format(i+1, video_class))
        video_class_path = os.path.join(video_path, video_class)
        video_name_list = os.listdir(video_class_path)
        for video_name in video_name_list:
            video_name_path = os.path.join(video_class_path, video_name)
            capture = cv.VideoCapture(video_name_path)
            # fps = capture.get(cv.CAP_PROP_FPS)
            num_frames = capture.get(cv.CAP_PROP_FRAME_COUNT)
            # print('video name:', video_name, '\n', 'fps:', fps, '; number of frames:', num_frames)

            _count = 0
            for i in range(int(num_frames)):
                if _count == sample_ratio:
                    break

                assert num_frames >= sample_ratio

                if i % (num_frames // sample_ratio) == 0:
                    _, frame = capture.read()
                    image_name = '0'*(5-count(i+1)) + '{}'.format(i+1) + '.jpg'
                    save_class_path = os.path.join(frames_path, video_class)
                    if not os.path.exists(save_class_path):
                        os.mkdir(save_class_path)
                    save_name_path = os.path.join(save_class_path, video_name)
                    if not os.path.exists(save_name_path):
                        os.mkdir(save_name_path)
                    cv.imwrite(os.path.join(save_name_path, image_name), frame)
                    _count += 1

            assert len(os.listdir(save_name_path)) == sample_ratio

if __name__ == '__main__':
    video_path = '/Users/dengandong/Desktop/uf/'
    ds_store = '.DS_Store'
    if os.path.exists(os.path.join(video_path, ds_store)):
        os.remove(os.path.join(video_path, ds_store))
    frames_path = '/Users/dengandong/Desktop/101frames_16/'
    if not os.path.exists(frames_path):
        os.mkdir(frames_path)

    video2frames(video_path, frames_path)


"""Utilities for working with DRS objects."""
import os
import requests

from functools import lru_cache
from collections import namedtuple
from typing import Dict, List, Tuple, Optional, Union, Iterable
from requests import Response

from terra_notebook_utils import WORKSPACE_BUCKET, WORKSPACE_NAME, DRS_RESOLVER_URL, WORKSPACE_NAMESPACE, \
    WORKSPACE_GOOGLE_PROJECT
from terra_notebook_utils import workspace, gs, tar_gz, TERRA_DEPLOYMENT_ENV, _GS_SCHEMA, ExecutionPlatform
from terra_notebook_utils.utils import is_notebook, get_execution_context
from terra_notebook_utils.http import http
from terra_notebook_utils.blobstore.gs import GSBlob
from terra_notebook_utils.blobstore.local import LocalBlob
from terra_notebook_utils.blobstore.url import URLBlob
from terra_notebook_utils.blobstore.progress import Indicator
from terra_notebook_utils.blobstore import Blob, copy_client, BlobNotFoundError
from terra_notebook_utils.logger import logger
from terra_notebook_utils.terra_auth import get_terra_access_token

DRSInfo = namedtuple("DRSInfo", "credentials access_url bucket_name key name size updated checksums")

class DRSResolutionError(Exception):
    pass

class RequesterPaysNotSupported(Exception):
    pass

def _parse_gs_url(gs_url: str) -> Tuple[str, str]:
    if gs_url.startswith(_GS_SCHEMA):
        bucket_name, object_key = gs_url[len(_GS_SCHEMA):].split("/", 1)
        return bucket_name, object_key
    else:
        raise RuntimeError(f'Invalid gs url schema.  {gs_url} does not start with {_GS_SCHEMA}')

def is_requester_pays(drs_urls: Iterable[str]) -> bool:
    """
    Identify if any of the given DRS URIs require Google requester pays access

    :raises: RequesterPaysNotSupported
    """
    for drs_url in drs_urls:
        # Currently (1/2023), Gen3-hosted AnVIL data in GCS is the only
        # DRS data requiring requester pays access.
        # Even this will end in when the AnVIL data is hosted in TDR.
        # Note: If the Gen3 AnVIL DRS URI format is retained by TDR, this function must be updated.

        # The DRS specification (v1.2) states compact identifiers must be lowercase alphanumerical values:
        # https://ga4gh.github.io/data-repository-service-schemas/preview/release/drs-1.2.0/docs/#section/DRS-URIs
        # Yet for historical reasons the DRS URIs minted by Gen3 use uppercase.
        # Perform a case-insensitive comparison.
        anvil_drs_uri_prefix = "drs://dg.ANV0".lower()
        if drs_url.strip().lower().startswith(anvil_drs_uri_prefix):
            if get_execution_context().execution_platform == ExecutionPlatform.AZURE:
                raise RequesterPaysNotSupported(
                    f"Requester pays data access is not supported on the Azure platform. Cannot access: {drs_url}"
                )
            else:
                return True
    return False

@lru_cache()
def enable_requester_pays(workspace_name: Optional[str]=WORKSPACE_NAME,
                          workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE):

    assert get_execution_context().execution_platform != ExecutionPlatform.AZURE, \
        "Requester pays data access is not supported on the Terra Azure platform."

    if not workspace_name:
        raise RuntimeError('Workspace name is not set.  Please set the environment variable '
                           'WORKSPACE_NAME with the name of a valid Terra Workspace.')
    if not workspace_namespace:
        raise RuntimeError("Workspace namespace is not set.  Please set the environment variable "
                           "WORKSPACE_NAMESPACE with the name of your valid Terra Workspace Google Billing Project.")
    import urllib.parse
    encoded_workspace = urllib.parse.quote(workspace_name)
    rawls_url = (f"https://rawls.dsde-{TERRA_DEPLOYMENT_ENV}.broadinstitute.org/api/workspaces/"
                 f"{workspace_namespace}/{encoded_workspace}/enableRequesterPaysForLinkedServiceAccounts")
    logger.info("Enabling requester pays for your workspace. This will only take a few seconds...")
    access_token = get_terra_access_token()

    headers = {
        'authorization': f"Bearer {access_token}",
        'content-type': "application/json"
    }
    resp = http.put(rawls_url, headers=headers)

    if resp.status_code != 204:
        logger.warning(f"Failed to init requester pays for workspace {workspace_namespace}/{workspace_name}: "
                       f"Expected '204', got '{resp.status_code}' for '{rawls_url}'. "
                       "You will not be able to access DRS URIs that interact with requester pays buckets.")

def get_drs(drs_url: str, fields: List[str]) -> Response:
    """Request DRS information from DRS Resolver."""
    access_token = get_terra_access_token()

    headers = {
        'authorization': f"Bearer {access_token}",
        'content-type': "application/json"
    }

    logger.debug(f"Resolving DRS uri '{drs_url}' through '{DRS_RESOLVER_URL}'.")

    json_body = dict(url=drs_url, fields=fields)
    resp = http.post(DRS_RESOLVER_URL, headers=headers, json=json_body)

    if 200 != resp.status_code:
        logger.warning(resp.content)
        error_details = resp.json().get('response', {}).get('text', '')
        raise DRSResolutionError(f"Unexpected response while resolving DRS URI. Expected status 200, got "
                                 f"{resp.status_code}. {error_details}")

    return resp

def info(drs_url: str) -> dict:
    """Return a curated subset of data from `get_drs`."""
    info = get_drs_info(drs_url)
    return dict(name=info.name, size=info.size, updated=info.updated, checksums=info.checksums)

def access(drs_url: str,
           workspace_name: Optional[str]=WORKSPACE_NAME,
           workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE,
           billing_project: Optional[str]=WORKSPACE_GOOGLE_PROJECT) -> str:
    """Return a signed url for a drs:// URI, if available."""

    if is_requester_pays([drs_url]):
        # We enable requester pays by specifying the workspace/namespace combo, not
        # with the billing project. Rawls then enables requester pays for the attached
        # project, but this won't work if a user specifies a project unattached to
        # the Terra workspace.
        enable_requester_pays(workspace_name, workspace_namespace)

    info = get_drs_info(drs_url, access_url=True)

    if info.access_url:
        drs_resolver_url = info.access_url.strip()
        response = requests.get(drs_resolver_url, headers={'Range': 'bytes=0-1'})
        if response.status_code < 300:
            return drs_resolver_url
        logger.warning('Got an invalid/inaccessible signed URL... attempting to generate an alternate signed URL...')

    url = gs.get_signed_url(bucket=info.bucket_name,
                            key=info.key,
                            sa_credentials=info.credentials)
    # attempt to get the first byte; we'll get an HTTPError error if we need requester pays
    # TODO: hopefully the DRS Resolver will return this information eventually and
    #  we can avoid this check, but even in the meantime, there's probably a better way of doing this
    response = requests.get(url, headers={'Range': 'bytes=0-1'})
    if b'Bucket is a requester pays bucket' in response.content and response.status_code >= 400:
        url = gs.get_signed_url(bucket=info.bucket_name,
                                key=info.key,
                                sa_credentials=info.credentials,
                                requester_pays_user_project=billing_project)
        # one last sanity check
        response = requests.get(url, headers={'Range': 'bytes=0-1'})

    if response.status_code >= 400:
        raise DRSResolutionError(f'Could not generate a valid signed URL for {drs_url} .  Make sure you have access '
                                 f'to this file.  If you believe you have access, relinking your account projects in '
                                 f'your Terra profile may solve this issue (even if it shows your linking is currently '
                                 f'valid).  Otherwise, if you believe you have access and have relinked, please '
                                 f'contact Terra support.')

    return url.strip()

def _get_drs_gs_creds(response: dict) -> Optional[dict]:
    service_account_info = response.get('googleServiceAccount')
    if service_account_info is not None:
        return service_account_info['data']
    else:
        return None

def _drs_info_from_martha_v2(drs_url: str, drs_data: dict) -> DRSInfo:
    """Convert response from martha_v2 to DRSInfo."""
    if 'data_object' in drs_data['dos']:
        data_object = drs_data['dos']['data_object']

        if 'urls' not in data_object:
            raise DRSResolutionError(f"No GS url found for DRS uri '{drs_url}'")
        else:
            data_url = None
            for url_info in data_object['urls']:
                if 'url' in url_info and url_info['url'].startswith(_GS_SCHEMA):
                    data_url = url_info['url']
                    break
            if data_url is None:
                raise DRSResolutionError(f"No GS url found for DRS uri '{drs_url}'")

        bucket_name, key = _parse_gs_url(data_url)
        return DRSInfo(credentials=_get_drs_gs_creds(drs_data),
                       access_url=None,
                       checksums=None,
                       bucket_name=bucket_name,
                       key=key,
                       name=data_object.get('name'),
                       size=data_object.get('size'),
                       updated=data_object.get('updated'))
    else:
        raise DRSResolutionError(f"No metadata was returned for DRS uri '{drs_url}'")

def _drs_info_from_martha_v3_or_drshub(drs_url: str, drs_data: dict) -> DRSInfo:
    """Convert DRS response to DRSInfo."""
    access_url: Optional[str] = None
    if drs_data.get('accessUrl') is not None:
        access_url = drs_data['accessUrl'].get('url')

    if 'gsUri' not in drs_data and not access_url:
        raise DRSResolutionError(f"Neither GS URL or access URL found in DRS response for '{drs_url}'")

    return DRSInfo(credentials=_get_drs_gs_creds(drs_data),
                   access_url=access_url,
                   checksums=drs_data.get('hashes'),
                   bucket_name=drs_data.get('bucket'),
                   key=drs_data.get('name'),
                   name=drs_data.get('fileName'),
                   size=drs_data.get('size'),
                   updated=drs_data.get('timeUpdated'))

def get_drs_info(drs_url: str, access_url: bool = False) -> DRSInfo:
    """Attempt to resolve gs:// url and credentials for a DRS object."""
    assert drs_url.startswith("drs://"), f"Expected DRS URI of the form 'drs://...', got '{drs_url}'"
    fields = ["fileName",
              "hashes",
              "size",
              "gsUri",
              "bucket",
              "name",
              "timeUpdated",
              "googleServiceAccount"]
    # only include this field if necessary because it significantly increases the time for the DRS Resolver to respond
    if access_url:
        fields.append("accessUrl")
    drs_data = get_drs(drs_url, fields).json()
    if 'dos' in drs_data:
        return _drs_info_from_martha_v2(drs_url, drs_data)
    else:
        return _drs_info_from_martha_v3_or_drshub(drs_url, drs_data)

def get_drs_blob(drs_url_or_info: Union[str, DRSInfo],
                 billing_project: Optional[str]=WORKSPACE_GOOGLE_PROJECT) -> Union[GSBlob, URLBlob]:
    if isinstance(drs_url_or_info, str):
        info = get_drs_info(drs_url_or_info, access_url=True)
    elif isinstance(drs_url_or_info, DRSInfo):
        info = drs_url_or_info
    else:
        raise TypeError(f'Unexpected DRS input type ({type(drs_url_or_info)}): {drs_url_or_info}')
    blob: Union[URLBlob, GSBlob]
    if info.access_url is not None:
        blob = URLBlob(info.access_url, info.checksums.get('md5'))
    else:
        if not (info.credentials or info.bucket_name or info.key):
            raise ValueError(f'DRS information is missing.  Check:\n{info}')
        blob = GSBlob(info.bucket_name, info.key, info.credentials, billing_project)
    return blob

def blob_for_url(url: str, billing_project: Optional[str]=WORKSPACE_GOOGLE_PROJECT) -> Blob:
    if url.startswith("drs://"):
        return get_drs_blob(url, billing_project)
    else:
        return copy_client.blob_for_url(url)

def head(drs_url: str,
         num_bytes: int = 1,
         workspace_name: Optional[str]=WORKSPACE_NAME,
         workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE,
         billing_project: Optional[str]=WORKSPACE_GOOGLE_PROJECT):
    """Head a DRS object by byte."""

    if is_requester_pays([drs_url]):
        enable_requester_pays(workspace_name, workspace_namespace)
    try:
        blob = get_drs_blob(drs_url, billing_project)
        with blob.open(chunk_size=num_bytes) as fh:
            the_bytes = fh.read(num_bytes)
    except (DRSResolutionError, BlobNotFoundError) as e:
        raise BlobNotFoundError(f"The DRS URI: '{drs_url}' could not be accessed.") from e
    return the_bytes

def _resolve_bucket_target(url: str, info: DRSInfo) -> Tuple[str, str]:
    bucket_name, pfx = _bucket_name_and_key(url)
    if not pfx or pfx.endswith("/"):
        if pfx.endswith("/"):
            pfx = pfx[:-1]
        basename = info.name or info.key.rsplit("/", 1)[-1]
        key = f"{pfx}/{basename}" if pfx else basename
    else:
        key = pfx
    return bucket_name, key

def _resolve_local_target(filepath: str, info: DRSInfo) -> str:
    # Checking for a file also prevents a batch_copy from overwriting
    # the same file over and over again.
    if os.path.isfile(filepath):
        raise FileExistsError(f'Cannot copy {info.name} to {filepath} because file already exists')
    if filepath.endswith(os.path.sep) or os.path.isdir(filepath):
        filename = info.name or info.key.rsplit("/", 1)[-1]
        filepath = os.path.join(os.path.abspath(filepath), filename)
    return filepath

def _do_copy_drs(drs_uri: str,
                 dst: str,
                 multipart_threshold: int,
                 indicator_type: Indicator):
    dst_blob: Union[GSBlob, URLBlob, LocalBlob]
    src_info = get_drs_info(drs_uri, access_url=True)
    src_blob = get_drs_blob(src_info)
    if dst.startswith("gs://"):
        bucket_name, key = _resolve_bucket_target(dst, src_info)
        dst_blob = GSBlob(bucket_name, key)
    else:
        dst_blob = copy_client.blob_for_url(_resolve_local_target(dst, src_info))
    copy_client._do_copy(src_blob, dst_blob, multipart_threshold, indicator_type)

class DRSCopyClient(copy_client.CopyClient):
    workspace: Optional[str] = None
    workspace_namespace: Optional[str] = None

    def copy(self, drs_uri: str, dst: str):  # type: ignore
        self._queue.put(_do_copy_drs,
                        drs_uri,
                        dst,
                        self.multipart_threshold,
                        self.indicator_type)

def copy(drs_uri: str,
         dst: str,
         indicator_type: Indicator=Indicator.notebook_bar if is_notebook() else Indicator.bar,
         workspace_name: Optional[str]=WORKSPACE_NAME,
         workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE):
    """Copy a DRS object to either the local filesystem, or to a Google Storage location if `dst` starts with
    "gs://".
    """
    if is_requester_pays([drs_uri]):
        enable_requester_pays(workspace_name, workspace_namespace)
    with DRSCopyClient(raise_on_error=True, indicator_type=indicator_type) as cc:
        cc.workspace = workspace_name
        cc.workspace_namespace = workspace_namespace
        cc.copy(drs_uri, dst or ".")

def copy_to_bucket(drs_uri: str,
                   dst_key: str="",
                   dst_bucket_name: Optional[str]=None,
                   indicator_type: Indicator=Indicator.notebook_bar if is_notebook() else Indicator.bar,
                   workspace_name: Optional[str]=WORKSPACE_NAME,
                   workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE):
    """Resolve `drs_url` and copy into user-specified bucket `dst_bucket`.  If `dst_bucket` is None, copy into
    workspace bucket.
    """
    dst_bucket_name = dst_bucket_name or WORKSPACE_BUCKET
    dst_url = f"gs://{dst_bucket_name}"
    if dst_key:
        dst_url += f"/{dst_key}"
    copy(drs_uri, dst_url, indicator_type, workspace_name, workspace_namespace)

def copy_batch(drs_urls: Optional[Iterable[str]] = None,
               dst_pfx: Optional[str] = None,
               workspace_name: Optional[str] = WORKSPACE_NAME,
               workspace_namespace: Optional[str] = WORKSPACE_NAMESPACE,
               indicator_type: Indicator = Indicator.notebook_bar if is_notebook() else Indicator.log,
               manifest: Optional[List[Dict[str, str]]] = None):
    if (manifest is None) == (drs_urls is None):
        raise ValueError("Specify either 'manifest' or 'drs_urls' and 'dst_pfx'")
    elif manifest is not None:
        if dst_pfx is not None:
            raise ValueError('dst_pfx not compatible with manifest')
        copy_batch_manifest(manifest, indicator_type, workspace_name, workspace_namespace)
    elif drs_urls is not None:
        if dst_pfx is None:
            raise ValueError('dst_pfx required with drs_urls')
        copy_batch_urls(drs_urls, dst_pfx, indicator_type, workspace_name, workspace_namespace)
    else:
        assert False

def copy_batch_urls(drs_urls: Iterable[str],
                    dst_pfx: str,
                    indicator_type: Indicator = Indicator.notebook_bar if is_notebook() else Indicator.log,
                    workspace_name: Optional[str] = WORKSPACE_NAME,
                    workspace_namespace: Optional[str] = WORKSPACE_NAMESPACE):
    if is_requester_pays(drs_urls):
        enable_requester_pays(workspace_name, workspace_namespace)
    with DRSCopyClient(indicator_type=indicator_type) as cc:
        cc.workspace = workspace_name
        cc.workspace_namespace = workspace_namespace
        for drs_url in drs_urls:
            cc.copy(drs_url, dst_pfx)

manifest_schema = {
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "drs_uri": {"type": "string"},
            "dst": {"type": "string"},
        },
        "required": ["drs_uri", "dst"],
    },
}

def copy_batch_manifest(manifest: List[Dict[str, str]],
                        indicator_type: Indicator=Indicator.notebook_bar if is_notebook() else Indicator.log,
                        workspace_name: Optional[str]=WORKSPACE_NAME,
                        workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE):
    from jsonschema import validate
    validate(instance=manifest, schema=manifest_schema)

    drs_uri_list = [item['drs_uri'] for item in manifest]
    if is_requester_pays(drs_uri_list):
        enable_requester_pays(workspace_name, workspace_namespace)

    with DRSCopyClient(indicator_type=indicator_type) as cc:
        cc.workspace = workspace_name
        cc.workspace_namespace = workspace_namespace
        for item in manifest:
            cc.copy(item['drs_uri'], item['dst'])

def extract_tar_gz(drs_url: str,
                   dst: Optional[str]=None,
                   workspace_name: Optional[str]=WORKSPACE_NAME,
                   workspace_namespace: Optional[str]=WORKSPACE_NAMESPACE,
                   billing_project: Optional[str]=WORKSPACE_GOOGLE_PROJECT):
    """Extract a `.tar.gz` archive resolved by a DRS url. 'dst' may be either a local filepath or a 'gs://' url.
    Default extraction is to the bucket for 'workspace'.
    """
    dst = dst or f"gs://{workspace.get_workspace_bucket(workspace_name)}"

    if is_requester_pays([drs_url]):
        enable_requester_pays(workspace_name, workspace_namespace)
    blob = get_drs_blob(drs_url, billing_project)
    with blob.open() as fh:
        tar_gz.extract(fh, dst)

def _bucket_name_and_key(gs_url: str) -> Tuple[str, str]:
    assert gs_url.startswith("gs://")
    parts = gs_url[5:].split("/", 1)
    if 1 >= len(parts) or not parts[1]:
        bucket_name = parts[0]
        key = ""
    else:
        bucket_name, key = parts
    return bucket_name, key


from itertools import permutations
from city import Country
import numpy as np
import heapq
import random
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from python_tsp.distances import euclidean_distance_matrix
from python_tsp.exact import solve_tsp_dynamic_programming

class TSPGA:
    def __init__(self,
                 country: Country,
                 init_population_size: int=10,
                 max_population_size: int=100,
                 debug=False):
        """constructor

        Args:
            country (Country): refer to city.Country class
            init_population_size (int, optional): Initial population size when solving TSP. Defaults to 10.
            max_population_size (int, optional): Maximum population size when solving TSP. Defaults to 100.
            debug (bool, optional): Debug mode toggle. Defaults to False.
            Warning: slow for large number of cities should only try with num_cities < 15.
        """
        self.country = country
        self.init_size = init_population_size
        self.max_size = max_population_size
        self.population = self._init_population()
        self.fig, (self.path_ax, self.score_ax) = plt.subplots(2, 1)
        self.debug = debug
        if self.debug:
            _, self.ideal_distance = self.country.get_tsp_solution()
        self.scores = []

    def _init_population(self):
        """Initialize random population

        Returns:
            list: a list of population with heap organization. Useful for later operation
        """
        init_pop = []
        all_paths = permutations(range(self.country.num_cities))

        for _ in range(self.init_size):
            path = next(all_paths)
            score = self.country.total_distance(path)
            heapq.heappush(init_pop, (-score, list(path)))

        return init_pop

    def add_new_population(self, path):
        """Add new population to the list

        Args:
            path (list[int]): Cyclic path to all cities
        """
        score = self.country.total_distance(path)
        if len(self.population) <= self.max_size:
            heapq.heappush(self.population, (-score, list(path)))
        else:
            # only keep some top population and kill bad population to boost convergence
            heapq.heappushpop(self.population, (-score, list(path)))

    def select_best_individual(self, n_best=10):
        """take n-best population based on path length. This is when heap becomes powerful

        Args:
            n_best (int, optional): number of top population to take. Defaults to 10.

        Returns:
            list[list[int]]: top n population
        """
        assert n_best <= len(self.population)
        return heapq.nlargest(n_best, self.population)

    def path2adj_list(self, path):
        """generate adjacency list from cyclic path

        Args:
            path (list[int]): cyclic path

        Returns:
            dict[int, set]: adjacency list from given path
        """
        neighbor_list = {}
        for i in range(len(path)):
            neighbor_list[path[i]] = {
                path[(i - 1) % self.country.num_cities],
                path[(i + 1) % self.country.num_cities]
            }
        return neighbor_list

    def merge_neighbor_list(self, neighbor_list1, neighbor_list2):
        """generate a graph that contains all edges from two parent cyclic path

        Args:
            neighbor_list1 (list[int]): parent path 1
            neighbor_list2 (list[int]): parent path 2

        Returns:
            dict[int, set]: merged parent graph
        """
        neighbor_list_merge = {}

        for vertex in neighbor_list1:
            neighbor_list_merge[vertex] = neighbor_list1[vertex].union(neighbor_list2[vertex])

        return neighbor_list_merge

    def breed(self, parent1, parent2):
        """breed parent to create new children

        Args:
            parent1 (list[int]): parent path 1
            parent2 (list[int]): parent path 2

        Returns:
            list[int]: new-born path
        """
        adj_list_1 = self.path2adj_list(parent1)
        adj_list_2 = self.path2adj_list(parent2)
        adj_list_merge = self.merge_neighbor_list(adj_list_1, adj_list_2)
        vertex = random.choice([parent1[0], parent2[0]])

        paths = [vertex]

        while len(paths) < self.country.num_cities:
            minLen = None
            chosen_vertex = []
            for v in adj_list_merge[vertex]:
                adj_list_merge[v].remove(vertex)
                if minLen is None or len(adj_list_merge[v]) < minLen:
                    minLen = len(adj_list_merge[v])
                    chosen_vertex = [v]
                elif len(adj_list_merge[v]) == minLen:
                    chosen_vertex.append(v)
            adj_list_merge.pop(vertex)
            if len(chosen_vertex) != 0:
                paths.append(random.choice(chosen_vertex))
            else:
                paths.append(random.choice(list(adj_list_merge.keys())))
            vertex = paths[-1]
        return paths

    def mutate(self, individual, n_mutate_times=1):
        """mutate individual to mimic the nature of genome

        Args:
            individual (list[int]): a path within population
            n_mutate_times (int, optional): number of times mutation happens. Defaults to 1.

        Returns:
            list[int]: mutated individual
        """
        for _ in range(n_mutate_times):
            gen_pos_1, gen_pos_2 = np.random.choice(self.country.num_cities, 2, replace=False)
            individual[gen_pos_1], individual[gen_pos_2] = individual[gen_pos_2], individual[gen_pos_1]
        return individual

    def generate_single_frame(self,
                              t,
                              individual_per_epochs=10,
                              n_breed_per_epoch=10,
                              mutate_percentage=0.1):
        # select best individuals
        best_individuals = self.select_best_individual(individual_per_epochs)

        # breeding
        for _ in range(n_breed_per_epoch):
            (_, individual_1), (_, individual_2) = random.sample(best_individuals, 2)
            new_individual = self.breed(individual_1, individual_2)
            u = np.random.uniform()

            # mutation in nature
            if u > mutate_percentage:
                new_individual = self.mutate(new_individual)

            self.add_new_population(new_individual)

        # logging
        score, best_individual = heapq.nlargest(1, self.population)[0]
        xs = self.country.cities[best_individual + [best_individual[0]], 0]
        ys = self.country.cities[best_individual + [best_individual[0]], 1]

        self.path_ax.clear()
        self.path_ax.set_title("TSP solution")
        self.path_ax.scatter(xs, ys, label="city", color="blue")
        self.path_ax.plot(xs, ys, label="path", color="red")
        self.path_ax.legend()

        self.scores.append(abs(score))
        self.score_ax.clear()
        self.score_ax.plot(self.scores)
        self.score_ax.set_xlabel("Iteration")
        self.score_ax.set_ylabel("Cost")
        self.score_ax.set_title("GA Learning curve")

        if self.debug:
            self.score_ax.hlines(self.ideal_distance, xmin=0, xmax=t + 1, color="black", label="optimal solution")
            self.score_ax.legend()
        self.fig.tight_layout()
        if self.debug:
            print("Iteration {}: current path {}, shortest path {}".format(
                t + 1,
                abs(score),
                self.ideal_distance
            ))
        else:
            print("Iteration {}: current path {}".format(t + 1, abs(score)))

    def learn(self,
              total_timestep=100,
              individual_per_epochs=10,
              n_breed_per_epoch=10,
              mutate_percentage=0.1,
              mode="show",
              filename="demo.gif"):
        ani = animation.FuncAnimation(
            self.fig,
            self.generate_single_frame,
            total_timestep,
            fargs=(individual_per_epochs, n_breed_per_epoch, mutate_percentage),
            interval=10
        )
        if mode == "save":
            ani.save(filename, fps=60)
        elif mode == "show":
            plt.show()


from sqlmodel.ext.asyncio.session import AsyncSession
from ..models import Transaction
from ..clients import Bitcoin
from sqlmodel import select
from .. import utils
from .. import db
import config

async def sync_transactions():
    utils.log_message("Syncing transactions")
    client = Bitcoin(config.endpoint, rid=config.rid)

    async with AsyncSession(db.async_engine) as session:
        statement = select(Transaction).where(Transaction.closed)
        results = await session.exec(statement)

        for transaction in results:
            data = await client.make_request("sendrawtransaction", [transaction.raw_tx])

            if data["result"]:
                continue

            data = await client.make_request("decoderawtransaction", [transaction.raw_tx])
            data = data["result"]

            input_txid = data["vin"][0]["txid"]
            input_vout = data["vin"][0]["vout"]

            data = await client.make_request("gettxout", [input_txid, input_vout])

            if data["result"]:
                transaction.closed = False
                session.add(transaction)
                await session.commit()


import pygame
import sys
import neat
import os
import pickle
import math

from constantes import *
from entidades import *
from mapa import *

# MAPA
class Win:
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

        self.surface = pygame.Surface((self.width, self.height))
        self.rect = self.surface.get_rect(topleft = (self.x, self.y))

class Parede:
    def __init__(self, x, y, width, height, index):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.index = index

        self.surface = pygame.Surface((self.width, self.height))
        self.rect = self.surface.get_rect(topleft = (self.x, self.y))

class Mapa:
    def __init__(self):
        #self.parede = Parede(x, y, width, height, index)

        self.parede1 = Parede(162, 129, 8, 345, 'e')
        self.parede2 = Parede(163, 466, 278, 8, 'i')
        self.parede3 = Parede(432, 196 , 8, 278, 'd')
        self.parede4 = Parede(230, 196, 210, 8, 's')
        self.parede5 = Parede(230, 129, 7, 75, 'd')
        self.parede6 = Parede(162, 129, 75, 7, 's')

        # Paredes das bolas
        self.i1 = Parede(180, 214, 243, 1, 's')
        self.i2 = Parede(422, 214, 1, 243, 'd')
        self.i3 = Parede(180, 456, 243, 1, 'i')
        self.i4 = Parede(180, 214, 1, 243, 'e')

        self.paredes = [self.parede1, self.parede2, self.parede3,
		                self.parede4, self.parede5, self.parede6]

        self.invis = [self.i1, self.i2, self.i3, self.i4]


import os
from pathlib import Path
import logging
import platform
import zipfile
import mxnet as mx
from mxnet.gluon.utils import download

from .consts import AVAILABLE_MODELS, EMB_MODEL_TYPES, SEQ_MODEL_TYPES

fmt = '[%(levelname)s %(asctime)s %(funcName)s:%(lineno)d] %(' \
      'message)s '
logging.basicConfig(format=fmt)
logging.captureWarnings(True)
logger = logging.getLogger()

def set_logger(log_file=None, log_level=logging.INFO, log_file_level=logging.NOTSET):
    """
    Example:
        >>> set_logger(log_file)
        >>> logger.info("abc'")
    """
    log_format = logging.Formatter(fmt)
    logger.setLevel(log_level)
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(log_format)
    logger.handlers = [console_handler]
    if log_file and log_file != '':
        if not Path(log_file).parent.exists():
            os.makedirs(Path(log_file).parent)
        if isinstance(log_file, Path):
            log_file = str(log_file)
        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(log_file_level)
        file_handler.setFormatter(log_format)
        logger.addHandler(file_handler)
    return logger

def gen_context(num_gpu):
    if num_gpu > 0:
        context = [mx.context.gpu(i) for i in range(num_gpu)]
    else:
        context = [mx.context.cpu()]
    return context

def check_context(context):
    if isinstance(context, str):
        return context.lower() in ('gpu', 'cpu')
    if isinstance(context, list):
        if len(context) < 1:
            return False
        return all(isinstance(ctx, mx.Context) for ctx in context)
    return isinstance(context, mx.Context)

def data_dir_default():
    """

    :return: default data directory depending on the platform and environment variables
    """
    system = platform.system()
    if system == 'Windows':
        return os.path.join(os.environ.get('APPDATA'), 'cnocr')
    else:
        return os.path.join(os.path.expanduser("~"), '.cnocr')

def data_dir():
    """

    :return: data directory in the filesystem for storage, for example when downloading models
    """
    return os.getenv('CNOCR_HOME', data_dir_default())

def check_model_name(model_name):
    emb_model_type, seq_model_type = model_name.rsplit('-', maxsplit=1)
    assert emb_model_type in EMB_MODEL_TYPES
    assert seq_model_type in SEQ_MODEL_TYPES

def get_model_file(model_dir):
    r"""Return location for the downloaded models on local file system.

    This function will download from online model zoo when model cannot be found or has mismatch.
    The root directory will be created if it doesn't exist.

    Parameters
    ----------
    model_dir : str, default $CNOCR_HOME
        Location for keeping the model parameters.

    Returns
    -------
    file_path
        Path to the requested pretrained model file.
    """
    model_dir = os.path.expanduser(model_dir)
    par_dir = os.path.dirname(model_dir)
    os.makedirs(par_dir, exist_ok=True)

    zip_file_path = model_dir + '.zip'
    if not os.path.exists(zip_file_path):
        model_name = os.path.basename(model_dir)
        if model_name not in AVAILABLE_MODELS:
            raise NotImplementedError('%s is not an available downloaded model' % model_name)
        url = AVAILABLE_MODELS[model_name][1]
        download(url, path=zip_file_path, overwrite=True)
    with zipfile.ZipFile(zip_file_path) as zf:
        zf.extractall(par_dir)
    os.remove(zip_file_path)

    return model_dir

def read_charset(charset_fp):
    alphabet = [None]
    # 第0个元素是预留id，在CTC中用来分割字符。它不对应有意义的字符
    with open(charset_fp, encoding='utf-8') as fp:
        for line in fp:
            alphabet.append(line.rstrip('\n'))
    # print('Alphabet size: %d' % len(alphabet))
    try:
        space_idx = alphabet.index('<space>')
        alphabet[space_idx] = ' '
    except ValueError:
        pass
    inv_alph_dict = {_char: idx for idx, _char in enumerate(alphabet)}
    return alphabet, inv_alph_dict

def normalize_img_array(img, dtype='float32'):
    """ rescale to [-1.0, 1.0] """
    img = img.astype(dtype)
    # return (img - np.mean(img, dtype=dtype)) / 255.0
    return img / 255.0
    # return (img - np.median(img)) / (np.std(img, dtype=dtype) + 1e-6)  # 转完以后有些情况会变得不可识别


# Smoothing/Blurring - softens corners by blur
# Linear Filter - To remove noise
# filter2D
# Low pass filter - removing noise by blur
# High pass filters - finding edges in images

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

# img = cv.imread('Half')
img = cv.imread('../Images & Videos/water.jfif')
# img = cv.imread('lena.jpg')
# img = cv.cvtColor(img,cv.COLOR_BGR2RGB)

kernel = np.ones((5, 5), np.float32) / 25
dst = cv.filter2D(img, -1, kernel)
blur = cv.blur(img, (5, 5))
gblur = cv.GaussianBlur(img, (5, 5), 0)
# Median Filter - salt & pepper noise (kernel size odd except 1)
median = cv.medianBlur(img, 5)
# when we want smooth image but sharp edges
bilateral_filter = cv.bilateralFilter(img, 9, 75, 75)

images = [img, dst, blur, gblur, median, bilateral_filter]
titles = ['Orignal', '2D Convolution', 'Blur', 'Gblur', 'Median', 'bilateral_filter']

for i in range(6):
    plt.subplot(2, 3, i + 1)
    plt.imshow(images[i])
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])

plt.show()