#!/usr/bin/env python3

"""
This module reads the data in...
"""

import numpy as np
import pickle
import os.path
import random

import logging
logging.basicConfig(level=0)

# This named tuple holds the matrices which make one minibatch
class Matrices:
    def __init__(self, minibatch_size, max_sent_len, ngrams):
        self.source_ngrams = dict()  # { N -> matrix}
        self.target_ngrams = dict()  # { N -> matrix}

        for N in ngrams:
            self.source_ngrams[N] = np.zeros((minibatch_size, max_sent_len), np.int)
            self.target_ngrams[N] = np.zeros((minibatch_size, max_sent_len), np.int)

        self.src_len = np.zeros((minibatch_size, 1), np.int)
        self.trg_len = np.zeros((minibatch_size, 1), np.int)
        self.targets = np.zeros((minibatch_size, 1), np.float32)
        self.matrix_dict = dict()

        for N in ngrams:
            self.matrix_dict["source_ngrams_{}".format(N)] = self.source_ngrams[N]  # we need a string identifier
            self.matrix_dict["target_ngrams_{}".format(N)] = self.target_ngrams[N]  # we need a string identifier

        self.matrix_dict["src_len"] = self.src_len
        self.matrix_dict["trg_len"] = self.trg_len

class Vocabularies(object):
    def __init__(self, ngrams):
        self.source_ngrams = dict()  # {order -> {"<MASK>":0,"<UNK>":1}} # source language ngrams
        self.target_ngrams = dict()  # {order -> {"<MASK>":0,"<UNK>":1}} # target language characters

        for N in ngrams:
            self.source_ngrams[N] = {"<MASK>": 0, "<UNK>": 1}  # source language ngrams
            self.target_ngrams[N] = {"<MASK>": 0, "<UNK>": 1}  # # target language characters

        self.trainable = True    # If false, it will use <UNK>

    def get_id(self, label, dict_obj):
        if self.trainable:
            return dict_obj.setdefault(label, len(dict_obj))  # auto-grows
        else:
            return dict_obj.get(label, dict_obj["<UNK>"])

def iter_data(training_source, training_target, max_pairs=None):
    counter = 0

    with open(training_source, encoding='utf-8') as src, open(training_target, encoding='utf-8') as trg:
        for src_line, trg_line in zip(src, trg):
            # if 5 <= len(src_line.strip().split()) <= 30 and \
            #         5 <= len(trg_line.strip().split()) <= 30: # sentence length must be between 5 and 30 tokens
            if True:
                yield src_line, trg_line
                counter += 1

                if max_pairs is not None and counter >= max_pairs > 0:
                    break

class InfiniteDataIterator:

    def __init__(self, training_source, training_target, max_iterations=None, max_pairs=None):
        self.training_source = training_source
        self.training_target = training_target
        self.max_iterations = max_iterations
        self.max_pairs = max_pairs
        self.data = list(iter_data(self.training_source, self.training_target, self.max_pairs))  # must memorize

    def __iter__(self):
        """
        Returns randomized training pairs as ((source_sent,target_sent),-1/+1)
        """
        # [(0,0),(1,1),(2,2)...]  #indices of source, target
        positive_indices = list(zip(range(len(self.data)), range(len(self.data))))
        counter = 0

        while True:
            indices = list(range(len(self.data)))
            random.shuffle(indices)  # shuffled indices
            negative_indices = list(enumerate(indices))  # [(0,326543),(1,96457),...]
            all_examples = positive_indices + negative_indices
            random.shuffle(all_examples)

            for src_idx, trg_idx in all_examples:
                # index where I should take the source sentence, index where I should take the target sentence
                if src_idx == trg_idx:  # same -> positive example
                    yield self.data[src_idx], 1.0
                else:  # different -> negative example
                    yield (self.data[src_idx][0], self.data[trg_idx][1]), 0.0

            counter += 1

            if self.max_iterations is not None and counter == self.max_iterations:
                break

def ngram_iterator(src, N, max_sentence_len):
    if N == 1:
        yield from src[:max_sentence_len]
    else:
        for i in range(len(src) - N + 1):
            if i >= max_sentence_len:
                break

            yield src[i: i + N]

def read_vocabularies(voc_fname, training_source, training_target, force_rebuild, ngrams):
    # ngrams -> (1,2,3)... iterable of Ns
    counter = 0

    if force_rebuild or not os.path.exists(voc_fname):
        # make sure no feature has 0 index
        logging.info("Making one pass to gather vocabulary")
        vs = Vocabularies(ngrams)

        for (sent_src, sent_target), _ in InfiniteDataIterator(training_source,
                                                               training_target,
                                                               max_iterations=1,
                                                               max_pairs=1000000):
            counter += 1

            if counter > 0 and counter % 1000 == 0:
                logging.info("Seen {} sentence pairs...".format(counter))

            for N in ngrams:
                for ngram in ngram_iterator(sent_src, N, len(sent_src)):
                    vs.get_id(ngram, vs.source_ngrams[N])

                for ngram in ngram_iterator(sent_target, N, len(sent_target)):
                    vs.get_id(ngram, vs.target_ngrams[N])

        logging.info("Saving new vocabularies to " + voc_fname)
        save_vocabularies(vs, voc_fname)
    else:
        logging.info("Loading vocabularies from " + voc_fname)
        vs = load_vocabularies(voc_fname)
    return vs

def save_vocabularies(vs, f_name):
    with open(f_name, "wb") as f:
        pickle.dump(vs, f, pickle.HIGHEST_PROTOCOL)

def load_vocabularies(f_name):
    with open(f_name, "rb") as f:
        return pickle.load(f)

def fill_batch(minibatch_size, max_sent_len, vs, data_iterator, ngrams):
    """ Iterates over the data_iterator and fills the index matrices with fresh data
        ms = matrices, vs = vocabularies
    """
    ms = Matrices(minibatch_size, max_sent_len, ngrams)
    batchsize, max_sentence_len = ms.source_ngrams[ngrams[0]].shape
    row = 0

    for (sent_src, sent_target), target in data_iterator:
        for N in ngrams:
            for j, ngram in enumerate(ngram_iterator(sent_src, N, max_sent_len)):
                ms.source_ngrams[N][row, j] = vs.get_id(ngram, vs.source_ngrams[N])
            for j, ngram in enumerate(ngram_iterator(sent_target, N, max_sent_len)):
                ms.target_ngrams[N][row, j] = vs.get_id(ngram, vs.target_ngrams[N])

        ms.src_len[row] = len(sent_src.strip().split())
        ms.trg_len[row] = len(sent_target.strip().split())
        ms.targets[row] = target
        row += 1

        if row == batchsize:
            yield ms.matrix_dict, ms.targets

            row = 0
            ms = Matrices(minibatch_size, max_sent_len, ngrams)
    else:
        if row > 0:
            yield ms.matrix_dict, ms.targets

# if __name__ == "__main__":
#     vs = read_vocabularies('bidirectional-ngrams',
#                            "data/JRC-Acquis.en-fi.fi",
#                            "data/JRC-Acquis.en-fi.en",
#                            False,
#                            (3, 4))
#     vs.trainable = False
#     ms = Matrices(100, 500, (3, 4))  # minibatchsize, max_sent_len, ngrams
#     raw_data = InfiniteDataIterator("data/JRC-Acquis.en-fi.fi", "data/JRC-Acquis.en-fi.en", 1)
#     for minibatch, targets in fill_batch(100, 500, vs, raw_data, (3, 4)):
#         print(minibatch["source_ngrams_3"])
#         print(minibatch["target_ngrams_3"])
#         print(targets)
#         break


import logging
from io import SEEK_CUR
from typing import Iterable, Iterator

from .exceptions import FlvDataError
from .format import FlvDumper, FlvParser
from .io_protocols import RandomIO
from .models import BACK_POINTER_SIZE, FlvHeader, FlvTag
from .utils import AutoRollbacker, OffsetRepositor

__all__ = 'FlvReader', 'FlvWriter'

logger = logging.getLogger(__name__)

class FlvReader:
    def __init__(
        self,
        stream: RandomIO,
        *,
        backup_timestamp: bool = False,
        restore_timestamp: bool = False,
    ) -> None:
        self._stream = stream
        self._backup_timestamp = backup_timestamp
        self._restore_timestamp = restore_timestamp
        self._parser = FlvParser(
            stream,
            backup_timestamp=backup_timestamp,
            restore_timestamp=restore_timestamp,
        )

    def read_header(self) -> FlvHeader:
        header = self._parser.parse_header()
        previous_tag_size = self._parser.parse_previous_tag_size()
        if previous_tag_size != 0:
            raise FlvDataError(f'First back-pointer must be 0: {previous_tag_size}')
        return header

    def read_tag(self, *, no_body: bool = False) -> FlvTag:
        tag = self._parser.parse_tag(no_body=no_body)
        previous_tag_size = self._parser.parse_previous_tag_size()
        if previous_tag_size != tag.tag_size:
            raise FlvDataError(f'Wrong back-pointer: {previous_tag_size}')
        return tag

    def read_tags(self, *, no_body: bool = False) -> Iterator[FlvTag]:
        while True:
            try:
                yield self.read_tag(no_body=no_body)
            except EOFError:
                break

    def rread_tag(self, *, no_body: bool = False) -> FlvTag:
        self._seek_to_previous_tag()
        with OffsetRepositor(self._stream):
            return self.read_tag(no_body=no_body)

    def rread_tags(self, *, no_body: bool = False) -> Iterator[FlvTag]:
        while True:
            try:
                yield self.rread_tag(no_body=no_body)
            except EOFError:
                break

    def read_body(self, tag: FlvTag) -> bytes:
        with OffsetRepositor(self._stream):
            self._stream.seek(tag.body_offset)
            return self._stream.read(tag.body_size)

    def _seek_to_previous_tag(self) -> int:
        try:
            self._stream.seek(-BACK_POINTER_SIZE, SEEK_CUR)
        except OSError as e:
            if e.errno == 22 and e.strerror == 'Invalid argument':
                raise EOFError()
            else:
                raise
        previous_tag_size = self._parser.parse_previous_tag_size()
        return self._stream.seek(-(4 + previous_tag_size), SEEK_CUR)

class FlvWriter:
    def __init__(self, stream: RandomIO) -> None:
        self._stream = stream
        self._dumper = FlvDumper(stream)

    def write_header(self, header: FlvHeader) -> int:
        with AutoRollbacker(self._stream):
            self._dumper.dump_header(header)
            self._dumper.dump_previous_tag_size(0)
            return header.size + BACK_POINTER_SIZE

    def write_tag(self, tag: FlvTag) -> int:
        with AutoRollbacker(self._stream):
            self._dumper.dump_tag(tag)
            self._dumper.dump_previous_tag_size(tag.tag_size)
            return tag.tag_size + BACK_POINTER_SIZE

    def write_tags(self, tags: Iterable[FlvTag]) -> int:
        return sum(map(self.write_tag, tags))


# -*- coding: utf-8 -*-
"""
Created on Sat Jan  2 00:02:58 2016

@author: owner
"""

import re
import cv2
import numpy as np
from decimal import Decimal

def is_string_like(obj):
    try:
        obj + ''
    except (TypeError, ValueError):
        return False
    return True

def array2string(array):

    if array.ndim != 1 and array.ndim != 2:
        raise ValueError('This function only supports array with ndim=1 or ndim=2')

    string = ''

    if array.ndim == 1:
        n_elems = array.shape[0]

        for n in range(n_elems):
            string += str(Decimal(float(array[n]))) if issubclass(array.dtype.type, np.floating) else str(Decimal(int(array[n])))
            if n != n_elems - 1:
                string += ' '
    else:
        rows = array.shape[0]
        cols = array.shape[1]

        for row in range(rows):
            for col in range(cols):
                string += str(Decimal(float(array[row, col]))) if issubclass(array.dtype.type, np.floating) else str(Decimal(int(array([row, col]))))
                if col != (cols - 1):
                    string += ' '
            if row != rows - 1:
                string += '\n'

    return string

def string2array2D(rows, cols, string, dtype=np.float64):

    array = np.zeros([rows, cols], dtype)

    string_rows = string.split('\n')

    for row, string_row in enumerate(string_rows):
        string_cols = string_row.split(' ')
        for col, string_col in enumerate(string_cols):
            array[row, col] = float(string_col)

    return array

def string2array1D(length, string, dtype=np.float64):

    array = np.zeros([length], dtype)

    elems = string.split(' ')

    for n, elem in enumerate(elems):
        array[n] = float(elem)

    return array

## Save numpy ndarray with ndim==3 to text file
#  @param filename Output text file name
#  @param data Array object to save
def saveText3D(filename, data):

    with open(filename, 'wb') as outfile:

        # writing a header just for the sake of readability
        outfile.write(bytes('# Array shape: {0}\n'.format(data.shape), 'utf-8'))

        # iterating through an array along the last axis
        for data_slice in data:

            # writing data into outfile
            np.savetxt(outfile, data_slice)

            # writing out a break to indicate different slices
            outfile.write(bytes('# New slice\n', 'utf-8'))

## Read random offset points
#  @param filenames File names that contains depth data
#  @param array_shape Array shape of reading data. [n_joints, n_offsets, n_dimensions]
#  @return offset_points list of 3D array that contains random offset points from true joint positions.
def readText3D(filenames):

    shape_check = re.compile('\(.*\)')

    data = []
    for filename in filenames:
        # check array shape
        with open(filename, 'rb') as infile:
            header = infile.readline().decode('utf-8')
            array_shape_str = shape_check.search(header).group(0)
            array_shape = tuple([int(x) for x in  array_shape_str[1:-1].split(', ')])

        data_slice = np.loadtxt(filename)
        data_slice = data_slice.reshape(array_shape)
        data.append(data_slice)

    return data

## Read numpy ndarray with ndim==2 from text file
#  @param filenames File names that contains silhouette data
#  @return joints 2d array that contains 3D joint positions
def saveText2D(filename, data):

    np.savetxt(filename, data)

## Read numpy ndarray with ndim==2 from text file
#  @param filenames File names that contains silhouette data
#  @return joints 2d array that contains 3D joint positions
def readText2D(filenames):

    data = []
    for filename in filenames:
        data_slice = np.loadtxt(filename)
        data.append(data_slice)

    return data

## Read depth data
#  @param filenames File names that contains depth data
#  @return depthmaps 16-bit, single-channel depth maps
def readDepthMap(filenames):

    depthmaps = []
    for filename in filenames:
        img = cv2.imread(filename, cv2.IMREAD_ANYDEPTH)
        depthmaps.append(img)

    return depthmaps

## Read joint and depth data for pose estimation
#  @param joint_prefix Directory that contains reading file.
#  @param depth_prefix
#  @param frame_begin Frame number that reading start with.
#  @param frame_end Frame number that reading stop with.
#  @param frame_step Step size of reading.
#  @param joint_extension
#  @param depth_extension
#  @return joints 2d array that contains 3D joint positions
#  @return depthmaps List of 16-bit, single-channel depth maps
def readDepthAndJoints(depth_prefix, joints_prefix, frame_begin, frame_end, frame_step, depth_extension, joints_extension):

    if(frame_step == 0):
        raise ValueError('frame_step must not be 0')

    # file names
    depth_names = []
    joints_names = []

    for i in range(frame_begin, frame_end + 1, frame_step):
        depth_names.append(depth_prefix + str(i) + depth_extension)
        joints_names.append(joints_prefix + str(i) + joints_extension)

    depthmaps = readDepthMap(depth_names)
    joints = readText2D(joints_names)

    return depthmaps, joints


import shesha.config as conf
import shesha.constants as scons

from shesha.constants import CONST

from shesha.util import dm_util, influ_util, kl_util
from shesha.util import hdf5_util as h5u

import numpy as np

import pandas as pd
from scipy import interpolate
from shesha.sutra_wrap import carmaWrap_context, Dms

from typing import List

from rich.progress import track

import os
try:
    shesha_dm = os.environ['SHESHA_DM_ROOT']
except KeyError as err:
    shesha_dm = os.environ['SHESHA_ROOT'] + "/data/dm-data"

def dm_init(context: carmaWrap_context, p_dms: List[conf.Param_dm],
            p_tel: conf.Param_tel, p_geom: conf.Param_geom,
            p_wfss: List[conf.Param_wfs] = None) -> Dms:
    """Create and initialize a Dms object on the gpu

    Args:
        context: (carmaWrap_context): context
        p_dms: (list of Param_dms) : dms settings
        p_tel: (Param_tel) : telescope settings
        p_geom: (Param_geom) : geom settings
        p_wfss: (list of Param_wfs) : wfs settings
    :return:
        Dms: (Dms): Dms object
    """
    max_extent = 0
    if (p_wfss is not None):
        xpos_wfs = []
        ypos_wfs = []
        for i in range(len(p_wfss)):
            xpos_wfs.append(p_wfss[i].xpos)
            ypos_wfs.append(p_wfss[i].ypos)
    else:
        xpos_wfs = [0]
        ypos_wfs = [0]
    if (len(p_dms) != 0):
        dms = Dms()
        types_dm = [p_dm.type for p_dm in p_dms]
        if scons.DmType.TT in types_dm:
            first_TT = types_dm.index(scons.DmType.TT)
            if np.any(np.array(types_dm[first_TT:]) != scons.DmType.TT):
                raise RuntimeError("TT must be defined at the end of the dms parameters")

        for i in range(len(p_dms)):
            max_extent = _dm_init(context, dms, p_dms[i], xpos_wfs, ypos_wfs, p_geom,
                                  p_tel.diam, p_tel.cobs, p_tel.pupangle, max_extent)

    return dms

def _dm_init(context: carmaWrap_context, dms: Dms, p_dm: conf.Param_dm, xpos_wfs: list,
             ypos_wfs: list, p_geom: conf.Param_geom, diam: float, cobs: float,
             pupAngle: float, max_extent: int):
    """ inits a Dms object on the gpu

    Args:
        context: (carmaWrap_context): context
        dms: (Dms) : dm object

        p_dm: (Param_dms) : dm settings

        xpos_wfs: (list) : list of wfs xpos

        ypos_wfs: (list) : list of wfs ypos

        p_geom: (Param_geom) : geom settings

        diam: (float) : diameter of telescope

        cobs: (float) : cobs of telescope

        pupAngle: (float) : rotation/clocking angle of the pupil in degrees

        max_extent: (int) : maximum dimension of all dms

    :return:
        max_extent: (int) : new maximum dimension of all dms

    """

    if (p_dm.pupoffset is not None):
        p_dm._puppixoffset = p_dm.pupoffset / diam * p_geom.pupdiam
    # For patchDiam
    patchDiam = dm_util.dim_dm_patch(p_geom.pupdiam, diam, p_dm.type, p_dm.alt, xpos_wfs,
                                     ypos_wfs)

    if (p_dm.type == scons.DmType.PZT):
        if p_dm.file_influ_fits == None:
            if p_dm._pitch is None:
                p_dm._pitch = patchDiam / float(p_dm.nact - 1)
            print(f"DM pitch = {p_dm._pitch:8.5f} pix = {p_dm._pitch*diam/p_geom.pupdiam:8.5f} m",
                    flush=True)
            # + 2.5 pitch each side
            extent = p_dm._pitch * (p_dm.nact + p_dm.pzt_extent)
            p_dm._n1, p_dm._n2 = dm_util.dim_dm_support(p_geom.cent, extent,
                                                        p_geom.ssize)

            # calcul defaut influsize
            make_pzt_dm(p_dm, p_geom, cobs, pupAngle)
        else:
            init_custom_dm(p_dm, p_geom, diam)

        # max_extent
        max_extent = max(max_extent, p_dm._n2 - p_dm._n1 + 1)

        dim = max(p_dm._n2 - p_dm._n1 + 1, p_geom._mpupil.shape[0])
        p_dm._dim_screen = dim
        ninflupos = p_dm._influpos.size
        n_npts = p_dm._ninflu.size  #// 2
        dms.add_dm(context, p_dm.type, p_dm.alt, dim, p_dm._ntotact, p_dm._influsize,
                   ninflupos, n_npts, p_dm.push4imat, 0, context.active_device)
        #infludata = p_dm._influ.flatten()[p_dm._influpos]
        dms.d_dms[-1].pzt_loadarrays(p_dm._influ, p_dm._influpos.astype(np.int32),
                                     p_dm._ninflu, p_dm._influstart, p_dm._i1, p_dm._j1)

    elif (p_dm.type == scons.DmType.TT):

        if (p_dm.alt == 0) and (max_extent != 0):
            extent = int(max_extent * 1.05)
            if (extent % 2 != 0):
                extent += 1
        else:
            extent = p_geom.pupdiam + 16
        p_dm._n1, p_dm._n2 = dm_util.dim_dm_support(p_geom.cent, extent, p_geom.ssize)
        # max_extent
        max_extent = max(max_extent, p_dm._n2 - p_dm._n1 + 1)

        dim = p_dm._n2 - p_dm._n1 + 1
        make_tiptilt_dm(p_dm, patchDiam, p_geom, diam)
        p_dm._dim_screen = dim
        dms.add_dm(context, p_dm.type, p_dm.alt, dim, 2, dim, 1, 1, p_dm.push4imat, 0,
                   context.active_device)
        dms.d_dms[-1].tt_loadarrays(p_dm._influ)

    elif (p_dm.type == scons.DmType.KL):

        extent = p_geom.pupdiam + 16
        p_dm._n1, p_dm._n2 = dm_util.dim_dm_support(p_geom.cent, extent, p_geom.ssize)
        # max_extent
        max_extent = max(max_extent, p_dm._n2 - p_dm._n1 + 1)

        dim = p_dm._n2 - p_dm._n1 + 1

        make_kl_dm(p_dm, patchDiam, p_geom, cobs)

        ninflu = p_dm.nkl
        p_dm._dim_screen = dim

        dms.add_dm(context, p_dm.type, p_dm.alt, dim, p_dm.nkl, p_dm._ncp, p_dm._nr,
                   p_dm._npp, p_dm.push4imat, p_dm._ord.max(), context.active_device)

        dms.d_dms[-1].kl_loadarrays(p_dm._rabas, p_dm._azbas, p_dm._ord, p_dm._cr,
                                    p_dm._cp)

    else:

        raise TypeError("This type of DM doesn't exist ")
        # Verif
        # res1 = pol2car(*y_dm(n)._klbas,gkl_sfi(*y_dm(n)._klbas, 1));
        # res2 = yoga_getkl(g_dm,0.,1);

    return max_extent

def _dm_init_factorized(context: carmaWrap_context, dms: Dms, p_dm: conf.Param_dm,
                        xpos_wfs: list, ypos_wfs: list, p_geom: conf.Param_geom,
                        diam: float, cobs: float, pupAngle: float, max_extent: int):
    """ inits a Dms object on the gpu
    NOTE: This is the

    Args:
        context: (carmaWrap_context): context

        dms: (Dms) : dm object

        p_dm: (Param_dms) : dm settings

        xpos_wfs: (list) : list of wfs xpos

        ypos_wfs: (list) : list of wfs ypos

        p_geom: (Param_geom) : geom settings

        diam: (float) : diameter of telescope

        cobs: (float) : cobs of telescope

        pupAngle: (float) : rotation/clocking angle of the pupil in degrees

        max_extent: (int) : maximum dimension of all dms

    :return:
        max_extent: (int) : new maximum dimension of all dms

    """

    if (p_dm.pupoffset is not None):
        p_dm._puppixoffset = p_dm.pupoffset / diam * p_geom.pupdiam
    # For patchDiam
    patchDiam = dm_util.dim_dm_patch(p_geom.pupdiam, diam, p_dm.type, p_dm.alt, xpos_wfs,
                                     ypos_wfs)

    if (p_dm.type == scons.DmType.PZT) and p_dm.file_influ_fits is not None:
        init_custom_dm(p_dm, p_geom, diam)
    else:
        if (p_dm.type == scons.DmType.PZT):
            p_dm._pitch = patchDiam / float(p_dm.nact - 1)
            # + 2.5 pitch each side
            extent = p_dm._pitch * (p_dm.nact + p_dm.pzt_extent)

            # calcul defaut influsize
            make_pzt_dm(p_dm, p_geom, cobs, pupAngle)

        elif (p_dm.type == scons.DmType.TT):
            if (p_dm.alt == 0) and (max_extent != 0):
                extent = int(max_extent * 1.05)
                if (extent % 2 != 0):
                    extent += 1
            else:
                extent = p_geom.pupdiam + 16

        elif (p_dm.type == scons.DmType.KL):
            extent = p_geom.pupdiam + 16
        else:
            raise TypeError("This type of DM doesn't exist ")

        # Verif
        # res1 = pol2car(*y_dm(n)._klbas,gkl_sfi(*y_dm(n)._klbas, 1));
        # res2 = yoga_getkl(g_dm,0.,1);

        p_dm._n1, p_dm._n2 = dm_util.dim_dm_support(p_geom.cent, extent, p_geom.ssize)

    # max_extent
    max_extent = max(max_extent, p_dm._n2 - p_dm._n1 + 1)

    dim = max(p_dm._n2 - p_dm._n1 + 1, p_geom._mpupil.shape[0])

    if (p_dm.type == scons.DmType.PZT):
        ninflupos = p_dm._influpos.size
        n_npts = p_dm._ninflu.size  #// 2
        dms.add_dm(context, p_dm.type, p_dm.alt, dim, p_dm._ntotact, p_dm._influsize,
                   ninflupos, n_npts, p_dm.push4imat, 0, context.active_device)
        #infludata = p_dm._influ.flatten()[p_dm._influpos]
        dms.d_dms[-1].pzt_loadarrays(p_dm._influ, p_dm._influpos.astype(np.int32),
                                     p_dm._ninflu, p_dm._influstart, p_dm._i1, p_dm._j1)
    elif (p_dm.type == scons.DmType.TT):
        make_tiptilt_dm(p_dm, patchDiam, p_geom, diam)
        dms.add_dm(context, p_dm.type, p_dm.alt, dim, 2, dim, 1, 1, p_dm.push4imat, 0,
                   context.active_device)
        dms.d_dms[-1].tt_loadarrays(p_dm._influ)
    elif (p_dm.type == scons.DmType.KL):
        make_kl_dm(p_dm, patchDiam, p_geom, cobs)
        ninflu = p_dm.nkl

        dms.add_dm(context, p_dm.type, p_dm.alt, dim, p_dm.nkl, p_dm._ncp, p_dm._nr,
                   p_dm._npp, p_dm.push4imat, p_dm._ord.max(), context.active_device)
        dms.d_dms[-1].kl_loadarrays(p_dm._rabas, p_dm._azbas, p_dm._ord, p_dm._cr,
                                    p_dm._cp)

    return max_extent

def dm_init_standalone(context: carmaWrap_context, p_dms: list, p_geom: conf.Param_geom,
                       diam=1., cobs=0., pupAngle=0., wfs_xpos=[0], wfs_ypos=[0]):
    """Create and initialize a Dms object on the gpu

    Args:
        p_dms: (list of Param_dms) : dms settings

        p_geom: (Param_geom) : geom settings

        diam: (float) : diameter of telescope (default 1.)

        cobs: (float) : cobs of telescope (default 0.)

        pupAngle: (float) : pupil rotation angle (degrees, default 0.)

        wfs_xpos: (array) : guide star x position on sky (in arcsec).

        wfs_ypos: (array) : guide star y position on sky (in arcsec).

    """
    max_extent = [0]
    if (len(p_dms) != 0):
        dms = Dms()
        for i in range(len(p_dms)):
            _dm_init(context, dms, p_dms[i], wfs_xpos, wfs_ypos, p_geom, diam, cobs,
                     pupAngle, max_extent)
    return dms

def make_pzt_dm(p_dm: conf.Param_dm, p_geom: conf.Param_geom, cobs: float,
                pupAngle: float):
    """Compute the actuators positions and the influence functions for a pzt DM.
    NOTE: if the DM is in altitude, central obstruction is forced to 0

    Args:
        p_dm: (Param_dm) : dm parameters

        p_geom: (Param_geom) : geometry parameters

        cobs: (float) : telescope central obstruction

        pupAngle: (float) : rotation/clocking angle of the pupil in degrees

    Returns:
        influ: (np.ndarray(dims=3, dtype=np.float64)) : cube of the IF for each actuator

    """
    # Petal DM (segmentation of M4)
    if (p_dm.influ_type == scons.InfluType.PETAL):
        makePetalDm(p_dm, p_geom, pupAngle)
        return

    # prepare to compute IF on partial (local) support of size <smallsize>
    coupling = p_dm.coupling
    pitch = p_dm._pitch  # unit is pixels
    smallsize = 0

    if (p_dm.influ_type == scons.InfluType.RADIALSCHWARTZ):
        smallsize = influ_util.makeRadialSchwartz(pitch, coupling)
    elif (p_dm.influ_type == scons.InfluType.SQUARESCHWARTZ):
        smallsize = influ_util.makeSquareSchwartz(pitch, coupling)
    elif (p_dm.influ_type == scons.InfluType.BLACKNUTT):
        smallsize = influ_util.makeBlacknutt(pitch, coupling)
    elif (p_dm.influ_type == scons.InfluType.GAUSSIAN):
        smallsize = influ_util.makeGaussian(pitch, coupling)
    elif (p_dm.influ_type == scons.InfluType.BESSEL):
        smallsize = influ_util.makeBessel(pitch, coupling, p_dm.type_pattern)
    elif (p_dm.influ_type == scons.InfluType.DEFAULT):
        smallsize = influ_util.makeRigaut(pitch, coupling)
    else:
        print("ERROR influtype not recognized ")
    p_dm._influsize = smallsize

    # compute location (x,y and i,j) of each actuator:
    if p_dm.type_pattern is None:
        p_dm.type_pattern = scons.PatternType.SQUARE

    if p_dm.type_pattern == scons.PatternType.HEXA:
        print("Pattern type : hexa")
        xypos = dm_util.createHexaPattern(pitch, p_geom.pupdiam * 1.1)
        p_dm.keep_all_actu = True
    elif p_dm.type_pattern == scons.PatternType.HEXAM4:
        print("Pattern type : hexaM4")
        p_dm.keep_all_actu = True
        xypos = dm_util.createDoubleHexaPattern(pitch, p_geom.pupdiam * 1.1, pupAngle)
        if p_dm.margin_out is not None:
            print(f'p_dm.margin_out={p_dm.margin_out} is being '
                  'used for pupil-based actuator filtering')
            pup_side = p_geom._ipupil.shape[0]
            cub_off = dm_util.filterActuWithPupil(xypos + pup_side // 2 - 0.5,
                                                  p_geom._ipupil,
                                                  p_dm.margin_out * p_dm.get_pitch())
            xypos = cub_off - pup_side // 2 + 0.5
            p_dm.set_ntotact(xypos.shape[1])
    elif p_dm.type_pattern == scons.PatternType.SQUARE:
        print("Pattern type : square")
        xypos = dm_util.createSquarePattern(pitch, p_dm.nact + 4)
    else:
        raise ValueError("This pattern does not exist for pzt dm")

    if p_dm.keep_all_actu:
        inbigcirc = np.arange(xypos.shape[1])
    else:
        if (p_dm.alt > 0):
            cobs = 0
        inbigcirc = dm_util.select_actuators(xypos[0, :], xypos[1, :], p_dm.nact,
                                             p_dm._pitch, cobs, p_dm.margin_in,
                                             p_dm.margin_out, p_dm._ntotact)
    p_dm._ntotact = inbigcirc.size

    # converting to array coordinates:
    xypos += p_geom.cent

    # filtering actuators outside of a disk radius = rad (see above)
    xypos = xypos[:, inbigcirc]
    ntotact = xypos.shape[1]
    xpos = xypos[0, :]
    ypos = xypos[1, :]
    i1t = (xpos - smallsize / 2 - 0.5 - p_dm._n1).astype(np.int32)
    j1t = (ypos - smallsize / 2 - 0.5 - p_dm._n1).astype(np.int32)

    p_dm._xpos = xpos
    p_dm._ypos = ypos
    p_dm._i1 = i1t
    p_dm._j1 = j1t

    # Allocate array of influence functions
    influ = np.zeros((smallsize, smallsize, ntotact), dtype=np.float32)

    # Computation of influence function for each actuator
    print("Computing Influence Function type : ", p_dm.influ_type)
    for i in track(range(ntotact)):

        i1 = i1t[i]
        x = np.tile(np.arange(i1, i1 + smallsize, dtype=np.float32),
                    (smallsize, 1)).T  # pixel coords in ref frame "dm support"
        # pixel coords in ref frame "pupil support"
        x += p_dm._n1
        # pixel coords in local ref frame
        x -= xpos[i]

        j1 = j1t[i]
        y = np.tile(np.arange(j1, j1 + smallsize, dtype=np.float32),
                    (smallsize, 1))  # idem as X, in Y
        y += p_dm._n1
        y -= ypos[i]
        # print("Computing Influence Function #%d/%d \r" % (i, ntotact), end=' ')

        if (p_dm.influ_type == scons.InfluType.RADIALSCHWARTZ):
            influ[:, :, i] = influ_util.makeRadialSchwartz(pitch, coupling, x=x, y=y)
        elif (p_dm.influ_type == scons.InfluType.SQUARESCHWARTZ):
            influ[:, :, i] = influ_util.makeSquareSchwartz(pitch, coupling, x=x, y=y)
        elif (p_dm.influ_type == scons.InfluType.BLACKNUTT):
            influ[:, :, i] = influ_util.makeBlacknutt(pitch, coupling, x=x, y=y)
        elif (p_dm.influ_type == scons.InfluType.GAUSSIAN):
            influ[:, :, i] = influ_util.makeGaussian(pitch, coupling, x=x, y=y)
        elif (p_dm.influ_type == scons.InfluType.BESSEL):
            influ[:, :, i] = influ_util.makeBessel(pitch, coupling, x=x, y=y,
                                                   patternType=p_dm.type_pattern)
        elif (p_dm.influ_type == scons.InfluType.DEFAULT):
            influ[:, :, i] = influ_util.makeRigaut(pitch, coupling, x=x, y=y)
        else:
            print("ERROR influtype not recognized (defaut or gaussian or bessel)")

    if (p_dm._puppixoffset is not None):
        xpos += p_dm._puppixoffset[0]
        ypos += p_dm._puppixoffset[1]

    if p_dm.segmented_mirror:
        # mpupil to ipupil shift
        # Good centering assumptions
        s = (p_geom._ipupil.shape[0] - p_geom._mpupil.shape[0]) // 2

        from skimage.morphology import label
        k = 0
        for i in track(range(ntotact)):
            # Pupil area corresponding to influ data
            i1, j1 = i1t[i] + s - smallsize // 2, j1t[i] + s - smallsize // 2
            pupilSnapshot = p_geom._ipupil[i1:i1 + smallsize, j1:j1 + smallsize]
            if np.all(pupilSnapshot):  # We have at least one non-pupil pixel
                continue
            labels, num = label(pupilSnapshot, background=0, return_num=True)
            if num <= 1:
                continue
            k += 1
            maxPerArea = np.array([
                    (influ[:, :, i] * (labels == k).astype(np.float32)).max()
                    for k in range(1, num + 1)
            ])
            influ[:, :, i] *= (labels == np.argmax(maxPerArea) + 1).astype(np.float32)
        print(f'{k} cross-spider influence functions trimmed.')

    influ = influ * float(p_dm.unitpervolt / np.max(influ))

    p_dm._influ = influ

    comp_dmgeom(p_dm, p_geom)

    dim = max(p_geom._mpupil.shape[0], p_dm._n2 - p_dm._n1 + 1)
    off = (dim - p_dm._influsize) // 2

def init_custom_dm(p_dm: conf.Param_dm, p_geom: conf.Param_geom, diam: float):
    """Read Fits for influence pzt fonction and form

    Args:
        p_dm: (Param_dm) : dm settings

        p_geom: (Param_geom) : geom settings

        diam: (float) : tel diameter

    Conversion. There are sereval coordinate systems.
    Some are coming from the input fits file, others from compass.
    Those systems differ by scales and offsets.
    Coord systems from the input fits file:
        - they all have the same scale: the coordinates are expressed in
          pixels
        - one system is the single common frame where fits data are described [i]
        - one is local to the minimap [l]
    Coord systems from compass:
        - they all have the same scale (different from fits one) expressed in
          pixels
        - one system is attached to ipupil (i=image, largest support) [i]
        - one system is attached to mpupil (m=medium, medium support) [m]
        - one system is local to minimap [l)]

    Variables will be named using
    f, c: define either fits or compass

    """
    from astropy.io import fits as pfits

    # read fits file
    file_name = p_dm.file_influ_fits
    if(not os.path.isfile(file_name)):
        file_name = shesha_dm + "/" + p_dm.file_influ_fits

    hdul = pfits.open(file_name)
    print("Read influence function from fits file : ", file_name)

    dm_fits_version = hdul[0].header['VERSION']

    # read mandatory keywords from the FITS file
    f_xC = hdul[0].header['XCENTER']
    f_yC = hdul[0].header['YCENTER']
    f_pixsize = hdul[0].header['PIXSIZE']
    if(dm_fits_version < 1.2 ):
        fi_i1 , fi_j1  = hdul[1].data
        f_influ        = hdul[2].data
        f_xpos, f_ypos = hdul[3].data
    else:
        fi_i1, fi_j1 = hdul['I1_J1'].data
        f_influ = hdul['INFLU'].data
        f_xpos, f_ypos = hdul['XPOS_YPOS'].data

    # Analysis of the requirements set in the COMPASS configuration file
    cases = [ p_dm.diam_dm is not None, p_dm._pitch is not None,
              p_dm.diam_dm_proj is not None ]

    # Projecting the dm in the tel pupil plane with the desired factor
    if cases == [False, False, False]:
        f_pupm = hdul[0].header['PUPM']
        scale = diam / f_pupm
        print('Custom DM: stretching DM to fit PUPM (%f) to compass (%f)' % (f_pupm, diam))
    elif cases == [True, False, False]:
        scale = diam / p_dm.diam_dm
    elif cases == [False, True, False]:
        f_pitchm = hdul[0].header['PITCHM']
        scale = p_dm._pitch / f_pitchm
    elif cases == [False, False, True]:
        f_pupm = hdul[0].header['PUPM']
        scale = p_dm.diam_dm_proj / f_pupm
    else:
        err_msg = '''Invalid rescaling parameters
        To set the scale of the custom dm, MAXIMUM ONE of the following parameters should be set:
        - p_dm.set_pitch(val) : if the pitch in the tel pupil plane is known
        - p_dm.set_diam_dm(val) : if the pupil diameter in the dm plane is known
        - p_dm.set_diam_dm_proj(val) : if the dm pupil diameter projected in the tel pupil plane is known
        If none of the above is set, the dm will be scaled so that the PUPM parameter in the fits file matches the tel pupil.
        '''
        raise RuntimeError(err_msg)

    f_pixsize *= scale
    print("Custom dm scaling factor to pupil plane :", scale)

    # Scaling factor from fits to compass system. The float32() acts as a
    # roundoff, required to avoid weird behavior when exporting/importing
    # custom DMs from/to compass
    scaleToCompass = np.float32(f_pixsize) / np.float32(p_geom._pixsize)

    # shift to add to coordinates from fits to compass
    # Compass = Fits * scaleToCompass + offsetToCompass
    # we have (compass - centreCompass) = (Fits-f_xC) * scaleToCompass
    offsetXToCompass = p_geom.cent - f_xC * scaleToCompass
    offsetYToCompass = p_geom.cent - f_yC * scaleToCompass

    # computing IF spport size in compass system
    iSize, jSize, ntotact = f_influ.shape
    if iSize != jSize:
        raise ('Error')

    ess1 = np.ceil((fi_i1+iSize) * scaleToCompass + offsetXToCompass) \
        - np.floor(fi_i1 * scaleToCompass + offsetXToCompass)
    ess1 = np.max(ess1)

    ess2 = np.ceil((fi_j1+jSize) * scaleToCompass + offsetYToCompass) \
        - np.floor(fi_j1 * scaleToCompass + offsetYToCompass)
    ess2 = np.max(ess2)
    smallsize = np.maximum(ess1, ess2).astype(int)

    # Allocate influence function maps and other arrays
    p_dm._ntotact = ntotact
    p_dm._influsize = np.int64(smallsize)
    p_dm._i1 = np.zeros(ntotact, dtype=np.int32)
    p_dm._j1 = np.zeros(ntotact, dtype=np.int32)
    p_dm._xpos = np.zeros(ntotact, dtype=np.float32)
    p_dm._ypos = np.zeros(ntotact, dtype=np.float32)
    p_dm._influ = np.zeros((smallsize, smallsize, ntotact), dtype=np.float32)

    # loop on actuators
    for i in range(ntotact):
        # pix coordinate of the minimap in fits system
        fi_x = np.arange(iSize) + fi_i1[i]
        fi_y = np.arange(jSize) + fi_j1[i]

        # transfer to compass scale
        ci_x = fi_x * scaleToCompass + offsetXToCompass
        ci_y = fi_y * scaleToCompass + offsetYToCompass

        # Creation of compass coordinates
        ci_i1 = fi_i1[i] * scaleToCompass + offsetXToCompass
        ci_j1 = fi_j1[i] * scaleToCompass + offsetYToCompass
        ci_i1 = np.floor(ci_i1).astype(np.int32)
        ci_j1 = np.floor(ci_j1).astype(np.int32)
        ci_xpix = ci_i1 + np.arange(smallsize)
        ci_ypix = ci_j1 + np.arange(smallsize)
        # WARNING: xpos and ypos are approximate !! For debug only ...
        # p_dm._xpos[i] = ci_i1 + smallsize/2.0
        # p_dm._ypos[i] = ci_j1 + smallsize/2.0

        f = interpolate.interp2d(ci_y, ci_x, f_influ[:, :, i], kind='cubic')
        temp = f(ci_ypix, ci_xpix) * p_dm.unitpervolt
        # temp[np.where(temp<1e-6)] = 0.
        p_dm._influ[:, :, i] = temp

        # ....
        p_dm._i1[i] = ci_i1
        p_dm._j1[i] = ci_j1

    tmp = p_geom.ssize - (np.max(p_dm._i1 + smallsize))
    margin_i = np.minimum(tmp, np.min(p_dm._i1))
    tmp = p_geom.ssize - (np.max(p_dm._j1 + smallsize))
    margin_j = np.minimum(tmp, np.min(p_dm._j1))

    p_dm._xpos = f_xpos * scaleToCompass + offsetXToCompass
    p_dm._ypos = f_ypos * scaleToCompass + offsetYToCompass

    p_dm._n1 = int(np.minimum(margin_i, margin_j))
    p_dm._n2 = p_geom.ssize - 1 - p_dm._n1

    p_dm._i1 -= p_dm._n1
    p_dm._j1 -= p_dm._n1

    comp_dmgeom(p_dm, p_geom)

def make_tiptilt_dm(p_dm: conf.Param_dm, patchDiam: int, p_geom: conf.Param_geom,
                    diam: float):
    """Compute the influence functions for a tip-tilt DM

    Args:
        p_dm: (Param_dm) : dm settings

        patchDiam: (int) : patchDiam for dm size

        p_geom: (Param_geom) : geom settings

        diam: (float) : telescope diameter
    :return:
        influ: (np.ndarray(dims=3,dtype=np.float64)) : cube of the IF

    """
    dim = max(p_dm._n2 - p_dm._n1 + 1, p_geom._mpupil.shape[0])
    #norms = [np.linalg.norm([w.xpos, w.ypos]) for w in p_wfs]

    nzer = 2
    influ = dm_util.make_zernike(nzer + 1, dim, patchDiam, p_geom.cent - p_dm._n1 - 0.5,
                                 p_geom.cent - p_dm._n1 - 0.5, 1)[:, :, 1:]

    # normalization factor: one unit of tilt gives 1 arcsec:
    current = influ[dim // 2 - 1, dim // 2 - 1, 0] - \
        influ[dim // 2 - 2, dim // 2 - 2, 0]
    fact = p_dm.unitpervolt * diam / p_geom.pupdiam * 4.848 / current

    influ = influ * fact
    p_dm._ntotact = influ.shape[2]
    p_dm._influsize = influ.shape[0]
    p_dm._influ = influ

    return influ

def make_kl_dm(p_dm: conf.Param_dm, patchDiam: int, p_geom: conf.Param_geom,
               cobs: float) -> None:
    """Compute the influence function for a Karhunen-Loeve DM

    Args:
        p_dm: (Param_dm) : dm settings

        patchDiam: (int) : patchDiam for dm size

        p_geom: (Param_geom) : geom settings

        cobs: (float) : telescope cobs

    """
    dim = p_geom._mpupil.shape[0]

    print("KL type: ", p_dm.type_kl)

    if (p_dm.nkl < 13):
        nr = np.int64(5.0 * np.sqrt(52))  # one point per degree
        npp = np.int64(10.0 * nr)
    else:
        nr = np.int64(5.0 * np.sqrt(p_dm.nkl))
        npp = np.int64(10.0 * nr)

    radp = kl_util.make_radii(cobs, nr)

    kers = kl_util.make_kernels(cobs, nr, radp, p_dm.type_kl, p_dm.outscl)

    evals, nord, npo, ordd, rabas = kl_util.gkl_fcom(kers, cobs, p_dm.nkl)

    azbas = kl_util.make_azimuth(nord, npp)

    ncp, ncmar, px, py, cr, cp, pincx, pincy, pincw, ap = kl_util.set_pctr(
            patchDiam, nr, npp, p_dm.nkl, cobs, nord)

    p_dm._ntotact = p_dm.nkl
    p_dm._nr = nr  # number of radial points
    p_dm._npp = npp  # number of elements
    p_dm._ord = ordd  # the radial orders of the basis
    p_dm._rabas = rabas  # the radial array of the basis
    p_dm._azbas = azbas  # the azimuthal array of the basis
    p_dm._ncp = ncp  # dim of grid
    p_dm._cr = cr  # radial coord in cartesien grid
    p_dm._cp = cp  # phi coord in cartesien grid
    p_dm._i1 = np.zeros((p_dm.nkl), dtype=np.int32) + \
        (dim - patchDiam) // 2
    p_dm._j1 = np.zeros((p_dm.nkl), dtype=np.int32) + \
        (dim - patchDiam) // 2
    p_dm._ntotact = p_dm.nkl
    p_dm.ap = ap

def comp_dmgeom(p_dm: conf.Param_dm, p_geom: conf.Param_geom):
    """Compute the geometry of a DM : positions of actuators and influence functions

    Args:
        dm: (Param_dm) : dm settings

        geom: (Param_geom) : geom settings
    """
    smallsize = p_dm._influsize
    nact = p_dm._ntotact
    dm_dim = int(p_dm._n2 - p_dm._n1 + 1)
    mpup_dim = p_geom._mpupil.shape[0]

    if (dm_dim < mpup_dim):
        print('DM support is smaller than mpupil')
        offs = (mpup_dim - dm_dim) // 2
    else:
        print('DM support is larger than mpupil')
        offs = 0
        mpup_dim = dm_dim

    indgen = np.tile(np.arange(smallsize, dtype=np.int32), (smallsize, 1))

    tmpx = np.tile(indgen, (nact, 1, 1))
    tmpy = np.tile(indgen.T, (nact, 1, 1))

    tmpx += offs + p_dm._i1[:, None, None]
    tmpy += offs + p_dm._j1[:, None, None]

    tmp = tmpx + mpup_dim * tmpy

    # bug in limit of def zone -10 destoe influpos for all actuator
    tmp[tmpx < 0] = mpup_dim * mpup_dim + 10  # -10
    tmp[tmpy < 0] = mpup_dim * mpup_dim + 10  # -10
    tmp[tmpx > dm_dim - 1] = mpup_dim * mpup_dim + 10  # -10
    tmp[tmpy > dm_dim - 1] = mpup_dim * mpup_dim + 10  # -10
    itmps = np.argsort(tmp.flatten()).astype(np.int32)
    tmps = tmp.flatten()[itmps].astype(np.int32)
    itmps = itmps[np.where(itmps > -1)]

    istart = np.zeros((mpup_dim * mpup_dim), dtype=np.int32)
    npts = np.zeros((mpup_dim * mpup_dim), dtype=np.int32)

    tmps_unique, cpt = np.unique(tmps, return_counts=True)
    if (tmps_unique > npts.size - 1).any():
        tmps_unique = tmps_unique[:-1]
        cpt = cpt[:-1]

    for i in range(tmps_unique.size):
        npts[tmps_unique[i]] = cpt[i]
    istart[1:] = np.cumsum(npts[:-1])

    p_dm._influpos = itmps[:np.sum(npts)].astype(np.int32)
    # infludata = p_dm._influ.flatten()[p_dm._influpos]
    # p_dm._influ = infludata[:,None,None]
    # p_dm._influpos = p_dm._influpos / (smallsize * smallsize)
    p_dm._ninflu = npts.astype(np.int32)
    p_dm._influstart = istart.astype(np.int32)

    p_dm._i1 += offs
    p_dm._j1 += offs

    # ninflu = np.zeros((istart.size * 2))
    # ninflu[::2] = istart.astype(np.int32)
    # ninflu[1::2] = npts.astype(np.int32)

    # p_dm._ninflu = ninflu

def correct_dm(context, dms: Dms, p_dms: list, p_controller: conf.Param_controller,
               p_geom: conf.Param_geom, imat: np.ndarray = None, dataBase: dict = {},
               use_DB: bool = False):
    """Correct the geometry of the DMs using the imat (filter unseen actuators)

    Args:
        context: (carmaWrap_context): context
        dms: (Dms) : Dms object
        p_dms: (list of Param_dm) : dms settings
        p_controller: (Param_controller) : controller settings
        p_geom: (Param_geom) : geom settings
        imat: (np.ndarray) : interaction matrix
        dataBase: (dict): dictionary containing paths to files to load
        use_DB: (bool): dataBase use flag
    """
    print("Filtering unseen actuators... ")
    if imat is not None:
        resp = np.sqrt(np.sum(imat**2, axis=0))

    ndm = p_controller.ndm.size
    inds = 0

    for nmc in range(ndm):
        nm = p_controller.ndm[nmc]
        nactu_nm = p_dms[nm]._ntotact
        # filter actuators only in stackarray mirrors:
        if (p_dms[nm].type == scons.DmType.PZT):
            if "dm" in dataBase:
                influpos, ninflu, influstart, i1, j1, ok = h5u.load_dm_geom_from_dataBase(
                        dataBase, nmc)
                p_dms[nm].set_ntotact(ok.shape[0])
                p_dms[nm].set_influ(p_dms[nm]._influ[:, :, ok.tolist()])
                p_dms[nm].set_xpos(p_dms[nm]._xpos[ok])
                p_dms[nm].set_ypos(p_dms[nm]._ypos[ok])
                p_dms[nm]._influpos = influpos
                p_dms[nm]._ninflu = ninflu
                p_dms[nm]._influstart = influstart
                p_dms[nm]._i1 = i1
                p_dms[nm]._j1 = j1
            else:
                tmp = resp[inds:inds + p_dms[nm]._ntotact]
                ok = np.where(tmp > p_dms[nm].thresh * np.max(tmp))[0]
                nok = np.where(tmp <= p_dms[nm].thresh * np.max(tmp))[0]

                p_dms[nm].set_ntotact(ok.shape[0])
                p_dms[nm].set_influ(p_dms[nm]._influ[:, :, ok.tolist()])
                p_dms[nm].set_xpos(p_dms[nm]._xpos[ok])
                p_dms[nm].set_ypos(p_dms[nm]._ypos[ok])
                p_dms[nm].set_i1(p_dms[nm]._i1[ok])
                p_dms[nm].set_j1(p_dms[nm]._j1[ok])

                comp_dmgeom(p_dms[nm], p_geom)
                if use_DB:
                    h5u.save_dm_geom_in_dataBase(nmc, p_dms[nm]._influpos,
                                                 p_dms[nm]._ninflu,
                                                 p_dms[nm]._influstart, p_dms[nm]._i1,
                                                 p_dms[nm]._j1, ok)

            dim = max(p_dms[nm]._n2 - p_dms[nm]._n1 + 1, p_geom._mpupil.shape[0])
            ninflupos = p_dms[nm]._influpos.size
            n_npts = p_dms[nm]._ninflu.size
            dms.remove_dm(nm)
            dms.insert_dm(context, p_dms[nm].type, p_dms[nm].alt, dim,
                          p_dms[nm]._ntotact, p_dms[nm]._influsize, ninflupos, n_npts,
                          p_dms[nm].push4imat, 0, p_dms[nm].dx / p_geom._pixsize,
                          p_dms[nm].dy / p_geom._pixsize, p_dms[nm].theta, p_dms[nm].G,
                          context.active_device, nm)
            dms.d_dms[nm].pzt_loadarrays(p_dms[nm]._influ, p_dms[nm]._influpos.astype(
                    np.int32), p_dms[nm]._ninflu, p_dms[nm]._influstart, p_dms[nm]._i1,
                                         p_dms[nm]._j1)

        inds += nactu_nm
    print("Done")

def makePetalDm(p_dm, p_geom, pupAngleDegree):
    '''
    makePetalDm(p_dm, p_geom, pupAngleDegree)

    The function builds a DM, segmented in petals according to the pupil
    shape. The petals will be adapted to the EELT case only.

    <p_geom> : compass object p_geom. The function requires the object p_geom
               in order to know what is the pupil mask, and what is the mpupil.
    <p_dm>   : compass petal dm object p_dm to be created. The function will
               transform/modify in place the attributes of the object p_dm.
    <pupAngleDegree> : rotation/clocking angle of the pupil in degrees

    '''
    p_dm._n1 = p_geom._n1
    p_dm._n2 = p_geom._n2
    influ, i1, j1, smallsize, nbSeg = make_petal_dm_core(p_geom._mpupil, pupAngleDegree)
    p_dm._influsize = smallsize
    p_dm.set_ntotact(nbSeg)
    p_dm._i1 = i1
    p_dm._j1 = j1
    p_dm._xpos = i1 + smallsize / 2 + p_dm._n1
    p_dm._ypos = j1 + smallsize / 2 + p_dm._n1
    p_dm._influ = influ

    # generates the arrays of indexes for the GPUs
    comp_dmgeom(p_dm, p_geom)

def make_petal_dm_core(pupImage, pupAngleDegree):
    """
    <pupImage> : image of the pupil
    <pupAngleDegree> : rotation angle of the pupil in degrees

    La fonction renvoie des fn d'influence en forme de petale d'apres
    une image de la pupille, qui est supposee etre segmentee.

    influ, i1, j1, smallsize, nbSeg = make_petal_dm_core(pupImage, 0.0)
    """
    # Splits the pupil into connex areas.
    # <segments> is the map of the segments, <nbSeg> in their number.
    # binary_opening() allows us to suppress individual pixels that could
    # be identified as relevant connex areas
    from scipy.ndimage import label
    from scipy.ndimage.morphology import binary_opening
    s = np.ones((2, 2), dtype=bool)
    segments, nbSeg = label(binary_opening(pupImage, s))

    # Faut trouver le plus petit support commun a tous les
    # petales : on determine <smallsize>
    smallsize = 0
    i1t = []  # list of starting indexes of influ functions
    j1t = []
    i2t = []  # list of ending indexes of influ functions
    j2t = []
    for i in range(nbSeg):
        petal = segments == (i + 1)  # identification (boolean) of a given segment
        profil = np.sum(petal, axis=1) != 0
        extent = np.sum(profil).astype(np.int32)
        i1t.append(np.min(np.where(profil)[0]))
        i2t.append(np.max(np.where(profil)[0]))
        if extent > smallsize:
            smallsize = extent

        profil = np.sum(petal, axis=0) != 0
        extent = np.sum(profil).astype(np.int32)
        j1t.append(np.min(np.where(profil)[0]))
        j2t.append(np.max(np.where(profil)[0]))
        if extent > smallsize:
            smallsize = extent

    # extension de la zone minimale pour avoir un peu de marge
    smallsize += 2

    # Allocate array of influence functions
    influ = np.zeros((smallsize, smallsize, nbSeg), dtype=np.float32)

    npt = pupImage.shape[0]
    i0 = j0 = npt / 2 - 0.5
    petalMap = build_petals(nbSeg, pupAngleDegree, i0, j0, npt)
    ii1 = np.zeros(nbSeg)
    jj1 = np.zeros(nbSeg)
    for i in range(nbSeg):
        ip = (smallsize - i2t[i] + i1t[i] - 1) // 2
        jp = (smallsize - j2t[i] + j1t[i] - 1) // 2
        i1 = np.maximum(i1t[i] - ip, 0)
        j1 = np.maximum(j1t[i] - jp, 0)
        if (j1 + smallsize) > npt:
            j1 = npt - smallsize
        if (i1 + smallsize) > npt:
            i1 = npt - smallsize
        #petal = segments==(i+1) # determine le segment pupille veritable
        k = petalMap[i1 + smallsize // 2, j1 + smallsize // 2]
        petal = (petalMap == k)
        influ[:, :, k] = petal[i1:i1 + smallsize, j1:j1 + smallsize]
        ii1[k] = i1
        jj1[k] = j1

    return influ, ii1, jj1, int(smallsize), nbSeg

def build_petals(nbSeg, pupAngleDegree, i0, j0, npt):
    """
    Makes an image npt x npt of <nbSeg> regularly spaced angular segments
    centred on (i0, j0).
    Origin of angles is set by <pupAngleDegree>.

    The segments are oriented as defined in document "Standard Coordinates
    and Basic Conventions", ESO-193058.
    This document states that the X axis lies in the middle of a petal, i.e.
    that the axis Y is along the spider.
    The separation angle between segments are [-30, 30, 90, 150, -150, -90].
    For this reason, an <esoOffsetAngle> = -pi/6 is introduced in the code.

    nbSeg = 6
    pupAngleDegree = 5.0   # pupil rotation/clocking angle
    i0 = j0 = 112.3
    npt = 222
    p = build_petals(nbSeg, pupAngleDegree, i0, j0, npt)
    """
    # conversion to radians
    rot = pupAngleDegree * np.pi / 180.0

    # building coordinate maps
    esoOffsetAngle = -np.pi / 6  # -30°, ESO definition.
    x = np.arange(npt) - i0
    y = np.arange(npt) - j0
    X, Y = np.meshgrid(x, y, indexing='ij')
    theta = (np.arctan2(Y, X) - rot + 2 * np.pi - esoOffsetAngle) % (2 * np.pi)

    # Compute separation angle between segments: start and end.
    angleStep = 2 * np.pi / nbSeg
    startAngle = np.arange(nbSeg) * angleStep
    endAngle = np.roll(startAngle, -1)
    endAngle[-1] = 2 * np.pi  # last angle is 0.00 and must be replaced by 2.pi
    petalMap = np.zeros((npt, npt), dtype=int)
    for i in range(nbSeg):
        nn = np.where(np.logical_and(theta >= startAngle[i], theta < endAngle[i]))
        petalMap[nn] = i
    return petalMap


import json
from django.shortcuts import render
from django.core.paginator import Paginator
from django.http import HttpResponse
from django.shortcuts import redirect
from django.db.models import Q
from django.utils import timezone

from apps.pregunta.models import Pregunta, Respuesta
from apps.usuario.models import Usuario
from apps.pregunta.forms import PreguntaForm, RespuestaForm
from apps.utils.ServiceResponse import ServiceResponse

# Create your views here.

def list(request):
    pregunta = Pregunta.objects.all().order_by('-fecha')
    query = request.GET.get("buscar")
    if query:
        pregunta = Pregunta.objects\
        .filter(titulo__icontains=query)

    paginator = Paginator(pregunta, 10)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    contexto = {'page_obj': page_obj, 'page': page_number}
    return render(request, 'pregunta/list.html', contexto)

def delete(request, id_pregunta):
    mensaje = ''
    userId = request.user.id
    usuario = Usuario.objects.filter(user_id = userId).exists()
    pregunta = Pregunta.objects.get(id=id_pregunta)
    if usuario:
        usu = Usuario.objects.get(user_id = userId)
        if pregunta.usuario_id == usu.id:
            if request.method == 'POST':
                pregunta.delete()
                return redirect('list')
        else:
            mensaje='No es el creador de la pregunta'
    else:
        mensaje='Debe loguearse para poder eliminar una pregunta'
    return render(request, 'pregunta/delete.html', {'pregunta': pregunta, 'msg': mensaje})

def delete_respuesta(request, id_respuesta):
    mensaje = ''
    userId = request.user.id
    usuario = Usuario.objects.filter(user_id = userId).exists()
    respuesta = Respuesta.objects.get(id=id_respuesta)
    if usuario:
        usu = Usuario.objects.get(user_id = userId)
        if respuesta.pregunta.usuario_id == usu.id:
            if request.method == 'POST':
                respuesta.delete()
                return redirect('list')
        else:
            mensaje = 'No es el creador de la pregunta'
    return render(request, 'pregunta/delete_respuesta.html', {'respuesta': respuesta, 'msg': mensaje})

def respuesta_correcta(request, id_respuesta):
    userId = request.user.id
    respuesta = Respuesta.objects.get(id__exact=id_respuesta)
    id_pregunta = respuesta.pregunta_id
    usu = Usuario.objects.get(user_id = userId)
    if respuesta.pregunta.usuario_id == usu.id:
        cont = Respuesta.objects.filter(Q(pregunta_id=id_pregunta) & Q(correcta=True)).count()
        if cont == 0:
            respuesta.correcta = True
            respuesta.save()
            response = ServiceResponse()
            response.addData('ok')
            data = json.dumps(response.getResult())
            return HttpResponse(data, content_type='application/json')

def nuevo(request):
    mensaje = ''
    userId = request.user.id
    usuario = Usuario.objects.filter(user_id = userId).exists()
    if request.method == 'POST':
        form = PreguntaForm(request.POST, request.FILES)
        if form.is_valid():
            existe_pregunta = Pregunta.objects.filter(detalle_pregunta=request.POST['detalle_pregunta']).exists()
            if existe_pregunta:
                mensaje = 'Existe la pregunta'
            else:
                pregunta = form.save(commit=False)
                pregunta.fecha = timezone.now()
                if usuario:
                    usu = Usuario.objects.get(user_id = userId)
                    pregunta.usuario_id = usu.id
                pregunta.save()
                return redirect('list')
    else:
        form = PreguntaForm()
    return render(request, 'pregunta/form.html', {'form': form, 'msg': mensaje})

def edit(request, id_pregunta):
    mensaje = ''
    pregunta = Pregunta.objects.get(id=id_pregunta)
    if request.method == 'GET':
        form = PreguntaForm(instance=pregunta)
    else :
        form = PreguntaForm(request.POST, instance=pregunta)
        if form.is_valid():
            count = Pregunta.objects.filter(Q(detalle_pregunta=request.POST['detalle_pregunta']) & ~Q(id = pregunta.id)).count()
            if count == 0:
                form.save()
                return redirect('list')
            else:
                mensaje = 'Ya existe la pregunta'
    return render(request, 'pregunta/form.html', {'form':form, 'msg': mensaje })

def nueva_respuesta(request, id_pregunta):
    mensaje = ''
    if request.method == 'POST':
        form = RespuestaForm(request.POST, request.FILES)
        if form.is_valid():
            respuesta = form.save(commit=False)
            respuesta.pregunta_id = id_pregunta
            respuesta.fecha = timezone.now()
            respuesta.save()
            return redirect('/pregunta/list_respuesta/'+str(id_pregunta))

    else:
        form = RespuestaForm()
    return render(request, 'pregunta/form_respuesta.html', {'form': form, 'msg': mensaje})

def list_respuesta(request, id_pregunta):
    respuesta = Respuesta.objects.filter(pregunta_id=id_pregunta).order_by('-correcta')
    query = request.GET.get("buscar")
    if query:
        respuesta = Respuesta.objects\
        .filter(Q(pregunta_id=id_pregunta) & Q(correcta__icontains=query))

    paginator = Paginator(respuesta, 10)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    contexto = {'page_obj': page_obj, 'page': page_number}
    return render(request, 'pregunta/list_respuesta.html', contexto)


from collections import Counter
def common(str1, str2):
    dict1 = Counter(str1)
    dict2 = Counter(str2)

    common_dict = dict1 & dict2

    if len(common_dict) == 0:
        return (-1)

    commonChars = list(common_dict.elements())

    commonChars = sorted(commonChars)
    print("".join(commonChars))

if __name__ == '__main__':
    str1 = "geeks"
    str2 = "forgeeks"

    common(str1, str2)


import csv
import collections
from data_display.utils.string_display import get_strings_from_cache

# use streams pre-emptively in case table sizes get very big
from django.http import StreamingHttpResponse

# Some copy-pasta from the django docs:
class Echo:
    """
        A dummy class used to fit the "writer" interface in csv.
    """
    def write(self, value):
        return value

def export_as_csv(model):
    """
    :param Django model:
    :return: csv file representation of the Django model, wrapped in an HTTP Response
    """
    rows = collections.deque(model.objects.all().values_list())
    rows.appendleft(
        get_strings_from_cache(model._meta.get_fields())
    )
    pseudo_buffer = Echo()
    writer = csv.writer(pseudo_buffer)
    response = StreamingHttpResponse((writer.writerow(row) for row in rows),
                                     content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
    return response


import argparse

from components.muc4_tools import get_event_keywords
from components.load_muc4 import load_muc4

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--all-human-names", type=str,
                        default="data/muc34/outputs/all-human-names.txt")
    args = parser.parse_args()
    return args

def main(args):

    _, dev_events, _, tst_events, _ = load_muc4()
    keywords = list(map(get_event_keywords, dev_events)) +\
        list(map(get_event_keywords, tst_events))
    human_names = [
        _name
        for _event_keywords in keywords
        for _name, _category in _event_keywords.items()
        if _category.endswith("NAME")
        # for _subname in _name.split(" ")
    ]
    human_names = list(set(human_names))

    with open(args.all_human_names, 'w') as f:
        f.writelines(
            list(map(lambda x: x + "\n", human_names))
        )

if __name__ == "__main__":
    args = parse_args()
    main(args)


import configparser
import os

class Config:
    def __init__(self):
        cur_path = os.path.dirname(os.path.realpath(__file__))
        self.conf_path = os.path.join(cur_path, "spider.ini")
        self.conf = configparser.ConfigParser()
        self.conf.read(self.conf_path, encoding='utf-8')

    def get_conf_data(self):
        """
        返回配置文件的配置信息
        :return: dict
        """
        dic = {}
        dic['product'] = self.conf.get('bug', 'product')
        dic['iver'] = self.conf.get('bug', 'iver')
        return dic

    def change_conf_data(self, conf_dict):
        """
        更新配置文件信息
        :param conf_dict: 字典类型
        :return:
        """
        try:
            self.conf.set('bug', 'product', conf_dict['product'])
            self.conf.set('bug', 'iver', conf_dict['iver'])
        except Exception as e:
            print(e, "\n配置文件传入参数有误，请传入字典类型，且包含'product'和'iver'这两个key")
        self.conf.write(open(self.conf_path, 'w', encoding='utf-8'))

if __name__ == "__main__":
    conf = Config()
    conf_data = conf.get_conf_data()
    print(conf_data)
    conf_dic = {}
    conf_dic['product'] = 'nimo_ios'
    conf_dic['iver'] = '1.9.0'
    conf.change_conf_data(conf_dict=conf_dic)
    new_conf_data = conf.get_conf_data()
    print(new_conf_data)

    conf.change_conf_data(conf_dict={'product': 'nimo_android', 'iver': '1.8.0'})
    print(conf.get_conf_data())


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

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from settings import USER_AGENTS
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from lxml import etree
from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware
from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
import signal
import random
import time
import requests
import base64
import scrapy

# User-Agent 下载中间件
class RandomUserAgent(object):
    def __init__(self):
        # 初始化User-Agent'
        self.user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) \
        Chrome/22.0.1207.1 Safari/537.1"

    def process_request(self, request, spider):
        # 随机选中User-Agent
        self.user_agent = random.choice(USER_AGENTS)
        request.headers.setdefault('User-Agent', self.user_agent)

# ip_port 下载中间件
class RandomProxy(object):
    def __init__(self):
        self.proxy_auth = ""
        self.proxy_api = "http://dps.kuaidaili.com/api/getdps/?orderid=958655825381063&num=50&ut=1&sep=3"
        # 返回的时代理ip:port列表
        self.proxy_list = requests.get(self.proxy_api).text.split()

    def process_request(self, request, spider):
        proxy = random.choice(self.proxy_list)
        base64_userpass = base64.b64encode(self.proxy_auth)
        request.meta['proxy'] = "http://" + proxy
        # if self.proxy_auth != None:
        request.headers['Proxy-Authorization'] = "Basic " + base64_userpass

# article ChromeDriver中间件
class ArticleChromeDriverMiddleware(object):
    def process_request(self, request, spider):
        if request.meta.has_key('ChromeDriver'):
            options = Options()
            options.set_headless(headless=True)
            driver = webdriver.Chrome(chrome_options=options)
            try:
                driver.get(request.url)
                # 睡眠1.5秒等待ChromeDriver加载资源
                time.sleep(1.5)
                body = driver.page_source
                url = driver.current_url
                # 无论是否在try还是except里return，finally都会执行
                return scrapy.http.HtmlResponse(url, body=body, request=request, encoding='utf-8')
            except Exception:
                return scrapy.http.HtmlResponse(request.url, body=body, request=request, encoding='utf-8')
            # driver.service.process.send_signal(signal.SIGTERM)
            finally:
                driver.quit()

# comment ChromeDriver中间件
class CommentChromeDriverMiddleware(object):
    def process_request(self, request, spider):
        if request.meta.has_key('ChromeDriver'):
            options = Options()
            options.set_headless(headless=True)
            driver = webdriver.Chrome(chrome_options=options)
            driver.get(request.url)
            time.sleep(0.5)
            selector = etree.HTML(driver.page_source)
            # 循环退出条件：当页面加载更多的a标签不存在时
            while selector.xpath("//div[@class='more']/a"):
                selector = etree.HTML(driver.page_source)
                # 一直点击加载更多
                driver.find_element_by_class_name("more").click()

            body = driver.page_source
            url = driver.current_url
            # 解决driver.quit出现错误
            # driver.service.process.send_signal(signal.SIGTERM)
            driver.quit()
            # 返回处理后请求响应
            return scrapy.http.HtmlResponse(url, body=body, request=request, encoding='utf-8')


from qiskit import QiskitError
from qiskit.compiler import assemble
from qiskit.providers.aer import QasmSimulator
from .tools import mixed_unitary_noise_model, \
                   reset_noise_model, kraus_noise_model, no_noise, \
                   simple_cnot_circuit, simple_u3_circuit

# Write the benchmarking functions here.
# See "Writing benchmarks" in the asv docs for more information.

class SimpleU3TimeSuite:
    """
    Benchmark simple circuits with just one U3 gate

    The methods defined in this class will be executed by ASV framework as many
    times as the combination of all parameters exist in `self.params`, for
    exmaple: self.params = ([1,2,3],[4,5,6]), will run all methdos 9 times:
        time_method(1,4)
        time_method(1,5)
        time_method(1,6)
        time_method(2,4)
        time_method(2,5)
        time_method(2,6)
        time_method(3,4)
        time_method(3,5)
        time_method(3,6)

    For each noise model, we want to test various configurations of number of
    qubits
    """

    def __init__(self):
        self.timeout = 60 * 20
        self.backend = QasmSimulator()
        self.circuits = []
        for i in 5, 10, 15:
            circuit = simple_u3_circuit(i)
            self.circuits.append(assemble(circuit, self.backend, shots=1))

        self.param_names = [
            "Simple u3 circuits", "Noise Model"
        ]
        self.params = (self.circuits, [
            no_noise(),
            mixed_unitary_noise_model(),
            reset_noise_model(),
            kraus_noise_model()
        ])

    def time_simple_u3(self, qobj, noise_model_wrapper):
        """ Benchmark for circuits with a simple u3 gate """
        result = self.backend.run(
            qobj, noise_model=noise_model_wrapper()
        ).result()
        if result.status != 'COMPLETED':
            raise QiskitError("Simulation failed. Status: " + result.status)

class SimpleCxTimeSuite:
    """
    Benchmark simple circuits with just on CX gate

    For each noise model, we want to test various configurations of number of
    qubits
    """

    def __init__(self):
        self.timeout = 60 * 20
        self.backend = QasmSimulator()
        self.circuits = []
        self.param_names = [
            "Simple cnot circuits", "Noise Model"
        ]
        for i in 5, 10, 15:
            circuit = simple_cnot_circuit(i)
            self.circuits.append(assemble(circuit, self.backend, shots=1))
        self.params = (self.circuits, [
            no_noise(),
            mixed_unitary_noise_model(),
            reset_noise_model(),
            kraus_noise_model()
        ])

    def time_simple_cx(self, qobj, noise_model_wrapper):
        """ Benchmark for circuits with a simple cx gate """
        result = self.backend.run(
            qobj, noise_model=noise_model_wrapper()
        ).result()
        if result.status != 'COMPLETED':
            raise QiskitError("Simulation failed. Status: " + result.status)


from django.conf.urls import url
from app.views import *

urlpatterns = [
    # Matches any html file - to be used for gentella
    # Avoid using your .html in your resources.
    # Or create a separate django app.
    # url(r'^.*\.html', views.gentella_html, name='gentella'),

#新增评论
    url(r'^add/$', ReviewCreate.as_view(), name="review-add"),

    url(r'^.*\.html',HaOpsView.as_view(),),

    # The home page
    url(r'^$', IndexView.as_view()),

]


import openpyxl

def convert_result_to_excel(result,excel_file_path):
  wb=openpyxl.Workbook()
  sheet=wb['Sheet']

  header_list=list(result[0].keys())
  for c in range(len(header_list)):
    sheet.cell(row=1,column=c+1).value=header_list[c]

  for r in range(len(result)):
    for c in range(len(header_list)):
      sheet.cell(row=r+2,column=c+1).value=result[r][header_list[c]]

  wb.save(excel_file_path)


from django import forms

from .models import RSVP

class RSVPForm(forms.ModelForm):
    class Meta:
        model = RSVP
        fields = ['name', 'email', 'attending', 'guests', 'address1', 'address2', 'city', 'zip_code',
                  'state', 'song_request']
        widgets = {
            'attending': forms.RadioSelect(
                choices=[(True, 'Yes'), (False, 'No')],
                attrs={'class': 'form-check-input'},
            )
        }


from __future__ import annotations

from typing import Optional, Tuple

from retentioneering.backend.tracker import (
    collect_data_performance,
    time_performance,
    track,
)
from retentioneering.constants import DATETIME_UNITS
from retentioneering.utils.doc_substitution import docstrings

from ..types import EventstreamType

class LabelCroppedPathsHelperMixin:
    @docstrings.with_indent(12)
    @time_performance(
        scope="label_cropped_paths",
        event_name="helper",
        event_value="combine",
    )
    def label_cropped_paths(
        self: EventstreamType,
        left_cutoff: Optional[Tuple[float, DATETIME_UNITS]],
        right_cutoff: Optional[Tuple[float, DATETIME_UNITS]],
    ) -> EventstreamType:
        """
        A method of ``Eventstream`` class that creates new synthetic event(s) for each user based
        on the timeout threshold: ``cropped_left`` and ``cropped_right``.

        Parameters
        ----------
            %(LabelCroppedPaths.parameters)s

        Returns
        -------
        Eventstream
            Input ``eventstream`` with new synthetic events.

        """
        calling_params = {
            "left_cutoff": left_cutoff,
            "right_cutoff": right_cutoff,
        }
        not_hash_values = ["left_cutoff", "right_cutoff"]

        # avoid circular import
        from retentioneering.data_processors_lib import (
            LabelCroppedPaths,
            LabelCroppedPathsParams,
        )
        from retentioneering.preprocessing_graph import PreprocessingGraph
        from retentioneering.preprocessing_graph.nodes import EventsNode

        p = PreprocessingGraph(source_stream=self)  # type: ignore

        params = dict(left_cutoff=left_cutoff, right_cutoff=right_cutoff)

        node = EventsNode(processor=LabelCroppedPaths(params=LabelCroppedPathsParams(**params)))  # type: ignore
        p.add_node(node=node, parents=[p.root])
        result = p.combine(node)
        del p
        collect_data_performance(
            scope="label_cropped_paths",
            event_name="metadata",
            called_params=calling_params,
            not_hash_values=not_hash_values,
            performance_data={},
            eventstream_index=self._eventstream_index,
            parent_eventstream_index=self._eventstream_index,
            child_eventstream_index=result._eventstream_index,
        )

        return result


import argparse
import BLPalette
import GPLPalette
import PaletteCore

extraExt = '.extra'

argparser = argparse.ArgumentParser(
  description="Convert between Blockland and GIMP palette formats",
  epilog="Only provide one mode.")
argparser.add_argument('--to-gpl', metavar='FILENAME', help="convert Blockland .txt to .gpl")
argparser.add_argument('--to-bl-txt', metavar='FILENAME', help="convert .gpl to Blockland .txt")
argparser.add_argument('--no-restore-alphas', action='store_true',
                       help="Don't read alpha values from extra data.")
argparser.add_argument('--no-restore-divs', action='store_true',
                       help="Don't read divs from extra data.")
argparser.add_argument('--no-restore-extra', action='store_true',
                       help="Don't read any extra data.")

args = argparser.parse_args()

if args.to_gpl != None and args.to_bl_txt != None:
  print("Both modes given at once.")
elif args.to_gpl != None:
  blpal = BLPalette.BLPalette()
  gplpal = GPLPalette.GPLPalette()

  outfilename = args.to_gpl[:args.to_gpl.rindex('.')] + '.gpl'
  extrafilename = outfilename + extraExt
  print("Reading {:s}...".format(args.to_gpl))
  with open(args.to_gpl, "r") as infile:
    for line in infile:
      blpal.add_line(line)

  print("Converting data structures...")
  for color in blpal.palette:
    gplpal.add_entry(GPLPalette.GPLColor.fromColor(color))

  print("Writing extra data to {:s}...".format(extrafilename))
  with open(extrafilename, 'w') as extrafile:
    extrafile.write(str(len(blpal.palette)) + ' ')
    for color in enumerate(blpal.palette):
      extrafile.write(str(color[1].a) + ' ')
    extrafile.write(str(len(blpal.divs)) + ' ')
    for div in blpal.divs:
      extrafile.write(str(div[0]) + ' ')
    for div in blpal.divs:
      extrafile.write(str(div[1]) + ' ')

  print("Writing to {:s}...".format(outfilename))
  with open(outfilename, "w") as outfile:
    for line in gplpal:
      outfile.write(line + '\n')

  print("Done!")
elif args.to_bl_txt != None:
  blpal = BLPalette.BLPalette()
  gplpal = GPLPalette.GPLPalette()

  outfilename = args.to_bl_txt[:args.to_bl_txt.rindex('.')] + '.txt'
  extrafilename = args.to_bl_txt + extraExt
  print("Reading {:s}...".format(args.to_bl_txt))
  with open(args.to_bl_txt, "r") as infile:
    for line in infile:
      gplpal.add_line(line)

  print("Converting data structures...")
  for color in gplpal.palette:
    blpal.add_entry(BLPalette.BLColor.fromColor(color))

  if not args.no_restore_extra:
    print("Reading extra data from {:s}...".format(extrafilename))
    extra = ""
    with open(extrafilename, 'r') as extrafile:
      extra = extrafile.read()
    extra = extra.split()
    alphacount = int(extra[0])
    alphastart = 1
    alphaend = 1 + alphacount
    divcount = int(extra[alphaend])
    divlinesstart = 1 + alphacount + 1
    divlinesend = 1 + alphacount + 1 + divcount
    divnamesstart = 1 + alphacount + 1 + divcount
    divnamesend = 1 + alphacount + 1 + divcount + divcount
    alphas = extra[alphastart:alphaend]
    divlines = extra[divlinesstart:divlinesend]
    divnames = extra[divnamesstart:divnamesend]
    if not args.no_restore_alphas:
      for alpha in enumerate(alphas):
        blpal.palette[alpha[0]].a = int(alpha[1])
    if not args.no_restore_divs:
      for i in range(0, divcount):
        blpal.add_div(int(divlines[i]), divnames[i])

  print("Writing to {:s}...".format(outfilename))
  with open(outfilename, "w") as outfile:
    for line in blpal:
      outfile.write(line + '\n')

  print("Done!")
else:
  print("No mode given.")
  argparser.print_usage()


import datetime
import os
import re

import pyspark.sql as psql

from seldonite import collect, sources, run

def clean_date(txt):
    try:
        if txt.startswith('java.util.GregorianCalendar'):
            year_match = re.search('(?<=,YEAR=)\d+', txt)
            month_match = re.search('(?<=,MONTH=)\d+', txt)
            day_match = re.search('(?<=,DAY_OF_MONTH=)\d+', txt)

            if (year_match and month_match and day_match):
                year = int(year_match.group(0))
                month = int(month_match.group(0)) + 1
                day = int(day_match.group(0))
                return datetime.date(year, month, day)
            else:
                raise Exception("Unrecognised date format")
        elif txt.startswith('{"$date":'):
            match = re.search('[0-9]+', txt)
            if match:
                return datetime.date.fromtimestamp(int(match.group(0)[:-3]))
            else:
                raise Exception("Unrecognised date format")
        else:
            raise Exception("Unrecognised date format")
    except Exception as e:
        raise Exception(f"Received error: {str(e)}, for text: {txt}")

def main():
    master_url = 'k8s://https://10.140.16.25:6443'
    db_connection_string = os.environ['MONGO_CONNECTION_STRING']

    spark_conf = {
        'spark.kubernetes.authenticate.driver.serviceAccountName': 'ben-dev',
        'spark.kubernetes.driver.pod.name': 'seldonite-driver',
        'spark.driver.host': 'seldonite-driver',
        'spark.driver.port': '7078'
    }

    db_name = 'political_events'
    db_table_in = 'reuters_news'
    db_table_out = 'reuters_news_2'

    mongo_source = sources.news.MongoDB(db_connection_string, db_name, db_table_in, partition_size_mb=64)

    collector = collect.Collector(mongo_source)

    clean_date_udf = psql.functions.udf(clean_date, returnType=psql.types.DateType())
    collector.apply_udf(clean_date_udf, 'publish_date')

    runner = run.Runner(collector, master_url=master_url, num_executors=11, executor_cores=4, executor_memory='48g', driver_memory='64g', spark_conf=spark_conf)
    runner.send_to_database(db_connection_string, db_name, db_table_out)

if __name__ == '__main__':
    main()


import os

import netCDF4
import pytest
import matplotlib.pyplot as plt

import config
from weatherpy import maps
from weatherpy.radar.nexradl2 import Nexrad2Plotter

RADAR_FILE = 'Level2_KGLD_20170713_0200.nc'

radar_dataset = None

@pytest.fixture(scope='module', autouse=True)
def lazyloaddataset():
    print('loading...')
    global radar_dataset
    radar_dataset = netCDF4.Dataset(os.sep.join([config.TEST_DATA_DIR, RADAR_FILE]))
    yield
    print('closing...')
    radar_dataset.close()

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_reflectivity():
    fig = plt.figure()
    _run_basic_test_with('Reflectivity')
    return fig

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_velocity():
    fig = plt.figure()
    _run_basic_test_with('RadialVelocity')
    return fig

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_differential_reflectivity():
    fig = plt.figure()
    _run_basic_test_with('DifferentialReflectivity')
    return fig

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_correlation_coefficient():
    fig = plt.figure()
    _run_basic_test_with('CorrelationCoefficient')
    return fig

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_reflectivity_with_limiting_range_ring():
    fig = plt.figure()
    plotter = Nexrad2Plotter(radar_dataset)
    orig_map = plotter.default_map()
    map_to_use = maps.LargeScaleMap(orig_map.crs)
    map_to_use.extent = orig_map.extent

    mapper, _ = plotter.make_plot(mapper=map_to_use)
    mapper.draw_default()
    plotter.range_ring(mapper)
    return fig

@pytest.mark.mpl_image_compare(tolerance=20)
def test_plot_reflectivity_with_nonlimiting_range_ring():
    fig = plt.figure()
    plotter = Nexrad2Plotter(radar_dataset)
    orig_map = plotter.default_map()
    map_to_use = maps.LargeScaleMap(orig_map.crs)
    map_to_use.extent = orig_map.extent

    mapper, _ = plotter.make_plot(mapper=map_to_use)
    mapper.draw_default()
    plotter.range_ring(mapper, limit=False, fit_to_ring=False)
    return fig

def _run_basic_test_with(radartype=None, hires=True, sweep=0):
    plotter = Nexrad2Plotter(radar_dataset)
    orig_map = plotter.default_map()
    map_to_use = maps.LargeScaleMap(orig_map.crs)
    map_to_use.extent = orig_map.extent

    plotter.set_radar(radartype, hires, sweep)
    mapper, _ = plotter.make_plot(mapper=map_to_use)
    mapper.draw_default()


from aiogram import types, Dispatcher
from start.create_bot import bot, dispetcher, db

@dispetcher.message_handler(commands = ['send_message'])
async def send_message_all_user(message: types.Message):
    users = await db.get_all_user()
    for user in users:
        await bot.send_message(user[0], message.text[13:])

def register_client_handlers(dispetcher: Dispatcher):
    dispetcher.register_message_handler(send_message_all_user, commands = ['send_message'])


from patchman.models import Plate,Brand, PlateLog, DBSession
from formencode import validators
from formencode.schema import Schema
from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import render_to_response
from pyramid.view import view_config
from pyramid_uniform import Form, FormRenderer
from sqlalchemy.exc import IntegrityError
from webhelpers import paginate
from webhelpers.paginate import Page
import logging
import transaction

log = logging.getLogger(__name__)

class PlateForm(Schema):
    """ plate form schema for validation
        TODO: DRY code, sqlalchemy model validation instead?
    """
    filter_extra_fields = True
    allow_extra_fields = True
    code = validators.String(not_empty=True)
    brand_id = validators.Int()
    active = validators.Bool()
    notes = validators.String()

@view_config(route_name="plate_list")
def list(request):
    """plates list """
    search = request.params.get("search", "")

    sort= "code"
    if request.GET.get("sort") and request.GET.get("sort") in ["code", "brand"]:
        sort = request.GET.get("sort")
    if sort == "brand":
        sort = "brand.name"

    direction = "asc"
    if request.GET.get("direction") and request.GET.get("direction") in ["asc", "desc"]:
        direction = request.GET.get("direction")

    # db query
    dbsession = DBSession()
    query = dbsession.query(Plate).outerjoin(Brand).\
        filter(Plate.code.like(search + "%")).\
                   order_by(sort + " " + direction)

    # paginate
    page_url = paginate.PageURL_WebOb(request)
    plates = Page(query,
                     page=int(request.params.get("page", 1)),
                     items_per_page=10,
                     url=page_url)

    if "partial" in request.params:
        # Render the partial list page
        return render_to_response("plate/listPartial.html",
                                  {"plates": plates},
                                  request=request)
    else:
        # Render the full list page
        return render_to_response("plate/list.html",
                                  {"plates": plates},
                                  request=request)

@view_config(route_name="plate_search")
def search(request):
    """plates list searching """
    sort = request.GET.get("sort") if request.GET.get("sort") else "code"
    direction = "desc" if request.GET.get("direction") == "asc" else "asc"
    query = {"sort": sort, "direction": direction}

    return HTTPFound(location = request.route_url("plate_list", _query=query))

@view_config(route_name="plate_new", renderer="plate/new.html", permission="add")
def new(request):
    """new plate """
    brands = get_brands()

    form = Form(request, schema=PlateForm)
    if "form_submitted" in request.POST and form.validate():
        dbsession = DBSession()
        plate = form.bind(Plate())
        existing = dbsession.query(Plate).filter_by(code=plate.code).count()
        if not existing:
            dbsession.add(plate)
            request.session.flash("warning;Nueva Patente guardada!")
        else:
            request.session.flash("error; Ya existe la patente!")
        return HTTPFound(location = request.route_url("plate_list"))

    return dict(form=FormRenderer(form),
                brands=brands,
                action_url=request.route_url("plate_new"))

@view_config(route_name="plate_edit", renderer="plate/edit.html", permission="edit")
def edit(request):
    """plate edit """
    id = request.matchdict['id']
    dbsession = DBSession()
    plate = dbsession.query(Plate).filter_by(id=id).first()
    if plate is None:
        request.session.flash("error;Patente no encontrada!")
        return HTTPFound(location=request.route_url("plate_list"))

    brands = get_brands()

    form = Form(request, schema=PlateForm, obj=plate)
    if "form_submitted" in request.POST and form.validate():
        form.bind(plate)
        dbsession.add(plate)
        request.session.flash("warning;Se guardo la patente!")
        return HTTPFound(location = request.route_url("plate_list"))

    action_url = request.route_url("plate_edit", id=id)
    return dict(form=FormRenderer(form),
                brands=brands,
                action_url=action_url)

@view_config(route_name="plate_delete", permission="delete")
def delete(request):
    """plate delete """
    id = request.matchdict['id']
    dbsession = DBSession()
    plate = dbsession.query(Plate).filter_by(id=id).first()
    if plate is None:
        request.session.flash("error;Patente no encontrada!")
        return HTTPFound(location=request.route_url("plate_list"))

    try:
        transaction.begin()
        dbsession.delete(plate);
        transaction.commit()
        request.session.flash("warning;Se elimino la patente!")
    except IntegrityError:
        # delete error
        transaction.abort()
        request.session.flash("error;La patente no se pudo eliminar!")

    return HTTPFound(location=request.route_url("plate_list"))

def get_brands():
    """Gets all brands with id, name value pairs """
    dbsession = DBSession()
    brands_q = dbsession.query(Brand).order_by(Brand.name)
    brands = [(brand.id, brand.name) for brand in brands_q.all()]

    return brands


import cv2
import json
import numpy as np

class Parameters:
    """
    Parameters used for transferring between camera view and Bird Eye view,
    and between Bird Eye view and lat / lon
    List of Parameters:

        unwarp_M:       The matrix used for unwarping (from Camera view to Bird Eye View)
        unwarp_Minv:    Inverted unwarp_M (from Bird Eye to Camera View)
        lonA, lonB:     longitude = lonA * x + lonB, where x - pixel coordinate
        latA, latB:     latitude = latA * y + latB, where y - pixel coordinate

    This class contains also methods that allow to load data from json file and generate the necessary
    parameters

    """

    def __init__(self):
        self.unwarp_M = None # The matrix used for unwarping (from Camera view to Bird Eye View)
        self.unwarp_Minv = None # Inverted unwarp_M (from Bird Eye to Camera View)
        self.lonA, self.lonB = None, None # longitude = lonA * x + lonB, where x - pixel coordinate
        self.latA, self.latB = None, None # latitude = latA * y + latB, where y - pixel coordinate

        self.elevation = None # Elevation of an intersection

        self.lanes_mask = None

    def __generate_unwarp_matrices(self, cameraPoints, birdEyePoints):
        """
        Generates the matrices responsible for switching between Camera and Bird Eye views.
        Arguments:
            cameraPoints  - list of lists of pixel coordinates from the Camera Image
                            [ [x1, y1], [x2, y2], [x3, y3], [x4, y4] ]
            birdEyePoints - list of lists of pixel coordinates from the Bird Eye Image
                            [ [x1, y1], [x2, y2], [x3, y3], [x4, y4] ]
        """

        # Check data

        assert isinstance(cameraPoints, (list, tuple)), (
            'cameraPoints should be a list or a tuple. It is a {}'.format(type(cameraPoints))
        )
        assert isinstance(birdEyePoints, (list, tuple)), (
            'birdEyePoints should be a list or a tuple. It is a {}'.format(type(birdEyePoints))
        )
        assert len(cameraPoints) == 4, (
            'Len of cameraPoints should be 4. It is {}'.format(len(cameraPoints))
        )
        assert len(birdEyePoints) == 4, (
            'Len of birdEyePoints should be 4. It is {}'.format(len(birdEyePoints))
        )
        for i in range(4):
            assert isinstance(cameraPoints[i], (list, tuple)), (
                'cameraPoints[{}] should be a list or a tuple. It is a {}'
                    .format(i, type(cameraPoints[i]))
            )
            assert isinstance(birdEyePoints[i], (list, tuple)), (
                'birdEyePoints[{}] should be a list or a tuple. It is a {}'
                    .format(i, type(birdEyePoints[i]))
            )
            assert len(cameraPoints[i]) == 2, (
                'Len of cameraPoints[{}] should be 2. It is {}'.format(i, len(cameraPoints))
            )
            assert len(birdEyePoints[i]) == 2, (
                'Len of birdEyePoints[{}] should be 2. It is {}'.format(i, len(birdEyePoints))
            )

        cameraPoints = np.float32(cameraPoints)
        birdEyePoints = np.float32(birdEyePoints)

        self.unwarp_M = cv2.getPerspectiveTransform(cameraPoints, birdEyePoints)
        self.unwarp_Minv = cv2.getPerspectiveTransform(birdEyePoints, cameraPoints)

    def __generate_latlon_coefs(self, birdEyeCoordinates, latLonCoordinates):
        """
        Generates the coeffitiens responsible for going from pixels to latitudes, longitudes.
        Arguments:
            birdEyeCoordinates -    list of lists of coordinates from the Bird Eye Image
                                    [ [x1, y1], [x2, y2] ]
            latLonCoordinates -     list of lists of longitudes and latitudes corresponding with
                                    points stored in birdEyeCoordinates
                                    [ [lon1, lat1], [lon2, lat2] ]
        """

        # Check data

        assert isinstance(birdEyeCoordinates, (list, tuple)), (
            'birdEyeCoordinates should be a list or tuple. It is a {}'.format(type(birdEyeCoordinates))
        )
        assert isinstance(latLonCoordinates, (list, tuple)), (
            'latLonCoordinates should be a list or tuple. It is a {}'.format(type(latLonCoordinates))
        )
        assert len(birdEyeCoordinates) == 2, (
            'Len of birdEyeCoordinates should be 2. It is {}'.format(len(birdEyeCoordinates))
        )
        assert len(latLonCoordinates) == 2, (
            'Len of latLonCoordinates should be 2. It is {}'.format(len(latLonCoordinates))
        )
        for i in range(2):
            assert isinstance(birdEyeCoordinates[i], (list, tuple)), (
                'birdEyeCoordinates[{}] should be a list or tuple. It is a {}'
                    .format(i, type(birdEyeCoordinates[i]))
            )
            assert isinstance(latLonCoordinates[i], (list, tuple)), (
                'latLonCoordinates[{}] should be a list or tuple. It is a {}'
                    .format(i, type(latLonCoordinates[i]))
            )
            assert len(birdEyeCoordinates[i]) == 2, (
                'Len of birdEyeCoordinates[{}] should be 2. It is {}'.format(i, len(birdEyeCoordinates))
            )
            assert len(latLonCoordinates) == 2, (
                'Len of latLonCoordinates[{}] should be 2. It is {}'.format(i, len(latLonCoordinates))
            )

        x1, y1 = birdEyeCoordinates[0]
        x2, y2 = birdEyeCoordinates[1]
        lon1, lat1 = latLonCoordinates[0]
        lon2, lat2 = latLonCoordinates[1]

        self.lonA = (lon1 - lon2) / (x1 - x2)
        self.lonB = lon1 - x1 * self.lonA

        self.latA = (lat1 - lat2) / (y1 - y2)
        self.latB = lat1 - y1 * self.latA

    def generateParameters(self, jsonfile):
        """
        Computes and sets parameters based on the given json file
        Arguments:
            jsonfile - path to the file
        """

        with open(jsonfile) as f:
            data = json.load(f)
        self.__generate_unwarp_matrices(data['cameraPoints'], data['birdEyePoints'])
        self.__generate_latlon_coefs(data['birdEyeCoordinates'], data['latLonCoordinates'])

        if 'elevation' in data:
            self.elevation = data['elevation']

        if 'lanes_mask' in data:
            self.lanes_mask = data['lanes_mask']

    def birdEye2Geocoordinates(self, x, y):
        """
        Computes longitude and latitude given the image point coordinates
        Arguments:
            x, y - point coordinates (bird eye view)
        Returns:
            longitude, latitude (float, float) - geocoordinates
        """
        longitude = self.lonA * x + self.lonB
        latitude = self.latA * y + self.latB
        return longitude, latitude

    def geocoordinates2BirdEye(self, longitude, latitude):
        """
        Computes birdeye view x and y given the geocoordinates
        Arguments:
            longitude, latitude - geocoordinates
        Returns:
            x, y (int, int) - point coordinates
        """
        x = (longitude - self.lonB) / self.lonA
        y = (latitude - self.latB) / self.latA

        return np.round(x).astype(int), np.round(y).astype(int)

    def camera2BirdEye(self, x, y):
        src = np.array([[[x, y]]], dtype='float32')
        trans = cv2.perspectiveTransform(src, self.unwarp_M)
        return trans[0][0]

    def camera2Geocoordinates(self, x, y):
        birdEyeX, birdEyeY = self.camera2BirdEye(x, y)
        return self.birdEye2Geocoordinates(birdEyeX, birdEyeY)


import math
from ultralytics import YOLO
import cv2
from gtts import gTTS
import os

cap = cv2.VideoCapture(0)

frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
out = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10, (frame_width, frame_height))

model = YOLO('yolov8n.pt')

classNames = ["person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", "boat",
              "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat",
              "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella",
              "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
              "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup",
              "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli",
              "carrot", "hot dog", "pizza", "donut", "cake", "chair", "sofa", "pottedplant", "bed",
              "diningtable", "toilet", "tvmonitor", "laptop", "mouse", "remote", "keyboard", "cell phone",
              "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors",
              "teddy bear", "hair drier", "toothbrush"
              ]

box_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]

detected_classes = set()

while True:
    success, img = cap.read()
    results = model(img, stream=True)

    for r in results:
        boxes = r.boxes
        for i, box in enumerate(boxes):
            x1, y1, x2, y2 = box.xyxy[0]
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            color = box_colors[i % len(box_colors)]
            cv2.rectangle(img, (x1, y1), (x2, y2), color, 3)

            conf = math.ceil((box.conf[0] * 100)) / 100
            cls = int(box.cls[0])
            class_name = classNames[cls]
            label = f'{class_name}{conf}'
            t_size = cv2.getTextSize(label, 0, fontScale=1, thickness=2)[0]
            c2 = x1 + t_size[0], y1 - t_size[1] - 3
            cv2.rectangle(img, (x1, y1), c2, color, -1, cv2.LINE_AA)
            cv2.putText(img, label, (x1, y1 - 2), 0, 1, [255, 255, 255], thickness=1, lineType=cv2.LINE_AA)

            if class_name not in detected_classes:
                tts = gTTS(text=class_name, lang='en')
                tts.save("object_audio.mp3")
                os.system("mpg321 object_audio.mp3")
                detected_classes.add(class_name)

    out.write(img)
    cv2.imshow("Image", img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

out.release()
cap.release()
cv2.destroyAllWindows()