"""
Модуль для приведения выходных данных к читаемому виду.
"""
import os

from utils.manipulate_data import load_data, save_data  # pylint: disable=import-error

def fix_download_bug():
    """
    Функция правит мета данные при некорректном скачивании данных с БД.
    """

    meta_data = load_data('output/meta_data_latest.pkl')
    saved_videos = list(os.listdir("saved_data"))
    saved_videos.remove('.ipynb_checkpoints')
    latest_idx = len([i for i in meta_data['comparison_submeta'] if i is not None]) - 1
    video_not_to_delete = os.path.split(meta_data['local_features_paths'][latest_idx])[-1]
    saved_videos.remove(video_not_to_delete)
    os.remove(os.path.join("saved_data", saved_videos[0]))
    # pylint: disable=invalid-name
    for idx, (z1, z2) in enumerate(zip(meta_data['comparison_submeta'][latest_idx]['was_main_video_downloaded'],
                                       meta_data['comparison_submeta'][latest_idx][
                                           'was_main_video_compared_with_current'])):
        if z1 != z2:
            meta_data['comparison_submeta'][latest_idx]['was_main_video_downloaded'][idx] = False
            save_data(meta_data, 'output/meta_data_latest.pkl')
            break


import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="ssfm",
    version="0.0.1",
    author="Lykos94",
    scripts=['ssfm',],
    author_email="lukaszmoskwa94@gmail.com",
    description="Curses based File Manager application",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/Lykos94/ssfm",
    packages=[
        'ssfm_modules',
        'ssfm_modules.core',
        'ssfm_modules.draw',
        'ssfm_modules.extensions',
        'ssfm_modules.extensions.window_extension',
        'ssfm_modules.extensions.git_extension',
        'ssfm_modules.extensions.file_extension',
    ],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
        "Operating System :: Unix"
    ],
    python_requires='>=3.6',
)


import cv2
import numpy as np

src_img = np.load('./data/data_3df0.npy')
src_lab = np.load('./data/label_3df0.npy')
print(src_img.shape)

ind = 100
# cv2.imshow('Imgae '+str(ind)+' CPA: '+str(src_lab[ind]), src_img[ind])
# cv2.waitKey(0)
cv2.imwrite('./img/Imgae '+str(ind)+' CPA: '+str(src_lab[ind]) + '.jpg', src_img[ind])


import pygame
import random

class Snake:
	def __init__(self, window_width, window_height, space):
		self.reset(window_width, window_height, space)

	def reset(self, window_width, window_height, space):
		self.sprite_width = 50
		self.sprite_height = 50
		self.color = (26, 188, 156)

		self.sprites = [pygame.Surface((self.sprite_width, self.sprite_height)) for i in range(3)]

		self.rects = [sprite.get_rect() for sprite in self.sprites]
		for i in range(len(self.rects)):
			self.sprites[i].fill(self.color)
			space.remove((window_width // 2 - self.sprite_width // 2, window_height // 2 + self.sprite_height * i - self.sprite_height // 2))
			self.rects[i].center = (window_width // 2, window_height // 2 + self.sprite_height * i)

		self.dx = 0
		self.dy = -1
		self.speed = 50

	def update(self, window_width, window_height, space):
		space.append(self.rects[-1].topleft)
		for i in range(-1, -len(self.sprites), -1):
			self.rects[i].center = self.rects[i - 1].center

		self.rects[0].left += self.dx * self.speed
		self.rects[0].top += self.dy * self.speed
		if self.rects[0].left >= window_width:
			self.rects[0].left = 0
		if self.rects[0].right <= 0:
			self.rects[0].right = window_width
		if self.rects[0].top >= window_height:
			self.rects[0].top = 0
		if self.rects[0].bottom <= 0:
			self.rects[0].bottom = window_height
		try:
			space.remove(self.rects[0].topleft)
		except:
			pass

class Food:
	def __init__(self, space):
		self.sprite_width = 50
		self.sprite_height = 50
		self.color = (230, 126, 34)
		self.surface = pygame.Surface((self.sprite_width, self.sprite_height))
		self.rect = self.surface.get_rect()
		self.surface.fill(self.color)
		self.appear(space)

	def appear(self, space):
		self.rect.topleft = random.choice(space)


"""Module containing the ModelWrapper lightning module and its config."""
from typing import Dict, Tuple
from attrs import define

import torch
from torch import optim

import pytorch_lightning as pl

from jigsaw.composite import Composite as JigsawComposite
from jigsaw.piece import LossFunction as JigsawLossFunction
from jigsaw.piece import Module as JigsawModule

from draft.model.keys import FeatureKeys, LabelKeys
from draft.model.embedding import Embedding, EmbeddingConfig
from draft.model.team_modules import TeamConvolution, TeamConvolutionConfig
from draft.model.match_prediction import MatchPrediction, MatchPredictionConfig

BATCH_AXIS = 'examples'

@define
class ModelConfig:
    """Configuration used for the model wrapper."""
    embedding_config: EmbeddingConfig
    team_convolution_config: TeamConvolutionConfig
    match_prediction_config: MatchPredictionConfig
    learning_rate: float = 1e-3
    weight_decay: float = 1e-5

class Model(pl.LightningModule):
    """Wrapper around an ml module."""

    def __init__(self, config: ModelConfig):
        """Initialize the model with the given config.

        Args:
            config: The config used for this wrapper
        """
        super().__init__()
        self.key_feature_hero_picks = FeatureKeys.FEATURE_HERO_PICKS
        self.key_label_radiant_win = LabelKeys.LABEL_RADIANT_WIN
        self.save_hyperparameters()
        composite = JigsawComposite(
            components=[
                Embedding(config.embedding_config),
                TeamConvolution(config.team_convolution_config),
                MatchPrediction(config.match_prediction_config),
            ]
        )
        self.inner_modules = JigsawComposite(composite.extract(JigsawModule))
        self.losses = torch.jit.ignore(
            JigsawComposite(composite.extract(JigsawLossFunction))
        )
        self.learning_rate = config.learning_rate
        self.weight_decay = config.weight_decay

    def preprocess_input(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        """Preprocessing function for the inputs.

        Args:
            x: (batch_size, 10) vector with the IDs for each hero
        """
        return {self.key_feature_hero_picks: x}

    def preprocess_label(self, y: torch.Tensor) -> Dict[str, torch.Tensor]:
        """Preprocessing function for the labels.

        Args:
            y: (batch_size, 1) vector with the results of each match
        """
        return {self.key_label_radiant_win: y.float()}

    def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        """Compute the model predictions.

        Args:
            x: (batch_size, 10) vector with the IDs for the heroes picked
        """
        data = self.preprocess_input(x)
        return self.inner_modules(data)

    def training_step(self, batch: Tuple[torch.Tensor, torch.Tensor], batch_idx: int):
        """Run a training step.

        Args:
            batch: A tensor with drafts and outcomes
            batch_idx: The index for this batch
        """
        x, y = batch
        data = self.forward(x)

        losses = self.losses(data | self.preprocess_input(x) | self.preprocess_label(y))
        loss = sum([v for _, v in losses.items()])
        self.log('train_loss', loss, on_epoch=True, on_step=False)

        # self.train_accuracy(out, y)
        # self.log('train_acc', self.train_accuracy, on_epoch=True, on_step=False)
        return loss

    def validation_step(self, batch: Tuple[torch.Tensor, torch.Tensor], batch_idx: int):
        """Run a validation step.

        Args:
            batch: A tensor with drafts and outcomes
            batch_idx: The index for this batch
        """
        x, y = batch
        data = self.forward(x)

        losses = self.losses(data | self.preprocess_label(y))
        loss = sum([v for _, v in losses.items()])
        self.log('val_loss', loss, on_epoch=True, on_step=False)

        # self.val_accuracy(out, y)
        # self.log('val_acc', self.val_accuracy, on_epoch=True, on_step=False)
        return {'loss': loss}

    def configure_optimizers(self):
        """Set up the optimizer."""
        optimizer = optim.Adam(
            self.parameters(),
            lr=self.learning_rate,
            weight_decay=self.weight_decay,
        )
        return optimizer

    def export(self, path: str):
        """Export the model to the given path using ONNX.

        Args:
            path: The path to export the model to
        """
        model_args = torch.ones((1, 10)).int()
        torch.onnx.export(
            self,
            model_args,
            path,
            opset_version=14,
            input_names=self.inner_modules.inputs(),
            output_names=self.inner_modules.outputs(),
            dynamic_axes={
                key: {0: BATCH_AXIS}
                for key in self.inner_modules.inputs() + self.inner_modules.outputs()
            },
        )


#レコーディングダイエットアプリ
import json, os, textwrap, re
import tkinter as tk

import matplotlib.pyplot as plt#
import matplotlib.ticker as ticker#
from matplotlib.dates import date2num#
from matplotlib.dates import DateFormatter#

from tkinter import messagebox as mbox
from time import strftime
from datetime import datetime

class AppBase(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        header_frame = tk.Frame(self)
        header_frame.pack()
        self.header_label = tk.Label(header_frame)
        self.header_label.pack(side='left', anchor='center')
        self.menu_btn = tk.Button(header_frame, text='メニューに戻る')
        self.pack()

    def userentry_widgets(self):
        self.userentry_frame = tk.LabelFrame(self,\
            text='各項目を入力してください')
        tk.Label(self.userentry_frame, text='名前', anchor='e').grid(row=0, column=0)
        self.name = tk.Entry(self.userentry_frame, width=10)
        self.name.grid(row=0, column=1, columnspan=2)
        gender_frame = tk.Frame(self.userentry_frame)
        tk.Label(gender_frame, text='性別', anchor='e').pack(side='left')
        self.gender_iv = tk.IntVar()
        self.gender_dict = {0:'女性', 1:'男性'}
        self.gender_iv.set(0)
        for i,j in self.gender_dict.items():
            rb = tk.Radiobutton(gender_frame, value=i,
                                text=j, variable=self.gender_iv)
            rb.pack(side='left')
        gender_frame.grid(row=0, column=4, columnspan=4)
        tk.Label(self.userentry_frame, text='生年月日', anchor='e').grid(row=1, column=0)
        self.birthday = tk.Entry(self.userentry_frame, width=10)
        self.birthday.insert(0, '1970/01/01')
        self.birthday.grid(row=1, column=1, columnspan=2)
        tk.Label(self.userentry_frame, text='身長', anchor='e').grid(row=1, column=3)
        self.height = tk.Entry(self.userentry_frame, width=5)
        self.height.grid(row=1, column=4)
        tk.Label(self.userentry_frame, text='cm', anchor='w').grid(row=1, column=5)
        tk.Label(self.userentry_frame, text='体重', anchor='e').grid(row=1, column=6)
        self.weight = tk.Entry(self.userentry_frame, width=5)
        self.weight.grid(row=1, column=7)
        tk.Label(self.userentry_frame, text='kg', anchor='w').grid(row=1, column=8)
        tk.Label(self.userentry_frame, text='目標体重', anchor='e').grid(row=2, column=6)
        self.goal = tk.Entry(self.userentry_frame, width=5)
        self.goal.grid(row=2, column=7)
        tk.Label(self.userentry_frame, text='kg', anchor='e').grid(row=2, column=8)
        self.userentry_btn = tk.Button(self.userentry_frame, text='上記の内容で登録する')
        self.userentry_btn.grid(row=3, column=0, columnspan=9, sticky='ew')
        self.userentry_frame.pack()

    def menu_widgets(self):
        self.menu_frame = tk.Frame(self)
        tk.Label(self.menu_frame, text='＜レコーディングダイエット＞\n　メニューを選択して下さい').grid(row=0, column=0, columnspan=5)
        self.meal_btn = tk.Button(self.menu_frame,\
            text='食 事 を\n記録する', command=self.meal_widgets)
        self.motion_btn = tk.Button(self.menu_frame,\
            text='運 動 を\n記録する', command=self.motion_widgets)
        self.data_btn = tk.Button(self.menu_frame,\
            text='記録確認\n（修正）', command=self.data_totalization)
        self.meal_btn.grid(row=1, column=0, padx=2)
        self.motion_btn.grid(row=1, column=1)
        self.data_btn.grid(row=1, column=2, padx=2)
        self.userselect_btn = tk.Button(self.menu_frame, text='ユーザー選択に戻る')
        self.userselect_btn.grid(row=2, column=0, columnspan=3, sticky='ew', padx=2, pady=2)
        self.menu_frame.pack()

    #---食品登録関連---
    def meal_widgets(self):#レコーディング用・カロリー登録用ウィジェット
        self.menu_frame.pack_forget()
        self.menu_btn.config(command=lambda:self.return_menu(self.meal_frame))
        self.menu_btn.pack(side='right', anchor='e', padx=10)
        self.meal_frame = tk.LabelFrame(self, text='食事の記録(食品データは追加されません)　※ * 印の項目は入力必須です')
        self.recwidget_frame = tk.Frame(self.meal_frame)
        tk.Label(self.recwidget_frame, text='*日付', anchor='e').grid(row=0, column=0, sticky='we')
        self.date = tk.Entry(self.recwidget_frame, width=10)
        self.date.insert(0, strftime('%Y/%m/%d'))
        self.date.grid(row=0, column=1, sticky='we')
        tk.Label(self.recwidget_frame, text='*時間', anchor='e').grid(row=0, column=3, sticky='we')#, columnspan=2)
        self.time = tk.Entry(self.recwidget_frame, width=5)#リストボックスにする？
        self.time.insert(0, strftime('%H:%M'))
        self.time.grid(row=0, column=4, columnspan=2, sticky='we')
        tk.Label(self.recwidget_frame, text=' 体重', anchor='e').grid(row=0, column=6, columnspan=3, sticky='we')
        self.weight =  tk.Entry(self.recwidget_frame, width=5)
        self.weight.grid(row=0, column=9, sticky='we')
        tk.Label(self.recwidget_frame, text='kg', anchor='w').grid(row=0, column=10, sticky='we')
        tk.Label(self.recwidget_frame, text='*品名', anchor='e').grid(row=2, column=0, sticky='we')
        self.food =  tk.Entry(self.recwidget_frame)
        self.food.grid(row=2, column=1, columnspan=10, sticky='we')
        tk.Label(self.recwidget_frame, text=' *総カロリー', anchor='e').grid(row=2, column=11, columnspan=2, sticky='we')
        self.calorie =  tk.Entry(self.recwidget_frame, width=5)
        self.calorie.grid(row=2, column=13, columnspan=2, sticky='we')
        tk.Label(self.recwidget_frame, text='kcal', anchor='w').grid(row=2, column=15, sticky='we')
        self.rec_btn = tk.Button(self.recwidget_frame, text='食事内容を\n記録する', command=self.recording_meal)
        self.rec_btn.grid(row=2, column=16, columnspan=4, sticky='we', ipadx=2)
        tk.Label(self.recwidget_frame,\
            text='※既存のデータから食品を選択する場合、一番下の\n「登録した食品を組み合わせて新規の食品データを作る」から指定して下さい',\
                justify='left', fg='blue').grid(row=3, column=0, columnspan=20, sticky='we')
        self.meal_frame.pack()
        self.recwidget_frame.grid(row=0, column=0, columnspan=30, sticky='we')

        self.register_frame = tk.LabelFrame(self.meal_frame,
                                            text='食品データを新規登録する　※ * 印の項目は入力必須です')
        tk.Label(self.register_frame, fg='blue',\
            text='品目を選んで品名とカロリーを入力して下さい\n'\
                +'(品目は上書きで追加可能、登録済みの品名は内容を更新します)',\
                    justify='left').grid(row=0, column=0, columnspan=10, sticky='we')
        tk.Label(self.register_frame, text='*品 目 ', anchor='e').grid(row=1, column=0, sticky='we')
        self.item = tk.Spinbox(self.register_frame, values=[i for i in self.cal.keys()], width=10)
        self.item.grid(row=1, column=1, sticky='we')# columnspan=3,入れると矢印出ねえ
        tk.Label(self.register_frame, text='*品 名 ', anchor='e').grid(row=1, column=2, sticky='we')
        self.itemfood =  tk.Entry(self.register_frame, width=35)
        self.itemfood.grid(row=1, column=3, columnspan=4, sticky='we')
        tk.Label(self.register_frame, text='*カロリー', anchor='e').grid(row=2, column=0, sticky='we')
        self.unit_cal =  tk.Entry(self.register_frame, width=5)#1単位あたりのカロリー
        self.unit_cal.grid(row=2, column=1, sticky='we')
        tk.Label(self.register_frame, text='kcal(1g当たり)　　一食分の量', anchor='e').grid(row=2, column=2, columnspan=3, sticky='we')
        self.intake =  tk.Entry(self.register_frame, width=5)#摂取量
        self.intake.grid(row=2, column=5, sticky='we')
        tk.Label(self.register_frame, text='g', anchor='w').grid(row=2, column=6, sticky='we')
        tk.Label(self.register_frame, text='備考').grid(row=3, column=0, sticky='we')
        self.remarks =  tk.Entry(self.register_frame)#備考欄
        self.remarks.grid(row=3, column=1, columnspan=6, sticky='we')
        self.reg_btn = tk.Button(self.register_frame,\
            text='食品データを\n登録する', command=self.registar_food)
        self.reg_btn.grid(row=3, column=7, columnspan=4, sticky='we')
        self.register_frame.grid(row=1, column=0, columnspan=20, sticky='we')

        self.combo_frame = tk.LabelFrame(self.meal_frame,\
            text='登録した食品を組み合わせて新規の食品データを作る')
        self.combo_frame.grid(row=2, column=0, columnspan=20, sticky='we')
        tk.Label(self.combo_frame, fg='blue',\
             text='操作方法： 品目を選ぶ ⇒「この品目から選択」を押す ⇒ 品名を選ぶ ⇒ 摂取量を入力\n'\
                 +'　※選択した品名の1食分の量等を確認する場合は、下の「備考を取得する」を押してください\n'\
                 +'⇒「この内容を入力する」を押す(入力中の食品は下に表示されます)\n'\
                 +'　※入力中の食品を取り消す場合は同じ操作で「この内容を削除する」を押してください\n'\
                 +'⇒ 組み合わせる食品をすべて選択したら、一番下の「上記の内容で転記する」を押す\n'\
                 +'　※[食事の記録]及び[食品データを新規登録する]の品名、カロリー欄等に内容を転記します',\
                 justify='left').grid(row=0, column=0, columnspan=30, sticky='we')
        self.combo_dict = {}
        self.combo_item = tk.Spinbox(self.combo_frame,\
            values=[i for i in self.cal.keys()], width=10)
        self.combo_item.grid(row=1, column=1, sticky='we')
        tk.Button(self.combo_frame, text='この品目\nから選択',\
            command=self.disp_combo_food).grid(row=1, column=2, sticky='we')

    def disp_combo_food(self):
        self.combo_food = tk.Spinbox(self.combo_frame, width=14,\
            values=[i for i in self.cal[self.combo_item.get()].keys()])
        self.combo_food.grid_forget()#他の品目を選んでいた場合
        self.combo_food.grid(row=1, column=3, sticky='we')
        tk.Label(self.combo_frame, text='摂取量',\
            anchor='e').grid(row=1, column=4, sticky='we')
        self.conbo_gram = tk.Entry(self.combo_frame, width=5)
        self.conbo_gram.grid(row=1, column=5, sticky='we')
        tk.Label(self.combo_frame,text='g', anchor='w')\
            .grid(row=1, column=6, sticky='we')
        self.combo_entbtn = tk.Button(self.combo_frame,\
            text='この内容を\n入力する', command=self.add_to_combo)
        self.combo_entbtn.grid(row=1, column=7, columnspan=2, sticky='we')#, ipadx=3)
        self.combo_delbtn = tk.Button(self.combo_frame,\
            text='入力中から\n削除する', command=self.delete_combo)
        self.combo_delbtn.grid(row=1, column=9, columnspan=2, sticky='we')#, ipadx=3)
        self.combo_footer = tk.Frame(self.combo_frame)
        remarks_label_frame = tk.LabelFrame(self.combo_footer,\
            text='上記の食品に登録された備考を確認する')
        self.remarks_label = tk.Label(remarks_label_frame)#, anchor='e')
        self.remarks_label.pack(side='left')
        remarks_label_frame.pack(fill='x')
        tk.Button(remarks_label_frame, text='備考を取得する',\
            command=self.get_remarks).pack(side='right')
        combo_label_frame = tk.LabelFrame(self.combo_footer,\
            text='現在入力中の食品')
        combo_label_frame.pack(fill='x')
        combo_text = textwrap.wrap(','.join(i+j[0]+'g:'+str(j[1])+'kcal'\
                 for i,j in self.combo_dict.items()), 65)
        self.combo_label = tk.Label(combo_label_frame, fg='red',\
            text='\n'.join(combo_text)\
                if self.combo_dict else '何も入力されていません')
        self.combo_label.pack()
        self.combo_regbtn = tk.Button(self.combo_footer,\
            text='上記の内容で転記する', command=self.posting_combo)
        self.combo_regbtn.pack(fill='x')
        self.combo_footer.grid(row=2, column=0, columnspan=20, sticky='we')
    #---食品登録関連ここまで---

    #---運動登録関連---
    def motion_widgets(self):
        self.menu_frame.pack_forget()
        self.menu_btn.config(command=lambda:self.return_menu(self.motion_frame))
        self.menu_btn.pack(side='right', anchor='e', padx=10)
        self.motion_frame = tk.LabelFrame(self, text='運動の記録　※ * 印の項目は入力必須です')
        tk.Label(self.motion_frame, fg='blue',\
             text='操作方法： 日付と運動時間を入力 ⇒　活動内容を選択\n'\
                 +'⇒「活動内容から動作を選ぶ」を押す ⇒ 動作を選択\n'\
                 +'⇒ 頑張り度を選択 ⇒「カロリー計算」を押す\n'\
                 +'　※消費カロリーは体重を使って計算します。なるべく入力してください\n'\
                 +'　　(体重の入力がなければ、同日又は直近の日の平均体重が選択されます)\n'\
                 +'⇒ 計算結果に問題がなければ、「この内容で記録する」を押す',\
                 justify='left').grid(row=0, column=0, columnspan=30, sticky='we')
        tk.Label(self.motion_frame, text=' *日付', anchor='e').grid(row=1, column=0, sticky='we')
        self.m_date = tk.Entry(self.motion_frame, width=10)
        self.m_date.insert(0, strftime('%Y/%m/%d'))
        self.m_date.grid(row=1, column=1, sticky='we')
        tk.Label(self.motion_frame, text=' *運動時間', anchor='e').grid(row=1, column=2, columnspan=4, sticky='we')
        self.m_time = tk.Entry(self.motion_frame, width=5)
        self.m_time.grid(row=1, column=6, sticky='we')
        tk.Label(self.motion_frame, text='分　', anchor='w').grid(row=1, column=7, sticky='we')
        tk.Label(self.motion_frame, text='体重', anchor='w').grid(row=1, column=8, sticky='we')
        self.m_weight =  tk.Entry(self.motion_frame, width=5)
        self.m_weight.grid(row=1, column=9, sticky='we')
        tk.Label(self.motion_frame, text='kg', anchor='w').grid(row=1, column=10, sticky='we')
        radio_frame = tk.Frame(self.motion_frame)
        tk.Label(radio_frame, text='活動内容：', anchor='w').pack(side='left')
        self.motion_iv = tk.IntVar()
        self.motion_dict = {0:'運動', 1:'生活'}
        self.motion_iv.set(0)
        for i,j in self.motion_dict.items():
            rb = tk.Radiobutton(radio_frame, value=i,
                                text=j, variable=self.motion_iv)
            rb.pack(side='left')
        radio_frame.grid(row=2, column=0, columnspan=4, sticky='we')
        tk.Button(self.motion_frame, text='活動内容から動作を選ぶ',\
            command=self.disp_activity).grid(row=2, column=4, columnspan=4, sticky='we')
        self.motion_frame.pack()

    def disp_activity(self):
        tk.Label(self.motion_frame, text='*動作', anchor='e').grid(row=3, column=0, sticky='we')
        self.activity = tk.Spinbox(self.motion_frame,\
            values=[i for i in self.mets[self.motion_dict[self.motion_iv.get()]].keys()], width=15)
        self.activity.grid(row=3, column=1, columnspan=3, sticky='we')
        tk.Label(self.motion_frame, text=' *頑張り度', anchor='e').grid(row=3, column=4, columnspan=2, sticky='we')
        self.work_dict = {'最弱(×0.7)':0.7, '弱い(×0.8)':0.8, 'やや弱い(×0.9)':0.9,\
            '普通(×1)':1, 'やや強い(×1.2)':1.2, '強い(×1.4)':1.4, '最強(×1.6)':1.6}
        self.work = tk.Spinbox(self.motion_frame,\
            values=[i for i in self.work_dict.keys()], width=10)
        self.work.grid(row=3, column=6, columnspan=2, sticky='we')
        tk.Button(self.motion_frame, text='カロリー計算',\
            command=self.burn_cal_culc).grid(row=3, column=8, columnspan=2, sticky='we')
        self.burn_frame = tk.LabelFrame(self.motion_frame, text='計算結果')
        self.burn_btn = tk.Button(self.burn_frame, text='この内容で記録する')
        self.burn_btn.pack(side='right')
        self.burn_label = tk.Label(self.burn_frame, anchor='e', fg='red')
        self.burn_label.pack(side='right', fill='x')

    def data_widgets(self):#閲覧するデータの期間を指定し、修正等も可能にする
        self.menu_frame.pack_forget()
        self.menu_btn.config(command=lambda:self.return_menu(self.data_frame))
        self.menu_btn.pack(side='right', anchor='e', padx=10)
        self.data_frame = tk.LabelFrame(self, text='データメニューを選択してください')
        self.graph_btn = tk.Button(self.data_frame, text='データをグラフ化', command=self.disp_graph)
        self.graph_btn.grid(row=0, rowspan=2, column=0, columnspan=5, sticky='ewsn')
        self.fix_btn = tk.Button(self.data_frame, text='データを修正する')
        self.fix_btn.grid(row=0, rowspan=2, column=5, columnspan=5, sticky='ewsn')
        self.data_frame.pack()

class RecordingDiet(AppBase):
    def __init__(self, master=None):
        super().__init__(master)
        self.user_exist()

    #userdata確認関連
    def user_exist(self):
        if os.path.exists('userdata.json'):
            self.open_menu()
        else:
            self.header_label.config(text='個人データを登録します')
            self.userentry_widgets()
            self.userentry_btn.config(command=lambda:self.make_userdata())

    def open_menu(self):#userdetaがあればmenuを開く
        self.user = self.load_json('userdata.json')
        self.mets = self.load_json('METsdatabase.json')
        self.cal = self.load_json('caloriedatabase.json')
        self.header_label.config(text=f'{self.user["user"]["name"]}さん、ようこそ')
        self.menu_widgets()

    def return_menu(self, frame):
        self.open_menu()
        self.menu_btn.pack_forget()
        frame.pack_forget()

    def make_userdata(self):
        name = self.name.get()
        gender = self.gender_iv.get()
        birthday = self.birthday.get()
        height = self.height.get()
        weight = self.weight.get()
        goal = self.goal.get()
        if all([name, birthday, height, weight, goal]):# str(gender)※必ず選択されるので不要
            try:
                birthday = datetime.strptime(birthday,'%Y/%m/%d').strftime('%Y/%m/%d')#不正な日付か
                if birthday > datetime.now().strftime('%Y/%m/%d'):#生まれている人か
                    raise KeyError
            except:
                mbox.showerror('エラー', '日付が不正です！')
                return

            keys = ['name', 'gender', 'birthday', 'height', 'weight', 'goal']
            values = [name, gender, birthday, float(height), float(weight), float(goal)]
            d = {'user':{k:v for k,v in zip(keys, values)}}
            self.write_json('userdata.json', d)#(f'{name}data.json', d)※userを複数作成する場合
            self.userentry_frame.pack_forget()
            self.open_menu()
        else:
            mbox.showerror('エラー', '入力漏れがあります！')

    def get_age(self, x):#年齢を基礎代謝計算に用いるため必要
        today = int(time.today().strftime('%Y%m%d'))
        birthday = int(datetime.strptime(x,'%Y/%m/%d').strftime('%Y%m%d'))
        return int((today - birthday)/10000)

    #JSON関連
    def write_json(self, jsonfile, dictionary):
        with open(jsonfile, 'w')as f:
            json.dump(dictionary, f)#, ensure_ascii=False, sort_keys=True)

    def load_json(self, jsonfile):
        with open(jsonfile, 'r')as f:
            return json.load(f)

    #食品登録関連
    def get_remarks(self):
        item = self.combo_item.get()
        food = self.combo_food.get()
        remarks = self.cal[item][food][1]
        txt = remarks if remarks != '' else '備考は登録されていません'
        self.remarks_label.config(text=textwrap.wrap(txt, 40))

    def add_to_combo(self):
        gram = self.conbo_gram.get()
        key = self.combo_food.get()
        cal = self.cal[self.combo_item.get()][key][0]
        if all([gram, key]):
            self.remarks_label.config(text='')
            self.combo_dict[key] = [gram, int(float(gram)*float(cal))]
            combo_text = textwrap.wrap(','.join(i+j[0]+'g:'+str(j[1])+'kcal'\
                for i,j in self.combo_dict.items()), 65)
            self.combo_label.config(text='\n'.join(combo_text))
        else:
            mbox.showerror('エラー', '入力漏れがあります！')

    def delete_combo(self):
        try:
            target = self.combo_food.get()
            delete = self.combo_dict.pop(target)
            mbox.showinfo('削除完了', f'組み合わせから\n{target}{delete[0]}g:{delete[1]}kcalを削除しました')
            combo_text = textwrap.wrap(','.join(i+j[0]+'g:'+str(j[1])+'kcal'\
                 for i,j in self.combo_dict.items()), 65)
            self.combo_label.config(text='\n'.join(combo_text)\
                 if self.combo_dict else '何も入力されていません')
        except:
            mbox.showerror('エラー', 'その食品はありません！')

    def posting_combo(self):
        if self.combo_dict:
            remarks = ','.join(i+str(j[0])+'g' for i,j in self.combo_dict.items())
            totalgram = sum(int(i[0]) for i in self.combo_dict.values())
            totalcal = sum(int(i[1]) for i in self.combo_dict.values())
            unitcal = round(totalcal/totalgram, 2)
            self.food.delete(0, 'end')
            self.food.insert(0, remarks)
            self.calorie.delete(0, 'end')
            self.calorie.insert(0, totalcal)
            self.unit_cal.delete(0, 'end')
            self.unit_cal.insert(0, unitcal)
            self.intake.delete(0, 'end')
            self.intake.insert(0, totalgram)
            self.remarks.delete(0, 'end')
            self.remarks.insert(0, remarks)
            self.combo_dict.clear()
            self.combo_label.config(text='何も入力されていません')
            self.conbo_gram.delete(0, 'end')
        else:
            mbox.showerror('エラー', '何も入力されていません！')

    def recording_meal(self):#食事記録('get')
        date = self.date.get()#必須項目
        time = self.time.get()#必須項目
        food = self.food.get()#必須項目
        calorie = self.calorie.get()#必須項目
        if not all([date, time, food, calorie]):
            mbox.showerror('エラー', '入力漏れがあります！')
            return
        try:#日時を0埋めに直す
            date = datetime.strptime(date, '%Y/%m/%d').strftime('%Y/%m/%d')#不正な日付か
            time = datetime.strptime(time, '%H:%M').strftime('%H:%M')#不正な時間か
            if date > datetime.now().strftime('%Y/%m/%d'):
                raise KeyError
        except:
            mbox.showerror('エラー', '日時が不正です！')
            return
        if date in self.user:#日付があるか
            if 'get' in self.user[date]:#食事入力があるか
                if time in self.user[date]['get']:#時間があるか
                    if not food in self.user[date]['get'][time]:#食事重複なし
                        self.user[date]['get'][time][food] = int(calorie)
                    else:#食事が重複
                        mbox.showerror('エラー', 'そのデータは既に登録済みです')
                        return
                else:#時間、食事なし
                    value = {food : int(calorie)}
                    self.user[date]['get'][time] = value
            else:#食事入力なし
                value = {time : {food : int(calorie)}}
                self.user[date]['get'] = value
        else:#日付、食事、時間なし(新規登録)
            value = {'get': {time : {food : int(calorie)}}}
            self.user[date] = value

        weight = self.weight.get()#体重はdate['weight']のリスト内へ入れる
        if weight:#体重入力あり
            if 'weight' in self.user[date]:
                self.user[date]['weight'].append(float(weight))
            else:#体重登録なし(新規登録)
                self.user[date]['weight'] = [float(weight)]
        self.write_json('userdata.json', self.user)
        u = self.load_json('userdata.json')#DEBUG
        self.food.delete(0, 'end')
        self.calorie.delete(0, 'end')
        self.weight.delete(0, 'end')
        print(f'\n{u}')#DEBUG

    def registar_food(self):#食品データ登録
        item = self.item.get()#必須項目
        food = self.itemfood.get()#必須項目
        cal = self.unit_cal.get()#必須項目
        if not all([item, food, cal]):
            mbox.showerror('エラー', '入力漏れがあります！')
            return

        remarks = self.remarks.get()#備考
        intake = f'1食{self.intake.get()}g' if self.intake.get() else ''#1食分の量(備考に追記)
        if item in self.cal:#品目があるか
            if food in self.cal[item]:#品名があるか
                answer = mbox.askokcancel('登録前確認',\
                    f'既に登録済みの品名です。上書きしますか？\n\n食 品 名：{food}\n'\
                    +'\n'.join(f'1g当たり：{i}kcal' if isinstance(i,float)\
                     else f' 備　考 ：{i}' for i in self.cal[item][food]))
                if answer:#上書きする
                    self.cal[item][food] = [float(cal), f'{remarks}、{intake}']
                else:#上書きしない
                    return
            else:#品名がない(新規登録)
                self.cal[item][food] = [float(cal),f'{remarks}、{intake}']
        else:#品目がない(新規登録)
            self.cal[item] = {food : [float(cal),f'{remarks}、{intake}']}

        self.write_json('caloriedatabase.json', self.cal)
        c = self.load_json('caloriedatabase.json')#DEBUG
        print(f'{c}')#DEBUG OK

    #運動登録関連
    def burn_cal_culc(self):
        date = self.m_date.get()#必須
        weight = self.m_weight.get()#無ければ他から取ってこさせる
        mets = self.mets[self.motion_dict[self.motion_iv.get()]][self.activity.get()]#動作
        work = self.work_dict[self.work.get()]
        time = self.m_time.get()#ここでは判定だけに用いる
        try:#日を0埋めに直す
            date = datetime.strptime(date, '%Y/%m/%d').strftime('%Y/%m/%d')#不正な日付か
            if date > datetime.now().strftime('%Y/%m/%d'):
                raise KeyError
        except:
            mbox.showerror('エラー', '日付が不正です！')
            return

        index = sorted(self.user['daily_total'])#平均体重存在確認用
        if not weight:#体重入力無し
            if not date in self.user:#この日の体重データがない
                if not index:#平均体重データもない
                    weight = self.user['user']['weight']#開始時の体重で計算
                else:#平均体重データはある
                    weight = self.user['daily_total'][index[-1]][0]#直近の平均体重で計算
            else:#この日の体重データがある
                lst = self.user[date]['weight']
                weight = round(sum(lst)/len(lst), 1)#この日の平均体重で計算
        else:#体重入力がある場合
            weight = float(weight)#浮動小数点数に変換

        if all([mets, weight, time, work]):
            self.burn_cal = int(mets * float(weight) * int(time)/60 * work * 1.05)
            self.burn_label.config(text=f'{self.activity.get()}{time}分(体重{weight}kgで計算)\n頑張り度：{self.work.get()}\n消費カロリー：{self.burn_cal}kcal')
            self.burn_btn.config(command=self.recording_motion)
            self.burn_frame.grid(row=4, rowspan=3, column=0, columnspan=10, sticky='wesn')
            self.burn_weight = weight
        else:
            mbox.showerror('エラー', '入力漏れがあります！')

    def recording_motion(self):#運動記録('burn')
        date = datetime.strptime(self.m_date.get(), '%Y/%m/%d').strftime('%Y/%m/%d')#必須項目
        motion = f'{self.activity.get()}{self.m_time.get()}分'#必須項目
        lst = [self.work.get(), self.burn_weight, self.burn_cal]#
        if date in self.user:#日付があるか
            if 'burn' in self.user[date]:#運動入力があるか
                if not motion in self.user[date]['burn']:#運動重複なし
                    self.user[date]['burn'][motion] = lst
                else:#運動が重複
                    mbox.showerror('エラー', 'そのデータは既に登録済みです')
                    return
            else:#運動入力なし
                self.user[date]['burn'] = {motion : lst}
        else:#日付、運動なし(新規登録)
            self.user[date] = {'burn': {motion : lst}}

        weight = self.m_weight.get()#体重はdate['weight']のリスト内へ入れる
        if weight:#体重入力あり
            if 'weight' in self.user[date]:
                self.user[date]['weight'].append(float(weight))
            else:#体重登録なし(新規登録)
                self.user[date]['weight'] = [float(weight)]
        self.write_json('userdata.json', self.user)
        u = self.load_json('userdata.json')#DEBUG
        self.burn_frame.grid_forget()
        self.m_time.delete(0, 'end')
        self.m_weight.delete(0, 'end')
        print(f'\n{u}')#DEBUG

    #データ集計・修正関連
    def data_totalization(self):#self.user['daily_total'][日付]]内の各リストに日計データを差し込む
        #毎回データをすべて再集計する(登録済みの内容を消す)
        self.user['daily_total'].clear()

        #self.user['daily_total'][日付] = [ave_weight, total_burn, total_get]の辞書内リスト形式で定義
        #   ※特定の要素がなければ0(体重の場合は前日と同じ値)を挿入する
        for k,v in self.user.items():
            #print(k,v)#DEBUG
            if re.match('\d{4}/\d{2}/\d{2}', k):#kが日付のものだけ集計する
                self.user['daily_total'][k] = []
                if 'weight' in v:#体重データがある
                    ave_weight = round(sum(v['weight'])/len(v['weight']), 1)
                    #print(round(sum(v['weight'])/len(v['weight']), 1))#DEBUG OK
                elif all((i for i in self.user['daily_total'].values())):#体重データはないが平均体重がある
                    index = sorted(self.user['daily_total'])
                    ave_weight = self.user['daily_total'][index[-2]][0]#前の平均体重
                else:#その日の体重データも平均体重も無い
                    ave_weight = self.user['user']['weight']#開始時の体重を平均体重とする
                self.user['daily_total'][k].append(ave_weight)#リスト[0]に追加

                if 'burn' in v:#burn(運動)がある
                    total_burn = sum(lst[-1] for motion,lst in v['burn'].items())
                    #print(total_burn)#DEBUG OK
                else:#運動がない
                    total_burn = 0
                self.user['daily_total'][k].append(total_burn)#リスト[1]に追加

                if 'get' in v:#get(食事)がある
                    gets = [[cal for cal in food_cal.values()]for time,food_cal in v['get'].items()]
                    total_get = sum(sum(cal) for cal in gets)
                    #print(total_get)#DEBUG OK
                else:#食事がない
                    total_get = 0
                self.user['daily_total'][k].append(total_get)#リスト[2]に追加
        #データ書き込み
        self.write_json('userdata.json', self.user)
        u = self.load_json('userdata.json')#DEBUG
        print(u)#DEBUG OK
        self.data_widgets()

    def disp_graph(self):
        days = [datetime.strptime(i,'%Y/%m/%d') for i in sorted(self.user['daily_total'])]
        weights = []
        burns = []
        gets = []
        for lst in self.user['daily_total'].values():
            weights.append(lst[0])
            burns.append(lst[1])
            gets.append(lst[2])
        #print(f'days:{days}\nweights:{weights}\nburns{burns}\ngets{gets}')#DEBUG OK
        fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, figsize=(9, 6))
        ax1.plot(days, weights, label='Weight')
        ax2.plot(days, burns, label='Burned_calorie', color='r')
        ax3.plot(days, gets, label='Intaked_calorie', color='g')
        ax1.set_title('Recording Diet Graph')
        ax3.set_xlabel('Days')
        ax1.set_ylabel('Weight')
        ax2.set_ylabel('Burned calorie')
        ax3.set_ylabel('Intaked calorie')
        plt.xticks(days, rotation=90, size='small')
        new_xticks = date2num(days)
        ax1.xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
        ax1.xaxis.set_major_formatter(DateFormatter('%Y/%m/%d'))
        ax2.xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
        ax2.xaxis.set_major_formatter(DateFormatter('%Y/%m/%d'))
        ax3.xaxis.set_major_locator(ticker.FixedLocator(new_xticks))
        ax3.xaxis.set_major_formatter(DateFormatter('%Y/%m/%d'))
        fig.tight_layout()#表示範囲の自動修正
        plt.grid(True)
        plt.legend()#線の名前を表示
        plt.show()

root = tk.Tk()
root.title("Let's レコーディングダイエット")
root.geometry('500x600+400+300')
app = RecordingDiet(root)
app.mainloop()


import cv2
import sys
import json
import util
import pytesseract

bw_threshold = 160
image_density_threshold = 0.001

def main():
    if len(sys.argv) < 2:
        raise Exception("Inputs:\n\tparameter 1: Test image to use for processing.")
    input_image = sys.argv[1]
    print("Image: %s" % input_image)
    image = cv2.imread(input_image)
    img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    img_bw =cv2.imread(input_image, cv2.IMREAD_GRAYSCALE)
    x_coords, y_coords = util.get_cell_boundaries(img_bw)
    row = 0
    for y_coord in y_coords:
        column = 0
        for x_coord in x_coords:
            raw_image = img_rgb[y_coord[0]:y_coord[1], x_coord[0]:x_coord[1]]
            image_height, image_width = img_bw.shape
            image_sum = raw_image.sum()
            image_density = image_sum / (image_width * image_height * 255)
            if image_density < image_density_threshold or image_density >= 1 - image_density_threshold:
                number = 0
            else:
                custom_oem_psm_config = r'--oem 3 --psm 10'
                number = pytesseract.image_to_string(image, config=custom_oem_psm_config)
                util.show_image(raw_image,
                                title="Row %s, col %s: %s" % (row, column, number))
            print("Row %s\tColumn %s\t%s" % (row, column, number))
            column += 1
        row += 1

if __name__ == '__main__':
    main()


from marketing_automation.active_campaign.entities.active_campaign_contact_automation import ActiveCampaignContactAutomation
from marketing_automation.active_campaign.entities.active_campaign_contact_note import ActiveCampaignContactNote
from marketing_automation.active_campaign.entities.active_campaign_deal import ActiveCampaignDeal
from marketing_automation.active_campaign.entities.active_campaign_deal_note import ActiveCampaignDealNote
from marketing_automation.active_campaign.entities.active_campaign_deal_task import ActiveCampaignDealTask
from unified.core.actions import Actions
from marketing_automation.active_campaign import util
from marketing_automation.active_campaign.entities.active_campaign_account import ActiveCampaignAccount
from marketing_automation.active_campaign.entities.active_campaign_tracked_event import ActiveCampaignTrackedEvent
import json
import requests

class ActiveCampaignActions(Actions):
    """ActiveCampaign Actions wrapper class.

        Extends from common Actions class.
    """

    def get_account_fields_data(self, account_entity, context):
        """ Get the account custom  fields data"""

        custom_fields_data = util.rest('GET', 'accountCustomFieldMeta', context, None).text
        custom_fields_data = json.loads(custom_fields_data)

        fields = []
        custom_fields_data = custom_fields_data["accountCustomFieldMeta"]

        if account_entity.address_1 is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Address 1":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.address_1
                    }
                    fields.append(field_data)

        if account_entity.address_2 is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Address 2":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.address_2
                    }
                    fields.append(field_data)

        if account_entity.city is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "City":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.city
                    }
                    fields.append(field_data)

        if account_entity.state is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "State/Province":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.state
                    }
                    fields.append(field_data)

        if account_entity.postal_code is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Postal Code":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.postal_code
                    }
                    fields.append(field_data)

        if account_entity.country is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Country":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.country
                    }
                    fields.append(field_data)

        if account_entity.phone_number is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Phone Number":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.phone_number
                    }
                    fields.append(field_data)

        if account_entity.description is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Description":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.description
                    }
                    fields.append(field_data)

        if account_entity.number_of_employees is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Number of Employees":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.number_of_employees
                    }
                    fields.append(field_data)

        if account_entity.annual_revenue is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Annual Revenue":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.annual_revenue
                    }
                    fields.append(field_data)

        if account_entity.industry is not None:
            for obj in custom_fields_data:
                if obj["fieldLabel"] == "Industry/Vertical":
                    field_data = {
                        "customFieldId": obj["id"],
                        "fieldValue": account_entity.industry
                    }
                    fields.append(field_data)
        return fields

    def create_account(self, context, payload):
        """ Create a new account."""

        account_entity = ActiveCampaignAccount(**payload)

        # Get custom feilds data
        fields = self.get_account_fields_data(account_entity, context)

        if len(fields)!=0:
            account_data = {
                "account": {
                    "name": account_entity.name,
                    "accountUrl": account_entity.website,
                    "fields": fields
                }
            }
        else:
            account_data = {
                "account": {
                    "name": account_entity.name,
                    "accountUrl": account_entity.website,
                }
            }

        resp = util.rest('POST', 'accounts', context, account_data)
        return json.loads(resp.text)

    def update_account(self, context, payload):
        """ Create a new account."""

        account_entity = ActiveCampaignAccount(**payload)

        # Get custom fields data
        fields = self.get_account_fields_data(account_entity, context)

        if len(fields)!=0:
            account_data = {
                "account": {
                    "name": account_entity.name,
                    "accountUrl": account_entity.website,
                    "fields": fields
                }
            }
        else:
            account_data = {
                "account": {
                    "name": account_entity.name,
                    "accountUrl": account_entity.website,
                }
            }

        resp = util.rest('PUT', f'accounts/{account_entity.account_id}', context, account_data).text
        return json.loads(resp)

    def add_contact_note(self, context, payload):
        """Add a note to contact"""

        note_entity = ActiveCampaignContactNote(**payload)
        note_data = {
            "note": {
                "note": note_entity.note,
                "relid": note_entity.contact_id,
                "reltype": "Subscriber",
            }
        }

        client = util.get_client(context)
        return client.notes.create_a_note(note_data)

    def add_contact_to_automation(self, context, payload):
        """Add a contact to an automation."""

        contact_entity = ActiveCampaignContactAutomation(**payload)
        contact_data = {
            "contactAutomation": {
                "contact": int(contact_entity.contact_id),
                "automation": int(contact_entity.automation_id)
            }
        }

        client = util.get_client(context)
        return client.contacts.add_a_contact_to_an_automation(contact_data)

    def get_deal_stages_id(self, deal_entity,  context):
        """ Get deal Stages data"""

        stage_data = util.rest('GET', 'dealStages', context, None).text
        stage_data = json.loads(stage_data)
        stage_data = stage_data["dealStages"]

        for obj in stage_data:
            if deal_entity.stage == obj["title"]:
                id = obj["id"]
                return id
        return None

    def create_deal(self, context, payload):
        """Create a new deal."""

        deal_entity = ActiveCampaignDeal(**payload)

        # Get stage id
        stage_id = self.get_deal_stages_id(deal_entity, context)

        deal_data = {
            "deal": {
                "contact": deal_entity.contact_id,
                "account": deal_entity.account_id,
                "currency": deal_entity.currency,
                "group": deal_entity.pipeline_id,
                "owner": deal_entity.owner_id,
                "stage": stage_id,
                "title": deal_entity.title,
                "value": deal_entity.value,
            }
        }

        client = util.get_client(context)
        response = client.deals.create_a_deal(deal_data)
        id = response["deal"]["id"]

        custom_fields_data = {
            "dealCustomFieldDatum":{
                "customFieldId": 1,
                "dealId": id,
                "fieldValue": deal_entity.forecasted_close_date
            }
        }

        update_forecast_date = util.rest('POST', 'dealCustomFieldData', context, custom_fields_data).text
        return response

    def update_deal(self, context, payload):
        """Update an existing deal"""

        deal_entity = ActiveCampaignDeal(**payload)

        # Get Stage Id
        stage_id = self.get_deal_stages_id(deal_entity, context)

        deal_data = {
            "deal": {
                "contact": deal_entity.contact_id,
                "account": deal_entity.account_id,
                "currency": deal_entity.currency,
                "group": deal_entity.pipeline_id,
                "owner": deal_entity.owner_id,
                "stage": stage_id,
                "title": deal_entity.title,
                "value": deal_entity.value,
            }
        }

        client = util.get_client(context)
        response = client.deals.update_a_deal(deal_entity.deal_id, deal_data)

        custom_fields_data = {
            "dealCustomFieldDatum":{
                "customFieldId": 1,
                "dealId": deal_entity.deal_id,
                "fieldValue": deal_entity.forecasted_close_date
            }
        }

        update_forecast_date = util.rest('POST', 'dealCustomFieldData', context, custom_fields_data).text
        return response

    def add_note_to_deal(self, context, payload):
        """Create a deal note"""

        note_entity = ActiveCampaignDealNote(**payload)
        note_data = {
            "note": {
                "note": note_entity.note
            }
        }

        client = util.get_client(context)
        return client.deals.create_a_deal_note(note_entity.deal_id, note_data)

    def create_deal_task(self, context, payload):
        """Create a deal task"""

        task_entity = ActiveCampaignDealTask(**payload)
        resp = util.rest('GET', 'dealTasktypes', context, None).text
        resp = json.loads(resp)

        for dealtype in resp["dealTasktypes"]:
            if dealtype["title"] == task_entity.task_type:
                task_type_id = dealtype["id"]

        task_data = {
            "dealTask": {
                "title": task_entity.title,
                "relid": task_entity.deal_id,
                "note": task_entity.note,
                "duedate": task_entity.due_date,
                "dealTasktype": task_type_id,
                "assignee": task_entity.assignee_id,
            }
        }

        resp = util.rest('POST', 'dealTasks', context, task_data).text
        return json.loads(resp)

    def create_tracked_event(self, context, payload):

        headers= {
            "Content-Type":"application/x-www-form-urlencoded"
        }
        event_info = ActiveCampaignTrackedEvent(**payload)
        data={
            "actid": event_info.account_id,
            "key": event_info.event_key,
            "event":event_info.event_name,
            "eventdata": int(event_info.event_value),
            "visit":{"email":event_info.contact_email_address}
        }
        resp = requests.request('POST', 'https://trackcmp.net/event', headers=headers, data=data).text
        return json.loads(resp)


# python3
r"""Learners of generalized method of moments with deep networks.

References:

- DeepGMMLearner:
  Andrew Bennett, Nathan Kallus, and Tobias Schnabel. Deep generalized method of
  moments forinstrumental variable analysis.  InAdvances in Neural Information
  Processing Systems, pages3559–3569, 2019.

- AdversarialSEMLearner
  L. Liao, Y. L. Chen, Z. Yang, B. Dai, Z. Wang and M. Kolar, 2020. Provably
  efficient neural estimation of structural equation model: An adversarial
  approach. arXiv preprint arXiv:2007.01290.

- AGMMLearner
  Dikkala, N., Lewis, G., Mackey, L. and Syrgkanis, V., 2020. Minimax estimation
  of conditional moment models. Advances in Neural Information Processing
  Systems, 33.
"""

import datetime
from typing import Dict, List, Optional

from acme import core
from acme.tf import savers as tf2_savers
from acme.tf import utils as tf2_utils
from acme.utils import counting
from acme.utils import loggers
import numpy as np
import sonnet as snt
import tensorflow.compat.v2 as tf

from src.ope.deep_gmm.oadam import oadam

# Default Acme checkpoint TTL is 5 days.
_CHECKPOINT_TTL = int(datetime.timedelta(days=30).total_seconds())

def _optimizer_class(name):
  return oadam.OAdam if name == 'OAdam' else getattr(snt.optimizers, name)

def _td_error(critic_network, o_tm1, a_tm1, r_t, d_t, o_t, a_t) -> tf.Tensor:
  """Computes TD error."""
  q_tm1 = critic_network(o_tm1, a_tm1)
  q_t = critic_network(o_t, a_t)
  if q_tm1.shape != q_t.shape:
    raise ValueError(f'Shape of q_tm1 {q_tm1.shape.as_list()} does not '
                     f'match that of q_t {q_t.shape.as_list()}')
  d_t = tf.reshape(d_t, q_tm1.shape)
  g = q_tm1 - d_t * q_t
  r_t = tf.reshape(r_t, q_tm1.shape)
  td_error = r_t - g
  return td_error, q_tm1, q_t

def _orthogonal_regularization(network: snt.Module,) -> tf.Tensor:
  """Copied from third_party/py/dice_rl/estimators/neural_dice.py."""
  reg = 0.
  for w in network.trainable_variables:
    if w.name.endswith('/w:0'):
      prod = tf.matmul(tf.transpose(w), w)
      reg += tf.reduce_sum(tf.math.square(prod * (1 - tf.eye(prod.shape[0]))))
  return reg

def _l2_regularization(network: snt.Module,) -> tf.Tensor:
  """Copied from third_party/py/dice_rl/estimators/neural_dice.py."""
  reg = 0.
  for w in network.trainable_variables:
    if w.name.endswith('/w:0'):
      reg += tf.reduce_sum(tf.square(w))
  return reg

class DeepGMMLearnerBase(core.Learner):
  """Deep GMM learner base class."""

  def __init__(self,
               policy_network: snt.Module,
               critic_network: snt.Module,
               f_network: snt.Module,
               discount: float,
               dataset: tf.data.Dataset,
               use_tilde_critic: bool,
               tilde_critic_network: snt.Module = None,
               tilde_critic_update_period: int = None,
               critic_optimizer_class: str = 'OAdam',
               critic_lr: float = 1e-4,
               critic_beta1: float = 0.5,
               critic_beta2: float = 0.9,
               f_optimizer_class: str = 'OAdam',
               f_lr: float = None,  # Either f_lr or f_lr_multiplier must be
                                    # None.
               f_lr_multiplier: Optional[float] = 1.0,
               f_beta1: float = 0.5,
               f_beta2: float = 0.9,
               critic_regularizer: float = 0.0,
               f_regularizer: float = 1.0,  # Ignored if use_tilde_critic = True
               critic_ortho_regularizer: float = 0.0,
               f_ortho_regularizer: float = 0.0,
               critic_l2_regularizer: float = 0.0,
               f_l2_regularizer: float = 0.0,
               checkpoint_interval_minutes: int = 10.0,
               clipping: bool = True,
               clipping_action: bool = True,
               bre_check_period: int = 0,  # Bellman residual error check.
               bre_check_num_actions: int = 0,  # Number of sampled actions.
               dev_dataset: tf.data.Dataset = None,
               counter: counting.Counter = None,
               logger: loggers.Logger = None,
               checkpoint: bool = True):

    self._policy_network = policy_network
    self._critic_network = critic_network
    self._f_network = f_network
    self._discount = discount
    self._clipping = clipping
    self._clipping_action = clipping_action
    self._bre_check_period = bre_check_period
    self._bre_check_num_actions = bre_check_num_actions

    # Development dataset for hyper-parameter selection.
    self._dev_dataset = dev_dataset
    self._dev_actions_dataset = self._sample_actions()

    # General learner book-keeping and loggers.
    self._counter = counter or counting.Counter()
    self._logger = logger or loggers.TerminalLogger('learner', time_delta=1.)

    # Necessary to track when to update tilde critic networks.
    self._num_steps = tf.Variable(0, dtype=tf.int32)

    self._use_tilde_critic = use_tilde_critic
    self._tilde_critic_network = tilde_critic_network
    self._tilde_critic_update_period = tilde_critic_update_period
    if use_tilde_critic and tilde_critic_update_period is None:
      raise ValueError('tilde_critic_update_period must be provided if '
                       'use_tilde_critic is True.')

    # Batch dataset and create iterator.
    self._iterator = iter(dataset)

    # Create optimizers if they aren't given.
    self._critic_optimizer = _optimizer_class(critic_optimizer_class)(
        critic_lr, beta1=critic_beta1, beta2=critic_beta2)

    if f_lr is not None:
      if f_lr_multiplier is not None:
        raise ValueError(f'Either f_lr ({f_lr}) or f_lr_multiplier '
                         f'({f_lr_multiplier}) must be None.')
    else:
      f_lr = f_lr_multiplier * critic_lr
    # Prevent unreasonable value in hyper-param search.
    f_lr = max(min(f_lr, 1e-2), critic_lr)

    self._f_optimizer = _optimizer_class(f_optimizer_class)(
        f_lr, beta1=f_beta1, beta2=f_beta2)

    # Regularization on network values.
    self._critic_regularizer = critic_regularizer
    self._f_regularizer = f_regularizer

    # Orthogonal regularization strength.
    self._critic_ortho_regularizer = critic_ortho_regularizer
    self._f_ortho_regularizer = f_ortho_regularizer

    # L2 regularization strength.
    self._critic_l2_regularizer = critic_l2_regularizer
    self._f_l2_regularizer = f_l2_regularizer

    # Expose the variables.
    self._variables = {
        'critic': self._critic_network.variables,
    }

    # Create a checkpointer object.
    self._checkpointer = None
    self._snapshotter = None

    if checkpoint:
      objects_to_save = {
          'counter': self._counter,
          'critic': self._critic_network,
          'f': self._f_network,
          'tilde_critic': self._tilde_critic_network,
          'critic_optimizer': self._critic_optimizer,
          'f_optimizer': self._f_optimizer,
          'num_steps': self._num_steps,
      }
      self._checkpointer = tf2_savers.Checkpointer(
          objects_to_save={k: v for k, v in objects_to_save.items()
                           if v is not None},
          time_delta_minutes=checkpoint_interval_minutes,
          checkpoint_ttl_seconds=_CHECKPOINT_TTL)
      self._snapshotter = tf2_savers.Snapshotter(
          objects_to_save={
              'critic': self._critic_network,
              'f': self._f_network,
          },
          time_delta_minutes=60.)

  def _check_bellman_residual_error(self, q_tm1, r_t, d_t, o_t):
    """Estimate of mean squared Bellman residual error."""
    # Ignore stochasticity in s'.
    # E[err(a')|s,a,s']^2 = E[\bar{err}]^2 - Var(err) / N.
    if (self._bre_check_period > 0 and
        tf.math.mod(self._num_steps, self._bre_check_period) == 0):
      q_t_sum = 0.
      q_t_sq_sum = 0.
      for _ in range(self._bre_check_num_actions):
        a_t = self._policy_network(o_t)
        if self._clipping_action:
          if not a_t.dtype.is_floating:
            raise ValueError(f'Action dtype ({a_t.dtype}) is not floating.')
          a_t = tf.clip_by_value(a_t, -1., 1.)
        q_t = self._critic_network(o_t, a_t)
        if q_tm1.shape != q_t.shape:
          raise ValueError(f'Shape of q_tm1 {q_tm1.shape.as_list()} does not '
                           f'match that of q_t {q_t.shape.as_list()}')
        q_t_sum += q_t
        q_t_sq_sum += tf.square(q_t)
      q_t_mean = q_t_sum / self._bre_check_num_actions
      q_t_var = q_t_sq_sum / self._bre_check_num_actions - tf.square(q_t_mean)
      d_t = tf.reshape(d_t, q_tm1.shape)
      r_t = tf.reshape(r_t, q_tm1.shape)
      td_error = r_t - (q_tm1 - d_t * q_t_mean)
      td_mse = tf.reduce_mean(tf.square(td_error))
      td_var = tf.reduce_mean(tf.square(d_t) * tf.reduce_mean(q_t_var))
      bre_mse = td_mse - td_var / self._bre_check_num_actions
    else:
      bre_mse = tf.constant(0., dtype=tf.float32)
    return bre_mse

  @tf.function
  def _step(self) -> Dict[str, tf.Tensor]:
    # Get data from replay (dropping extras if any). Note there is no
    # extra data here because we do not insert any into Reverb.
    sample = next(self._iterator)
    o_tm1, a_tm1, r_t, d_t, o_t = sample.data[:5]
    a_t = self._policy_network(o_t)
    if self._clipping_action:
      if not a_t.dtype.is_floating:
        raise ValueError(f'Action dtype ({a_t.dtype}) is not floating.')
      a_t = tf.clip_by_value(a_t, -1., 1.)

    # Cast the additional discount to match the environment discount dtype.
    discount = tf.cast(self._discount, dtype=d_t.dtype)
    d_t = discount * d_t

    if self._use_tilde_critic:
      tilde_td_error = _td_error(
          self._tilde_critic_network, o_tm1, a_tm1, r_t, d_t, o_t, a_t)[0]
      # In the same shape as tilde_td_error.
      f_regularizer = 0.25 * tf.square(tilde_td_error)
    else:
      # Scalar.
      tilde_td_error = 0.
      f_regularizer = self._f_regularizer

    with tf.GradientTape() as tape:
      td_error, q_tm1, q_t = _td_error(
          self._critic_network, o_tm1, a_tm1, r_t, d_t, o_t, a_t)
      f = self._f_network(o_tm1, a_tm1)
      if f.shape != td_error.shape:
        raise ValueError(f'Shape of f {f.shape.as_list()} does not '
                         f'match that of td_error {td_error.shape.as_list()}')

      moment = tf.reduce_mean(f * td_error)
      f_reg_loss = tf.reduce_mean(f_regularizer * tf.square(f))
      u = moment - f_reg_loss

      # Add regularizations.

      # Regularization on critic net output values.
      if self._critic_regularizer > 0.:
        critic_reg_loss = self._critic_regularizer * (
            tf.reduce_mean(tf.square(q_tm1)) +
            tf.reduce_mean(tf.square(q_t))) / 2.
      else:
        critic_reg_loss = 0.

      # Ortho regularization on critic net.
      if self._critic_ortho_regularizer > 0.:
        critic_ortho_reg_loss = (
            self._critic_ortho_regularizer *
            _orthogonal_regularization(self._critic_network))
      else:
        critic_ortho_reg_loss = 0.

      # Ortho regularization on f net.
      if self._f_ortho_regularizer > 0.:
        f_ortho_reg_loss = (
            self._f_ortho_regularizer *
            _orthogonal_regularization(self._f_network))
      else:
        f_ortho_reg_loss = 0.

      # L2 regularization on critic net.
      if self._critic_l2_regularizer > 0.:
        critic_l2_reg_loss = (
            self._critic_l2_regularizer *
            _l2_regularization(self._critic_network))
      else:
        critic_l2_reg_loss = 0.

      # L2 regularization on f net.
      if self._f_l2_regularizer > 0.:
        f_l2_reg_loss = (
            self._f_l2_regularizer *
            _l2_regularization(self._f_network))
      else:
        f_l2_reg_loss = 0.

      loss = (u + critic_reg_loss
              + critic_ortho_reg_loss - f_ortho_reg_loss
              + critic_l2_reg_loss - f_l2_reg_loss)

    bre_mse = self._check_bellman_residual_error(q_tm1, r_t, d_t, o_t)

    # Get trainable variables.
    critic_variables = self._critic_network.trainable_variables
    f_variables = self._f_network.trainable_variables

    # Compute gradients.
    gradients = tape.gradient(loss, critic_variables + f_variables)
    critic_gradients = gradients[:len(critic_variables)]
    f_gradients = gradients[len(critic_variables):]

    # Maybe clip gradients.
    if self._clipping:
      # # clip_by_global_norm
      # critic_gradients = tf.clip_by_global_norm(critic_gradients, 40.)[0]
      # f_gradients = tf.clip_by_global_norm(f_gradients, 40.)[0]

      # clip_by_value
      critic_gradients = [tf.clip_by_value(g, -1.0, 1.0)
                          for g in critic_gradients]
      f_gradients = [tf.clip_by_value(g, -1.0, 1.0) for g in f_gradients]

    # Apply critic gradients to minimize the loss.
    self._critic_optimizer.apply(critic_gradients, critic_variables)

    # Apply f gradients to maximize the loss.
    f_gradients = [-g for g in f_gradients]
    self._f_optimizer.apply(f_gradients, f_variables)

    if self._use_tilde_critic:
      if tf.math.mod(self._num_steps, self._tilde_critic_update_period) == 0:
        source_variables = self._critic_network.variables
        tilde_variables = self._tilde_critic_network.variables

        # Make online -> tilde network update ops.
        for src, dest in zip(source_variables, tilde_variables):
          dest.assign(src)
    self._num_steps.assign_add(1)

    # Losses to track.
    results = {
        'loss': loss,
        'u': u,
        'f_reg_loss': f_reg_loss,
        'td_mse': tf.reduce_mean(tf.square(td_error)),
        'f_ms': tf.reduce_mean(tf.square(f)),
        'moment': moment,
        'global_steps': tf.convert_to_tensor(self._num_steps),
        'bre_mse': bre_mse,
    }
    if self._use_tilde_critic:
      results.update({
          'tilde_td_mse': tf.reduce_mean(tf.square(tilde_td_error))})
    if self._critic_regularizer > 0.:
      results.update({'critic_reg_loss': critic_reg_loss})
    if self._critic_ortho_regularizer > 0.:
      results.update({'critic_ortho_reg_loss': critic_ortho_reg_loss})
    if self._f_ortho_regularizer > 0.:
      results.update({'f_ortho_reg_loss': f_ortho_reg_loss})
    if self._critic_l2_regularizer > 0.:
      results.update({'critic_l2_reg_loss': critic_l2_reg_loss})
    if self._f_l2_regularizer > 0.:
      results.update({'f_l2_reg_loss': f_l2_reg_loss})
    return results

  def num_steps(self):
    return self._num_steps.numpy()

  def step(self):
    # Run the learning step.
    fetches = self._step()

    # Update our counts and record it.
    counts = self._counter.increment(steps=1)
    fetches.update(counts)

    # Checkpoint and attempt to write the logs.
    if self._checkpointer is not None:
      self._checkpointer.save()
    if self._snapshotter is not None:
      self._snapshotter.save()
    self._logger.write(fetches)

  def get_variables(self, names: List[str]) -> List[List[np.ndarray]]:
    return [tf2_utils.to_numpy(self._variables[name]) for name in names]

  def _sample_actions(self):
    if self._dev_dataset is not None:
      a_ts = []
      for sample in self._dev_dataset:
        o_t = sample.data[4]
        a_t = self._policy_network(o_t)
        if self._clipping_action:
          if not a_t.dtype.is_floating:
            raise ValueError(f'Action dtype ({a_t.dtype}) is not floating.')
          a_t = tf.clip_by_value(a_t, -1., 1.)
        a_ts.append(a_t)
      return tf.data.Dataset.from_tensor_slices(a_ts)
    else:
      return None

  def dev_td_error_and_f_values(self):
    """Return TD error and f values from the dev dataset."""
    td_errors = []
    fs = []
    for sample, a_t in zip(self._dev_dataset, self._dev_actions_dataset):
      o_tm1, a_tm1, r_t, d_t, o_t = sample.data[:5]

      # Cast the additional discount to match the environment discount dtype.
      discount = tf.cast(self._discount, dtype=d_t.dtype)
      d_t = discount * d_t

      td_error, _, _ = _td_error(
          self._critic_network, o_tm1, a_tm1, r_t, d_t, o_t, a_t)
      f = self._f_network(o_tm1, a_tm1)
      if f.shape != td_error.shape:
        raise ValueError(f'Shape of f {f.shape.as_list()} does not '
                         f'match that of td_error {td_error.shape.as_list()}')

      td_errors.append(td_error)
      fs.append(f)
    td_errors = tf.concat(td_errors, axis=0)
    fs = tf.concat(fs, axis=0)
    return {
        'td_errors': td_errors,
        'fs': fs,
        'global_steps': tf.convert_to_tensor(self._num_steps),
    }

class DeepGMMLearner(DeepGMMLearnerBase):
  r"""Deep GMM learner.

  Reference:
  A. Bennett, N. Kallus, and T. Schnabel. Deep generalized method of moments for
  instrumental variable analysis. In Advances in Neural Information Processing
  Systems 32, pages 3564–3574. 2019.

  Open source code: https://github.com/CausalML/DeepGMM

  Objective (with 0.25 scaling on the original value)
  argmin_q sup_f U
  U = E[f * (R - Q_tm1 + gamma * Q_t)]
      - 0.25 * E[f^2 * (R - \tilde{Q}_tm1 + gamma * \tilde{Q}_t)^2]

  Additional optional losses include:
    - L2 regularization on Q values.
    - Orthogonal loss for Q net.
    - Orthogonal loss for f net.

  If not using the tilde Q net, one can specify a L2 regularizer for f
  values with a constant strength to replace the second term in U.
  """

  def __init__(self,
               policy_network: snt.Module,
               critic_network: snt.Module,
               f_network: snt.Module,
               discount: float,
               dataset: tf.data.Dataset,
               tilde_critic_network: snt.Module,
               tilde_critic_update_period: int = 1,
               critic_optimizer_class: str = 'OAdam',
               critic_lr: float = 1e-4,
               critic_beta1: float = 0.5,  # From open-sourced code.
               critic_beta2: float = 0.9,  # From open-sourced code.
               f_optimizer_class: str = 'OAdam',
               f_lr: float = None,  # Either f_lr or f_lr_multiplier must be
                                    # None.
               f_lr_multiplier: Optional[float] = 1.0,
               f_beta1: float = 0.5,  # From open-sourced code.
               f_beta2: float = 0.9,  # From open-sourced code.
               checkpoint_interval_minutes: int = 10.0,
               clipping: bool = True,
               clipping_action: bool = True,
               bre_check_period: int = 0,  # Bellman residual error check.
               bre_check_num_actions: int = 0,  # Number of sampled actions.
               dev_dataset: tf.data.Dataset = None,
               counter: counting.Counter = None,
               logger: loggers.Logger = None,
               checkpoint: bool = True):
    super().__init__(
        policy_network=policy_network,
        critic_network=critic_network,
        f_network=f_network,
        discount=discount,
        dataset=dataset,
        use_tilde_critic=True,
        tilde_critic_network=tilde_critic_network,
        tilde_critic_update_period=tilde_critic_update_period,
        critic_optimizer_class=critic_optimizer_class,
        critic_lr=critic_lr,
        critic_beta1=critic_beta1,
        critic_beta2=critic_beta2,
        f_optimizer_class=f_optimizer_class,
        f_lr=f_lr,
        f_lr_multiplier=f_lr_multiplier,
        f_beta1=f_beta1,
        f_beta2=f_beta2,
        checkpoint_interval_minutes=checkpoint_interval_minutes,
        clipping=clipping,
        clipping_action=clipping_action,
        bre_check_period=bre_check_period,
        bre_check_num_actions=bre_check_num_actions,
        dev_dataset=dev_dataset,
        counter=counter,
        logger=logger,
        checkpoint=checkpoint)

class AdversarialSEMLearner(DeepGMMLearnerBase):
  """Adversarial SEM learner.

  Reference:
  L. Liao, Y. L. Chen, Z. Yang, B. Dai, Z. Wang and M. Kolar, 2020. Provably
  efficient neural estimation of structural equation model: An adversarial
  approach. arXiv preprint arXiv:2007.01290.
  """

  def __init__(self,
               policy_network: snt.Module,
               critic_network: snt.Module,
               f_network: snt.Module,
               discount: float,
               dataset: tf.data.Dataset,
               critic_optimizer_class: str = 'OAdam',
               critic_lr: float = 1e-4,
               critic_beta1: float = 0.,
               critic_beta2: float = 0.01,
               f_optimizer_class: str = 'OAdam',
               f_lr: float = None,  # Either f_lr or f_lr_multiplier must be
                                    # None.
               f_lr_multiplier: Optional[float] = 1.0,
               f_beta1: float = 0.,
               f_beta2: float = 0.01,
               critic_regularizer: float = 0.0,
               f_regularizer: float = 0.5,  # From paper.
               critic_l2_regularizer: float = 1e-4,
               f_l2_regularizer: float = 1e-4,
               checkpoint_interval_minutes: int = 10.0,
               clipping: bool = True,
               clipping_action: bool = True,
               bre_check_period: int = 0,  # Bellman residual error check.
               bre_check_num_actions: int = 0,  # Number of sampled actions.
               dev_dataset: tf.data.Dataset = None,
               counter: counting.Counter = None,
               logger: loggers.Logger = None,
               checkpoint: bool = True):
    super().__init__(
        policy_network=policy_network,
        critic_network=critic_network,
        f_network=f_network,
        discount=discount,
        dataset=dataset,
        use_tilde_critic=False,
        critic_optimizer_class=critic_optimizer_class,
        critic_lr=critic_lr,
        critic_beta1=critic_beta1,
        critic_beta2=critic_beta2,
        f_optimizer_class=f_optimizer_class,
        f_lr=f_lr,
        f_lr_multiplier=f_lr_multiplier,
        f_beta1=f_beta1,
        f_beta2=f_beta2,
        critic_regularizer=critic_regularizer,
        f_regularizer=f_regularizer,
        critic_l2_regularizer=critic_l2_regularizer,
        f_l2_regularizer=f_l2_regularizer,
        checkpoint_interval_minutes=checkpoint_interval_minutes,
        clipping=clipping,
        clipping_action=clipping_action,
        bre_check_period=bre_check_period,
        bre_check_num_actions=bre_check_num_actions,
        dev_dataset=dev_dataset,
        counter=counter,
        logger=logger,
        checkpoint=checkpoint)

class AGMMLearner(DeepGMMLearnerBase):
  """AGMM learner.

  Reference:
  Dikkala, N., Lewis, G., Mackey, L. and Syrgkanis, V., 2020. Minimax estimation
  of conditional moment models. Advances in Neural Information Processing
  Systems, 33.

  Open source code: https://github.com/microsoft/AdversarialGMM
  """

  def __init__(self,
               policy_network: snt.Module,
               critic_network: snt.Module,
               f_network: snt.Module,
               discount: float,
               dataset: tf.data.Dataset,
               critic_optimizer_class: str = 'OAdam',
               critic_lr: float = 1e-4,
               critic_beta1: float = 0.,  # From open-sourced code.
               critic_beta2: float = 0.01,  # From open-sourced code.
               f_optimizer_class: str = 'OAdam',
               f_lr: float = None,  # Either f_lr or f_lr_multiplier must be
                                    # None.
               f_lr_multiplier: Optional[float] = 1.0,
               f_beta1: float = 0.,  # From open-sourced code.
               f_beta2: float = 0.01,  # From open-sourced code.
               f_regularizer: float = 1.0,  # From open-sourced code.
               critic_l2_regularizer: float = 1e-4,
               f_l2_regularizer: float = 1e-4,
               checkpoint_interval_minutes: int = 10.0,
               clipping: bool = True,
               clipping_action: bool = True,
               bre_check_period: int = 0,  # Bellman residual error check.
               bre_check_num_actions: int = 0,  # Number of sampled actions.
               dev_dataset: tf.data.Dataset = None,
               counter: counting.Counter = None,
               logger: loggers.Logger = None,
               checkpoint: bool = True):
    super().__init__(
        policy_network=policy_network,
        critic_network=critic_network,
        f_network=f_network,
        discount=discount,
        dataset=dataset,
        use_tilde_critic=False,
        critic_optimizer_class=critic_optimizer_class,
        critic_lr=critic_lr,
        critic_beta1=critic_beta1,
        critic_beta2=critic_beta2,
        f_optimizer_class=f_optimizer_class,
        f_lr=f_lr,
        f_lr_multiplier=f_lr_multiplier,
        f_beta1=f_beta1,
        f_beta2=f_beta2,
        f_regularizer=f_regularizer,
        critic_l2_regularizer=critic_l2_regularizer,
        f_l2_regularizer=f_l2_regularizer,
        checkpoint_interval_minutes=checkpoint_interval_minutes,
        clipping=clipping,
        clipping_action=clipping_action,
        bre_check_period=bre_check_period,
        bre_check_num_actions=bre_check_num_actions,
        dev_dataset=dev_dataset,
        counter=counter,
        logger=logger,
        checkpoint=checkpoint)


from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render
import re

class SessionFilter(MiddlewareMixin):
    def process_request(self,request):
        if (request.session.get('user_id') or request.session.get('customer_id')):
            return None
        else:
            #寰呮坊鍔犻�昏緫锛氭暟娆¤緭鍏ュ瘑鐮侀敊璇嫆缁濈櫥闄�
            out = ['ad/regist',
                    'ad/login',]#域名过滤列表
            f = None
            for o in out:
                n = re.search(o,request.get_full_path().lower())
                if n:
                    f = n
            if  f:
                return None
            else:
                return render(request, 'login.html')


import pytest

from CBLClient.FileLogging import FileLogging
from keywords.utils import log_info

log_level_dict = {
    "debug": 0,
    "verbose": 1,
    "info": 2,
    "warning": 3,
    "error": 4,
}

@pytest.mark.listener
@pytest.mark.database
@pytest.mark.parametrize("log_level, plain_text, max_size, max_rotate_count", [
    pytest.param("verbose", True, 1000 * 512 * 1000, 1000, marks=pytest.mark.ce_sanity),
    ("verbose", False, 0, 0),
    ("info", False, 0, 0),
    ("warning", False, 0, 0),
    ("error", False, 0, 0),
    ("debug", True, 3 * 512 * 1000, 3),
    ("verbose", True, 4 * 512 * 1000, 4),
    ("info", True, 5 * 512 * 1000, 5),
    ("warning", True, 6 * 512 * 1000, 6),
    ("error", True, 7 * 512 * 1000, 7),
    pytest.param("verbose", True, 1000 * 512 * 1000, 1000, marks=pytest.mark.sanity)
])
def test_file_logging(params_from_base_test_setup, log_level, plain_text, max_size, max_rotate_count):
    base_url = params_from_base_test_setup["base_url"]
    liteserv_version = params_from_base_test_setup["liteserv_version"]
    liteserv_platform = params_from_base_test_setup["liteserv_platform"]
    log_info(liteserv_platform)
    log_obj = FileLogging(base_url)

    log_directory = log_obj.configure(log_level=log_level, plain_text=plain_text,
                                      max_rotate_count=max_rotate_count, max_size=max_size)
    log_info("logs are enabled at: {}".format(log_directory))
    if liteserv_version < "2.5.0":
        pytest.skip('This test cannot run with CBL version below 2.5.0')

    cbl_log_level = log_obj.get_log_level()
    cbl_plain_text = log_obj.get_plain_text_status()
    cbl_max_size = log_obj.get_max_size()
    cbl_max_rotate_count = log_obj.get_max_rotate_count()
    log_info("log_level {}, max_size {}, max_rotate_count {}, plain_text_status {}".format(cbl_log_level, cbl_max_size,
                                                                                cbl_max_rotate_count,
                                                                                cbl_plain_text))

    if max_size == 0 and "c-" not in liteserv_platform:
        max_size = 524288

    if max_rotate_count == 0 and "c-" not in liteserv_platform:
        max_rotate_count = 1

    assert log_level_dict[log_level] == cbl_log_level, "Actual log levels is not matching with the set log level"
    assert plain_text == cbl_plain_text, "Actual plain text status is not matching with set plain text status"
    assert max_size == cbl_max_size, "Actual max_size is not matching with set max_size"
    assert max_rotate_count == cbl_max_rotate_count, "Actual max_rotate_count is not matching with set max_rotate_count"


import dis
import random

import pygame
from pygame import event

pygame.init()

pygame.display.set_caption('Snake game first trail')
display_width = 400
display_height = 400
snake_block = 10
food_block = snake_block
display_game = pygame.display.set_mode((display_width, display_height))

white = (255, 255, 255)
blue = (0, 0, 255)
red = (255, 0, 0)
green = (0, 255, 0)

clock = pygame.time.Clock()

score_font = pygame.font.SysFont("cosmeticians", 25)

def my_snake(snake_list, snake_block):
    for x in snake_list:
        pygame.draw.rect(display_game, blue, [x[0], x[1], snake_block, snake_block])

def def_game_close(game_close, snake_lenght):
    while game_close == True:
        display_game.fill(blue)
        value = score_font.render("Your Score: " + str(snake_lenght - 1), True, red)
        value1 = score_font.render("Press A-Play Again or Q-Quit", True, red)
        display_game.blit(value, [0, 0])
        display_game.blit(value1, [0, 100])
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_a:
                    game()
                if event.key == pygame.K_q:
                    game_close = False
                    return True

def game():
    game_over = False
    game_close = False
    snake_speed = 3
    snake_lenght = 1
    x1 = 0
    y1 = 0

    x1_change = 0
    y1_change = 0
    snake_list = []

    foodx = round(random.randrange(0, display_width - snake_block) / 10) * 10
    foody = round(random.randrange(0, display_width - snake_block) / 10) * 10

    event_key_previous = 0

    while not game_over:
        game_over = def_game_close(game_close, snake_lenght)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            if event.type == pygame.KEYDOWN and event_key_previous != event.key:
                event_key_previous = event.key
                if event.key == pygame.K_DOWN:
                    x1_change = 0
                    y1_change += 10
                if event.key == pygame.K_UP:
                    x1_change = 0
                    y1_change -= 10
                if event.key == pygame.K_RIGHT:
                    x1_change += 10
                    y1_change = 0
                if event.key == pygame.K_LEFT:
                    x1_change -= 10
                    y1_change = 0

        if y1 > display_height or y1 < 0 or x1 < 0 or x1 > display_width:
            game_close = True

        x1 += x1_change
        y1 += y1_change
        display_game.fill(white)

        pygame.draw.rect(display_game, red, [foodx, foody, food_block, food_block])

        snake_Head = []
        snake_Head.append(x1)
        snake_Head.append(y1)
        snake_list.append(snake_Head)
        if len(snake_list) > snake_lenght:
            del snake_list[0]

        # ако се върне назад умира
        for x in snake_list[:-1]:
            if x == snake_Head:
                game_close = True

        # рисува змията
        my_snake(snake_list, snake_block)

        # Изяжда храната
        if x1 == foodx and y1 == foody:
            foodx = round(random.randrange(0, display_width - snake_block) / 10) * 10
            foody = round(random.randrange(0, display_width - snake_block) / 10) * 10
            snake_speed += 1
            snake_lenght += 1

        value = score_font.render("Your Score: " + str(snake_lenght - 1), True, green)
        display_game.blit(value, [0, 0])

        pygame.display.update()
        clock.tick(snake_speed)

    pygame.quit()
    quit()

game()


import os
import pandas as pd
import numpy as np
import requests
import pickle
import nltk

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from collections import Counter

def cleanhtml(raw_html):
  cleantext = re.sub(CLEANR, '', raw_html)
  return cleantext

def getGameFromSteam(appid):
	response = requests.get(rf"https://store.steampowered.com/api/appdetails?appids={appid}")

	if response and "data" in response.json()[f"{appid}"]:
		return response.json()[f"{appid}"]["data"]
	else:
		return None

def loadObject(PATH):
	with open(PATH, 'rb') as inp:
		new_game = pickle.load(inp)
	return new_game

def saveObject(obj, USERS_PATH):
	with open(USERS_PATH, 'wb') as file:
		pickle.dump(obj, file, pickle.HIGHEST_PROTOCOL)
	return True

def initializeDataset(PATH):
	try:
		dataset = pd.read_csv(PATH)
	except:
		with ZipFile('dataset.zip', 'r') as zip_ref:
			zip_ref.extractall('')
		dataset = pd.read_csv(PATH)
	dataset.dropna(inplace=True, subset=['game_description', 'name', 'genre', 'popular_tags', 'developer', 'publisher'])

	# splittig the genre string to create a list of genre
	pop_tags_col_index = np.where(dataset.columns.values == "popular_tags")[0][0]

	dataset["genre"] = dataset.genre.str.split(',')
	dataset["popular_tags"] = dataset.popular_tags.str.split(',')

	if 'priority' not in dataset.columns:
		dataset["priority"] = 0

	# extracting percentage of positive review from dataset
	dataset["positive_review_percentage"] = dataset.all_reviews.str.extract(r'(\d+(?=%))')

	# extracting total number of reviews from dataset
	dataset["review_count"] = dataset.all_reviews.str.replace(",", "")
	dataset["review_count"] = dataset.review_count.str.extract(r'(\d+(?=))').replace(np.NaN, 0)
	dataset["review_count"] = dataset.review_count.astype(int)

	dataset = dataset[dataset.review_count > 300]

	# Extracting appid from dataset
	dataset["appid"] = dataset.url.str.extract(r'(\d+(?=/))')
	dataset.dropna(inplace=True, subset=["appid"])

	dataset["appid"] = dataset["appid"].astype(int)
	dataset["steam_appid"] = dataset["appid"].astype(int)

	dataset["positive_review_count"] = dataset.all_reviews.str.extract(r'(\d+(?=))')

	dataset = dataset.drop_duplicates(subset=['steam_appid'])
	dataset.set_index('appid', inplace=True)

	saveObject(dataset, 'dataset.pkl')
	return dataset

def loadDataset(PATH, DATASET_FILE='steam_games.csv'):
	try:
		with open(PATH, 'rb') as inp:
			dataset = pickle.load(inp)
		print("[ Processed Dataset Found ! Loading dataset ]")
		return dataset
	except:
		print("[ !! Processed Dataset NOT found !! ]")
		return initializeDataset(DATASET_FILE)

def countCategory(dataset, col):
	tags = dataset.dropna(subset=[col]) # dropping all games with where tags is na
	tags = [j for sub in tags[col].values for j in sub]
	tags = Counter(tags) # generating a counter of all tags

	return tags

def allCharsInString(string, chars):
	curr = 0
	for char in string:
		if curr == len(chars):
			return True
		if char == chars[curr]:
			curr += 1
	return curr == len(chars)

def hasTags(arr, tags):
	if type(arr) == float:
		return False

	for tag in tags:
		if tag not in arr:
			return False
	return True

def excludeTags(arr, tags):
	for tag in tags:
		if hasTags(arr, [tag]):
			return False
	return True

def searchGameByName(dataset, name):
	resultBySubstr = np.vectorize(lambda x: name.lower() in x.lower())(dataset["name"].dropna().values)
	resultByAllChar = np.vectorize(lambda x: allCharsInString(x.lower(), name.lower()))(dataset["name"].dropna().values)

	data = dataset[dataset["name"].isna()==False]

	topResults = data[resultBySubstr].sort_values(by=['priority', 'positive_review_percentage', 'review_count'], ascending=False)
	lastResults =data[resultByAllChar].sort_values(by=['priority', 'positive_review_percentage', 'review_count'], ascending=False)

	return pd.concat([topResults, lastResults])

def searchGameByTagAndName(dataset, tags, name, exclusions=[]):
	if tags==[] and name == '' and exclusions==[]:
		return dataset.sort_values(by=['priority', 'review_count', 'positive_review_percentage'], ascending=False)

	data = searchGameByName(dataset, name)

	filterTagsFunc = np.vectorize(lambda x: hasTags(x, tags) and excludeTags(x, exclusions))

	# resultByGenre = filterTagsFunc(data["genre"].values)
	# resultByPopularTag = filterTagsFunc(data["popular_tags"].values)

	# topResults = data[resultByPopularTag]
	# lastResults = data[resultByGenre]

	# result = pd.concat([topResults, lastResults]).drop_duplicates(subset=['name'])

	result = data[filterTagsFunc(data["popular_tags"].values)].drop_duplicates(subset='name')

	return result.sort_values(by=["priority"], ascending=[False])

def getLikedGames(dataset, user):
	return dataset[dataset.steam_appid.isin( user.gamesLiked.keys() )]

def searchGameByDeveloper(dataset, developer):
	resultBySubstr = np.vectorize(lambda x: developer.lower() in x.lower())(dataset["developer"].dropna().values)
	resultByAllChar = np.vectorize(lambda x: allCharsInString(x.lower(), developer.lower()))(dataset["developer"].dropna().values)
	data = dataset[dataset["developer"].isna()==False]

	return pd.concat([data[resultBySubstr], data[resultByAllChar]])

def getSimilarGames(dataset, similarity_matrix, new_games, appid, count):
    index = new_games[new_games.name == dataset.name[appid]].index[0]
    distances = similarity_matrix[index]
    similar_games = sorted( list(enumerate(distances)) ,reverse = True,key = lambda x : x[1])[1:count]

    result = dataset.loc[new_games.iloc[ list(map(lambda x: x[0], similar_games)) ]['steam_appid'].astype(int)]
    return result

def getSimilarGamesByList(dataset, similarity_matrix, new_games, arr, count):
	res = []
	for appid in arr:
		index = new_games[new_games.name == dataset.name[appid]].index[0]
		distances = similarity_matrix[index]
		similar_games = sorted( list(enumerate(distances)) ,reverse = True,key = lambda x : x[1])[1:count]
		res.extend(similar_games)

	res = sorted( res ,reverse = True,key = lambda x : x[1])
	result = dataset.loc[new_games.iloc[ list(map(lambda x: x[0], res)) ]['steam_appid'].astype(int)]
	return result

def createSimilarityMatrix(new_games):
	try:
		similarity = loadObject("data/similarity.pkl")
		print("[ similarity matrix found! ]")

	except (OSError, IOError) as e:

		print("[ !!similarity matrix not found!! creating now ] ")

		cv = CountVectorizer(max_features = 5000, stop_words = 'english')
		vectors = cv.fit_transform(new_games.tags).toarray()
		similarity = cosine_similarity(vectors)

		pickle.dump(similarity,open('data/similarity.pkl','wb'), protocol=4)

	print("[ Similarity matrix loaded ]")
	return similarity

new_games = loadObject("data/new_games.pkl")
similarity_matrix = createSimilarityMatrix(new_games)

print("[ Recommendation system loaded! ]")

if __name__ == "__main__":
	dataset = loadDataset("dataset.pkl")
	print(dataset.head().name)
	# print(getSimilarGamesByList(dataset, similarity_matrix, new_games, [379720, 578080],10).name)


from base64 import encode
import os.path
from flask import Blueprint, jsonify, render_template, url_for, send_file
from flask_login import login_required, current_user
from ..controller.ordemDeServicoController import (
    abertura_OrdemDeServico,
    registra_orcamento,
    aceita_ordemDeServico,
    avaliar_ordemServico,
    atender_ordemDeServico,
    concluir_ordemDeServico,
    finalizar_ordemDeServico,
    relatorio_ordemDeServico,
    busca_personalizada_ordemDeServico,
    relatorio_financeiro,
    altera_orcamento_ordemDeServico,
    total_ordemDeServico_status,
    busca_os_status_home,
    cancelar_ordemDeServico
)

ordem = Blueprint('ordem', __name__)

@ordem.route('/OrdemDeServico/Abertura', methods=['POST'])
@login_required
def abre_ordemDeServico():
    return abertura_OrdemDeServico(current_user.id)

@ordem.route('/OrdemDeServico/Alterar/<int:codigo>', methods=['POST'])
@login_required
def altera_route_financeiro(codigo):
    return altera_orcamento_ordemDeServico(codigo, current_user.id)

@ordem.route('/OrdemDeServico/Aceita/<int:codigo>', methods=['POST'])
@login_required
def aceit_ordemDeServico(codigo):
    tipo_usuario_logado = current_user.tipoFuncionario.name
    if tipo_usuario_logado == 'GERENTE' or tipo_usuario_logado == 'MECANICO':
        return aceita_ordemDeServico(codigo, current_user.id)
    else:
        return jsonify({'msg': "Usuário sem permissão"}), 401

@ordem.route('/OrdemDeServico/RegistraOrcamento/<int:codigo>', methods=['POST'])
@login_required
def regis_ordemDeServico(codigo):
    tipo_usuario_logado = current_user.tipoFuncionario.name
    if tipo_usuario_logado == 'GERENTE' or tipo_usuario_logado == 'MECANICO':
        return registra_orcamento(codigo, current_user.id)
    else:
        return jsonify({'msg': "Usuário sem permissão"}), 401

@ordem.route('/OrdemDeServico/Cancelar/<int:codigo>', methods=['POST'])
@login_required
def cancelar_ordemDeServico_route(codigo):
    return cancelar_ordemDeServico(codigo, current_user.id)

@ordem.route('/OrdemDeServico/Avaliar/<int:codigo>', methods=['POST'])
@login_required
def avalia_ordemDeServico(codigo):
    return avaliar_ordemServico(codigo, current_user.id)

@ordem.route('/OrdemDeServico/Atender/<int:codigo>', methods=['POST'])
@login_required
def atende_ordemDeServico(codigo):
    tipo_usuario_logado = current_user.tipoFuncionario.name
    if tipo_usuario_logado == 'GERENTE' or tipo_usuario_logado == 'MECANICO':
        return atender_ordemDeServico(codigo, current_user.id)
    else:
        return jsonify({'msg': "Usuário sem permissão"}), 401

@ordem.route('/OrdemDeServico/Concluir/<int:codigo>', methods=['POST'])
@login_required
def conclui_ordemDeServico(codigo):
    tipo_usuario_logado = current_user.tipoFuncionario.name
    if tipo_usuario_logado == 'GERENTE' or tipo_usuario_logado == 'MECANICO':
        return concluir_ordemDeServico(codigo, current_user.id)
    else:
        return jsonify({'msg': "Usuário sem permissão"}), 401

@ordem.route('/OrdemDeServico/Finalizar/<int:codigo>', methods=['POST'])
@login_required
def finaliza_ordemDeServico(codigo):
    return finalizar_ordemDeServico(codigo, current_user.id)

@ordem.route('/OrdemDeServico/Relatorio/OrdemDeServico', methods=['POST'])
@login_required
def relatorio_route_ordemDeServico():
    return relatorio_ordemDeServico()

@ordem.route('/OrdemDeServico/BuscaPersonalizada', methods=['POST'])
@login_required
def busca_route_ordemDeServico():
    return busca_personalizada_ordemDeServico()

@ordem.route('/OrdemDeServico/Relatorio/Financeiro', methods=['POST'])
@login_required
def relatorio_route_financeiro():
    tipo_usuario_logado = current_user.tipoFuncionario.name
    if tipo_usuario_logado == 'GERENTE':
        return relatorio_financeiro()
    else:
        return jsonify({'msg': "Usuário sem permissão"}), 401

@ordem.route('/OrdemDeServico/Home/Totais', methods=['POST'])
@login_required
def total_os_route():
    return total_ordemDeServico_status(current_user)

@ordem.route('/OrdemDeServico/Home/BuscaOS', methods=['POST'])
@login_required
def busca_os_home():
    return busca_os_status_home(current_user)