#DAMENIK THAQI
#DAMENIK.THAQI50@myhunter.cuny.edu

import matplotlib.pyplot as plt
import numpy as np

fileName = input('Enter file name: ')
threshold = 0.75

img = plt.imread(fileName)

count = 0
countPixel = 0

for i in range(img.shape[0]): # accessing rows
    for q in range(img.shape[1]): # accessing columns
        countPixel+=1

for i in range(img.shape[0]): # accessing rows
    for a in range(img.shape[1]): # accessing columns
        if (img[i, a, 0] > threshold) and (img[i, a, 1] > threshold) and (img[i, a, 2] > threshold): # if any of the color channels, r g b are < threshold. Each pixel is accessed & checked with the for loop [row,col, all color channels].
            count+=1 # we count up by one

print('Number of pixels whose rgb are smaller than threshold in original figure: ', count)
print('Percent of those pixels: ', (count/countPixel)*100, '%')


import os
import cv2
import pydload
import logging
from . import data_utils
from .craft_model import build_craft_vgg_model

model_links = {
                'generic-english': 'https://github.com/bedapudi6788/keras-craft/releases/download/checkpoint-release/generic-english'
            }

model_alternate_names = {
    'default': 'generic-english'
}

class Detector():
    model = build_craft_vgg_model()
    def __init__(self, model_name = 'default'):
        if model_name in model_alternate_names:
            model_name = model_alternate_names[model_name]

        home = os.path.expanduser("~")
        checkpoint_dir = os.path.join(home, '.keras_craft_' + model_name)
        checkpoint_path = os.path.join(checkpoint_dir, 'checkpoint')

        if not os.path.exists(checkpoint_dir):
            os.mkdir(checkpoint_dir)

        if not os.path.exists(checkpoint_path):
            print('Downloading checkpoint', model_links[model_name], 'to', checkpoint_path)
            pydload.dload(url=model_links[model_name], save_to_path=checkpoint_path, max_time=None)

        Detector.model.load_weights(checkpoint_path)

    def detect(self, image_paths, max_width=720, max_height=None, batch_size=8, return_cropped_images=False):
        '''
        Function for reading image(s), running predictions.

        :param image_paths: single or list of opencv images or image file paths
        :max_width: images are scaled to this width keeping the aspect ratio.
        :max_height: either max_width or max_height should be used.

        :return: list of numpy arrays. each array is all the detected boxes in the corresponding image.
        '''
        return_single = False

        if not isinstance(image_paths, list):
            logging.warn('Batch for better performance')
            return_single = True
            image_paths = [image_paths]

        if not (max_width or max_height):
            logging.warn("Both max_width and max_height are set to None. Only do this if you are aware of the implications.")

        image_paths = [cv2.imread(im) for im in image_paths]

        loaded_images, scales_before_padding, shapes_before_padding = data_utils.read_images(image_paths, max_width = max_width, max_height = max_height)

        model_predictions = Detector.model.predict(loaded_images, batch_size=batch_size)

        boxes = data_utils.keras_pred_to_boxes(model_predictions, shapes=shapes_before_padding, scales=scales_before_padding)

        if return_cropped_images:
            cropped_images = []

            for image, _boxes in zip(image_paths, boxes):
                cropped_images.append([data_utils.get_crop_in_box(image, box) for box in _boxes])

            if return_single:
                cropped_images = cropped_images[0]
                boxes = boxes[0]

            return boxes, cropped_images

        if return_single:
            boxes = boxes[0]

        return boxes


from typing import Optional
from fastapi import FastAPI
import uvicorn

app = FastAPI()

fake_itmes = [{'item_name': 'baz'}, {'item_name': 'bar'}, {'item_name': 'boo'}]

@app.get('/items/{item_id}')
def read_items(item_id: str, q: Optional[str] = None):
    if q:
        return {'item_id': item_id, "q":q}
    return {"item_id": item_id}

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)


import json, re, requests
from action import Action
from const import CARD_USAGE_LEAGUES, CARD_USAGE_URL, DETA_KEY, ON_DEMAND_ISR_URL
from datetime import datetime, timedelta
from deta import Deta
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from itertools import chain
from pytz import timezone
from typing import Optional
from utility import get_cards, search_card

app = FastAPI()

origins = ["*"]
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

db = Deta(DETA_KEY).Base("card-usages")

with open("kitty_card_ids.json", "r") as k:
    ids = json.load(k)
ids_reversed = {value: key for key, value in ids.items()}
usage_caches = {}

@app.get("/cards")
def get_card(name: Optional[str] = None, id: Optional[str] = None, stringify: Optional[bool] = False):
    if not name and not id:
        return {"result": get_cards()}
    else:
        return {"result": search_card(stringify, name, id)}

@app.get("/usages")
def get_card_usages(league: Optional[str] = None, target_date: Optional[str] = None):
    date = validate_date(datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d")) if not target_date else target_date

    if date in usage_caches.keys():
        return {"result": usage_caches[date] if not league else usage_caches[date][league]}

    usages = db.get(date)["usages"]
    usage_caches[date] = usages

    if league:
        usages = {league: usages[league]}

    for usage in usages.values():
        for tier, value in usage.items():
            usage[tier] = {
                "neutral": [x for x in value if ids_reversed[x][0] == "N"],
                "swarm": [x for x in value if ids_reversed[x][0] == "S"],
                "winter": [x for x in value if ids_reversed[x][0] == "W"],
                "shadowfen": [x for x in value if ids_reversed[x][0] == "F"],
                "ironclad": [x for x in value if ids_reversed[x][0] == "I"]
            }

    return {"result": usages if not league else usages[league]}

@app.get("/usage-ranks")
def get_card_usage_ranks(league: Optional[str] = None, target_date: Optional[str] = None):
    kingdoms = ["neutral", "swarm", "winter", "shadowfen", "ironclad"]

    result = {x: {} for x in CARD_USAGE_LEAGUES} if not league else {league: {}}
    date = validate_date(datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d") if not target_date else target_date)
    usages = get_card_usages(league, date)["result"]

    for key in result.keys():
        ranks = {x: [] for x in kingdoms}

        for value in (usages[key].values() if not league else usages.values()):
            for kingdom in kingdoms:
                ranks[kingdom][:0] = value[kingdom]

        for kingdom, cards in ranks.items():
            for card in cards:
                result[key][card] = ranks[kingdom].index(card) + 1

    return {"result": result if not league else result[league]}

@app.get("/usage-changes")
def get_card_usage_changes(league: Optional[str] = None, target_date: Optional[str] = None):
    kingdoms = ["neutral", "swarm", "winter", "shadowfen", "ironclad"]

    result = {x: {} for x in CARD_USAGE_LEAGUES} if not league else {league: {}}
    date = validate_date(datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d") if not target_date else target_date)
    now = get_card_usages(league, date)["result"]
    ago = get_card_usages(league, subtract_a_day(date))["result"]

    if now == ago: #아직 업데이트되지 않았을 때
        ago = get_card_usages(league, subtract_a_day(subtract_a_day(date)))["result"]

    for key in result.keys():
        now_list = {x: [] for x in kingdoms}
        ago_list = {x: [] for x in kingdoms}

        for now_value in (now[key].values() if not league else now.values()):
            for kingdom in kingdoms:
                now_list[kingdom][:0] = now_value[kingdom]

        for ago_value in (ago[key].values() if not league else ago.values()):
            for kingdom in kingdoms:
                ago_list[kingdom][:0] = ago_value[kingdom]

        for kingdom, cards in now_list.items():
            for card in cards:
                if card not in ago_list[kingdom]:
                    shift = "new"
                else:
                    temp = ago_list[kingdom].index(card) - now_list[kingdom].index(card)
                    now_tier = get_tier(now[key] if not league else now, card)
                    ago_tier = get_tier(ago[key] if not league else ago, card)

                    if now_tier < ago_tier: #하위 티어로 내려갔을 때
                        temp -= 1
                    elif now_tier > ago_tier: #상위 티어로 올라갔을 때
                        temp += 1

                    shift = str(temp)

                result[key][card] = shift

    return {"result": result if not league else result[league]}

@app.get("/ranged-usages")
def get_ranged_card_usages(id: str, league: Optional[str] = None, dates: Optional[int] = 7):
    result = {x: {} for x in CARD_USAGE_LEAGUES} if not league else {league: {}}
    date = validate_date(datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d"))

    for _ in range(dates):
        all_ranks = get_card_usage_ranks(league, date)["result"]

        for key in result.keys():
            ranks = all_ranks[key] if not league else all_ranks
            result[key][date[4:]] = "-" if id not in ranks.keys() else str(ranks[id])

        date = subtract_a_day(date)

    return {"result": result if not league else result[league]}

@app.get("/ranged-usage-changes")
def get_ranged_card_usage_changes(id: str, league: Optional[str] = None, dates: Optional[int] = 7):
    result = {x: {} for x in CARD_USAGE_LEAGUES} if not league else {league: {}}
    date = validate_date(datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d"))

    for _ in range(dates):
        all_changes = get_card_usage_changes(league, date)["result"]

        for key in result.keys():
            changes = all_changes[key] if not league else all_changes
            result[key][date[4:]] = "-" if id not in changes.keys() else changes[id]

        date = subtract_a_day(date)

    return {"result": result if not league else result[league]}

@app.get("/test/get-cached-usages")
def get_cached_usages():
    return {"result": usage_caches}

@app.post("/__space/v0/actions")
def post_actions(action: Action):
    if action.event.id == "save_card_usage":
        save_card_usages()

def save_card_usages():
    global usage_caches
    result = {x: {} for x in CARD_USAGE_LEAGUES}

    for league in CARD_USAGE_LEAGUES:
        response = requests.get(f"{CARD_USAGE_URL}{league}")
        regex = re.search('"tiers":.+?(?=,"breadcrumbs")', response.text)
        datas = json.loads(f"{{{regex.group()}}}")

        for tier in datas["tiers"]:
            result[league][tier["name"]] = [ids[x] for x in tier["cards"]]

    usage_caches = {}
    db.put(data={"usages": result}, key=datetime.now(timezone("Asia/Seoul")).strftime("%Y%m%d"))
    requests.post(ON_DEMAND_ISR_URL)

def validate_date(date: str):
    result = db.fetch({"key": date})

    return date if result.items else subtract_a_day(date)

def subtract_a_day(date: str):
    return (datetime.strptime(date, "%Y%m%d") - timedelta(days=1)).strftime("%Y%m%d")

def get_tier(source: dict, target: str):
    for tier, kingdom in source.items():
        for cards in kingdom.values():
            if target in cards:
                return tier

    return None


#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Author : Effort
# @Time : 2019/11/23 0023 10:01

from django import forms
from django.core.exceptions import ValidationError
from django.forms import widgets
from cmdb.models import WeblogicPort

class PortForm(forms.Form):
    WeblogicPort = forms.ChoiceField(
        choices=WeblogicPort.objects.all().values_list("nid", "port"),
        label="服务应用端口号",
        initial='8002',
        widget=forms.widgets.Select
    )
    #
    # 重写父类的init方法
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["WeblogicPort"].widget.choices = WeblogicPort.objects.all().values_list("nid", "port")
    #     self.fields["mcpinterf"].widget.choices = models.WeblogicService.objects.all().values_list("nid",
    #                                                                                "weblogicport__port"),


import pytest

from compiler.dataflow.conftest import DataflowTestHelper

@pytest.mark.parametrize("bidirectional", [True, False])
@pytest.mark.parametrize("inherit_relation", [True, False])
@pytest.mark.parametrize("assign_first", [True, False])
def test_dataflow_model_relation(
    dataflow_test_helper: DataflowTestHelper, bidirectional: bool, inherit_relation: bool, assign_first: bool
) -> None:
    relation_stmt: str = "%s.b [1] -- B%s" % ("AParent" if inherit_relation else "A", ".a [1]" if bidirectional else "")
    dataflow_test_helper.compile(
        """
entity AParent:
end
implement AParent using std::none

entity A extends AParent:
end
implement A using std::none

entity B:
end
implement B using std::none

%s

a = A()
b = B()

a.b = b

%s
        """
        % ("" if assign_first else relation_stmt, relation_stmt if assign_first else "")
    )
    bidirectional_rule: str = "<instance> b . a -> <instance> a"
    dataflow_test_helper.verify_graphstring(
        """
a -> <instance> a
b -> <instance> b

<instance> a . b -> b
%s
        """
        % (bidirectional_rule if bidirectional else ""),
    )
    if not bidirectional:
        with pytest.raises(AssertionError):
            dataflow_test_helper.verify_graphstring(bidirectional_rule)
    dataflow_test_helper.verify_leaves({"a": {"a"}, "b": {"b"}, "a.b": {"b"}, "b.a": {"b.a"}})

def test_dataflow_model_assignment_to_relation(dataflow_test_helper: DataflowTestHelper) -> None:
    dataflow_test_helper.compile(
        """
entity X:
end

entity U:
end

entity V:
    number n
end

X.u [1] -- U
U.v [1] -- V

implement X using std::none
implement U using std::none
implement V using std::none

n = 42

x = X()
x.u = U()
x.u.v = V()
x.u.v.n = n
        """,
    )
    dataflow_test_helper.verify_graphstring(
        """
x -> <instance> x
<instance> x . u -> <instance> u
<instance> u . v -> <instance> v
<instance> v . n -> n
n -> 42
        """,
    )
    dataflow_test_helper.verify_leaves({"x.u.v.n": {"n"}})

def test_dataflow_model_assignment_from_relation(dataflow_test_helper: DataflowTestHelper) -> None:
    dataflow_test_helper.compile(
        """
entity U:
end

entity V:
    number n
end

U.v [1] -- V

implement U using std::none
implement V using std::none

n = 42

u = U(v = v)
v = V(n = n)

uvn = u.v.n
        """,
    )
    dataflow_test_helper.verify_graphstring(
        """
n -> 42
u -> <instance> u
v -> <instance> v

<instance> u . v -> v
<instance> v . n -> n

uvn -> u . v . n
        """,
    )
    dataflow_test_helper.verify_leaves({"n": {"n"}, "u": {"u"}, "v": {"v"}, "u.v": {"v"}, "v.n": {"n"}, "uvn": {"n"}})

def test_dataflow_model_index(dataflow_test_helper: DataflowTestHelper) -> None:
    dataflow_test_helper.compile(
        """
entity A:
    number n
    number k
    number l
end

index A(n)

implement A using std::none

x = A(n = 42, k = 0)
y = A(n = 42, l = 1)
        """,
    )
    dataflow_test_helper.verify_graphstring(
        """
x -> <instance> x
y -> <instance> y

<instance> x . n -> [ 42 42 ]
<instance> y . n -> [ 42 42 ]

<instance> x . k -> 0
<instance> y . k -> 0

<instance> x . l -> 1
<instance> y . l -> 1
        """,
    )

def test_dataflow_model_default_attribute(dataflow_test_helper: DataflowTestHelper) -> None:
    dataflow_test_helper.compile(
        """
entity A:
    number n = 42
end

implement A using std::none

x = A()
y = A(n = 0)
        """,
    )
    dataflow_test_helper.verify_graphstring(
        """
x -> <instance> x
y -> <instance> y

<instance> x . n -> 42
<instance> y . n -> 0
        """,
    )

@pytest.mark.parametrize("refer_out", [True, False])
def test_dataflow_model_implementation(dataflow_test_helper: DataflowTestHelper, refer_out: bool) -> None:
    dataflow_test_helper.compile(
        """
entity A:
    number n
end

implementation i for A:
    self.n = %s
end

implement A using i

nn = 42
x = A()
        """
        % ("nn" if refer_out else 42),
    )
    dataflow_test_helper.verify_graphstring(
        """
nn -> 42
x -> <instance> x
<instance> x . n -> %s
        """
        % ("nn" if refer_out else 42),
    )

def test_dataflow_model_unsupported_bidirectional_doesnt_crash(dataflow_test_helper: DataflowTestHelper) -> None:
    dataflow_test_helper.compile(
        """
entity A:
end

entity B:
end

implement A using std::none
implement B using std::none

A.b [0:] -- B.a [0:]

a = A()
# Lists are not supported yet. Mustn't crash on trying to model the other side of the bidirectional relation
a.b = [B(), B()]
        """,
    )


#!/usr/bin/env python3
"""
DeviantArt downloader by @hatkidchan

Requirements:
    pip install rich deviantart requests
"""
from os.path import join, splitext, split as splitpath, exists
from concurrent.futures import ThreadPoolExecutor
from argparse import ArgumentParser
from urllib.parse import urlparse
from traceback import format_exc
from os import mkdir, environ
from time import sleep
from json import dumps
from rich.progress import BarColumn, DownloadColumn, TransferSpeedColumn
from rich.progress import TimeRemainingColumn, Progress
from requests import get as GET
from deviantart import Api

__description__ = '''\
Tags downloader from DeviantArt.
'''

class DeviantArtDownloader:
    def __init__(self, client_id, client_secret):
        self.api = Api(client_id, client_secret)
        self.progress = Progress(
            BarColumn(bar_width=None),
            "[progress.percentage]{task.percentage:>3.1f}%",
            DownloadColumn(),
            TransferSpeedColumn(),
            "[bold blue]{task.fields[filename]}",
        )
        self.all_t = self.progress.add_task('All', filename='All', start=0)
        self.total_length = 0;

    def download_worker(self, task_id, url, path):
        with open(path, 'wb') as f, GET(url, stream=True) as rq:
            length = int(rq.headers.get('Content-Length', 0))
            self.progress.start_task(task_id)
            self.progress.update(task_id, total=length)
            self.total_length += length
            self.progress.update(self.all_t, total=self.total_length)
            for chunk in rq.iter_content(chunk_size=4096):
                f.write(chunk)
                self.progress.update(task_id, advance=len(chunk))
                self.progress.update(self.all_t, advance=len(chunk))
        return task_id

    def search_content(self, tag, max_items=-1):
        n_items = 0
        offset = 0
        while True:
            data = self.api.browse('tags', tag=tag, offset=offset)
            for item in data['results']:
                yield item
                n_items += 1
                if n_items > max_items and max_items > 0:
                    return
            if not data['has_more']:
                break
            offset = data['next_offset']

    @staticmethod
    def _make_filename(item):
        src = item.content['src']
        ext = splitext(urlparse(src).path)[1]
        return splitpath(item.url)[1] + ext

    def download(self,
                 tag,
                 out_dir='.',
                 max_items=-1,
                 max_workers=8,
                 list_path=None):
        if not exists(out_dir):
            mkdir(out_dir)
        with self.progress, ThreadPoolExecutor(max_workers=max_workers) as pool:
            self.progress.start_task(self.all_t)
            futures = []
            for item in self.search_content(tag, max_items):
                if list_path:
                    with open(list_path, 'a') as flist:
                        flist.write(item.url + '\n')
                if not item.content:
                    continue
                filename = join(out_dir, self._make_filename(item))
                task_id = self.progress.add_task(
                        'download',
                        filename=item.title,
                        start=0)

                url = item.content['src']
                f = pool.submit(self.download_worker, task_id, url, filename)
                futures.append(f)
                while len(futures) >= max_workers:
                    for f in futures:
                        if f.done():
                            futures.remove(f)
                            self.progress.remove_task(f.result())
                    sleep(0.1)

if __name__ == '__main__':
    p = ArgumentParser(description=__description__)
    p.add_argument('--out-dir', dest='out_dir', default='.',
                   help='Folder to save files into')
    p.add_argument('--out-list', dest='out_list', default='',
                   help='File to save links into')
    p.add_argument('--client-id', dest='client_id',
                   default=environ.get('DA_CLIENT_ID'),
                   help='DeviantArt client id (defaults to $DA_CLIENT_ID)')
    p.add_argument('--client-secret', dest='client_secret',
                   default=environ.get('DA_CLIENT_SECRET'),
                   help='DeviantArt client key (defaults to $DA_CLIENT_SECRET)')
    p.add_argument('--max-items', dest='max_items', type=int, default=0,
                   help='Download at most N items. 0 means no limit')
    p.add_argument('--max-workers', dest='max_workers', type=int, default=8,
                   help='Maximum number of active download workers')
    p.add_argument('tag', help='Tag to search for downloading')
    args = p.parse_args()

    dl = DeviantArtDownloader(args.client_id, args.client_secret)
    dl.download(tag=args.tag,
                out_dir=args.out_dir,
                max_items=args.max_items,
                max_workers=args.max_workers,
                list_path=args.out_list or None)


#!/usr/bin/env python3

from absl import logging
from absl.testing import absltest

import numpy as np
from sklearn import metrics

from ypack import power

class UnitTests(absltest.TestCase):

    def test_power_method_symmetric(self):
        A = np.random.randn(50, 50)
        A = A + A.T
        ev, v1 = power.power_method_square_matrix(A)
        E, V = np.linalg.eig(A)
        self.assertAlmostEqual(np.abs(metrics.pairwise.cosine_similarity([v1], [V[:, 0]])).item(), 1, places=4)

    def test_power_method_nonsquare(self):
        A = np.random.randn(30, 50)
        sv, u1, v1 = power.power_method_nonsquare_matrix(A)
        U, S, Vt = np.linalg.svd(A)
        self.assertAlmostEqual(np.abs(metrics.pairwise.cosine_similarity([u1], [U[:, 0]])).item(), 1, places=4)
        self.assertAlmostEqual(np.abs(metrics.pairwise.cosine_similarity([v1], [Vt[0, :]])).item(), 1, places=4)

if __name__ == '__main__':
    absltest.main()


from utils.environment import Environment

# Se genera la clase DBConnection para la proteccion de la base de datos a usar
class DBConnection(Environment):
    def __init__(self):
        # Variables de Entorno de la conexión a la BD
        self.__HOST     = ""
        self.__USER     = ""
        self.__PWD      = ""
        self.__PORT     = ""
        self.__DATABASE = ""

        # Variables de Entorno de configuración SQLAlchemy
        sqlAlchemy = self.settingsSQLAlchemy()
        self.TRACK_MODIFICATIONS = sqlAlchemy["TRACK_MODIFICATIONS"]
        self.ECHO = sqlAlchemy["ECHO"]

    def initParameters(self, nameBD):
        # Variables de Entorno de la conexión a la BD
        db = self.settingBD(nameBD)
        self.__HOST     = db.get("HOST")
        self.__USER     = db.get("USER")
        self.__PWD      = db.get("PWD")
        self.__PORT     = db.get("PORT")
        self.__DATABASE = db.get("DATABASE")

    def run(self, nameBD):
        # Se declara el tipo de base de datos a usar
        self.initParameters(nameBD)

        dbConnection = {
            "SQLLITE"   : self.__sqlLite(),
            "MYSQL"     : self.__mysql(),
        }
        return dbConnection[nameBD]

    def __sqlLite(self):
        return "sqlite:///test.db"

    def __mysql(self):
        return f"mysql+mysqlconnector://{self.__USER}:{self.__PWD}@{self.__HOST}:{self.__PORT}/{self.__DATABASE}"


import random
import pandas as pd
import numpy as np
from json import dump
from sklearn.impute import KNNImputer
from sklearn.decomposition import NMF
from sklearn.metrics.pairwise import cosine_similarity
import os

APP_ROOT = os.path.dirname(os.path.abspath(__file__))
APP_STATIC = os.path.join(APP_ROOT, 'static')
movies = pd.read_csv(os.path.join(APP_STATIC,'movies.csv'))

#movies = pd.concat([movies,movies['genres'].str.get_dummies()],axis=1)
#movies_json = movies[['movieId','title','genres']].to_json('movies.json',orient='records')

ratings = pd.read_csv(os.path.join(APP_STATIC,'ratings.csv'))

top_ratings = ratings.groupby('movieId')['rating'].agg(['mean','count']).sort_values(['mean','count'],ascending=False)

THRESHOLD = 15
top = ratings.groupby('movieId')['rating'].agg(['mean','count']).sort_values(['mean','count'],ascending=False)
top = top[top['count'] > THRESHOLD]

reviews = pd.pivot_table(
            ratings,values='rating',
            index='userId',
            columns='movieId')
#imputer = KNNImputer(n_neighbors=5, weights='distance')
#reviews = pd.DataFrame(
#        imputer.fit_transform(reviews),
 #       columns=reviews.columns,
  #      index=reviews.index)

#nmf = NMF(n_components=20)
#rhat = nmf.fit_transform(reviews)

def get_top_predict(user_dict,k=10):
    seen = list(user_dict.keys())
    ids = top[~top.index.isin(seen)].head(k).index.tolist()
    return movieId_to_title(ids)

def add_user_to_reviews(user_dict,reviews):
    ''' Adds a new user to the reviews DataFrame
    Arguments:
    user_dict -- dictionary of movie ratings for the user in the form
                {movieId: rating}, where rating is 0.5 ... 5.0 in steps of 0.5
    reviews   -- DataFrame with columns 'movieId' and rows userId

    returns   -- DataFrame with added row of the new user rating
    '''
    user_series = pd.Series(
        data=user_dict,
        name=reviews.index.max()+1,
        dtype=float)
    return reviews.append(user_series)

def cos_similar(reviews):
    '''Given a DataFram of users and movie reviews, returns a DataFrame with the cosine similarity of each user.
    '''
    reviews = reviews.fillna(0.0)
    sim = cosine_similarity(reviews)
    user_similar = pd.DataFrame(sim,index=reviews.index,columns=reviews.index)
    return user_similar

def get_top_of_user(reviews,user_id,k=20):
    '''Returns a list of the k top-rated moviesIds of a given user'''
    #order = np.argsort(reviews.loc[user_id])[::-1]
    user_reviews = reviews.loc[user_id]
    top_rated = user_reviews.sort_values(ascending=False)
    return top_rated.index.tolist()[:k]

def get_similar_users(user_id,similar_matrix,k=20):
    ''' Returns a list of similar users.

    Arguments:
    user_id        -- id of user that the other users are compared to
    similar_matrix -- DataFram of user similarity, must contain user_id
    k              -- number of similar users returned. (Default=20)
    '''
    #cosim = cos_similar(similar_matrix)
    user = similar_matrix.loc[[user_id]]
    user = user.drop(user_id,axis=1)
    sort = np.argsort(user.values.flatten())
    print(len(sort))
    most_similar = user.columns[sort[::-1]].tolist()
    return most_similar[:k]

def recommend(user_id, top_list, ratings, k):
    '''Recommends a movie that the user_id has not been rated in the ratings dataframe. The best k recommendations from the top_list dataframe are returned as a list. '''
    seen = list(ratings[ratings.userId==user_id]['movieId'])
    return top_list[~top_list.index.isin(seen)].head(k).index.tolist()

def movieId_to_title(ids):
    ''' Given a list of movieIds, returns a corresponding list of movie titles.
    '''
    return movies.set_index('movieId').loc[ids]['title'].tolist()

def title_to_movieId(titles):
    ''' Given a list of movie titles, returns a corresponding list of movieIds.
    '''
    return movies.set_index('title').loc[titles]['movieId'].tolist()

def matrix_factor():
    return ''

def cosim_predict(dict_of_ratings):
    '''
    Gives a prediction of movies to see, based on reviews of movies given.
    '''
    seen = set(dict_of_ratings.keys())
    reviews_added  = add_user_to_reviews(dict_of_ratings,reviews)
    similar_matrix = cos_similar(reviews_added)
    similar_users  = get_similar_users(reviews_added.shape[0],similar_matrix,1)
    movieIds       = get_top_of_user(reviews, similar_users[0], len(seen)+20)
    return movieId_to_title([item for item in movieIds if item not in seen])

#### implement average of most similar users


# Author: Steven Ruggiero
# Email: Stevemruggiero@gmail.com

from ImportParamFromFile import ImportParamFromFile
from scipy.integrate import odeint
from ColDetModel import ColDetModel
from RunModel import RunModel
import multiprocessing
import math
import numpy as np
import sys
import pickle
import time

def ParamAdjust(itKey, parameters, y0, t0, t, baselineLeakage, scale, sampleTiming, ModelName=ColDetModel):
    #Run the model with the appropriate changes

    parameters[itKey] = parameters[itKey] * scale
    model = ModelName()
    sol = odeint(model.DifEqs, y0, t, args = (parameters,), full_output = 0, atol = 1.0e-1, hmax = t0[2])

    while (model.tSwitch > (t[-1] - (sampleTiming*3.5))):
        t = np.arange(t0[0],(t[-1]*1.5)+t0[2],t0[2])
        sol = odeint(model.DifEqs, y0, t, args = (parameters,), full_output = 0, atol = 1.0e-1, hmax = t0[2])

    switchIndex = math.ceil(model.tSwitch / t0[2])

    indexArray = -1

    gain = np.array([((sol[indexArray,19]-baselineLeakage)/baselineLeakage)/(scale-1)])
    #gain is percent change in leakage over percent change in parameter

    return [itKey, gain]

def RunTask(inQueue, outQueue):
    #Function run by the child processes to handle tasks and report results
    while(1):
        func,args = inQueue.get()

        outQueue.put(func(*args))

        inQueue.task_done()

if __name__ == '__main__':

    #initialization

    numCPUs = multiprocessing.cpu_count()

    inputSource='ColDet-model-parameters.csv'
    scales = (1.05, 1.04, 1.03, 1.02, 1.01, 0.99, 0.98, 0.97, 0.96, 0.95)
    #scales = (1.05,0.95)

    #Depreciated
    sampleTiming = 365
    y0, t0, parameters = ImportParamFromFile(inputSource, ColDetModel)

    #Runbase case
    t = np.arange(t0[0],t0[1]+t0[2],t0[2])

    model = ColDetModel()
    sol = odeint(model.DifEqs, y0, t, args = (parameters,), full_output = 0, atol = 1.0e-1, hmax = t0[2])

    indexArray = -1
    baselineLeakage = np.array(sol[indexArray,19])

    #reinit parameters to revert changes that may have occured calculation of the model
    y0, t0, parameters = ImportParamFromFile(inputSource, ColDetModel)

    #initializing multiprocessing
    processes = []
    resultsArrays= []
    finishQueues = []
    multiprocessing.freeze_support()
    runQueue = multiprocessing.JoinableQueue()

    #Set up results Queues and Processes
    for i in range(numCPUs):
        finishQueues.append(multiprocessing.Queue())
        print('Made result Queue #' + repr(i+1))
        processes.append(multiprocessing.Process(target = RunTask, args=(runQueue,finishQueues[i])))
        processes[i].start()
        print('Made process #' + repr(i+1))

    #Run sensativity analysis for each scale factor
    paramKeys = list(parameters.keys())

    for j in range(len(scales)):

        keyArray = np.empty(0)
        gainArray = np.empty(0)

        #Set up and push tasks to queue
        tasks = [(ParamAdjust, (key, parameters, y0, t0, t, baselineLeakage, scales[j], sampleTiming)) for key in paramKeys]
        for task in tasks:
            runQueue.put_nowait(task)

        runQueue.join()

        #Pull results and append them to a list defined in an outer scope
        results = []
        for i in range(len(finishQueues)):
            while(not finishQueues[i].empty()):
                results.append(finishQueues[i].get())

        for i in range(len(results)):
            keyArray = np.append(keyArray, results[i][0])
            gainArray = np.append(gainArray, results[i][1])

        #Combine keyArray and gainArray into one array
        compositeArray = np.append(np.transpose(np.atleast_2d(keyArray)), np.transpose(np.atleast_2d(gainArray)), axis=1)

        resultsArrays.append(compositeArray)

    for i in range(len(processes)):
            processes[i].terminate()
    #Dump results
    file = open('LocalSensitivity.p', 'wb')
    pickle.dump(resultsArrays, file)
    file.close()

    #Organize results into a spreadsheet
    resultsOutput = np.empty((len(keyArray)+1, 0))
    dType1 = np.dtype([('param', '<U32'), ('100 Days', '<U32')])
    dType2 = np.dtype('<U32')

    for i in range(len(scales)):

        resultsArrays[i].dtype = dType1
        resultsArrays[i] = np.sort(resultsArrays[i], axis = 0, order='param')
        resultsArrays[i].dtype = dType2

        dummyArray1 = np.array([['scale', scales[i], '']])
        dummyArray2 = np.full((len(keyArray), 1), '', dtype = '<U5')

        dummyArray2 = np.append(dummyArray2, resultsArrays[i], axis = 1)
        dummyArray3 = np.append(dummyArray1, dummyArray2, axis=0)
        resultsOutput = np.append(resultsOutput, dummyArray3, axis = 1)

    np.savetxt('SensativityAnalysis.csv', resultsOutput, fmt = '%s', delimiter = ',')


import json
import matplotlib.pyplot as plt
import numpy as np

INDICES = [0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0]

data = {}
means = {}
for i in INDICES:
    data.update({str(i): [[] for _ in range(51)]})
    means.update({str(i): [[] for _ in range(51)]})

for i in range(1, 11):
    with open(f'vary_n/data_{i}.json', 'r') as f:
        obj = json.load(f)
        for k, v in obj.items():
            for i in range(1, 51):
                data[k][i] += v[i]

# find means
for k in data.keys():
    for i in range(1, 51):
        means[k] = [np.mean(row) if len(row) != 0 else 0 for row in data[k]]

lines = []
indices = []

# Uncomment to show one line
INDICES = [0.5,1.0,2.0,5.0]
bestfit = False

for i in INDICES:
    if not means[str(i)][50]: continue
    line, = plt.plot(list(range(500, 25001, 500)), means[str(i)][1:])
    lines.append(line)
    indices.append(f"β={i}")
    if bestfit:
        x = np.linspace(500, 25000, 5000)
        y = 40/((x * i) ** 0.55)
        curve, = plt.plot(x, y)
        lines.append(curve)
        indices.append(f"Estimate for β={i}")

plt.xlabel("n")
plt.ylabel("RMSE")
plt.legend(lines, indices)
plt.show()


# accounts.renderers.py

import json
from typing import Any, Mapping, Optional

from rest_framework.renderers import JSONRenderer

class UserJSONRenderer(JSONRenderer):

    charset = 'utf-8'

    def render(
        self,
        data,
        media_type: Optional[str] = None,
        renderer_context: Optional[Mapping[str, Any]] = None,
    ) -> str:

        errors = data.get('errors', None)
        token = data.get('token', None)
        if errors is not None:
            return super(UserJSONRenderer, self).render(data)

        if token is not None and isinstance(token, bytes):
            data['token'] = token.decode('utf-8')

        return json.dumps({'user': data})


from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from router import light
from config.database import db
from router.control import auto, manual
from router.model import Light

origins = ['*']

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)

app.include_router(light.router)

@app.get("/")
def get_all_light():
    collection = db["smarthome_light"]
    data = collection.find({}, {"_id": False})
    result = list(data)

    return {"result": result}

@app.put("/")
def update_light(light: Light):
    if light.room_id not in range(0, 3):
        raise HTTPException(status_code=404, detail="Room Id not in range (0-2)")
    if light.brightness not in range(0, 256):
        raise HTTPException(status_code=404, detail="Brightness not in range (0-255)")

    res = list(db["smarthome_light"].find({"room_id": light.room_id}, {"_id": False}))
    if len(res) == 0:
        raise HTTPException(status_code=404, detail="Room not found")

    if res[0]["is_auto"]:
        auto("hardware", light)
    else:
        manual("hardware", light)

    return {"result": "Update success"}

# def read_root():
#     return {"msg": "Connect!"}


import json
import subprocess

import common
from autotest_lib.client.common_lib import utils
from autotest_lib.server import frontend

# _OMAHA_STATUS - URI of a file in GoogleStorage with a JSON object
# summarizing all versions currently being served by Omaha.
#
# The principal data is in an array named 'omaha_data'.  Each entry
# in the array contains information relevant to one image being
# served by Omaha, including the following information:
#   * The board name of the product, as known to Omaha.
#   * The channel associated with the image.
#   * The Chrome and Chrome OS version strings for the image
#     being served.
#
_OMAHA_STATUS = 'gs://chromeos-build-release-console/omaha_status.json'

# _BUILD_METADATA_PATTERN - Format string for the URI of a file in
# GoogleStorage with a JSON object that contains metadata about
# a given build.  The metadata includes the version of firmware
# bundled with the build.
#
_BUILD_METADATA_PATTERN = 'gs://chromeos-image-archive/%s/metadata.json'

# _FIRMWARE_UPGRADE_DENYLIST - a set of boards that are exempt from
# automatic stable firmware version assignment.  This denylist is
# here out of an abundance of caution, on the general principle of "if
# it ain't broke, don't fix it."  Specifically, these are old, legacy
# boards and:
#   * They're working fine with whatever firmware they have in the lab
#     right now.
#   * Because of their age, we can expect that they will never get any
#     new firmware updates in future.
#   * Servo support is spotty or missing, so there's no certainty that
#     DUTs bricked by a firmware update can be repaired.
#   * Because of their age, they are somewhere between hard and
#     impossible to replace.  In some cases, they are also already in
#     short supply.
#
# N.B.  HARDCODED BOARD NAMES ARE EVIL!!!  This denylist uses hardcoded
# names because it's meant to define a list of legacies that will shrivel
# and die over time.
#
# DO NOT ADD TO THIS LIST.  If there's a new use case that requires
# extending the denylist concept, you should find a maintainable
# solution that deletes this code.
#
# TODO(jrbarnette):  When any board is past EOL, and removed from the
# lab, it can be removed from the denylist.  When all the boards are
# past EOL, the denylist should be removed.

_FIRMWARE_UPGRADE_DENYLIST = set([
        'butterfly',
        'daisy',
        'daisy_skate',
        'daisy_spring',
        'lumpy',
        'parrot',
        'parrot_ivb',
        'peach_pi',
        'peach_pit',
        'stout',
        'stumpy',
        'x86-alex',
        'x86-mario',
        'x86-zgb',
])

def _read_gs_json_data(gs_uri):
    """Read and parse a JSON file from GoogleStorage.

    This is a wrapper around `gsutil cat` for the specified URI.
    The standard output of the command is parsed as JSON, and the
    resulting object returned.

    @param gs_uri   URI of the JSON file in GoogleStorage.
    @return A JSON object parsed from `gs_uri`.
    """
    with open('/dev/null', 'w') as ignore_errors:
        sp = subprocess.Popen(['gsutil', 'cat', gs_uri],
                              stdout=subprocess.PIPE,
                              stderr=ignore_errors)
        try:
            json_object = json.load(sp.stdout)
        finally:
            sp.stdout.close()
            sp.wait()
    return json_object

def _read_build_metadata(board, cros_version):
    """Read and parse the `metadata.json` file for a build.

    Given the board and version string for a potential CrOS image,
    find the URI of the build in GoogleStorage, and return a Python
    object for the associated `metadata.json`.

    @param board         Board for the build to be read.
    @param cros_version  Build version string.
    """
    image_path = frontend.format_cros_image_name(board, cros_version)
    return _read_gs_json_data(_BUILD_METADATA_PATTERN % image_path)

def _get_by_key_path(dictdict, key_path):
    """Traverse a sequence of keys in a dict of dicts.

    The `dictdict` parameter is a dict of nested dict values, and
    `key_path` a list of keys.

    A single-element key path returns `dictdict[key_path[0]]`, a
    two-element path returns `dictdict[key_path[0]][key_path[1]]`, and
    so forth.  If any key in the path is not found, return `None`.

    @param dictdict   A dictionary of nested dictionaries.
    @param key_path   The sequence of keys to look up in `dictdict`.
    @return The value found by successive dictionary lookups, or `None`.
    """
    value = dictdict
    for key in key_path:
        value = value.get(key)
        if value is None:
            break
    return value

def _get_model_firmware_versions(metadata_json, board):
    """Get the firmware version for all models in a unibuild board.

    @param metadata_json    The metadata_json dict parsed from the
                            metadata.json file generated by the build.
    @param board            The board name of the unibuild.
    @return If the board has no models, return {board: None}.
            Otherwise, return a dict mapping each model name to its
            firmware version.
    """
    model_firmware_versions = {}
    key_path = ['board-metadata', board, 'models']
    model_versions = _get_by_key_path(metadata_json, key_path)

    if model_versions is not None:
        for model, fw_versions in model_versions.iteritems():
            fw_version = (fw_versions.get('main-readwrite-firmware-version') or
                          fw_versions.get('main-readonly-firmware-version'))
            model_firmware_versions[model] = fw_version
    else:
        model_firmware_versions[board] = None

    return model_firmware_versions

def get_omaha_version_map():
    """Convert omaha versions data to a versions mapping.

    Returns a dictionary mapping board names to the currently preferred
    version for the Beta channel as served by Omaha.  The mappings are
    provided by settings in the JSON object read from `_OMAHA_STATUS`.

    The board names are the names as known to Omaha:  If the board name
    in the AFE contains '_', the corresponding Omaha name uses '-'
    instead.  The boards mapped may include boards not in the list of
    managed boards in the lab.

    @return A dictionary mapping Omaha boards to Beta versions.
    """
    def _entry_valid(json_entry):
        return json_entry['channel'] == 'beta'

    def _get_omaha_data(json_entry):
        board = json_entry['board']['public_codename']
        milestone = json_entry['milestone']
        build = json_entry['chrome_os_version']
        version = 'R%d-%s' % (milestone, build)
        return (board, version)

    omaha_status = _read_gs_json_data(_OMAHA_STATUS)
    return dict(_get_omaha_data(e) for e in omaha_status['omaha_data']
                    if _entry_valid(e))

def get_omaha_upgrade(omaha_map, board, version):
    """Get the later of a build in `omaha_map` or `version`.

    Read the Omaha version for `board` from `omaha_map`, and compare it
    to `version`.  Return whichever version is more recent.

    N.B. `board` is the name of a board as known to the AFE.  Board
    names as known to Omaha are different; see
    `get_omaha_version_map()`, above.  This function is responsible
    for translating names as necessary.

    @param omaha_map  Mapping of Omaha board names to preferred builds.
    @param board      Name of the board to look up, as known to the AFE.
    @param version    Minimum version to be accepted.

    @return Returns a Chrome OS version string in standard form
            R##-####.#.#.  Will return `None` if `version` is `None` and
            no Omaha entry is found.
    """
    omaha_version = omaha_map.get(board.replace('_', '-'))
    if version is None:
        return omaha_version
    if omaha_version is not None:
        if utils.compare_versions(version, omaha_version) < 0:
            return omaha_version
    return version

def get_firmware_versions(board, cros_version):
    """Get the firmware versions for a given board and CrOS version.

    During the CrOS auto-update process, the system will check firmware
    on the target device, and update that firmware if needed.  This
    function finds the version string of the firmware that would be
    installed from a given CrOS build.

    A build may have firmware for more than one hardware model, so the
    returned value is a dictionary mapping models to firmware version
    strings.

    The returned firmware version value will be `None` if the build
    isn't found in storage, if there is no firmware found for the build,
    or if the board is denylisted from firmware updates in the test
    lab.

    @param board          The board for the firmware version to be
                          determined.
    @param cros_version   The CrOS version bundling the firmware.
    @return A dict mapping from board to firmware version string for
            non-unibuild board, or a dict mapping from models to firmware
            versions for a unibuild board (see return type of
            _get_model_firmware_versions)
    """
    if board in _FIRMWARE_UPGRADE_DENYLIST:
        return {board: None}
    try:
        metadata_json = _read_build_metadata(board, cros_version)
        unibuild = bool(_get_by_key_path(metadata_json, ['unibuild']))
        if unibuild:
            return _get_model_firmware_versions(metadata_json, board)
        else:
            key_path = ['board-metadata', board, 'main-firmware-version']
            return {board: _get_by_key_path(metadata_json, key_path)}
    except Exception as e:
        # TODO(jrbarnette): If we get here, it likely means that the
        # build for this board doesn't exist.  That can happen if a
        # board doesn't release on the Beta channel for at least 6 months.
        #
        # We can't allow this error to propagate up the call chain
        # because that will kill assigning versions to all the other
        # boards that are still OK, so for now we ignore it.  Probably,
        # we should do better.
        return {board: None}


import win32api
import win32con
import win32gui
import time
import threading
import random
import inspect
import ctypes
import pyautogui
from PyQt5.QtWidgets import QApplication
from datetime import datetime

random.seed(datetime.now())

# 基础操作

def MouseMove(x, y):  # 移动鼠标的位置
    win32api.SetCursorPos((x, y))

def MouseClick(x=None, y=None):  # 鼠标点击指定的地方
    if x is not None and y is not None:
        MouseMove(x, y)
        time.sleep(0.005)
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    time.sleep(random.uniform(0.001, 0.02))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)

def GetMousePosition():  # 得到鼠标的位置
    return win32api.GetCursorPos()

def GetWindowHwnd():
    X, Y = GetMousePosition()
    hwnd_value = win32gui.WindowFromPoint((X, Y))
    return hwnd_value

def WaitTime(x):
    time.sleep(random.uniform(0.1, 0.5) * x)

def WaitTime_short(x):
    time.sleep(random.uniform(0.02, 0.05) * x)

def WaitTime_wait(x):
    # if type(x) != "str"
    if isinstance(x, int):
        time.sleep(x)
    else:
        time.sleep(int(x))

# 窗口类的一些操作

class MyWindows:  # 新建一个窗口类
    def __init__(self, hwnd):
        self.hwnd = hwnd
        # 点击挑战的时候的位置坐标 坐标为组队的时候
        self.random_x_fight = random.uniform(0.92, 0.95)
        self.random_y_fight = random.uniform(0.84, 0.90)
        self.random_x_other = random.uniform(0.7, 0.74)
        self.random_y_other = random.uniform(0.75, 0.76)
        self.random_x_YuLing = random.uniform(0.84, 0.88)
        self.random_y_YuLing = random.uniform(0.86, 0.88)
        self.random_Wx_select = None
        self.random_Wy_select = None
        self.random_Wx_other_select = None
        self.random_Wy_other_select = None

    def GetWindowsRect(self):  # 更新窗口位置的大小并返回出

        self.left, self.top, self.right, self.bottom = win32gui.GetWindowRect(
            self.hwnd)

    def checkWindows(self):  # 检查窗口防止句柄输入错误
        hwnd_return = win32gui.GetWindowText(self.hwnd)
        if hwnd_return == "阴阳师-网易游戏":
            self.GetWindowsRect()
        else:
            return 1
        return 0

    def ChangeWindows(self, left, top, width, hight):  # 改变窗口的位置
        hwnd_return = win32gui.GetWindowText(self.hwnd)
        if hwnd_return == "阴阳师-网易游戏":
            win32gui.MoveWindow(self.hwnd, left, top, width,
                                hight, True)  # 改变应用的位置和大小
            self.GetWindowsRect()
        else:
            return 1
        return 0

    '''TODO:
    1.需要完成随机函数 √
    2.丰富找不到的情况时候的图片 任务√ 御魂满√
    3.开始逐步完成并替代点击的函数逻辑
    '''
    def posClickRandom(self, left, top, width, height):  # 分别为框左边 框顶 框宽度 框高度
        return left + random.randint(1, width), top + random.randint(1, height) #有概率点不到

    def getWindowsScreen(self):  # 获取游戏截图，目前无用
        screen = QApplication.primaryScreen()
        img = screen.grabWindow(self.hwnd).toImage()
        img.save("{}.jpg".format(self.hwnd))

###########################################################
    def getAllPhotosPos(self, filename): #需要测试
        emposs = pyautogui.locateAllOnScreen(filename)
        myemposs = []
        for pos in emposs:
            myemposs.append(pos)
        print(myemposs)
        return myemposs

    def getPhotoPos(self, filename):
        posBox = pyautogui.locateOnScreen(filename, confidence = 0.6)
        return posBox

    # 点击部分代码
    def WindowsMoveClick(self, random_x, random_y):
        MouseClick(int((self.right - self.left) * random_x) + self.left,
                   int((self.bottom - self.top) * random_y) + self.top)

    def WindowsClickSnackFight(self):
        pos = self.getPhotoPos("./img/necessary/tiaozhan_ok.png")
        if pos is None:
            for i in range(2):
                time.sleep(3)
                if pos is not None:
                    x, y = self.posClickRandom(pos.left, pos.top, pos.width, pos.height)
                    MouseClick(x, y)
                    return 1
            return 0
        elif pos is not None:
            x, y = self.posClickRandom(pos.left, pos.top, pos.width, pos.height)
            MouseClick(x, y)
            return 1

    def WindowsClickOther(self):
        self.random_x_other = random.uniform(0.7, 0.74)
        self.random_y_other = random.uniform(0.75, 0.76)
        self.WindowsMoveClick(self.random_x_other, self.random_y_other)

    def WindowsClickSelectFight(self):
        MouseClick(self.random_Wx_select + random.randint(-5, 5),
                   self.random_Wy_select + random.randint(-5, 5))

    def WindowsClickSelectOther(self):
        MouseClick(self.random_Wx_other_select + random.randint(-5, 5),
                   self.random_Wy_other_select + random.randint(-5, 5))

def turn_two(class1, class2):
    class1.WindowsClickOther()
    class1.WindowsClickOther()
    WaitTime(2)
    class2.WindowsClickOther()
    class2.WindowsClickOther()
    WaitTime(2)

def turnOneSelect(class1):
    class1.WindowsClickSelectOther()
    WaitTime(1)

# 用图片来判断结束与否，是否会消耗过多的时间？无所谓好像
# 测试时间 图片对比所需时间
def snake_two(class1, class2, num, times):
    WaitTime_wait(1)
    if num == "1":
         x = class1.WindowsClickSnackFight()
    elif num == "2":
        x = class2.WindowsClickSnackFight()
    if x == 0:
        return 0
    WaitTime_wait(times)
    turn_two(class1, class2)
    WaitTime_short(3)
    turn_two(class1, class2)
    WaitTime_short(4)
    turn_two(class1, class2)
    WaitTime_short(2)
    turn_two(class1, class2)
    WaitTime_short(2)
    turn_two(class1, class2)

def selectOne(class1, times):
    WaitTime_wait(1)
    class1.WindowsClickSelectFight()
    WaitTime_wait(times)
    class1.WindowsClickSelectOther()
    WaitTime_short(20)
    class1.WindowsClickSelectOther()
    WaitTime_short(22)
    class1.WindowsClickSelectOther()
    WaitTime_short(22)
    class1.WindowsClickSelectOther()
    WaitTime_short(40)
    class1.WindowsClickSelectOther()
    WaitTime_short(4)

def stop_thread(thread, exctype=SystemExit):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(thread.ident)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
        tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


from collections import deque
class Solution:
    def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
        """LC 733 Floor fill
        Time complexity: O(E + N)
        Space O(N)
        Difficulty: easy
        Args:
            image (List[List[int]]): [description]
            sr (int): [description]
            sc (int): [description]
            newColor (int): [description]

        Returns:
            List[List[int]]: [description]
        """
        queue = deque()
        m = len(image)
        n = len(image[0])
        check = [[False] * n for _ in range(m)]
        queue.append((sr, sc))
        check[sr][sc] = True
        target = image[sr][sc]
        while len(queue) > 0:
            x, y = queue.popleft()
            image[x][y] = newColor
            direction = [0, 1, 0, -1, 0]
            for i in range(4):
                next_x, next_y = x+direction[i], y + direction[i + 1]
                if 0 <= next_x < m and 0 <= next_y < n and not check[next_x][next_y] and image[next_x][next_y] == target:
                    queue.append((next_x, next_y))
                    check[next_x][next_y] = True
        return image


from collections import Counter

class Excel:

    def __init__(self, H: int, W: str):
        # list of dict, value stores numeric, sum stores a Counter() including cell, cnt
        # Counter({(1, 'A'): 2, (1, 'B'): 1, (2, 'A'): 1, (2, 'B'): 1}) (r, c): cnt
        self.cells = [{letter: {"value": 0, "sum": None} for letter in string.ascii_uppercase} for h in range(H + 1)]

    def set(self, r: int, c: str, v: int) -> None:
        self.cells[r][c] = {"value": v, "sum": None}

    def get(self, r: int, c: str) -> int:
        cell = self.cells[r][c]
        addrs = cell.get("sum")
        if not addrs:
            return cell["value"]
        # print(addrs)
        res = 0
        for addr, count in addrs.items():
            res += self.get(*addr) * count
        return res

        # return sum(self.get(*addr) * count for addr, count in addrs.items())

    def sum(self, r: int, c: str, strs: List[str]) -> int:
        self.cells[r][c]["sum"] = self._parse(strs)
        return self.get(r, c)

    def _parse(self, strs: List[str]):
        counter = Counter()
        for s in strs:
            start, end = s.split(":")[0], s.split(":")[1] if ":" in s else s
            # print(s, start, end) # A1:B2 A1 B2
            for r in range(int(start[1:]), int(end[1:]) + 1): # get row index, 1->2
                for ci in range(ord(start[0]), ord(end[0]) + 1): # get col index, A->B
                    counter[(r, chr(ci))] += 1
        return counter

# Your Excel object will be instantiated and called as such:
# obj = Excel(height, width)
# obj.set(row,column,val)
# param_2 = obj.get(row,column)
# param_3 = obj.sum(row,column,numbers)


from os.path import split

from typing import List, Optional, Tuple

def extract_categories(infile: str, verbose: Optional[bool] = None) -> Tuple[List[str], List[str]]:
    path, uri = split(infile)
    uri, ext = uri.split('.YAMLDATA.yml')

    if verbose:
        print(f'Check {uri}')
        print(infile)

    with open(infile, 'r', encoding='utf-8') as yml_data_fh:
        content = yml_data_fh.read()

    mius = content.split('#MIU#Header#End#')
    categories = []
    reviewers = []

    for miu in mius:
        cat = [line.lstrip('category   : ').strip('"') for line in miu.split('\n') if line.startswith('category')]
        categories.extend(cat)
        rev = [line.lstrip('reviewer   : ').strip('"') for line in miu.split('\n') if line.startswith('reviewer')]
        reviewers.extend(rev)

    return categories, reviewers


import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import pandas as pd
from typing import Union, Tuple, List

from QUANTAXIS.QAFactor import plotting
from QUANTAXIS.QAFactor import performance as perf
from QUANTAXIS.QAFactor import utils
from QUANTAXIS.QAFactor.plotting_utils import GridFigure, customize
from QUANTAXIS.QAFactor.process import get_clean_factor_and_forward_returns

@customize
def create_summary_tear_sheet(
        factor_data: pd.DataFrame,
        by_datetime: bool = True,  # 按日期计算
        by_group: bool = False,  # 按分组计算
        long_short: bool = True,  # 多空组合
        group_neutral: bool = False,  # 分组中性
        periods: Union[int, Tuple[int], List[int]] = 1,
        frequence: str = '1d'):
    """
    创建一个小型的汇总表格，包括因子的收益率分析，IC 值，换手率等分析

    参数
    ---
    :param factor_data: 因子数据
    :param long_short: 是否构建多空组合，在该组合上进行进行分析。
    :param group_neutral: 是否进行行业中性
    """
    if isinstance(periods, int):
        periods = [
            periods,
        ]
    # 收益分析
    mean_quant_ret, std_quant = perf.mean_return_by_quantile(
        factor_data,
        by_group=by_group,
        demeaned=long_short,
        group_adjust=group_neutral)
    mean_quant_rateret = mean_quant_ret.apply(
        utils.rate_of_return,
        axis=0,
        base_period=mean_quant_ret.columns[0]
    )
    std_quant_rate = std_quant.apply(
        utils.std_conversion,
        axis=0,
        base_period=std_quant.columns[0]
    )

    mean_quant_ret_bydatetime, std_quant_bydatetime = perf.mean_return_by_quantile(
        factor_data,
        by_datetime=by_datetime,
        by_group=by_group,
        demeaned=long_short,
        group_adjust=group_neutral)

    mean_quant_rateret_bydatetime = mean_quant_ret_bydatetime.apply(
        utils.rate_of_return,
        axis=0,
        base_period=mean_quant_ret_bydatetime.columns[0]
    )
    std_quant_rate_bydatetime = std_quant_bydatetime.apply(
        utils.std_conversion,
        axis=0,
        base_period=std_quant_bydatetime.columns[0]
    )

    alpha_beta = perf.factor_alpha_beta(
        factor_data=factor_data,
        demeaned=long_short,
        group_adjust=group_neutral
    )

    mean_ret_spread_quant, std_spread_quant = perf.mean_returns_spread(
        mean_quant_rateret,
        upper_quant=factor_data.factor_quantile.max(),
        lower_quant=factor_data.factor_quantile.min(),
        std_err=std_quant_rate)

    fr_cols = utils.get_forward_returns_columns(factor_data.columns)

    vertical_sections = 2 + len(fr_cols) * 3
    gf = GridFigure(rows=vertical_sections, cols=1)

    plotting.plot_quantile_statistics_table(factor_data)
    plotting.plot_returns_table(
        alpha_beta,
        mean_quant_rateret,
        mean_ret_spread_quant
    )
    plotting.plot_quantile_returns_bar(
        mean_quant_rateret,
        by_group=by_group,
        ylim_percentiles=None,
        ax=gf.next_row()
    )

    # Information Analysis
    ic = perf.factor_information_coefficient(factor_data)
    plotting.plot_information_table(ic)

    # Turnover Analysis
    # FIXME: 股票是 T+1，意味着频率只能是 Day 及以上频率
    quantile_factor = factor_data["factor_quantile"]
    quantile_turnover = {
        p: pd.concat(
            [
                perf.quantile_turnover(quantile_factor,
                                       q,
                                       p)
                for q in range(1,
                               int(quantile_factor.max()) + 1)
            ],
            axis=1,
        )
        for p in periods
    }
    autocorrelation = pd.concat(
        [
            perf.factor_rank_autocorrelation(factor_data,
                                             period) for period in periods
        ],
        axis=1,
    )

    plotting.plot_turnover_table(autocorrelation, quantile_turnover)

    plt.show()
    gf.close()


import collections
import copy
import datetime
import os
from typing import Any, Dict, List, Mapping, Optional, Sequence, Union

from absl import logging
from tfx import types
from tfx import version
from tfx.dsl.io import fileio
from tfx.orchestration import data_types_utils
from tfx.orchestration import node_proto_view
from tfx.proto.orchestration import execution_result_pb2
from tfx.proto.orchestration import pipeline_pb2
from tfx.types import artifact as tfx_artifact
from tfx.types import artifact_utils
from tfx.types.value_artifact import ValueArtifact
from tfx.utils import proto_utils

from ml_metadata.proto import metadata_store_pb2

_SYSTEM = '.system'
_EXECUTOR_EXECUTION = 'executor_execution'
_DRIVER_EXECUTION = 'driver_execution'
_STATEFUL_WORKING_DIR = 'stateful_working_dir'
_DRIVER_OUTPUT_FILE = 'driver_output.pb'
_EXECUTOR_OUTPUT_FILE = 'executor_output.pb'
_VALUE_ARTIFACT_FILE_NAME = 'value'
# The fixed special value to indicate that the binary will set the output URI
# value during its execution.
# LINT.IfChange
RESOLVED_AT_RUNTIME = '{resolved_at_runtime}'
# LINT.ThenChange(<Internal source code>)
_ORCHESTRATOR_GENERATED_BCL_DIR = 'orchestrator_generated_bcl'

def make_output_dirs(
    output_dict: Mapping[str, Sequence[types.Artifact]]) -> None:
  """Make dirs for output artifacts' URI."""
  for _, artifact_list in output_dict.items():
    for artifact in artifact_list:
      # Omit lifecycle management for external artifacts.
      if artifact.is_external:
        continue
      if isinstance(artifact, ValueArtifact):
        # If this is a ValueArtifact, create the file if it does not exist.
        if not fileio.exists(artifact.uri):
          artifact_dir = os.path.dirname(artifact.uri)
          fileio.makedirs(artifact_dir)
          with fileio.open(artifact.uri, 'w') as f:
            # Because fileio.open won't create an empty file, we write an
            # empty string to it to force the creation.
            f.write('')
      else:
        # Otherwise create a dir.
        fileio.makedirs(artifact.uri)

def remove_output_dirs(
    output_dict: Mapping[str, Sequence[types.Artifact]]) -> None:
  """Remove dirs of output artifacts' URI."""
  for _, artifact_list in output_dict.items():
    for artifact in artifact_list:
      # Omit lifecycle management for external artifacts.
      if artifact.is_external:
        continue
      if fileio.isdir(artifact.uri):
        fileio.rmtree(artifact.uri)
      else:
        fileio.remove(artifact.uri)

def clear_output_dirs(
    output_dict: Mapping[str, Sequence[types.Artifact]]) -> None:
  """Clear dirs of output artifacts' URI."""
  for _, artifact_list in output_dict.items():
    for artifact in artifact_list:
      # Omit lifecycle management for external artifacts.
      if artifact.is_external:
        continue
      # Clear out the contents of the output directory while preserving the
      # output directory itself. Needed to preserve any storage attributes of
      # the output directory.
      if not fileio.isdir(artifact.uri):
        continue
      child_paths = [
          os.path.join(artifact.uri, filename)
          for filename in fileio.listdir(artifact.uri)
      ]
      for path in child_paths:
        if fileio.isdir(path):
          fileio.rmtree(path)
        else:
          fileio.remove(path)

def remove_stateful_working_dir(stateful_working_dir: str) -> None:
  """Remove stateful_working_dir."""
  # Clean up stateful working dir
  # Note that:
  # stateful_working_dir = os.path.join(
  #    self._node_dir,
  #    _SYSTEM,
  #    _STATEFUL_WORKING_DIR, <-- we want to clean from this level down.
  #    dir_suffix)
  stateful_working_dir = os.path.abspath(
      os.path.join(stateful_working_dir, os.pardir))
  try:
    fileio.rmtree(stateful_working_dir)
  except fileio.NotFoundError:
    logging.warning(
        'stateful_working_dir %s is not found, not going to delete it.',
        stateful_working_dir)

def _attach_artifact_properties(spec: pipeline_pb2.OutputSpec.ArtifactSpec,
                                artifact: types.Artifact):
  """Attaches properties of an artifact using ArtifactSpec."""
  for key, value in spec.additional_properties.items():
    if not value.HasField('field_value'):
      raise RuntimeError('Property value is not a field_value for %s' % key)
    if value.field_value.HasField('proto_value'):
      # Proto properties need to be unpacked from the google.protobuf.Any
      # message to its concrete message before setting the artifact property
      property_value = proto_utils.unpack_proto_any(
          value.field_value.proto_value)
    else:
      property_value = data_types_utils.get_metadata_value(value.field_value)
    setattr(artifact, key, property_value)

  for key, value in spec.additional_custom_properties.items():
    if not value.HasField('field_value'):
      raise RuntimeError('Property value is not a field_value for %s' % key)
    value_type = value.field_value.WhichOneof('value')
    if value_type == 'int_value':
      artifact.set_int_custom_property(key, value.field_value.int_value)
    elif value_type == 'string_value':
      artifact.set_string_custom_property(key, value.field_value.string_value)
    elif value_type == 'double_value':
      artifact.set_float_custom_property(key, value.field_value.double_value)
    elif value_type == 'proto_value':
      proto_value = proto_utils.unpack_proto_any(value.field_value.proto_value)
      artifact.set_proto_custom_property(key, proto_value)
    else:
      raise RuntimeError(f'Unexpected value_type: {value_type}')

def _get_node_dir(
    pipeline_runtime_spec: pipeline_pb2.PipelineRuntimeSpec, node_id: str
) -> str:
  """Gets node dir for the given pipeline node."""
  return os.path.join(
      pipeline_runtime_spec.pipeline_root.field_value.string_value, node_id
  )

class OutputsResolver:
  """This class has methods to handle launcher output related logic."""

  def __init__(self,
               pipeline_node: Union[pipeline_pb2.PipelineNode,
                                    node_proto_view.NodeProtoView],
               pipeline_info: pipeline_pb2.PipelineInfo,
               pipeline_runtime_spec: pipeline_pb2.PipelineRuntimeSpec,
               execution_mode: 'pipeline_pb2.Pipeline.ExecutionMode' = (
                   pipeline_pb2.Pipeline.SYNC)):
    self._pipeline_node = pipeline_node
    self._pipeline_info = pipeline_info
    self._pipeline_root = (
        pipeline_runtime_spec.pipeline_root.field_value.string_value)
    self._pipeline_run_id = (
        pipeline_runtime_spec.pipeline_run_id.field_value.string_value)
    self._execution_mode = execution_mode
    self._node_dir = _get_node_dir(
        pipeline_runtime_spec, pipeline_node.node_info.id
    )

  def generate_output_artifacts(
      self, execution_id: int) -> Dict[str, List[types.Artifact]]:
    """Generates output artifacts given execution_id."""
    return generate_output_artifacts(
        execution_id=execution_id,
        outputs=self._pipeline_node.outputs.outputs,
        node_dir=self._node_dir,
        pipeline_root=self._pipeline_root)

  def get_executor_output_uri(self, execution_id: int) -> str:
    """Generates executor output uri given execution_id."""
    return get_executor_output_uri(self._node_dir, execution_id)

  def get_driver_output_uri(self) -> str:
    driver_output_dir = os.path.join(
        self._node_dir, _SYSTEM, _DRIVER_EXECUTION,
        str(int(datetime.datetime.now().timestamp() * 1000000)))
    fileio.makedirs(driver_output_dir)
    return os.path.join(driver_output_dir, _DRIVER_OUTPUT_FILE)

  def get_stateful_working_directory(self,
                                     execution_id: Optional[int] = None) -> str:
    """Generates stateful working directory given (optional) execution id.

    Args:
      execution_id: An optional execution id which will be used as part of the
        stateful working dir path if provided. The stateful working dir path
        will be <node_dir>/.system/stateful_working_dir/<execution_id>. If
        execution_id is not provided, for backward compatibility purposes,
        <pipeline_run_id> is used instead of <execution_id> but an error is
        raised if the execution_mode is not SYNC (since ASYNC pipelines have no
        pipeline_run_id).

    Returns:
      Path to stateful working directory.

    Raises:
      ValueError: If execution_id is not provided and execution_mode of the
        pipeline is not SYNC.
    """
    return get_stateful_working_directory(self._node_dir, self._execution_mode,
                                          self._pipeline_run_id, execution_id)

  def make_tmp_dir(self, execution_id: int) -> str:
    """Generates a temporary directory."""
    return make_tmp_dir(self._node_dir, execution_id)

def _generate_output_artifact(
    output_spec: pipeline_pb2.OutputSpec) -> types.Artifact:
  """Generates each output artifact given output_spec."""
  artifact = artifact_utils.deserialize_artifact(output_spec.artifact_spec.type)
  _attach_artifact_properties(output_spec.artifact_spec, artifact)

  if output_spec.artifact_spec.is_async:
    # Mark the artifact state as REFERENCE to distinguish it from PUBLISHED
    # (LIVE in MLMD) intermediate artifacts emitted during a component's
    # execution. At the end  of the component's execution, its state will remain
    # REFERENCE instead of changing to PUBLISHED.
    artifact.state = tfx_artifact.ArtifactState.REFERENCE

  return artifact

def _validate_external_uri(external_uri: str,
                           pipeline_root: Optional[str]) -> None:
  """Validates a user-defined external artifact URI."""
  if external_uri == RESOLVED_AT_RUNTIME:
    return

  if pipeline_root and pipeline_root in external_uri:
    raise ValueError('External artifact URI %s is not allowed within the '
                     'pipeline base directory.' % external_uri)

def generate_output_artifacts(
    execution_id: int,
    outputs: Mapping[str, pipeline_pb2.OutputSpec],
    node_dir: str,
    pipeline_root: Optional[str] = None) -> Dict[str, List[types.Artifact]]:
  """Generates output artifacts.

  Args:
    execution_id: The id of the execution.
    outputs: Mapping from artifact key to its OutputSpec value in pipeline IR.
    node_dir: The root directory of the node.
    pipeline_root: Path to root directory of the pipeline.

  Returns:
    Mapping from artifact key to the list of TFX artifacts.

  Raises:
    ValueError: If any external artifact uri is inside the pipeline_root.
  """

  output_artifacts = collections.defaultdict(list)
  for key, output_spec in outputs.items():
    artifact = _generate_output_artifact(output_spec)
    if output_spec.artifact_spec.external_artifact_uris:
      for external_uri in output_spec.artifact_spec.external_artifact_uris:
        _validate_external_uri(external_uri, pipeline_root)
        external_artifact = copy.deepcopy(artifact)
        external_artifact.uri = external_uri
        external_artifact.is_external = True

        logging.debug('Creating external output artifact uri %s',
                      external_artifact.uri)
        output_artifacts[key].append(external_artifact)

    else:
      artifact.uri = os.path.join(node_dir, key, str(execution_id))
      if isinstance(artifact, ValueArtifact):
        artifact.uri = os.path.join(artifact.uri, _VALUE_ARTIFACT_FILE_NAME)

      logging.debug('Creating output artifact uri %s', artifact.uri)
      output_artifacts[key].append(artifact)

  return output_artifacts

def get_executor_output_uri(node_dir, execution_id: int) -> str:
  """Generates executor output uri given execution_id."""
  # LINT.IfChange(execution_output_uri)
  execution_dir = os.path.join(node_dir, _SYSTEM, _EXECUTOR_EXECUTION,
                               str(execution_id))
  fileio.makedirs(execution_dir)
  return os.path.join(execution_dir, _EXECUTOR_OUTPUT_FILE)
  # LINT.ThenChange(<Internal source code for executor_output_uri>)

def get_stateful_working_directory(node_dir: str,
                                   execution_mode: pipeline_pb2.Pipeline
                                   .ExecutionMode = pipeline_pb2.Pipeline.SYNC,
                                   pipeline_run_id: str = '',
                                   execution_id: Optional[int] = None) -> str:
  """Generates stateful working directory.

  Args:
    node_dir: The root directory of the node.
    execution_mode: Execution mode of the pipeline.
    pipeline_run_id: Optional pipeline_run_id, only available if execution mode
      is SYNC.
    execution_id: An optional execution id which will be used as part of the
      stateful working dir path if provided. The stateful working dir path will
      be <node_dir>/.system/stateful_working_dir/<execution_id>. If execution_id
      is not provided, for backward compatibility purposes, <pipeline_run_id> is
      used instead of <execution_id> but an error is raised if the
      execution_mode is not SYNC (since ASYNC pipelines have no
      pipeline_run_id).

  Returns:
    Path to stateful working directory.

  Raises:
    ValueError: If execution_id is not provided and execution_mode of the
      pipeline is not SYNC.
  """
  if (execution_id is None and execution_mode != pipeline_pb2.Pipeline.SYNC):
    raise ValueError(
        'Cannot create stateful working dir if execution id is `None` and '
        'the execution mode of the pipeline is not `SYNC`.')

  if execution_id is None:
    dir_suffix = pipeline_run_id
  else:
    dir_suffix = str(execution_id)

  # TODO(b/150979622): We should introduce an id that is not changed across
  # retries of the same component run to provide better isolation between
  # "retry" and "new execution". When it is available, introduce it into
  # stateful working directory.
  # NOTE: If this directory structure is changed, please update
  # the remove_stateful_working_dir function in this file accordingly.
  stateful_working_dir = os.path.join(node_dir, _SYSTEM, _STATEFUL_WORKING_DIR,
                                      dir_suffix)
  try:
    fileio.makedirs(stateful_working_dir)
  except Exception:  # pylint: disable=broad-except
    logging.exception('Failed to make stateful working dir: %s',
                      stateful_working_dir)
    raise
  return stateful_working_dir

def make_tmp_dir(node_dir: str, execution_id: int) -> str:
  """Generates a temporary directory."""
  result = os.path.join(node_dir, _SYSTEM, _EXECUTOR_EXECUTION,
                        str(execution_id), '.temp', '')
  fileio.makedirs(result)
  return result

def tag_output_artifacts_with_version(
    output_artifacts: Optional[Mapping[str, Sequence[types.Artifact]]] = None):
  """Tag output artifacts with the current TFX version."""
  if not output_artifacts:
    return
  for unused_key, artifact_list in output_artifacts.items():
    for artifact in artifact_list:
      if not artifact.has_custom_property(
          artifact_utils.ARTIFACT_TFX_VERSION_CUSTOM_PROPERTY_KEY):
        artifact.set_string_custom_property(
            artifact_utils.ARTIFACT_TFX_VERSION_CUSTOM_PROPERTY_KEY,
            version.__version__)

def populate_output_artifact(
    executor_output: execution_result_pb2.ExecutorOutput,
    output_dict: Mapping[str, Sequence[types.Artifact]]):
  """Populate output_dict to executor_output."""
  for key, artifact_list in output_dict.items():
    artifacts = execution_result_pb2.ExecutorOutput.ArtifactList()
    for artifact in artifact_list:
      artifacts.artifacts.append(artifact.mlmd_artifact)
    executor_output.output_artifacts[key].CopyFrom(artifacts)

def populate_exec_properties(
    executor_output: execution_result_pb2.ExecutorOutput,
    exec_properties: Mapping[str, Any]):
  """Populate exec_properties to executor_output."""
  for key, value in exec_properties.items():
    v = metadata_store_pb2.Value()
    if isinstance(value, str):
      v.string_value = value
    elif isinstance(value, int):
      v.int_value = value
    elif isinstance(value, float):
      v.double_value = value
    else:
      logging.info(
          'Value type %s of key %s in exec_properties is not '
          'supported, going to drop it', type(value), key)
      continue
    executor_output.execution_properties[key].CopyFrom(v)

def get_orchestrator_generated_bcl_dir(
    pipeline_runtime_spec: pipeline_pb2.PipelineRuntimeSpec, node_id: str
) -> str:
  """Generates a root directory to hold orchestrator generated BCLs for the given node.

  Args:
    pipeline_runtime_spec: pipeline runtime specifications.
    node_id: unique id of the node within the pipeline.

  Returns:
    Path to orchestrator generated bcl root dir, which has the format
    `<node_dir>/.system/orchestrator_generated_bcl`
  """
  node_dir = _get_node_dir(pipeline_runtime_spec, node_id)
  orchestrator_generated_bcl_dir = os.path.join(
      node_dir, _SYSTEM, _ORCHESTRATOR_GENERATED_BCL_DIR
  )
  if not fileio.exists(orchestrator_generated_bcl_dir):
    try:
      fileio.makedirs(orchestrator_generated_bcl_dir)
    except Exception:  # pylint: disable=broad-except
      logging.exception(
          'Failed to make orchestrator generated bcl dir: %s',
          orchestrator_generated_bcl_dir,
      )
      raise
  return orchestrator_generated_bcl_dir


from typing import Optional
from typing import Union

import pyglet
from constants import UNICODE_PIECE_SYMBOLS
from core.piece import ColourType
from gui.layout import Layout
from gui.sprites import PieceSprite
from gui.sprites import RectangleSprite

PromotionSprites = Union[PieceSprite, RectangleSprite]

def promotion_sprites_generator(layout: Layout, white_promotion_batch, black_promotion_batch, background_group, promotion_group) -> list[PromotionSprites]:
    """Generates the sprites used by the PromotionOverlay"""
    promotion_backgrounds: list[PromotionSprites] = [RectangleSprite(2 * layout.square_size,
                                                                     (1 if colour.value else 6) * layout.square_size,
                                                                     4 * layout.square_size,
                                                                     layout.square_size,
                                                                     (100, 100, 200),
                                                                     white_promotion_batch if colour == ColourType.WHITE else black_promotion_batch,
                                                                     background_group)
                                                     for colour in ColourType]

    promotion_pieces: list[PromotionSprites] = [PieceSprite(str(piece_symbol),
                                                            (i + 2) * layout.square_size + layout.piece_offset[0],
                                                            (1 if colour else 6) * layout.square_size + layout.piece_offset[1],
                                                            layout.piece_size,
                                                            white_promotion_batch if colour == ColourType.WHITE.value else black_promotion_batch,
                                                            promotion_group)
                                                for i, piece_type in enumerate(UNICODE_PIECE_SYMBOLS[1:5])
                                                for colour, piece_symbol in enumerate(piece_type)]
    return promotion_backgrounds + promotion_pieces

class PromotionOverlay:
    """Pawn promotion overlay which draws the selectable promotion pieces"""
    background_group = pyglet.graphics.OrderedGroup(0)
    promotion_group = pyglet.graphics.OrderedGroup(1)
    white_promotion_batch = pyglet.graphics.Batch()
    black_promotion_batch = pyglet.graphics.Batch()
    promotion_overlay_sprites: list[PromotionSprites]

    def __init__(self, layout: Layout):
        self.promotion_overlay_sprites = promotion_sprites_generator(
            layout, self.white_promotion_batch, self.black_promotion_batch, self.background_group, self.promotion_group)

    def draw(self, promotion_colour: Optional[ColourType]):
        if promotion_colour is None:
            return
        elif promotion_colour == ColourType.WHITE:
            self.white_promotion_batch.draw()
        else:
            self.black_promotion_batch.draw()


import cherrypy
from various import *
from mako.template import Template
import smtplib

from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate, make_msgid
from email import Encoders
from email.Header import Header

from passwords import smtp_server, smtp_login, smtp_password

def getSMTPServer() :
    server = smtplib.SMTP(smtp_server)
    server.login(smtp_login, smtp_password)
    return server

def sendMail(to, mail) :
    """ Send a mail to a specified address. The mail comes from noreply@naphtaline.com and should never be responded to
    """
    try :
        server = getSMTPServer()
        server.sendmail('noreply@naphtaline.com', to, mail)
        server.quit()
    except :
        pass

def send_mail(mail):

    msg = MIMEMultipart()
    msg.set_charset('utf-8')
    msg['From'] = mail['From']
    msg['Bcc'] = COMMASPACE.join(mail['To'])
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = mail['Subject']
    msg['Reply-To'] = mail['Reply-To']
    msg['Message-ID'] = mail['Message-ID']
    msg['In-Reply-To'] = mail['In-Reply-To']
    #msg['Content-Type'] = 'text/plain; charset=UTF-8'

    msg.attach( MIMEText(mail['text'], _charset="utf-8") )

    for f in mail['files']:
        part = MIMEBase('application', "octet-stream")
        part.set_payload( open('./files/%s' % f['id'],"rb").read() )
        Encoders.encode_base64(part)
        part.add_header('Content-Disposition', 'attachment; filename="%s"' % f['name'])
        msg.attach(part)

    try:
        smtp = getSMTPServer()
        content = msg.as_string()
        smtp.sendmail(mail['Reply-To'], mail['To'], content)
        smtp.close()
    except :
        pass

def sendNewEntryNotifications(con, lang, project, category, id, files) :

    cursor = con.cursor()

    # list of recipients
    query = """
    SELECT mail
    FROM Client
    WHERE
        subscription = True
        AND allowed = True
        AND project = %s
    """
    cursor.execute(query, (projectID(con, project), ))
    recipients = []
    for row in cursor.fetchall() :
        recipients.append(row[0])

    # content of the message
    query = """
    SELECT
        Entry.relativeID,
        Entry.name,
        Entry.description,
        Entry.status,
        Entry.severity,
        Client.login,
        Client.mail,
        extract( day FROM Entry.creationDate),
        extract( month FROM Entry.creationDate),
        extract( year FROM Entry.creationDate)
    FROM Entry
    INNER JOIN Client
    ON Entry.creator = Client.id
    WHERE Entry.id = %s"""
    cursor.execute(query, (id, ))
    row = cursor.fetchone()
    relativeID = int(row[0])
    title = row[1]
    description = unFormatText(row[2])
    status = int(row[3])
    severity = int(row[4])
    creator = row[5]
    creatorMail = row[6]
    day = row[7]
    month = row[8]
    year = row[9]

    # creating Message-ID for this entry, and therefore the entry-point for the thread
    messageID = make_msgid()
    query = """
    UPDATE Entry SET lastMessageID = %s WHERE id = %s"""
    cursor.execute(query, (messageID,id))
    con.commit()

    mytemplate = Template(filename='templates/'+lang+'/mails/newEntry.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8')
    text = mytemplate.render(
        creator = creator,
        title = title,
        description = description,
        files = files,
        status = status,
        severity = severity
    )
    category = category[0].upper()+category[1:].lower()

    h = Header()
    h.append(category, 'utf-8')
    h.append(u'#'+str(relativeID)+u':', 'utf-8')
    h.append(title, 'utf-8')

    mail = {
        'From' : creator + ' <' + creatorMail + '>',
        'To' : recipients,
        'Subject' : h,
        'Reply-To' : project + '@projects.naphtaline.net',
        'Message-ID' : messageID,
        'In-Reply-To' : '',
        'text' : text,
        'files' : files,
    }

    send_mail(mail)

def sendEditEntryNotifications(con, lang, project, category, id, files) :

    cursor = con.cursor()

    # list of recipients
    query = """
    SELECT mail
    FROM Client
    WHERE
        subscription = True
        AND allowed = True
        AND project = %s
    """
    cursor.execute(query, (projectID(con, project), ))
    recipients = []
    for row in cursor.fetchall() :
        recipients.append(row[0])

    # content of the message
    query = """
    SELECT
        Entry.relativeID,
        Entry.name,
        Entry.description,
        Entry.status,
        Entry.severity,
        Client.login,
        Client.mail,
        extract( day FROM Entry.creationDate),
        extract( month FROM Entry.creationDate),
        extract( year FROM Entry.creationDate),
        Entry.lastMessageID
    FROM Entry
    INNER JOIN Client
    ON Entry.maintainer = Client.id
    WHERE Entry.id = %s"""
    cursor.execute(query, (id, ))

    row = cursor.fetchone()
    relativeID = int(row[0])
    title = row[1]
    description = unFormatText(row[2])
    status = int(row[3])
    severity = int(row[4])
    maintainer = row[5]
    maintainerMail = row[6]
    day = row[7]
    month = row[8]
    year = row[9]
    inReplyTo = row[10]

    # updating Message-ID
    messageID = make_msgid()
    query = """
    UPDATE Entry SET lastMessageID = %s WHERE id = %s"""
    cursor.execute(query, (messageID,id))
    con.commit()

    mytemplate = Template(filename='templates/'+lang+'/mails/editEntry.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8')
    text = mytemplate.render(
        title = title,
        description = description,
        files = files,
        status = status,
        severity = severity,
        category = category,
        relativeID = relativeID,
        maintainer = maintainer
    )
    category = category[0].upper()+category[1:].lower()

    h = Header()
    h.append(u'Re:', 'utf-8')
    h.append(category, 'utf-8')
    h.append(u'#'+str(relativeID)+u':', 'utf-8')
    h.append(title, 'utf-8')

    mail = {
        'From' : maintainer + ' <' + maintainerMail + '>',
        'To' : recipients,
        'Subject' : h,
        'Reply-To' : project + '@projects.naphtaline.net',
        'Message-ID' : messageID,
        'In-Reply-To' : inReplyTo,
        'text' : text,
        'files' : files,
    }

    send_mail(mail)

def sendNewCommentNotifications(con, lang, project, category, id, content, files) :

    cursor = con.cursor()

    # creating the list of recipients
    query = """
    SELECT mail
    FROM Client
    WHERE
        subscription = True
        AND allowed = True
        AND project = %s
    """
    cursor.execute(query, (projectID(con, project), ))
    recipients = []
    for row in cursor.fetchall() :
        recipients.append(row[0])

    # creating the message
    query = """
    SELECT
        Client.login,
        Client.mail,
        Comment.entry,
        extract( day FROM Comment.creationDate),
        extract( month FROM Comment.creationDate),
        extract( year FROM Comment.creationDate),
        Entry.name,
        Entry.relativeID,
        Entry.lastMessageID
    FROM Comment
    INNER JOIN Entry
    ON Comment.entry = Entry.id
    INNER JOIN Client
    ON Comment.author = Client.id
    WHERE Comment.id = %s"""
    cursor.execute(query, (id, ))
    row = cursor.fetchone()
    author = row[0]
    authorMail = row[1]
    idEntry = int(row[2])
    day = int(row[3])
    month = int(row[4])
    year = int(row[5])
    title = row[6]
    relativeID = int(row[7])
    inReplyTo = row[8]

    # updating Message-ID
    messageID = make_msgid()
    query = """
    UPDATE Entry SET lastMessageID = %s WHERE id = %s"""
    cursor.execute(query, (messageID,id))
    con.commit()

    mytemplate = Template(filename='templates/'+lang+'/mails/newcomment.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8')
    text = mytemplate.render(
        author = author,
        comment = content,
        files = files
    )
    category = category[0].upper()+category[1:].lower()

    h = Header()
    h.append(u'Re:', 'utf-8')
    h.append(category, 'utf-8')
    h.append(u'#'+str(relativeID)+u':', 'utf-8')
    h.append(title, 'utf-8')

    mail = {
        'From' : author + ' <' + authorMail + '>',
        'To' : recipients,
        'Subject' : h,
        'Reply-To' : project + '@projects.naphtaline.net',
        'Message-ID' : messageID,
        'In-Reply-To' : inReplyTo,
        'text' : text,
        'files' : files,
    }

    send_mail(mail)

def sendDeleteNotifications(con, lang, project, category, relativeID, userID):
    """ Send a notification in case of delete """
    cursor = con.cursor()
    # creating the list of recipients
    query = """
    SELECT mail
    FROM Client
    WHERE
        subscription = True
        AND allowed = True
        AND project = %s"""
    cursor.execute(query, (projectID(con, project), ))
    recipients = []
    for row in cursor.fetchall() :
        recipients.append(row[0])
    # get message content
    query = """
    SELECT name, lastMessageID
    FROM Entry
    WHERE relativeID = %s AND category = %s"""
    categoryCode = {'bug' : 1, 'feature' : 2}
    cursor.execute(query, (relativeID, categoryCode[category]))
    row = cursor.fetchone()
    title = row[0]
    inReplyTo = row[1]
    # user caracteristics (the one who deleted the entry)
    query = """SELECT login, mail FROM Client WHERE id=%s"""
    cursor.execute(query, (userID, ))
    row = cursor.fetchone()
    author = row[0]
    authorMail = row[1]
    # load template
    mytemplate = Template(filename='templates/'+lang+'/mails/delete.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8')
    text = mytemplate.render(creator = author)
    category = category[0].upper()+category[1:].lower()

    h = Header()
    h.append(u'Re:', 'utf-8')
    h.append(category, 'utf-8')
    h.append(u'#'+str(relativeID)+u':', 'utf-8')
    h.append(title, 'utf-8')

    # make messageID
    # no need to save it to the database : the entry will be deleted
    messageID = make_msgid()

    mail = {'From' : author + ' <' + authorMail + '>',
            'To' : recipients,
            'Subject' : h,
            'Reply-To' : project + '@projects.naphtaline.net',
            'Message-ID' : messageID,
            'In-Reply-To' : inReplyTo,
            'text' : text,
            'files' : []}

    send_mail(mail)