from datetime import datetime, timedelta
import re
from time import mktime, time

from google.appengine.api import memcache
from google.appengine.ext import db

from werkzeug.contrib.securecookie import SecureCookie
from werkzeug.contrib.sessions import ModificationTrackingDict, generate_key
from werkzeug.security import gen_salt

from tipfy import Tipfy, REQUIRED_VALUE, cached_property, get_config
from tipfy.ext.db import (PickleProperty, get_protobuf_from_entity,
    get_entity_from_protobuf)

#: Default configuration values for this module. Keys are:
#:
#: - ``default_backend``: Default session backend when none is specified.
#:   Built-in options are `datastore`, `memcache` or `securecookie`.
#:   Default is `securecookie`.
#:
#: - ``secret_key``: Secret key to generate session cookies. Set this to
#:   something random and unguessable. Default is
#:   :data:`tipfy.REQUIRED_VALUE` (an exception is raised if it is not set).
#:
#: - ``cookie_name``: Name of the cookie to save a session or session id.
#:   Default is `tipfy.session`.
#:
#: - ``cookie_args``: default keyword arguments to set a cookie or
#:   securecookie. Keys are:
#:
#:   - ``session_expires``: Session expiration time in seconds. Limits
#:     the duration of the contents of a cookie, even if a session cookie
#:     exists. If ``None``, the contents lasts as long as the cookie is valid.
#:     Default is ``None``.
#:
#:   - ``max_age``: Cookie max age in seconds. Limits the duration
#:     of a session cookie. If ``None``, the cookie lasts until the client
#:     is closed. Default is ``None``.
#:
#:   - ``domain``: Domain of the cookie. To work accross subdomains the
#:     domain must be set to the main domain with a preceding dot, e.g.,
#:     cookies set for `.mydomain.org` will work in `foo.mydomain.org` and
#:     `bar.mydomain.org`. Default is ``None``, which means that cookies will
#:     only work for the current subdomain.
#:
#:   - ``path``: Path in which the authentication cookie is valid.
#:     Default is `/`.
#:
#:   - ``secure``: Make the cookie only available via HTTPS.
#:
#:   - ``httponly``: Disallow JavaScript to access the cookie.
#:
#:   - ``force``: If ``True``, force cookie to be saved on each request,
#:     even if the session data isn't changed. Default to ``False``.
default_config = {
    'default_backend': 'securecookie',
    'secret_key':      REQUIRED_VALUE,
    'cookie_name':     'tipfy.session',
    'cookie_args': {
        'session_expires': None,
        'max_age':         None,
        'domain':          None,
        'path':            '/',
        'secure':          None,
        'httponly':        False,
        'force':           False,
    }
}

# Validate session keys.
_sha1_re = re.compile(r'^[a-f0-9]{40}$')

def is_valid_key(key):
    """Check if a session key has the correct format."""
    return _sha1_re.match(key) is not None

class SessionStore(object):
    """A session store that works with multiple backends. This is responsible
    for providing and persisting sessions, flash messages, secure cookies and
    ordinary cookies.
    """
    def __init__(self, request, config, backends, default_backend):
        # The current request.
        self.request = request
        # Configuration for the sessions.
        self.config = config
        # Secure cookies to be saved at the end of request.
        self._cookies = {}
        # Tracked sessions.
        self._sessions = {}
        # A dictionary of support backend classes.
        self.backends = backends
        # The default backend to use when none is provided.
        self.default_backend = default_backend

    def get_session(self, key=None, backend=None, **kwargs):
        """Returns a session for a given key. If the session doesn't exist, a
        new session is returned.

        :param key:
            Cookie unique name. If not provided, uses the ``cookie_name``
            value configured for this module.
        :param kwargs:
            Options to save the cookie. Normally not used as the configured
            defaults are enough for most cases. Possible keywords are same as
            in ``werkzeug.contrib.securecookie.SecureCookie.save_cookie``:

            - expires
            - session_expires
            - max_age
            - path
            - domain
            - secure
            - httponly
            - force
        :return:
            A dictionary-like session object.
        """
        key = key or self.config['cookie_name']

        if key not in self._sessions:
            _kwargs = self.config['cookie_args'].copy()
            _kwargs.update(kwargs)

            backend = backend or self.default_backend
            self._sessions[key] = self.backends[backend].get_session(self,
                key, **_kwargs)

        return self._sessions[key]

    def get_flash(self, key=None, backend=None, **kwargs):
        """Returns a flash message. Flash messages are deleted when first read.

        :param key:
            Cookie unique name. If not provided, uses the ``flash_cookie_name``
            value configured for this module.
        :param kwargs:
            Options to save the cookie. Normally not used as the configured
            defaults are enough for most cases.

            See :meth:`SessionStore.get_session`.
        :return:
            The data stored in the flash, or an empty list.
        """
        session = self.get_session(key=key, backend=backend, **kwargs)
        return session.pop('_flash', [])

    def set_flash(self, data, key=None, backend=None, **kwargs):
        """Sets a flash message. Flash messages are deleted when first read.

        :param data:
            Dictionary to be saved in the flash message.
        :param key:
            Cookie unique name. If not provided, uses the ``flash_cookie_name``
            value configured for this module.
        :param kwargs:
            Options to save the cookie. Normally not used as the configured
            defaults are enough for most cases.

            See :meth:`SessionStore.get_session`.
        :return:
            ``None``.
        """
        session = self.get_session(key=key, backend=backend, **kwargs)
        session.setdefault('_flash', []).append(data)

    def get_secure_cookie(self, key, load=True, override=False, **kwargs):
        """Returns a secure cookie. Cookies get through this method are
        registered and automatically saved at the end of request.

        :param key:
            Cookie unique name.
        :param load:
            ``True`` to try to load an existing cookie from the request. If it
            is not set, a clean secure cookie is returned. ``False`` to return
            a new secure cookie. Default is ``False``.
        :param override:
            If ``True``, loads or creates a new cookie instead of reusing one
            previously set in the session store. Default to ``False``.
        :param kwargs:
            Options to save the cookie. Normally not used as the configured
            defaults are enough for most cases.

            See :meth:`SessionStore.get_session`.
        :return:
            A ``werkzeug.contrib.SecureCookie`` instance.
        """
        if override is True or key not in self._cookies:
            if load:
                cookie = self.load_secure_cookie(key)
            else:
                cookie = self.create_secure_cookie()

            self.set_cookie(key, cookie, **kwargs)

        return self._cookies[key][0]

    def load_secure_cookie(self, key):
        """Loads and returns a secure cookie from request. If it is not set, a
        new secure cookie is returned.

        This cookie must be saved using a response object at the end of a
        request. To get a cookie that is saved automatically, use
        :meth:`SessionStore.get_secure_cookie`.

        :param key:
            Cookie unique name.
        :return:
            A ``werkzeug.contrib.SecureCookie`` instance.
        """
        return SecureCookie.load_cookie(self.request, key=key,
            secret_key=self.config['secret_key'])

    def create_secure_cookie(self, data=None):
        """Returns a new secure cookie.

        This cookie must be saved using a response object at the end of a
        request. To get a cookie that is saved automatically, use
        :meth:`SessionStore.get_secure_cookie`.

        :param data:
            A dictionary to be loaded into the secure cookie.
        :return:
            A ``werkzeug.contrib.SecureCookie`` instance.
        """
        if data is not None and not isinstance(data, dict):
            raise ValueError('Secure cookie data must be a dict.')

        return SecureCookie(data=data, secret_key=self.config['secret_key'])

    def set_cookie(self, key, value, **kwargs):
        """Registers a cookie or secure cookie to be saved or deleted.

        :param key:
            Cookie unique name.
        :param value:
            A cookie value or a ``werkzeug.contrib.SecureCookie`` instance.
        :param kwargs:
            Keyword arguments to save the cookie. Normally not used as the
            configured defaults are enough for most cases.

            See :meth:`SessionStore.get_session`.
        :return:
            ``None``.
        """
        _kwargs = self.config['cookie_args'].copy()
        _kwargs.update(kwargs)
        self._cookies[key] = (value, _kwargs)

    def delete_cookie(self, key, **kwargs):
        """Registers a cookie or secure cookie to be deleted.

        :param key:
            Cookie unique name.
        :param kwargs:
            Keyword arguments to delete the cookie. Normally not used as the
            configured defaults are enough for most cases.

            See :meth:`SessionStore.get_session`.
        :return:
            ``None``.
        """
        self._cookies[key] = (None, kwargs)

    def save_session(self, response):
        """Saves all sessions to a response object.

        :param response:
            A :class:`tipfy.Response` instance.
        :return:
            ``None``.
        """
        if not self._cookies and not self._sessions:
            return

        cookie_args = self.config['cookie_args']

        # Save all cookies.
        for key, (value, kwargs) in self._cookies.iteritems():
            kwargs = kwargs.copy() or cookie_args.copy()

            if not value:
                # Cookie is empty or marked for deletion, so delete it.
                response.delete_cookie(key, path=kwargs.get('path', '/'),
                    domain=kwargs.get('domain', None))
                # Delete session, if it exists.
                session = self._sessions.pop(key, None)
                if session:
                    session.delete_session(self, response, key, **kwargs)
            elif isinstance(value, basestring):
                # Save a normal cookie. Remove securecookie specific args.
                kwargs.pop('force', None)
                kwargs.pop('session_expires', None)
                response.set_cookie(key, value=value, **kwargs)
            elif isinstance(value, SecureCookie):
                # Save a secure cookie if modified or forced.
                kwargs = self._prepare_kwargs(kwargs)
                value.save_cookie(response, key=key, **kwargs)

        # Save all sessions.
        for key, value in self._sessions.iteritems():
            cookie_data = self._cookies.get(key)
            if cookie_data:
                kwargs = cookie_data[1].copy()
            else:
                kwargs = cookie_args.copy()

            kwargs = self._prepare_kwargs(kwargs)
            value.save_session(self, response, key, **kwargs)

    def _prepare_kwargs(self, kwargs):
        max_age = kwargs.pop('max_age', None)
        session_expires = kwargs.pop('session_expires', None)

        if max_age and 'expires' not in kwargs:
            kwargs['expires'] = time() + max_age

        if session_expires:
            kwargs['session_expires'] = datetime.fromtimestamp(time() +
                session_expires)

        return kwargs

class SessionModel(db.Model):
    """Stores session data."""
    kind_name = 'Session'

    #: Creation date.
    created = db.DateTimeProperty(auto_now_add=True)
    #: Modification date.
    updated = db.DateTimeProperty(auto_now=True)
    #: Session data, pickled.
    data = PickleProperty()

    @classmethod
    def kind(cls):
        """Returns the datastore kind we use for this model.
        """
        return cls.kind_name

    @property
    def sid(self):
        """Returns the session id, which is the same as the key name.

        :return:
            A session unique id.
        """
        return self.key().name()

    @classmethod
    def get_namespace(cls):
        """Returns the namespace to be used in memcache.

        :return:
            A namespace string.
        """
        return cls.__module__ + '.' + cls.__name__

    @classmethod
    def get_by_sid(cls, sid):
        """Returns a ``Session`` instance by session id.

        :param sid:
            A session id.
        :return:
            An existing ``Session`` entity.
        """
        data = cls.get_cache(sid)
        if data:
            session = get_entity_from_protobuf(data)
        else:
            session = SessionModel.get_by_key_name(sid)
            if session:
                session.set_cache()

        return session

    @classmethod
    def create(cls, sid, data=None):
        """Returns a new, empty session entity.

        :param sid:
            A session id.
        :return:
            A new and not saved session entity.
        """
        return cls(key_name=sid, data=data or {})

    @classmethod
    def get_cache(cls, sid):
        return memcache.get(sid, namespace=cls.get_namespace())

    def set_cache(self):
        """Saves a new cache for this entity."""
        memcache.set(self.sid, get_protobuf_from_entity(self),
            namespace=self.__class__.get_namespace())

    def delete_cache(self):
        """Saves a new cache for this entity."""
        memcache.delete(self.sid, namespace=self.__class__.get_namespace())

    def put(self):
        """Saves the session and updates the memcache entry."""
        self.set_cache()
        db.put(self)

    def delete(self):
        """Deletes the session and the memcache entry."""
        self.delete_cache()
        db.delete(self)

class BaseSession(ModificationTrackingDict):
    """A container for session data. This is a dictionary that tracks
    changes.
    """
    __slots__ = ModificationTrackingDict.__slots__ + ('sid', 'new')

    def __init__(self, data, sid, new=False):
        ModificationTrackingDict.__init__(self, data)
        self.sid = sid
        self.new = new

class DatastoreSession(BaseSession):
    """A session container for datastore sessions."""
    __slots__ = BaseSession.__slots__ + ('entity',)

    model_class = SessionModel

    def __init__(self, data, sid, new=False, entity=None):
        BaseSession.__init__(self, data, sid, new=new)
        self.entity = entity

    @classmethod
    def get_by_sid(cls, sid):
        """Returns a session given a session id."""
        entity = None

        if sid and is_valid_key(sid):
            entity = cls.model_class.get_by_sid(sid)

        if not entity:
            return cls({}, generate_key(gen_salt(10)), new=True)

        return cls(entity.data, sid, new=False, entity=entity)

    @classmethod
    def delete_by_sid(cls, sid):
        entity = cls.model_class.get_by_sid(sid)
        if entity:
            entity.delete()

    @classmethod
    def get_session(cls, store, key, **kwargs):
        """Returns a session that is tracked by the session store."""
        cookie = store.get_secure_cookie(key, **kwargs)
        sid = cookie.get('_sid', None)
        session = cls.get_by_sid(sid)

        if sid != session.sid:
            cookie['_sid'] = session.sid

        if session.entity:
            seconds = kwargs.get('session_expires')
            if seconds and (session.entity.created + \
                timedelta(seconds=seconds) < datetime.utcnow()):
                # Entity is too old.
                session.clear()
                session.entity = None

        return session

    def save_session(self, store, response, key, **kwargs):
        """Saves a session."""
        if not self.modified:
            return

        self.entity = self.model_class.create(self.sid, dict(self))
        self.entity.put()

    def delete_session(self, store, response, key, **kwargs):
        """Deletes a session."""
        if self.entity and self.entity.is_saved():
            self.entity.delete()

class MemcacheSession(BaseSession):
    """A session container for memcache sessions."""
    @classmethod
    def get_namespace(cls):
        return cls.__module__ + '.' + cls.__name__

    @classmethod
    def get_by_sid(cls, sid):
        """Returns a session given a session id."""
        data = None

        if sid and is_valid_key(sid):
            data = memcache.get(sid, namespace=cls.get_namespace())

        if not data:
            return cls({}, generate_key(gen_salt(10)), new=True)

        return cls(data, sid, new=False)

    @classmethod
    def delete_by_sid(cls, sid):
        memcache.delete(sid, namespace=cls.get_namespace())

    @classmethod
    def get_session(cls, store, key, **kwargs):
        """Returns a session that is tracked by the session store."""
        cookie = store.get_secure_cookie(key, **kwargs)
        sid = cookie.get('_sid', None)
        session = cls.get_by_sid(sid)

        if sid != session.sid:
            cookie['_sid'] = session.sid

        return session

    def save_session(self, store, response, key, **kwargs):
        """Saves a session."""
        if not self.modified:
            return

        expires = kwargs.get('session_expires', kwargs.get('expires', 0))
        if isinstance(expires, datetime):
            expires = mktime(expires.timetuple())

        memcache.set(self.sid, dict(self), time=expires,
            namespace=self.__class__.get_namespace())

    def delete_session(self, store, response, key, **kwargs):
        """Deletes a session."""
        if self.sid:
            self.__class__.delete_by_sid(self.sid)

class SecureCookieSession(SecureCookie):
    """A session container for SecureCookie sessions."""
    @classmethod
    def get_session(cls, store, key, **kwargs):
        return cls.load_cookie(store.request, key=key,
            secret_key=store.config['secret_key'])

    def save_session(self, store, response, key, **kwargs):
        """Saves a session."""
        self.save_cookie(response, key=key, **kwargs)

    def delete_session(self, store, response, key, **kwargs):
        """Deletes a session."""
        response.delete_cookie(key, path=kwargs.get('path', '/'),
                    domain=kwargs.get('domain', None))

class SessionMiddleware(object):
    #: A dictionary with the default supported backends.
    default_backends = {
        'datastore':    DatastoreSession,
        'memcache':     MemcacheSession,
        'securecookie': SecureCookieSession,
    }

    def __init__(self, backends=None, default_backend=None):
        self.backends = backends or self.default_backends
        self.default_backend = default_backend or get_config(__name__,
            'default_backend')

    def pre_dispatch(self, handler):
        handler.request.registry['session_store'] = SessionStore(
            handler.request, get_config(__name__), self.backends,
            self.default_backend)

    def post_dispatch(self, handler, response):
        registry = handler.request.registry
        if 'session_store' in registry:
            registry['session_store'].save_session(response)

        return response

    def pre_dispatch_handler(self):
        request = Tipfy.request
        request.registry['session_store'] = SessionStore(request,
            get_config(__name__), self.backends, self.default_backend)

    def post_dispatch_handler(self, response):
        registry = Tipfy.request.registry
        if 'session_store' in registry:
            registry['session_store'].save_session(response)

        return response

class BaseSessionMixin(object):
    """Base class for all session-related mixins."""
    @cached_property
    def session_store(self):
        return self.request.registry['session_store']

class CookieMixin(BaseSessionMixin):
    """A mixin that adds set_cookie() and delete_cookie() methods to a
    :class:`tipfy.RequestHandler`. Must be used with :class:`SessionMiddleware`.
    """
    def set_cookie(self, key, value, **kwargs):
        self.session_store.set_cookie(key, value, **kwargs)

    def delete_cookie(self, key, **kwargs):
        self.session_store.delete_cookie(key, **kwargs)

class SecureCookieMixin(BaseSessionMixin):
    """A mixin that adds a get_secure_cookie() method to a
    :class:`tipfy.RequestHandler`. Must be used with :class:`SessionMiddleware`.
    """
    def get_secure_cookie(self, key, load=True, override=False, **kwargs):
        """Returns a tracked secure cookie. See
        :meth:`SessionStore.get_secure_cookie`.
        """
        return self.session_store.get_secure_cookie(key, load, override,
            **kwargs)

class FlashMixin(BaseSessionMixin):
    """A mixin that adds get_flash() and set_flash() methods to a
    :class:`tipfy.RequestHandler`. Must be used with :class:`SessionMiddleware`.
    """
    def get_flash(self, key=None, backend=None, **kwargs):
        """Returns a flash message. See :meth:`SessionStore.get_flash`."""
        return self.session_store.get_flash(key, backend, **kwargs)

    def set_flash(self, data, key=None, backend=None, **kwargs):
        """Sets a flash message. See :meth:`SessionStore.set_flash`."""
        self.session_store.set_flash(data, key, backend, **kwargs)

class MessagesMixin(BaseSessionMixin):
    """A :class:`tipfy.RequestHandler` mixin for system messages."""
    @cached_property
    def messages(self):
        """A list of status messages to be displayed to the user."""
        if getattr(self, '_MessagesMixin__messages', None) is None:
            # Initialize messages list and check for flashes on first access.
            self.__messages = []
            self.__messages.extend(self.session_store.get_flash())

        return self.__messages

    def set_message(self, level, body, title=None, life=None, flash=False):
        """Adds a status message.

        :param level:
            Message level. Common values are "success", "error", "info" or
            "alert".
        :param body:
            Message contents.
        :param title:
            Optional message title.
        :param life:
            Message life time in seconds. User interface can implement
            a mechanism to make the message disappear after the elapsed time.
            If not set, the message is permanent.
        :return:
            ``None``.
        """
        message = {'level': level, 'title': title, 'body': body, 'life': life}
        if flash is True:
            self.session_store.set_flash(message)
        else:
            self.messages.append(message)

class SessionMixin(BaseSessionMixin):
    """A :class:`tipfy.RequestHandler` that provides access to the current
    session.
    """
    @cached_property
    def session(self):
        """A dictionary-like object that is persisted at the end of the
        request.
        """
        return self.session_store.get_session()

    def get_session(self, key=None, backend=None, **kwargs):
        """Returns a session. See :meth:`SessionStore.get_session`."""
        return self.session_store.get_session(key, backend, **kwargs)

class AllSessionMixins(CookieMixin, SecureCookieMixin, FlashMixin,
    MessagesMixin, SessionMixin):
    """All session mixins combined in one."""


"""
httpserver部分主程序
获取http请求
解析http请求
将请求发送给WebFrame
从WebFrame接收反馈数据
将数据组织为Response格式发送给客户端
"""
from socket import *
from threading import Thread
from config import *
import re
import json

# 与后端应用交互
def connect_frame(env):
    """
    将请求发送给WebFrame
    从WebFrame接收反馈数据
    """
    s = socket()
    try:
        s.connect((frame_ip, frame_port))
    except Exception as e:
        print(e)
        return
    # 发送json请求
    data = json.dumps(env)
    s.send(data.encode())
    # 获取返回的数据 (json格式)
    data = s.recv(1024 * 1024 * 10).decode()
    if data:
        return json.loads(data)  # 返回字典

# 主体功能写入类
class HTTPServer:
    def __init__(self):
        self.address = (HOST, PORT)
        self.create_socket()
        self.bind()

    # 创建套接字
    def create_socket(self):
        self.sock = socket()
        self.sock.setsockopt(SOL_SOCKET,
                             SO_REUSEADDR,
                             DEBUG)

    # 绑定地址
    def bind(self):
        # 有些属性可以在调用函数时再生产
        self.host = HOST
        self.port = PORT
        self.sock.bind(self.address)

    # 服务启动函数 (多线程并发)
    def serve_forever(self):
        self.sock.listen(3)
        print("Listen the port %d" % self.port)
        while True:
            connfd, addr = self.sock.accept()
            print("Connect from", addr)
            t = Thread(target=self.handle,
                       args=(connfd,))
            t.setDaemon(True)
            t.start()

    # 处理具体的浏览器请求
    def handle(self, connfd):
        # http请求
        request = connfd.recv(4096).decode()
        pattern = r"(?P<method>[A-Z]+)\s+(?P<info>/\S*)"
        try:
            env = re.match(pattern, request).groupdict()
        except:
            connfd.close()
            return
        else:
            # 与webframe交互 (数据字典/None)
            data = connect_frame(env)
            if data:
                self.response(connfd, data)

    # 组织响应格式
    def response(self, connfd, data):
        # data->{'status':'200','data':'zzzz'}
        if data['status'] == '200':
            res="HTTP/1.1 200 OK\r\n"
            res+="Content-Type:text/html\r\n"
            res+="\r\n"
            res+=data['data']
        elif data['status'] == '404':
            res="HTTP/1.1 404 Not Found\r\n"
            res+="Content-Type:text/html\r\n"
            res+="\r\n"
            res+=data['data']
        connfd.send(res.encode()) # 响应给浏览器

if __name__ == '__main__':
    httpd = HTTPServer()
    httpd.serve_forever()  # 启动服务


import logging
from abc import (
    ABC,
    abstractmethod,
)
from collections import defaultdict
from typing import Optional

from reconcile.gql_definitions.common.ocm_environments import (
    query as ocm_environment_query,
)
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationActionCounter as ReconcileActionCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationReconcileCounter as ReconcileCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationReconcileErrorCounter as ReconcileErrorCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationValidationErrorsGauge as ValidationErrorsGauge,
)
from reconcile.oum.models import (
    ClusterError,
    ClusterRoleReconcileResult,
    ClusterUserManagementSpec,
    OrganizationUserManagementConfiguration,
)
from reconcile.oum.providers import (
    GroupMemberProvider,
    init_ldap_group_member_provider,
)
from reconcile.utils import (
    gql,
    metrics,
)
from reconcile.utils.ocm.base import (
    CAPABILITY_MANAGE_CLUSTER_ADMIN,
    OCMClusterGroupId,
)
from reconcile.utils.ocm.cluster_groups import (
    add_user_to_cluster_group,
    delete_user_from_cluster_group,
    get_cluster_groups,
)
from reconcile.utils.ocm_base_client import (
    OCMBaseClient,
    init_ocm_base_client,
)
from reconcile.utils.runtime.integration import (
    PydanticRunParams,
    QontractReconcileIntegration,
)

class OCMUserManagementIntegrationParams(PydanticRunParams):
    ocm_environment: Optional[str] = None
    ocm_organization_ids: Optional[set[str]] = None
    group_provider_specs: list[str]

class OCMUserManagementIntegration(
    QontractReconcileIntegration[OCMUserManagementIntegrationParams], ABC
):
    def run(self, dry_run: bool) -> None:
        with metrics.transactional_metrics(self.name):
            # init group providers
            self.group_member_providers = get_group_providers(
                self.params.group_provider_specs
            )

            for ocm_env in self.get_ocm_environments():
                self.reconcile_ocm_environment(dry_run, ocm_env)

    @property
    def group_member_provider_ids(self) -> set[str]:
        return set(self.group_member_providers.keys())

    def get_ocm_environments(self) -> list[OCMEnvironment]:
        return ocm_environment_query(
            gql.get_api().query,
            variables={"name": self.params.ocm_environment}
            if self.params.ocm_environment
            else None,
        ).environments

    @abstractmethod
    def get_user_mgmt_config_for_ocm_env(
        self, ocm_env: OCMEnvironment, org_ids: Optional[set[str]]
    ) -> dict[str, OrganizationUserManagementConfiguration]:
        """
        Discover cluster user mgmt configurations in the given OCM environment.
        If org_ids are provided, only return configurations for the given organizations.

        To be implemented by subclasses.
        """

    def reconcile_ocm_environment(self, dry_run: bool, ocm_env: OCMEnvironment) -> None:
        """
        Processes user management configuration for all OCM organizations
        within the given OCM environment.
        """
        org_configs = self.get_user_mgmt_config_for_ocm_env(
            ocm_env, self.params.ocm_organization_ids
        )

        ocm_api = init_ocm_base_client(ocm_env, self.secret_reader)
        for org_id, org_config in org_configs.items():
            specs = build_specs_from_config(org_config, self.group_member_providers)
            self.reconcile_ocm_organization(
                dry_run, ocm_api, org_id, ocm_env.name, specs
            )

    def reconcile_ocm_organization(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        org_id: str,
        ocm_env: str,
        cluster_specs: list[ClusterUserManagementSpec],
    ) -> None:
        """
        Process user management configuration for all clusters in the
        given OCM organization.
        """
        metrics.inc_counter(
            ReconcileCounter(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            )
        )

        validation_errors = 0
        errors: list[Exception] = []
        for spec in cluster_specs:
            if spec.errors:
                validation_errors += 1
                validation_msg = "\n".join([m.message for m in spec.errors])
                self.signal_cluster_validation_error(
                    dry_run, ocm_api, spec, Exception(validation_msg)
                )
                continue
            reconcile_result = reconcile_cluster_roles(
                dry_run=dry_run,
                ocm_api=ocm_api,
                org_id=org_id,
                spec=spec,
            )

            # expose action metrics
            # we do this also in error situations to signal partial work
            # that has been done on a cluster
            metrics.inc_counter(
                ReconcileActionCounter(
                    integration=self.name,
                    ocm_env=ocm_env,
                    org_id=org_id,
                    action=ReconcileActionCounter.Action.AddUser,
                ),
                by=reconcile_result.users_added,
            )
            metrics.inc_counter(
                ReconcileActionCounter(
                    integration=self.name,
                    ocm_env=ocm_env,
                    org_id=org_id,
                    action=ReconcileActionCounter.Action.RemoveUser,
                ),
                by=reconcile_result.users_removed,
            )

            # signal errors
            if reconcile_result.error:
                self.signal_cluster_reconcile_error(
                    dry_run, ocm_api, spec, reconcile_result.error
                )
                errors.append(reconcile_result.error)

        # expose organization level metrics
        metrics.inc_counter(
            ReconcileErrorCounter(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            ),
            by=1 if len(errors) > 0 else 0,
        )
        metrics.set_gauge(
            ValidationErrorsGauge(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            ),
            validation_errors,
        )

    @abstractmethod
    def signal_cluster_reconcile_success(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        message: str,
    ) -> None:
        """
        This method is called when the cluster reconcile operation was successful.
        """

    @abstractmethod
    def signal_cluster_validation_error(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        error: Exception,
    ) -> None:
        """
        This method is called when the configuration for a cluster is invalid.

        If this method throws an exception, reconciliation on the rest of the clusters
        of an organization will stop.
        """

    @abstractmethod
    def signal_cluster_reconcile_error(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        error: Exception,
    ) -> None:
        """
        This method is called when the cluster reconcile operation failed.

        If this method throws an exception, reconciliation on the rest of the clusters
        of an organization will stop.
        """

def reconcile_cluster_roles(
    dry_run: bool,
    ocm_api: OCMBaseClient,
    org_id: str,
    spec: ClusterUserManagementSpec,
) -> ClusterRoleReconcileResult:
    """
    Make sure that the cluster roles and given users are synced onto the cluster.
    This implies adding missing users and removing stale ones.
    Cluster roles not mentioned in the spec are not touched.
    """
    result = ClusterRoleReconcileResult()

    try:
        org_id = spec.cluster.organization_id
        cluster = spec.cluster.ocm_cluster
        desired_groups: dict[OCMClusterGroupId, set[str]] = spec.roles
        current_groups: dict[OCMClusterGroupId, set[str]] = {
            role_id: {u.id for u in group.users.items}
            for role_id, group in get_cluster_groups(
                ocm_api=ocm_api, cluster_id=cluster.id
            ).items()
            if group.users
        }
        # only process roles present in the desired state and ignore the ones that are not
        # this way a role can still be managed manually while another one is managed by this integration
        for group in desired_groups.keys():
            desired_members = desired_groups.get(group, set())
            current_members = current_groups.get(group, set())

            # add missing users
            for missing_user in desired_members - current_members:
                logging.info(
                    f"add user {missing_user} to {group.value} - cluster id={cluster.id}, name={cluster.name}, org={org_id}"
                )
                if not dry_run:
                    add_user_to_cluster_group(
                        ocm_api=ocm_api,
                        cluster_id=cluster.id,
                        user_name=missing_user,
                        group=group,
                    )
                result.users_added += 1
            # remove obsolete users
            for obsolete_user in current_members - desired_members:
                logging.info(
                    f"remove user {obsolete_user} from {group.value} -  cluster id={cluster.id}, name={cluster.name}, org={org_id}"
                )
                if not dry_run:
                    delete_user_from_cluster_group(
                        ocm_api=ocm_api,
                        cluster_id=cluster.id,
                        user_name=obsolete_user,
                        group=group,
                    )
                result.users_removed += 1
    except Exception as e:
        result.error = e

    return result

def build_specs_from_config(
    org_config: OrganizationUserManagementConfiguration,
    group_member_providers: dict[str, GroupMemberProvider],
) -> list[ClusterUserManagementSpec]:
    """
    Transforms the external provider/group references into actual user names.
    """
    # collect external group refs by provider so we can do a bulk resolve
    external_group_refs_by_provider: dict[str, set[str]] = defaultdict(set)
    for cluster_config in org_config.cluster_configs:
        for role_external_group_ref in cluster_config.roles.values():
            for external_group_ref in role_external_group_ref:
                external_group_refs_by_provider[external_group_ref.provider].add(
                    external_group_ref.group_id
                )

    # resolve all groups using the provider implementations
    external_group_members_by_provider: dict[str, dict[str, set[str]]] = {}
    for provider, group_ids in external_group_refs_by_provider.items():
        external_group_members_by_provider[provider] = group_member_providers[
            provider
        ].resolve_groups(group_ids)

    # build specs
    cluster_specs: list[ClusterUserManagementSpec] = []
    for cluster_config in org_config.cluster_configs:
        spec = ClusterUserManagementSpec(
            cluster=cluster_config.cluster,
            roles={},
            errors=cluster_config.errors,
        )
        cluster_specs.append(spec)

        # fill roles
        for (
            role_id,
            external_group_refs,
        ) in cluster_config.roles.items():
            spec.roles[role_id] = set()
            if (
                cluster_config.cluster.ocm_cluster.is_osd()
                and role_id == OCMClusterGroupId.CLUSTER_ADMINS
                and not cluster_config.cluster.is_capability_set(
                    CAPABILITY_MANAGE_CLUSTER_ADMIN, "true"
                )
            ):
                spec.errors.append(
                    ClusterError(
                        message="This cluster does not have the capability to manage the cluster-admins role. Go to https://red.ht/ohss-incident and request cluster-admin access."
                    )
                )
            for group_ref in external_group_refs:
                members = external_group_members_by_provider[group_ref.provider].get(
                    group_ref.group_id
                )
                if members is None:
                    spec.errors.append(
                        ClusterError(
                            message=f"{group_ref.provider} group {group_ref.group_id} for {role_id.value} not found"
                        )
                    )
                    continue
                spec.roles[role_id].update(members)

            if spec.errors:
                spec.roles = {}

    return cluster_specs

def get_group_providers(
    group_provider_specs: list[str],
) -> dict[str, GroupMemberProvider]:
    """
    Initialize the group member providers.
    """
    providers: dict[str, GroupMemberProvider] = {}
    for provider_spec in group_provider_specs:
        provider_name, provider_type, provider_args = provider_spec.split(":")
        if provider_type == "ldap":
            providers[provider_name] = init_ldap_group_member_provider(provider_args)
        else:
            raise ValueError(f"unknown group member provider type {provider_type}")
    return providers


# This files contains your custom actions which can be used to run
# custom Python code.
#
# See this guide on how to implement these action:
# https://rasa.com/docs/rasa/custom-actions

from typing import Any, Text, Dict, List, Union

from rasa_sdk import Action, Tracker , FormValidationAction
from rasa_sdk.events import SlotSet, EventType,UserUtteranceReverted, ConversationPaused
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.types import DomainDict

from datetime import date,datetime,timedelta
import json

global recipient_email
recipient_email = "mata.khalili@nokia-bell-labs.com"

global user_name
user_name = "there"

global outlook_free_time
outlook_free_time = "4 pm"

global final_date
final_date = (date.today() + timedelta(days=10)) #assume that it's withtin 10 days to deliver the parcel

global locations
locations = {'chappe':[], 'curie':['E', 'D', 'B', 'C'],'shannon':[],
             'pascal':[], 'laplace':['East','West'],'torricelli':[],
             'ritchie':[], 'copernic':['G','H', 'EBC'], 'bourseul':[],
             'galilee':[], 'montgomerie':[], 'huygens':[], 'newton':['F','B'] }

class AppointmentInfoForm(Action):
    def name(self) -> Text:
        return "user_details_form"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:

        required_slots = ["language","confirm_time","recipient","another_day","date", "time","location", "entrance"]

        for slot_name in required_slots:

            if tracker.slots.get(slot_name) is None:
                # The slot is not filled yet. Request the user to fill this slot next.
                return [SlotSet("requested_slot", slot_name)]

        # All slots are filled.
        return [SlotSet("requested_slot", None)]

class ActionSubmit(Action):
    def name(self) -> Text:
        return "action_submit"

    def run(
        self,
        dispatcher,
        tracker: Tracker,
        domain: "DomainDict",
    ) -> List[Dict[Text, Any]]:

        recipient=tracker.get_slot("recipient")
        date =tracker.get_slot("date")
        time=tracker.get_slot("time")
        building=tracker.get_slot("location")
        entrance= tracker.get_slot("entrance")

        dispatcher.utter_message(response="utter_details_thanks",
                                  Recipient=recipient,
                                  Date =date,
                                  Time=time,
                                  Location=building,
                                  Entrance= entrance)

        # try to add data to json file in the following format
        output=[]
        Appointment_data= {'Recipient':recipient,
                           'Date':date,
                           'Time':time,
                           'Building':building,
                           'Entrance':entrance}

        output.append(Appointment_data)
        json_output = json.dumps(output, indent = 6)

        # Writing to Output.json in a file
        with open("Appointment_details.json", "w") as outfile:
          outfile.write(json_output)

        return [ConversationPaused()]

class ActionGreet(Action):

    def name(self) -> Text:
        return "action_greet"

    def run(self, dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
        dispatcher.utter_message(response="utter_greet",
                                 name=user_name)

        return []

class ValidateNameForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_user_details_form"

    async def validate_language(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        allowed_languages=['english','french','anglais','francais','en','fr']
        language = value.lower()
        if language in allowed_languages:
            if language == "english" or language == "anglais" or language == "en":
                return {"language":"en"}
            else:
                return {"language":"fr"}
        else:
            dispatcher.utter_message(text="Supported languages are English and French only")
            return {"language":None }

    async def validate_confirm_time(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        if value:
            Date = date.today().strftime('%Y-%m-%d')
            return {"recipient":recipient_email,"date":Date,"time": "4 pm","confirm_time": True,"another_day": False}
        else:
            return {"recipient":recipient_email,"another_day": False,"confirm_time": False }

    async def validate_date(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        Format = '%Y-%m-%d'
        if value=="Today":
            Date = date.today().strftime(Format)
            return {"date":Date}
        elif value == "Tomorrow":
            Date = (date.today() + timedelta(days=1)).strftime(Format)
            return {"date":Date}
        elif value == "Another day":
            return {"another_day": True,"date":None}
        elif value == "Another Person":
            return {"time": "Not specified yet","date": "Not specified yet",
                    "location": "Not specified yet", "entrance":"Not specified yet","recipient":None}
        else:
            # return {"date":value}
            # if tracker.get_slot("another_day"):
              try:
                Date = datetime.strptime(value, Format).date()
                Today=date.today()
                if Date >= Today and Date <= final_date: # to make sure that the date is within the allowed period
                    return {"date":value}
                else:
                    if tracker.get_slot("language") == "en":
                       dispatcher.utter_message(text="Please enter a valid date ,date should be between "+Today.strftime('%Y-%m-%d')+" and "+final_date.strftime('%Y-%m-%d'))
                    else:
                       dispatcher.utter_message(text="Veuillez entrer une date valide, la date doit être comprise entre "+Today.strftime('%Y-%m-%d')+" et "+final_date.strftime('%Y-%m-%d'))
                    return {"date":None}
              except:
                try:
                    Format = '%d-%m-%Y'
                    Date = datetime.strptime(value, Format).date()
                    Today=date.today()
                    if Date >= Today and Date <= final_date: # to make sure that the date is within the allowed period
                      return {"date":value}
                    else:
                      if tracker.get_slot("language") == "en":
                        dispatcher.utter_message(text="Please enter a valid date ,date should be between "+Today.strftime('%Y-%m-%d')+" and "+final_date.strftime('%Y-%m-%d'))
                      else:
                        dispatcher.utter_message(text="Veuillez entrer une date valide, la date doit être comprise entre "+Today.strftime('%Y-%m-%d')+" et "+final_date.strftime('%Y-%m-%d'))
                      return {"date":None}
                except:
                  try:
                    Format = '%d/%m/%Y'
                    Date = datetime.strptime(value, Format).date()
                    Today=date.today()
                    if Date >= Today and Date <= final_date: # to make sure that the date is within the allowed period
                      return {"date":value}
                    else:
                      if tracker.get_slot("language") == "en":
                        dispatcher.utter_message(text="Please enter a valid date ,date should be between "+Today.strftime('%Y-%m-%d')+" and "+final_date.strftime('%Y-%m-%d'))
                      else:
                        dispatcher.utter_message(text="Veuillez entrer une date valide, la date doit être comprise entre "+Today.strftime('%Y-%m-%d')+" et "+final_date.strftime('%Y-%m-%d'))
                      return {"date":None}
                  except:
                    if tracker.get_slot("language") == "en":
                      dispatcher.utter_message(text="Please enter a valid date in the following format: "+Format)
                    else:
                      dispatcher.utter_message(text="Veuillez saisir la date au format suivant: "+ Format)
                    return {"date":None}

    def validate_time(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        if tracker.get_slot("language") == "en":
         try:
          time = datetime.strptime(value, "%I:%M %p")
          final_time= datetime.strftime(time, "%H:%M")
          return{"time":final_time}
         except:
            try:
               time = datetime.strptime(value, "%I:%M%p")
               final_time= datetime.strftime(time, "%H:%M")
               return{"time":final_time}
            except:
                try:
                   time = datetime.strptime(value, "%I %p")
                   final_time= datetime.strftime(time, "%H:%M")
                   return{"time":final_time}
                except:
                     try:
                       time = datetime.strptime(value, "%I%p")
                       final_time= datetime.strftime(time, "%H:%M")
                       return{"time":final_time}
                     except:
                        try:
                            time = datetime.strptime(value, "%I:%M")
                            final_time= datetime.strftime(time, "%H:%M")
                            return{"time":final_time}
                        except:
                            if tracker.get_slot("language") == "en":
                               dispatcher.utter_message(text="Please enter a valid time (e.g 3:00 PM)")
                            else:
                               dispatcher.utter_message(text="Veuillez entrer une heure valide (e.g 3:00 PM)")
                            return{"time":None}
        else:
            try:
              x = value.index('h')
              if x==len(value)-1:
                time= value[0:x]+':'+'00'
              else:
                time = value[0:x]+ ':' + value[x+1:len(value)]
              return({"time":time})
            except:
              return({"time":value})

    async def validate_location(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `location` value."""

        # If the building isn't exist
        print(f"location given = {slot_value}")

        building = slot_value.lower()

        if building in locations:

            if len(locations.get(building))>0:
               return{"location": slot_value}
            else:
              if tracker.get_slot("language") == "en":
                return {"entrance":"main","location": slot_value}
              else:
                return {"entrance":"principale","location": slot_value}
        else:
            buildings= ','.join([str(elem) for elem in locations])
            if tracker.get_slot("language") == "en":
                 dispatcher.utter_message(text="That's not valid Building. \nYou should choose one of the following: \n"+buildings)
            else:
                 dispatcher.utter_message(text="Ce n'est pas un bâtiment valide. \nVous devez choisir l'une des options suivantes: \n"+buildings)
            return {"location": None}

    def validate_entrance(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        try:
         Entrances= locations.get(tracker.get_slot("location").lower())
         entrances = ','.join([str(elem) for elem in Entrances])
         if value in Entrances:
             return {"entrance": value}
         else:
            if tracker.get_slot("language") == "en":
                dispatcher.utter_message(text="That's not valid Entrance. \nYou should choose one of the following: \n"+entrances)
            else:
                dispatcher.utter_message(text="Ce n'est pas une entrée valide. \n Vous devez choisir l'un des éléments suivants : \n"+entrances)
            return {"entrance": None}
        except:
            return {"entrance": None}

class ActionCustomFallback(Action):

    def name(self) -> Text:
        return "action_custom_fallback"

    def run(self, dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:

        dispatcher.utter_message(template="utter_custom")

        return[UserUtteranceReverted()]  # to consider it a fallback action


import gym
from gym import wrappers
import numpy as np

import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical

from collections import deque
from actor_critic import PolicyModel as Policy

def reinforce(n_episodes=1000, max_time=1000, gamma=1.0, print_every=1000):
    env = gym.make("CartPole-v0")
    # env = wrappers.Monitor(env, "reinforce", force=True)
    env.seed(0)
    policy = Policy()
    optimizer = optim.Adam(policy.parameters(), lr=1e-2)
    scores_deque = deque(maxlen=100)
    scores = []
    for episode in range(1, n_episodes + 1):

        log_probs_tensor = []
        rewards = []

        state = env.reset()
        for t in range(max_time):
            action, log_prob = policy.choose_action(state)

            log_probs_tensor.append(log_prob)
            state, reward, done, _ = env.step(action)
            rewards.append(reward)
            if done:
                break

        scores_deque.append(sum(rewards))
        scores.append(sum(rewards))

        discounts = [gamma ** i for i in range(len(rewards) + 1)]
        R = sum([d * r for d, r in zip(discounts, rewards)])

        policy_loss = []
        for log_prob in log_probs_tensor:
            policy_loss.append(-log_prob * R)

        policy_loss = torch.cat(policy_loss).sum()

        optimizer.zero_grad()
        policy_loss.backward()
        optimizer.step()

        if episode % print_every == 0:
            print(
                "Episode {}\tAverage Score: {:.2f}".format(
                    episode, np.mean(scores_deque)
                )
            )
        # if np.mean(scores_deque) >= 195.0:
        #     print(
        #         "Environment solved in {:d} episodes!\tAverage Score: {:.2f}".format(
        #             episode - 100, np.mean(scores_deque)
        #         )
        #     )
            # break
    return scores


import os
from sqlalchemy import Column, String, Integer, create_engine
from flask_sqlalchemy import SQLAlchemy
import json

database_name = "postgres"
database_path = "postgres://{}@{}/{}".format('postgres:Liszt762!','localhost:5432', database_name)

db = SQLAlchemy()

def setup_db(app, database_path=database_path):
    app.config["SQLALCHEMY_DATABASE_URI"] = database_path
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    db.app = app
    db.init_app(app)
    db.create_all()
    return db

def migrations(app):
    migrate = Migrate(app, db)

class Actor(db.Model):
    __tablename__ = 'actors'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    age = Column(String, nullable=False)
    gender = Column(String, nullable=False)

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def insert(self):
        db.session.add(self)
        db.session.commit()

    def update(self):
        db.session.commit()

    def delete(self):
        db.session.delete(self)
        db.session.commit()

    def format(self):
        return ({
            'id': self.id,
            'name': self.name,
            'age': self.age,
            'gender': self.gender,

        })

    def __repr__(self):
        return f'Actor: {self.id}, {self.name}'

class Movie(db.Model):
    __tablename__ = 'movies'
    id = Column(db.Integer(), primary_key=True)
    title = Column(db.String(), nullable=False)
    release_date = Column(db.Date(), nullable=False)
    genre = Column(db.String(), nullable=False)

    def __init__(self, title, release_date):
        self.title = title
        self.release_date = release_date

    def insert(self):
        db.session.add(self)
        db.session.commit()

    def update(self):
        db.session.commit()

    def delete(self):
        db.session.delete(self)
        db.session.commit()

    def format(self):
        return ({
            'id': self.id,
            'title': self.title,
            'release_date': self.release_date.strftime("%m/%d/%y"),
            'genre': self.genre,
        })


#!/usr/bin/python
"""
Bar chart demo with pairs of bars grouped for easy comparison.
"""
import numpy as np
import matplotlib.pyplot as plt

n_groups = 7

means_men = (0.0, 16.0, 27.4, 60.8, 81.6, 87.3, 81.0)
#std_men = (2, 3, 4, 1, 2)

means_women = (0.0,3.3, 08.8, 6.3, 11.2, 5.4,4.5)

#women = (3, 5, 2, 3, 3)

fig, ax = plt.subplots()

index = np.arange(n_groups)
bar_width = 0.35

opacity = 0.4
#error_config = {'ecolor': '0.3'}

rects1 = plt.bar(index, means_men, bar_width,
                 alpha=opacity,
                 color='b',
                 #yerr=std_men,
                 #error_kw=error_config,
                 label='Pull')

rects2 = plt.bar(index + bar_width, means_women, bar_width,
                 alpha=opacity,
		 color='r',
		 #yerr=std_women,
		 #error_kw=error_config,
		 label='Push')

plt.xlabel('Number of Concurrent Downloaders')
plt.ylabel('Misses / Hits')
plt.title('Ratio of Misses to Hits')
plt.xticks(index + bar_width / 2, ('1', '2', '3', '4', '5', '6','7'))
plt.legend()

plt.tight_layout()
plt.show()


from Controller import Controller
from PIDcontroller import PIDController

from Robot import RobotState

import time

class LineFollowController(Controller):

    def __init__(self, robot, actWhenOffLine=True, lineSensitivity=23):
        Controller.__init__(self, robot)

        self.PIDCOntroller_light = None #PIDController(k_proportional=12.2, k_integral=0.52, k_derivative=0.0)#30)#, k_integral=0.07, k_derivative=5)
        # self.PIDCOntroller_light = PIDController(k_proportional=10.2, k_integral=0.01,k_derivative=0)#30)#, k_integral=0.07, k_derivative=5)

        self.PIDCOntroller_motorMiddle = None #PIDController(k_proportional=0.7)
        # self.startMotorMiddle = self.robot.motorMiddle.position

        self.resetPIDs()

        self.light = self.robot.getLightValue()

        self.BLACK_LIGHT_VAL = -1#50
        self.WHITE_LIGHT_VAL = -1#40
        self.BASE_DRIVE_POWER = 30#50

        self.BASE_NORMALISER = -1

        self.light_target = -1#(self.BLACK_LIGHT_VAL + self.WHITE_LIGHT_VAL) / 2

        self.positionTracer = []

        self.negErrorStreak = 0

        self.lineFollowPolarity = 1

        # do something when off the line
        self.actWhenOffLine = actWhenOffLine

        self.lineSensitivity = lineSensitivity

    def resetPIDs(self):

        self.PIDCOntroller_light = PIDController(k_proportional=12.2, k_integral=0.52, k_derivative=0.0)#30)#, k_integral=0.07, k_derivative=5)
        # self.PIDCOntroller_light = PIDController(k_proportional=10.2, k_integral=0.01,k_derivative=0)#30)#, k_integral=0.07, k_derivative=5)

        self.PIDCOntroller_motorMiddle = PIDController(k_proportional=0.7)

    def update(self):

        if self.robot.stateChangeOccurred:
            self.resetPIDs()
            self.robot.stateChangeOccurred = False

        # if the black/white light values have not been set, search for them
        if self.robot.state == RobotState.LINE_COLOUR_EVALUATE: #self.light_target  == -1:
            self.findLightvalues()
        elif self.robot.state == RobotState.LINE_FOLLOW:
            #TODO: fix light sensor values after uncommenting to fix this bug
            self.light = self.robot.getLightValue() # / self.BASE_NORMALISER

            light_error = ((self.light_target - self.light) / self.BASE_NORMALISER)  * self.lineFollowPolarity
            light_drive = self.PIDCOntroller_light.updatePosition(light_error, self.light)

            motorMiddleError = (self.robot.motorMiddleStartPosition + light_drive*3) - self.robot.motorMiddle.position
            middle_drive = self.PIDCOntroller_motorMiddle.updatePosition(motorMiddleError, self.robot.motorMiddle.position)
            # print(middle_drive)
            self.robot.motorMiddle.run_direct(duty_cycle_sp=min(100, max(middle_drive, -100)))

            # print('ki: {}'.format(light_error))

            self.positionTracer.append(self.light)
            # print(light_drive)
            self.__drive(light_drive)

            # if light_error <= -1:
            #     self.negErrorStreak += 1
            # else:
            #     self.negErrorStreak = 0
            #
            # print(self.negErrorStreak)

# UNCOMMENT FOR OFF LINE DETECTION-------------------------------------
            if self.actWhenOffLine:
                # constant at value of 23
                off_edge_const = self.lineSensitivity
                if (light_drive < -off_edge_const and self.lineFollowPolarity == 1) or (light_drive > off_edge_const and self.lineFollowPolarity == -1):
                    self.resetPIDs()
                    self.lineFollowPolarity *= -1
                    self.robot.setState(RobotState.OFF_LINE)
                    self.robot.motorLeft.stop()
                    self.robot.motorRight.stop()
                    self.robot.motorMiddle.stop()
                    print('OFFLINE!!!!!')
                    time.sleep(2)
# UNCOMMENT FOR OFF LINE DETECTION-------------------------------------

            # self.positionTracer.append(self.robot.getLightValue())
        # elif self.robot.state == RobotState.OBSTACLE_TRACE:
        #     if self.robot.getLightValue() - self.BLACK_LIGHT_VAL < 5:
        #         self.robot.motorLeft.stop()
        #         self.robot.motorRight.stop()
        #         time.sleep(1)
        #         print('FOUND LINE!')
        #         self.robot.setState(RobotState.DEAD)
                # self.robot.setState(RobotState.LINE_FOLLOW)

    def findLightvalues(self):
        lightValuesRange = []

        # spin in a full circle and record a range of light sensor values
        endGyroVal = self.robot.getGyroValue() + 340

        # start robot on black line always
        # startBlack = self.robot.getLightValue()
        prevLightVal = self.robot.getLightValue()
        itter = 0
        while self.robot.getGyroValue() < endGyroVal or (prevLightVal - self.robot.getLightValue()) < 3:
            print( self.robot.getLightValue())
            # drive in a circle
            self.robot.motorLeft.run_timed(duty_cycle_sp=40, time_sp=50)
            self.robot.motorRight.run_timed(duty_cycle_sp=-40, time_sp=50)

            # record light values
            lightValuesRange.append(self.robot.getLightValue())

            itter += 1
            if itter % 2 == 0:
                prevLightVal = self.robot.getLightValue()

        # for i in range(0, len(lightValuesRange)):
        #     print(lightValuesRange)

        # make a histogram of all the light values
        buckets = {} # dictionary of all light values

        # add each light value to a histogram bucket
        for i in range(0, len(lightValuesRange)):
            if lightValuesRange[i] in buckets.keys():
                buckets[lightValuesRange[i]] += 1
            else:
                buckets[lightValuesRange[i]] = 1

        # print the histogram values
        for i in buckets:
            print('{}: {}'.format(i, buckets[i]))

        for i in range(1, 101):
            if i in buckets.keys():
                self.positionTracer.append(buckets[i])
            else:
                self.positionTracer.append(0)

        dWhite = dict(buckets.items()[len(buckets)/2:])
        dBlack = dict(buckets.items()[:len(buckets)/4])

        self.WHITE_LIGHT_VAL = max(dWhite, key=dWhite.get)
        self.BLACK_LIGHT_VAL = min(lightValuesRange)#max(dBlack, key=dBlack.get)
        self.light_target = (self.BLACK_LIGHT_VAL + self.WHITE_LIGHT_VAL) / 2
        self.BASE_NORMALISER = float((self.WHITE_LIGHT_VAL - self.BLACK_LIGHT_VAL) / 2)

        self.robot.LIGHT_BLACK_VAL = self.BLACK_LIGHT_VAL
        self.robot.LIGHT_WHITE_VAL = self.WHITE_LIGHT_VAL
        self.robot.LIGHT_MID_VAL = self.light_target

        print('final vals: b:{}, w:{}, t:{}'.format(self.BLACK_LIGHT_VAL, self.WHITE_LIGHT_VAL, self.light_target))

        # find the two maximum values, these are black and white values
        # maxWhite = max(buckets)
        # del buckets[max(buckets)]
        #
        # maxBlack = max(buckets)
        # del buckets[max(buckets)]
        #
        # self.WHITE_LIGHT_VAL = maxWhite
        # self.BLACK_LIGHT_VAL = maxBlack
        # self.light_target = (self.BLACK_LIGHT_VAL + self.WHITE_LIGHT_VAL) / 2

        self.robot.setState(RobotState.LINE_FOLLOW)

    def __drive(self, d_light):

        # if d_light > self.BASE_DRIVE_POWER:
        #     d_light = self.BASE_DRIVE_POWER
        # elif d_light < -self.BASE_DRIVE_POWER:
        #     d_light = -self.BASE_DRIVE_POWER

        self.robot.motorLeft.run_timed(duty_cycle_sp=max(-100, min(self.BASE_DRIVE_POWER - d_light, 100)), time_sp=50)
        self.robot.motorRight.run_timed(duty_cycle_sp=max(-100, min(self.BASE_DRIVE_POWER + d_light, 100)), time_sp=50)

        # self.light = self.robot.getLightValue()


from src.aws_uploader import AWSUploader
from src.config_manager import ConfigManager
from src.gcloud_uploader import GCloudUploader

class UploaderFactory:
    def __init__(self, config: ConfigManager):
        self._config = config

    def get_uploader(self):
        if self._config.cloud_provider == "AWS":
            return AWSUploader()
        if self._config.cloud_provider == "gcloud":
            return GCloudUploader(config=self._config)
        raise RuntimeError(
            "CLOUD_PROVIDER was missing from your environment or "
            "didn't match any known options."
        )


from __future__ import absolute_import, print_function, division

from ..datadefs import BindDictFuncs
from ..debuggable import Debuggable
from ..parse import generic  # For pylint-placating dummy defs
from ..systems import xdatetime

class Error(Exception):
  """Base class for all exceptions defined in formatter modules."""

class ParseError(Error):
  """Item was not parsed correctly."""

class DecodeError(Error):
  """Item was not decoded correctly."""

# pylint: disable=too-many-instance-attributes,too-many-public-methods
class Formatter(Debuggable):
  """Base class for formatter objects."""
  SPACES = '              '

  GPS_LEAP_OFFSET = xdatetime.Constants.GPS_LEAP_OFFSET

  # Formatter levels to allow reproducing older behavior
  FMT_ORIGINAL = 0            # Earliest version with retained results
  FMT_UPDATED = 1             # With some updates
  FMT_WAAS_BASIC = 2          # Introduce WAAS type decodes
  FMT_NEW_SIGNAL_DECODES = 3  # New system-dependent signal decodes

  # Dummy defs to placate pylint, overridden in subclasses
  EXTRACTER = generic.Extracter
  PARSER = generic.Parser
  DECODER = generic.Decoder

  def __init__(self, infile=None):
    self.show_gps_time = False
    self.dump_raw_data = False
    self._output = []
    self._errors = []
    self._last_summary = None
    self.extracter = self.EXTRACTER(infile)
    self.decoder = self.DECODER()
    self.formatter_dict = BindDictFuncs('FORMATTER_DICT', self)
    self.filter = set()
    self.stop_on_error = False     # Turn parse/decode errors into exceptions
    self.hide_warnings = False     # Exclude warnings from stderr
    self.exclude_warnings = False  # Exclude warnings from output
    self.show_offsets = False      # Don't show offsets by default
    self.fmt_level = 999999        # Allow everything by default

  def SetFormatLevel(self, level):
    """Set formatter compatibility level."""
    self.fmt_level = level

  def Put(self, item):  # pylint: disable=too-many-branches
    """Process one item, added formatted result to list(s)."""
    if self.filter:
      parser = item.PARSE_CLASS.GetParser(item.msgtype)
      if parser not in self.filter:
        return
    parsed = self.PARSER.Parse(item)  # Need parser set up for DESCRIPTION
    if self.show_offsets:
      self.Send(0, '@ %08X:' % item.charpos)
    summary = item.Summary(full=True)
    self._last_summary = summary
    if summary:
      self.Send(0, summary)
    if item.parse_error:
      self.SendError(2, item.parse_error)
    if self.dump_raw_data:
      self.DumpRaw(4, item)
    if item.parse_error and self.stop_on_error:
      raise ParseError(item.parse_error)
    if not parsed:
      return
    decoded = self.decoder.Decode(item)
    formatter = self.formatter_dict.get(item.parser)
    # If not found, try parent classes
    if not formatter:
      for parser in item.parser.__mro__:
        formatter = self.formatter_dict.get(parser)
        if formatter:
          break
    if item.decode_error:
      err = item.decode_error
      if isinstance(err, str):
        self.SendError(2, err)
      elif isinstance(err, tuple):
        self.SendError(2, err[0])
        if formatter:
          formatter(item, err[1], error=True)
      else:
        self.SendError(2, 'Unexpected decode error type')
      if self.stop_on_error:
        raise DecodeError(item.decode_error)
    if not decoded:
      return
    if formatter:
      formatter(item)
    return

  def Send(self, indent, text):
    """Send text to output list."""
    self._output.append(self.SPACES[:indent] + text)

  def SendError(self, indent, text):
    """Send error to output list."""
    if text.startswith('Residual mismatch in '):
      return  ### Temp hide this
    if self.fmt_level < self.FMT_UPDATED and text.startswith('Extra comma in'):
      text = text.replace(' GPRMC', '')
    if not self.exclude_warnings:
      self._output.append(self.SPACES[:indent] + '*** ' + text)
    if not self.hide_warnings:
      if self._last_summary:
        self._errors.append('%s: %s' % (text, self._last_summary))
      else:
        self._errors.append(text)

  @staticmethod
  def DumpRaw(indent, item):
    """Dummy raw dump, overridden in subclasses."""
    _, _ = indent, item

  def Get(self):
    """Get and clear pending output."""
    result = self._output
    self._output = []
    return result

  def GetErrors(self):
    """Get and clear any errors."""
    result = self._errors
    self._errors = []
    return result

  @staticmethod
  def EncodeTime(time, frac_digits=2):
    """Format a time as HH:MM:SS."""
    strip = 6 - frac_digits if frac_digits else 7
    rnd = 10 ** (9 - frac_digits) // 2
    return time.strftime('%H:%M:%S.%f', rnd)[:-strip]

  @staticmethod
  def EncodeDateTime(dtime, frac_digits=2, fixed_leap=None):
    """Format a date/time as YYYY-MM-DD HH:MM:SS."""
    strip = 6 - frac_digits if frac_digits else 7
    rnd = 10 ** (9 - frac_digits) // 2
    return dtime.strftime('%Y-%m-%d %H:%M:%S.%f',
                          roundofs=rnd, fixed_leap=fixed_leap)[:-strip]

  def GetDateTimeStr(self, dtime, frac_digits=2):
    """Format a date/time as either GPS or UTC date/time."""
    if self.show_gps_time:
      return self.EncodeDateTime(dtime, frac_digits,
                                 fixed_leap=self.GPS_LEAP_OFFSET) + ' GPS'
    return self.EncodeDateTime(dtime, frac_digits) + ' UTC'

  def GetWeekTowStr(self, dtime, frac_digits=3):
    """Format a date/time as a GPS week/sec."""
    dtime_str = self.GetDateTimeStr(dtime, frac_digits=frac_digits)
    week, sec, nano = dtime.gps_week_sec_nano(roundofs=0)
    second = sec + nano / 1.0E9
    return 'GPS week/sec = %d/%.3f (%s)' % (week, second, dtime_str)

  @staticmethod
  def EncodeGPSDateTime(dtime, frac_digits=2):
    """Format a date/time as a GPS-style week/sec."""
    strip = 6 - frac_digits if frac_digits else 7
    rnd = 10 ** (9 - frac_digits) // 2
    try:
      week, sec, nano = dtime.gps_week_sec_nano(roundofs=rnd)
    except AttributeError:
      return None
    return ('%d/%d.%06d' % (week, sec, nano // 1000))[:-strip]

  @staticmethod
  def EncodeAlt(value, units='M'):
    """Format an altitude, with a units specification."""
    if units == 'M':
      return '%.3fm/%.3f\'' % (value, value / .3048)
    return '%.3f%s' % (value, units)

  @staticmethod
  def DecodeChar(char, map_dict, default='???'):
    """Decode a character, based on a map."""
    return '%s (%s)' % (char, map_dict.get(char, default))

  @staticmethod
  def DecodeNum(num, map_dict, default='???'):
    """Decode a number, based on a map."""
    return '%d (%s)' % (num, map_dict.get(num, default))

  @staticmethod
  def DecodeEnum(value, map_list):
    """Decode an enum, based on a name map."""
    try:
      return '%d (%s)' % (value, map_list[value])
    except IndexError:
      return '%d (???)' % value

  @staticmethod
  def ExtractBits(value, names, group=1):
    """Extract a series of named bits from a value."""
    bit = 1
    num = 0
    result = []
    current = []
    for name in names:
      if value & bit and name:
        current.append((bit, name))
      bit <<= 1
      num += 1
      if num % group == 0 and current:
        result.append(current)
        current = []
    if current:
      result.append(current)
    return result

  def DumpBits(self,  # pylint: disable=too-many-arguments
               indent, header, fmt, value, names, group=1):
    """Format a series of bits by names."""
    decode = self.ExtractBits(value, names, group)
    if not decode:
      self.Send(indent, (header + fmt) % value)
      return
    self.Send(indent, (header + fmt + ':') % value)
    fmt += ' = %s'
    for grp in decode:
      line = ', '.join([fmt % (bit, name) for bit, name in grp])
      self.Send(indent + 4, line)

  @staticmethod
  def ExtractNibbles(value, names):
    """Extract a series of nibbles from a value."""
    result = []
    for name in names:
      if name:
        result.append((value & 0xF, name))
      value >>= 4
    return result

  def DumpNibbles(self, indent, header, value, names):
    """Foramt a value as nibbles."""
    decode = self.ExtractNibbles(value, names)
    if not decode:
      self.Send(indent, header % value)
      return
    self.Send(indent, (header + ':') % value)
    for count, name in decode:
      self.Send(indent + 2, '%2d %s' % (count, name))

  def DumpULongs(self, indent, name, data):
    """Format a series of unsigned longs."""
    out_list = [name + ':']
    for val in data:
      out_list.append('%.08X' % val)
    self.Send(indent, ' '.join(out_list))

  @staticmethod
  def FormatTuple(fmt, data):
    """Format a named tuple as specified."""
    return [fmt % x for x in zip(data._fields, data)]

class BinaryFormatter(Formatter):
  """Base class for generic binary message formatter object."""

  def DumpRaw(self, indent, item):
    """Dump raw data if binary."""
    if not item.IS_BINARY:
      return
    little_endian = item.ENDIANNESS == 'little'
    for line in self.FormatRawData(item.data, item.length, little_endian):
      self.Send(indent, line)

  def DumpBitString(self, indent, name, bits):
    """Dump BitString item."""
    digits = (bits.nbits + 3) // 4
    self.Send(indent, '%s: %0*X' % (name, digits, bits.data))

  def DumpBitStrings(self, indent, nlist, dlist):
    """Dump list of BitStrings labeled by list of names."""
    for name, data in zip(nlist, dlist):
      self.DumpBitString(indent, name, data)

  @staticmethod
  def FormatRawData(data, datalen, little_endian=False):
    """Format raw hex dump."""
    adrfmt = '%.04X' if datalen > 4096 else '%.03X'
    idx, buf = 0, iter(bytearray(data))
    result = []
    while idx < datalen:
      num = min(16, datalen - idx)
      dlist = [adrfmt % idx, '']
      for ofs in range(num):
        if ofs % 4 == 0:
          dlist.append('')
        dlist.append('%.02X' % next(buf))
      if little_endian:
        for ofs in range(num, 16):
          if ofs %4 == 0:
            dlist.append('')
          dlist.append('  ')
        dlist.reverse()
      result.append(' '.join(dlist))
      idx += num
    return result


'''
SpaceShip Logo Image :- Icons made by Skyclick from  www.flaticon.com
Spaceship in game :- Icons made by Freepik from  www.flaticon.com
EnemyShip in game :- Icons made by Smashicons from  www.flaticon.com
Background Image :- Background vector created by pikisuperstar - www.freepik.com
Bullet Image :- Icons made by Those Icons from  www.flaticon.com

'''

import pygame
from pygame import mixer

import random
import math

# Initialize
pygame.init()

# Create Screen
# (Width, Height)
# (0, 0) => top-left of the Window || (800, 600) => bottom-right
screen = pygame.display.set_mode((800, 600))

# Title and Window
pygame.display.set_caption("Space Invader")
icon = pygame.image.load("spaceship.png")
pygame.display.set_icon(icon)

# background
backgroundImg = pygame.image.load("background.png")

# music
mixer.music.load("music/background.wav")
mixer.music.play(-1)

# Player
playerImg = pygame.image.load("space-invaders.png")
playerX = 370
playerY = 480
playerX_change = 0
playerY_change = 0

# Enemy
enemyImg = []
enemyX, enemyY = [], []
enemyX_change, enemyY_change = [], []
num_of_enemies = 4

for i in range(num_of_enemies):
    enemyImg.append(pygame.image.load("invader2.png"))
    enemyX.append(random.randint(0, 800))
    enemyY.append(random.randint(50, 150))
    enemyX_change.append(4)
    enemyY_change.append(20)

# bullet
bulletImg = pygame.image.load("bullet.png")
bulletX, bulletY = 0, 480
bulletX_change, bulletY_change = 3, 10
# READY state means is not visible
# FIRE state means Bullet is moving
bullet_state = 'ready'

# score
score_value = 0
font = pygame.font.Font('freesansbold.ttf', 16)

textX = 10
textY = 10

#  Game Over
gameOver = pygame.font.Font("freesansbold.ttf", 64)

def game_over_text():
    over = gameOver.render("Game Over", True, (255, 255, 255))
    screen.blit(over, (225, 200))

def show_score(textX, textY):
    score = font.render("Score :" + str(score_value), True, (255, 255, 255))
    screen.blit(score, (textX, textY))

# functions
def player(X, Y):
    screen.blit(playerImg, (X, Y))

def enemy(X_e, Y_e, i):
    screen.blit(enemyImg[i], (X_e, Y_e))

def fire_bullet(X, Y):
    global bullet_state
    bullet_state = "fire"
    screen.blit(bulletImg, (X + 16, Y + 10))

def isCollision(bulletX, bulletY, enemyX, enemyY):
    distance = math.sqrt((bulletX - enemyX)**2 + (bulletY - enemyY)**2)
    if distance <= 27:
        return True
    return False

# Game Loop
running = True

score = 0
while running:
    # screen color will update
    screen.fill((255, 255, 255))
    # background image
    screen.blit(backgroundImg, (0, 0))

    for event in pygame.event.get():

        if event.type == pygame.QUIT:  # or event.type == (pygame.K_AT + pygame.K_F4):
            running = False
        # main character movement
        if event.type == pygame.KEYDOWN:
            print("Key Stroke is PRESSED")
            if event.key == pygame.K_LEFT:
                playerX_change = -5

            if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                playerX_change = 5
                print("RIGHT is pressed")

            if event.key == pygame.K_SPACE:
                if bullet_state is 'ready':
                    bullet_sound = mixer.Sound("music/laser.wav")
                    bullet_sound.play()
                    bulletX = playerX
                    fire_bullet(bulletX, bulletY)

        if event.type == pygame.KEYUP:
            playerY_change = 0
            playerX_change = 0
            print("KEY Stroke is RELEASED")

    # player() function will bw called to draw PlayerImg on Canvas
    playerX += playerX_change
    playerY += playerY_change

    # setting boundries
    # images are considered from (0, 0), not from centre.
    if playerX <= 0:
        playerX = 0
    elif playerX >= 736:
        playerX = 736

    if playerY <= 0:
        playerY = 0
    elif playerY >= 536:
        playerY = 536

    for i in range(num_of_enemies):
        # Game Over
        if enemyY[i] > 440:
            for j in range(num_of_enemies):
                enemyY[i] = 1000
            game_over_text()
            break

        enemyX[i] += enemyX_change[i]
        if enemyX[i] <= 0:
            enemyX[i] = 0
            enemyX_change[i] = 3
            enemyY[i] += enemyY_change[i]
        elif enemyX[i] >= 736:
            enemyX[i] = 736
            enemyX_change[i] = -3
            enemyY[i] += enemyY_change[i]

        # collision
        collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
        if collision:
            explosion_sound = mixer.Sound("music/explosion.wav")
            explosion_sound.play()
            bulletY = 480
            bullet_state = 'ready'
            score_value += 1
            print(score)
            enemyX[i] = random.randint(0, 800)
            enemyY[i] = random.randint(50, 150)

        enemy(enemyX[i], enemyY[i], i)

    #bullet Movement

    # reset bullet
    if bulletY <= 0:
        bulletY = 480
        bullet_state = 'ready'

    # bullet movement
    if bullet_state is 'fire':
        fire_bullet(bulletX, bulletY)
        bulletY -= bulletY_change

    # player will be drawn
    player(playerX, playerY)
    show_score(textX, textY)
    pygame.display.update()

'''
    After loading the background image, movements of the game character might get slow
    due to the Background image as it takes tim time to load image and iteration slows down.
'''


# Pseudocode will prolly help to transfer it in code
# basically u1 = v1
# u2 = v2 - <v2,u1>/u1^2 * u1(make dot product function for this probably)
# u3 = v3 - <v3,u1>/u1^2* u1 - <v3,u2>/u^2
# taken test matrix will add input matrix soon
from Input_Matrix import *
from Transpose import *

# Its easier to deal with the vectors as rows while doing this hence I coded it that way by calling transpose in the end to transpose the matrix

def gram_schmidt(matrix):
    Gram_schmidt_matrix = []
    # this loop iterates through each indiviual vector in the matrix
    for v in matrix:
        w = v[:]  # Create a copy of the current vector
        # this loop basically makes that vector orthogonal to the ones present in the GSP matrix
        for b in Gram_schmidt_matrix:
            proj = 0  # initialize projection
            for i in range(len(v)):
                proj += v[i] * b[i]  # Projection of v onto b
            # Projection part of the dot product divided by the norm of the vector
            proj /= sum(b[i] * b[i] for i in range(len(b)))

            for i in range(len(v)):
             # Subtract the projection of v onto b from v
                w[i] -= proj * b[i]
        # Calculate the norm of the vector to normalize it to 1
        norm = sum(i ** 2 for i in w) ** 0.5
        # computer arithmetic isnt perfect so due to that floating point errors can add up
        # leading to us getting another vector which is in fact a zero vector
        if norm > 1e-10:
            for i in range(len(w)):
                w[i] /= norm  # Normalize the vector
            # Append the normalized vector to the Gram_schmidt_matrix
            Gram_schmidt_matrix.append(w)
        # appending zero vector to the remaining rows which werent linearly independent
        else:
            for i in range(len(w)):
                w[i] = 0
            Gram_schmidt_matrix.append(w)

    return (Gram_schmidt_matrix)

matrix = (transpose(inputmatrix(rows, cols)))
orthonormal_basis = ((gram_schmidt(matrix)))
print(transpose(orthonormal_basis))


#1) Realiza una función llamada area_rectangulo() que devuelva el área del rectángulo a partir de una base y
#una altura. Calcula el área de un rectángulo de 15 de base y 10 de altura

import sys
argumentos = sys.argv
if len(argumentos) != 3:
    print("Error: numero de argumentos incorrecto")
    sys.exit(1)
def area_rectangulo(numero1, numero2):
    # Acciones a realizar
    if type(numero1) != int or type(numero2) != int:
        resultado = "Error: los argumentos no son numeros"
    else:
        resultado = numero1 * numero2
    return resultado
resultado_suma = area_rectangulo(int(argumentos[1]), int(argumentos[2]))
print(resultado_suma)


weight_value = {1:3,2:5,3:10,4:9,10:2}
used = {0:[]}
# find all available weight for weight C
def find_available(C, weight_value):
    weight_list = list(weight_value.keys())
    for i in range(len(weight_list)):
        if (weight_list[i] > C):
            return weight_list[:i]
    return weight_list
result = []
result.append(0)
def find_result(weight_value, C):
    if C == 0:
        return 0
    weight_list = find_available(C, weight_value)
    if len(weight_list) == 0:
        return 0
    max_value = 0
    max_list = []
    for item in weight_list:
        if item not in used[C-item]:
            cur_list =  used[C-item] + [item]
            cur_value = result[C-item] + weight_value[item]
        else:
            cur_list = used[C-item]
            cur_value = result[C-item]
        max_list = cur_list if cur_value > max_value else max_list
        max_value = cur_value if cur_value > max_value else max_value
    used[C] = max_list
    return max_value
C = 21
for i in range(1, C+1):
    result.append(find_result(weight_value, i))
print("for backpack that weight", C, " the max value is ", result[C])
print("the max value combination is ", used[C])


from django.conf import settings
from django.conf.urls.static import static
from django.urls import include, path

from jobsapp.views import EditProfileView, EmployerProfileEditView

from .views import *

app_name = "accounts"

urlpatterns = [
     path("searchcompany/", SearchcompanyView.as_view(), name="searchcompany"),
    path("employee/register/", RegisterEmployeeView.as_view(), name="employee-register"),
    path("employer/register/", RegisterEmployerView.as_view(), name="employer-register"),
    path("employer/<int:id>/",  JobCompanyView.as_view(), name="employer-company"),
    path("employee/profile/update/", EditProfileView.as_view(), name="employee-profile-update"),
    path("employer/profile/update/", EmployerProfileEditView.as_view(), name="employer-profile-update"),
    path("logout/", LogoutView.as_view(), name="logout"),
    path("login/", LoginView.as_view(), name="login"),
    path("companysearch/", CompanyListView.as_view(), name="companylist"),
    path("companysearch/<int:id>", Viewalljobs.as_view(), name="viewcompanyjobs"),

] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


from selenium import webdriver
import time
import random
from PIL import Image
from chapter2.ShowapiRequest import ShowapiRequest
from base.find_element import FindElement

class RegisterFunction(object):

    def __init__(self,url):
        self.driver = self.get_driver(url)
    # 获取并打开url
    def get_driver(self,url):
        driver = webdriver.Chrome()
        driver.get(url)
        driver.maximize_window()
        return driver

    # 输入用户信息
    def send_user_info(self,key,data):
        self.get_user_element(key).send_keys(data)

    # 获取用户信息，获取element

    def get_user_element(self,key):
        find_element = FindElement(self.driver)
        user_element = find_element.get_element(key)
        return user_element

    # 获取随机数
    def get_range_user(self):
        user_info = ''.join(random.sample('1234567890abcdefgh', 5))
        return user_info

    # 获取图片

    def get_code_image(self,file_name):
        self.driver.save_screenshot(file_name)
        code_element = self.get_user_element("code_image")
        # 打印出验证码地址（元素坐标）
        # print(code_element.location)
        left = code_element.location['x']
        top = code_element.location['y']
        right = code_element.size['width'] + left
        height = code_element.size['height'] + top
        # 调用保存图片
        im = Image.open(file_name)
        # 裁剪图片
        img = im.crop((left, top, right, height))
        img.save(file_name)

        # 解析图片获取验证码

    def code_online(self,file_name):
        self.get_code_image(file_name)
        r = ShowapiRequest("http://route.showapi.com/184-5", "100157", "c58c05e4469d48788594d9ae7063da63")
        r.addBodyPara("img_base64", "file_name")
        r.addBodyPara("typeId", "35")
        r.addBodyPara("convert_to_jpg", "0")
        r.addBodyPara("needMorePrecise", "0")
        res = r.post()
        text = res.json()['showapi_res_body']['Result']
        return text  # 返回信息

    def main(self):
        user_name_info = self.get_range_user()
        user_email = user_name_info + "@qq.com"
        file_name = "D:/python tupian/imooc.png"
        code_text = self.code_online(file_name)
        self.send_user_info('user_email',user_email)
        self.send_user_info('user_name',user_name_info)
        self.send_user_info('password', "1111123")
        self.send_user_info('code_text',code_text)
        self.get_user_element('register_button').click()
        time.sleep(5)
        self.driver.close()

if __name__ == '__main__':
    register_function = RegisterFunction('http://www.5itest.cn/register')
    register_function.main()