import torch
from torch.utils.data.dataset import Dataset
import pickle
import numpy as np
from torchvision import transforms
from PIL import Image
import scipy
import cv2
import glob
import os
import matplotlib.pyplot as plt
from utils import sample_n, get_freespace_points, get_occupancy_points, get_mask_points

class RenderPeopleDataset(Dataset):
	def __init__(self, dataroot, num_views=1,usage='train',num_samples=5000,scale=0.1,random_multiview=False):

		# Path setup
		self.root = dataroot
		self.RENDER = os.path.join(self.root, '0','color')
		self.MASK = os.path.join(self.root, '0','gr_mask')
		self.PARAM = os.path.join(self.root, '0','camera')
		self.BBOX = os.path.join(self.root, '0','boundingBox')
		self.DEPTH = os.path.join(self.root, '0', 'complete_depth')
		self.OBJ = os.path.join(self.root.replace('train_512_RenderPeople_all_sparse','meshes'))

		self.bb_min = np.array([0, -1, -0.4])
		self.bb_max = np.array([3.6, 3.0, 2.6])

		self.is_train = (usage == 'train')

		self.num_views =num_views

		self.num_samples = num_samples

		self.to_tensor=transforms.ToTensor()

		self.scale = scale

		views = sorted(glob.glob(os.path.join(self.root,'*')))
		self.views=[]
		for v in views:
			_,num = os.path.split(v)
			if int(num)==56 or int(num)==83:
				continue
			else:
				self.views.append(num)

		self.subjects = self.get_subjects()
		self.random_multiview = random_multiview

	def get_subjects(self):
		#all_subjects = glob.glob(os.path.join(self.root,'0/color/*.png'))
		#all_subjects = sorted([i.replace('.png','').replace(os.path.join(self.root,'0/color/'),'') for i in all_subjects])
		all_subjects = ['%i'%i for i in range(0,1)]

		#var_subjects = ['%i'%i for i in range(330,345)]
		var_subjects=[]
		if len(var_subjects) == 0:
			return all_subjects

		if self.is_train:
			return sorted(list(set(all_subjects) - set(var_subjects)))
		else:
			return sorted(list(var_subjects))

	def calculate_scaling_and_origin_rp(self,bbox, height):
		"""
		Calculates the origin and scaling of an image for the RenderPeople dataset

		Parameters
		---------
		bbox - the bounding box coords: ndarray of shape (4,2)
		height - the height of the final image: int
		"""
		bb1_t=bbox-1
		bbc1_t=bb1_t[2:4,0:3]
		origin = np.multiply([bb1_t[1,0]-bbc1_t[1,0],bb1_t[0,0]-bbc1_t[0,0]],2)
		squareSize = np.maximum(bb1_t[0,1]-bb1_t[0,0]+1,bb1_t[1,1]-bb1_t[1,0]+1)
		scaling = np.multiply(np.true_divide(squareSize,height),2)

		return origin, scaling

	def get_render(self, subject, num_views, yid=0, pid=0, random_sample=False):
		'''
		Return the render data
		:param subject: subject name
		:param num_views: how many views to return
		:param view_id: the first view_id. If None, select a random one.
		:return:
			'img': [num_views, C, W, H] images
			'calib': [num_views, 4, 4] calibration matrix
			'extrinsic': [num_views, 4, 4] extrinsic matrix
			'mask': [num_views, 1, W, H] masks
		'''

		# The ids are an even distribution of num_views around view_id
		view_ids = [self.views[(yid + len(self.views) // num_views * offset) % len(self.views)] for offset in range(num_views)]

		if random_sample:
			view_ids = np.random.choice(self.views, num_views, replace=False)

		render_list = []
		mask_list = []
		K_list = []
		R_list = []
		C_list = []
		origin_list = []
		scale_list = []
		depth_list = []
		p_list = []
		p_freespace_list = []
		p_occupancy_list = []
		p_mask_list = []

		for vid in view_ids:
			K_path = os.path.join(self.PARAM.replace('/0/','/%s/'%(vid)),'K.txt')
			R_path = os.path.join(self.PARAM.replace('/0/','/%s/'%(vid)),'R.txt')
			C_path = os.path.join(self.PARAM.replace('/0/','/%s/'%(vid)),'cen.txt')
			render_path = os.path.join(self.RENDER.replace('/0/','/%s/'%(vid)), subject+'.png')
			mask_path = os.path.join(self.MASK.replace('/0/','/%s/'%(vid)), subject+'.png')
			bbox_path = os.path.join(self.BBOX.replace('/0/','/%s/'%(vid)),subject+'.txt')
			gt_depth_path = os.path.join(self.DEPTH.replace('/0/','/%s/'%(vid)), subject+'.txt')

			# loading calibration data
			K = torch.tensor(np.loadtxt(K_path,delimiter=',',dtype='f')).float()
			R = torch.tensor(np.loadtxt(R_path,delimiter=',',dtype='f')).float()
			center = np.loadtxt(C_path,delimiter=',',dtype='f')
			center = torch.tensor(center[...,np.newaxis]).float()
			bbox = np.loadtxt(bbox_path,delimiter=',',dtype='f')
			origin,scaling = self.calculate_scaling_and_origin_rp(bbox,512)
			origin = torch.tensor(origin[np.newaxis,...]).float()
			scaling = torch.tensor(scaling[np.newaxis,np.newaxis,...]).float()
			gt_depth = torch.tensor(np.genfromtxt(gt_depth_path,delimiter=',',dtype='f')).float()

			valid_depth=self.to_tensor(np.where(gt_depth>=1.0, np.ones_like(gt_depth),np.zeros_like(gt_depth))*np.where(gt_depth<=8.0, np.ones_like(gt_depth),np.zeros_like(gt_depth)))

			mask = Image.open(mask_path).convert('L')
			render = Image.open(render_path).convert('RGB')

			mask = transforms.ToTensor()(mask).float()
			mask = torch.where(mask>0,torch.ones_like(mask),torch.zeros_like(mask))
			mask = mask*valid_depth
			mask_list.append(mask)

			render = self.to_tensor(render).float()
			render = mask.expand_as(render) * render

			p,p_freespace,p_occupancy,p_mask = self.sample(mask, K, R, center, origin, scaling, depth_img=gt_depth)

			render_list.append(render)
			K_list.append(K)
			R_list.append(R)
			C_list.append(center)
			origin_list.append(origin)
			scale_list.append(scaling)
			depth_list.append(gt_depth)
			p_list.append(p)
			p_freespace_list.append(p_freespace)
			p_occupancy_list.append(p_occupancy)
			p_mask_list.append(p_mask)

			#verts_tensor = torch.Tensor(self.mesh_dic[subject].vertices).T
			#faces = torch.Tensor(self.mesh_dic[subject].faces).T

			'''xyz_tensor = perspective(verts_tensor[np.newaxis,...], calib[np.newaxis,...], transforms_tensor[np.newaxis,...])
			uv = xyz_tensor[:, :2, :]
			color = index(render[np.newaxis,...], uv).detach().cpu().numpy()[0].T
			color = color * 0.5 + 0.5
			save_obj_mesh_with_color('/media/mulha024/i/PIFu-master/test_mesh.obj', verts_tensor.T, faces.T, color)
			print("Saved the mesh")'''

		return {
			'color': torch.stack(render_list, dim=0),
			'K': torch.stack(K_list, dim=0),
			'R': torch.stack(R_list, dim=0),
			'C': torch.stack(C_list, dim=0),
			'origin': torch.stack(origin_list, dim=0),
			'scaling': torch.stack(scale_list, dim=0),
			'mask': torch.stack(mask_list, dim=0),
			'gt_depth': torch.stack(depth_list, dim=0),
			'p': torch.stack(p_list, dim=0),
			'p_freespace': torch.stack(p_freespace_list, dim=0),
			'p_occupancy': torch.stack(p_occupancy_list, dim=0),
			'p_mask': torch.stack(p_mask_list, dim=0)
			}

	def sample(self, mask, K, R, C, origin, scaling, depth_img=None):
		per_sample = self.num_samples//4
		p = sample_n(mask.unsqueeze(0), per_sample*3) # N,2
		p_freespace=None
		p_occupancy=None

		if depth_img is not None:
			p_freespace = get_freespace_points(p[:,:per_sample,:], K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),depth_img=depth_img.unsqueeze(0).unsqueeze(1), scale=self.scale)
			p_occupancy = get_occupancy_points(p[:,per_sample:per_sample*2,:], K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),depth_img=depth_img.unsqueeze(0).unsqueeze(1), scale=self.scale)
		else:
			p_freespace = get_freespace_points(p[:,:per_sample,:], K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),scale=self.scale)
			p_occupancy = get_occupancy_points(p[:,per_sample:per_sample*2,:], K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0), scale=self.scale)

		p_mask = get_mask_points(mask.unsqueeze(0),K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0), origin.unsqueeze(0), scaling.unsqueeze(0), per_sample, self.bb_min, self.bb_max)
		p=p[:,per_sample*2:,:]

		return p[0], p_freespace[0], p_occupancy[0], p_mask[0]

	def __len__(self):
		return len(self.subjects) * len(self.views)

	def get_item(self, index):
		# In case of a missing file or IO error, switch to a random sample instead
		# try:
		sid = index % len(self.subjects)
		tmp = index // len(self.subjects)
		yid = tmp % len(self.views)
		pid = tmp // len(self.views)

		# name of the subject 'rp_xxxx_xxx'
		subject = self.subjects[sid]
		res = {
			'name': subject,
			'sid': sid,
			'yid': yid,
			'pid': pid,
			'bb_min': self.bb_min,
			'bb_max': self.bb_max,
		}

		render_data = self.get_render(subject, num_views=self.num_views, yid=yid, pid=pid, random_sample=self.random_multiview)
		res.update(render_data)

		#sample_data = self.sample(res['mask'], res['K'], res['R'], res['C'], res['origin'], res['scaling'], depth_img=res['gt_depth'])
		#res.update(sample_data)

		return res

	def __getitem__(self, index):
		return self.get_item(index)

class RenderPeopleDataset_temp(Dataset):
	def __init__(self, usage='train', pickle_file='./data/RenderPeople_dvr.pkl',skip_every_n_image=1, num_samples=5000, scale=0.1):

		self.to_tensor=transforms.ToTensor()
		self.num_samples = num_samples
		self.scale = scale
		self.bb_min = torch.FloatTensor([0,-1,-0.5])
		self.bb_max = torch.FloatTensor([3.6, 3,2.6])

		with open(pickle_file, 'rb') as f:
			self.data_info = pickle.load(f)[usage]
			self.idx = [i for i in range(0, len(self.data_info[0][0]), skip_every_n_image)]
			self.data_len = len(self.idx)

	def sample(self, mask, K, R, C, origin, scaling, depth_img=None):
		per_sample = self.num_samples//4
		p = sample_n(mask, per_sample*3) # N,2
		p_freespace=None
		p_occupancy=None

		if depth_img is not None:
			p_freespace = get_freespace_points(p[:per_sample,:].unsqueeze(0), K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),depth_img=depth_img.unsqueeze(0), scale=self.scale)
			p_occupancy = get_occupancy_points(p[per_sample:per_sample*2,:].unsqueeze(0), K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),depth_img=depth_img.unsqueeze(0), scale=self.scale)
		else:
			p_freespace = get_freespace_points(p[:per_sample,:].unsqueeze(0), K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0),scale=self.scale)
			p_occupancy = get_occupancy_points(p[per_sample:per_sample*2,:].unsqueeze(0), K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0),origin.unsqueeze(0),scaling.unsqueeze(0), scale=self.scale)

		p_mask = get_mask_points(mask.unsqueeze(0),K.unsqueeze(0),R.unsqueeze(0),C.unsqueeze(0), origin.unsqueeze(0), scaling.unsqueeze(0), per_sample, self.bb_min, self.bb_max)
		p=p[per_sample*2:,:]

		return p, p_freespace[0],p_occupancy[0],p_mask[0]

	def __getitem__(self, index):
		output = {}

		color = self.to_tensor(Image.open(self.data_info[0][0][index]))

		mask = cv2.imread(self.data_info[0][2][index])
		K = np.loadtxt(self.data_info[0][4][index],delimiter=',')
		R = np.loadtxt(self.data_info[0][5][index],delimiter=',')
		C = np.loadtxt(self.data_info[0][6][index],delimiter=',')

		K=self.to_tensor(np.asarray(K,dtype='f'))
		R=self.to_tensor(np.asarray(R,dtype='f'))
		C=self.to_tensor(np.asarray(C[...,np.newaxis],dtype='f'))

		gt_depth = np.genfromtxt(self.data_info[0][7][index],delimiter=',')

		#Build the normal from the 3 files
		gt_norm_1_0 = np.genfromtxt(self.data_info[0][8][index],delimiter=',')
		gt_norm_2_0 = np.genfromtxt(self.data_info[0][9][index],delimiter=',')
		gt_norm_3_0 = np.genfromtxt(self.data_info[0][10][index],delimiter=',')
		gt_norm = np.concatenate((gt_norm_1_0[...,np.newaxis], gt_norm_2_0[...,np.newaxis], gt_norm_3_0[...,np.newaxis]), axis=2)
		gt_norm = self.to_tensor(np.asarray(gt_norm,dtype='f'))

		#Refine the mask with valid depth
		valid_depth=np.where(gt_depth>=1.0, np.ones_like(gt_depth),np.zeros_like(gt_depth))*np.where(gt_depth<=8.0, np.ones_like(gt_depth),np.zeros_like(gt_depth))
		mask_refined = self.to_tensor(np.where(mask>100,np.ones_like(mask),np.zeros_like(mask))*valid_depth[...,np.newaxis])
		mask_refined_1D = mask_refined[0,...]

		gt_depth = self.to_tensor(np.asarray(gt_depth,dtype='f'))

		origin = self.data_info[0][13][index]
		origin = self.to_tensor(np.asarray(origin[np.newaxis,...],dtype='f'))

		scaling = np.asarray([self.data_info[0][14][index]],dtype='f')
		scaling=self.to_tensor(scaling[np.newaxis,...])

		#Refine color image, depth image and normal image using the refined mask
		color=torch.where(mask_refined>0,color,torch.ones_like(color))
		gt_depth=gt_depth*mask_refined_1D[np.newaxis,...]
		gt_norm=gt_norm*mask_refined

		p, p_freespace, p_occupancy, p_mask = self.sample(mask_refined, K, R, C, origin, scaling, depth_img=gt_depth)

		output['color'] = color

		output['mask'] = mask_refined

		output['K'] = K

		output['R'] = R

		output['C'] = C

		output['gt_depth'] = gt_depth

		output['gt_normal'] = gt_norm

		output['origin'] = origin

		output['scaling'] = scaling

		output['p'] = p

		output['p_freespace'] = p_freespace

		output['p_occupancy'] = p_occupancy

		output['p_mask'] = p_mask

		output['bb_min'] = self.bb_min

		output['bb_max'] = self.bb_max

		return output

	def __len__(self):
		return self.data_len

class HUMBIDataset(Dataset):
	def __init__(self, usage='train', pickle_file='./data/HUMBI_dvr.pkl',skip_every_n_image=1):

		self.to_tensor=transforms.ToTensor()

		with open(pickle_file, 'rb') as f:
			self.data_info = pickle.load(f)[usage]
			self.idx = [i for i in range(0, len(self.data_info[0][0]), skip_every_n_image)]
			self.data_len = len(self.idx)

	def __getitem__(self, index):
		output = {}

		color_1 = self.to_tensor(Image.open(self.data_info[0][0][index]))
		color_2 = self.to_tensor(Image.open(self.data_info[1][0][index]))

		dp_1 = self.to_tensor(Image.open(self.data_info[0][1][index]))
		dp_2 = self.to_tensor(Image.open(self.data_info[1][1][index]))

		mask_1 =self.to_tensor(Image.open(self.data_info[0][2][index]))
		mask_2 = self.to_tensor(Image.open(self.data_info[1][2][index]))

		mask_1=torch.where(mask_1[-1,...]>0, torch.ones_like(mask_1[-1,...]),torch.zeros_like(mask_1[-1,...]))
		mask_2=torch.where(mask_2[-1,...]>0, torch.ones_like(mask_2[-1,...]),torch.zeros_like(mask_2[-1,...]))
		mask_1=mask_1[np.newaxis,...]
		mask_2=mask_2[np.newaxis,...]

		color_1 = color_1*mask_1
		color_2 = color_2*mask_2

		dp_1 = dp_1*mask_1
		dp_2 = dp_2*mask_2

		K = np.loadtxt(self.data_info[0][4][index],delimiter=',')
		R = np.loadtxt(self.data_info[0][5][index],delimiter=',')
		C = np.loadtxt(self.data_info[0][6][index],delimiter=',')

		iuv_1 = self.data_info[1][11][index]
		iuv_2 = self.data_info[1][12][index]

		origin_1 = self.data_info[0][13][index]
		origin_2 = self.data_info[1][13][index]

		scaling_1 = 2*np.asarray([self.data_info[0][14][index]],dtype='f')
		scaling_2 = 2*np.asarray([self.data_info[1][14][index]],dtype='f')

		output['color_1'] = color_1
		output['color_2'] = color_2

		output['dp_1'] = dp_1
		output['dp_2'] = dp_2

		output['mask_1'] = mask_1
		output['mask_2'] = mask_2

		output['K'] = self.to_tensor(np.asarray(K,dtype='f'))

		output['R'] = self.to_tensor(np.asarray(R,dtype='f'))

		output['C'] = self.to_tensor(np.asarray(C[...,np.newaxis],dtype='f'))

		output['origin_1'] = self.to_tensor(np.asarray(origin_1[np.newaxis,...],dtype='f'))
		output['origin_2'] = self.to_tensor(np.asarray(origin_2[np.newaxis,...],dtype='f'))

		output['scaling_1'] = self.to_tensor(scaling_1[np.newaxis,...])
		output['scaling_2'] = self.to_tensor(scaling_2[np.newaxis,...])

		output['iuv_1'] = self.to_tensor(np.asarray(iuv_1,dtype='f'))
		output['iuv_2'] = self.to_tensor(np.asarray(iuv_2,dtype='f'))

		return output

	def __len__(self):
		return self.data_len


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

"""Implementation of DataType."""

import enum

class DataType(enum.Enum):
    """Represents the different data types for a SystemLink tag."""

    UNKNOWN = 0
    """An unknown or invalid data type.

    Not a valid input to API calls, but used to represent tags whose data type isn't
    recognized.
    """

    DOUBLE = 1
    """A 64-bit floating-point tag following the IEEE standard.

    Double tags support collecting aggregate values for the min, max, mean, and count.
    """

    INT32 = 2
    """A 32-bit signed integer tag.

    Int32 tags support collecting aggregate values for the min, max, mean, and count.
    The mean is represented as a double.
    """

    STRING = 3
    """A string tag for arbitrary values.

    String tags support collecting aggregate values for the count.
    """

    BOOLEAN = 4
    """A boolean tag.

    Bool tags support collecting aggregate values for the count.
    """

    UINT64 = 5
    """A 64-bit unsigned integer tag.

    UInt64 tags support collecting aggregate values for the min, max, mean, and count.
    The mean is represented as a double value and will truncate large values.
    """

    DATE_TIME = 6
    """A date and time tag that stores values in UTC ISO 8601 format.

    DateTime tags support collecting aggregate values for the count.
    """

    @property
    def api_name(self) -> str:
        """Web API name of the enum value."""
        return _API_NAME[self]

    @classmethod
    def from_api_name(cls, name: str) -> "DataType":
        return cls(_FROM_API_NAME.get(name, cls.UNKNOWN))

_API_NAME = {
    DataType.UNKNOWN: "UNKNOWN",
    DataType.DOUBLE: "DOUBLE",
    DataType.INT32: "INT",
    DataType.STRING: "STRING",
    DataType.BOOLEAN: "BOOLEAN",
    DataType.UINT64: "U_INT64",
    DataType.DATE_TIME: "DATE_TIME",
}

_FROM_API_NAME = {
    "DOUBLE": DataType.DOUBLE,
    "INT": DataType.INT32,
    "STRING": DataType.STRING,
    "BOOLEAN": DataType.BOOLEAN,
    "U_INT64": DataType.UINT64,
    "DATE_TIME": DataType.DATE_TIME,
}


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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import time
import os.path
import urllib

class PlantPipeline(object):
    def process_item(self, item, spider):
        # today = time.strftime('%Y%m%d',time.localtime())
        fileName = item['plantCategory'] + '.txt'
        with open(fileName,'ab+') as fp:
            fp.write(item['plantName'].encode('utf8') + b'')
            # fp.write('0' + b'\n')
            time.sleep(1)
        return item


import os
import uuid
import json
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
from modelscope.outputs import OutputKeys

def handle_upload(event, context):
    files = event.files
    image = files.get('image')
    if 'image' not in files or image is None:
        return {
            'statusCode': 400,
            'body': 'missing image'
        }

    form = event.form
    name = form.get('name')
    if 'name' not in form or name == '':
        return {
            'statusCode': 400,
            'body': 'missing name'
        }

    image_root_path = '/image_tmp'
    if not os.path.exists(image_root_path):
        os.mkdir(image_root_path)

    prefix = uuid.uuid1()
    image_path = image_root_path + str(prefix) + '-' + name
    with open(image_path, 'wb') as f:
        f.write(image.stream.read())

    img_captioning = pipeline(Tasks.image_captioning, model='damo/ofa_image-caption_coco_distilled_en', model_revision='v1.0.1')
    result = img_captioning(image_path)

    os.remove(image_path)

    return {
        'statusCode': 200,
        'body': result[OutputKeys.CAPTION]
    }

def handle_web(event, context):
    body = json.loads(event.body.decode('utf-8'))
    url = body.get('url')
    if 'url' not in body or url == '':
        return {
            'statusCode': 400,
            'body': 'missing url'
        }

    img_captioning = pipeline(Tasks.image_captioning, model='damo/ofa_image-caption_coco_distilled_en', model_revision='v1.0.1')
    result = img_captioning(url)

    return {
        'statusCode': 200,
        'body': result[OutputKeys.CAPTION]
    }


from dataclasses import asdict, dataclass
from json import dumps, loads
from typing import Optional, Sequence, Union

# This kind of import is needed to avoid Sphinx errors.
import opentelemetry.sdk.metrics._internal
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
from opentelemetry.util.types import Attributes

@dataclass(frozen=True)
class NumberDataPoint:
    """Single data point in a timeseries that describes the time-varying scalar
    value of a metric.
    """

    attributes: Attributes
    start_time_unix_nano: int
    time_unix_nano: int
    value: Union[int, float]

    def to_json(self, indent=4) -> str:
        return dumps(asdict(self), indent=indent)

@dataclass(frozen=True)
class HistogramDataPoint:
    """Single data point in a timeseries that describes the time-varying scalar
    value of a metric.
    """

    attributes: Attributes
    start_time_unix_nano: int
    time_unix_nano: int
    count: int
    sum: Union[int, float]
    bucket_counts: Sequence[int]
    explicit_bounds: Sequence[float]
    min: float
    max: float

    def to_json(self, indent=4) -> str:
        return dumps(asdict(self), indent=indent)

@dataclass(frozen=True)
class Buckets:
    offset: int
    bucket_counts: Sequence[int]

@dataclass(frozen=True)
class ExponentialHistogramDataPoint:
    """Single data point in a timeseries whose boundaries are defined by an
    exponential function. This timeseries describes the time-varying scalar
    value of a metric.
    """

    attributes: Attributes
    start_time_unix_nano: int
    time_unix_nano: int
    count: int
    sum: Union[int, float]
    scale: int
    zero_count: int
    positive: Buckets
    negative: Buckets
    flags: int
    min: float
    max: float

    def to_json(self, indent=4) -> str:
        return dumps(asdict(self), indent=indent)

@dataclass(frozen=True)
class ExponentialHistogram:
    """Represents the type of a metric that is calculated by aggregating as an
    ExponentialHistogram of all reported measurements over a time interval.
    """

    data_points: Sequence[ExponentialHistogramDataPoint]
    aggregation_temporality: (
        "opentelemetry.sdk.metrics.export.AggregationTemporality"
    )

@dataclass(frozen=True)
class Sum:
    """Represents the type of a scalar metric that is calculated as a sum of
    all reported measurements over a time interval."""

    data_points: Sequence[NumberDataPoint]
    aggregation_temporality: (
        "opentelemetry.sdk.metrics.export.AggregationTemporality"
    )
    is_monotonic: bool

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "data_points": [
                    loads(data_point.to_json(indent=indent))
                    for data_point in self.data_points
                ],
                "aggregation_temporality": self.aggregation_temporality,
                "is_monotonic": self.is_monotonic,
            },
            indent=indent,
        )

@dataclass(frozen=True)
class Gauge:
    """Represents the type of a scalar metric that always exports the current
    value for every data point. It should be used for an unknown
    aggregation."""

    data_points: Sequence[NumberDataPoint]

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "data_points": [
                    loads(data_point.to_json(indent=indent))
                    for data_point in self.data_points
                ],
            },
            indent=indent,
        )

@dataclass(frozen=True)
class Histogram:
    """Represents the type of a metric that is calculated by aggregating as a
    histogram of all reported measurements over a time interval."""

    data_points: Sequence[HistogramDataPoint]
    aggregation_temporality: (
        "opentelemetry.sdk.metrics.export.AggregationTemporality"
    )

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "data_points": [
                    loads(data_point.to_json(indent=indent))
                    for data_point in self.data_points
                ],
                "aggregation_temporality": self.aggregation_temporality,
            },
            indent=indent,
        )

DataT = Union[Sum, Gauge, Histogram]
DataPointT = Union[NumberDataPoint, HistogramDataPoint]

@dataclass(frozen=True)
class Metric:
    """Represents a metric point in the OpenTelemetry data model to be
    exported."""

    name: str
    description: Optional[str]
    unit: Optional[str]
    data: DataT

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "name": self.name,
                "description": self.description or "",
                "unit": self.unit or "",
                "data": loads(self.data.to_json(indent=indent)),
            },
            indent=indent,
        )

@dataclass(frozen=True)
class ScopeMetrics:
    """A collection of Metrics produced by a scope"""

    scope: InstrumentationScope
    metrics: Sequence[Metric]
    schema_url: str

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "scope": loads(self.scope.to_json(indent=indent)),
                "metrics": [
                    loads(metric.to_json(indent=indent))
                    for metric in self.metrics
                ],
                "schema_url": self.schema_url,
            },
            indent=indent,
        )

@dataclass(frozen=True)
class ResourceMetrics:
    """A collection of ScopeMetrics from a Resource"""

    resource: Resource
    scope_metrics: Sequence[ScopeMetrics]
    schema_url: str

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "resource": loads(self.resource.to_json(indent=indent)),
                "scope_metrics": [
                    loads(scope_metrics.to_json(indent=indent))
                    for scope_metrics in self.scope_metrics
                ],
                "schema_url": self.schema_url,
            },
            indent=indent,
        )

@dataclass(frozen=True)
class MetricsData:
    """An array of ResourceMetrics"""

    resource_metrics: Sequence[ResourceMetrics]

    def to_json(self, indent=4) -> str:
        return dumps(
            {
                "resource_metrics": [
                    loads(resource_metrics.to_json(indent=indent))
                    for resource_metrics in self.resource_metrics
                ]
            },
            indent=indent,
        )


import pandas as pd

def makeSyllableStenoDF(df, verbose = False):
    """This function will make a dataframe that contains how a syllable is written from English syllable to Steno.
    It expects a dataframe that has been through the MakeColumns for Analysis functions.
    It expects a 'EqualStructureWithSyllables' column.  It will use that subset of data to map Strokes to syllables"""
    if verbose:
        print("Starting makeSyllableStenoDF...")
    if 'EqualStructureWithSyllables' not in df.columns.tolist():
        if verbose:
            print("Expected a 'EqualStructureWithSyllables' column to perform makeSyllableStenoDF.  Returning df as is....")
        return df
    dfSameLengthStrokesAndSyllables = df[df['EqualStructureWithSyllables'] == True]
    dfDifferentLengthStrokesAndSyllables = df[df['EqualStructureWithSyllables'] != True]

    syllablesList =[]
    strokesList = []
    wordList =[]

    for i in range(len(dfSameLengthStrokesAndSyllables)):
        syllables = dfSameLengthStrokesAndSyllables.iloc[i]['syllables']
        strokes = dfSameLengthStrokesAndSyllables.iloc[i]['Strokes']
        word = dfSameLengthStrokesAndSyllables.iloc[i]['Word']
        for syllable in syllables:
            syllablesList.append(syllable) #expects python 3.8 or greater, ordered dictionaries
        for stroke in strokes:
            strokesList.append(stroke)
        for j in range(len(strokes)):
            wordList.append(word)
    return pd.DataFrame({"stroke": strokesList,
                         "syllable":syllablesList,
                         'word':wordList})

class StenoSyllableMapping:
    """This class is going to be the keys for a dictionary that's a list of English syllables and how they're written in Steno"""
    def __int__(self, syllable, stenoList):
        self.stenoList = stenoList
        self.words = words #should be the list of words
        self.syllable = syllable

    def __repr__(self):  #??possibly right??
        return self.syllable

# def flipKeysAndValues(dictParam):
#     returningDict = {}
#     for item in dictParam.items():
#         returningDict

def getNthSyllableResult(targetSyllable : str, syllableDict : dict, rank = 1, returnWords = False,  verbose = False):
    """This function is to be used right after makeSyllableStenoDF.  It will get all the instances of the target syllable
    and return the nth ranked one.  Default is rank 1 which means the most common Steno outline denoting that
    syllable.  If you enter a rank greater than the number of possible options, it returns  the lowest rank one now.  I might
    consider changing that to a paramater to return None instead..."""

    #validation:
    if type(targetSyllable) is str:
        targetSyllable = targetSyllable.lower()
    else:
        if verbose:
            print('targetSyllable, {}, isn\'t a string.  Returning None...'.format(targetSyllable))
        return None
    workingDF = syllableDict.get(targetSyllable, None)  # expecting a dataframe as key if it's in the dict
    if workingDF is None:
        if verbose:
            print("Unable to find {} in the syllableDict, returning None...".format(targetSyllable))
        return None
    elif (type(workingDF) is not pd.DataFrame):
        if verbose:
            print("Expected the return value of syllabledict[{}] to be a dataframe..  Instead it's a {}.  Returning None...".format(
                targetSyllable, str(type(workingDF))
            ))
        return None
    elif "stroke" not in workingDF.columns.tolist():
        if verbose:
            print("Expected 'stroke' column in the syllableDict value(), returning none..." )
        return None

    # meat and potatoes:
    possibleStrokes = set(workingDF['stroke'])

    StrokesCountsDict = {}
    for i in range(len(workingDF)):
        CountingDict.CountingDictAdd(StrokesCountsDict,
                                     workingDF.iloc[i]['stroke'])

    workingDF['count'] = workingDF['stroke'].apply(lambda x: StrokesCountsDict[x])

    #workingDF['count'] = workingDF.apply(lambda x: len(x[x['stroke'] == ])) #I think this makes a count column that should be the count of the instances of that stroke in the dataframe

    possibleCounts = sorted(set(workingDF['count'])) #sorted in ascending order by default, lowest to highest

    if (rank > len(possibleStrokes)):
        if verbose:
            print("The rank you entered, {}, is greater than the amount of possibilities.  There are {} possible options for {}.".format(
              rank, len(possibleStrokes), targetSyllable))
            print("Therefore, we're returning the last choice for {} which is {}".
                  format(targetSyllable,
                         list(set(workingDF[workingDF['count'] == possibleCounts[0] ]['stroke']) )[0] )  )
            #TODO consider returning false here or making it a param option
        return list(set(workingDF[workingDF['count']==possibleCounts[0] ] ['stroke']) )[0]

    elif (rank <= len(possibleCounts) and rank >=1):
        return list(set(workingDF[workingDF['count'] == possibleCounts[rank*-1] ] ['stroke'] ))[0] #fix

    elif rank <1:
        if verbose:
            print("Invalid rank entered, {}.  Enter a rank 1 or higher.  Returning None...".format(rank))
        return None

if __name__ == '__main__':
    import NoahAndChrisTesting, MakeColumnsForAnalysis, BigOuterMerge, FilterBeforeLogic, CountingDict
    testDF = NoahAndChrisTesting.ChrisDF
    testDF = FilterBeforeLogic.filter1(
        BigOuterMerge.BigOuterMerge(testDF), verbose= True )
    testDF = MakeColumnsForAnalysis.doAll(testDF, verbose= True)

    syllableToStenoResultDF = makeSyllableStenoDF(testDF, verbose=True)

    SyllableMappings = {}
    for syllable in set(syllableToStenoResultDF['syllable']):
        SyllableMappings[syllable] = syllableToStenoResultDF[syllableToStenoResultDF['syllable'] == syllable]

    #
    # testCal = getNthSyllableResult('cal', SyllableMappings, verbose=True )
    # print('--------------------')
    # testCal2 = getNthSyllableResult('cal', SyllableMappings, rank=2, verbose=True)
    #
    # print('--------------------')
    # print('--------------------')
    #
    testSyllable = 'est'.lower()
    testal = getNthSyllableResult(testSyllable, SyllableMappings, verbose=True)
    print("{} primarily written as {}".format(testSyllable, testal))
    print('--------------------')
    testal2 = getNthSyllableResult(testSyllable, SyllableMappings, rank=2, verbose=True)
    print("{} secondarily written as {}.".format(testSyllable, testal2))

    quit()


import numpy as np

def bisection(f, a, b, tol):
    '''
    f: function to find the root of
    a: Lower bracket
    b: Upper bracket
    tol: required tolerance
    '''
    bis = (b + a) / 2

    if np.abs(a - b) < tol:
        return bis

    else:
        if np.sign(f(bis)) == np.sign(f(a)):
            return bisection(f, bis, b, tol)
        else:
            return bisection(f, a, bis, tol)

def secant(f, x_1, x_0, tol):
    m = (f(x_1) - f(x_0)) / (x_1 - x_0)
    x_new = (m * x_1 - f(x_1)) / m #Point where secant line crosses zero

    if np.abs(f(x_new)) < tol:
        return x_new

    else:
        return secant(f, x_new, x_1, tol)

def newton(f, f_p, x_0, tol,f_args=[],f_p_args=[]): #Logical option for Keppler because we have good x_0 and f_p
    '''
    f: function
    f_p: function first derivative
    x_0: initial
    f_args: additional arguments for f
    f_p_args: additional arguments for f_p
    '''
    if np.abs(f(x_0, *f_args)) < tol:
        return x_0

    else:
        m = f_p(x_0, *f_p_args)
        x_new = (m * x_0 - f(x_0, *f_args)) / m
        return newton(f, f_p, x_new, tol,f_args=f_args, f_p_args=f_p_args)


# -*- coding: utf-8 -*-
# Copyright (c) 2011 RelationWare, Benno Luthiger. All rights reserved.
# See also LICENSE.txt

from Testing import ZopeTestCase
import transaction

from Products.Silva.testing import FunctionalLayer
from Products.Silva.tests.layer import users, setUp, tearDown

class RwTestCase(ZopeTestCase.Sandboxed, ZopeTestCase.ZopeTestCase):
    layer = FunctionalLayer

    def setUp(self):
        transaction.abort()
        self.beforeSetUp()
        try:
            #self.app = self._app()
            self.app = self.layer.get_application()
            setUp(self)
            self.silva = self.root = self.getRoot()
            self.catalog = self.silva.service_catalog
            self.layer.login('author')
            #self.login()
            self.app.REQUEST.AUTHENTICATED_USER = \
                self.app.acl_users.getUser(ZopeTestCase.user_name)
            self.afterSetUp()
        except:
            self.beforeClose()
            self._clear()
            raise

    def afterSetUp(self):
        pass

    def afterClear(self):
        pass

    def beforeSetUp(self):
        '''Called before the ZODB connection is opened,
           at the start of setUp(). By default begins
           a new transaction.
        '''
        transaction.begin()

    def beforeClose(self):
        '''Called before the ZODB connection is closed,
           at the end of tearDown(). By default aborts
           the transaction.
        '''
        transaction.abort()

    def _clear(self, call_close_hook=0):
        '''Clears the fixture.'''
        try:
            if call_close_hook:
                self.beforeClose()
        finally:
            tearDown(self)
            self._close()
            self.afterClear()

    def _close(self):
        '''Closes the ZODB connection.'''
        super(RwTestCase, self)._close()

    def getRoot(self):
        """Returns the silva root object, i.e. the "fixture root".
           Override if you don't like the default.
        """
        return self.app.root

    #add Silva Objects helper methods

    def addObject(self, container, type_name, id, product='Silva', **kw):
        getattr(container.manage_addProduct[product],
            'manage_add%s' % type_name)(id, **kw)
        # gives the new object a _p_jar ...
        transaction.savepoint()
        return getattr(container, id)

    def add_folder(self, object, id, title, **kw):
        folder = self.addObject(object, 'Folder', id, title=title, **kw)
        # Emulate old add index feature
        policy = kw.get('policy_name', None)
        if policy == 'Silva Document':
            self.addObject(folder, 'Document', 'index',
                           title=title, product='SilvaDocument')
        if policy == 'Silva AutoTOC':
            self.addObject(folder, 'AutoTOC', 'index', title=title)
        return folder

    def add_publication(self, object, id, title, **kw):
        publication = self.addObject(
            object, 'Publication', id, title=title, **kw)
        policy = kw.get('policy_name', None)
        if policy == 'Silva Document':
            self.addObject(publication, 'Document', 'index',
                           title=title, product='SilvaDocument')
        if policy == 'Silva AutoTOC':
            self.addObject(publication, 'AutoTOC', 'index', title=title)
        return publication

    def add_document(self, object, id, title):
        return self.addObject(object, 'Document', id, title=title,
                              product='SilvaDocument')


import re, urllib

from ircbot import web

r_li = re.compile(r'(?ims)
.*?
')
r_tag = re.compile(r'<[^>]+>')
r_parens = re.compile(r'(?<=\()(?:[^()]+|\([^)]+\))*(?=\))')
r_word = re.compile(r'^[A-Za-z0-9\' -]+$')

uri = 'http://encarta.msn.com/dictionary_/%s.html'
r_info = re.compile(
    r'(?:ResultBody">

(.*?) )|(?:(.*?))'
)

def dict(ircbot, input):
    if not input.group(2):
        return ircbot.reply("Nothing to define.")
    word = input.group(2)
    word = urllib.quote(word.encode('utf-8'))

    def trim(thing):
        if thing.endswith(' '):
            thing = thing[:-6]
        return thing.strip(' :.')

    bytes = web.get(uri % word)
    results = {}
    wordkind = None
    for kind, sense in r_info.findall(bytes):
        kind, sense = trim(kind), trim(sense)
        if kind:
            wordkind = kind
        elif sense:
            results.setdefault(wordkind, []).append(sense)
    result = input.group(2).encode('utf-8') + ' - '
    for key in sorted(results.keys()):
        if results[key]:
            result += (key or '') + ' 1. ' + results[key][0]
            if len(results[key]) > 1:
                result += ', 2. ' + results[key][1]
            result += '; '
    result = result.rstrip('; ')
    if result.endswith('-') and (len(result) < 30):
        ircbot.reply('Sorry, no definition found.')
    else:
        ircbot.say(result)
dict.commands = ['dict']


"""
304. Range Sum Query 2D - Immutable

Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined
by its upper left corner (row1, col1) and lower right corner (row2, col2).

Given matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

Time complexity : O(1) time per query, O(mn) time pre-computation. The pre-computation in the
constructor takes O(mn) time. Each sumRegion query takes O(1) time.

Space complexity : O(mn). The algorithm uses O(mn) space to store the cumulative region sum.
"""

class NumMatrix:

    def __init__(self, matrix):
        """
        :type matrix: List[List[int]]
        """
        if not matrix:
            return
        self.M = len(matrix)
        self.N = len(matrix[0])
        self.dp = [[0] * (self.N+1) for _ in range(self.M + 1)]

        for i in range(1, self.M+1):
            for j in range(1, self.N+1):
                self.dp[i][j] = matrix[i-1][j-1] + self.dp[i-1][j] + self.dp[i][j-1] - self.dp[i-1][j-1]
        print(self.dp)

    def sumRegion(self, row1, col1, row2, col2):
        """
        :type row1: int
        :type col1: int
        :type row2: int
        :type col2: int
        :rtype: int
        """
        return self.dp[row2+1][col2+1] - self.dp[row2+1][col1] - self.dp[row1][col2+1] + self.dp[row1][col1]


#

# ?FastApi
from typing import List

from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel, EmailStr, SecretStr, validator

from managers.auth import is_admin, is_approver, is_complainer, oauth2_schema
from managers.complaint import ComplaintManager

# ?Schemas,Models,Manager
from schemas.request.complaint import ComplaintIn
from schemas.response.complaint import ComplaintOut

# De

# router
router = APIRouter(tags=["Complaints"])

#!get_complaints
@router.get(
    "/complaints/",
    dependencies=[Depends(oauth2_schema)],
    response_model=List[ComplaintOut],
    status_code=200,
)
async def get_complaints(request: Request):
    user = request.state.user
    return await ComplaintManager.get_complaints(user)

#!create_complaint
@router.post(
    "/complaints/",
    dependencies=[Depends(oauth2_schema), Depends(is_complainer)],
    response_model=ComplaintOut,
    status_code=201,
)
async def create_complaint(request: Request, complaint: ComplaintIn):
    user = request.state.user
    return await ComplaintManager.create_complaint(complaint.dict(), user)

#!delete_complaint
@router.delete(
    "/complaints/{complaint_id}/",
    dependencies=[Depends(oauth2_schema), Depends(is_admin)],
    status_code=204,
)
async def delete_complaint(complaint_id: int):
    await ComplaintManager.delete_complaint(complaint_id)

#!approve_complaint
@router.put(
    "/complaints/{complaint_id}/approve/",
    dependencies=[Depends(oauth2_schema), Depends(is_approver)],
    status_code=204,
)
async def approve_complaint(complaint_id: int):
    await ComplaintManager.approve(complaint_id)

#!approve_complaint
@router.put(
    "/complaints/{complaint_id}/reject/",
    dependencies=[Depends(oauth2_schema), Depends(is_approver)],
    status_code=204,
)
async def reject_complaint(complaint_id: int):
    await ComplaintManager.reject(complaint_id)


from flask import make_response, jsonify
from marshmallow.exceptions import ValidationError
from app.schemas import ResponseError
from . import api

@api.errorhandler(ValidationError)
def validation_error(e):
    error = {
        'code': 400,
        'title': 'Validation Error',
        'detail': e.messages
    }
    error = ResponseError().dump({'data': error})
    return make_response(jsonify(error))

@api.app_errorhandler(404)
def page_not_found(e):
    error = {
        'code': 404,
        'title': 'Not Found',
        'detail': 'Данная страница не найдена',
    }
    error = ResponseError().dump({'data': error})
    return make_response(jsonify(error))