"""Defines event listeners for:

    - creating table objects
    - generating trigger/procedure DDL for audit tables.

"""
from sqlalchemy import Column, DateTime, String, Table

from sqlalchemy_postgresql_audit.ddl import (
    get_audit_spec,
    get_create_trigger_ddl,
    get_drop_trigger_ddl,
)
from sqlalchemy_postgresql_audit.dialect import (
    DEFAULT_AUDIT_TABLE_FUNCTION_NAMING_CONVENTION,
    DEFAULT_AUDIT_TABLE_NAMING_CONVENTION,
    DEFAULT_AUDIT_TABLE_TRIGGER_CONVENTION,
)

def create_audit_table(target, parent):
    """Create an audit table and generate procedure/trigger DDL.

    Naming conventions can be defined for a few of the named elements:

        - audit.table: Controls the name of the table
        - audit.function: Controls the name of the function
        - audit.trigger: Controls the name of the trigger on the table

    This function creates a new companion table to store row versions.

    Any :class:`sqlalchemy.sql.schema.Column`s specified in `table.info['session_settings']` will
    be copied and included in the audit table.

    This function will leave a key in the audited table:

        .. code-block:: python

            table.info['audit.is_audited']

    And a key in the audit table:

        .. code-block:: python

            table.info['audit.is_audit_table']

    Additionally you can find the relevant create/drop ddl at the followng keys:

        .. code-block:: python

            table.info['audit.create_ddl']
            table.info['audit.drop_ddl']

    :param target: The :class:`sqlalchemy.sql.schema.Table` to make an audit table for
    :param parent: The :class:`sqlalchemy.sql.schema.MetaData` to associate the audit table with.
    :return: None
    """
    audit_spec = get_audit_spec(target)

    if not audit_spec.get("enabled"):
        return

    audit_table_naming_convention = parent.naming_convention.get(
        "audit.table", DEFAULT_AUDIT_TABLE_NAMING_CONVENTION
    )
    audit_function_naming_convention = parent.naming_convention.get(
        "audit.function", DEFAULT_AUDIT_TABLE_FUNCTION_NAMING_CONVENTION
    )
    audit_trigger_naming_convention = parent.naming_convention.get(
        "audit.trigger", DEFAULT_AUDIT_TABLE_TRIGGER_CONVENTION
    )

    audit_table_name = audit_table_naming_convention % {
        "table_name": target.name,
        "schema": audit_spec["schema"] or "public",
    }
    audit_function_name = audit_function_naming_convention % {
        "table_name": target.name,
        "schema": audit_spec["schema"] or "public",
    }
    audit_trigger_name = audit_trigger_naming_convention % {
        "table_name": target.name,
        "schema": audit_spec["schema"] or "public",
    }

    columns = [
        Column(col.name, col.type, nullable=True) for col in target.columns.values()
    ]
    session_setting_columns = [col.copy() for col in audit_spec["session_settings"]]
    for col in session_setting_columns:
        col.name = "audit_{}".format(col.name)

    column_elements = session_setting_columns + columns

    audit_table = Table(
        audit_table_name,
        target.metadata,
        Column("audit_operation", String(1), nullable=False),
        Column("audit_operation_timestamp", DateTime, nullable=False),
        Column("audit_current_user", String(64), nullable=False),
        *column_elements,
        schema=audit_spec["schema"]
    )

    target.info["audit.create_ddl"] = get_create_trigger_ddl(
        target.columns,
        audit_table.columns,
        audit_function_name,
        audit_trigger_name,
        target.fullname,
        audit_table.fullname,
        session_setting_columns,
    )

    target.info["audit.drop_ddl"] = get_drop_trigger_ddl(
        audit_function_name, audit_trigger_name, target.fullname
    )
    audit_table.info["audit.target_table"] = target

    audit_table.info["audit.is_audit_table"] = True
    target.info["audit.is_audited"] = True


import matplotlib.pyplot as plt

class Plotter:
    def plot(self, xy_df, sort_column=None, x_label=None, y_label=None):
        if sort_column is not None:
            xy_df = xy_df.sort_values([sort_column])

        plt.plot(xy_df.iloc[:, 0], xy_df.iloc[:, 1].to_list())

        if x_label is not None:
            plt.xlabel(x_label)

        if y_label is not None:
            plt.ylabel(y_label)

        plt.show()

    def show_histogram(self, df):
        df.hist()
        plt.show()

    def show_box_plot(self, df):
        plt.figure(figsize=(8, 4), dpi=288)
        df.plot(kind='box', subplots=True)
        plt.show()


#coding:utf8
"""
    数据统计脚本
    依赖包：time, pymongo, pandas
"""
__auth__ = "胡岸"
class Statistic(object):
    """生产环境数据统计脚本"""
    def __init__(self, strdate=""):
        from datetime import datetime, timedelta
        self.db = {
            "HOST":"localhost",
            "PORT":28010,
        }
        # 获得查询日期的开始和结束时间戳
        self.sdate = {
            "stime":int(0), # 查询日期的开始时间戳
            "etime":int(0)  # 查询日期的结束时间戳
        }
        if strdate:
            self.sdate = {
                "sdate":strdate,
                "stime":int(datetime.strptime(strdate, "%Y-%m-%d").timestamp()),
                "etime":int(datetime.strptime(strdate, "%Y-%m-%d").timestamp() + 86400)
            }
        else:
            self.sdate = {
                "sdate":datetime.strftime(datetime.now() + timedelta(days=-1), "%Y-%m-%d"),
                "stime":int(datetime.strptime((datetime.now() + timedelta(days=-1)).strftime("%Y-%m-%d"), "%Y-%m-%d").timestamp()),
                "etime":int(datetime.strptime((datetime.now() + timedelta(days=-1)).strftime("%Y-%m-%d"), "%Y-%m-%d").timestamp() + 86400)
            }
        self.result = {
            "date": self.sdate["sdate"],    # 日期
            "totaluser":int(0),     # 累计注册用户
            "newuser":int(0),       # 新增注册用户
            "newstore":int(0),      # 新增商户，即新开店的账号
            "loginuser":int(0),     # 登录用户数量
            "loginip":int(0),       # 登录ip数量
            "loginnum":int(0),      # 登录记录数
            "chargenum":int(0),     # 充值次数
            "chargesum":int(0),     # 充值金额
            "chargeuser":int(0),    # 充值账号数
            "mallview":int(0),      # 商城访问次数，商品和店铺页面打开次数合计
            "storeview":int(0),     # 店铺访问次数，只包括店铺页面的访问记录
            "topstore":[],          # 店铺访问top10
            "waresview":int(0),     # 商品访问次数，商品页面打开次数合计
            "topwares":[],          # 商品访问次数TOP10
        }
        self.client = ""    # 保存数据库连接
        self.conn = ""      # 保存数据连接

    def mongo_conn(self, dbname="iTROdb", coname="iTRO_User"):
        """建立数据库连接"""
        from pymongo import MongoClient
        try:
            self.client.server_info()
        except Exception as errinfo:
            self.client = MongoClient(host=self.db["HOST"], port=self.db["PORT"])
        finally:
            self.conn = self.client.get_database(dbname)
            self.conn = self.conn.get_collection(coname)

    def to_mongo(self):
        """写入数据到108数据库"""
        from pymongo import MongoClient
        client = MongoClient("172.26.143.73", 28010)
        # client = MongoClient("47.92.72.108", 28010)
        conn = client.iTROdb.iTRO_Statistic
        conn.update(
            {"sdate":self.sdate["sdate"]},
            {"$set":{
                "totaluser":self.result["totaluser"],
                "newuser":self.result["newuser"],
                "newstore":self.result["newstore"],
                "loginuser":self.result["loginuser"],
                "loginip":self.result["loginip"],
                "loginnum":self.result["loginnum"],
                "chargenum":self.result["chargenum"],
                "chargesum":self.result["chargesum"],
                "chargeuser":self.result["chargeuser"],
                "mallview":self.result["mallview"],
                "storeview":self.result["storeview"],
                "topstore":self.result["topstore"],
                "waresview":self.result["waresview"],
                "topwares":self.result["topwares"],
            }},
            upsert=True
        )

    def s_totaluser(self):
        """指定日期查询累计用户数量"""
        self.mongo_conn(dbname="iTROdb", coname="iTRO_User")
        self.result["totaluser"] = self.conn.find({"CreateDt":{"$lt":self.sdate["etime"]}}).count()

    def s_newuser(self):
        """指定日期查询新增注册用户"""
        self.mongo_conn(dbname="iTROdb", coname="iTRO_User")
        self.result["newuser"] = self.conn.find({"CreateDt":{"$gte":self.sdate["stime"], "$lt":self.sdate["etime"]}}).count()

    def s_newstore(self):
        """查询新增商户，iTRO_OpenShopOrder.status=2，以支付保证金订单成功为准"""
        self.mongo_conn(dbname="iTROdb", coname="iTRO_OpenShopOrder")
        self.result["newstore"] = self.conn.find({"paydatee":{"$gte":self.sdate["stime"], "$lt":self.sdate["etime"]}}).count()

    def s_charge(self):
        """查询充值次数、金额合计和充值账号数量，以充值成功为准"""
        self.mongo_conn(dbname="iTROdb", coname="iTRO_CzOrder")
        t = self.conn.aggregate([
            {"$match":{"date":{"$gte":self.sdate["stime"],"$lt":self.sdate["etime"]},"status":True}},
            {"$project":{"orderid":"$orderid","userid":"$userid","ordermoney":"$ordermonry"}},
            {"$group":{
                "_id":"null",
                "chargenum":{"$sum":1},
                "chargesum":{"$sum":"$ordermoney"},
                "chargeuser":{"$addToSet":"$userid"}
            }}
        ])
        t = list(t)
        if t :
            tmp = t[0]
            self.result["chargenum"] = tmp["chargenum"]
            self.result["chargesum"] = tmp["chargesum"]
            self.result["chargeuser"] = len(tmp["chargeuser"])
        else:
            self.result["chargenum"] = int(0)
            self.result["chargesum"] = int(0)
            self.result["chargeuser"] = int(0)

    def s_login(self):
        """登录用户数、IP数统计和登录记录数"""
        self.mongo_conn(dbname="iTROdb", coname="iTRO_Log")
        t = self.conn.find(
            {"LogTime":{"$regex":self.sdate["sdate"]}, "SType":1},
            {"_id":0, "UserId":1, "LogTime":1, "LogIp":1}
        )
        t = list(t)
        counter = set([i["UserId"] for i in t])
        self.result["loginuser"] = len(counter)
        counter = set([i["LogIp"] for i in t])
        self.result["loginip"] = len(counter)
        self.result["loginnum"] = len(t)

    def s_mallview(self):
        """网页访问数据，包括商品访问次数和店铺页面访问次数"""
        from pandas import DataFrame as df
        self.mongo_conn(dbname="iTROdb", coname="iTRO_CNZZ")
        t = self.conn.find(
            {"date":self.sdate["sdate"]},
            {"_id":0,"type":1,"ip":1,"sid":1,"pid":1}
        )
        t = df(list(t))
        if len(t) == 0:
            print("%s, s_mallview no data" % self.sdate["sdate"])
            return
        else:
            # 商城访问次数，包括店铺和商品
            self.result["mallview"] = len(t)
            # 店铺访问次数，只包括店铺的访问记录
            t_store = t[t["type"]==2]
            self.result["storeview"] = len(t_store)
            tmp = t_store.sid.value_counts()
            # 记录店铺页面访问次数的top10
            j = 10 if len(tmp) > 10 else len(tmp)
            for i in range(j):
                self.result["topstore"].append(tmp.index[i])
            # 商品访问次数，只统计商品页面的记录次数
            # 提取商品页面访问记录
            t_wares = t[t["type"]==1]
            # 商品页面的全部访问次数
            self.result["waresview"] = len(t_wares)
            # 统计商品页面访问次数的top10
            j = 10 if len(tmp) > 10 else len(tmp)
            for i in range(j):
                self.result["topwares"].append(tmp.index[i])

    def s_order(self):
        """统计交易子订单"""

# 查询数据
def get_data(self,sdate,edate):
    """查询数据"""
from pandas import DataFrame as df
from pymongo import MongoClient
sdate = "2018-01-22"
edate = "2018-03-04"
client = MongoClient("47.92.72.108",28010)
conn = client.iTROdb.iTRO_Statistic
td = conn.find({"sdate":{"$gte":sdate, "$lte":edate}},{"_id":0,"totaluser":1,"newuser":1,"loginuser":1,"loginnum":1,"loginip":1,"newstore":1,"chargeuser":1,"chargesum":1,"chargenum":1,"mallview":1,"storeview":1,"waresview":1,"sdate":1})
td = list(td)
td = df(td)
td.index = td["sdate"]
td = td.ix[:,["totaluser","newuser","loginuser","loginnum","loginip","newstore","chargeuser","chargesum","chargenum","mallview","storeview","waresview"]]
td.T.to_clipboard()

if __name__ == "__main__":
    # from itro_scripts.itro_statistic import Statistic
    import sys
    sdate = "" if len(sys.argv) == 1 else sys.argv[1]
    s = Statistic(sdate)
    s.s_totaluser()
    s.s_newuser()
    s.s_newstore()
    s.s_charge()
    s.s_login()
    s.s_mallview()
    s.to_mongo()
    print(s.result)

{'datetime': 'datetime.datetime', 'timedelta': 'datetime.timedelta', 'MongoClient': 'pymongo.MongoClient', 'df': 'pandas.DataFrame'}


import numpy as np
from PIL import Image
import glob
import cv2
from tqdm import tqdm

BASE_PATH = 'datasets/simple/dataset-0/train'

def get_stat(img):
    mean = [0,0,0]
    std = [0,0,0]
    for chanel in range(3):
        mean[chanel] = np.mean(img[:,:,chanel])/255
        std[chanel] = np.std(img[:,:,chanel])/255
    return mean, std
def get_stat_all_imgs(all_imgs):
    count = 0
    total_mean = [0,0,0]
    total_std = [0,0,0]
    for img_path in tqdm(all_imgs):
        try:
            img = cv2.imread(img_path)
            mean ,std = get_stat(img)
            total_mean = np.add(mean,total_mean)
            total_std = np.add(std,total_std)
            count += 1
        except:
            continue
    final_mean = [0,0,0]
    final_std = [0,0,0]
    for i in range(3):
        final_mean[i] = total_mean[i]/count
        final_std[i] = total_std[i]/count
    return final_mean , final_std

def main():
    dir_list = glob.glob(BASE_PATH + "/*")
    all_imgs = []
    for dir in dir_list:
        img_list = glob.glob(dir + "/*")
        all_imgs.extend(img_list)
    final_mean,final_std = get_stat_all_imgs(all_imgs)
    np.save('final_mean',final_mean)
    np.save('final_std',final_std)
    print(final_mean,final_std)

if __name__ == '__main__':
    main()


import discord
from discord_components import *
from discord.ext import commands
from random import choice
from asyncio import TimeoutError

ch1 = ["Rock", "Scissors", "Paper"]

class rps(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.command()
    async def rps(self, ctx):

        comp = choice(ch1)

        yet = discord.Embed(title=f"{ctx.author.display_name}'s Rock Paper Scissors Game!", description=">Status: You haven't click on any button yet!", color=0xFFEA00)

        win = discord.Embed(title=f"{ctx.author.display_name}, You Won!", description=f">Status: **You have won!** Bot had chosen {comp}", color=0x00FF00)

        out = discord.Embed(title=f"{ctx.author.display_name}, You didn't click on time", description=">Status: **Timed Out!**", color=discord.Color.red())

        lost = discord.Embed(title=f"{ctx.author.display_name}, You Lost!", description=f">Status: **You have lost!** Bot had chosen {comp}", color=0x00FF00)

        tie = discord.Embed(title=f"{ctx.author.display_name}, It was a Tie!", description=f">Status: **Tie!** Bot has chosen {comp}", color=0x00FF00)

        m = await ctx.send(
            embed=yet,
            components=[[Button(style=1, label="Rock"), Button(
                style=3, label="Paper"), Button(style=4, label="Scissors")]]
        )

        def check(res):
            return ctx.author == res.user and res.channel == ctx.channel

        try:
            res = await self.bot.wait_for("button_click", check=check, timeout=15)
            player = res.component.label

            if player == comp:
                await m.edit(embed=tie, components=[])

            if player == "Rock" and comp == "Paper":
                await m.edit(embed=lost, components=[])

            if player != "Rock" and comp == "Scissors":
                await m.edit(embed=win, components=[])

            if player == "Paper" and comp == "Rock":
                await m.edit(embed=win, components=[])

            if player == "Paper" and comp == "Scissors":
                await m.edit(embed=lost, components=[])

            if player == "Scissors" and comp != "Rock":
                await m.edit(embed=lost, components=[])

            if player == "Scissors" and comp == "Paper":
                await m.edit(embed=win, components=[])

        except TimeoutError:
            await m.edit(
                embed=out,
                components=[],
            )

def setup(bot):
    bot.add_cog(rps(bot))


from typing import List
from unittest import TestCase, main
from itertools import product

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        phoneDict = {
            '2': ["a", "b", "c"],
            '3': ["d", "e", "f"],
            '4': ["g", "h", "i"],
            '5': ["j", "k", "l"],
            '6': ["m", "n", "o"],
            '7': ["p", "q", "r", "s"],
            '8': ["t", "u", "v"],
            '9': ["w", "x", "y", "z"],
            '0': [" "]
        }
        digitsList = list(digits)
        searchList = []
        for dig in digitsList:
            searchList.append(phoneDict.get(dig))

        ans = []
        for com in list(product(*searchList)):
            if len(com) != 0:
                ans.append(''.join(com))

        return ans

class TestSolution(TestCase):
    def setUp(self) -> None:
        self.sol = Solution()

    def test_first_case(self):
        ans = self.sol.letterCombinations(digits="23")
        self.assertEqual(ans, ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"])

    def test_second_case(self):
        ans = self.sol.letterCombinations(digits="")
        self.assertEqual(ans, [])

if __name__ == '__main__':
    main()


"""
Modules for extracting time synchronization statistics according to Tri-Message protocol. All functions assume
payload for ONE data packet/decoder ONLY. These modules will be called separately by API800 and API900 loaders, they
are in themselves helper functions. They do not depend on API formats, they take the time sync payloads as parameters
and use Tri-Message protocol to compute latencies, check criteria, and correct the "machine" start time B0 based on the
minimum latencies.
"""

from typing import Dict, List, Optional, Tuple, Union

# noinspection Mypy
import numpy as np

class TriMessageStats:
    """
    Stores statistics about the tri-message exchanges
    ALL timestamps in microseconds
    Properties:
        packet_id: an identifier for the packet that contains the data.  Used for reporting purposes
        latency1: latencies measured by timestamps 1 and 2
        latency3: latencies measured by timestamps 2 and 3
        offset1: offsets measured by timestamps 1 and 2
        offset3: offsets measured by timestamps 2 and 3
        best_latency: minimum latency that meets all criteria
        best_offset: best offset that meets all criteria
        best_latency_array_index: number of latency array with best latency
        best_latency_index: index in latency array of best latency
        num_messages: number of tri-message exchanges
    """

    def __init__(self,
                 packet_id: Union[str, int],
                 a1: np.ndarray,
                 a2: np.ndarray,
                 a3: np.ndarray,
                 b1: np.ndarray,
                 b2: np.ndarray,
                 b3: np.ndarray):
        """
        Calculate latency, offset, and their qualities.
        :param packet_id: an identifier for reporting purposes
        :param a1: array of server timestamp 1
        :param a2: array of server timestamp 2
        :param a3: array of server timestamp 3
        :param b1: array of device timestamp 1
        :param b2: array of device timestamp 2
        :param b3: array of device timestamp 3
        """
        self.packet_id: Union[str, int] = packet_id
        self.num_messages: int = len(a1)
        # compute latencies and offsets
        latencies_tuple: Tuple[np.ndarray, np.ndarray] = latencies(a1, a2, a3, b1, b2, b3)
        self.latency1: np.ndarray = latencies_tuple[0]
        self.latency3: np.ndarray = latencies_tuple[1]
        offsets_tuple: Tuple[np.ndarray, np.ndarray] = offsets(a1, a2, a3, b1, b2, b3)
        self.offset1: np.ndarray = offsets_tuple[0]
        self.offset3: np.ndarray = offsets_tuple[1]

        self.best_latency: Optional[float] = None
        self.best_latency_array_index: Optional[int] = None
        self.best_latency_index: Optional[int] = None
        self.best_offset: Optional[float] = 0.0

        self.find_best_latency()
        self.find_best_offset()

    def find_best_latency(self) -> None:
        """
        Finds the best latency among the latencies
        """
        try:
            # find value and index of minimum latency of nonzero latencies
            d1_min: float = np.min(self.latency1[self.latency1 != 0])
            d3_min: float = np.min(self.latency3[self.latency3 != 0])

            if d3_min > d1_min:
                self.best_latency = d1_min  # server round trip is shorter
                self.best_latency_array_index = 1
                self.best_latency_index = np.where(self.latency1 == d1_min)[0][0]
            else:
                self.best_latency = d3_min
                self.best_latency_array_index = 3
                self.best_latency_index = np.where(self.latency3 == d3_min)[0][0]
        except ValueError:
            # all latencies for one of the arrays is zero; the data is untrustworthy.  set the defaults
            self.best_latency = None
            self.best_latency_array_index = None
            self.best_latency_index = None

    def find_best_offset(self) -> None:
        """
        Finds the best offset among the offsets
        """
        # if no best latency, find it
        if self.best_latency is None:
            self.find_best_latency()
        # best latency = best offset, if best latency is still None, best offset is 0.0
        if self.best_latency_array_index == 1:
            self.best_offset = self.offset1[self.best_latency_index]
        elif self.best_latency_array_index == 3:
            self.best_offset = self.offset3[self.best_latency_index]
        else:
            self.best_offset = 0.0

    def set_latency(self,
                    a1_coeffs: np.ndarray,
                    a2_coeffs: np.ndarray,
                    a3_coeffs: np.ndarray,
                    b1_coeffs: np.ndarray,
                    b2_coeffs: np.ndarray,
                    b3_coeffs: np.ndarray) -> None:
        """
        set the latency and find the best
        :param a1_coeffs: server timestamp 1
        :param a2_coeffs: server timestamp 2
        :param a3_coeffs: server timestamp 3
        :param b1_coeffs: device timestamp 1
        :param b2_coeffs: device timestamp 2
        :param b3_coeffs: device timestamp 3
        """
        # compute latencies
        self.latency1, self.latency3 = latencies(a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs)
        self.find_best_latency()

    def set_offset(self,
                   a1_coeffs: np.ndarray,
                   a2_coeffs: np.ndarray,
                   a3_coeffs: np.ndarray,
                   b1_coeffs: np.ndarray,
                   b2_coeffs: np.ndarray,
                   b3_coeffs: np.ndarray) -> None:
        """
        set the offset and find the best
        :param a1_coeffs: server timestamp 1
        :param a2_coeffs: server timestamp 2
        :param a3_coeffs: server timestamp 3
        :param b1_coeffs: device timestamp 1
        :param b2_coeffs: device timestamp 2
        :param b3_coeffs: device timestamp 3
        """
        # latency calculations are required:
        if self.best_latency is None:
            self.set_latency(a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs)
        # compute offsets
        self.offset1, self.offset3 = offsets(a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs)
        self.find_best_offset()

def latencies(a1_coeffs: np.ndarray,
              a2_coeffs: np.ndarray,
              a3_coeffs: np.ndarray,
              b1_coeffs: np.ndarray,
              b2_coeffs: np.ndarray,
              b3_coeffs: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
    """
    Compute latencies in microseconds based on message exchange timestamps.

    Parameters
    ----------
    a1_coeffs, ... b3_coeffs: tri-message timestamps as loaded in transmit_receive_timestamps_microsec

    Returns
    -------
    d1_coeffs: array of server round trip latencies in microseconds
    d3_coeffs: array of device round trip latencies in microseconds
    """
    # Compute latencies in microseconds
    d1_coeffs: np.ndarray = 0.5 * ((a2_coeffs - a1_coeffs) - (b2_coeffs - b1_coeffs))
    d3_coeffs: np.ndarray = 0.5 * ((b3_coeffs - b2_coeffs) - (a3_coeffs - a2_coeffs))

    # convert negative latencies to 0.  negative latencies should not exist naturally
    d1_coeffs[d1_coeffs < 0] = 0
    d3_coeffs[d3_coeffs < 0] = 0

    return d1_coeffs, d3_coeffs

def offsets(a1_coeffs: np.ndarray,
            a2_coeffs: np.ndarray,
            a3_coeffs: np.ndarray,
            b1_coeffs: np.ndarray,
            b2_coeffs: np.ndarray,
            b3_coeffs: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
    """
    Compute offsets in microseconds based on message exchange timestamps.

    Parameters
    ----------
    a1_coeffs, ... b3_coeffs: tri-message timestamps as loaded in transmit_receive_timestamps_microsec

    Returns
    -------
    o1_coeffs: array of server round trip latencies in microseconds
    o3_coeffs: array of device round trip latencies in microseconds
    """
    # assume the generic equation f = a - b + d
    # where d is latency, f is offset, b is machine time and a is time sync server time
    # refer to latency equations above for definitions of d1_coeffs and d3_coeffs
    # with latency d1_coeffs, the equation is f1 = a1_coeffs - b1_coeffs + d1_coeffs
    # with latency d3_coeffs, the equation is f3 = a3_coeffs - b3_coeffs + d3_coeffs
    # In the absence of latency, offset can be calculated this way:
    # o1_coeffs = (a1_coeffs - b1_coeffs + a2_coeffs - b2_coeffs) / 2.
    # o3_coeffs = (a3_coeffs - b3_coeffs + a2_coeffs - b2_coeffs) / 2.
    # get latencies
    latencies_tuple: Tuple[np.ndarray, np.ndarray] = latencies(a1_coeffs,
                                                               a2_coeffs,
                                                               a3_coeffs,
                                                               b1_coeffs,
                                                               b2_coeffs,
                                                               b3_coeffs)
    d1_coeffs: np.ndarray = latencies_tuple[0]
    d3_coeffs: np.ndarray = latencies_tuple[1]
    # use latency to compute offset in microseconds
    o1_coeffs: np.ndarray = a1_coeffs - b1_coeffs + d1_coeffs
    o3_coeffs: np.ndarray = a3_coeffs - b3_coeffs + d3_coeffs

    return o1_coeffs, o3_coeffs

def validate_timestamps(a1_coeffs: np.ndarray, a2_coeffs: np.ndarray, a3_coeffs: np.ndarray,
                        b1_coeffs: np.ndarray, b2_coeffs: np.ndarray, b3_coeffs: np.ndarray) -> \
        Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    it's possible some of the tri-message values are duplicated; the duplicates and other invalid times
    must be removed.

       Parameters
       -------
       a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs: arrays of message exchange timestamps

       Returns
       -------
       a1_coeffs[valid_indices], a2_coeffs[valid_indices], a3_coeffs[valid_indices],
       b1_coeffs[valid_indices], b2_coeffs[valid_indices], b3_coeffs[valid_indices]:
       arrays of valid message exchange timestamps
    """
    num_timestamps = len(a1_coeffs)
    # if length is 1 or less, no need to validate, just return all the values
    if num_timestamps <= 1:
        return a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs
    # if here, there's more than 1 exchange to check
    valid_times: List[Dict] = [{}, {}, {}, {}, {}, {}]
    valid_indices = []
    all_timestamps = [a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs]
    # for each set of timestamps a1x, a2x, etc. in all exchanges
    for data_index in range(6):
        for time_index in range(num_timestamps):
            # compare the time to existing information
            time = all_timestamps[data_index][time_index]
            if time not in valid_times:
                # it's not in valid times, it's a new time
                valid_times[data_index][time] = time_index
    for index in valid_times[0].values():
        # if it's not in the first one, it's not valid.  if it doesn't show up in all others, it's not valid
        if index in valid_times[1].values() and index in valid_times[2].values() and \
                index in valid_times[3].values() and index in valid_times[4].values() and \
                index in valid_times[5].values():
            valid_indices.append(index)
    return a1_coeffs[valid_indices], a2_coeffs[valid_indices], a3_coeffs[valid_indices], \
        b1_coeffs[valid_indices], b2_coeffs[valid_indices], b3_coeffs[valid_indices]

def transmit_receive_timestamps_microsec(coeffs: np.ndarray) -> Tuple[
        np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
       Recover Tri-Message timestamp coefficients. Uses concept of
       Tri-Message to synchronize a device with a reference server:

           First, server A transmits a message to device B with timestamp a1_coeffs. B receives the message at timestamp
            b1_coeffs. B then transmits a message back to A with timestamp b2_coeffs. A receives this message at
            timestamp a2_coeffs. A transmits a second message to B at timestamp a3_coeffs. B receives the message at
            timestamp b3_coeffs.

       Parameters
       ----------
       coeffs: array of tri-message coefficients (a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs)

       Returns
       -------
       a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs: arrays of message exchange timestamps
    """

    if len(coeffs) % 6 != 0:
        raise Exception("Tri-Message contains partial exchange, unsafe to use it for computations.")

    # Timing coefficients
    step: int = 6  # each tri-message exchange contains 6 timestamps, 3 from server and 3 from device
    stop: int = int(len(coeffs) / 6) * step

    a1_coeffs: np.ndarray = coeffs[0: stop: step]  # server first transmit timestamps in epoch microseconds
    a2_coeffs: np.ndarray = coeffs[1: stop: step]  # server first receive timestamps in epoch microseconds
    a3_coeffs: np.ndarray = coeffs[2: stop: step]  # server second transmit timestamps in epoch microseconds
    b1_coeffs: np.ndarray = coeffs[3: stop: step]  # device first receive timestamps in mach microseconds
    b2_coeffs: np.ndarray = coeffs[4: stop: step]  # device first transmit timestamps in mach microseconds
    b3_coeffs: np.ndarray = coeffs[5: stop: step]  # device second receive timestamps in mach microseconds

    # make sure each tri-message exchange contains 6 timestamps (done with modulo check above)
    # assert len(a1_coeffs) == len(a2_coeffs) == len(a3_coeffs) == len(b1_coeffs) == len(b2_coeffs) == len(b3_coeffs)

    return a1_coeffs, a2_coeffs, a3_coeffs, b1_coeffs, b2_coeffs, b3_coeffs


import datetime
import discord
from discord.ext import commands
import io
import numpy as np
from mongoengine import *
from PIL import Image
import pytesseract
import requests
import uuid
import yaml

class Reports(Document):
    reportId = IntField(required=True)
    serverName = StringField(max_length=100,required=True)
    userId = IntField(required=True)
    imageText = StringField(max_length=1337,required=True)

TOKEN = 'token_here'
bot = commands.Bot(command_prefix='cs!')
config = yaml.load(open("config.yaml"), Loader=yaml.FullLoader)
connect('csampolicebot', host='127.0.0.1', port=27017)

def is_staff():
    def predicate(ctx):
        return ctx.message.guild.get_role(config["moderation_role_ID"]) in ctx.message.author.roles
    return commands.check(predicate)

def is_owner():
    def predicate(ctx):
        return ctx.author.id == 1337 # to be replaced with bot/server owner ID
    return commands.check(predicate)

@bot.event
async def on_ready():
    print('Successfully connected! Hello!')

@bot.event
async def on_message(message):
    staff_role = message.guild.get_role(config["moderation_role_ID"])
    if not staff_role in message.author.roles:
        urls = set()
        for part in message.content.lower().split():
            if part.startswith("http") and (part.endswith(".jpg") or part.endswith(".png") or part.endswith(".jpeg")):
                urls.add(part)
        if message.attachments:
            for attachment in message.attachments:
                # just like embeds, attachments that are not images do not interest us so we won't add them to the URL list to scan
                if attachment.url.lower().endswith(".jpg") or attachment.url.lower().endswith(".png") or attachment.url.lower().endswith(".jpeg"):
                    urls.add(attachment.url)
        if len(urls) > 0:
            for url in urls:
                result, text = scancontent(url)
                if result:
                    user = message.author
                    role = message.guild.get_role(config["punished_role_ID"])
                    report_id = uuid.uuid1().int>>65
                    channel = bot.get_channel(config["alert_channel"])
                    embed = discord.Embed(title="User muted", description="", color=0x00ff00)
                    embed.add_field(name="User", value="{} <\@{}>".format(user, user.id), inline=False)
                    embed.add_field(name="Reason", value="Blacklisted word detected in image", inline=False)
                    embed.add_field(name="Mute ID", value="{}".format(report_id), inline=False)
                    embed.timestamp = datetime.datetime.utcnow()
                    report = Reports(reportId=report_id, serverName=message.guild.name, userId=user.id, imageText=text)
                    report.save()
                    await channel.send(embed=embed)
                    await user.add_roles(role)
                    await message.delete()
    await bot.process_commands(message)

@bot.command()
@is_owner()
async def delete(ctx, rID):
    try:
        report = Reports.objects.get(reportId=rID)
        report.delete()
        msg = discord.Embed(title="Report deletion", description="", color=0x00ff00)
        msg.add_field(name=":white_check_mark: Success", value="The report indicated by you has been deleted successfully.", inline=False)
        msg.timestamp = datetime.datetime.utcnow()
    except Reports.DoesNotExist:
        msg = discord.Embed(title="Report deletion", description="", color=0x00ff00)
        msg.add_field(name=":x: Something's wrong!", value="Sorry! I couldn't find any reports to delete for the ID you provided.", inline=False)
        msg.timestamp = datetime.datetime.utcnow()
    await ctx.send(embed=msg)

@bot.command()
@is_staff()
async def lookup(ctx, rID):
    try:
        report = Reports.objects.get(reportId=rID)
        user =  await bot.fetch_user(report.userId)
        msg = discord.Embed(title="Report information", description="", color=0x00ff00)
        msg.add_field(name="User", value="{} <\@{}>".format(user, user.id), inline=False)
        msg.add_field(name="Server", value="{}".format(report.serverName), inline=False)
        msg.add_field(name="Reported image content", value=report.imageText, inline=False)
        msg.timestamp = datetime.datetime.utcnow()
    except Reports.DoesNotExist:
        report = None
        msg = discord.Embed(title="Report information", description="", color=0x00ff00)
        msg.add_field(name="No report found :(", value="Sorry! I couldn't find any reports for the ID you provided.", inline=False)
        msg.timestamp = datetime.datetime.utcnow()
    await ctx.send(embed=msg)

def scancontent(url):
    try:
        r = requests.get(url, stream=True)
        r.raise_for_status()
        im = Image.open(io.BytesIO(r.content))
        image =  np.array(im)
        text = pytesseract.image_to_string(image)
        # Freeing memory because we don't need the image anymore, text is enough
        im.close()
        image = None
        for word in config["banned_words"]:
            if word in text.lower():
                return True, text
    except requests.exceptions.HTTPError as err:
        print(err)
    return False, text

def main():
    bot.run(TOKEN)

if __name__ == '__main__':
    main()


import os
import sys
import time
import pyaudio
import threading
from datetime import datetime
import numpy as np
from picamera import PiCamera

mambrane_fix_date = "13-11-2018"
date = time.strftime("%d-%m-%Y")
mambrane_age = datetime.strptime(date, "%d-%m-%Y") - datetime.strptime(mambrane_fix_date, "%d-%m-%Y")

###################### CAPTURE INFO #####################
ampli_info = "Little Devil"
ampli_volume = "3"
laser_info = "Green class 2"
camera_info = "Sony IMX219 V2.1 8 MPX 1080p30 - 720p60"
mambrane_info = str(mambrane_age.days) + " days"
#########################################################

camera = PiCamera()
camera.resolution = (1280, 720)
camera.start_preview()

nb_f = 2
f = [] * nb_f
if (len(sys.argv) > 1):
    f.insert(0, float(sys.argv[1]))
f.insert(1, 0.0)
a = 2.0

volume = 0.1    # range [0.0, 1.0]
fs = 44100
duration = 3.0
samples = [0.0] * nb_f
file_name = 0

path = "./dataset_lissajous/"
if not  os.path.exists(path):
    os.mkdir(path)
csv =  open(path + date + ".info.csv", "w")
csv.write("ampli : " + ampli_info + ",\nampli_volume : " + ampli_volume + ",\ncode_volume : " + str(volume) + ",\nlaser : " + laser_info + ",\ncamera : " + camera_info + ",\nmambrane_old : " + mambrane_info + ",\n")
csv.close()

p = pyaudio.PyAudio()
stream = [] * nb_f
for x in range(nb_f):
    stream.insert(x, p.open(format = pyaudio.paFloat32, channels = 1, rate = fs, output = True))

class freq_1_thread(threading.Thread):
    def run(self):
        stream[0].write(volume * samples[0])

class freq_2_thread(threading.Thread):
    def run(self):
        stream[1].write(volume * samples[1])

class capture_thread(threading.Thread):
    def run(self):
        camera.capture(path + file_name)

def start_threads():
    play_freq_1 = freq_1_thread()
    play_freq_2 = freq_2_thread()
    cap_laser = capture_thread()
    play_freq_1.start()
    play_freq_2.start()
    time.sleep(0.5)
    cap_laser.start()
    cap_laser.join()
    play_freq_1.join()
    play_freq_2.join()

def stop_stream():
    for x in range(nb_f):
        stream[x].stop_stream()
        stream[x].close()
        stream.stop_stream()
        stream.close()

if (len(sys.argv) > 1):
    while f[0] <= float(sys.argv[2]):
        try:
            f[1] = (a + 1.0) / a * f[0]
            for x in range(nb_f):
                samples.insert(0, (np.sin(2 * np.pi * np.arange(fs * duration) * f[x] / fs)).astype(np.float32))
            file_name = "dataset:" + date + "_hz:" + str(round(f[0], 1)) + "_" + str(round(f[1],3)) + "_lsj:" + str(int(a + 1)) + "." + str(int(a)) + ".jpg"
            start_threads()
            sys.stdout.write("\033[1;32;40m" + str(f[0]) +  "\033[0;37;40m + " + "\033[1;34;40m" +  str(round(f[1], 3)) + "\t\033[0;37;40m hz\t" + "\033[1;33;40m" + str(int(a + 1)) + "/" + str(int(a)) + "\n\033[0;37;40m")
            sys.stdout.flush()
            a += 1
            if a == 10.0:
                sys.stdout.write("\n")
                sys.stdout.flush()
                a = 1
                f[0] += 1.0
        except KeyboardInterrupt:
            exit(0)
    stop_stream()
else:
    sys.stdout.write("\033[1;31;40musage: frequence_min frequence_max\n\033[0;37;40m")
    sys.stdout.flush()
p.terminate()


import numpy as np
import cv2 as cv
import glob
import os
from config import Config

config = Config()

CRITERIA = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
COLS = config.get("chessboard", "cols")
ROWS = config.get("chessboard", "rows")
CELL_LENGTH = config.get("chessboard", "cell_length")
CAMERA_CONFIG_PATH = config.get("calibration", "calibration_file")
IMAGE_PATH = config.get("calibration", "image_path")
VIDEO_PATH = config.get("calibration", "video_path")
TRAINING_IMAGES_AMNT = config.get("calibration", "training_images_amnt")
USE_WEBCAM = config.get("webcam")
MAX_REPROJECTION_ERROR = config.get("calibration", "max_reprojection_error")
VERBOSE = config.get("verbose")

OBJP = np.zeros((COLS * ROWS, 3), np.float32)
OBJP[:,:2] = np.mgrid[0:ROWS,0:COLS].T.reshape(-1,2) * CELL_LENGTH

class Color:
    """Color constants in BGR format"""
    RED = (0, 0, 255)
    GREEN = (0, 255, 0)
    BLUE = (255, 0, 0)
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    YELLOW = (0, 255, 255)
    PURPLE = (255, 0, 255)
    CYAN = (255, 255, 0)

def get_corners_manually(img: np.ndarray) -> np.ndarray:
    """Get the corners of the chessboard manually by clicking on the corners of the chessboard.

    Parameters
    ----------
    img : np.ndarray
        The image of the chessboard.

    Returns
    -------
    corners : np.ndarray
        The corners of the chessboard.
    """
    print("Click on the four corners of the chessboard. When done, press any key to continue.")
    manual_corners = []
    def click_event(event, x, y, flags, params):
        """Callback function for the mouse click event. Adds the clicked point to the list of corners and displays the point on the image."""
        if event == cv.EVENT_LBUTTONDOWN:
            print(x, ' ', y)
            manual_corners.append([x, y])
            font = cv.FONT_HERSHEY_SIMPLEX
            strXY = str(x) + ', ' + str(y)
            cv.putText(visual_image, strXY, (x, y), font, .5, (255, 255, 0), 2)
            cv.circle(visual_image, (x, y),1, (0, 0, 255),1)
            cv.imshow('Manual corners', visual_image)
    visual_image = img.copy()
    cv.imshow('Manual corners', visual_image)
    cv.setMouseCallback('Manual corners', click_event)
    cv.waitKey(0)
    cv.destroyAllWindows()
    if len(manual_corners) != 4:
        print("The number of corners is not 4. Please try again.")
        return get_corners_manually(img)
    corners = interpolate_chessboard_corners(manual_corners, ROWS, COLS)
    cv.destroyAllWindows()
    return corners

def save_camera_config(mtx, dist, rvecs, tvecs, file=CAMERA_CONFIG_PATH):
    """Save the camera configuration to a file.

    Parameters
    ----------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    file : str, optional
        The path to the file, by default CAMERA_CONFIG_PATH (set in config.yml)
    """
    np.savez(file, mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)

def load_camera_config(file=CAMERA_CONFIG_PATH):
    """Load the camera configuration from a file.

    Parameters
    ----------
    file : str, optional
        The path to the file, by default CAMERA_CONFIG_PATH (set in config.yml)

    Returns
    -------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    opt_cam_mtx : np.ndarray
        The optimal camera matrix.
    """
    with np.load(file) as X:
        mtx, dist, rvecs, tvecs= [X[i] for i in ('mtx', 'dist', 'rvecs', 'tvecs')]
    return mtx, dist, rvecs, tvecs

def draw_world_axis(img, rVecs, tVecs, cameraMatrix, dist, size=1):
    """Draws the world axis on the image.

    Parameters
    ----------
    img : np.ndarray
        The image to draw the axis on.
    rVecs : np.ndarray
        The rotation vectors.
    tVecs : np.ndarray
        The translation vectors.
    cameraMatrix : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    size : int, optional
        The size of the axis, by default 1

    Returns
    -------
    img : np.ndarray
        The image with the axis drawn on it.
    """
    points = np.float32([[0,0,0],[size * CELL_LENGTH,0,0],[0,size * CELL_LENGTH,0],[0,0,-size * CELL_LENGTH]])
    imgpts, jac = cv.projectPoints(points, rVecs, tVecs, cameraMatrix, dist)
    point_one = tuple(map(int, imgpts[0].ravel()))
    point_two = tuple(map(int, imgpts[1].ravel()))
    point_three = tuple(map(int, imgpts[2].ravel()))
    point_four = tuple(map(int, imgpts[3].ravel()))

    cv.line(img, point_one, point_two, Color.RED, 3) #going right
    cv.line(img, point_one, point_three, Color.GREEN, 3) #to camera
    cv.line(img, point_one, point_four, Color.BLUE, 3) #going up
    return img

def draw_cube(img, rvecs, tvecs, cam_mtx, dist, size=1):
    """Draws a cube on the image.

    Parameters
    ----------
    img : np.ndarray
        The image to draw the cube on.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    cam_mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    size : int, optional
        The size of the cube, by default 1

    Returns
    -------
    img : np.ndarray
        The image with the cube drawn on it.
    """
    points = np.float32([[0,0,0], [0,size*CELL_LENGTH,0], [size*CELL_LENGTH,size*CELL_LENGTH,0], [size*CELL_LENGTH,0,0], #Bottom 4 points
    [0,0,-size*CELL_LENGTH],[0,size*CELL_LENGTH,-size*CELL_LENGTH],[size*CELL_LENGTH,size*CELL_LENGTH,-size*CELL_LENGTH],[size*CELL_LENGTH,0,-size*CELL_LENGTH] ]) #Top 4 points -> Z might  have to be negative
    #3D points projected to 2D coordinates
    points, jac = cv.projectPoints(points, rvecs, tvecs, cam_mtx, dist)

    points = [tuple(map(int, points[i].ravel())) for i in range(0, 8)]
    colors = {
        "bottom": Color.CYAN,
        "top": Color.CYAN,
        "sides": Color.CYAN
    }
    thiccness = 1
    groups = [
        {"points": points[:4], "color": colors["bottom"]},
        {"points": [points[0], points[4]], "color": colors["sides"]},
        {"points": [points[1], points[5]], "color": colors["sides"]},
        {"points": [points[2], points[6]], "color": colors["sides"]},
        {"points": [points[3], points[7]], "color": colors["sides"]},
        {"points": points[4:8], "color": colors["top"]},
    ]

    for group in groups:
        for j in range(len(group["points"])):
            cv.line(img, group["points"][j], group["points"][(j+1)%len(group["points"])], group["color"], thiccness)
    return img

def interpolate_chessboard_corners(corners, rows, cols):
    """Interpolates the chessboard corners.

    Parameters
    ----------
    corners : np.ndarray
        The corners of the chessboard.
    rows : int
        The number of rows in the chessboard.
    cols : int
        The number of columns in the chessboard.

    Returns
    -------
    np.ndarray
        The interpolated corners.
    """
    tl, tr, br, bl = [np.array(corner) for corner in corners] # top left, top right, bottom right, bottom left
    points = np.array([tl, tr, br, bl], dtype = "float32")

    width_a = np.linalg.norm(br - bl)
    width_b = np.linalg.norm(tr - tl)
    max_width = int(max(width_a, width_b))

    height_a = np.linalg.norm(tr - br)
    height_b = np.linalg.norm(tl - bl)
    max_height = int(max(height_a, height_b))

    #mapping 4 new corners from the new heigth and width
    dst = np.array([
		[0, 0],
		[max_width - 1, 0],
		[max_width - 1, max_height - 1],
		[0, max_height - 1]
        ], dtype = "float32")

    #linearly expanding all the points
    x_coord = np.linspace(dst[0][0], dst[1][0], rows)
    y_coord = np.linspace(dst[0][1], dst[3][1], cols)

    #making a holder array that holds all points
    rect = np.zeros((rows*cols, 2), dtype = "float32")
    counter = 0
    for j in range(cols):
        for k in range(rows):
            rect[counter] = [x_coord[k],y_coord[j]]
            counter += 1
    #Compute perspective transform from unwarped points to warped points
    M = cv.getPerspectiveTransform(dst, points)

    # Apply the transformation matrix to the array of all points
    transformed_points = cv.perspectiveTransform(rect.reshape(-1,1,2), M)
    return transformed_points

def preprocess_image(img):
    """Preprocesses the image. This includes converting it to grayscale, thresholding it, dilating it and sharpening the edges.

    Parameters
    ----------
    img : np.ndarray
        The image to preprocess.

    Returns
    -------
    img : np.ndarray
        The preprocessed image.
    """
    img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    img = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)[1]
    kernel = np.ones((1, 1), np.uint8)
    img = cv.dilate(img, kernel, iterations=1)
    # sharpen the edges of the image
    kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
    img = cv.filter2D(img, -1, kernel)
    if VERBOSE == 2:
        cv.imshow("preprocessed", img)
        cv.waitKey(0)
        cv.destroyAllWindows()
    return img

def train_camera():
    """Trains the camera using the images in the IMAGE_PATH folder.

    Returns
    -------
    object_points : list
        The object points in real world space.
    image_points : list
        The image points in image space.
    shape : tuple
        The shape of the images.
    """
    object_points = []
    image_points = []
    images = glob.glob(IMAGE_PATH + '*.jpg')
    discarded_images = []
    if len(images) == 0:
        print(f"No images found in '{os.path.abspath(IMAGE_PATH)}'! Exiting...")
        exit()
    for i, fname in enumerate(images):
        if TRAINING_IMAGES_AMNT != 0 and i >= TRAINING_IMAGES_AMNT:
            break
        if VERBOSE == 1:
            print(f"Processing image {fname}...")
        img = cv.imread(fname)
        ret, corners, gray = find_chessboard_corners(img)
        if ret:
            object_points.append(OBJP)
            image_points.append(corners)
        else:
            discarded_images.append(fname)
    if len(discarded_images) > 0:
        print(f"Discarded {len(discarded_images)} images because the reprojection error was above {MAX_REPROJECTION_ERROR}.")
        if VERBOSE == 1:
            print("Discarded images:")
            for i, image in enumerate(discarded_images):
                print(f"\t{i+1}. {image}")
    return object_points, image_points, gray.shape

def find_chessboard_corners(img, camera=False, preprocess=False):
    """Finds the chessboard corners in the image.
    First, it tries to find the corners automatically. If that fails, an image preprocessing step is applied and attempts to find the corners automatically again. If that fails, it asks the user to manually select the corners.

    Parameters
    ----------
    img : np.ndarray
        The image to find the corners in.
    camera : bool, optional
        Whether the image is from the camera or not, by default False. If it is from the camera, it will not ask the user to manually select the corners.

    Returns
    -------
    ret : bool
        Whether the corners were found or not.
    corners : np.ndarray
        The corners of the chessboard.
    gray : np.ndarray
        The grayscale image.
    """
    corners_found = False
    if preprocess:
        gray = preprocess_image(img)
    else:
        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ret, corners = cv.findChessboardCorners(gray, (ROWS, COLS), None)
    if ret == True:
        corners_found = True
        #corners = cv.cornerSubPix(gray, corners, (11,11), (-1,-1), CRITERIA)
        if VERBOSE == 2:
            cv.drawChessboardCorners(img, (ROWS, COLS), corners, ret)
            cv.imshow('img', img)
            cv.waitKey(0)
            cv.destroyAllWindows()
    elif not camera and not preprocess:
        return find_chessboard_corners(img, camera, preprocess=True)
    elif not camera:
        corners_found = True
        corners = get_corners_manually(img)
        #corners = cv.cornerSubPix(gray, manual_corners, (11,11), (-1,-1), CRITERIA)
        if VERBOSE == 2:
            cv.drawChessboardCorners(img, (ROWS, COLS), corners, True)
            cv.imshow('img', img)
            cv.waitKey(0)
            cv.destroyAllWindows()
    if corners_found:
        # calculate the reprojection error by calibrating a camera on just this image
        ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera([OBJP], [corners], gray.shape[::-1], None, None)
        if VERBOSE == 1:
            print(f"Reprojection error: {ret}")
        if ret > MAX_REPROJECTION_ERROR:
            if VERBOSE == 1:
                print(f"Reprojection error too high: ({ret:.4f} > {MAX_REPROJECTION_ERROR})")
            corners_found = False
    return corners_found, corners, gray

def calibrate_camera():
    """Calibrates the camera and saves the calibration data to a file. If the file already exists, it will load the calibration data from the file.

    Returns
    -------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    """
    if not os.path.isfile(CAMERA_CONFIG_PATH):
        print('Calibrating camera...')
        #object_points, image_points, img_shape = train_camera()
        object_points, image_points, img_shape = avi_train_camera()
        if len(object_points) == 0 or len(image_points) == 0:
            print("All images have been discarded due to high reprojection error. Exiting...")
            exit()
        ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(object_points, image_points, img_shape[::-1], None, None)
        print (f"Mean reprojection error: {ret}")
        save_camera_config(mtx, dist, rvecs, tvecs)
        return mtx, dist, rvecs, tvecs
    else:
        return load_camera_config()

def video(mtx, dist, rvecs, tvecs):
    """Opens the webcam and draws the cube on the chessboard in the video feed. It also saves the video to a file.

    Parameters
    ----------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    """
    # Inspired from here https://github.com/pavithran-s/Camera_Calibaration/blob/master/draw_cube.ipynb
    print("Opening webcam...")
    webcam = cv.VideoCapture(0, cv.CAP_DSHOW)
    webcam.set(cv.CAP_PROP_FRAME_WIDTH, 1920)
    webcam.set(cv.CAP_PROP_FRAME_HEIGHT, 1080)
    if webcam.isOpened():
        print("Webcam opened")
    else:
        print("Unable to read camera feed. Exiting...")
        exit()
    width = int(webcam.get(3))
    height = int(webcam.get(4))
    # write video file as mp4
    fourcc = cv.VideoWriter_fourcc(*'mp4v')
    out = cv.VideoWriter('output.mp4', fourcc, 5, (width, height))
    while True:
        has_frame, frame = webcam.read()
        if has_frame == False:
            break
        ret, corners, gray = find_chessboard_corners(frame, camera=True)
        if ret == True:
            corners2 = cv.cornerSubPix(gray, corners, (11,11), (-1,-1), CRITERIA)
            ret, rvecs, tvecs = cv.solvePnP(OBJP, corners2, mtx, dist)
            frame = draw_world_axis(frame, rvecs, tvecs, mtx, dist, size=5)
            frame = draw_cube(frame, rvecs, tvecs, mtx, dist, size=3)
        cv.imshow('images',frame)
        out.write(frame)
        if cv.waitKey(1) == ord('q'):
            break
    out.release()
    cv.destroyAllWindows()
    webcam.release()

def avi_train_camera():
    """Trains the camera using the video in the VIDEO_PATH folder.

    Returns
    -------
    object_points : list
        The object points in real world space.
    image_points : list
        The image points in image space.
    shape : tuple
        The shape of the images.
    """
    print("Opening avi video...")
    cap = cv.VideoCapture(VIDEO_PATH + 'video_test.avi')
    object_points = []
    image_points = []

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            print("Video could not be read")
            break
        if cv.waitKey(1) == ord('s'):
            ret2, corners, gray = find_chessboard_corners(frame, camera=True)
            if ret2 == True:
                object_points.append(OBJP)
                image_points.append(corners)
                cv.imshow('recorded',frame)
        cv.imshow('images',frame)
        if cv.waitKey(1) == ord('q'):
            break
    cv.destroyAllWindows()
    cap.release()
    return object_points, image_points, gray.shape

def clip_video(mtx, dist, rvecs, tvecs):
    """Draws the world axis on the chessboard in the frame of the video from the video folder.

    Parameters
    ----------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    """
    print("Opening avi video...")
    cap = cv.VideoCapture(VIDEO_PATH + 'Extrinsics/checkerboard4.avi')

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            print("Video could not be read")
            break
        if cv.waitKey(1) == ord('s'):
            _, corners, _ = find_chessboard_corners(frame)
            _, rvecs, tvecs = cv.solvePnP(OBJP, corners, mtx, dist)
            frame = draw_world_axis(frame, rvecs, tvecs, mtx, dist, size=5)
            # write the image to a file at the same location
            cv.imshow(f'Axis ', frame)
            cv.waitKey(0)
            cv.destroyAllWindows()
        cv.imshow('Video',frame)
        if cv.waitKey(1) == ord('q'):
            break
    cv.destroyAllWindows()
    cap.release()

def image(mtx, dist, rvecs, tvecs):
    """Draws the cube on the chessboard in the images from the test folder.

    Parameters
    ----------
    mtx : np.ndarray
        The camera matrix.
    dist : np.ndarray
        The distortion coefficients.
    rvecs : np.ndarray
        The rotation vectors.
    tvecs : np.ndarray
        The translation vectors.
    """
    image_path = config.get('test_image_path')
    images = glob.glob(os.path.join(image_path, '*.jpg'))
    if len(images) == 0:
        print(f"No images found in the folder {image_path}. Exiting...")
        exit()
    for fname in images:
        img = cv.imread(fname)
        _, corners, _ = find_chessboard_corners(img)
        _, rvecs, tvecs = cv.solvePnP(OBJP, corners, mtx, dist)
        img = draw_world_axis(img, rvecs, tvecs, mtx, dist, size=5)
        img = draw_cube(img, rvecs, tvecs, mtx, dist, size=3)
        # write the image to a file at the same location
        cv.imwrite(f'{image_path}_cube.jpg', img)
        cv.imshow(f'cube {fname}', img)
        cv.waitKey(0)
        cv.destroyAllWindows()

def main():
    """Main function. It will calibrate the camera (or load the camera parameters from file) and then either open the webcam or draw the cube on the chessboard in the images from the test folder."""
    mtx, dist, rvecs, tvecs = calibrate_camera()
    if USE_WEBCAM:
        #video(mtx, dist, rvecs, tvecs)
        clip_video(mtx, dist, rvecs, tvecs)
    else:
        image(mtx, dist, rvecs, tvecs)

if __name__ == '__main__':
    main()


from contextlib import contextmanager
from typing import ContextManager

from zkay.zkay_ast.analysis.partition_state import PartitionState
from zkay.zkay_ast.analysis.side_effects import has_side_effects
from zkay.zkay_ast.ast import VariableDeclarationStatement, IfStatement, \
    Block, ExpressionStatement, MeExpr, AssignmentStatement, RequireStatement, AllExpr, ReturnStatement, \
    FunctionCallExpr, BuiltinFunction, ConstructorOrFunctionDefinition, StatementList, WhileStatement, ForStatement, \
    ContinueStatement, BreakStatement, DoWhileStatement, LocationExpr, TupleExpr, PrivacyLabelExpr
from zkay.zkay_ast.visitor.visitor import AstVisitor

def alias_analysis(ast):
    v = AliasAnalysisVisitor()
    v.visit(ast)

class AliasAnalysisVisitor(AstVisitor):

    def __init__(self, log=False):
        super().__init__('node-or-children', log)
        self.cond_analyzer = GuardConditionAnalyzer()

    def visitConstructorOrFunctionDefinition(self, ast: ConstructorOrFunctionDefinition):
        s: PartitionState[PrivacyLabelExpr] = PartitionState()
        s.insert(MeExpr().privacy_annotation_label())
        s.insert(AllExpr().privacy_annotation_label())
        for d in ast.parent.state_variable_declarations:
            s.insert(d.idf)
        for p in ast.parameters:
            s.insert(p.idf)
        ast.body.before_analysis = s
        return self.visit(ast.body)

    def propagate(self, statements, before_analysis: PartitionState[PrivacyLabelExpr]) -> PartitionState[PrivacyLabelExpr]:
        last = before_analysis.copy()
        # push state through each statement
        for statement in statements:
            statement.before_analysis = last
            # print('before', statement, last)
            self.visit(statement)
            last = statement.after_analysis
        # print('after', statement, last)
        return last.copy()

    def visitStatementList(self, ast: StatementList):
        ast.after_analysis = self.propagate(ast.statements, ast.before_analysis)

    def visitBlock(self, ast: Block):
        last = ast.before_analysis.copy()

        # add fresh names from this block
        for name in ast.names.values():
            last.insert(name)

        ast.after_analysis = self.propagate(ast.statements, last)

        # remove names falling out of scope
        for name in ast.names.values():
            ast.after_analysis.remove(name)

    def visitIfStatement(self, ast: IfStatement):
        # condition
        before_then = self.cond_analyzer.analyze(ast.condition, ast.before_analysis)

        # then
        ast.then_branch.before_analysis = before_then
        self.visit(ast.then_branch)
        after_then = ast.then_branch.after_analysis

        # else
        if ast.else_branch:
            before_else = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.before_analysis)
            ast.else_branch.before_analysis = before_else
            self.visit(ast.else_branch)
            after_else = ast.else_branch.after_analysis
        else:
            after_else = ast.before_analysis

        # join branches
        ast.after_analysis = after_then.join(after_else)

    def visitWhileStatement(self, ast: WhileStatement):
        # Body always executes after the condition, but it is also possible that it is not executed at all
        # Condition is true before the body
        # After the loop, the condition is false

        if has_side_effects(ast.condition) or has_side_effects(ast.body):
            ast.before_analysis = ast.before_analysis.separate_all()

        before_body = self.cond_analyzer.analyze(ast.condition, ast.before_analysis)
        ast.body.before_analysis = before_body
        self.visit(ast.body)

        # Either no loop iteration or at least one loop iteration
        skip_loop = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.before_analysis)
        did_loop = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.body.after_analysis)

        # join
        ast.after_analysis = skip_loop.join(did_loop)

    def visitDoWhileStatement(self, ast: DoWhileStatement):
        # Body either executes with or without condition, but it is also possible that it is not executed at all
        # No information about condition before the body
        # After the loop, the condition is false

        # Could be subsequent loop iteration after condition with side effect
        cond_se = has_side_effects(ast.condition)
        if cond_se or has_side_effects(ast.body):
            ast.before_analysis = ast.before_analysis.separate_all()

        ast.body.before_analysis = ast.before_analysis.copy()
        self.visit(ast.body)

        # ast.before_analysis is only used by expressions inside condition -> body has already happened at that point
        ast.before_analysis = ast.body.after_analysis.copy()
        ast.after_analysis = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.before_analysis)

    def visitForStatement(self, ast: ForStatement):
        last = ast.before_analysis.copy()

        # add names introduced in init
        for name in ast.names.values():
            last.insert(name)

        if ast.init is not None:
            ast.init.before_analysis = last.copy()
            self.visit(ast.init)
            ast.before_analysis = ast.init.after_analysis.copy() # init should be taken into account when looking up things in the condition

        if has_side_effects(ast.condition) or has_side_effects(ast.body) or (ast.update is not None and has_side_effects(ast.update)):
            ast.before_analysis = last.separate_all()

        ast.body.before_analysis = self.cond_analyzer.analyze(ast.condition, ast.before_analysis)
        self.visit(ast.body)
        if ast.update is not None:
            # Update is always executed after the body (if it is executed)
            ast.update.before_analysis = ast.body.after_analysis.copy()
            self.visit(ast.update)

        skip_loop = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.init.after_analysis)
        did_loop = self.cond_analyzer.analyze(ast.condition.unop('!'), ast.update.after_analysis if ast.update else ast.body.after_analysis)

        # join
        ast.after_analysis = skip_loop.join(did_loop)

        # drop names introduced in init
        for name in ast.names.values():
            ast.after_analysis.remove(name)

    def visitVariableDeclarationStatement(self, ast: VariableDeclarationStatement):
        e = ast.expr
        if e and has_side_effects(e):
            ast.before_analysis = ast.before_analysis.separate_all()

        # visit expression
        if e:
            self.visit(e)

        # state after declaration
        after = ast.before_analysis.copy()

        # name of variable is already in list
        name = ast.variable_declaration.idf
        assert (after.has(name))

        # make state more precise
        if e and e.privacy_annotation_label():
            after.merge(name, e.privacy_annotation_label())

        ast.after_analysis = after

    def visitRequireStatement(self, ast: RequireStatement):
        if has_side_effects(ast.condition):
            ast.before_analysis = ast.before_analysis.separate_all()

        self.visit(ast.condition)

        # state after require
        after = ast.before_analysis.copy()

        # make state more precise
        c = ast.condition
        if isinstance(c, FunctionCallExpr) and isinstance(c.func, BuiltinFunction) and c.func.op == '==':
            lhs = c.args[0].privacy_annotation_label()
            rhs = c.args[1].privacy_annotation_label()
            if lhs and rhs:
                after.merge(lhs, rhs)

        ast.after_analysis = after

    def visitAssignmentStatement(self, ast: AssignmentStatement):
        lhs = ast.lhs
        rhs = ast.rhs
        if has_side_effects(lhs) or has_side_effects(rhs):
            ast.before_analysis = ast.before_analysis.separate_all()

        # visit expression
        self.visit(ast.lhs)
        self.visit(ast.rhs)

        # state after assignment
        after = ast.before_analysis.copy()
        recursive_assign(lhs, rhs, after)

        # save state
        ast.after_analysis = after

    def visitExpressionStatement(self, ast: ExpressionStatement):
        if has_side_effects(ast.expr):
            ast.before_analysis = ast.before_analysis.separate_all()

        # visit expression
        self.visit(ast.expr)

        # if expression has effect, we are already at TOP
        ast.after_analysis = ast.before_analysis.copy()

    def visitReturnStatement(self, ast: ReturnStatement):
        ast.after_analysis = ast.before_analysis

    def visitContinueStatement(self, ast: ContinueStatement):
        ast.after_analysis = ast.before_analysis

    def visitBreakStatement(self, ast: BreakStatement):
        ast.after_analysis = ast.before_analysis

    def visitStatement(self, _):
        raise NotImplementedError()

class GuardConditionAnalyzer(AstVisitor):
    def __init__(self, log=False):
        super().__init__('node-or-children', log)
        self._neg = False
        self._analysis = None

    def analyze(self, cond, before_analysis: PartitionState) -> PartitionState:
        if has_side_effects(cond):
            return before_analysis.copy().separate_all()
        else:
            self._neg = False
            self._analysis = before_analysis.copy()
            self.visit(cond)
            return self._analysis

    @contextmanager
    def _negated(self) -> ContextManager:
        self._neg = not self._neg
        yield
        self._neg = not self._neg

    def visitFunctionCallExpr(self, ast: FunctionCallExpr):
        if isinstance(ast.func, BuiltinFunction):
            op = ast.func.op
            if op == '!':
                with self._negated():
                    self.visit(ast.args[0])
            elif (op == '&&' and not self._neg) or (op == '||' and self._neg):
                self.visit(ast.args[0])
                self.visit(ast.args[1])
            elif op == 'parenthesis':
                self.visit(ast.args[0])
            elif (op == '==' and not self._neg) or (op == '!=' and self._neg):
                recursive_merge(ast.args[0], ast.args[1], self._analysis)

def _recursive_update(lhs, rhs, analysis: PartitionState, merge: bool):
    if isinstance(lhs, TupleExpr) and isinstance(rhs, TupleExpr):
        for l, r in zip(lhs.elements, rhs.elements):
            _recursive_update(l, r, analysis, merge)
    else:
        lhs = lhs.privacy_annotation_label()
        rhs = rhs.privacy_annotation_label()
        if lhs and rhs and analysis.has(rhs):
            if merge:
                analysis.merge(lhs, rhs)
            else:
                analysis.move_to(lhs, rhs)
        elif lhs:
            analysis.move_to_separate(lhs)

def recursive_merge(lhs, rhs, analysis: PartitionState):
    _recursive_update(lhs, rhs, analysis, True)

def recursive_assign(lhs, rhs, analysis: PartitionState):
    _recursive_update(lhs, rhs, analysis, False)