#! /usr/bin/env python
# coding=utf-8

import os
import time
import json
import logging
import random
import hashlib
import datetime
import requests

class ApiClientBase(object):
    api_host = "http://api.cn.ronghub.com"

    def __init__(self, key=None, secret=None):
        self._app_key = key
        self._app_secret = secret

        if self._app_key is None:
            self._app_key = os.environ.get('rongcloud_app_key', '')
        if self._app_secret is None:
            self._app_secret = os.environ.get('rongcloud_app_secret', '')

    @staticmethod
    def _merge_dict(data, *override):
        result = {}
        for current_dict in (data, ) + override:
            result.update(current_dict)
        return result

    def _make_common_signature(self):
        """生成通用签名, 一般情况下，您不需要调用该方法 文档详见 http://docs.rongcloud.cn/server.html#_API_调用签名规则
        :return: {'app-key':'xxx','nonce':'xxx','timestamp':'xxx','signature':'xxx'}
        """
        nonce = str(random.random())
        timestamp = str(int(time.time()) * 1000)
        signature = hashlib.sha1((self._app_secret + nonce + timestamp).encode(
            'utf-8')).hexdigest()

        return {
            "rc-app-key": self._app_key,
            "rc-nonce": nonce,
            "rc-timestamp": timestamp,
            "rc-signature": signature
        }

    def _headers(self):
        """Default HTTP headers """
        return self._merge_dict(
            self._make_common_signature(),
            {"content-type": "application/x-www-form-urlencoded"})

    def _http_call(self, url, method, **kwargs):
        """Makes a http call. Logs response information."""
        logging.info("Request[{0}]: {1}".format(method, url))
        start_time = datetime.datetime.now()

        logging.debug("Header: {0}".format(kwargs['headers']))
        logging.debug("Params: {0}".format(kwargs['data']))
        response = requests.request(method, url, verify=False, **kwargs)

        duration = datetime.datetime.now() - start_time
        logging.info("Response[{0:d}]: {1}, Duration: {2}.{3}s.".format(
            response.status_code, response.reason, duration.seconds,
            duration.microseconds))
        return response

    def _filter_params(self, params):
        _r = dict()
        for k, v in params.items():
            if v is not None:
                _r[k] = v
        return _r

    def call_api(self, action, params=None, **kwargs):
        """
        :param action: Method Name，
        :param params: Dictionary,form params for api.
        :param timeout: (optional) Float describing the timeout of the request.
        :return:
        """
        headers = self._headers()
        data = self._filter_params(params)
        if action in ['/message/private/publish_template.json',
                      '/message/system/publish_template.json']:
            headers['content-type'] = "application/json"
            data = json.dumps(data)
        return self._http_call(url=self.api_host + action,
                               method="POST",
                               data=data,
                               headers=headers,
                               **kwargs)

class Response:
    def __init__(self, response, desc):
        self.desc = desc
        self.response = response

    @property
    def result(self):
        """调用结果"""
        return self.response.json()

    @property
    def status(self):
        """Http 返回码"""
        return self.response.status_code

    @property
    def ok(self):
        """调用成功返回True，其它返回False"""
        return self.response.ok

    def get(self):
        """返回调用结果"""
        return self.result

    def __str__(self):
        """打印字符串"""
        return str(self.result)

class ApiClient(ApiClientBase):
    """Server 开发指南, 请参阅 http://www.rongcloud.cn/docs/server.html"""

    def getToken(self, userId, name, portraitUri):
        """
        获取 Token 方法 方法
        @param  portraitUri:用户头像 URI，最大长度 1024 字节.用来在 Push 推送时显示用户的头像。（必传）
        @param  name:用户名称，最大长度 128 字节.用来在 Push 推送时显示用户的名称.用户名称，最大长度 128 字节.用来在 Push 推送时显示用户的名称。（必传）
        @param  userId:用户 Id，最大长度 64 字节.是用户在 App 中的唯一标识码，必须保证在同一个 App 内不重复，重复的用户 Id 将被当作是同一用户。（必传）

        @return code:返回码，200 为正常.如果您正在使用开发环境的 AppKey，您的应用只能注册 100 名用户，达到上限后，将返回错误码 2007.如果您需要更多的测试账户数量，您需要在应用配置中申请“增加测试人数”。
        @return token:用户 Token，可以保存应用内，长度在 256 字节以内.用户 Token，可以保存应用内，长度在 256 字节以内。
        @return userId:用户 Id，与输入的用户 Id 相同.用户 Id，与输入的用户 Id 相同。
	    """

        desc = {
            "name": "TokenReslut",
            "desc": "getToken返回结果",
            "fields":
            [{"name": "code",
              "type": "Integer",
              "desc":
              "返回码，200 为正常.如果您正在使用开发环境的 AppKey，您的应用只能注册 100 名用户，达到上限后，将返回错误码 2007.如果您需要更多的测试账户数量，您需要在应用配置中申请“增加测试人数”。"
              },
             {"name": "token",
              "type": "String",
              "desc":
              "用户 Token，可以保存应用内，长度在 256 字节以内.用户 Token，可以保存应用内，长度在 256 字节以内。"},
             {"name": "userId",
              "type": "String",
              "desc": "用户 Id，与输入的用户 Id 相同.用户 Id，与输入的用户 Id 相同。"}]
        }
        r = self.call_api(action='/user/getToken.json',
                          params={"userId": userId,
                                  "name": name,
                                  "portraitUri": portraitUri})
        return Response(r, desc)

    def refreshUser(self, userId, name=None, portraitUri=None):
        """
        刷新用户信息方法 方法
        @param  userId:用户 Id，最大长度 64 字节.是用户在 App 中的唯一标识码，必须保证在同一个 App 内不重复，重复的用户 Id 将被当作是同一用户。（必传）
        @param  name:用户名称，最大长度 128 字节。用来在 Push 推送时，显示用户的名称，刷新用户名称后 5 分钟内生效。（可选，提供即刷新，不提供忽略）
        @param  portraitUri:用户头像 URI，最大长度 1024 字节。用来在 Push 推送时显示。（可选，提供即刷新，不提供忽略）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/user/refresh.json',
                          params={"userId": userId,
                                  "name": name,
                                  "portraitUri": portraitUri})
        return Response(r, desc)

    def checkOnlineUser(self, userId):
        """
        检查用户在线状态 方法 方法
        @param  userId:用户 Id，最大长度 64 字节。是用户在 App 中的唯一标识码，必须保证在同一个 App 内不重复，重复的用户 Id 将被当作是同一用户。（必传）

        @return code:返回码，200 为正常。
        @return status:在线状态，1为在线，0为不在线。
	    """

        desc = {"name": "CheckOnlineReslut",
                "desc": "checkOnlineUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "status",
                                                      "type": "String",
                                                      "desc":
                                                      "在线状态，1为在线，0为不在线。"}]}
        r = self.call_api(action='/user/checkOnline.json',
                          params={"userId": userId})
        return Response(r, desc)

    def blockUser(self, userId, minute):
        """
        封禁用户方法 方法
        @param  minute:封禁时长,单位为分钟，最大值为43200分钟。（必传）
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/user/block.json',
                          params={"userId": userId,
                                  "minute": minute})
        return Response(r, desc)

    def unBlockUser(self, userId):
        """
        解除用户封禁方法 方法
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/user/unblock.json',
                          params={"userId": userId})
        return Response(r, desc)

    def queryBlockUser(self):
        """
        获取被封禁用户方法 方法

        @return code:返回码，200 为正常。
        @return users:被封禁用户列表。
        @return userId:被封禁用户 ID。
        @return blockEndTime:封禁结束时间。(yyyy-mm-dd hh:mm:ss)
	    """

        desc = {"name": "QueryBlockUserReslut",
                "desc": "queryBlockUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "users",
                                                      "type": "List<String>",
                                                      "desc": "被封禁用户列表。"},
                           {"name": "userId",
                            "type": "String",
                            "desc": "被封禁用户 ID。"},
                           {"name": "blockEndTime",
                            "type": "String",
                            "desc": "封禁结束时间。(yyyy-mm-dd hh:mm:ss)"}]}
        r = self.call_api(action='/user/block/query.json', params={})
        return Response(r, desc)

    def addUserBlacklist(self, userId, blackUserId):
        """
        添加用户到黑名单方法 方法
        @param  blackUserId:被加到黑名单的用户Id。(必传)
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/user/blacklist/add.json',
                          params={"userId": userId,
                                  "blackUserId": blackUserId})
        return Response(r, desc)

    def removeBlacklistUser(self, userId, blackUserId):
        """
        从黑名单中移除用户方法 方法
        @param  blackUserId:被移除的用户Id。(必传)
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/user/blacklist/remove.json',
                          params={"userId": userId,
                                  "blackUserId": blackUserId})
        return Response(r, desc)

    def queryBlacklistUser(self, userId):
        """
        获取某用户的黑名单列表方法 方法
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
        @return users:黑名单用户数组。
	    """

        desc = {"name": "QueryBlacklistUserReslut",
                "desc": "queryBlacklistUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "users",
                                                      "type": "List<String>",
                                                      "desc": "黑名单用户数组。"}]}
        r = self.call_api(action='/user/blacklist/query.json',
                          params={"userId": userId})
        return Response(r, desc)

    def publishMessage(self,
                       fromUserId,
                       toUserId,
                       objectName,
                       content,
                       pushContent=None,
                       pushData=None,
                       count=None,
                       verifyBlacklist=None,
                       isPersisted=None,
                       isCounted=None):
        """
        发送单聊消息方法（一个用户向另外一个用户发送消息，单条消息最大 128k。每分钟最多发送 6000 条信息，每次发送用户上限为 1000 人，如：一次发送 1000 人时，示为 1000 条消息。） 方法
        @param  toUserId:接收用户 Id，可以实现向多人发送消息，每次上限为 1000 人。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  content:发送消息内容，参考融云消息类型表。示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  pushContent:定义显示的 Push 内容，如果 objectName 为融云内置消息类型时，则发送后用户一定会收到 Push 信息。如果为自定义消息，则 pushContent 为自定义消息显示的 Push 内容，如果不传则用户不会收到 Push 通知。(可选)
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData。(可选)
        @param  count:针对 iOS 平台，Push 时用来控制未读消息显示数，只有在 toUserId 为一个用户 Id 的时候有效。(可选)
        @param  verifyBlacklist:是否过滤发送人黑名单列表，0 表示为不过滤、 1 表示为过滤，默认为 0 不过滤。(可选)
        @param  isPersisted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行存储，0 表示为不存储、 1 表示为存储，默认为 1 存储消息。(可选)
        @param  isCounted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行未读消息计数，0 表示为不计数、 1 表示为计数，默认为 1 计数，未读消息数增加 1。(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/private/publish.json',
                          params={"fromUserId": fromUserId,
                                  "toUserId": toUserId,
                                  "objectName": objectName,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "count": count,
                                  "verifyBlacklist": verifyBlacklist,
                                  "isPersisted": isPersisted,
                                  "isCounted": isCounted})
        return Response(r, desc)

    def publishTemplateMessage(self,
                               fromUserId,
                               toUserId,
                               objectName,
                               values,
                               content,
                               pushContent=None,
                               pushData=None,
                               verifyBlacklist=None):
        """
        发送单聊模板消息方法(一个用户向多个用户发送不同消息内容，单条消息最大 128k.每分钟最多发送 6000 条信息，每次发送用户上限为 1000 人。) 方法
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  toUserId:接收用户 Id，提供多个本参数可以实现向多人发送消息，上限为 1000 人。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  content:发送消息内容，内容中定义标识通过 values 中设置的标识位内容进行替换，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  values:消息内容中，标识位对应内容。（必传）
        @param  pushContent:如果为自定义消息，定义显示的 Push 内容，内容中定义标识通过 values 中设置的标识位内容进行替换.如消息类型为自定义不需要 Push 通知，则对应数组传空值即可。（可选）
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData。如不需要 Push 功能对应数组传空值即可。（可选）
        @param  verifyBlacklist:是否过滤发送人黑名单列表，0 为不过滤、 1 为过滤，默认为 0 不过滤.(可选)是否过滤发送人黑名单列表，0 为不过滤、 1 为过滤，默认为 0 不过滤.(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/private/publish_template.json',
                          params={"fromUserId": fromUserId,
                                  "toUserId": toUserId,
                                  "objectName": objectName,
                                  "values": values,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "verifyBlacklist": verifyBlacklist})
        return Response(r, desc)

    def systemPublishMessage(self,
                             fromUserId,
                             toUserId,
                             objectName,
                             content,
                             pushContent=None,
                             pushData=None,
                             isPersisted=None,
                             isCounted=None):
        """
        发送系统消息方法(一个用户向一个或多个用户发送系统消息，单条消息最大 128k，会话类型为 SYSTEM。每秒钟最多发送 100 条消息，每次最多同时向 100 人发送，如：一次发送 100 人时，示为 100 条消息。) 方法
        @param  content:发送消息内容，内容中定义标识通过 values 中设置的标识位内容进行替换，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  toUserId:接收用户 Id，提供多个本参数可以实现向多人发送消息，上限为 1000 人。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  pushContent:如果为自定义消息，定义显示的 Push 内容，内容中定义标识通过 values 中设置的标识位内容进行替换.如消息类型为自定义不需要 Push 通知，则对应数组传空值即可。（可选）
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData。如不需要 Push 功能对应数组传空值即可。（可选）
        @param  isPersisted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行存储，0 表示为不存储、 1 表示为存储，默认为 1 存储消息。(可选)
        @param  isCounted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行未读消息计数，0 表示为不计数、 1 表示为计数，默认为 1 计数，未读消息数增加 1。(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/system/publish.json',
                          params={"fromUserId": fromUserId,
                                  "toUserId": toUserId,
                                  "objectName": objectName,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "isPersisted": isPersisted,
                                  "isCounted": isCounted})
        return Response(r, desc)

    def systemPublishTemplateMessage(self,
                                     fromUserId,
                                     toUserId,
                                     objectName,
                                     values,
                                     content,
                                     pushContent=None,
                                     pushData=None):
        """
        发送系统模板消息方法(一个用户向一个或多个用户发送系统消息，单条消息最大 128k，会话类型为 SYSTEM.每秒钟最多发送 100 条消息，每次最多同时向 100 人发送，如：一次发送 100 人时，示为 100 条消息。) 方法
        @param  toUserId:接收用户 Id，提供多个本参数可以实现向多人发送消息，上限为 100 人。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  content:发送消息内容，内容中定义标识通过 values 中设置的标识位内容进行替换，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  values:消息内容中，标识位对应内容。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  pushContent:定义显示的 Push 内容，如果 objectName 为融云内置消息类型时，则发送后用户一定会收到 Push 信息. 如果为自定义消息，则 pushContent 为自定义消息显示的 Push 内容，如果不传则用户不会收到 Push 通知。(可选)
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData.如不需要 Push 功能对应数组传空值即可。（可选）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/system/publish_template.json',
                          params={"fromUserId": fromUserId,
                                  "toUserId": toUserId,
                                  "objectName": objectName,
                                  "values": values,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData})
        return Response(r, desc)

    def publishGroupMessage(self,
                            fromUserId,
                            toGroupId,
                            objectName,
                            content,
                            pushContent=None,
                            pushData=None,
                            isPersisted=None,
                            isCounted=None):
        """
        发送群组消息方法(以一个用户身份向群组发送消息，单条消息最大 128k.每秒钟最多发送 20 条消息，每次最多向 3 个群组发送，如：一次向 3 个群组发送消息，示为 3 条消息.) 方法
        @param  content:发送消息内容，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  toGroupId:接收群Id，提供多个本参数可以实现向多群发送消息，最多不超过 3 个群组。（必传）
        @param  fromUserId:发送人用户 Id 。（必传）
        @param  pushContent:定义显示的 Push 内容，如果 objectName 为融云内置消息类型时，则发送后用户一定会收到 Push 信息. 如果为自定义消息，则 pushContent 为自定义消息显示的 Push 内容，如果不传则用户不会收到 Push 通知。(可选)
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData。(可选)
        @param  isPersisted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行存储，0 表示为不存储、 1 表示为存储，默认为 1 存储消息。(可选)
        @param  isCounted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行未读消息计数，0 表示为不计数、 1 表示为计数，默认为 1 计数，未读消息数增加 1。(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/group/publish.json',
                          params={"fromUserId": fromUserId,
                                  "toGroupId": toGroupId,
                                  "objectName": objectName,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "isPersisted": isPersisted,
                                  "isCounted": isCounted})
        return Response(r, desc)

    def publishDiscussionMessage(self,
                                 fromUserId,
                                 toDiscussionId,
                                 objectName,
                                 content,
                                 pushContent=None,
                                 pushData=None,
                                 isPersisted=None,
                                 isCounted=None):
        """
        发送讨论组消息方法(以一个用户身份向讨论组发送消息，单条消息最大 128k，每秒钟最多发送 20 条消息.) 方法
        @param  content:发送消息内容，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  toDiscussionId:接收讨论组 Id。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  pushContent:定义显示的 Push 内容，如果 objectName 为融云内置消息类型时，则发送后用户一定会收到 Push 信息. 如果为自定义消息，则 pushContent 为自定义消息显示的 Push 内容，如果不传则用户不会收到 Push 通知。(可选)
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData.(可选)
        @param  isPersisted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行存储，0 表示为不存储、 1 表示为存储，默认为 1 存储消息.(可选)
        @param  isCounted:当前版本有新的自定义消息，而老版本没有该自定义消息时，老版本客户端收到消息后是否进行未读消息计数，0 表示为不计数、 1 表示为计数，默认为 1 计数，未读消息数增加 1。(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/discussion/publish.json',
                          params={"fromUserId": fromUserId,
                                  "toDiscussionId": toDiscussionId,
                                  "objectName": objectName,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "isPersisted": isPersisted,
                                  "isCounted": isCounted})
        return Response(r, desc)

    def publishChatroomMessage(self, fromUserId, toChatroomId, objectName,
                               content):
        """
        发送聊天室消息方法(一个用户向聊天室发送消息，单条消息最大 128k。每秒钟限 100 次。) 方法
        @param  content:发送消息内容，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式.（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型.（必传）
        @param  toChatroomId:接收聊天室Id，提供多个本参数可以实现向多个聊天室发送消息.（必传）
        @param  fromUserId:发送人用户 Id.（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/chatroom/publish.json',
                          params={"fromUserId": fromUserId,
                                  "toChatroomId": toChatroomId,
                                  "objectName": objectName,
                                  "content": content})
        return Response(r, desc)

    def broadcastMessage(self,
                         fromUserId,
                         objectName,
                         content,
                         pushContent=None,
                         pushData=None,
                         os=None):
        """
        发送广播消息方法(发送消息给一个应用下的所有注册用户，如用户未在线会对满足条件（绑定手机终端）的用户发送 Push 信息，单条消息最大 128k，会话类型为 SYSTEM.每小时只能发送 1 次，每天最多发送 3 次。) 方法
        @param  content:发送消息内容，参考融云消息类型表.示例说明；如果 objectName 为自定义消息类型，该参数可自定义格式。（必传）
        @param  objectName:消息类型，参考融云消息类型表.消息标志；可自定义消息类型。（必传）
        @param  fromUserId:发送人用户 Id。（必传）
        @param  pushContent:定义显示的 Push 内容，如果 objectName 为融云内置消息类型时，则发送后用户一定会收到 Push 信息. 如果为自定义消息，则 pushContent 为自定义消息显示的 Push 内容，如果不传则用户不会收到 Push 通知.(可选)
        @param  pushData:针对 iOS 平台为 Push 通知时附加到 payload 中，Android 客户端收到推送消息时对应字段名为 pushData。(可选)
        @param  os:针对操作系统发送 Push，值为 iOS 表示对 iOS 手机用户发送 Push ,为 Android 时表示对 Android 手机用户发送 Push ，如对所有用户发送 Push 信息，则不需要传 os 参数。(可选)

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/broadcast.json',
                          params={"fromUserId": fromUserId,
                                  "objectName": objectName,
                                  "content": content,
                                  "pushContent": pushContent,
                                  "pushData": pushData,
                                  "os": os})
        return Response(r, desc)

    def addWordFilter(self, word):
        """
        添加敏感词方法(设置敏感词后，App 中用户不会收到含有敏感词的消息内容，默认最多设置 50 个敏感词。) 方法
        @param  word:敏感词，最长不超过 32 个字符。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/wordfilter/add.json', params={"word": word})
        return Response(r, desc)

    def deleteWordfilter(self, word):
        """
        移除敏感词方法(从敏感词列表中，移除某一敏感词。) 方法
        @param  word:敏感词，最长不超过 32 个字符。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/wordfilter/delete.json',
                          params={"word": word})
        return Response(r, desc)

    def listWordfilter(self):
        """
        查询敏感词列表方法 方法

        @return code:返回码，200 为正常。
        @return word:敏感词内容。
	    """

        desc = {"name": "ListWordfilterReslut",
                "desc": "listWordfilterReslut返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "word",
                                                      "type": "String",
                                                      "desc": "敏感词内容。"}]}
        r = self.call_api(action='/wordfilter/list.json', params={})
        return Response(r, desc)

    def historyMessage(self, date):
        """
        消息历史记录下载地址获取 方法消息历史记录下载地址获取方法。获取 APP 内指定某天某小时内的所有会话消息记录的下载地址。（目前支持二人会话、讨论组、群组、聊天室、客服、系统通知消息历史记录下载） 方法
        @param  date:指定北京时间某天某小时，格式为2014010101,表示：2014年1月1日凌晨1点。（必传）

        @return code:返回码，200 为正常。
        @return url:历史消息下载地址。
        @return date:历史记录时间。
	    """

        desc = {"name": "HistoryMessageReslut",
                "desc": "historyMessage返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "url",
                                                      "type": "String",
                                                      "desc": "历史消息下载地址。"},
                           {"name": "date",
                            "type": "String",
                            "desc": "历史记录时间。"}]}
        r = self.call_api(action='/message/history.json',
                          params={"date": date})
        return Response(r, desc)

    def HistoryMessageDelete(self, date):
        """
        消息历史记录删除方法(删除 APP 内指定某天某小时内的所有会话消息记录。调用该接口返回成功后，date参数指定的某小时的消息记录文件将在随后的5-10分钟内被永久删除。) 方法
        @param  date:指定北京时间某天某小时，格式为2014010101,表示：2014年1月1日凌晨1点.（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/message/history/delete.json',
                          params={"date": date})
        return Response(r, desc)

    def groupSync(self, userId, group_id_name):
        """
        同步用户所属群组方法(当第一次连接融云服务器时，需要向融云服务器提交 userId 对应的用户当前所加入的所有群组，此接口主要为防止应用中用户群信息同融云已知的用户所属群信息不同步。) 方法
        @param  group_id_name:该用户的群信息，如群 Id 已经存在，则不会刷新对应群组名称，如果想刷新群组名称请调用刷新群组信息方法。
        @param  userId:被同步群信息的用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/sync.json',
                          params={"userId": userId,
                                  "group_id_name": group_id_name})
        return Response(r, desc)

    def groupCreate(self, userId, groupId, groupName):
        """
        创建群组方法(创建群组，并将用户加入该群组，用户将可以收到该群的消息，同一用户最多可加入 500 个群，每个群最大至 3000 人，App 内的群组数量没有限制.注：其实本方法是加入群组方法 /group/join 的别名。) 方法
        @param  groupName:群组 Id 对应的名称。（必传）
        @param  groupId:创建群组 Id。（必传）
        @param  userId:要加入群的用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/create.json',
                          params={"userId": userId,
                                  "groupId": groupId,
                                  "groupName": groupName})
        return Response(r, desc)

    def groupjoin(self, userId, groupId, groupName):
        """
        将用户加入指定群组，用户将可以收到该群的消息，同一用户最多可加入 500 个群，每个群最大至 3000 人。 方法
        @param  groupName:要加入的群 Id 对应的名称。（必传）
        @param  groupId:要加入的群 Id。（必传）
        @param  userId:要加入群的用户 Id，可提交多个，最多不超过 1000 个。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/join.json',
                          params={"userId": userId,
                                  "groupId": groupId,
                                  "groupName": groupName})
        return Response(r, desc)

    def groupQuit(self, userId, groupId):
        """
        退出群组方法(将用户从群中移除，不再接收该群组的消息.) 方法
        @param  groupId:要退出的群 Id.（必传）
        @param  userId:要退出群的用户 Id.（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/quit.json',
                          params={"userId": userId,
                                  "groupId": groupId})
        return Response(r, desc)

    def groupDismiss(self, userId, groupId):
        """
        解散群组方法。(将该群解散，所有用户都无法再接收该群的消息。) 方法
        @param  groupId:要解散的群 Id。（必传）
        @param  userId:操作解散群的用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/dismiss.json',
                          params={"userId": userId,
                                  "groupId": groupId})
        return Response(r, desc)

    def groupRefresh(self, groupId, groupName):
        """
        刷新群组信息方法 方法
        @param  groupName:群名称。（必传）
        @param  groupId:群组 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/refresh.json',
                          params={"groupId": groupId,
                                  "groupName": groupName})
        return Response(r, desc)

    def groupUserQuery(self, groupId):
        """
        查询群成员方法 方法
        @param  groupId:群组Id。（必传）

        @return code:返回码，200 为正常。
        @return id:群成员用户Id。
	    """

        desc = {"name": "GroupUserQueryReslut",
                "desc": "groupUserQuery返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "id",
                                                      "type": "String",
                                                      "desc": "群成员用户Id。"}]}
        r = self.call_api(action='/group/user/query.json',
                          params={"groupId": groupId})
        return Response(r, desc)

    def addGagGroupUser(self, userId, groupId, minute):
        """
        添加禁言群成员方法(在 App 中如果不想让某一用户在群中发言时，可将此用户在群组中禁言，被禁言用户可以接收查看群组中用户聊天信息，但不能发送消息。) 方法
        @param  minute:禁言时长，以分钟为单位，最大值为43200分钟.（必传）
        @param  groupId:群组 Id.（必传）
        @param  userId:用户 Id.（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/user/gag/add.json',
                          params={"userId": userId,
                                  "groupId": groupId,
                                  "minute": minute})
        return Response(r, desc)

    def rooBackGagGroupUser(self, userId, groupId):
        """
        移除禁言群成员方法 方法
        @param  groupId:群组Id。（必传）
        @param  userId:用户Id。支持同时移除多个群成员（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/group/user/gag/rollback.json',
                          params={"userId": userId,
                                  "groupId": groupId})
        return Response(r, desc)

    def listGagGroupUser(self, groupId):
        """
        查询被禁言群成员方法 方法
        @param  groupId:群组Id。（必传）

        @return code:返回码，200 为正常.
        @return users:群组成员列表
        @return time:解禁时间。（yyyy-mm-dd hh:mm:ss）
        @return users:群成员 Id。
	    """

        desc = {"name": "ListGagGroupUserReslut",
                "desc": "lisitGagGroupUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常."}, {"name": "users",
                                                      "type": "List<String>",
                                                      "desc": "群组成员列表"},
                           {"name": "time",
                            "type": "String",
                            "desc": "解禁时间。（yyyy-mm-dd hh:mm:ss）"},
                           {"name": "users",
                            "type": "String",
                            "desc": "群成员 Id。"}]}
        r = self.call_api(action='/group/user/gag/list.json',
                          params={"groupId": groupId})
        return Response(r, desc)

    def chatroomCreate(self, chatroom_id_name):
        """
        创建聊天室方法 方法
        @param  chatroom_id_name:id:要创建的聊天室的id；name:要创建的聊天室的name。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/create.json',
                          params={"chatroom_id_name": chatroom_id_name})
        return Response(r, desc)

    def chatroomJoin(self, userId, chatroomId):
        """
        加入聊天室方法 方法
        @param  chatroomId:要加入的聊天室 Id。（必传）
        @param  userId:要加入聊天室的用户 Id，可提交多个，最多不超过 50 个。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/join.json',
                          params={"userId": userId,
                                  "chatroomId": chatroomId})
        return Response(r, desc)

    def chatroomDestroy(self, chatroomId):
        """
        销毁聊天室方法 方法
        @param  chatroomId:要销毁的聊天室 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/destroy.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)

    def chatroomQuery(self, chatroomId):
        """
        查询聊天室信息方法 方法
        @param  chatroomId:要查询的聊天室id（必传）

        @return code:返回码，200 为正常。
        @return chatRooms:聊天室信息数组。
        @return chrmId:聊天室 ID。
        @return name:聊天室名称。
        @return chrmId:聊天室创建时间。
	    """

        desc = {"name": "ChatroomQueryReslut",
                "desc": "chatroomQuery返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "chatRooms",
                                                      "type": "String",
                                                      "desc": "聊天室信息数组。"},
                           {"name": "chrmId",
                            "type": "String",
                            "desc": "聊天室 ID。"}, {"name": "name",
                                                 "type": "String",
                                                 "desc": "聊天室名称。"},
                           {"name": "chrmId",
                            "type": "String",
                            "desc": "聊天室创建时间。"}]}
        r = self.call_api(action='/chatroom/query.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)

    def chatroomUserQuery(self, chatroomId, count, order):
        """
        查询聊天室内用户方法 方法
        @param  order:加入聊天室的先后顺序， 1 为加入时间正序， 2 为加入时间倒序。（必传）
        @param  count:要获取的聊天室成员数，上限为 500 ，超过 500 时最多返回 500 个成员。（必传）
        @param  chatroomId:要查询的聊天室 ID（必传）

        @return code:返回码，200 为正常。
        @return total:聊天室中用户数。
        @return users:聊天室成员数组。
        @return id:聊天室用户Id。
        @return time:加入聊天室时间。
	    """

        desc = {"name": "ChatroomUserQueryReslut",
                "desc": "chatroomUserQuery返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "total",
                                                      "type": "Integer",
                                                      "desc": "聊天室中用户数。"},
                           {"name": "users",
                            "type": "List<String>",
                            "desc": "聊天室成员数组。"}, {"name": "id",
                                                  "type": "String",
                                                  "desc": "聊天室用户Id。"},
                           {"name": "time",
                            "type": "String",
                            "desc": "加入聊天室时间。"}]}
        r = self.call_api(action='/chatroom/user/query.json',
                          params={"chatroomId": chatroomId,
                                  "count": count,
                                  "order": order})
        return Response(r, desc)

    def stopDistributionChatroomMessage(self, chatroomId):
        """
        聊天室消息停止分发方法(可实现控制对聊天室中消息是否进行分发，停止分发后聊天室中用户发送的消息，融云服务端不会再将消息发送给聊天室中其他用户。) 方法
        @param  chatroomId:聊天室 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/message/stopDistribution.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)

    def resumeDistributionChatroomMessage(self, chatroomId):
        """
        聊天室消息恢复分发方法 方法
        @param  chatroomId:聊天室 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/message/resumeDistribution.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)

    def addGagChatroomUser(self, userId, chatroomId, minute):
        """
        添加禁言聊天室成员方法（在 App 中如果不想让某一用户在聊天室中发言时，可将此用户在聊天室中禁言，被禁言用户可以接收查看聊天室中用户聊天信息，但不能发送消息.） 方法
        @param  minute:禁言时长，以分钟为单位，最大值为43200分钟。（必传）
        @param  chatroomId:聊天室 Id。（必传）
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/user/gag/add.json',
                          params={"userId": userId,
                                  "chatroomId": chatroomId,
                                  "minute": minute})
        return Response(r, desc)

    def rollbackGagChatroomUser(self, userId, chatroomId):
        """
        移除禁言聊天室成员方法 方法
        @param  chatroomId:聊天室Id。（必传）
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/user/gag/rollback.json',
                          params={"userId": userId,
                                  "chatroomId": chatroomId})
        return Response(r, desc)

    def listGagChatroomUser(self, chatroomId):
        """
        查询被禁言聊天室成员方法 方法
        @param  chatroomId:聊天室 Id。（必传）

        @return code:返回码，200 为正常。
        @return users:聊天室被禁言用户列表。
        @return time:解禁时间。
        @return userId:群成员Id。
	    """

        desc = {"name": "ListGagChatroomUserReslut",
                "desc": "listGagChatroomUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "users",
                                                      "type": "List<String>",
                                                      "desc": "聊天室被禁言用户列表。"},
                           {"name": "time",
                            "type": "String",
                            "desc": "解禁时间。"}, {"name": "userId",
                                               "type": "String",
                                               "desc": "群成员Id。"}]}
        r = self.call_api(action='/chatroom/user/gag/list.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)

    def addChatroomBlockUser(self, userId, chatroomId, minute):
        """
        添加封禁聊天室成员方法 方法
        @param  minute:封禁时长，以分钟为单位，最大值为43200分钟。（必传）
        @param  chatroomId:聊天室 Id。（必传）
        @param  userId:用户 Id。（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/user/block/add.json',
                          params={"userId": userId,
                                  "chatroomId": chatroomId,
                                  "minute": minute})
        return Response(r, desc)

    def rollbackBlockChatroomUser(self, userId, chatroomId):
        """
        移除封禁聊天室成员方法 方法
        @param  chatroomId:聊天室 Id.（必传）
        @param  userId:用户 Id.（必传）

        @return code:返回码，200 为正常。
	    """

        desc = {"name": "CodeSuccessReslut",
                "desc": "http 返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}]}
        r = self.call_api(action='/chatroom/user/block/rollback.json',
                          params={"userId": userId,
                                  "chatroomId": chatroomId})
        return Response(r, desc)

    def listBlockChatroomUser(self, chatroomId):
        """
        查询被封禁聊天室成员方法 方法
        @param  chatroomId:聊天室 Id.（必传）

        @return code:返回码，200 为正常。
        @return users:聊天室成员数组。
        @return time:解禁时间。
        @return userId:群成员 Id。
	    """

        desc = {"name": "ListBlockChatroomUserReslut",
                "desc": "listBlockChatroomUser返回结果",
                "fields": [{"name": "code",
                            "type": "Integer",
                            "desc": "返回码，200 为正常。"}, {"name": "users",
                                                      "type": "List<String>",
                                                      "desc": "聊天室成员数组。"},
                           {"name": "time",
                            "type": "String",
                            "desc": "解禁时间。"}, {"name": "userId",
                                               "type": "String",
                                               "desc": "群成员 Id。"}]}
        r = self.call_api(action='/chatroom/user/block/list.json',
                          params={"chatroomId": chatroomId})
        return Response(r, desc)


import config
import cv2 as cv
import numpy as np
import os
from detectors import SkeletonDetector, BODY_MODEL
from stereo import DisparityCalculator, StereoCapture, StereoParams, prepare_for_vis
import time
import random

ROOT_DIR = os.path.dirname(__file__)

colors = [
    (11, 138, 236),
    (136, 7, 105),
    (200, 4, 4),
    (200, 4, 200),
    (242, 113, 211),
    (4, 4, 200),
    (4, 200, 200),
    (4, 200, 4),
]

def draw_skeleton(image, people_joints):
    rendered = image.copy()
    if rendered.ndim == 2 or rendered.shape[-1] == 1:
        rendered = cv.cvtColor(rendered, cv.COLOR_GRAY2BGR)

    for n, person in enumerate(people_joints):
        color = colors[n % len(colors)][::-1]
        for i in range(len(person)):
            if person[i] is not None:
                for j in BODY_MODEL[i][1]:
                    if person[j] is not None:
                        cv.line(rendered, (int(person[i][0]), int(person[i][1])),
                                (int(person[j][0]), int(person[j][1])), color, 1)

        joints = [p for p in person if p is not None]
        joints = np.reshape(joints, [len(joints), 1, 2])
        joints = np.int32(joints)
        cv.polylines(rendered, joints, True, (0, 0, 255), 2)

    return rendered

def min_max_norm(x):
    mn, mx = x.min(), x.max()
    return (x - mn) / (mx - mn)

def get_iou(bbox1, bbox2):
    x1, y1, w1, h1 = bbox1
    x2, y2, w2, h2 = bbox2

    xy1 = np.array([x1, y1], np.float32)
    wh1 = np.array([w1, h1], np.float32)

    xy2 = np.array([x2, y2], np.float32)
    wh2 = np.array([w2, h2], np.float32)

    mins1 = xy1
    maxes1 = xy1 + wh1

    mins2 = xy2
    maxes2 = xy2 + wh2

    intersect_mins = np.maximum(mins1, mins2)
    intersect_maxes = np.minimum(maxes1, maxes2)
    intersect_wh = np.maximum(0, intersect_maxes - intersect_mins)

    intersect_areas = np.prod(intersect_wh)

    areas1 = np.prod(wh1)
    areas2 = np.prod(wh2)

    union_areas = areas1 + areas2 - intersect_areas

    iou = intersect_areas / np.maximum(union_areas, 1e-6)

    return iou

def main():
    intrinsics_path = os.path.join(ROOT_DIR, 'models', 'intrinsics.yml')
    if not os.path.exists(intrinsics_path):
        raise RuntimeError('Can\'t find a intrinsics file!')

    extrinsics_path = os.path.join(ROOT_DIR, 'models', 'extrinsics.yml')
    if not os.path.exists(extrinsics_path):
        raise RuntimeError('Can\'t find a extrinsics file!')

    skeleton_model_path = os.path.join(ROOT_DIR, 'models', 'pose-unet-96x128.pb')
    if not os.path.exists(skeleton_model_path):
        raise RuntimeError('Can\'t find a skeleton detector model!')

    # facial_landmarks_model_path = os.path.join(ROOT_DIR, 'models', 'shape_predictor_68_face_landmarks.dat')
    # if not os.path.exists(facial_landmarks_model_path):
    #     raise RuntimeError('Can\'t find a facial landmark detection model!')

    # age_pca_path = os.path.join(ROOT_DIR, 'models', 'agedb-pca.pkl')
    # if not os.path.exists(age_pca_path):
    #     raise RuntimeError('Can\'t find an age pca model!')

    # gender_svm_path = os.path.join(ROOT_DIR, 'models', 'agedb-gender_svm.pkl')
    # if not os.path.exists(gender_svm_path):
    #     raise RuntimeError('Can\'t find a gender svm model!')

    # age_svm_path = os.path.join(ROOT_DIR, 'models', 'agedb-svm.pkl')
    # if not os.path.exists(age_svm_path):
    #     raise RuntimeError('Can\'t find an age svm model!')

    # age_svr_path = os.path.join(ROOT_DIR, 'models', 'agedb-svr.pkl')
    # if not os.path.exists(age_svr_path):
    #     raise RuntimeError('Can\'t find an age svr model!')

    # age_features_stat_path = os.path.join(ROOT_DIR, 'models', 'agedb-mean_std.npz')
    # if not os.path.exists(age_features_stat_path):
    #     raise RuntimeError('Can\'t find an age features statistics file!')

    skeleton_detector = SkeletonDetector(skeleton_model_path, (128, 96), 8)

    # age_gender_predictor = AgeGenderPredictor(
    #     facial_landmarks_model_path, age_pca_path, gender_svm_path, age_svm_path, age_svr_path, age_features_stat_path)

    stereo_params = StereoParams(intrinsics_path, extrinsics_path)

    if config.ENABLE_DISPARITY:
        disp_calc = DisparityCalculator(**config.SGBM_params)

    if isinstance(config.VIDEO_SOURCE, int):
        cap = StereoCapture(config.VIDEO_SOURCE, stereo_params)
    else:
        cap = StereoCapture(config.VIDEO_SOURCE)

    print('Capture: (%i, %i) %.2f' % (int(
        cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT)), cap.get(cv.CAP_PROP_FPS)), flush=True)

    if config.DEBUG_WRITE:
        writer = 0
        if config.ENABLE_DISPARITY:
            disp_writer = None
        left_writer = None
        right_writer = None

    try:
        print('Processing is started.', flush=True)

        people = []
        last_id = 0
        key = None

        start = time.time()
        while True:
            cap_start = time.time()
            ret, (left_frame, right_frame) = cap.read()
            cap_elapsed = time.time() - cap_start
            if not ret:
                break

            # disparity calculation

            if config.ENABLE_DISPARITY:
                disp_start = time.time()
                disparity_map = disp_calc(left_frame, right_frame)
                disp_elapsed = time.time() - disp_start
            else:
                disp_elapsed = 1e-3

            # skeleton detection

            skeleton_start = time.time()
            frame_people_skeletons, (joints_map, bones_map) = skeleton_detector(left_frame, ret_maps=True)
            skeleton_elapsed = time.time() - skeleton_start

            # tracking

            tracking_start = time.time()

            frame_people = []
            for person in frame_people_skeletons:
                head = person[0]
                neck = person[1]

                if head is not None and neck is not None:
                    dist = np.linalg.norm(np.array(head)-neck) * 1.25
                    bbox = [head[0]-dist, head[1]-dist, head[0]+dist, head[1]+dist]

                    frame_people.append((bbox, person))

            if not people:
                people = list(zip(range(last_id, len(frame_people)), frame_people, [None]*len(frame_people)))
                last_id = last_id + len(frame_people)
            else:
                new_people = []
                if frame_people:
                    for i in range(len(people)):
                        person = people[i]

                        best_j = None
                        best_iou = 0
                        for j in range(len(frame_people)):
                            iou = get_iou(person[1][0], frame_people[j][0])
                            if max(0, iou - 0.5) > 0 and best_iou < iou:
                                best_iou = iou
                                best_j = j

                        if best_j is not None:
                            new_people.append((person[0], frame_people[best_j], person[2]))
                            del frame_people[best_j]

                    if new_people:
                        last_id = max(new_people, key=lambda x: x[0])[0]+1
                    for person in frame_people:
                        new_people.append((last_id, person, None))
                        last_id += 1

                people = new_people

            tracking_elapsed = time.time() - tracking_start

            # age & gender detection

            if people and key is not None and key & 0xFF == ord('d'):
                # age_gender_start = time.time()

                # _, people_data, _ = zip(*people)
                # people_bboxes, _ = zip(*people_data)

                # predictions = age_gender_predictor(left_frame, people_bboxes)

                # for i, gender, age in predictions:
                #     people[i] = (people[i][0], people[i][1], (gender, age))

                age_gender_elapsed = 1e-3  # time.time() - age_gender_start
            else:
                age_gender_elapsed = 1e-3

            end = time.time()
            elapsed = end - start
            frame_elapsed = end - cap_start
            start = end

            print('\rFPS: %.2f; CAP: %.2f; DISP: %.2f; SKLTN: %.2f; TRACK: %.2f; AGE: %.2f; FRAME: %.2f, PPL_FOUND: %i' %
                  (1/elapsed, 1/cap_elapsed, 1/disp_elapsed, 1/skeleton_elapsed, 1/tracking_elapsed, 1/age_gender_elapsed, 1/frame_elapsed, len(people)), end='', flush=True)

            display_image = cv.cvtColor(left_frame, cv.COLOR_GRAY2BGR)

            display_image[..., 2] = np.uint8(
                255*np.clip((np.float32(display_image[..., 2]) / 255) + 0.9*min_max_norm(np.max(joints_map, axis=-1)), 0, 1))
            display_image[..., 1] = np.uint8(
                255*np.clip((np.float32(display_image[..., 1]) / 255) + 0.5*min_max_norm(np.max(np.linalg.norm(bones_map, axis=-1), axis=-1)), 0, 1))

            if people:
                _, people_data, _ = zip(*people)
                _, skeletons = zip(*people_data)

                display_image = draw_skeleton(display_image, skeletons)
                for id_, (bbox, _), extra in people:
                    cv.rectangle(display_image, (int(bbox[0]), int(bbox[1])),
                                 (int(bbox[2]), int(bbox[3])), (32, 32, 225))

                    person_line = ('#%i' % id_)
                    if extra is not None:
                        gender, age = extra

                        person_line = '%s a: %i' % (person_line, age)
                        person_line = '%s %s' % (person_line, 'M' if gender == 1 else 'F')

                    cv.putText(display_image, person_line, (int(bbox[0])+5, int(bbox[1])+16),
                               cv.FONT_HERSHEY_SIMPLEX, 0.5, (160, 32, 225), 2)

            cv.imshow('demo', display_image)
            if config.ENABLE_DISPARITY:
                cv.imshow('disparity', prepare_for_vis(disparity_map))

            if config.DEBUG_WRITE:
                if isinstance(writer, int):
                    writer += 1
                    if writer > 2:
                        writer = cv.VideoWriter('./demo.mp4', cv.VideoWriter_fourcc(*'avc1'), 1/elapsed, (int(
                            cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))))
                        if config.ENABLE_DISPARITY:
                            disp_writer = cv.VideoWriter('./demo-disps.mp4', cv.VideoWriter_fourcc(*'avc1'), 1/elapsed, (int(
                                cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))), False)
                        left_writer = cv.VideoWriter('./demo-lefts.mp4', cv.VideoWriter_fourcc(*'avc1'), 1/elapsed, (int(
                            cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))), False)
                        right_writer = cv.VideoWriter('./demo-rights.mp4', cv.VideoWriter_fourcc(*'avc1'), 1/elapsed, (int(
                            cap.get(cv.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))), False)
                else:
                    writer.write(display_image)
                    if config.ENABLE_DISPARITY:
                        disp_writer.write(prepare_for_vis(disparity_map))
                    left_writer.write(left_frame)
                    right_writer.write(right_frame)

            key = cv.waitKey(1)
            if key == 27:
                break
            if key & 0xFF == ord('q'):
                break
    except:
        print('', flush=True)
        raise
    finally:
        cap.release()
        if config.DEBUG_WRITE:
            if not isinstance(writer, int):
                writer.release()
                if config.ENABLE_DISPARITY:
                    disp_writer.release()
                left_writer.release()
                right_writer.release()

if __name__ == "__main__":
    main()


from zope.interface import implements
from zope.component import adapts
from AccessControl import getSecurityManager
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.interfaces import IContentish
from plone.app.layout.nextprevious.interfaces import INextPreviousProvider
from plone.app.layout.navigation.interfaces import INavigationRoot
from plone.app.folder.folder import IATUnifiedFolder

class NextPrevious(object):
    """ adapter for acting as a next/previous provider """
    implements(INextPreviousProvider)
    adapts(IATUnifiedFolder)

    def __init__(self, context):
        self.context = context
        props = getToolByName(context, 'portal_properties').site_properties
        self.vat = props.getProperty('typesUseViewActionInListings', ())
        self.security = getSecurityManager()
        order = context.getOrdering()
        if not isinstance(order, list):
            order = order.idsInOrder()
        if not isinstance(order, list):
            order = None
        self.order = order

    @property
    def enabled(self):
        includeInNav = not self.context.getExcludeFromNav()
        nextPreviousEnabled = self.context.getNextPreviousEnabled()
        # only report 'enabled' True when both conditions are met
        return includeInNav and nextPreviousEnabled

    def getFirstItem(self):
        items = self.context.objectValues()[:]
        return items and items[0] or None

    def getLastItem(self):
        items = self.context.objectValues()[:]
        items.reverse()
        return items and items[0] or None

    def isFirstItem(self, obj):
        if obj is None:
            raise TypeError("Cannot compare 'None' to objects in container.")
        firstItem = self.getFirstItem()
        return obj == firstItem and True or False

    def isLastItem(self, obj):
        if obj is None:
            raise TypeError("Cannot compare 'None' to objects in container.")
        lastItem = self.getLastItem()
        return obj == lastItem and True or False

    def getNextItem(self, obj, camefrom=None):
        """ return info about the next item in the container """
        if not self.order:
            return None
        pos = self.context.getObjectPosition(obj.getId())
        for oid in self.order[pos+1:]:
            data = self.getData(self.context[oid])
            if data:
                return data
        # couldn't find one, let's see of our parent knows
        parent = INextPreviousProvider(self.context.aq_parent)
        if parent and parent.enabled:
            return parent.getNextItem(self.context, camefrom)
        return None

    def getPreviousItem(self, obj, camefrom=None):
        """ return info about the previous item in the container """
        if not self.order:
            return None
        order_reversed = list(reversed(self.order))
        pos = order_reversed.index(obj.getId())
        for oid in order_reversed[pos+1:]:
            data = self.getData(self.context[oid])
            if data:
                return data
        # couldn't find one, let's see of our parent knows
        if self.isFirstItem(obj) and INavigationRoot.providedBy(self.context):
            return None
        else:
            parent = INextPreviousProvider(self.context.aq_parent)
            if parent and parent.enabled:
                return parent.getPreviousItem(self.context, camefrom)
        return None

    def getData(self, obj):
        """ return the expected mapping, see `INextPreviousProvider` """
        if not self.security.checkPermission('View', obj):
            return None
        elif not IContentish.providedBy(obj):
            # do not return a not contentish object
            # such as a local workflow policy for example (#11234)
            return None
        # pay attention to the 'excludedFromNav' attribute itself too
        elif obj.getExcludeFromNav():
            return None

        portal_properties = getToolByName(self.context, 'portal_properties')
        navtree_properties = getattr(portal_properties, 'navtree_properties')
        if obj.portal_type in navtree_properties.getProperty(
            'metaTypesNotToList', ()):
            return None

        ptype = obj.portal_type
        url = obj.absolute_url()
        if ptype in self.vat:       # "use view action in listings"
            url += '/view'
        return dict(id=obj.getId(), url=url, title=obj.Title(),
            description=obj.Description(), portal_type=ptype)


import logging
from pprint import pprint
from sys import stdout as STDOUT

# Example 1
a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
print('First four:', a[:4])
print('Last four: ', a[-4:])
print('Middle two:', a[3:-3])

# Example 2
assert a[:5] == a[0:5]

# Example 3
assert a[5:] == a[5:len(a)]

# Example 4
print(a[:5])
print(a[:-1])
print(a[4:])
print(a[-3:])
print(a[2:5])
print(a[2:-1])
print(a[-3:-1])

# Example 5
a[:]      # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
a[:5]     # ['a', 'b', 'c', 'd', 'e']
a[:-1]    # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
a[4:]     #                     ['e', 'f', 'g', 'h']
a[-3:]    #                          ['f', 'g', 'h']
a[2:5]    #           ['c', 'd', 'e']
a[2:-1]   #           ['c', 'd', 'e', 'f', 'g']
a[-3:-1]  #                          ['f', 'g']

# Example 6
first_twenty_items = a[:20]
last_twenty_items = a[-20:]

# Example 7
try:
    a[20]
except:
    logging.exception('Expected')
else:
    assert False

# Example 8
b = a[4:]
print('Before:   ', b)
b[1] = 99
print('After:    ', b)
print('No change:', a)

# Example 9
print('Before ', a)
a[2:7] = [99, 22, 14]
print('After  ', a)

# Example 10
b = a[:]
assert b == a and b is not a

# Example 11
b = a
print('Before', a)
a[:] = [101, 102, 103]
assert a is b           # Still the same list object
print('After ', a)      # Now has different contents

079_high_performance/exercises/template/Writing High Performance Python/item_05.py


import time
from .deployer_file import Deployer
from django.shortcuts import render
from rest_framework.views import APIView
from django.http.response import JsonResponse
import os
from AzureSite.settings import BASE_DIR
import json
import random
import string
import glob
from django.views.generic import TemplateView
from azure.storage.blob import BlockBlobService
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.storage import StorageManagementClient
from azure.mgmt.storage.models import StorageAccountCreateParameters
# from .databricks_linux import databricks

# read Data for form config File
def read_mapping():
    """

    :return: data containing sections and sectionAttributes to be shown on frontend
    """
    path = str(os.path.join(BASE_DIR, 'AzureSite\sections.json')).replace('\\', '/')
    with open(path) as file:
        data = json.load(file)
    return data

# Funtion for generating random unique id
def random_id(request):
    """

    :param request:
    :return: Returns a random string of 16 characters
    """
    uniqueID = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(16))
    return JsonResponse({'id': uniqueID})

def azure_account(data):
    """

    :param data: request data
    :return: resource_id and client_id
    """
    sections_data = data['sections']
    client_id = ''
    secret = ''
    tenant = ''
    subscription_id = ''
    try:
        for section in range(len(sections_data)):
            try:
                if sections_data[section]['title']=='Subscription Details':
                    subscription_id = sections_data[section]['sectionAttributes'][0]['value']
                    tenant = sections_data[section]['sectionAttributes'][1]['value']
                    client_id = sections_data[section]['sectionAttributes'][2]['value']
                    secret = sections_data[section]['sectionAttributes'][3]['value']
                    resource_group = sections_data[section]['sectionAttributes'][4]['value']
                elif sections_data[section]['title'] =='Storage Account Details':
                    storage_account_name = sections_data[section]['sectionAttributes'][0]['value']
            except Exception as e:
                print('error in for loop of azure_function', e)
        credentials = ServicePrincipalCredentials(
            client_id=client_id,
            secret=secret,
            tenant=tenant
        )

        resource_client = ResourceManagementClient(credentials, subscription_id)
        storage_client = StorageManagementClient(credentials, subscription_id)

    except Exception as e:
        print('error in azure_account function:', e)
    return resource_client, storage_client, storage_account_name, resource_group

class azure_functions(APIView):
    def get(self, request):
        return JsonResponse({"success": "No Data to Display"})

    def post(self, request):
        try:
            resource_client, storage_client, storage_account_name, resource_group = azure_account(request.data)
            # Checking availability for storage account name
            if storage_account_name != '':
                availability = storage_client.storage_accounts.check_name_availability(storage_account_name)
                reason = availability.reason
                if reason is not None and reason == "AlreadyExists":
                    return JsonResponse({"status": "success", "message": ""})
                else:
                    return JsonResponse({"status": "failed", "message": "Storage Account doesn't exist"})
            # Checking availability for resource group
            else:
                resource_availability = resource_client.resource_groups.check_existence(resource_group)
                if resource_availability:
                    return JsonResponse({"status": "success", "message": ""})
                else:
                    return JsonResponse({"status": "failed", "message": "Resource Group Doesn't exist"})
        except Exception as e:
            print('In azure_function exception', e)
            return JsonResponse({'status': 'failed'})

class index(TemplateView):
    template_name = "index.html"

    def get(self, request):
        try:
            form_data = read_mapping()
        except Exception as e:
            return render(request, self.template_name, context={'message': 'failed'})
        # return JsonResponse({'message': 'Successful'})
        return render(request, self.template_name, context={'message': 'Successful'})

class SupplyChain(APIView):

    def get(self, request):
        """

        :param request: request data
        :return: JsonResponse containing form sections detail
        """
        try:
            data = read_mapping()
        except Exception as e:
            return JsonResponse({'Output': 'Error in reading json file ' + str(e)})
        return JsonResponse({'message': 'Successful', 'formConfig': data})

    def post(self, request):
        """

        :param request: Request Data
        :return:
        """
        path = str(os.path.join(BASE_DIR +'\AzureSite\parameters.json')).replace('\\', '/')
        try:
            with open(path) as file:
                param_file = json.load(file)
                adf_parameters = param_file['ADFParameters']['values']
                vault_parameters = param_file['KeyVaultParameters']['values']
                adf_dict = { '$schema': param_file['general']['schema'],
                            'contentVersion': param_file['general']['contentVersion'], 'parameters': {}}
                vault_dict = { '$schema': param_file['general']['schema'],
                              'contentVersion': param_file['general']['contentVersion'], 'parameters': {}}
            # Container Name to be used in Azure Deployment
            container_name = param_file['containername']
            # Convert Parameter list to lowercase
            adf_parameters = list(map(lambda func: func.lower(), adf_parameters))
            vault_parameters = list(map(lambda func: func.lower(), vault_parameters))
        except Exception as e:
            print('error opening json file')
        try:
            section_data = request.data['sections']
        except Exception as e:
            return JsonResponse({"message": "error in request data" + str(e)})

        for section_attr in range(len(section_data)):
            try:
                sectionAttributes = section_data[section_attr]['sectionAttributes']
                for sectionAttribute in range(len(sectionAttributes)):
                    try:
                        name = sectionAttributes[sectionAttribute]['internalName']
                        value = sectionAttributes[sectionAttribute]['value']
                        if name.lower() in adf_parameters:
                            adf_dict['parameters'][name] = {'value': value}
                        if name.lower() in vault_parameters:
                            vault_dict['parameters'][name] = {'value': value}
                    except Exception as e:
                        print('error in inner loop', sectionAttribute, str(e))
            except Exception as e:
                print('error in in outer loop at index ', section_attr)
                return JsonResponse({'Output': 'error'})
        # For optional Table

        select_variables = ["SAP", "SalesForce", "Oracle", "BQ"]

        try:
            selected_value = adf_dict['parameters']['Sources']['value']
            # Extract submitted data for selected field

            if selected_value in select_variables:
                temp_param = (selected_value) + 'Tables'
                subsections = section_data[3]['subsections']['sections'][selected_value]['subsectionAttributes']

                schemas = []
                tables = []
                for i in range(len(subsections)):
                    if subsections[i]['internalName'] == selected_value+'Schema':
                        schemas = subsections[i]['value'].split(',')
                    elif subsections[i]['internalName'] == selected_value+'Tables':
                        tables = subsections[i]['value'].split(',')
                    else:

                        adf_dict['parameters'][subsections[i]['internalName']] = {'value': subsections[i]['value']}
                adf_dict['parameters'][temp_param] = {"value": []}
                for i in range(len(schemas)):
                    adf_dict['parameters'][temp_param]["value"].append({
                        "table_schema": schemas[i],
                        "table_name": tables[i]})
            else:
                # when Blob Option is choosen
                adf_dict['parameters']["BlobTable"] = {"value": []}
                tables = section_data[3]['subsections']['sections'][selected_value]['subsectionAttributes'][0]['value'].split(',')
                for table in range(len(tables)):
                    adf_dict['parameters']["BlobTable"]['value'].append({'table_name': tables[table]})

        except KeyError as e:
            print("Keyerror Exception in creating table data: ", e)
            return JsonResponse({"message": "error"})
        except Exception as e:
            print('Exception in creating table data: ', e)
            return JsonResponse({"message": "error"})

        # Get all required parameters from submitted data for Azure Deployment
        try:
            connectionstring = vault_dict['parameters']['StorageConnectionString']['value']
            account_name = vault_dict['parameters']['StorageAccountName']['value']
            access_key = vault_dict['parameters']['StorageAccessKey']['value']

            for key in range(len(section_data)):
                try:
                    if section_data[key]['title']=='Subscription Details':
                        subscription_id = section_data[key]['sectionAttributes'][0]['value']
                        tenant = section_data[key]['sectionAttributes'][1]['value']
                        client_id = section_data[key]['sectionAttributes'][2]['value']
                        secret = section_data[key]['sectionAttributes'][3]['value']
                        resource_group = section_data[key]['sectionAttributes'][4]['value']
                    elif section_data[key]['title'] =='Storage Account Details':
                        storage_account_name = section_data[key]['sectionAttributes'][0]['value']
                except Exception as e:
                    print("Exception in initializing various keys: ", e)
                    client_id = ''
                    secret = ''
                    tenant = ''
                    subscription_id = ''
                    resource_group = ''

        except KeyError as key:
            print('Key Not found', key)
        # Azure Deployment code
        try:
            with open('upload_files/ADFParameters.json', 'w') as adf:
                json.dump(adf_dict, adf)
            with open('upload_files/KeyVaultParameters.json', 'w') as kv:
                json.dump(vault_dict, kv)

            print("adf and kv files created")
            # resource_client, storage_client = azure_account(request.data)
            # Create a storage blob container to store the files
            # Create the BlockBlockService that is used to call the Blob service for the storage account.
            block_blob_service = BlockBlobService(
                account_name=account_name,
                account_key=access_key)
            print('blob created')
            # Create a container called 'quickstartblobs'.
            # container_name = 'marketplacecodes'
            create_container = False
            counter = 0
            while create_container != True and counter < 5:
                create_container = block_blob_service.create_container(container_name)
                counter = counter + 1
                print("create_container status: ", create_container)
                time.sleep(10)
            # Set the permission so the blobs are public.
            # block_blob_service.set_container_acl(
            #     container_name, public_access=PublicAccess.Container)
            # retrive conn string from storage variable "storageconnstring" for azure push

            blob_name_adf = "ADFParameters.json"
            blob_name_kv = "KeyVaultParameters.json"
            blob_client = BlockBlobService(connection_string=connectionstring)
            for filepath in glob.iglob('upload_files/*.json'):
                try:
                    file_name = filepath.split("\\")
                    response_obj = blob_client.create_blob_from_path(container_name=container_name, blob_name=file_name[1],
                                                             file_path=os.path.join(BASE_DIR, filepath))
                    print("blob_create status: ", response_obj)
                    time.sleep(5)
                except Exception as e:
                    print("Exception in creating Blob: ", e)

            # Deplayment of blobs
            try:
                deployment_obj = Deployer(resource_group, subscription_id, client_id, secret,
                                          tenant, container_name)
                adf_obj = deployment_obj.deploy("upload_files/DataFactoryDeployment.json",
                                                "upload_files/ADFParameters.json", connectionstring)
                kv_obj = deployment_obj.deploy("upload_files/KeyVaultDeployment.json",
                                               "upload_files/KeyVaultParameters.json", connectionstring)
            except Exception as e:
                print("Exception in deploy the data factory: ", e)
            # Remove conatiner from storage account after deployment
            try:
                try:
                    databricksToken = vault_dict['parameters']['DataBricksToken']
                    databricksScope = vault_dict['parameters']['DataBricksScope']
                    databricksURL = vault_dict['parameters']['DataBricksWorkspaceURL']
                    # databricks.main(databricksURL, databricksToken, databricksScope)
                except Exception as e:
                    print('exception in databricks function')

                time.sleep(720)
                delete_container = blob_client.delete_container(container_name)
                print("delete container: ", delete_container)
            except Exception as e:
                print("Exception in removing conatiner from storage account: ", e)
        except Exception as e:
            print("exception in azure conn: " + str(e))
        print('done')

        return JsonResponse({"message": "Successful"})


from enum import Enum
import math

class Vertex:
    """
    Graph vertex: A graph vertex (node) with data
    """
    def __init__(self, c = None, p = None,dist = None,n = None):
        """
        Vertex constructor
        @param color, parent, auxilary data1, auxilary data2
        """
        self.c = c
        self.p = p
        self.n = n
        self.dist = dist

class Graph:
    """
    Graph data: Any object which is used as a graph vertex data
    """
    def __init__(self, lv, le):
        """
        Data constructor
        @param A list of values assigned to object's attributes
        """
        self.lv = lv
        self.le = le

class Edge:
    """
    Graph data: Any object which is used as a graph vertex data
    """
    def __init__(self, src, dst, weight):
        """
        Data constructor
        @param A list of values assigned to object's attributes
        """
        self.src = src
        self.dst = dst
        self.weight = weight

class VertexColor(Enum):
        BLACK = 0
        GRAY = 127
        WHITE = 255

def makeGraph(lv,le):
	return Graph(lv,le)

def printGraph(G):
	for v in G.lv:
		print(v.n)
	for e in G.le:
		print(e.src.n + " " + e.dst.n + " - " + str(e.weight))

def getInDegrees(G):
	l = []
	for v in G.lv:
		cnt = 0
		for e in G.le:
			if e.dst.n == v.n:
				cnt = cnt + 1
		l.append(cnt)

	return l

def getOutDegrees(G):
	l = []
	for v in G.lv:
		cnt = 0
		for e in G.le:
			if e.src.n == v.n:
				cnt = cnt + 1
		l.append(cnt)

	return l

def initializeSingleSource(G,s):
	for v in G.lv:
		v.dist = math.inf
		v.p = None
	s.dist = 0

def relax(u,v,w):
	if v.dist > u.dist + w:
		v.dist = u.dist + w
		v.p = u

def bellmanFord(G,s):
	initializeSingleSource(G,s)
	for i in range(0,len(G.lv)):
		for e in G.le:
			relax(e.src, e.dst, e.weight)
	for e in G.le:
		if e.dst.dist > e.src.dist + e.weight:
			return False
	return True

def getEdge(G,u,v):
	for e in G.le:
		if e.src == u and e.dst == v:
			return e
	return None

def shortestPath(G,s,v):
	bellmanFord(G,s)
	l = []
	duzina = 0
	a = v
	while a is not None:
		l.append(a.n)
		if a.p != None:
			duzina = duzina + getEdge(G,a.p,a).weight
		a = a.p
	l.reverse()
	return l, duzina

def updateEdge(G,s,v,weight):
	edge = getEdge(G,s,v)
	if edge == None:
		e = Edge(s,v,weight)
		G.le.append(e)
	else:
		edge.weight = weight

#********************************************
a = Vertex(n = "a")
b = Vertex(n = "b")
c = Vertex(n = "c")
d = Vertex(n = "d")
e = Vertex(n = "e")
f = Vertex(n = "f")
g = Vertex(n = "g")
lv = [a,b,c,d,e,f,g]
ab = Edge(a,b,8)
ac = Edge(a,c,6)
bd = Edge(b,d,10)
cd = Edge(c,d,15)
ce = Edge(c,e,9)
de = Edge(d,e,14)
df = Edge(d,f,4)
ef = Edge(e,f,13)
eg = Edge(e,g,17)
fg = Edge(f,g,7)
le = [ab,ac,bd,cd,ce,de,df,ef,eg,fg]

G = makeGraph(lv,le)
printGraph(G)
l1 = getInDegrees(G)
l2 = getOutDegrees(G)

for i in l1:
	print(i)

print("\n")

for i in l2:
	print(i)

print("\n")
l,d = shortestPath(G,a,g)

print(d)
for i in l:
	print(i)

updateEdge(G,b,c,-4)
printGraph(G)


from django.http import HttpResponse
from django.shortcuts import render
from . models import Gig

#waffle decorator for feature toggling
from waffle.decorators import waffle_switch

@waffle_switch('gig_global_features')
def index(request):
	gig_list = Gig.objects.order_by('-created_date')
	context = {
		'gig_list':gig_list,
	}
	return render(request, 'gigs/index.html', context)

@waffle_switch('gig_global_features')
def gig_detail(request, gig_pk):
	gig = Gig.objects.get(pk=gig_pk)
	context = {'gig': gig}
	return render(request, 'gigs/gig_detail.html', context)


import requests
import urllib.request
import json
import time
import threading
import random, string

class PostThread(threading.Thread):
    def __init__(self, random_str):
        threading.Thread.__init__(self)
        self.id = random_str
        self.msg_count = 0
        self.start()

    def run(self):
        for i in range(1):
            random_str = ''.join(random.sample(string.ascii_letters + string.digits, 8))
            print(random_str)
            item = {
                "fromId": 101676,
                "fromType": "fromType",
                "model": 0,
                "msgBody": {
                    "msgContent": {
                        "arn": "arn",
                        "durationTime": 7,
                        "extras": "extras",
                        "fileSize": 2,
                        "format": "format",
                        "height": 5,
                        "mediaCrc32": "mediaCrc32",
                        "mediaId": "mediaId",
                        "text": "text",
                        "width": 5
                    },
                    "msgType": "text"
                },
                "msgType": "msgType",
                "targetId": 2,
                "targetType": "targetType",
                "version": 0,
                "time_stamp": time.time()
            }
            request_url = "http://127.0.0.1:5000/messages/public"
            r = requests.post(url=request_url, data=json.dumps(item), headers=header)
            # print(r)

if __name__ == "__main__":
    random_str = ''.join(random.sample(string.ascii_letters + string.digits, 32))
    # print(random_str)

    header = {"Accept": "application/json",
    # "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Content-Type": "application/json",
    "Host": "10.152.21.175:5000",
    "Origin": "http://10.152.21.175:5000",
    "Referer": "http://10.152.21.175:5000/ui/",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
        }

    # item = {"account": ["qiaoyuan", "tianxb", "heyx1"], "message": "test", "tag": 1, "title": "title", "extend": ""}
    # request_url = "https://cbazc8kgok.execute-api.cn-northwest-1.amazonaws.com.cn/test/"
    # r = requests.post(url=request_url, data=json.dumps(item))
    # print(r)

    for i in range(10):
        pt = PostThread(str(i))

    # class DataItem(object):
    #     """
    #     asdasdasd
    #     """
    #     # __slots__ = ['name', 'age', 'address']
    #     def __init__(self, name, age, address):
    #         self.name = name
    #         self.age = age
    #         self.address = address
    #
    #
    # def dump(obj):
    #     for attr in dir(obj):
    #         print("  obj.%s = %r" % (attr, getattr(obj, attr)))
    #
    #
    # test = DataItem('1', '2', '3')
    # dump(test)
    #
    # print(test.__dict__)
    #
    # print(test.__eq__())


# -*- coding: utf-8 -*-
import random
import sys, pygame

from bonus import Bonus
from menu import Menu
from not_bug import NotBug
from player import Player
from bug import Bug
from results import Results
from timer import Timer
from suriken import Suriken
from pygame.image import load

pygame.init()
pygame.mouse.set_visible(False)
''' Вікно '''
size = width, height = 600, 630
window = pygame.display.set_mode(size)
''' Скріни '''
info_screen = pygame.Surface((600, 30))
screen = pygame.Surface((600, 600))
bg_image = load('images/bg.png')
'''Шрифти'''
pygame.font.init()
inf_font = pygame.font.Font(None, 27)
'''Меню'''
menu = Menu()
menu.menu(window)

player = Player()
player.name = "".join(menu.current_login)
left = right = False
sprite_group = pygame.sprite.Group()
sprite_group.add(player)

bug_army = []
bug_police = []
bonus_array = []
timer = Timer()
suriken_move = []
fps = pygame.time.Clock()

while True:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                left = True
            if event.key == pygame.K_RIGHT:
                right = True

            if event.key == pygame.K_SPACE:
                if not suriken_move:
                    suriken = Suriken(x=player.rect.x, y=player.rect.y)
                    suriken_move.append(suriken)
                    sprite_group.add(suriken)

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                left = False
            if event.key == pygame.K_RIGHT:
                right = False

            if event.key == pygame.K_ESCAPE:
                timer.pause_game()
                menu.menu(window)
                timer.unpause_game()

    '''Заливка'''
    screen.blit(bg_image, (0, 0))
    info_screen.fill((50, 50, 50))

    player.move(left, right)
    # Створюємо баги
    if len(bug_army) < 4:
        while len(bug_army) != 4:
            rand = random.randrange(10, 550)
            rand_speed = random.randrange(1, 5)
            bug = Bug(x=rand, speed=rand_speed)
            bug_army.append(bug)
            sprite_group.add(bug)
    # Створюємо не баги
    if len(bug_police) < 2:
        while len(bug_police) != 2:
            rand = random.randrange(10, 550)
            rand_speed = random.randrange(1, 5)
            notbug = NotBug(x=rand, speed=rand_speed)
            bug_police.append(notbug)
            sprite_group.add(notbug)

    if not bonus_array and int(timer.lost_time()) % 10 == 0:
            rand = random.randrange(10, 550)
            rand_speed = random.randrange(10, 11)
            bonus = Bonus(x=rand, speed=rand_speed)
            bonus_array.append(bonus)
            sprite_group.add(bonus)
    sprite_group.draw(screen)

    for b in bug_army:
        b.move(player, bug_army, suriken_move)
    for b in bug_police:
        b.move(player, bug_police, screen)
    for i in suriken_move:
        i.move(suriken_move)
    for i in bonus_array:
        i.move(player, bonus_array, timer)

    info_screen.blit(inf_font.render(u'Багов закрыто: '+str(player.bug_kill), 1, (212, 120, 49)), (10,5))
    info_screen.blit(inf_font.render(u'Время до релиза: ' + str(int(timer.lost_time())), 1, (212, 120, 49)), (190, 5))
    info_screen.blit(inf_font.render(u'Багов в релизе: ' + str(player.bug_miss), 1, (212, 120, 49)), (400, 5))

    if timer.lost_time() < 0 or player.bug_kill <= -1:
        # Після натискання Ретест обнуляємо всі елементи в грі
        res = Results(player)
        res.results(window, player)
        player.refresh()
        timer.start_game()
        for i in bug_army:
            i.remove(bug_army)
        for i in bug_police:
            i.remove(bug_police)

    window.blit(info_screen, (0, 0))
    window.blit(screen, (0, 30))

    pygame.display.flip()
    fps.tick(60)