from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math
import time
import random
import copy as cp

from xoa.commons.logger import *

import numpy as np

from .div import DiversificationStrategy

MAX_ERROR = 100.0

class ClassicHedgeStrategy(DiversificationStrategy):
    '''
    algorithm code referred from:
    https://github.com/johnmyleswhite/BanditsBook/blob/master/python/algorithms/hedge/hedge.py
    '''
    def __init__(self, arms, temperature, values, counts, title=""):
        name = "HEDGE"
        if title != "":
            name = name + "_" + title

        super(ClassicHedgeStrategy, self).__init__(name, num_arms, values, counts, 0.0)
        self.temperature = temperature

    def categorical_draw(self, probs):
        z = random.random()
        cum_prob = 0.0
        for i in range(len(probs)):
            prob = probs[i]
            cum_prob += prob
            if cum_prob > z:
                return i
        raise ValueError("unrealistic status.")

    def next(self, step):
        z = sum([math.exp(v * self.temperature) for v in self.values])
        probs = [math.exp(v * self.temperature) / z for v in self.values]
        return self.categorical_draw(probs)

    def update(self, chosen_arm, curr_acc, opt):
        reward = curr_acc # TODO:reward design required
        self.counts[chosen_arm] = self.counts[chosen_arm] + 1
        value = self.values[chosen_arm]

        self.values[chosen_arm] = value + reward

class BayesianHedgeStrategy(DiversificationStrategy):
    ''' An extension of GP-Hedge algorthm'''
    def __init__(self, arms, temperature, values, counts,
                    s_space, choosers,
                    title="",
                    unbiased_estimation=False,
                    reward_scaling=None):
        name = "BO-HEDGE"
        if title != "":
            name = name + "_" + title

        super(BayesianHedgeStrategy, self).__init__(name, len(arms), values, counts, 0.0)

        self.temperature = temperature
        self.search_space = s_space
        self.choosers = choosers
        self.arms = arms
        self.nominees = None
        self.unbiased_estimation = unbiased_estimation
        self.reward_scaling = reward_scaling

    def categorical_draw(self, probs):
        z = random.random()
        cum_prob = 0.0
        for i in range(len(probs)):
            prob = probs[i]
            cum_prob += prob
            if cum_prob > z:
                return i
        raise ValueError("unrealistic status.")

    def nominate(self):
        all_nominees = []

        for arm in self.arms:

            optimizer = arm['model']
            aquisition_func = arm['acq_func']
            chooser = self.choosers[optimizer]
            # default error value.
            est_value = MAX_ERROR
            test_error = MAX_ERROR

            next_index, estimate = chooser.next(aquisition_func)
            if estimate is not None:
                i = estimate['means'].index(next_index)
                est_value = estimate['means'][i]

                try:
                    test_error = self.search_space.get_errors(next_index)
                except Exception as ex:
                    warn('No record in the search history: {}'.format(next_index))

            all_nominees.append({
                "optimizer": optimizer,
                "aquisition_func" : aquisition_func,
                "best_index" : next_index,
                "true_err" : test_error,
                "est_err" : est_value
            })
        return all_nominees

    def next(self, step):
        self.nominees = self.nominate()
        arm_index = 0
        try:
            z = sum([math.exp(v * self.temperature) for v in self.values])
            probs = [round(math.exp(v * self.temperature) / z, 3) for v in self.values]
            debug('probability:{}'.format(probs))
            arm_index = self.categorical_draw(probs)
        except Exception as ex:
            warn("Exception on hedge: {}".format(ex))
            arm_index = random.randrange(len(probs))
            debug("Uniform random select: {}".format(arm_index))

        return arm_index

    def update(self, arm_index, curr_acc, opt=None):

        self.counts[arm_index] = self.counts[arm_index] + 1

        if self.unbiased_estimation is False:
            for n in range(len(self.nominees)):
                selected_nominee = self.nominees[n]
                est_err = selected_nominee['est_err']
                value = self.values[n]
                self.values[n] = value + self.scale_reward(est_err)
        else:
            for n in range(len(self.nominees)):
                selected_nominee = self.nominees[n]
                err = selected_nominee['true_err']
                value = self.values[n]
                self.values[n] = value + self.scale_reward(err)

    def scale_reward(self, err):
        if self.reward_scaling is None:
            return -1.0 * err
        elif self.reward_scaling == "LOG_ERR":

            # scaling log with error

            abs_log_err = math.fabs(math.log10(err))
            return -1.0 * abs_log_err
        else:
            raise TypeError('unsupported reward scaling:{}'.format(self.reward_scaling))


from collections import Counter

class Solution:
    def __init__(self, input_file_name):
        self.puzzle_input = self.read_input_from_file(input_file_name)

    def read_input_from_file(self, file_name):
        with open(file_name) as file:
            return [line.strip() for line in file]

    def get_answer_part_1(self):
        two_same_letters = 0
        three_same_letters = 0
        for box in self.puzzle_input:
            letters_counter = Counter(box).values()
            if 2 in letters_counter:
                two_same_letters += 1
            if 3 in letters_counter:
                three_same_letters += 1

        print("Part 1:", two_same_letters * three_same_letters)

    def get_answer_part_2(self):
        for first_box_id in range(len(self.puzzle_input)):
            for second_box_id in range(first_box_id + 1, len(self.puzzle_input)):
                first_box = self.puzzle_input[first_box_id]
                second_box = self.puzzle_input[second_box_id]
                if len(first_box) != len(second_box):
                    continue

                same_letters = list(filter(lambda x: x[0] == x[1], zip(first_box, second_box)))
                if len(same_letters) - len(first_box) == -1:
                    print("Part 2: ", end="")
                    for f, s in same_letters:
                        print(f, end="")
                    print("")

    def get_answers(self):
        self.get_answer_part_1()
        self.get_answer_part_2()

if __name__ == "__main__":
    try:
        solutions = Solution("input.txt")
        solutions.get_answers()
    except FileNotFoundError as err:
        print("File input.txt not found.")


def gcd(a, b):
    if b > 0:
        return gcd(b, a % b)
    else:
        return a

t = int(input())
for _ in range(0, t):
    a, b = map(int, input().split())
    if a > b:
        gcdres = gcd(a, b)
    else:
        gcdres = gcd(b, a)
    lcmres = (a * b) // gcdres
    print(lcmres, gcdres)


import time
import pandas as pd
import numpy as np
import re
import datetime
import json
import random
import sys

import bs4 as bs
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from splinter import Browser
from webdriver_manager.chrome import ChromeDriverManager

# WSL Set Up (Only use this if running from WSL)
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import os.path

import utils

def launchBrowser():

    chrome_options = Options()
    chrome_options.add_argument("--headless") # Ensure GUI is off
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_prefs = {}
    chrome_options.experimental_options["prefs"] = chrome_prefs
    chrome_prefs["profile.default_content_settings"] = {"images": 2}
    # homedir = os.path.expanduser("~")
    # webdriver_service = Service(f"{homedir}/chromedriver/stable/chromedriver")
    webdriver_service = Service("/usr/local/bin/chromedriver")
    browser = Browser('chrome',service=webdriver_service, options=chrome_options)

def GetTableFromSoup(table_html, headers_html, include_href = True):

    colnames = []
    headers = headers_html.find_all("th")
    for header in headers:
        colnames.append(header.text)
    if include_href:
        colnames.append("href")

    data = []
    rows = table_html.find_all("tr")
    for row in rows:
        row.find_all("td")
        # get the href
        cell_data = [ele.text.strip() for ele in row.find_all("td")]
        if include_href:

            try:
                href = row.find("a").get("href")
                cell_data.append(href)
            except:
                cell_data.append(None)

        data.append(cell_data)

    data = pd.DataFrame(data, columns = colnames)

    return data

def main():
    browser = launchBrowser()

    ## PLAYER STATS ##
    all_seasons = pd.DataFrame()
    for season in range(2015,2024):
        url = f"https://www.espn.com/golf/stats/player/_/season/{season}"
        browser.visit(url)
        time.sleep(2.5)

        while True:
            # click show more
            try:
                browser.find_by_text('Show More').click()
                time.sleep(1)
            except:
                break

        soup = BeautifulSoup(browser.html, 'html.parser')

        tabs = soup.find_all("table")
        headers = soup.find_all("thead")
        a = GetTableFromSoup(tabs[0], headers[0], include_href=True)
        b = GetTableFromSoup(tabs[1], headers[1], include_href=False)
        # combine a and b into a dataframe
        df = pd.concat([a, b], axis = 1)
        # drop row with none values
        df = df.dropna()
        # add season column
        df['season'] = season

        # concat
        all_seasons = pd.concat([all_seasons, df], axis = 0)

    # convert earnings to numeric
    all_seasons['earnings'] = all_seasons['earnings'].str.replace('$', '').str.replace(',', '').astype(float)
    # rename all to snake case
    all_seasons.columns = [col.lower().replace(' ', '_') for col in all_seasons.columns]

    ## PLAYER BIO ##
    # unique hrefs:
    hrefs = all_seasons['href'].unique()

    player_bio = pd.DataFrame()
    for href in hrefs:
        browser.visit(href)
        try:
            time.sleep(0.2)
            soup = BeautifulSoup(browser.html, 'html.parser')
            # find ul with player header in the class
            bio_elems = soup.find('ul', class_ = re.compile('PlayerHeader__Bio')).find_all('li')
            info = []
            names = []
            for elem in bio_elems:
                divs = elem.find_all('div')
                names.append(divs[0].text.strip())
                info.append(divs[1].text.strip())

            df = pd.DataFrame(info, index = names).T
            df['href'] = href
            player_bio = pd.concat([player_bio, df], axis = 0)

        except:
            pass

    # clean up
    birthdates = player_bio['Birthdate'].str.split(' ')
    cleaned = []
    for birthdate in birthdates:
        try:
            cleaned.append(birthdate[0])
        except:
            cleaned.append(None)

    player_bio['Birthdate'] = cleaned
    # extract id from href
    player_bio['espn_id'] = player_bio['href'].str.split('/').str[-2]
    # drop height and weight
    player_bio = player_bio.drop(['Height', 'Weight', 'HT/WT'], axis = 1)
    # rename all to snake case
    player_bio.columns = [col.lower().replace(' ', '_') for col in player_bio.columns]

    # merge all_seasons and player_bio
    all_seasons = all_seasons.merge(player_bio[['href','espn_id']], on = 'href', how = 'left')
    # drop href
    all_seasons = all_seasons.drop('href', axis = 1)

    # write to db
    utils.write_to_db(all_seasons, "espn_stats", append=False)
    utils.write_to_db(player_bio, "espn_bio", append=False)

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(e)
        sys.exit(1)


import hmac
from lib.config import HMAC_KEY

def hmacEncrypt(text):
    """
    :param text: str, 原始字符串
    :return: str
    """
    key = HMAC_KEY
    if not isinstance(HMAC_KEY, bytes):
        key = bytes(HMAC_KEY, 'utf-8')
    if not isinstance(text, bytes):
        text = bytes(text, 'utf-8')
    h = hmac.new(key, text, digestmod='MD5')
    return h.hexdigest()


import logging

'''
logging.basicConfig(level=logging.DEBUG)

logging.info("Info Message")
logging.warning("Warning Message")
logging.error("Error Message")
logging.debug("Debug Message")
logging.critical("Critical Message")

print()
print ("--------------------------")
print()

logger = logging.getLogger("H4x0r")
logger.info("Info Message")
logger.critical("Crtical Message")
logger.log(logging.ERROR, "An error occcured")
'''

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('H4x0r')

logger.setLevel(logging.DEBUG)

handler = logging.FileHandler("logs.log")
handler.setLevel(logging.INFO)

formatter = logging.Formatter("%(levelname)s - %(asctime)s: %(message)s ")
handler.setFormatter(formatter)

logger.addHandler(handler)

logger.debug("Debug Message")
logger.info("Info Message")


import os
import json
import numpy as np
import pandas as pd
import math
from matplotlib import pyplot as plt
from sklearn.metrics import mean_absolute_error as mae

# 1 is me
def create_errors_of_data(file1, file2):
    with open(file1,'r') as data_file:
        data1 = json.loads(data_file.read())
    with open(file2,'r') as data_file:
        data2 = json.loads(data_file.read())

    size = len(data1)
    # print(size)
    l = []
    for i in range(size):
        di = {}
        train1 = data1[i]['train_config']
        train2 = data2[i]['train_config']

        d = data1[i]['eval_results']
        tgt_langs1 = list(d.keys())
        perfs1 = np.array(list(d.values()), dtype = float)

        d = data2[i]['eval_results']
        tgt_langs2 = list(d.keys())
        perfs2 = np.array(list(d.values()), dtype = float)
        error = np.absolute(perfs1-perfs2)

        di["train_config"] = train1
        d = dict(zip(tgt_langs1, error))
        di["eval_error"] = d
        l.append(di)
    return l

def create_rmse_per_task(file1, file2):
    with open(file1,'r') as data_file:
        data1 = json.loads(data_file.read())
    with open(file2,'r') as data_file:
        data2 = json.loads(data_file.read())

    size = len(data1)
    # print(size)
    l = []
    for i in range(size):
        di = {}
        train1 = data1[i]['train_config']
        train2 = data2[i]['train_config']

        d = data1[i]['eval_results']
        tgt_langs1 = list(d.keys())
        y_pred = np.array(list(d.values()), dtype = float)

        d = data2[i]['eval_results']
        tgt_langs2 = list(d.keys())
        y_test = np.array(list(d.values()), dtype = float)

        # mse = np.square(np.subtract(y_test,y_pred)).mean()
        # rmse = math.sqrt(mse)

        rmse = mae(y_test, y_pred)

        l.append(rmse)
    return np.average(np.array(l))

def create_average_perf_per_lang(file):
    with open(file,'r') as data_file:
        data = json.loads(data_file.read())
    l = []
    size = len(data)

    # p = np.zeros()
    # print(size)
    d = data[0]['eval_error']
    tgt_langs = list(d.keys())
    error = np.array(list(d.values()), dtype = float)
    flag = 0

    for i in range(size):
        di = {}
        train = data[i]['train_config']

        d = data[i]['eval_error']
        tgt_langs1 = list(d.keys())
        if tgt_langs1 != tgt_langs :
            flag = -1
            break
        error1 = np.array(list(d.values()), dtype = float)

        error = (error+error1)

    error = error/size

    if flag == -1:
        print("NOT ALL SAME EVAL LANG")

    return tgt_langs, error

dir1 = "ensem/preds/"
dir2 = "test_gold/"

outdir = "analysis/preds/"
error_per_task = []
for filename in os.listdir(dir1):
    l = {}
    t = {}
    if ".json" in filename:
        file1 = dir1+filename
        file2 = dir2+filename
        l = create_errors_of_data(file1, file2)

        if "surprise" in filename:
            t["filename"] = filename
            t["rmse"] = create_rmse_per_task(file1, file2)
            error_per_task.append(t)

            out = outdir+filename
            with open(out, "w") as outfile:
                json.dump(l, outfile)
        # p = create_average_perf_per_lang(file1)
        # break

out = "analysis/error_per_task.json"
with open(out, "w") as outfile:
    json.dump(error_per_task, outfile)

dir = "analysis/preds/"

error_per_lang = []
for filename in os.listdir(dir):
    l = {}
    if ".json" in filename:
        file = dir+filename
        l["file"] = filename
        lang, err = create_average_perf_per_lang(file)
        d = dict(zip(lang, err))
        l["error_lang"] = d
        error_per_lang.append(l)

out = "analysis/error_per_lang.json"
with open(out, "w") as outfile:
    json.dump(error_per_lang, outfile)

        # p = create_average_perf_per_lang(file1)
        # break


from dateutil.parser import isoparse

from oblique.database import crud

def test_pkg_infos_basic(client, db):
    db_pkg = crud.create_package(db, "transformers#api_1")
    crud.create_releases(db, [("v0.0.1", isoparse("2002-07-25T11:27:16"), False)], db_pkg.id)

    r = client.post("/api/pkg_infos", json={"pkg_name": "transformers#api_1"})

    assert r.status_code == 200
    data = r.json()
    assert data["last_release"] == "2002-07-25T11:27:16"
    assert data["n_versions"] == 1
    assert data["n_versions_yanked"] == 0

def test_pkg_infos_force_refresh(client, db):
    db_pkg = crud.create_package(db, "transformers#api_2")
    crud.create_releases(db, [("v0.0.1", isoparse("2002-07-25T11:27:16"), False)], db_pkg.id)

    r = client.post("/api/pkg_infos", json={"pkg_name": "transformers#api_2", "force_refresh": True})

    assert r.status_code == 200
    data = r.json()
    assert data["last_release"] == "2021-08-09T14:27:16"
    assert data["n_versions"] == 3
    assert data["n_versions_yanked"] == 1

def test_pkg_infos_unknown_package(client, db):
    r = client.post("/api/pkg_infos", json={"pkg_name": "unknown#api"})

    assert r.status_code == 404


from flask_restx import Resource

from app.modules.statistic.event.event_controller import EventController
from app.modules.statistic.event.event_dto import EventDto
from app.modules.auth.decorator import token_required, admin_token_required

api = EventDto.api

event_request = EventDto.model_request
event_response = EventDto.model_response

check_parser = api.parser()

@api.route('')
class EventList(Resource):
    # @token_required
    @api.response(code=200,model=event_response,description='Model for event response.')
    def get(self):
        '''
        Returns all events in the system
        ----------------------

        :return List of events.
        '''
        controller = EventController()
        return controller.get()

@api.route('/count')
class EventsCount(Resource):
    # @token_required
    @api.response(code=200,description='')
    def get(self):
        '''
        Return number of event in the system
        --------------------
        :return Num of events.
        '''
        controller = EventController()
        return controller.num_events()

@api.route('/<int:sid>/<int:cid>')
class Event(Resource):
    # @token_required
    @api.response(code=200,description='')
    def get(self,sid,cid):
        '''
        Returns number of event of sensor by SID.

        :param object_id

        :return Num of event.
        '''
        controller = EventController()
        return controller.get_by_sid_cid(sid,cid)


'''
This script runs noise_tests.test_min_signal_amplitude on a specified chip set
Requires noise_tests library
Requires a .json file containing chip-ids and daisy chain data formatted like
{
    'board': <board-name>,
    'chip_set': [
        [<chip-id>, <io-chain>],
        ...
        ]
}
'''

from __future__ import print_function
import argparse
import logging
from helpers.script_logging import ScriptLogger
import helpers.pathnames as pathnames
import helpers.larpix_scripting as larpix_scripting
import time
import larpix.larpix as larpix
import helpers.noise_tests as noise_tests
from sys import (exit, stdout)
import json
import os

start_time = time.localtime()
default_config = 'physics.json'

parser = argparse.ArgumentParser()
parser.add_argument('-b','--board', default=pathnames.default_board_file(start_time),
                    help='input file containing chipset info (optional, default: '
                    '%(default)s)')
parser.add_argument('-o','--outdir', default=pathnames.default_script_logdir(start_time),
                    help='output directory for log file and data file '
                    '(optional, default: %(default)s)')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('--global_threshold_correction', default=0, type=int,
                    help='Amount to increase the global threshold on each chip'
                    ' for sensitivity test'
                    '(optional, default: %(default)s)')
parser.add_argument('--pixel_trim_correction', default=0, type=int,
                    help='Amount to increase the pixel trim on each channel'
                    ' for sensitivity test '
                    '(optional, default: %(default)s)')
parser.add_argument('--max_dac_amp', default=10, type=int,
                    help='(optional, default: %(default)s)')
parser.add_argument('--min_dac_amp', default=0, type=int,
                    help='(optional, default: %(default)s)')
parser.add_argument('--n_pulses', default=10, type=int,
                    help='(optional, default: %(default)s)')
parser.add_argument('-s','--configuration_file', default=None,
                    help='initial chip configuration file to load '
                    'by default will look in %s for individual chip configurations, '
                    'if chip config not found, will load %s, '
                    'if this file does not exist, will load %s and generate new default '
                    '(optional)' % (pathnames.default_config_dir(start_time),
                                    pathnames.default_config_file(start_time),
                                    default_config))
parser.add_argument('-c','--chips', default=None, nargs='+', type=int,
                    help='chips to include in scan '
                    '(optional, default: all chips in chipset file)')
args = parser.parse_args()

infile = args.board
outdir = args.outdir
verbose = args.verbose
global_threshold_correction = args.global_threshold_correction
pixel_trim_correction = args.pixel_trim_correction
max_dac_amp = args.max_dac_amp
min_dac_amp = args.min_dac_amp
n_pulses = args.n_pulses
config_file = args.configuration_file
if config_file is None:
    config_file = pathnames.default_config_dir(start_time)
    default_config = pathnames.make_default_config(start_time, default_config)
chips_to_scan = args.chips

return_code = 0

script_logfile = outdir + '/' + \
    os.path.basename(pathnames.default_script_logfile(start_time))
data_logfile = outdir + '/' + os.path.basename(pathnames.default_data_logfile(start_time))
sl = ScriptLogger(start_time, script_logfile=script_logfile, data_logfile=data_logfile)
log = sl.get_script_log()
log.info('arguments: %s' % str(args))

try:
    controller = larpix.Controller(timeout=0.01)
    # Initial configuration of chips
    board_info = larpix_scripting.load_board(controller, infile)
    log.info('begin initial configuration of chips for board %s' % board_info)
    config_ok, different_registers = larpix_scripting.load_chip_configurations(
        controller, board_info, config_file, silence=True, default_config=default_config)
    if config_ok:
        log.info('initial configuration of chips complete')

    # Run sensitivity test on each chip
    board_results = []
    for chip_idx,chip in enumerate(controller.chips):
        try:
            start_time = time.time()
            chip_id = chip.chip_id
            io_chain = chip.io_chain
            chip_info = (io_chain, chip_id)
            if chips_to_scan is None:
                pass
            else:
                if not chip_id in chips_to_scan:
                    log.info('skipping %d-c%d' % chip_info)
                    board_results += [None]
                    continue

            larpix_scripting.clear_buffer(controller)
            chip_results = noise_tests.test_min_signal_amplitude(\
                controller=controller,chip_idx=chip_idx,
                threshold=chip.config.global_threshold + global_threshold_correction,
                trim=[trim + pixel_trim_correction
                      for trim in chip.config.pixel_trim_thresholds],
                n_pulses=n_pulses,
                min_dac_amp=min_dac_amp,
                max_dac_amp=max_dac_amp,
                reset_cycles=chip.config.reset_cycles)

            board_results += [chip_results]
            larpix_scripting.clear_stored_packets(controller)
            finish_time = time.time()
            if verbose:
                log.debug('%d-c%d sensitivity test took %.2f s' % \
                              (io_chain, chip_id, finish_time - start_time))
        except Exception as error:
            log.exception(error)
            log.error('%d-c%d sensitivity test failed!' % chip_info)
            controller.disable(chip_id=chip_id, io_chain=io_chain)
            return_code = 2
            continue

    log.info('all chips sensitivity check complete')

    # Print sensitivity test results
    for chip_idx,chip in enumerate(controller.chips):
        chip_id = chip.chip_id
        io_chain = chip.io_chain
        if board_results[chip_idx] is None:
            log.info('%s-%d-c%d skipped' % (board_info, io_chain, chip_id))
            continue
        for channel in sorted(board_results[chip_idx].keys()):
            log.info('%s-%d-c%d-ch%d min dac: %d dac, efficiency: %.2f' % \
                         (board_info, io_chain, chip_id, channel,
                          board_results[chip_idx][channel]['min_pulse_dac'],
                          board_results[chip_idx][channel]['eff']))

except Exception as error:
    log.exception(error)
    return_code = 1

exit(return_code)


# 线程, 进程
# 进程是资源单位, 每一个进程至少要有一个线程
# 线程是执行单位

# 启动每一个程序默认都会有一个主线程
from threading import Thread  # 线程类

def func():
    for i in range(10):
        print("func", i)

if __name__ == '__main__':
    func()
    for i in range(1000):
        print("main", i)

#  多线程

def func():
    for i in range(10):
        print("func", i)

if __name__ == '__main__':
    t = Thread(target=func)  # 创建线程并给线程安排任务
    t.start()  # 多线程状态为可以开始工作状态, 具体的执行时间由CPU决定

    for i in range(10):
        print("main", i)

# 多线程 类
class MyThread(Thread):  #
    def run(self):  # 固定的    -> 当线程被执行的时候, 被执行的就是run()
        for i in range(10):
            print("子线程", i)

if __name__ == '__main__':
    t = MyThread()
    # t.run()  # 方法的调用了. -> 单线程????
    t.start()  # 开启线程

    for i in range(10):
        print("主线程", i)

#  多线程 传参
def func(name):
    for i in range(10):
        print(name, i)

if __name__ == '__main__':
    t = Thread(target=func, args=("周杰伦", ))  # 创建线程并给线程安排任务，传入的参数是元组，所以“周杰伦”后面要加逗号
    t.start()  # 多线程状态为可以开始工作状态, 具体的执行时间由CPU决定

    for i in range(10):
        print("main", i)

# 多线程 传参
class MyThread(Thread):  #
    def __init__(self, name=None):
        Thread.__init__(self, name=None)
        self.name = name

    def run(self):  # 固定的    -> 当线程被执行的时候, 被执行的就是run()
        for i in range(10):
            print(self.name, i)

if __name__ == '__main__':
    t = MyThread(name="周杰伦")
    # t.run()  # 这就是方法的调用了. -> 单线程
    t.start()  # 开启线程

    for i in range(10):
        print("主线程", i)


import copy
from typing import Sequence, Tuple

from bsuite.baselines import base
from bsuite.baselines.utils import replay

import dm_env
from dm_env import specs
import numpy as np
import sonnet as snt
import tensorflow as tf
#tf.config.run_functions_eagerly(True)

from absl import app
from absl import flags
from absl import logging
import dm_memorytasks
import numpy as np
from datetime import datetime
import pandas as pd
import copy
import pygame

FLAGS = flags.FLAGS

_FRAMES_PER_SECOND = 30

flags.DEFINE_list(
    'screen_size', [2048, 1024],
    'Screen width/height in pixels. Scales the environment RGB observations to '
    'fit the screen size.')

flags.DEFINE_string(
    'docker_image_name', None,
    'Name of the Docker image that contains the Memory Tasks. '
    'If None, uses the default dm_memorytask name')

flags.DEFINE_boolean(
    'silent', False,
    'Whether or not the graphical interface should be displayed.')

flags.DEFINE_integer('seed', 123, 'Environment seed.')
flags.DEFINE_string('level_name', 'visible_goal_with_buildings_train',
                    'Name of memory task to run.')
flags.DEFINE_integer('episode_length_seconds', '360', 'Length of episode')

# Tuning parameters
flags.DEFINE_float('learning_rate', .0001, 'Learning rate of the network')
flags.DEFINE_float('epsilon', .1, 'Epsilon of the network')
flags.DEFINE_float('discount', .99, 'Discount of the network')

from typing import Sequence, Tuple
import tree
import trfl
import tensorflow_probability as tfp

tfd = tfp.distributions

# Action structure {'MOVE_BACK_FORWARD': 0, 'STRAFE_LEFT_RIGHT': 0, 'LOOK_LEFT_RIGHT': 0, 'LOOK_DOWN_UP': 0} (ranges from -1, 1)
# Define the 8 possible actions
# Ignore look up and look down since they dont help accomplish the task
_ACTIONS = [
    {'MOVE_BACK_FORWARD': 1},
    {'MOVE_BACK_FORWARD': -1},
    {'STRAFE_LEFT_RIGHT': -1},
    {'STRAFE_LEFT_RIGHT': 1},
    {'LOOK_LEFT_RIGHT': -1},
    {'LOOK_LEFT_RIGHT': 1},
    #{'LOOK_DOWN_UP': -1},
    #{'LOOK_DOWN_UP': 1},
]

# Define default no action
_NO_ACTION = {
    'MOVE_BACK_FORWARD': 0,
    'STRAFE_LEFT_RIGHT': 0,
    'LOOK_LEFT_RIGHT': 0,
    'LOOK_DOWN_UP': 0,
}

class DQN(base.Agent):
  """A simple DQN agent using TF2."""

  def __init__(
      self,
      action_spec: specs.DiscreteArray,
      network: snt.Module,
      batch_size: int,
      discount: float,
      replay_capacity: int,
      min_replay_size: int,
      sgd_period: int,
      target_update_period: int,
      optimizer: snt.Optimizer,
      epsilon: float,
      seed: int = None,
  ):

    # Internalise hyperparameters.
    self._num_actions = action_spec.num_values
    self._discount = discount
    self._batch_size = batch_size
    self._sgd_period = sgd_period
    self._target_update_period = target_update_period
    self._epsilon = epsilon
    self._min_replay_size = min_replay_size

    # Seed the RNG.
    tf.random.set_seed(seed)
    self._rng = np.random.RandomState(seed)

    # Internalise the components (networks, optimizer, replay buffer).
    self._optimizer = optimizer
    self._replay = replay.Replay(capacity=replay_capacity)
    self._online_network = network
    self._target_network = copy.deepcopy(network)
    self._forward = tf.function(network)
    self._total_steps = tf.Variable(0)

  def select_action(self, timestep: dm_env.TimeStep) -> base.Action:
    # Epsilon-greedy policy.
    if self._rng.rand() < self._epsilon:
      return self._rng.randint(self._num_actions)

    observation = tf.convert_to_tensor(timestep.observation['RGB_INTERLEAVED'][None, ...].astype('float32'))
    # Greedy policy, breaking ties uniformly at random.
    q_values = self._forward(observation).numpy()
    #print(np.flatnonzero(q_values == q_values.max()))
    action = self._rng.choice(np.flatnonzero(q_values == q_values.max()))
    return int(action)

  def update(
      self,
      timestep: dm_env.TimeStep,
      action: base.Action,
      new_timestep: dm_env.TimeStep,
  ):
    # Add this transition to replay.
    self._replay.add([
        timestep.observation['RGB_INTERLEAVED'].astype('float32'),
        action,
        new_timestep.reward,
        new_timestep.discount,
        new_timestep.observation['RGB_INTERLEAVED'].astype('float32'),
    ])

    self._total_steps.assign_add(1)
    #print(self._total_steps)
    #print(tf.math.mod(self._total_steps, self._sgd_period).numpy().item())
    if tf.math.mod(self._total_steps, self._sgd_period).numpy().item() != 0:
      #print("NOPE1")
      return

    if self._replay.size < self._min_replay_size:
      #print("NOPE2")
      return

    # Do a batch of SGD.
    transitions = self._replay.sample(self._batch_size)
    self._training_step(transitions)

 # @tf.function
  def _training_step(self, transitions: Sequence[tf.Tensor]) -> tf.Tensor:
    """Does a step of SGD on a batch of transitions."""

    o_tm1, a_tm1, r_t, d_t, o_t = transitions

    r_t = tf.cast(r_t, tf.float32)  # [B]
    d_t = tf.cast(d_t, tf.float32)  # [B]
    o_tm1 = tf.convert_to_tensor(o_tm1)
    o_t = tf.convert_to_tensor(o_t)

    with tf.GradientTape() as tape:
      q_tm1 = self._online_network(o_tm1)  # [B, A]
      q_t = self._target_network(o_t)  # [B, A]

      onehot_actions = tf.one_hot(a_tm1, depth=self._num_actions)  # [B, A]
      qa_tm1 = tf.reduce_sum(q_tm1 * onehot_actions, axis=-1)  # [B]
      qa_t = tf.reduce_max(q_t, axis=-1)  # [B]

      #print(qa_tm1)

      # One-step Q-learning loss.
      target = r_t + d_t * self._discount * qa_t
      td_error = qa_tm1 - target
      loss = 0.5 * tf.reduce_mean(td_error**2)  # []

    # Update the online network via SGD.
    variables = self._online_network.trainable_variables
    gradients = tape.gradient(loss, variables)
    self._optimizer.apply(gradients, variables)

    # Periodically copy online -> target network variables.
    if tf.math.mod(self._total_steps, self._target_update_period).numpy().item() == 0:
      for target, param in zip(self._target_network.trainable_variables,
                              self._online_network.trainable_variables):
        target.assign(param)
    #print('loss', loss)
    return loss

# Default parameters
# def default_agent(obs_spec: specs.Array,
#                   action_spec: specs.DiscreteArray):
#   """Initialize a DQN agent with default parameters."""
#   del obs_spec  # Unused.
#   network = snt.Sequential([
#       snt.Flatten(),
#       snt.nets.MLP([50, 50, action_spec.num_values]),
#   ])
#   optimizer = snt.optimizers.Adam(learning_rate=1e-3)
#   return DQN(
#       action_spec=action_spec,
#       network=network,
#       batch_size=32,
#       discount=0.99,
#       replay_capacity=10000,
#       min_replay_size=100,
#       sgd_period=1,
#       target_update_period=4,
#       optimizer=optimizer,
#       epsilon=0.05,
#       seed=42)

def main(argv):
  if not FLAGS.silent:
    # Init pygame so we can watch the agent
    pygame.init()
    try:
      pygame.mixer.quit()
    except NotImplementedError:
      pass
    pygame.display.set_caption('Memory Tasks DQN Agent')

  env_settings = dm_memorytasks.EnvironmentSettings(
      seed=FLAGS.seed, level_name=FLAGS.level_name, episode_length_seconds=FLAGS.episode_length_seconds)

  with dm_memorytasks.load_from_docker(
      name=FLAGS.docker_image_name, settings=env_settings) as env:

    if not FLAGS.silent:
      # Pygame setup
      screen = pygame.display.set_mode(
        (int(FLAGS.screen_size[0]), int(FLAGS.screen_size[1])))
      rgb_spec = env.observation_spec()['RGB_INTERLEAVED']
      surface = pygame.Surface((rgb_spec.shape[1], rgb_spec.shape[0]))
      clock = pygame.time.Clock()

    # Manually define the action spec
    action_spec = specs.DiscreteArray(
        dtype=int, num_values=len(_ACTIONS), name="action")

    # network = snt.Sequential([
    #   snt.Flatten(),
    #   snt.nets.MLP([50,50, action_spec.num_values]),
    # ])

    # 2
    # network = snt.Sequential([
    #   snt.Conv2D(32, 3, 4),
    #   tf.nn.relu,
    #   snt.Conv2D(64, 2, 2),
    #   snt.Flatten(),
    #   snt.Linear(action_spec.num_values),
    # ])

    network = snt.Sequential([
       snt.Conv2D(32, 3, 4),
      tf.nn.relu,
      snt.Conv2D(32, 2, 2),
      tf.nn.relu,
      snt.Conv2D(64, 2, 2),
      tf.nn.relu,
      snt.Conv2D(64, 2, 2),
      snt.nets.MLP([32,8]),
      snt.Flatten(),
      snt.Linear(action_spec.num_values),
    ])

    # network = snt.Sequential([
    #   snt.Conv2D(32, 3, 4),
    #   tf.nn.max_pool(ksize=3,strides=4, padding="SAME"),
    #   #tf.nn.relu,
    #   #snt.BatchNorm(create_scale=True, create_offset=False, is_tr),
    #   snt.Conv2D(64, 2, 2),
    #   tf.nn.max_pool(ksize=2,strides=2, padding="SAME"),
    #   #tf.nn.relu,
    #   #snt.BatchNorm(create_scale=True, create_offset=False),
    #   snt.Conv2D(64, 3, 1),
    #   tf.nn.max_pool(ksizes=3,strides=1, padding="SAME"),
    #   #tf.nn.relu,
    #   #snt.Dropout(0.5),
    #   #snt.BatchNorm(create_scale=True, create_offset=False),
    #   snt.nets.MLP([32]),
    #   tf.nn.relu,

    #   #snt.LSTM(hidden_size=16),
    #   #snt.LSTM(hidden_size=16),
    #   snt.Flatten(),
    #   snt.Linear(action_spec.num_values),
    # ])

    # Set params of our agent
    optimizer = snt.optimizers.Adam(learning_rate=FLAGS.learning_rate)
    agent = DQN(
      action_spec=action_spec,
      network=network,
      batch_size=64,
      discount=FLAGS.discount,
      replay_capacity=10000,
      min_replay_size=100,
      sgd_period=2,
      target_update_period=6,
      optimizer=optimizer,
      epsilon=FLAGS.epsilon,
      seed=50)
    # Observation: OrderedDict([('Score', Array(shape=(), dtype=dtype('float64'), name='Score')), ('RGB_INTERLEAVED', Array(shape=(72, 96, 3), dtype=dtype('uint8'), name='RGB_INTERLEAVED')), ('AvatarPosition', Array(shape=(3,), dtype=dtype('float32'), name='AvatarPosition'))])

    timestep = env.reset()
    score = 0
    action_tracker = {}
    result_dict = {"timestamp": [], "score": [], "reward":[]}
    while not timestep.last():
      # Returns the index of the traction
      selected_action_idx = agent.select_action(timestep)

      # Track the number of each action taken
      action_ref = selected_action_idx
      if action_ref not in action_tracker:
          action_tracker[action_ref] = 1
      else:
          action_tracker[action_ref] += 1

      #print(action_tracker)

      # Get the key/value of the action by index
      selected_action = _ACTIONS[selected_action_idx]

      # Make a deep copy of the initial array
      action = copy.deepcopy(_NO_ACTION)

      # Get the name of the action and set the value in the array
      selected_action_name = list(selected_action.keys())[0]
      action[selected_action_name] = selected_action[selected_action_name]

      previous_timestep = copy.deepcopy(timestep)
      # Give the actor's action to the envrionment
      timestep = env.step(action)

      agent.update(previous_timestep, selected_action_idx, timestep)

      if not FLAGS.silent:
        # Update the view based on the action
        frame = np.swapaxes(timestep.observation['RGB_INTERLEAVED'], 0, 1)
        pygame.surfarray.blit_array(surface, frame)
        pygame.transform.smoothscale(surface, screen.get_size(), screen)
        pygame.display.update()
        clock.tick(_FRAMES_PER_SECOND)

      if timestep.reward:
        score += timestep.reward
        logging.info('Total score: %1.1f, reward: %1.1f', score,
                     timestep.reward)
        result_dict["timestamp"].append(datetime.now())
        result_dict["score"].append(score)
        result_dict["reward"].append(timestep.reward)

    logging.info('Final score: %1.1f', score)
    logging.info(f'Actions taken (key is the index of _ACTION variable): {action_tracker}')
    result_df = pd.DataFrame(result_dict)
    result_df.to_csv(f"dqn_agent_results_{datetime.now().timestamp()}.csv")

if __name__ == '__main__':
  app.run(main)


import bmesh
import bpy
import math
import mathutils

ring_count = 16
segments = ring_count * 2
size = 1.0

scene = bpy.context.scene

mesh = bpy.data.meshes.new('SpiralSphere')
spiral_sphere = bpy.data.objects.new('SpiralSphere', mesh)

scene.objects.link(spiral_sphere)
scene.objects.active = spiral_sphere
spiral_sphere.select = True

bm = bmesh.new()

prev_ring = None
ring = []
prev_prev_last = None

for r in range(ring_count):
    ring = []

    for s in range(segments):
        xy_theta = s * 2 * math.pi / segments
        z_theta = (r + (s / segments)) * math.pi / ring_count

        x = math.cos(xy_theta) * math.sin(z_theta)
        y = math.sin(xy_theta) * math.sin(z_theta)
        z = math.cos(z_theta)

        vert = bm.verts.new((x, y, z))
        ring.append(vert)

    if r == 1:
        bm.faces.new([prev_ring[segments - 1], ring[0], prev_ring[0]])
    elif r > 1:
        bm.faces.new([prev_ring[segments - 1], ring[0], prev_ring[0], prev_prev_last])

    for s in range(segments - 1):
        if r == 0:
            if s != 0:
                bm.faces.new([ring[s], ring[s+1], ring[0]])
        else:
            bm.faces.new([ring[s], ring[s+1], prev_ring[s+1], prev_ring[s]])

    if prev_ring is not None:
        prev_prev_last = prev_ring[segments - 1]
    prev_ring = ring

bottom = bm.verts.new((0.0, 0.0, -1.0))

bm.faces.new([bottom, prev_ring[0], prev_prev_last])

for s in range(segments - 1):
    bm.faces.new([bottom, prev_ring[s+1], prev_ring[s]])

bm.verts.ensure_lookup_table()
bm.verts.index_update()

uv_layer = bm.loops.layers.uv.new()
for face in bm.faces:
    safe = False
    for l in range(len(face.loops)):
        loop = face.loops[l]
        x, y, z = loop.vert.co

        u = math.atan2(y, x) / (2 * math.pi)
        if u < 0.0:
            u += 1.0

        v = -math.acos(z) / math.pi

        if l == 0 and u == 0:
            safe = True
        if u == 0.0 and not safe:
            u = 1.0

        loop[uv_layer].uv = mathutils.Vector((u, v))

for v in bm.verts:
    v.co *= size

bm.to_mesh(mesh)
bm.free()


import os
os.environ.setdefault('PLANTMONITOR_MODULE_SETTINGS', 'conf.settings.testing')

import unittest

from pony import orm

import datetime

from ORM.pony_manager import PonyManager

from plantmonitor.storage import PonyMetricStorage, ApiMetricStorage

from yamlns import namespace as ns
import datetime

# Test against a fully functioning api
from multiprocessing import Process
import uvicorn
import time
import requests

class ApiClient_Test(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        from conf import envinfo
        assert envinfo.SETTINGS_MODULE == 'conf.settings.testing'

        cls.pony = PonyManager(envinfo.DB_CONF)

        cls.pony.define_all_models()
        cls.pony.binddb(create_tables=True)

        from api_server.plantmonitor_api import api

        cls.api = api

    @classmethod
    def tearDownClass(cls):
        cls.pony.db.drop_all_tables(with_all_data=True)
        cls.pony.db.disconnect()

    def setUp(self):

        from conf import envinfo
        self.assertEqual(envinfo.SETTINGS_MODULE, 'conf.settings.testing')

        self.pony.db.drop_all_tables(with_all_data=True)

        self.pony.db.create_tables()

        orm.db_session.__enter__()

        # log_level = "debug"
        log_level = "warning"

        # create api and launch local test server
        self.proc = Process(target=uvicorn.run,
                    args=(self.api,),
                    kwargs={
                        "host": "127.0.0.1",
                        "port": self.apiPort(),
                        "log_level": log_level},
                    daemon=True)
        self.proc.start()
        time.sleep(0.1)

    def tearDown(self):
        orm.rollback()
        orm.db_session.__exit__()
        self.proc.terminate()

    def apiPort(self):
        return 5000

    def createApiClient(self):
        #TODO configfile
        config = {
            "api_url":"http://localhost:{}".format(self.apiPort()),
            "version":"1.0",
        }
        return ApiMetricStorage(config)

    def createPlantDict(self):
        plant_name = 'SomEnergia_Alibaba'
        inverter_name = 'Mary'
        metrics = ns([
            ('daily_energy_h_wh', 0),
            ('daily_energy_l_wh', 17556),
            ('e_total_h_wh', 566),
            ('e_total_l_wh', 49213),
            ('h_total_h_h', 0),
            ('h_total_l_h', 18827),
            ('pac_r_w', 0),
            ('pac_s_w', 0),
            ('pac_t_w', 0),
            ('powerreactive_t_v', 0),
            ('powerreactive_r_v', 0),
            ('powerreactive_s_v', 0),
            ('temp_inv_dc', 320),
            ('time', datetime.datetime.now(datetime.timezone.utc)),
        ])

        return plant_name, inverter_name, metrics

    def samplePlantNS(self):
        plantNS = ns.loads("""\
                name: alibaba
                codename: SCSOM04
                description: la bonica planta
                inverters:
                - inverter:
                    name: '5555'
                temperatureAmbientSensors:
                - temperatureAmbientSensor:
                    name: benjami""")
        return plantNS

    def samplePlantData(self, time):
        plantData = {
              "plant": "alibaba",
              "devices":
                sorted([{
                    "id": "Meter:1234578",
                    "readings": [{
                        "time": time,
                        "export_energy_wh": 1,
                        "import_energy_wh": 123,
                        "r1_VArh": 1234,
                        "r2_VArh": 124,
                        "r3_VArh": 1234,
                        "r4_VArh": 124,
                    }],
                },
                {
                    "id": "SensorTemperatureAmbient:5555",
                    "readings": [{
                        "time": time,
                        "temperature_dc": 170,
                    }]
                }],key=lambda d: d['id']),
              }
        return plantData

    def createPlant(self):
        plantNS = self.samplePlantNS()

        alibaba = self.pony.db.Plant(name=plantNS.name, codename=plantNS.codename, description=plantNS.description)
        alibaba = alibaba.importPlant(plantNS)
        orm.flush()
        # do we need to add some data?
        # time = datetime.datetime.now(datetime.timezone.utc)
        # plant_data = self.samplePlantData(time)

        # storage = PonyMetricStorage()
        # storage.insertPlantData(plant_data)

    def test__api_hello_version(self):
        response = requests.get("http://localhost:{}/version".format(self.apiPort()))
        self.assertEqual(response.status_code, 200)
        self.assertDictEqual(response.json(),{"version":"1.0"})

    def test_Environment(self):
        #TODO will it be too late if the config is misconfigured?
        from conf import envinfo
        self.assertEqual(envinfo.SETTINGS_MODULE, 'conf.settings.testing')

    def test__ApiMetricStorage__storeInverterMeasures(self):
        plant_name, inverter_name, inverter_registers = self.createPlantDict()

        apiClient = self.createApiClient()
        result = apiClient.storeInverterMeasures(plant_name, inverter_name, inverter_registers)

        self.assertTrue(result)

    def test__ApiMetricStorage_insertPlantData__empty(self):
        plant_data = {
            "plant": "Alibaba",
            "version": "1.0",
        }

        apiClient = self.createApiClient()
        result = apiClient.insertPlantData(plant_data)

        expectedError = {"detail":[{"loc":["body","time"],"msg":"field required","type":"value_error.missing"},{"loc":["body","devices"],"msg":"field required","type":"value_error.missing"}]}

        self.assertDictEqual(result, expectedError)

    def test__ApiMetricStorage_insertPlantData__someData(self):
        self.createPlant()
        time = datetime.datetime.now(datetime.timezone.utc)

        plant_data = {
            "plant": "alibaba",
            "version": "1.0",
            "time": time.isoformat(),
            "devices":
            [{
                "id": "SensorTemperatureAmbient:benjami",
                "readings":
                [{
                        "temperature_dc": 120,
                        "time": time.isoformat(),
                }]
            }]
        }

        apiClient = self.createApiClient()
        result = apiClient.insertPlantData(plant_data)
        print(result)
        # expectedResponse = {"detail":[{"loc":["body","time"],"msg":"OK","type":"None"},{"loc":["body","devices"],"msg":"OK","type":"None"}]}

        self.assertDictEqual(result, plant_data)

class Storage_Test(unittest.TestCase):

    from yamlns.testutils import assertNsEqual

    def setUp(self):

        from conf import envinfo
        self.assertEqual(envinfo.SETTINGS_MODULE, 'conf.settings.testing')

        orm.rollback()
        self.pony = PonyManager(envinfo.DB_CONF)

        self.pony.define_all_models()
        self.pony.binddb(create_tables=True)

        self.pony.db.drop_all_tables(with_all_data=True)

        self.storage = PonyMetricStorage(self.pony.db)

        self.maxDiff=None
        # orm.set_sql_debug(True)

        self.pony.db.create_tables()

        # database.generate_mapping(create_tables=True)
        orm.db_session.__enter__()

    def tearDown(self):
        orm.rollback()
        orm.db_session.__exit__()
        self.pony.db.drop_all_tables(with_all_data=True)
        self.pony.db.disconnect()

    def samplePlantData(self, time):
        plantData = {
            "plant": "alibaba",
            "devices":
                sorted([{
                    "id": "Meter:1234578",
                    "readings": [{
                        "time": time,
                        "export_energy_wh": 1,
                        "import_energy_wh": 123,
                        "r1_VArh": 1234,
                        "r2_VArh": 124,
                        "r3_VArh": 1234,
                        "r4_VArh": 124,
                    }],
                },
                {
                    "id": "SensorTemperatureAmbient:5555",
                    "readings": [{
                        "time": time,
                        "temperature_dc": 170,
                    }]
                }],key=lambda d: d['id']),
            }
        return plantData

    def test_Environment(self):
        #TODO will it be too late if the config is misconfigured?
        from conf import envinfo
        self.assertEqual(envinfo.SETTINGS_MODULE, 'conf.settings.testing')

    def test_connection(self):
        with orm.db_session:
            self.assertEqual(self.pony.db.get_connection().status, 1)

    def test__datetimeToStr(self):
        time = datetime.datetime.now(datetime.timezone.utc)
        plant_data = self.samplePlantData(time)
        ApiMetricStorage.datetimeToStr(plant_data)
        expected_plant_data = self.samplePlantData(time.isoformat())

        self.assertDictEqual(plant_data, expected_plant_data)

    #TODO deprecated as soon as we switch plant_data
    # test instead registries_to_plant_data in task.py
    def __test_PublishOrmOneInverterRegistry(self):
        plant_name = 'SomEnergia_Alibaba'
        inverter_name = 'Mary'
        with orm.db_session:
            alcolea = self.pony.db.Plant(name=plant_name,  codename='SOMSC01', description='descripción de planta')
            inverter = self.pony.db.Inverter(name=inverter_name, plant=alcolea)
            metrics = ns([
                    ('daily_energy_h_wh', 0),
                    ('daily_energy_l_wh', 17556),
                    ('e_total_h_wh', 566),
                    ('e_total_l_wh', 49213),
                    ('h_total_h_h', 0),
                    ('h_total_l_h', 18827),
                    ('pac_r_w', 0),
                    ('pac_s_w', 0),
                    ('pac_t_w', 0),
                    ('powerreactive_t_v', 0),
                    ('powerreactive_r_v', 0),
                    ('powerreactive_s_v', 0),
                    ('temp_inv_dc', 320),
                    ('time', datetime.datetime.now(datetime.timezone.utc)),
                    # Sensors registers  obtained from inverters
                    ('probe1value', 443),
                    ('probe2value', 220),
                    ('probe3value', 0),
                    ('probe4value', 0),
                    ])

            self.storage.storeInverterMeasures(
                plant_name, inverter_name, metrics)

            metrics.pop('probe1value')
            metrics.pop('probe2value')
            metrics.pop('probe3value')
            metrics.pop('probe4value')

            expectedRegistry = dict(metrics)
            expectedRegistry['inverter'] = inverter.id

            inverterReadings = self.storage.inverterReadings()
            self.assertEqual(inverterReadings, [expectedRegistry])

    def __test_PublishOrmIfInverterNotExist(self):
        inverter_name = 'Alice'
        plant_name = 'SomEnergia_Alibaba'
        with orm.db_session:
            alcolea = self.pony.db.Plant(name=plant_name,  codename='SOMSC01', description='descripción de planta')
            inverter = self.pony.db.Inverter(name=inverter_name, plant=alcolea)
            metrics = ns([
                ('daily_energy_h_wh', 0),
                ('daily_energy_l_wh', 17556),
                ('e_total_h_wh', 566),
                ('e_total_l_wh', 49213),
                ('h_total_h_h', 0),
                ('h_total_l_h', 18827),
                ('pac_r_w', 0),
                ('pac_s_w', 0),
                ('pac_t_w', 0),
                ('powerreactive_t_v', 0),
                ('powerreactive_r_v', 0),
                ('powerreactive_s_v', 0),
                ('temp_inv_dc', 320),
                ('time', datetime.datetime.now(datetime.timezone.utc)),
                # Sensors registers  obtained from inverters
                ('probe1value', 443),
                ('probe2value', 220),
                ('probe3value', 0),
                ('probe4value', 0),
                ])
            self.storage.storeInverterMeasures(
                plant_name, "UnknownInverter", metrics)

            self.assertListEqual(self.storage.inverterReadings(), [])

    def __test_PublishOrmIfPlantNotExist(self):
        inverter_name = 'Alice'
        plant_name = 'SomEnergia_Alibaba'
        with orm.db_session:
            alcolea = self.pony.db.Plant(name=plant_name,  codename='SOMSC01', description='descripción de planta')
            inverter = self.pony.db.Inverter(name=inverter_name, plant=alcolea)
            metrics = ns([
                ('daily_energy_h_wh', 0),
                ('daily_energy_l_wh', 17556),
                ('e_total_h_wh', 566),
                ('e_total_l_wh', 49213),
                ('h_total_h_h', 0),
                ('h_total_l_h', 18827),
                ('pac_r_w', 0),
                ('pac_s_w', 0),
                ('pac_t_w', 0),
                ('powerreactive_t_v', 0),
                ('powerreactive_r_v', 0),
                ('powerreactive_s_v', 0),
                ('temp_inv_dc', 320),
                ('time', datetime.datetime.now(datetime.timezone.utc)),
                # Sensors registers  obtained from inverters
                ('probe1value', 443),
                ('probe2value', 220),
                ('probe3value', 0),
                ('probe4value', 0),
                ])
            self.storage.storeInverterMeasures(
                'UnknownPlant', inverter_name, metrics)

            self.assertListEqual(self.storage.inverterReadings(), [])

    def test__PonyMetricStorage_insertPlantData__storeTemperatureSensor(self):
        sensor_name = 'Alice'
        plant_name = 'SomEnergia_Alibaba'
        time = datetime.datetime.now(datetime.timezone.utc)

        with orm.db_session:
            alcolea = self.pony.db.Plant(name=plant_name,  codename='SOMSC01', description='descripción de planta')
            sensor = self.pony.db.SensorTemperatureAmbient(name=sensor_name, plant=alcolea)
            orm.flush()
            plant_data = {
                "plant": plant_name,
                "version": "1.0",
                "time": time.isoformat(), #consider using fastapi.jsonable_encoder
                "devices":
                [{
                    "id": "SensorTemperatureAmbient:Alice",
                    "readings":
                    [{
                        "temperature_dc": 120,
                        "time": time,
                    }]
                }]
            }
            self.storage.insertPlantData(plant_data)
            orm.flush()

            expected_plant_data = {
                'plant': plant_name,
                'devices':
                [{
                    'id': 'SensorTemperatureAmbient:Alice',
                    'readings':
                    [{
                        "temperature_dc": 120,
                        "time": time,
                    }]
                }],
            }
            self.assertDictEqual(self.storage.plantData(plant_name), expected_plant_data)

    def test__PonyMetricStorage_insertPlantData__storeTemperatureSensorWithoutTemperature(self):
        sensor_name = 'Alice'
        plant_name = 'SomEnergia_Alibaba'
        time = datetime.datetime.now(datetime.timezone.utc)

        with orm.db_session:
            alcolea = self.pony.db.Plant(name=plant_name,  codename='SOMSC01', description='descripción de planta')
            sensor = self.pony.db.SensorTemperatureAmbient(name=sensor_name, plant=alcolea)
            orm.flush()
            plant_data = {
                "plant": plant_name,
                "version": "1.0",
                "time": time.isoformat(), #consider using fastapi.jsonable_encoder
                "devices":
                [{
                    "id": "SensorTemperatureAmbient:Alice",
                    "readings":
                    [{
                        "temperature_dc": None,
                        "time": time,
                    }]
                }]
            }
            self.storage.insertPlantData(plant_data)
            orm.flush()

            expected_plant_data = {
                'plant': plant_name,
                'devices': [{'id': 'SensorTemperatureAmbient:Alice', 'readings':
                    [{
                        "temperature_dc": None,
                        "time": time,
                    }]
                }],
            }

            self.assertDictEqual(self.storage.plantData(plant_name), expected_plant_data)

# vim: et ts=4 sw=4


import numpy as np
import pytest
from shapely.geometry import box, Point

from dask_geomodeling import raster
from dask_geomodeling.utils import shapely_transform
from dask_geomodeling.raster.sources import MemorySource

@pytest.fixture
def source():
    yield MemorySource(
        data=np.full((1, 10, 10), 7, dtype=np.uint8),
        no_data_value=255,
        projection="EPSG:28992",
        pixel_size=10,
        pixel_origin=(135000, 456000),
    )

@pytest.fixture(scope="module", params=["exact", "zoomed_in", "zoomed_out"])
def vals_request(request):
    if request.param == "exact":
        bbox = (0, 0, 100, 80)
    elif request.param == "zoomed_in":
        bbox = (0, 0, 50, 40)
    elif request.param == "zoomed_out":
        bbox = (0, 0, 200, 160)
    return dict(
        mode="vals",
        bbox=bbox,
        projection="EPSG:28992",
        width=int(bbox[2] / 10),
        height=int(bbox[3] / 10),
    )

@pytest.fixture
def empty():
    yield MemorySource(
        data=np.full((0, 0, 0), 7, dtype=np.uint8),
        no_data_value=255,
        projection="EPSG:28992",
        pixel_size=20,
        pixel_origin=(0, 0),
    )

@pytest.fixture
def center():
    # the source raster has data at x 135000..135100 and y 456000..455900
    yield 135050, 455950

@pytest.fixture
def center_epsg3857(center):
    yield shapely_transform(Point(*center), "EPSG:28992", "EPSG:3857").coords[0]

def test_place_attrs(source, center):
    place = raster.Place(source, "EPSG:28992", center, [(50, 50)])

    # Place should not adapt these attributes:
    assert place.period == source.period
    assert place.timedelta == source.timedelta
    assert place.dtype == source.dtype
    assert place.fillvalue == source.fillvalue

    # If the place projection equals the store projection:
    assert place.projection == source.projection
    assert place.geo_transform == source.geo_transform

    extent_epsg28992 = (0, 0, 100, 100)
    extent_epsg4326 = shapely_transform(
        box(*extent_epsg28992), "EPSG:28992", "EPSG:4326"
    ).bounds
    x1, x2, y1, y2 = place.geometry.GetEnvelope()
    assert (x1, y1, x2, y2) == extent_epsg28992
    assert place.extent == pytest.approx(extent_epsg4326, rel=1e-4)

def test_place_invalid_statistic(source, center):
    with pytest.raises(ValueError):
        raster.Place(source, "EPSG:28992", center, [(50, 50)], statistic="nonexisting")

def test_place_attrs_reproject(source, center_epsg3857):
    # Place should adapt the spatial attributes (extent & geometry)
    place = raster.Place(
        source, "EPSG:3857", center_epsg3857, [(572050, 6812050), (570050, 6811050)]
    )
    # The native projection != store projection:
    assert place.projection is None
    assert place.geo_transform is None

    extent_epsg3857 = 570000, 6811000, 572100, 6812100
    extent_epsg4326 = shapely_transform(
        box(*extent_epsg3857), "EPSG:3857", "EPSG:4326"
    ).bounds
    x1, x2, y1, y2 = place.geometry.GetEnvelope()
    assert (x1, y1, x2, y2) == pytest.approx(extent_epsg3857, rel=1e-4)
    assert place.extent == pytest.approx(extent_epsg4326, rel=1e-4)

def test_place_empty(empty, center, vals_request):
    place = raster.Place(empty, "EPSG:28992", center, [(50, 50)])

    assert place.geometry is None
    assert place.extent is None
    assert place.get_data(**vals_request) is None

def test_place_no_coords(source, center, vals_request):
    place = raster.Place(source, "EPSG:28992", center, [])
    values = place.get_data(**vals_request)["values"]
    assert (values[:, :10, :10] == source.fillvalue).all()

def test_place_exact(source, center, vals_request):
    place = raster.Place(source, "EPSG:28992", center, [(50, 50)])
    values = place.get_data(**vals_request)["values"]
    # swap Y axis for readable test
    values = values[:, ::-1, :]
    assert (values[:, :10, :10] == 7).all()

def test_place_reproject(source, center_epsg3857):
    target = 572050, 6812050  # EPSG3857 coords somewhere inside RD validity
    place = raster.Place(source, "EPSG:3857", center_epsg3857, [target])
    x, y = shapely_transform(Point(*target), "EPSG:3857", "EPSG:28992").coords[0]
    values = place.get_data(
        mode="vals",
        bbox=(x - 40, y - 40, x + 40, y + 40),
        projection="EPSG:28992",
        width=8,
        height=8,
    )["values"]
    assert (values == 7).all()

def test_place_horizontal_shift(source, center, vals_request):
    # shift 1 cell (10 meters) to the right
    place = raster.Place(source, "EPSG:28992", center, [(60, 50)])
    values = place.get_data(**vals_request)["values"]
    # swap Y axis for readable test
    values = values[:, ::-1, :]
    assert (values[:, :10, 1:11] == 7).all()
    assert (values[:, :, 0] == 255).all()

def test_place_vertical_shift(source, center, vals_request):
    # shift 1 cell (10 meters) up
    place = raster.Place(source, "EPSG:28992", center, [(50, 60)])
    values = place.get_data(**vals_request)["values"]
    # swap Y axis for readable test
    values = values[:, ::-1, :]
    assert (values[:, 1:11, :10] == 7).all()
    assert (values[:, 0, :] == 255).all()

@pytest.mark.parametrize(
    "statistic,expected",
    [
        ("first", (255, 7, 7, 7)),  # (no features, first, second, both)
        ("last", (255, 7, 7, 7)),  # the default
        ("count", (0, 1, 1, 2)),
        ("sum", (0, 7, 7, 14)),
        ("mean", (255, 7, 7, 7)),
        ("min", (255, 7, 7, 7)),
        ("max", (255, 7, 7, 7)),
        ("argmin", (255, 0, 1, 0)),
        ("argmax", (255, 0, 1, 0)),
        ("std", (255, 0, 0, 0)),
        ("var", (255, 0, 0, 0)),
        ("median", (255, 7, 7, 7)),
        ("p99", (255, 7, 7, 7)),
    ],
)
def test_place_multiple(source, center, vals_request, statistic, expected):
    # place such that only the left and bottom ridges have values
    place = raster.Place(
        source, "EPSG:28992", center, [(-40, 50), (50, -40)], statistic
    )
    values = place.get_data(**vals_request)["values"]
    # swap Y axis for readable test
    values = values[:, ::-1, :]
    # zero features
    assert (values[:, 1:, 1:] == expected[0]).all()
    # one feature
    assert (values[:, 1:10, 0] == expected[1]).all()
    assert (values[:, 0, 1:10] == expected[2]).all()
    # two features
    assert (values[:, 0, 0] == expected[3]).all()

def test_place_outside(source, center, vals_request):
    x1, y1, x2, y2 = vals_request["bbox"]
    coordinates = [(x1 - 50, y1), (x1, y1 - 50), (x2 + 50, y2), (x2, y2 + 50)]
    place = raster.Place(source, "EPSG:28992", center, coordinates)
    values = place.get_data(**vals_request)["values"]
    assert (values[:, :, :] == 255).all()

def test_place_time_request(source, center):
    place = raster.Place(source, "EPSG:28992", center, [(150, 50)])
    assert source.get_data(mode="time") == place.get_data(mode="time")

def test_place_meta_request(source, center):
    place = raster.Place(source, "EPSG:28992", center, [(150, 50)])
    assert source.get_data(mode="meta") == place.get_data(mode="meta")

@pytest.mark.parametrize(
    "point,expected",
    [
        ((5, 15), 7),  # zone 1
        ((15, 15), 255),  # zone 2
        ((5, 5), 255),  # zone 3
        ((15, 5), 7),  # zone 4
        ((10, 15), 255),  # line 1-2
        ((5, 10), 255),  # line 1-3
        ((15, 10), 7),  # line 2-4
        ((10, 5), 7),  # line 3-4
        ((10, 10), 7),  # center
        ((1000, 1000), 255),  # outside
    ],
)
def test_place_point_request(source, center, point, expected):
    # For point requests, edges are important. Let's do a drawing:
    # 20  _______
    #    |       |
    #    |   1   |   2
    # 10 |_______|_______
    #            |       |
    #        3   |   4   |
    #  0         |_______|
    #    0       10     20
    # - zone 1 and 4 are filled; zone 2 and 3 are empty (see below coordinates)
    # A pixel includes its topleft corner and top and left edges
    # - line between 2-4 and 3-4 are filled; line 1-2 and 1-3 are empty
    # - center point at (10, 10) is filled
    coordinates = [(60, -40), (-40, 60)]
    place = raster.Place(source, "EPSG:28992", anchor=center, coordinates=coordinates)
    point_request = dict(
        mode="vals", bbox=point * 2, projection="EPSG:28992", width=1, height=1
    )
    values = place.get_data(**point_request)["values"]
    assert values.shape == (1, 1, 1)
    assert values.item() == expected


"""Util that calls Google Search."""
from typing import Any, Dict, List, Optional

from langchain_core.pydantic_v1 import BaseModel, Extra, root_validator
from langchain_core.utils import get_from_dict_or_env

class GoogleSearchAPIWrapper(BaseModel):
    """Wrapper for Google Search API.

    Adapted from: Instructions adapted from https://stackoverflow.com/questions/
    37083058/
    programmatically-searching-google-in-python-using-custom-search

    TODO: DOCS for using it
    1. Install google-api-python-client
    - If you don't already have a Google account, sign up.
    - If you have never created a Google APIs Console project,
    read the Managing Projects page and create a project in the Google API Console.
    - Install the library using pip install google-api-python-client

    2. Enable the Custom Search API
    - Navigate to the APIs & Services→Dashboard panel in Cloud Console.
    - Click Enable APIs and Services.
    - Search for Custom Search API and click on it.
    - Click Enable.
    URL for it: https://console.cloud.google.com/apis/library/customsearch.googleapis
    .com

    3. To create an API key:
    - Navigate to the APIs & Services → Credentials panel in Cloud Console.
    - Select Create credentials, then select API key from the drop-down menu.
    - The API key created dialog box displays your newly created key.
    - You now have an API_KEY

    Alternatively, you can just generate an API key here:
    https://developers.google.com/custom-search/docs/paid_element#api_key

    4. Setup Custom Search Engine so you can search the entire web
    - Create a custom search engine here: https://programmablesearchengine.google.com/.
    - In `What to search` to search, pick the `Search the entire Web` option.
    After search engine is created, you can click on it and find `Search engine ID`
      on the Overview page.

    """

    search_engine: Any  #: :meta private:
    google_api_key: Optional[str] = None
    google_cse_id: Optional[str] = None
    k: int = 10
    siterestrict: bool = False

    class Config:
        """Configuration for this pydantic object."""

        extra = Extra.forbid

    def _google_search_results(self, search_term: str, **kwargs: Any) -> List[dict]:
        cse = self.search_engine.cse()
        if self.siterestrict:
            cse = cse.siterestrict()
        res = cse.list(q=search_term, cx=self.google_cse_id, **kwargs).execute()
        return res.get("items", [])

    @root_validator()
    def validate_environment(cls, values: Dict) -> Dict:
        """Validate that api key and python package exists in environment."""
        google_api_key = get_from_dict_or_env(
            values, "google_api_key", "GOOGLE_API_KEY"
        )
        values["google_api_key"] = google_api_key

        google_cse_id = get_from_dict_or_env(values, "google_cse_id", "GOOGLE_CSE_ID")
        values["google_cse_id"] = google_cse_id

        try:
            from googleapiclient.discovery import build

        except ImportError:
            raise ImportError(
                "google-api-python-client is not installed. "
                "Please install it with `pip install google-api-python-client"
                ">=2.100.0`"
            )

        service = build("customsearch", "v1", developerKey=google_api_key)
        values["search_engine"] = service

        return values

    def run(self, query: str) -> str:
        """Run query through GoogleSearch and parse result."""
        snippets = []
        results = self._google_search_results(query, num=self.k)
        if len(results) == 0:
            return "No good Google Search Result was found"
        for result in results:
            if "snippet" in result:
                snippets.append(result["snippet"])

        return " ".join(snippets)

    def results(
        self,
        query: str,
        num_results: int,
        search_params: Optional[Dict[str, str]] = None,
    ) -> List[Dict]:
        """Run query through GoogleSearch and return metadata.

        Args:
            query: The query to search for.
            num_results: The number of results to return.
            search_params: Parameters to be passed on search

        Returns:
            A list of dictionaries with the following keys:
                snippet - The description of the result.
                title - The title of the result.
                link - The link to the result.
        """
        metadata_results = []
        results = self._google_search_results(
            query, num=num_results, **(search_params or {})
        )
        if len(results) == 0:
            return [{"Result": "No good Google Search Result was found"}]
        for result in results:
            metadata_result = {
                "title": result["title"],
                "link": result["link"],
            }
            if "snippet" in result:
                metadata_result["snippet"] = result["snippet"]
            metadata_results.append(metadata_result)

        return metadata_results


import re
import operator
import urwid

from ..helper import string_decode
from ..settings.const import settings
from ..db.attachment import Attachment
from ..errors import CompletionError

class AttachmentWidget(urwid.WidgetWrap):
    """
    one-line summary of an :class:`~alot.db.attachment.Attachment`.
    """

    def __init__(self, attachment, selectable=True):
        self._selectable = selectable
        self.attachment = attachment
        if not isinstance(attachment, Attachment):
            self.attachment = Attachment(self.attachment)
        att = settings.get_theming_attribute('thread', 'attachment')
        focus_att = settings.get_theming_attribute('thread',
                                                   'attachment_focus')
        widget = urwid.AttrMap(urwid.Text(self.attachment.__str__()),
                               att, focus_att)
        urwid.WidgetWrap.__init__(self, widget)

    def get_attachment(self):
        return self.attachment

    def selectable(self):
        return self._selectable

    def keypress(self, size, key):
        return key

class ChoiceWidget(urwid.Text):
    def __init__(self, choices, callback, cancel=None, select=None,
                 separator=' ', choices_to_return=None):
        self.choices = choices
        self.choices_to_return = choices_to_return or {}
        self.callback = callback
        self.cancel = cancel
        self.select = select
        self.separator = separator

        items = []
        for k, v in choices.items():
            if v == select and select is not None:
                items += ['[', k, ']:', v]
            else:
                items += ['(', k, '):', v]
            items += [self.separator]
        urwid.Text.__init__(self, items)

    def selectable(self):
        return True

    def keypress(self, size, key):
        if key == 'enter' and self.select is not None:
            self.callback(self.select)
        elif key == 'esc' and self.cancel is not None:
            self.callback(self.cancel)
        elif key in self.choices_to_return:
            self.callback(self.choices_to_return[key])
        elif key in self.choices:
            self.callback(self.choices[key])
        else:
            return key

class CompleteEdit(urwid.Edit):
    """
    This is a vamped-up :class:`urwid.Edit` widget that allows for
    tab-completion using :class:`~alot.completion.Completer` objects

    These widgets are meant to be used as user input prompts and hence
    react to 'return' key presses by calling a 'on_exit' callback
    that processes the current text value.

    The interpretation of some keypresses is hard-wired:
        :enter: calls 'on_exit' callback with current value
        :esc/ctrl g: calls 'on_exit' with value `None`, which can be
                     interpreted as cancellation
        :tab: calls the completer and tabs forward in the result list
        :shift tab: tabs backward in the result list
        :up/down: move in the local input history
        :ctrl f/b: moves curser one character to the right/left
        :meta f/b shift right/left: moves the cursor one word to the right/left
        :ctrl a/e: moves curser to the beginning/end of the input
        :ctrl d: deletes the character under the cursor
        :meta d: deletes everything from the cursor to the end of the next word
        :meta delete/backspace ctrl w: deletes everything from the cursor to
                                       the beginning of the current word
        :ctrl k: deletes everything from the cursor to the end of the input
        :ctrl u: deletes everything from the cursor to the beginning of the
                 input
    """

    def __init__(self, completer, on_exit,
                 on_error=None,
                 edit_text='',
                 history=None,
                 **kwargs):
        """
        :param completer: completer to use
        :type completer: alot.completion.Completer
        :param on_exit: "enter"-callback that interprets the input (str)
        :type on_exit: callable
        :param on_error: callback that handles
                         :class:`alot.errors.CompletionErrors`
        :type on_error: callback
        :param edit_text: initial text
        :type edit_text: str
        :param history: initial command history
        :type history: list or str
        """
        self.completer = completer
        self.on_exit = on_exit
        self.on_error = on_error
        self.history = list(history)  # we temporarily add stuff here
        self.historypos = None
        self.focus_in_clist = 0

        if not isinstance(edit_text, str):
            edit_text = string_decode(edit_text)
        self.start_completion_pos = len(edit_text)
        self.completions = None
        urwid.Edit.__init__(self, edit_text=edit_text, **kwargs)

    def keypress(self, size, key):
        # if we tabcomplete
        if key in ['tab', 'shift tab'] and self.completer:
            # if not already in completion mode
            if self.completions is None:
                self.completions = [(self.edit_text, self.edit_pos)]
                try:
                    self.completions += self.completer.complete(self.edit_text,
                                                                self.edit_pos)
                    self.focus_in_clist = 1
                except CompletionError as e:
                    if self.on_error is not None:
                        self.on_error(e)

            else:  # otherwise tab through results
                if key == 'tab':
                    self.focus_in_clist += 1
                else:
                    self.focus_in_clist -= 1
            if len(self.completions) > 1:
                ctext, cpos = self.completions[self.focus_in_clist %
                                               len(self.completions)]
                self.set_edit_text(ctext)
                self.set_edit_pos(cpos)
            else:
                self.completions = None
        elif key in ['up', 'down']:
            if self.history:
                if self.historypos is None:
                    self.history.append(self.edit_text)
                    self.historypos = len(self.history) - 1
                if key == 'up':
                    self.historypos = (self.historypos - 1) % len(self.history)
                else:
                    self.historypos = (self.historypos + 1) % len(self.history)
                self.set_edit_text(self.history[self.historypos])
        elif key == 'enter':
            self.on_exit(self.edit_text)
        elif key in ('ctrl g', 'esc'):
            self.on_exit(None)
        elif key == 'ctrl a':
            self.set_edit_pos(0)
        elif key == 'ctrl e':
            self.set_edit_pos(len(self.edit_text))
        elif key == 'ctrl f':
            self.set_edit_pos(min(self.edit_pos+1, len(self.edit_text)))
        elif key == 'ctrl b':
            self.set_edit_pos(max(self.edit_pos-1, 0))
        elif key == 'ctrl k':
            self.edit_text = self.edit_text[:self.edit_pos]
        elif key == 'ctrl u':
            self.edit_text = self.edit_text[self.edit_pos:]
            self.set_edit_pos(0)
        elif key == 'ctrl d':
            self.edit_text = (self.edit_text[:self.edit_pos] +
                              self.edit_text[self.edit_pos+1:])
        elif key in ('meta f', 'shift right'):
            self.move_to_next_word(forward=True)
        elif key in ('meta b', 'shift left'):
            self.move_to_next_word(forward=False)
        elif key == 'meta d':
            start_pos = self.edit_pos
            end_pos = self.move_to_next_word(forward=True)
            if end_pos is not None:
                self.edit_text = (self.edit_text[:start_pos] +
                                  self.edit_text[end_pos:])
                self.set_edit_pos(start_pos)
        elif key in ('meta delete', 'meta backspace', 'ctrl w'):
            end_pos = self.edit_pos
            start_pos = self.move_to_next_word(forward=False)
            if start_pos is not None:
                self.edit_text = (self.edit_text[:start_pos] +
                                  self.edit_text[end_pos:])
                self.set_edit_pos(start_pos)
        else:
            result = urwid.Edit.keypress(self, size, key)
            self.completions = None
            return result

    def move_to_next_word(self, forward=True):
        if forward:
            match_iterator = re.finditer(r'(\b\W+|$)', self.edit_text,
                                         flags=re.UNICODE)
            match_positions = [m.start() for m in match_iterator]
            op = operator.gt
        else:
            match_iterator = re.finditer(r'(\w+\b|^)', self.edit_text,
                                         flags=re.UNICODE)
            match_positions = reversed([m.start() for m in match_iterator])
            op = operator.lt
        for pos in match_positions:
            if op(pos, self.edit_pos):
                self.set_edit_pos(pos)
                return pos

class HeadersList(urwid.WidgetWrap):
    """ renders a pile of header values as key/value list """

    def __init__(self, headerslist, key_attr, value_attr, gaps_attr=None):
        """
        :param headerslist: list of key/value pairs to display
        :type headerslist: list of (str, str)
        :param key_attr: theming attribute to use for keys
        :type key_attr: urwid.AttrSpec
        :param value_attr: theming attribute to use for values
        :type value_attr: urwid.AttrSpec
        :param gaps_attr: theming attribute to wrap lines in
        :type gaps_attr: urwid.AttrSpec
        """
        self.headers = headerslist
        self.key_attr = key_attr
        self.value_attr = value_attr
        pile = urwid.Pile(self._build_lines(headerslist))
        if gaps_attr is None:
            gaps_attr = key_attr
        pile = urwid.AttrMap(pile, gaps_attr)
        urwid.WidgetWrap.__init__(self, pile)

    def __str__(self):
        return str(self.headers)

    def _build_lines(self, lines):
        max_key_len = 1
        headerlines = []
        # calc max length of key-string
        for key, value in lines:
            if len(key) > max_key_len:
                max_key_len = len(key)
        for key, value in lines:
            # todo : even/odd
            keyw = ('fixed', max_key_len + 1,
                    urwid.Text((self.key_attr, key)))
            valuew = urwid.Text((self.value_attr, value))
            line = urwid.Columns([keyw, valuew])
            headerlines.append(line)
        return headerlines

class TagWidget(urwid.AttrMap):
    """
    text widget that renders a tagstring.

    It looks up the string it displays in the `tags` section
    of the config as well as custom theme settings for its tag.

    Attributes that should be considered publicly readable:
        :attr tag: the notmuch tag
        :type tag: str
    """

    def __init__(self, tag, fallback_normal=None, fallback_focus=None):
        self.tag = tag
        representation = settings.get_tagstring_representation(tag,
                                                               fallback_normal,
                                                               fallback_focus)
        self.translated = representation['translated']
        self.hidden = self.translated == ''
        self.txt = urwid.Text(self.translated, wrap='clip')
        self.__hash = hash((self.translated, self.txt))
        normal_att = representation['normal']
        focus_att = representation['focussed']
        self.attmaps = {'normal': normal_att, 'focus': focus_att}
        urwid.AttrMap.__init__(self, self.txt, normal_att, focus_att)

    def set_map(self, attrstring):
        self.set_attr_map({None: self.attmaps[attrstring]})

    def width(self):
        # evil voodoo hotfix for double width chars that may
        # lead e.g. to strings with length 1 that need width 2
        return self.txt.pack()[0]

    def selectable(self):
        return True

    def keypress(self, size, key):
        return key

    def set_focussed(self):
        self.set_attr_map(self.attmaps['focus'])

    def set_unfocussed(self):
        self.set_attr_map(self.attmaps['normal'])

    def __cmp(self, other, comparitor):
        """Shared comparison method."""
        if not isinstance(other, TagWidget):
            return NotImplemented

        self_len = len(self.translated)
        oth_len = len(other.translated)

        if (self_len == 1) is not (oth_len == 1):
            return comparitor(self_len, oth_len)
        return comparitor(self.translated.lower(), other.translated.lower())

    def __lt__(self, other):
        """Groups tags of 1 character first, then alphabetically.

        This groups tags unicode characters at the begnining.
        """
        return self.__cmp(other, operator.lt)

    def __gt__(self, other):
        return self.__cmp(other, operator.gt)

    def __ge__(self, other):
        return self.__cmp(other, operator.ge)

    def __le__(self, other):
        return self.__cmp(other, operator.le)

    def __eq__(self, other):
        if not isinstance(other, TagWidget):
            return NotImplemented
        if len(self.translated) != len(other.translated):
            return False
        return self.translated.lower() == other.translated.lower()

    def __ne__(self, other):
        if not isinstance(other, TagWidget):
            return NotImplemented
        return self.translated.lower() != other.translated.lower()

    def __hash__(self):
        return self.__hash


import re
from autoreduce_db.reduction_viewer.models import Experiment, ReductionRun
from django_filters.filters import CharFilter
from django_filters.widgets import RangeWidget
from django_filters import FilterSet, DateFromToRangeFilter
from django.db.models import Q
from django.core.exceptions import ValidationError

def validate_run_number(self):
    """
    Validator method to ensure correct regex in run number field on Search page
    Raises ValidationError if regex does not match
    """
    if "," in self and "-" not in self:
        if not re.match(r'\d+,\d+', self):
            raise ValidationError("Invalid format. There must be a run number before and after the comma.")
    elif "-" in self and "," not in self:
        if not re.match(r'\d+-\d+', self):
            raise ValidationError("Invalid format. There must be a run number before and after the hyphen.")
    elif "-" in self and "," in self:
        if not re.match(r'[0-9, -]+[0-9,]$', self):
            raise ValidationError("Invalid format. Accepted format e.g. 60200-60205, 60210-60215")
    else:
        if not re.match(r'^\d+$', self):
            raise ValidationError("Invalid format. Run number must be numeric.")

# pylint:disable=unused-argument
def filter_run_number(queryset, name, value):
    """
    Method to determine regex used by user in run number field to filter runs
    '-' for a range
    ',' for seperate values
    Or a combination of the two
    """
    if "," in value and "-" not in value:
        list_values = value.split(',')
        query = Q(run_numbers__run_number__in=list_values)
        return queryset.filter(query)
    if "-" in value and "," not in value:
        list_values = value.split('-')
        query = Q(run_numbers__run_number__range=(list_values[0], list_values[1]))
        return queryset.filter(query)
    if "-" in value and "," in value:
        query = Q()
        list_values = value.split(',')
        for pair in list_values:
            seperated_pair = pair.split('-')
            query.add(Q(run_numbers__run_number__range=(seperated_pair[0], seperated_pair[1])), Q.OR)
        return queryset.filter(query)
    query = Q(run_numbers__run_number__exact=value)
    return queryset.filter(query)

class ReductionRunFilter(FilterSet):
    '''Filter model for filtering and querying ReductionRun models. '''
    created = DateFromToRangeFilter(widget=RangeWidget(attrs={
        'type': 'date',
        'placeholder': 'dd-mm-yyyy',
        'label': 'created'
    }))

    run_description = CharFilter(method="filter_run_description")
    run_number = CharFilter(field_name="run_number",
                            method=filter_run_number,
                            label='Run Number',
                            validators=[validate_run_number])

    class Meta:
        model = ReductionRun
        fields = ['run_number', 'instrument', 'run_description', 'created', 'status']

    def __init__(self, *args, run_description_qualifier=None, **kwargs):
        super().__init__(*args, **kwargs)
        # Call empty qs here to prevent all runs showing when search page first loaded
        if self.data == {}:
            self.queryset = self.queryset.none()
        self.run_description_qualifier = run_description_qualifier

    def filter_run_description(self, queryset, name, value):
        """
        Returns filtered queryset based on whether user checked the
        'contains' or 'exact' radio button for run description.
        """
        checkbox = self.run_description_qualifier
        if checkbox == "exact":
            query = Q(run_description__exact=value)
            return queryset.filter(query)
        elif checkbox == "contains":
            query = Q(run_description__contains=value)
            return queryset.filter(query)
        else:
            return queryset.none()

class ExperimentFilter(FilterSet):
    '''Filter model for filtering and querying Experiment models. '''

    class Meta:
        model = Experiment
        fields = ['reference_number']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Call empty qs here to prevent all runs showing when search page first loaded
        if self.data == {}:
            self.queryset = self.queryset.none()


import json
import requests
import csv
url="https://www.mmm920.com/products?coupon=0&keyword=ALL&points=0&redPacket=0"
headers={
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Authorization': 'null',
'Connection': 'keep-alive',
'Content-Length': '134',
'Content-Type': 'application/json',
'Host': 'w.mmm920.com',
'Origin': 'https://www.mmm920.com',
'Referer': 'https://www.mmm920.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
}
html=requests.post(url,headers=headers)
datas=json.loads(html.text)#把数据解析成json对象
for product in datas['products']:
    print(product['products']['name'])
    t = open('text.csv', 'a+', encoding='utf-8_sig', newline='')
    c = csv.writer(t)
    c.writerow(product['products']['name'])


#!/usr/bin/env python3

import sys
import random, time
from hackeriet.mqtt import MQTT
import nacl, base64
from nacl.public import PublicKey, SealedBox

from flask import Flask, render_template, request, redirect
app = Flask(__name__)

global humla
humla = "unknown"
global topic
topic = "_"
lastupdate = int(time.time())

def encrypt(s, pk_b64="AH1Es2z7G5q0S0wKPdKnGbie8ueeB8hfZzU6aQqyuBw="): # hackerpass ding-ip-secret-key
    box = SealedBox(PublicKey(base64.b64decode(pk_b64)))
    enc = box.encrypt(s)
    return(base64.urlsafe_b64encode(enc).decode('ascii'))

def space_state(mosq, obj, msg):
    global humla
    lastupdate = int(time.time())
    humla = msg.payload.decode()

mqtt = MQTT(space_state)
mqtt.subscribe("hackeriet/space_state", 0)
#mqtt.subscribe("hackeriet/topic", 0)

@app.after_request
def add_header(response):
    response.cache_control.no_cache = True
    response.cache_control.max_age = 0
    return response

@app.route("/hackeriet.gif")
def gif():
    n = random.choice([1,2,3,4,5,6])
    if humla == "OPEN":
        return redirect("/static/img/open/" + str(n) + ".gif")
    else:
        return redirect("/static/img/closed/" + str(n) + ".gif")

@app.route("/humla")
def tut():
    return humla

@app.route("/topic.jsonp")
def topictut():
    t = "hackerietTopic(\"" + topic + "\")"
    return t, 200, {'Content-Type': 'application/javascript'}

@app.route("/door.json")
def door_json():
    t = "{ \"status\": \"%s\" }" % humla
    return t, 200, {'Content-Type': 'application/javascript'}

@app.route("/", methods=["GET", "POST"])
def hello():
    addr = request.remote_addr
    if addr == "::1" or addr == "localhost" or addr == "127.0.0.1" and 'X-Forwarded-For' in request.headers:
        addr = request.headers['X-Forwarded-For']

    if request.method == 'POST':
        if request.form['person']:
            person = request.form['person']
        else:
            person = ''

        mqtt("hackeriet/ding", "%s <%s>" % (person, encrypt(bytes(addr,"ascii"))))

        return render_template('knocked.html')
    else:
        return render_template('index.html', humla=humla)

@app.route("/spaceapi.json")
def spaceapi():
  open="true" if humla == "OPEN" else "false"
  return render_template('spaceapi.json', humla=humla, open=open, lastupdate=lastupdate)

def main():
    #app.debug = True
    app.run(port=5001)

if __name__ == "__main__":
    main()


# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack.tests.functional import base

class TestVolume(base.BaseFunctionalTest):
    # Creating a volume backup is incredibly slow.
    TIMEOUT_SCALING_FACTOR = 1.5

    def setUp(self):
        super(TestVolume, self).setUp()
        self.skipTest('Volume functional tests temporarily disabled')
        if not self.user_cloud.has_service('volume'):
            self.skipTest('volume service not supported by cloud')

        if not self.user_cloud.has_service('object-store'):
            self.skipTest('volume backups require swift')

    def test_create_get_delete_volume_backup(self):
        volume = self.user_cloud.create_volume(
            display_name=self.getUniqueString(), size=1
        )
        self.addCleanup(self.user_cloud.delete_volume, volume['id'])

        backup_name_1 = self.getUniqueString()
        backup_desc_1 = self.getUniqueString()
        backup = self.user_cloud.create_volume_backup(
            volume_id=volume['id'],
            name=backup_name_1,
            description=backup_desc_1,
            wait=True,
        )
        self.assertEqual(backup_name_1, backup['name'])

        backup = self.user_cloud.get_volume_backup(backup['id'])
        self.assertEqual("available", backup['status'])
        self.assertEqual(backup_desc_1, backup['description'])

        self.user_cloud.delete_volume_backup(backup['id'], wait=True)
        self.assertIsNone(self.user_cloud.get_volume_backup(backup['id']))

    def test_create_get_delete_volume_backup_from_snapshot(self):
        volume = self.user_cloud.create_volume(size=1)
        snapshot = self.user_cloud.create_volume_snapshot(volume['id'])
        self.addCleanup(self.user_cloud.delete_volume, volume['id'])
        self.addCleanup(
            self.user_cloud.delete_volume_snapshot, snapshot['id'], wait=True
        )

        backup = self.user_cloud.create_volume_backup(
            volume_id=volume['id'], snapshot_id=snapshot['id'], wait=True
        )

        backup = self.user_cloud.get_volume_backup(backup['id'])
        self.assertEqual(backup['snapshot_id'], snapshot['id'])

        self.user_cloud.delete_volume_backup(backup['id'], wait=True)
        self.assertIsNone(self.user_cloud.get_volume_backup(backup['id']))

    def test_create_get_delete_incremental_volume_backup(self):
        volume = self.user_cloud.create_volume(size=1)
        self.addCleanup(self.user_cloud.delete_volume, volume['id'])

        full_backup = self.user_cloud.create_volume_backup(
            volume_id=volume['id'], wait=True
        )
        incr_backup = self.user_cloud.create_volume_backup(
            volume_id=volume['id'], incremental=True, wait=True
        )

        full_backup = self.user_cloud.get_volume_backup(full_backup['id'])
        incr_backup = self.user_cloud.get_volume_backup(incr_backup['id'])
        self.assertEqual(full_backup['has_dependent_backups'], True)
        self.assertEqual(incr_backup['is_incremental'], True)

        self.user_cloud.delete_volume_backup(incr_backup['id'], wait=True)
        self.user_cloud.delete_volume_backup(full_backup['id'], wait=True)
        self.assertIsNone(self.user_cloud.get_volume_backup(full_backup['id']))
        self.assertIsNone(self.user_cloud.get_volume_backup(incr_backup['id']))

    def test_list_volume_backups(self):
        vol1 = self.user_cloud.create_volume(
            display_name=self.getUniqueString(), size=1
        )
        self.addCleanup(self.user_cloud.delete_volume, vol1['id'])

        # We create 2 volumes to create 2 backups. We could have created 2
        # backups from the same volume but taking 2 successive backups seems
        # to be race-condition prone. And I didn't want to use an ugly sleep()
        # here.
        vol2 = self.user_cloud.create_volume(
            display_name=self.getUniqueString(), size=1
        )
        self.addCleanup(self.user_cloud.delete_volume, vol2['id'])

        backup_name_1 = self.getUniqueString()
        backup = self.user_cloud.create_volume_backup(
            volume_id=vol1['id'], name=backup_name_1
        )
        self.addCleanup(self.user_cloud.delete_volume_backup, backup['id'])

        backup = self.user_cloud.create_volume_backup(volume_id=vol2['id'])
        self.addCleanup(self.user_cloud.delete_volume_backup, backup['id'])

        backups = self.user_cloud.list_volume_backups()
        self.assertEqual(2, len(backups))

        backups = self.user_cloud.list_volume_backups(
            search_opts={"name": backup_name_1}
        )
        self.assertEqual(1, len(backups))
        self.assertEqual(backup_name_1, backups[0]['name'])


"""Check that the environment variables contain valid boto3 credentials."""
import logging
import os
import boto3
import boto3.session

def check(session):
    client = session.client('s3')
    try:
        response = client.list_buckets()
    except Exception as e:
        logging.exception(e)
        return None
    else:
        return [b['Name'] for b in response['Buckets']]

def check_implicit():
    session = boto3.session.Session()
    buckets = check(session)
    if buckets:
        print('implicit check OK: %r' % buckets)
    else:
        print('implicit check failed')

def check_explicit():
    key_id = os.environ.get('AWS_ACCESS_KEY_ID')
    secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
    if not (key_id and secret_key):
        print('no credentials found in os.environ, skipping explicit check')
        return

    session = boto3.session.Session(aws_access_key_id=key_id, aws_secret_access_key=secret_key)
    buckets = check(session)
    if buckets:
        print('explicit check OK: %r' % buckets)
    else:
        print('explicit check failed')

def main():
    check_implicit()
    check_explicit()

if __name__ == '__main__':
    main()