import os
import sys
import yaml

sys.path.append("..")
sys.path.append("../..")
import base64
import traceback
import argparse
from typing import List

from flask import Flask, render_template, jsonify
from flask_cors import CORS

from errudite.server.converter import \
    ListConverter, IntConverter, StrConverter, IntListConverter, \
    BoolConverter, InstanceKeyConverter, InstanceKeyListConverter
from errudite.server.api import API, APIQA, APIVQA
from errudite.rewrites import Rewrite
from errudite.builts import Attribute, Group
from errudite.targets.interfaces import InstanceKey
from errudite.targets.instance import Instance

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)  # pylint: disable=invalid-name
global api
api = None

app = Flask(__name__)
cors = CORS(app, resources={r"*": {"origins": "*"}})
app.config['SECRET_KEY'] = 'secret!'
app.url_map.converters['list'] = ListConverter
app.url_map.converters['int_list'] = IntListConverter
app.url_map.converters['str_list'] = ListConverter
app.url_map.converters['int'] = IntConverter
app.url_map.converters['str'] = StrConverter
app.url_map.converters['bool'] = BoolConverter
app.url_map.converters['instance_key'] = InstanceKeyConverter
app.url_map.converters['instance_key_list'] = InstanceKeyListConverter

SUCCESS_MSG = "successful query!"

def get_args():
    """Get the user arguments
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('--config_file',
        required=True,
        help='the configuration file required.')
    args = parser.parse_args()
    return args

args = get_args()
try:
    with open(args.config_file) as f:
        configs = yaml.safe_load(f)
    logger.info(configs)
    # construct the API
    API_CONSTRUCTOR =  API.by_name(configs["task"])
    api = API_CONSTRUCTOR(
        cache_path=configs["cache_path"],
        model_metas=configs["model_metas"],
        attr_file_name=configs["attr_file_name"],
        group_file_name=configs["group_file_name"],
        rewrite_file_name=configs["rewrite_file_name"]
    )
except Exception as e:
    api = None
    traceback.print_exc()
    logger.error(e)

def wrap_output(o, msg=None):
    if msg:
        msg = f'ERR! {msg}'
    else:
        msg = SUCCESS_MSG
    return jsonify({ "output" : o, "msg": msg })

@app.route('/')
def index():
    print(type(api))
    return render_template('index.html')

@app.route('/api/get_task')
def get_task(api: API=api):
    output, msg = None, None
    try:
        output = api.task
        msg = None
    except Exception as e:
        msg = f'{e}'
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_img/<img_id>')
def get_img(img_id: str, api: API=api):
    """Get the image"""
    # TODO: understand how to set the folder.
    VQA_FOLDER = ""
    output, msg = None, None
    try:
        imgFilename = 'COCO_val2014_' + str(img_id).zfill(12) + '.jpg'
        img_dir = os.path.join(VQA_FOLDER, 'Images', imgFilename)
        with open(img_dir, 'rb') as f:
            output = base64.b64encode(f.read())
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_meta_data')
def get_meta_data(api: API=api):
    output, msg = None, None
    try:
        err_overlaps =api.get_err_overlap(show_filtered_err_overlap=False)
        e_serialized = [ e.serialize() for e in Rewrite.values() ]
        groups = [ g.serialize() for g in Group.values() ]
        attrs = [ a.serialize() for a in Attribute.values() ]
        predictors = [ p.serialize() for p in api.predictors.values() ]
        output = {
            'total_size': len(Instance.qid_hash),
            'anchor_predictor': api.get_anchor_predictor(),
            'compare_predictor': api.get_compare_predictor(),
            'selected_rewrite': api.get_selected_rewrite(),
            'predictors': predictors,
            'attributes': attrs,
            'groups': groups,
            'err_overlaps': err_overlaps,
            'rewrites': e_serialized
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/set_anchor_predictor/<model>')
def set_anchor_predictor(model: str, api: API=api):
    output, msg = None, None
    try:
        api.set_anchor_predictor(model)
        return get_meta_data(api)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
        return wrap_output(output, msg)

@app.route('/api/set_compare_predictor/<model>')
def set_compare_predictor(model: str, api: API=api):
    output, msg = None, None
    try:
        api.set_compare_predictor(model)
        output = {
            'compare_predictor': api.get_compare_predictor(),
            'err_overlaps': api.get_err_overlap(),
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

inputs_sample_instances = [
    "str:selected_predictor",
    "str:cmd",
    "str:sample_method",
    "str:sample_rewrite",
    "int:sample_size",
    "int:test_size",
    "bool:show_filtered_arr",
    "bool:show_filtered_err_overlap",
    "bool:show_filtered_group",
    "bool:show_filtered_rewrite",
    "str_list:qids",
]
@app.route('/api/sample_instances')
@app.route('/api/sample_instances/<bool:selected_predictor>')
@app.route('/api/sample_instances/' + '/'.join([f'<{i}>' for i in inputs_sample_instances]))
def sample_instances(
        selected_predictor: str=None,
        cmd: str='',
        sample_method: str="rand",
        sample_rewrite: str=None,
        sample_size: int=10,
        test_size: int=None,
        show_filtered_arr: bool=False,
        show_filtered_err_overlap: bool=False,
        show_filtered_group: bool=False,
        show_filtered_rewrite: bool=False,
        qids: List[str]=None,
        api: API=api):
    output, msg = None, None
    try:
        data = api.sample_instances(
            selected_predictor, cmd, sample_method,
            sample_rewrite, sample_size, test_size,
            show_filtered_arr,
            show_filtered_err_overlap,
            show_filtered_group,
            show_filtered_rewrite,
            qids)
        output = {
            'attrs': data['attrs'],
            'rewrites': data['rewrites'],
            'groups': data['groups'],
            'err_overlaps': data['err_overlaps'],
            'sample_cache_idx': data['sample_cache_idx'],
            'sampled_keys': data['sampled_keys'],
            'info': data['info'],
            'questions': [q.serialize() for q in data['questions']],
            'contexts': [p.serialize() for p in data['contexts']],
            'answers': [a.serialize() for a in data['answers']],
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_more_samples/<int:direction>')
@app.route('/api/get_more_samples/<int:direction>/<int:sample_size>')
def get_more_samples(direction: int, sample_size: int=10, api: API=api):
    output, msg = None, None
    try:
        data = api.get_more_samples(direction, sample_size)
        output = {
            'sample_cache_idx': data['sample_cache_idx'],
            'sampled_keys': data['sampled_keys'],
            'questions': [q.serialize() for q in data['questions']],
            'contexts': [p.serialize() for p in data['contexts']],
            'answers': [a.serialize() for a in data['answers']],
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/create_built/<str:name>/<str:description>/<str:cmd>/<str:built_type>')
def creat_built(name: str, description: str, cmd: str, built_type: str, api: API=api):
    output, msg = None, None
    try:
        if built_type == 'group':
            built = Group.create(
                name, description, cmd,
                attr_hash=Attribute.store_hash(), group_hash=Group.store_hash())
        else:
            built = Attribute.create(
                name, description, cmd,
                attr_hash=Attribute.store_hash(), group_hash=Group.store_hash())
        output = built.serialize()
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/create_rewrite/<str:from_cmd>/<str:to_cmd>/<str:target_cmd>')
def create_rewrite(from_cmd: str, to_cmd: str, target_cmd: str, api: API=api):
    output, msg = None, None
    try:
        rewrite = api.create_rewrite(from_cmd, to_cmd, target_cmd)
        output = rewrite.serialize()
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/delete_built/<str:name>/<str:built_type>')
def delete_built(name: str, built_type: str, api: API=api):
    output, msg = None, None
    try:
        output = api.delete_built(name, built_type)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/export_built/<str:file_name>/<str:built_type>')
def export_built(file_name: str, built_type: str, api: API=api):
    output, msg = None, None
    try:
        output = api.export_built(file_name, built_type)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_one_attr_of_instances/<str:attr_name>/<instance_key_list:instance_keys>')
def get_one_attr_of_instances(attr_name: str, instance_keys: List[InstanceKey], api: API=api):
    output, msg = None, None
    try:
        output = api.get_one_attr_of_instances(attr_name, instance_keys)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_groups_of_instances/<instance_key_list:instance_keys>')
def get_groups_of_instances(instance_keys: List[InstanceKey], api: API=api):
    output, msg = None, None
    try:
        output = api.get_groups_of_instances(instance_keys)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)
@app.route('/api/get_rewrites_of_instances/<instance_key_list:instance_keys>')
def get_rewrites_of_instances(instance_keys: List[InstanceKey], api: API=api):
    output, msg = None, None
    try:
        output = api.get_rewrites_of_instances(instance_keys)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

inputs_attr_distribution = [
    "str_list:attr_names",
    "str:filter_cmd",
    "bool:use_sampled_data",
    "str:include_rewrite",
    "str:include_model",
    "int:test_size"
]
@app.route('/api/get_attr_distribution/' + '/'.join([f'<{i}>' for i in inputs_attr_distribution[:1] ]))
@app.route('/api/get_attr_distribution/' + '/'.join([f'<{i}>' for i in inputs_attr_distribution]))
def get_attr_distribution(
    attr_names: List[str],
    filter_cmd: str='',
    use_sampled_data: bool=False,
    include_rewrite: str=None,
    include_model: str=None,
    test_size: int=None,
    api: API=api):
    output, msg = None, None
    try:
        output = api.get_attr_distribution(
            attr_names=attr_names,
            filter_cmd=filter_cmd,
            test_size=test_size,
            use_sampled_data=use_sampled_data,
            include_model=include_model,
            include_rewrite=include_rewrite)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

inputs_built_distribution = [
    "str:built_type",
    "str_list:built_names",
    "str:filter_cmd",
    "bool:use_sampled_data",
    "str:include_model",
    "int:test_size"
]
@app.route('/api/get_built_distribution/' + '/'.join([f'<{i}>' for i in inputs_built_distribution[:2] ]))
@app.route('/api/get_built_distribution/' + '/'.join([f'<{i}>' for i in inputs_built_distribution]))
def get_built_distribution(
    built_type: str,
    built_names: List[str],
    filter_cmd: str='',
    use_sampled_data: bool=False,
    include_model: str=None,
    test_size: int=None,
    api: API=api):
    output, msg = None, None
    print
    try:
        output = api.get_built_distribution(
            built_type=built_type,
            built_names=built_names,
            filter_cmd=filter_cmd,
            test_size=test_size,
            use_sampled_data=use_sampled_data,
            include_model=include_model)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/get_err_overlap/<bool:show_filtered_err_overlap>')
def get_err_overlap(show_filtered_err_overlap: bool, api: API=api):
    output, msg = None, None
    try:
        output = api.get_err_overlap(show_filtered_err_overlap)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

### Run detections
@app.route('/api/detect_build_blocks/<str:target>/<str:qid>/<int:vid>/<int:start_idx>/<int:end_idx>')
def detect_build_blocks(target: str, qid: str, vid: int, start_idx: int, end_idx: int, api: API=api):
    output, msg = None, None
    try:
        output = api.detect_build_blocks(target, qid, vid, start_idx, end_idx)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/detect_rule_from_rewrite/<str:atext>/<str:btext>/<str:target_cmd>')
def detect_rule_from_rewrite(atext: str, btext: str, target_cmd: str, api: API=api) -> List['Identifier']:
    output, msg = None, None
    try:
        data = api.detect_rule_from_rewrite(atext, btext, target_cmd)
        output = [ r.serialize() for r in data.values() ]
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/evaluate_rewrites_on_groups/<str_list:rids>/<str_list:gnames>/<bool:on_tried>')
def evaluate_rewrites_on_groups(rids: List[str], gnames: List[str], on_tried: bool, API=api):
    output, msg = None, None
    try:
        output = api.evaluate_rewrites_on_groups(rids, gnames, on_tried)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/rewrite_group_instances/<str:rid>/<str:gname>/<int:sample_size>')
def rewrite_group_instances(rid: str, gname: str, sample_size: int, API=api):
    output, msg = None, None
    try:
        output = api.rewrite_group_instances(rid, gname, sample_size)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/rewrite_instances_by_rid')
@app.route('/api/rewrite_instances_by_rid/<str:rid>/<str_list:qids>')
@app.route('/api/rewrite_instances_by_rid/<str:rid>/<str_list:qids>/<int:sample_size>')
@app.route('/api/rewrite_instances_by_rid/<str:rid>/<str_list:qids>/<int:sample_size>/<bool:save>')
def rewrite_instances_by_rid(
    rid: str, qids: List[str]=None, sample_size: int=10, save: bool=False, api: API=api):
    output, msg = None, None
    try:
        output = api.rewrite_instances_by_rid(rid, qids, sample_size, save)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/formalize_rewritten_examples/<str:rid>')
def formalize_rewritten_examples(rid: str, api: API=api):
    output, msg = None, None
    try:
        if Rewrite.exists(rid):
            api.formalize_prev_tried_rewrites(rid)
            e = Rewrite.get(rid)
            output = e.serialize()
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/predict_on_manual_rewrite/<str:qtext>/<str_list:groundtruths>/<str:ctext>')
def predict_on_manual_rewrite(qtext: str, groundtruths: List[str], ctext: str, api: API=api):
    output, msg = None, None
    try:
        output = api.predict_on_manual_rewrite(qtext, groundtruths, ctext)
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

inputs_predict_formalize = [
    "str:qid",
    "str:rid",
    "str:q_rewrite",
    "str_list:groundtruths",
    "str:c_rewrite",
]
@app.route('/api/predict_formalize/' + '/'.join([f'<{i}>' for i in inputs_predict_formalize[:-1] ]))
@app.route('/api/predict_formalize/' + '/'.join([f'<{i}>' for i in inputs_predict_formalize]))
def predict_formalize(
    qid: str, rid: str, q_rewrite: str,
    groundtruths: List[str],
    c_rewrite: str=None, api: API=api) -> List['Identifier']:
    output, msg = None, None
    try:
        data = api.predict_formalize(qid, rid, q_rewrite, groundtruths, c_rewrite)
        output = {
            'key': data['key'],
            'question': data['question'].serialize() if data['question'] else None,
            'context': data['context'].serialize() if data['context'] else None,
            'groundtruths': [g.serialize() for g in data['groundtruths']] if data['groundtruths'] else None,
            'predictions': [g.serialize() for g in data['predictions']] if data['predictions'] else None
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

@app.route('/api/delete_selected_rules/<str_list:rids>')
def delete_selected_rules(rids: List[str], api: API=api):
    output, msg = None, None
    try:
        data = api.delete_selected_rules(rids)
        output = {
            'key': data['key'],
            'question': data['question'].serialize() if data['question'] else None,
            'context': data['context'].serialize() if data['context'] else None,
            'groundtruths': [g.serialize() for g in data['groundtruths']] if data['groundtruths'] else None,
            'predictions': [g.serialize() for g in data['predictions']] if data['predictions'] else None
        }
    except Exception as e:
        msg = e
        logger.error(e)
        traceback.print_exc()
    finally:
        return wrap_output(output, msg)

app.run(debug=False)


import numpy as np
import random
from matplotlib import pyplot as plt
import sys
sys.stdout = open('outputfile.txt', 'w')

#Note: I don't have a lot of experience with Python yet, so suggestions for improving the code are welcome!

relevant_ranks=['bronze','silver','gold']
DEBUG = False

def log(s):
    if DEBUG:
        print(s)

steps_gained_with_win ={
	'bronze':2,
	'silver':2,
	'gold':1,
	'platinum':1,
	'diamond':1
	}

steps_lost_with_loss ={
	'bronze':0,
	'silver':1,
	'gold':1,
	'platinum':1,
	'diamond':1
	}

def expected_games_without_tier_protection(game_win_prob, rank, bestof):

	"""The setting without tier protection is relatively easy.
	For bestof = 1, this function gives the exact expected number of games to reach the next rank in best-of-one.
	For bestof = 3, this function gives an approximation for the expected number of games to reach the next rank in best-of-three.
	This approximation assumes that any match takes 2.5 games.

	To determine this, this function creates a matrix to represent the system of equations of the form Ax=b
	Let x[n] represent the expected number of games to hit the next rank from state n
	For non-endpoint states n, it holds that x[n] = winrate * {1 + x[n+steps_gained_with_win]) + lossrate * (1 + x[n-steps_lost_with_loss])
	This implies that x[n] - winrate * x[n+steps_gained_with_win] - lossrate * x[n-steps_lost_with_loss] = 1
	A matrix is created with these numbers, making appropriate adjustments for endpoint states, then solved

	Parameters -
		game_win_prob: Probability to win any game
		rank: the rank we're interested in, e.g., 'silver'
		bestof: must be 1 or 3

	Returns - a number that represents the expected number of games to reach the next rank
	"""
	if (bestof == 1):
		multiplier = 1
		winrate = game_win_prob
	if (bestof == 3):
		multiplier = 2
		winrate = game_win_prob * game_win_prob + 2 * game_win_prob * game_win_prob * (1 - game_win_prob)
	lossrate = 1 - winrate

	steps_per_rank ={ k: v*4 for k, v in steps_per_tier.items()}
	A = np.zeros( (steps_per_rank[rank], steps_per_rank[rank]) )
	b = np.ones( steps_per_rank[rank] )

	for n in range(0, steps_per_rank[rank]):
		A[ n, n ] = 1
		A[ n, max(0, n - multiplier * steps_lost_with_loss[rank]) ] -= lossrate
		if (n + multiplier * steps_gained_with_win[rank] < steps_per_rank[rank]):
			A[ n, n + multiplier * steps_gained_with_win[rank] ] -= winrate

	x = np.linalg.solve(A, b)
	if (bestof == 1):
		return x[0]
	if (bestof == 3):
		return x[0] * 2.5

def give_index(rank, tier, step, protection):

	"""The challenging aspect with tier protection is turning the three-dimensional state space into a single dimension, as required for solving the LP
	There surely is a better way to do this, but my convoluted method should at least work
	State is given as (tier, step, protection)
	For steps>=2, protection must be 0
	For step 0 or step 1, protection can be 3, 2, 1, or 0.
	(Step 1, protection 3 never happens in practice. Also, step 1, protection 1 is equivalent to step 1, protection 0. They are included to allow fewer exceptions later in the code.)
	Consider Gold rank as an example.
	Index 0 is (4, 0, 0)
	Index 1 is (4, 1, 0)
	...
	Index 5 is (4, 5, 0)
	Index 6 is (3, 0, 0)
	Index 7 is (3, 1, 0)
	...
	Index 23 is (1, 5, 0)
	THEN
	Index 24 is (3, 0, 3)
	Index 25 is (3, 0, 2)
	Index 26 is (3, 0, 1)
	Index 27 is (2, 0, 3)
	Index 28 is (2, 0, 2)
	...
	Index 33 is (3, 1, 3)
	Index 34 is (3, 1, 2)
	Index 35 is (3, 1, 1)
	...
	Index 41 is (1, 1, 1)
	"""
	if (protection == 0):
		return (4 - tier) * steps_per_tier[rank] + step
	if (protection > 0 and step == 0):
		return 4 * steps_per_tier[rank] + (3 - tier) * 3 + 3 - protection
	if (protection > 0 and step == 1):
		return 4 * steps_per_tier[rank] + 9 + (3 - tier) * 3 + 3 - protection

def give_tier(index):
	if (index < 4 * steps_per_tier[rank] ):
		return 4 - ((index ) // steps_per_tier[rank])
	if (index >= 4 * steps_per_tier[rank] and index < 4 * steps_per_tier[rank] + 9):
		return 3 - ((index ) - 4 * steps_per_tier[rank]) // 3
	if (index >= 4 * steps_per_tier[rank] + 9):
		return 3 - ((index )- (4 * steps_per_tier[rank] + 9)) // 3

def give_step(index):
	if (index < 4 * steps_per_tier[rank]):
		return (index ) % steps_per_tier[rank]
	if (index >= 4 * steps_per_tier[rank] and index < 4 * steps_per_tier[rank] + 9):
		return 0
	if (index >= 4 * steps_per_tier[rank] + 9):
		return 1

def give_protection(index):
	if (index < 4 * steps_per_tier[rank]):
		return 0
	if (index >= 4 * steps_per_tier[rank] and index < 4 * steps_per_tier[rank] + 9):
		return 3 - ( (index - 4 * steps_per_tier[rank]) % 3)
	if (index >= 4 * steps_per_tier[rank] + 9):
		return 3 - ( (index - (4 * steps_per_tier[rank] + 9) ) % 3)

def describe_state(index):
	tier=str(give_tier(index))
	step=str(give_step(index))
	protection=str(give_protection(index))
	return "the state with tier " + tier + ", step " + step + ", and protection " + protection

def expected_games_with_tier_protection(game_win_prob, rank, bestof):
	"""The setting with tier protection is more involved.
	For bestof = 1, this function gives the exact expected number of games to reach the next rank in best-of-one.
	For bestof = 3, this function gives an approximation for the expected number of games to reach the next rank in best-of-three.
	This approximation assumes that any match takes 2.5 games.

	To determine this, this function creates a matrix to represent the system of equations of the form Ax=b

	Parameters -
		game_win_prob: Probability to win any game
		rank: the rank we're interested in, e.g., 'silver'
		bestof: must be 1 or 3

	Returns - a number that represents the expected number of games to reach the next rank
	"""

	if (bestof == 1):
		multiplier = 1
		winrate = game_win_prob
		protection = 3
	if (bestof == 3):
		multiplier = 2
		winrate = game_win_prob * game_win_prob + 2 * game_win_prob * game_win_prob * (1 - game_win_prob)
		protection = 1
	lossrate = 1 - winrate

	steps_per_rank ={ k: v*4 for k, v in steps_per_tier.items()}
	TotalNumberOfStates = steps_per_rank[rank] + 3 * 3 + 3 * 3
	A = np.zeros( (TotalNumberOfStates, TotalNumberOfStates) )
	b = np.ones( TotalNumberOfStates )

	for index in range(0, TotalNumberOfStates):
		log("\n")
		log("Index number " + str(give_index(rank, give_tier(index), give_step(index), give_protection(index))) + " is " + describe_state(index))
		A[ index, index ] = 1
		if (index + multiplier * steps_gained_with_win[rank] < steps_per_rank[rank]):
			#This means that a win won't advance you to the next rank and that you don't have protection
			if (give_step(index) + multiplier * steps_gained_with_win[rank] < steps_per_tier[rank]):
				#With a win, move to the next step in the same tier
				A[ index, index + multiplier * steps_gained_with_win[rank] ] -= winrate
				log( "--We take off winrate from " + describe_state(index + multiplier * steps_gained_with_win[rank]) )
			if (give_step(index) + multiplier * steps_gained_with_win[rank] >= steps_per_tier[rank]):
				#With a win, move to the next tier with protection, unless we're in Bronze Bo3 or Silver Bo3 and would end up in step 2+ with unnecessary protection
				new_step_with_win = give_step(index) + multiplier * steps_gained_with_win[rank] - steps_per_tier[rank]
				proper_protection = protection if new_step_with_win < 2 else 0
				A[ index, give_index(rank, give_tier(index) - 1, new_step_with_win, proper_protection) ] -= winrate
				log( "--We take off winrate from " + describe_state( give_index(rank, give_tier(index) - 1, new_step_with_win, proper_protection) ) )
			A[ index, max(0, index - multiplier * steps_lost_with_loss[rank]) ] -= lossrate
			log( "--We take off lossrate from " + describe_state( max(0, index - multiplier * steps_lost_with_loss[rank]) ) )
		if (index + multiplier * steps_gained_with_win[rank] >= steps_per_rank[rank] and give_protection(index) == 0):
			#This means that a win will advance you to the next rank
			A[ index, max(0, index - multiplier * steps_lost_with_loss[rank]) ] -= lossrate
			log( "--We take off lossrate from " + describe_state( max(0, index - multiplier * steps_lost_with_loss[rank]) ) )
		if (give_protection(index) > 0):
			#This means that you have protection
			new_step_with_win = give_step(index) + multiplier * steps_gained_with_win[rank]
			new_protection_with_win = 0 if new_step_with_win >= 2 else give_protection(index) - 1
			if (new_step_with_win < steps_per_tier[rank]):
				#With a win, move to the next step in the same tier
				A[ index, give_index(rank, give_tier(index), new_step_with_win, new_protection_with_win) ] -= winrate
				log( "--We take off winrate from " + describe_state( give_index(rank, give_tier(index), new_step_with_win, new_protection_with_win) ) )
			if (new_step_with_win >= steps_per_tier[rank]):
				#This should only happen in Bronze or Silver Bo3, but with a win, move to the next tier or rank
				new_step_with_win = new_step_with_win - steps_per_tier[rank]
				if (give_tier(index) > 1):
					proper_protection = protection if new_step_with_win < 2 else 0
					A[ index, give_index(rank, give_tier(index) - 1, new_step_with_win, proper_protection) ] -= winrate
					log( "--We take off winrate from " + describe_state( give_index(rank, give_tier(index) - 1, new_step_with_win, proper_protection) ) )
			A[ index, give_index(rank, give_tier(index), max(0, give_step(index) - multiplier * steps_lost_with_loss[rank]), give_protection(index) - 1) ] -= lossrate
			log( "--We take off lossrate from " + describe_state( give_index(rank, give_tier(index), max(0, give_step(index) - multiplier * steps_lost_with_loss[rank]), give_protection(index) - 1) ) )

	x = np.linalg.solve(A, b)
	if (bestof == 1):
		return x[0]
	if (bestof == 3):
		return x[0] * 2.5

def run_simulation(game_win_prob, rank, bestof):
	"""Simulaton approach, with tier protection.
	For bestof = 1, this approximates the expected number of games to reach the next rank in best-of-one.
	For bestof = 3, this approximates the expected number of games to reach the next rank in best-of-three.
	The approximation accurately takes into account that the expected number of games per match is different for matches won and matches lost.

	Parameters -
		game_win_prob: Probability to win any game
		rank: the rank we're interested in, e.g., 'silver'
		bestof: must be 1 or 3

	Returns - a number that approximates the expected number of games to reach the next rank
	"""

	if (bestof == 1):
		multiplier = 1
		winrate = game_win_prob
		protection = 3
	if (bestof == 3):
		multiplier = 2
		winrate = game_win_prob * game_win_prob + 2 * game_win_prob * game_win_prob * (1 - game_win_prob)
		protection = 1
		games_per_match_won = (2 * game_win_prob * game_win_prob + 3 * 2 * game_win_prob * game_win_prob * (1 - game_win_prob) ) / winrate
		games_per_match_lost = (2 * (1 - game_win_prob) * (1 - game_win_prob) + 3 * 2 * (1 - game_win_prob) * (1 - game_win_prob) * game_win_prob ) / (1 - winrate)
	lossrate = 1 - winrate

	games_per_simulation = []
	for x in range(50000):
		curr_tier = 4
		curr_step = 0
		curr_protection = 0
		match_count = 0
		curr_wins = 0
		curr_losses = 0
		continue_playing = True
		while(continue_playing):
			match_count += 1
			if(random.random() < winrate):
				match_win = True
				curr_wins +=1
			else:
				match_win = False
				curr_losses +=1
			if (match_win and curr_tier == 1 and curr_step + multiplier * steps_gained_with_win[rank] >= steps_per_tier[rank]):
				#Advance to the next rank
				continue_playing = False
			elif (match_win and curr_tier > 1 and curr_step + multiplier * steps_gained_with_win[rank] >= steps_per_tier[rank]):
				#Move to the next tier with protection
				curr_tier -= 1
				curr_protection = protection
				curr_step = curr_step + multiplier * steps_gained_with_win[rank] - steps_per_tier[rank]
			elif (match_win and curr_step + multiplier * steps_gained_with_win[rank] < steps_per_tier[rank]):
				#Move up steps in the same tier
				curr_step += multiplier * steps_gained_with_win[rank]
				curr_protection -= 1
			elif ((not match_win) and curr_step < multiplier * steps_lost_with_loss[rank] and curr_protection <= 0 and curr_tier < 4):
				#Fall back a tier
				curr_step = steps_per_tier[rank] - multiplier * steps_lost_with_loss[rank]
				curr_tier += 1
			elif ((not match_win) and curr_step < multiplier * steps_lost_with_loss[rank] and curr_protection > 0):
				#Remain at the start of the tier with reduced protection
				curr_protection -= 1
				curr_step = 0
			elif ((not match_win) and curr_step >= multiplier * steps_lost_with_loss[rank]):
				#Move down steps in the same tier
				curr_step -= multiplier * steps_lost_with_loss[rank]
				curr_protection -= 1
		if (bestof == 1):
			games_per_simulation.append(match_count)
		if (bestof == 3):
			game_count = curr_wins * games_per_match_won + curr_losses * games_per_match_lost
			games_per_simulation.append(game_count)
	return np.mean(games_per_simulation)

#Do some checks for verification
steps_per_tier ={
	'bronze':4,
	'silver':5,
	'gold':6,
	'platinum':7,
	'diamond':7
	}

rank = 'gold'
bestof = 1
win_prob = 0.55

number_games_exact = expected_games_without_tier_protection(win_prob, rank, bestof)
print(f'Under Preaseason 1 progression without tier protection, the exact E[games] required to go from the start of {rank.title()} to the next rank with a {win_prob*100:.1f}% winrate is {number_games_exact:.3f}.')
print(f'The correct number should be 195.364 games according to the work of others. So that is good, but things are easy without tier protection. \n')

for bestof in [1,3]:
	for rank in relevant_ranks:
		number_games_exact = expected_games_with_tier_protection(win_prob, rank, bestof)
		number_games_sim = run_simulation(win_prob, rank, bestof)
		print(f'Under Preaseason 1 progression with tier protection, the "exact" E[games] to go from {rank.title()} to the next rank in best-of-{bestof} with a {win_prob*100:.1f}% winrate is {number_games_exact:.3f}.')
		print(f'The simulation estimates {number_games_sim:.3f} games. Seems close enough. \n')

#Since everything checked out, let's get the Preseason 2 results for Limited.
steps_per_tier ={
	'bronze':4,
	'silver':5,
	'gold':5,
	'platinum':5,
	'diamond':5
	}

#First, output the Limited results as semicolon separated values
win_probs = [0.4+i*2/100 for i in range(0,16)]
bestof = 1
print("LIMITED win_prob; bronze to silver; silver to gold; gold to platinum")
for win_prob in win_probs:
	outputline=f'{win_prob:.3f}; '
	for rank in relevant_ranks:
		outputline += f'{expected_games_with_tier_protection(win_prob, rank, bestof):.3f}; '
	print(outputline)

#Second, output the Limited results as a plot
x_axis = np.arange(0.48, 0.64, 0.001)
y_axis = np.empty(161)
fig, ax = plt.subplots()
for rank in relevant_ranks:
	for i in range(0,161):
		y_axis[i]=expected_games_with_tier_protection(0.48+i/1000, rank, bestof)
	ax.plot(x_axis, y_axis, label=rank.title())
	ax.set(xlabel='Game win rate', ylabel='Expected number of games',
		   title='Expected games to reach the next rank in Limited')
	ax.set_xticklabels(['{:.0%}'.format(x) for x in ax.get_xticks()])
	ax.grid()
	plt.legend()
fig.savefig("Expected_number_of_games_Limited.png")

#Third, give the impact of tier protection
rank='gold'
for win_prob in [.5,.6]:
	number_games_exact = expected_games_without_tier_protection(win_prob, rank, bestof)
	print(f'Under Preaseason 2 Limited progression WITHOUT tier protection, the exact E[games] to go from {rank.title()} to the next rank in best-of-{bestof} with a {win_prob*100:.1f}% winrate is {number_games_exact:.3f}.')

#Next, let's get the Preseason 2 results for Constructed.
steps_per_tier ={
	'bronze':6,
	'silver':6,
	'gold':6,
	'platinum':6,
	'diamond':6
	}

for rank in relevant_ranks:

	#First, output the Constructed results as semicolon separated values
	win_probs = [0.45, 0.5, 0.52, 0.54, 0.56, 0.6, 0.62, 0.64, 0.66, 0.68, 0.7, 0.75, 0.8]
	print(f'\nCONSTRUCTED win_prob in rank {rank}; bo1; bo3; bo1 sim check; bo3 exact check')
	for win_prob in win_probs:
		outputline=f'{win_prob:.3f}; '
		outputline += f'{expected_games_with_tier_protection(win_prob, rank, 1):.3f}; '
		outputline += f'{run_simulation(win_prob, rank, 3):.3f}; '
		outputline += f'{run_simulation(win_prob, rank, 1):.3f}; '
		outputline += f'{expected_games_with_tier_protection(win_prob, rank, 3):.3f}'
		print(outputline)

	#Second, output the Constructed results as a plot
	x_axis = np.arange(0.48, 0.64, 0.01)
	y_axis = np.empty(17)
	fig, ax = plt.subplots()
	for i in range(0,17):
		y_axis[i]=expected_games_with_tier_protection(0.48+i/100, rank, 1)
	ax.plot(x_axis, y_axis, label='best-of-1')
	for i in range(0,17):
		y_axis[i]=run_simulation(0.48+i/100, rank, 3)
	ax.plot(x_axis, y_axis, label='best-of-3')

	ax.set(xlabel='Game win rate', ylabel='Expected number of games',
		   title=f'Expected games from {rank.title()} to the next rank in Constructed')
	ax.set_xticklabels(['{:.0%}'.format(x) for x in ax.get_xticks()])
	ax.grid()
	plt.legend()
	fig.savefig(f'Expected_number_of_games_Constructed_{rank.title()}.png')


import os
import logging
import pandas as pd
from typing import NoReturn
from omegaconf import DictConfig
import hydra

from src.configs.eval_params import EvalPipelineParamsSchema, EvalPipelineParams
from src.build_features import make_features, deserialize_transformer
from src.models import predict_model, deserialize_model

logger = logging.getLogger("eval_pipeline")
formatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
logger.setLevel(logging.INFO)

def eval_pipeline(params: EvalPipelineParams) -> NoReturn:
    logger.info(f"start eval pipeline with params {params}")
    df = pd.read_csv(params.input_df_path)
    logger.info(f"df.shape is {df.shape}")
    transformer = deserialize_transformer(params.transformer_path)
    transformed_df = make_features(transformer, df)
    logger.info(f"transformed_df.shape is {transformed_df.shape}")

    model = deserialize_model(params.model_path)
    preds = predict_model(
        model,
        transformed_df,
    )
    logger.info(f"preds.shape is {preds.shape}")
    pd.DataFrame(preds).to_csv(params.preds_path, header=False)
    logger.info(f"predicts saved to the {params.preds_path}")
    return preds

@hydra.main(config_path="configs", config_name="eval_config")
def eval_pipeline_command(cfg: DictConfig):
    os.chdir(hydra.utils.to_absolute_path("."))
    schema = EvalPipelineParamsSchema()
    params = schema.load(cfg)
    eval_pipeline(params)

if __name__ == "__main__":
    eval_pipeline_command()


import csv
import json
import os
import re
import time
import urllib.request
import urllib.parse
import urllib.error
import urllib.response
import requests

from open.refine import facet
from open.refine import history

REFINE_HOST = os.environ.get('OPENREFINE_HOST', os.environ.get('GOOGLE_REFINE_HOST', '127.0.0.1'))
REFINE_PORT = os.environ.get('OPENREFINE_PORT', os.environ.get('GOOGLE_REFINE_PORT', '3333'))

class RefineServer(object):
    """Communicate with a Refine server."""

    @staticmethod
    def server_url():
        """Return the URL to the Refine server."""
        server = 'http://' + REFINE_HOST
        if REFINE_PORT != '80':
            server += ':' + REFINE_PORT
        return server

    def __init__(self, server=None):
        if server is None:
            server = self.server_url()
        self.server = server[:-1] if server.endswith('/') else server
        self.__version = None     # see version @property below

    def urlopen(self, command, data=None, params=None, project_id=None, files=None):
        """Open a Refine URL and with optional query params and POST data.

        data: POST data dict
        param: query params dict
        project_id: project ID as string

        Returns requests.Response object."""
        # TODO: command to direct post or get request
        url = self.server + f'/command/core/{command}'
        if project_id:
            if 'delete' in command or data:
                data['project'] = project_id
            else:
                params['project'] = project_id
        response = requests.post(url, data=data, params=params, files=files)
        if response.status_code is not 200:
            response = requests.get(url, data=data, params=params, files=files)
        return response

    def urlopen_json(self, command, *args, **kwargs):
        """Open a Refine URL, optionally POST data, and return parsed JSON."""
        response = self.urlopen(command, *args, **kwargs)
        response = response.json()
        self.check_response_ok(response)
        return response

    @staticmethod
    def check_response_ok(response):
        if 'code' in response and response['code'] not in ('ok', 'pending'):
            error_message = ('server ' + response['code'] + ': ' +
                             response.get('message', response.get('stack', response)))
            raise Exception(error_message)

    def get_version(self):
        """Return version data.
        {"revision":"r1836","full_version":"2.0 [r1836]",
         "full_name":"Google Refine 2.0 [r1836]","version":"2.0"}"""
        return self.urlopen_json('get-version')

    @property
    def version(self):
        if self.__version is None:
            self.__version = self.get_version()['version']
        return self.__version

class Refine:
    """Class representing a connection to a Refine server."""
    def __init__(self, server):
        if isinstance(server, RefineServer):
            self.server = server
        else:
            self.server = RefineServer(server)

    def new_project(self, project_file=None, project_url=None, project_name=None, project_format='text/line-based/*sv',
                    project_file_name=None, **opts):
        if (project_file and project_url) or (not project_file and not project_url):
            raise ValueError('Either a project_file or project_url must be set. Both cannot be used.')
        params = {
            'project_name': self.set_project_name(project_name, project_file)
        }
        data = {
            'format': project_format,
            'options': self.set_options(project_format, **opts)
        }
        files = {
            'project-file': (project_file_name, open(project_file, 'rb'))
        }
        response = self.server.urlopen('create-project-from-upload', params=params, data=data, files=files)
        response.raise_for_status()
        project_id = self.get_project_id(response.url)
        files['project-file'][1].close()
        return RefineProject(self.server, project_id)

    @staticmethod
    def set_project_name(project_name, project_file):
        # expecting a redirect to the new project containing the id in the server_url
        if project_name is None:
            # make a name for itself by stripping extension and directories
            project_name = (project_file or 'New project').rsplit('.', 1)[0]
            project_name = os.path.basename(project_name)
        return project_name

    def set_options(self, file_format, **kwargs):
        options = self.default_options(file_format)
        for key, value in kwargs.items():
            options[key] = value
        return options

    def get_project_name(self, project_id):
        """Returns project name given project_id."""
        projects = self.list_projects()
        return projects[project_id]['name']

    def open_project(self, project_id):
        """Open a Refine project."""
        return RefineProject(self.server, project_id)

    def list_projects(self):
        """Return a dict of projects indexed by id.

        {u'1877818633188': {
            'id': u'1877818633188', u'name': u'akg',
            u'modified': u'2011-04-07T12:30:07Z',
            u'created': u'2011-04-07T12:30:07Z'
        },
        """
        # It's tempting to add in an index by name but there can be
        # projects with the same name.
        return self.server.urlopen_json('get-all-project-metadata')['projects']

    @staticmethod
    def get_project_id(url):
        url_params = urllib.parse.parse_qs(urllib.parse.urlparse(url).query)
        if 'project' in url_params:
            project_id = url_params['project'][0]
            return project_id
        else:
            raise Exception('Project not created')

    @staticmethod
    def default_options(file_format):
        new_project_defaults = {
            'text/line-based/*sv': {
                'encoding': '',
                'separator': ',',
                'ignore_lines': -1,
                'header_lines': 1,
                'skip_data_lines': 0,
                'limit': -1,
                'store_blank_rows': True,
                'guess_cell_value_types': True,
                'process_quotes': True,
                'store_blank_cells_as_nulls': True,
                'include_file_sources': False},
            'text/line-based': {
                'encoding': '',
                'lines_per_row': 1,
                'ignore_lines': -1,
                'limit': -1,
                'skip_data_lines': -1,
                'store_blank_rows': True,
                'store_blank_cells_as_nulls': True,
                'include_file_sources': False},
            'text/line-based/fixed-width': {
                'encoding': '',
                'column_widths': [20],
                'ignore_lines': -1,
                'header_lines': 0,
                'skip_data_lines': 0,
                'limit': -1,
                'guess_cell_value_types': False,
                'store_blank_rows': True,
                'store_blank_cells_as_nulls': True,
                'include_file_sources': False},
            'text/line-based/pc-axis': {
                'encoding': '',
                'limit': -1,
                'skip_data_lines': -1,
                'include_file_sources': False},
            'text/rdf+n3': {'encoding': ''},
            'text/xml/ods': {
                'sheets': [],
                'ignore_lines': -1,
                'header_lines': 1,
                'skip_data_lines': 0,
                'limit': -1,
                'store_blank_rows': True,
                'store_blank_cells_as_nulls': True,
                'include_file_sources': False},
            'binary/xls': {
                'xml_based': False,
                'sheets': [],
                'ignore_lines': -1,
                'header_lines': 1,
                'skip_data_lines': 0,
                'limit': -1,
                'store_blank_rows': True,
                'store_blank_cells_as_nulls': True,
                'include_file_sources': False}
        }
        if file_format in new_project_defaults.keys():
            return new_project_defaults[file_format]
        else:
            raise InvalidFileFormat

def RowsResponseFactory(column_index):
    """Factory for the parsing the output from get_rows().
    Uses the project's model's row cell index so that a row can be used
    as a dict by column name."""

    class RowsResponse(object):
        class RefineRows(object):
            class RefineRow(object):
                def __init__(self, row_response):
                    self.flagged = row_response['flagged']
                    self.starred = row_response['starred']
                    self.index = row_response['i']
                    self.row = [c['v'] if c else None
                                for c in row_response['cells']]

                def __getitem__(self, column):
                    # Trailing nulls seem to be stripped from row data
                    try:
                        return self.row[column_index[column]]
                    except IndexError:
                        return None

            def __init__(self, rows_response):
                self.rows_response = rows_response

            def __iter__(self):
                for row_response in self.rows_response:
                    yield self.RefineRow(row_response)

            def __getitem__(self, index):
                return self.RefineRow(self.rows_response[index])

            def __len__(self):
                return len(self.rows_response)

        def __init__(self, response):
            self.mode = response['mode']
            self.filtered = response['filtered']
            self.start = response['start']
            self.limit = response['limit']
            self.total = response['total']
            self.rows = self.RefineRows(response['rows'])

    return RowsResponse

class RefineProject:
    """An OpenRefine project."""
    def __init__(self, server, project_id=None):
        if not isinstance(server, RefineServer):
            if '/project?project=' in server:
                server, project_id = server.split('/project?project=')
                server = RefineServer(server)
            elif re.match(r'\d+$', server):     # just digits => project ID
                server, project_id = RefineServer(), server
            else:
                server = RefineServer(server)
        self.server = server
        if not project_id:
            raise Exception('Missing Refine project ID')
        self.project_id = project_id
        self.engine = facet.Engine()
        self.sorting = facet.Sorting()
        self.history_entry = None
        # following filled in by get_models()
        self.key_column = None
        self.has_records = False
        self.columns = None
        self.column_order = {}  # map of column names to order in UI
        self.rows_response_factory = None   # for parsing get_rows()
        self.get_models()
        # following filled in by get_reconciliation_services
        self.recon_services = None

    def do_json(self, command, data=None, params=None, include_engine=True):
        """Issue a command to the server, parse & return decoded JSON."""
        if params is None:
            params = {}
        params['project'] = self.project_id
        if include_engine:
            if data is None:
                data = {}
            data['engine'] = self.engine.as_json()
        if command == 'delete-project':
            response = self.server.urlopen(command, params=params)
            response = response.json()
        else:
            response = self.server.urlopen_json(command, params=params, data=data)
        if 'historyEntry' in response:
            # **response['historyEntry'] won't work as keys are unicode :-/
            he = response['historyEntry']
            self.history_entry = history.HistoryEntry(he['id'], he['time'],
                                                      he['description'])
        return response

    def project_name(self):
        return Refine(self.server).get_project_name(self.project_id)

    def project_url(self):
        """Return a URL to the project."""
        return '%s/project?project=%s' % (self.server.server, self.project_id)

    def do_raw(self, command, data):
        """Issue a command to the server & return a response object."""
        return self.server.urlopen(command, project_id=self.project_id, data=data)

    def get_models(self):
        """
        Fill out column metadata.

        Column structure is a list of columns in their order.
        The cellIndex is an index for that column's data into the list returned
        from get_rows().

        {"columnModel":{"columns":[],"columnGroups":[]},"recordModel":{"hasRecords":false},"overlayModels":{},
        "scripting":{"grel":{"name":"General Refine Expression Language (GREL)","defaultExpression":"value"},
        "jython":{"name":"Python / Jython","defaultExpression":"return value"},"clojure":{"name":"Clojure",
        "defaultExpression":"value"}}}
        """
        response = self.do_json('get-models', include_engine=False)
        column_model = response['columnModel']
        column_index = {}   # map of column name to index into get_rows() data
        self.columns = [column['name'] for column in column_model['columns']]
        for i, column in enumerate(column_model['columns']):
            name = column['name']
            self.column_order[name] = i
            column_index[name] = column['cellIndex']
        self.key_column = column_model.get('keyColumnName')
        self.has_records = response['recordModel'].get('hasRecords', False)
        self.rows_response_factory = RowsResponseFactory(column_index)
        return response

    def get_preference(self, name):
        """Returns the (JSON) value of a given preference setting."""
        response = self.server.urlopen_json('get-preference', params={'name': name})
        return json.loads(response['value'])

    def wait_until_idle(self, polling_delay=0.5):
        while True:
            response = self.do_json('get-processes', include_engine=False)
            if 'processes' in response and len(response['processes']) > 0:
                time.sleep(polling_delay)
            else:
                return

    def apply_operations(self, operations=None, file_path=None, wait=True):
        json_data = self.json_data(operations, file_path)
        response_json = self.do_json('apply-operations', data={'operations': json_data})

        if response_json['code'] == 'pending' and wait:
            self.wait_until_idle()
            return 'ok'
        return response_json['code']  # can be 'ok' or 'pending'

    @staticmethod
    def json_data(operations, file_path):
        if operations:
            if type(operations) is not list:
                operations = [operations]
            return json.dumps(operations)
        elif file_path:
            return open(file_path).read()
        else:
            raise ValueError("Operation(s) not specified.")

    def export(self, export_format='tsv'):
        """Return a fileobject of a project's data."""
        url = ('export-rows/' + urllib.parse.quote(self.project_name()) + '.' + export_format)
        response = self.do_raw(url, data={'format': export_format})
        return response.text

    def export_rows(self, **kwargs):
        """Return an iterable of parsed rows of a project's data."""
        return csv.reader(self.export(**kwargs), dialect='excel-tab')

    def delete(self):
        response_json = self.do_json('delete-project', include_engine=False)
        return 'code' in response_json and response_json['code'] == 'ok'

    def compute_facets(self, facets=None):
        """Compute facets as per the project's engine.

        The response object has two attributes, mode & facets. mode is one of
        'row-based' or 'record-based'. facets is a magic list of facets in the
        same order as they were specified in the Engine. Magic allows the
        original Engine's facet as index into the response, e.g.,

        name_facet = TextFacet('name')
        response = project.compute_facets(name_facet)
        response.facets[name_facet]     # same as response.facets[0]
        """
        if facets:
            self.engine.set_facets(facets)
        response = self.do_json('compute-facets')
        return self.engine.facets_response(response)

    def get_rows(self, facets=None, sort_by=None, start=0, limit=10):
        if facets:
            self.engine.set_facets(facets)
        if sort_by is None:
            self.sorting = facet.Sorting([])
        elif sort_by is not None:
            self.sorting = facet.Sorting(sort_by)
        response = self.do_json('get-rows',
                                params={'start': start, 'limit': limit}, data={'sorting': self.sorting.as_json()})
        return self.rows_response_factory(response)

    def reorder_rows(self, sort_by=None):
        if sort_by is not None:
            self.sorting = facet.Sorting(sort_by)
        response = self.do_json('reorder-rows', params={'sorting': self.sorting.as_json()})
        # clear sorting
        # self.sorting = facet.Sorting()
        return response

    def remove_rows(self, facets=None):
        if facets:
            self.engine.set_facets(facets)
        return self.do_json('remove-rows')

    def text_transform(self, column, expression, on_error='set-to-blank', repeat=False, repeat_count=10):
        response = self.do_json('text-transform', params={
            'columnName': column,
            'expression': expression,
            'onError': on_error,
            'repeat': repeat,
            'repeatCount': repeat_count
        })
        return response

    def edit(self, column, edit_from, edit_to):
        edits = [{'from': [edit_from], 'to': edit_to}]
        return self.mass_edit(column, edits)

    def mass_edit(self, column, edits, expression='value'):
        """edits is [{'from': ['foo'], 'to': 'bar'}, {...}]"""
        edits = json.dumps(edits)
        response = self.do_json('mass-edit', params={'columnName': column, 'expression': expression, 'edits': edits})
        return response

    clusterer_defaults = {
        'binning': {
            'type': 'binning',
            'function': 'fingerprint',
            'params': {},
        },
        'knn': {
            'type': 'knn',
            'function': 'levenshtein',
            'params': {
                'radius': 1,
                'blocking-ngram-size': 6,
            },
        },
    }

    def compute_clusters(self, column, clusterer_type='binning', refine_function=None, params=None):
        """Returns a list of clusters of {'value': ..., 'count': ...}."""
        clusterer = self.clusterer_defaults[clusterer_type]
        if params is not None:
            clusterer['params'] = params
        if refine_function is not None:
            clusterer['function'] = refine_function

        clusterer['column'] = column
        response = self.do_json('compute-clusters', data={'clusterer': str(clusterer)})
        return [[{'value': x['v'], 'count': x['c']} for x in cluster]
                for cluster in response]

    def annotate_one_row(self, row, annotation, state=True):
        if annotation not in ('starred', 'flagged'):
            raise ValueError('annotation must be one of starred or flagged')
        state = 'true' if state is True else 'false'
        return self.do_json('annotate-one-row', params={'row': row.index, annotation: state})

    def flag_row(self, row, flagged=True):
        return self.annotate_one_row(row, 'flagged', flagged)

    def star_row(self, row, starred=True):
        return self.annotate_one_row(row, 'starred', starred)

    def add_column(self, column, new_column, expression='value', column_insert_index=None, on_error='set-to-blank'):
        if column_insert_index is None:
            column_insert_index = self.column_order[column] + 1
        response = self.do_json(
            'add-column',
            params={
                'baseColumnName': column,
                'newColumnName': new_column,
                'expression': expression,
                'columnInsertIndex': column_insert_index,
                'onError': on_error
            }
        )
        self.get_models()
        return response

    def split_column(
            self, column, separator=',', mode='separator', regex=False, guess_cell_type=True,
            remove_original_column=True):
        response = self.do_json(
            'split-column',
            params={
                'columnName': column,
                'separator': separator,
                'mode': mode,
                'regex': regex,
                'guessCellType': guess_cell_type,
                'removeOriginalColumn': remove_original_column
            })
        self.get_models()
        return response

    def rename_column(self, column, new_column):
        response = self.do_json('rename-column', params={'oldColumnName': column, 'newColumnName': new_column})
        self.get_models()
        return response

    def reorder_columns(self, new_column_order):
        """Takes an array of column names in the new order."""
        if new_column_order is not str:
            new_column_order = str(new_column_order)
        response = self.do_json('reorder-columns', data={'columnNames': new_column_order})
        self.get_models()
        return response

    def move_column(self, column, index):
        """Move column to a new position."""
        if index == 'end':
            index = len(self.columns) - 1
        response = self.do_json('move-column', params={'columnName': column, 'index': index})
        self.get_models()
        return response

    def blank_down(self, column):
        response = self.do_json('blank-down', params={'columnName': column})
        self.get_models()
        return response

    def fill_down(self, column):
        response = self.do_json('fill-down', params={'columnName': column})
        self.get_models()
        return response

    def transpose_columns_into_rows(
            self, start_column, column_count,
            combined_column_name, separator=':', prepend_column_name=True,
            ignore_blank_cells=True):

        response = self.do_json(
            'transpose-columns-into-rows',
            params={
                'startColumnName': start_column,
                'columnCount': column_count,
                'combinedColumnName': combined_column_name,
                'prependColumnName': prepend_column_name,
                'separator': separator,
                'ignoreBlankCells': ignore_blank_cells
            }
        )
        self.get_models()
        return response

    def transpose_rows_into_columns(self, column, row_count):
        response = self.do_json('transpose-rows-into-columns', params={'columnName': column, 'rowCount': row_count})
        self.get_models()
        return response

    def get_operations(self):
        response = self.do_json('get-operations')
        self.get_models()
        return response

    # Reconciliation
    # http://code.google.com/p/google-refine/wiki/ReconciliationServiceApi
    def guess_types_of_column(self, column, service):
        """Query the reconciliation service for what it thinks this column is.

        service: reconciliation endpoint URL

        Returns [
           {"id":"/domain/type","name":"Type Name","score":10.2,"count":18},
           ...
        ]
        """
        response = self.do_json(
            'guess-types-of-column',
            params={
                'columnName': column,
                'service': service
            },
            include_engine=False)
        return response['types']

    def get_reconciliation_services(self):
        response = self.get_preference('reconciliation.standardServices')
        self.recon_services = response
        return response

    def get_reconciliation_service_by_name_or_url(self, name):
        recon_services = self.get_reconciliation_services()
        for recon_service in recon_services:
            if recon_service['name'] == name or recon_service['server_url'] == name:
                return recon_service
        return None

    def reconcile(self, column, service, reconciliation_type=None, reconciliation_config=None):
        """Perform a reconciliation asynchronously.

        config: {
            "mode": "standard-service",
            "service": "http://.../reconcile/",
            "identifierSpace": "http://.../ns/authority",
            "schemaSpace": "http://.../ns/type",
            "type": {
                "id": "/domain/type",
                "name": "Type Name"
            },
            "autoMatch": true,
            "columnDetails": []
        }

        Returns typically {'code': 'pending'}; call wait_until_idle() to wait
        for reconciliation to complete.
        """
        # Create a reconciliation config by looking up recon service info
        if reconciliation_config is None:
            service = self.get_reconciliation_service_by_name_or_url(service)
            if reconciliation_type is None:
                raise ValueError('Must have at least one of config or type')
            reconciliation_config = {
                'mode': 'standard-service',
                'service': service['server_url'],
                'identifierSpace': service['identifierSpace'],
                'schemaSpace': service['schemaSpace'],
                'type': {
                    'id': reconciliation_type['id'],
                    'name': reconciliation_type['name'],
                },
                'autoMatch': True,
                'columnDetails': [],
            }
        return self.do_json('reconcile', params={'columnName': column, 'config': str(reconciliation_config)})

class InvalidFileFormat(ValueError):
    pass


cor = {'amarelo':'\033[1;33m', 'limp':'\033[m', 'red':'\033[1;31m'}

extenso = ('zero', 'um', 'dois', 'três', 'quatro', 'cinco', 'seis', 'sete', 'oito', 'nove', 'dez', 'onze', 'doze', 'treze',
     'quatorze', 'quinze', 'dezesseis', 'dezessete', 'dezoito', 'dezenove', 'vinte')

n = int(input('Diga um número entre 0 e 20: '))
while n < 0 or n >20:
    print(cor['red'], 'Número inválido', cor['limp'], end='')
    n = int(input('Tente novamente com nº entre 0 e 20: '))

print('\nVc digitou o número {0}{1}{2}'.format(cor['amarelo'], extenso[n], cor['limp']))

from datetime import date
print(f'\nProcessado em {date.today()}')

#Para obter a posição e o nome ocupado nesta posição:
#for posicao, nome in enumerate(extenso):
#    print(f'posição {posicao} = {nome}')

#para obter a posição
#for c in range(0, len(extenso)):
#    print(c)


from flask import render_template, request
from . import main
from .. import db
from ..models import Message
import json
import re

@main.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@main.route('/messages', methods=['GET'])
def get_messages():
    result = dict()
    code = None

    messages = Message.query.all()
    if len(messages) > 0:
        message_results = list()
        for message in messages:
            if is_base64(message.content):
                message_to_add = {'id': message.id, 'content': message.content }
                message_results.append(message_to_add)
        result.update({ 'messages': message_results })
        result.update({ 'success': True })
        code = 200
    else:
        result.update({ 'success': False })
        result.update({ 'message': 'not found' })
        code = 404
    return json.dumps(result), code

@main.route('/messages', methods=['POST'])
def add_message():
    result = dict()
    code = None

    content = request.form['content']
    if content is None:
        result.update({ 'success': False })
        result.update({ 'message': 'missing content' })
        code = 500
    elif is_base64(content) is False:
        result.update({ 'success': False })
        result.update({ 'message': 'content is not base64 encoded' })
        code = 500
    else:
        message = Message(content=request.form['content'])
        db.session.add(message)
        result.update({ 'success': True })
        code = 200
    return json.dumps(result), code

def is_base64(string):
    if len(string) % 4 == 0 and re.match('^[A-Za-z0-9+\/=]+\Z', string):
        return(True)
    else:
        return(False)


# 从文件中获取UA以List返回,默认返回前20个
import sqlite3

def Enca_get_proxyUA(start_num=0, end_number=20):
    db = sqlite3.connect("./useragents.db")
    cursor = db.cursor().execute(f'select * from uas limit {start_num},{end_number}').fetchall()
    for ua in cursor:
        result_ualist = []
        for this_ua in ua[1:]:
            result_ualist.append(this_ua)
        # print('/'.join(result_ualist))
        return result_ualist

print(140 * 0.3)


import numpy as np
import random
#from scipy.fftpack import dct
import json
import sys
import config
from env import make_env
import time

final_mode = True
render_mode = True
RENDER_DELAY = False

def make_model(game):
  # can be extended in the future.
  model = Model(game)
  return model

def sigmoid(x):
  return 1 / (1 + np.exp(-x))

def relu(x):
  return np.maximum(x, 0)

def passthru(x):
  return x

def softmax(x):
  e_x = np.exp(x - np.max(x))
  return e_x / e_x.sum(axis=0)

def sample(p):
  return np.argmax(np.random.multinomial(1, p))

class Model:
  ''' simple feedforward model '''
  def __init__(self, game):
    self.noise_level = 0.0
    self.env_name = game.env_name

    self.input_size = game.input_size
    self.output_size = game.output_size

    self.shapes = [ (game.input_size, game.output_size) ]

    self.sample_output = False
    if game.activation == 'relu':
      self.activations = [relu]
    elif game.activation == 'sigmoid':
      self.activations = [sigmoid]
    elif game.activation == 'passthru':
      self.activations = [np.tanh]
    else:
      self.activations = [np.tanh]

    self.weight = []
    self.param_count = game.input_size * game.output_size

    for shape in self.shapes:
      self.weight.append(np.zeros(shape=shape))

    self.render_mode = False

  def make_env(self, seed=-1, render_mode=False, load_model=True):
    self.render_mode = render_mode
    self.env = make_env(self.env_name, seed=seed, render_mode=render_mode, load_model=load_model)

  def get_action(self, x):
    # if mean_mode = True, ignore sampling.
    h = np.array(x).flatten()

    num_layers = len(self.weight)
    for i in range(num_layers):
      w = self.weight[i]
      h = np.matmul(h, w)
      h = self.activations[i](h + np.random.randn()*self.noise_level)

    return h

  def set_model_params(self, model_params):
    pointer = 0
    for i in range(len(self.shapes)):
      w_shape = self.shapes[i]
      s_w = np.product(w_shape)
      s = s_w
      chunk = np.array(model_params[pointer:pointer+s])
      self.weight[i] = chunk[:s_w].reshape(w_shape)
      pointer += s

  def load_model(self, filename):
    with open(filename) as f:
      data = json.load(f)
    print('loading file %s' % (filename))
    self.data = data
    model_params = np.array(data[0]) # assuming other stuff is in data
    self.set_model_params(model_params)
    # also load the vae and rnn
    self.env.vae.load_json('tf_models/vae.json')
    self.env.rnn.load_json('tf_models/rnn.json')

  def get_random_model_params(self, stdev=0.1):
    #return np.random.randn(self.param_count)*stdev
    return np.random.standard_cauchy(self.param_count)*stdev # spice things up!

  def init_random_model_params(self, stdev=0.1):
    params = self.get_random_model_params(stdev=stdev)
    self.set_model_params(params)
    vae_params = self.env.vae.get_random_model_params(stdev=stdev)
    self.env.vae.set_model_params(vae_params)
    rnn_params = self.env.rnn.get_random_model_params(stdev=stdev)
    self.env.rnn.set_model_params(rnn_params)

def evaluate(model):
  # run 100 times and average score, according to the reles.
  model.env.seed(0)
  total_reward = 0.0
  N = 100
  for i in range(N):
    reward, t = simulate(model, train_mode=False, render_mode=False, num_episode=1)
    total_reward += reward[0]
  return (total_reward / float(N))

def simulate(model, train_mode=False, render_mode=True, num_episode=5, seed=-1, max_len=-1):

  reward_list = []
  t_list = []

  max_episode_length = 2100

  if train_mode and max_len > 0:
    max_episode_length = max_len

  if (seed >= 0):
    random.seed(seed)
    np.random.seed(seed)
    model.env.seed(seed)

  for episode in range(num_episode):

    obs = model.env.reset()

    if obs is None:
      obs = np.zeros(model.input_size)

    total_reward = 0.0

    for t in range(max_episode_length):

      if render_mode:
        model.env.render("human")
        if RENDER_DELAY:
          time.sleep(0.01)

      action = model.get_action(obs)

      prev_obs = obs

      obs, reward, done, info = model.env.step(action)

      if (render_mode):
        pass
        #print("action", action, "step reward", reward)
        #print("step reward", reward)
      total_reward += reward

      if done:
        break

    if render_mode:
      print("reward", total_reward, "timesteps", t)
      model.env.close()

    reward_list.append(total_reward)
    t_list.append(t)

  return reward_list, t_list

def main():

  global RENDER_DELAY
  global final_mode

  assert len(sys.argv) > 2, 'python model.py gamename render/norender path_to_model.json [seed]'

  gamename = sys.argv[1]

  game = config.games[gamename]

  final_mode_string = str(sys.argv[2])
  if (final_mode_string == "render"):
    final_mode = False # don't run 100 times, just visualize results.

  use_model = False
  if (len(sys.argv) > 3):
    use_model = True
    filename = sys.argv[3]
    print("filename", filename)

  the_seed = np.random.randint(10000)
  if len(sys.argv) > 4:
    the_seed = int(sys.argv[4])
    print("seed", the_seed)

  model = make_model(game)
  print('model size', model.param_count)

  if (use_model):
    model.make_env(render_mode=render_mode)
    model.load_model(filename)
  else:
    model.make_env(render_mode=render_mode, load_model=False)
    model.init_random_model_params(stdev=np.random.rand()*0.01)

  if final_mode:
    total_reward = 0.0
    np.random.seed(the_seed)
    model.env.seed(the_seed)
    reward_list = []

    for i in range(100):
      reward, steps_taken = simulate(model, train_mode=False, render_mode=False, num_episode=1)
      print("iteration", i, "reward", reward[0])
      total_reward += reward[0]
      reward_list.append(reward[0])
    print("seed", the_seed, "average_reward", total_reward/100, "stdev", np.std(reward_list))

  else:

    reward, steps_taken = simulate(model,
      train_mode=False, render_mode=render_mode, num_episode=1)
    print ("terminal reward", reward, "average steps taken", np.mean(steps_taken)+1)

if __name__ == "__main__":
  main()


import json

def main():
    # process the car roads
    with open("./Assignments/A05/assets/api/data/roads.geojson", 'r') as infile:
        car_roads = json.load(infile)
        # wipe the properties tag in each document
        for road in car_roads['features']:
            road['properties'] = {'type': 'carroad'}
        with open("./Assignments/A05/assets/api/data/roads_wiped.geojson", 'w') as outfile:
            json.dump(car_roads,outfile,indent='  ')

    # process the rail roads
    with open("./Assignments/A05/assets/api/data/railroads.geojson", 'r') as infile:
        rail_roads = json.load(infile)
        # wipe the properties tag in each document
        for road in rail_roads['features']:
            road['properties'] = {'type': 'railroad'}
        with open("./Assignments/A05/assets/api/data/railroads_wiped.geojson", 'w') as outfile:
            json.dump(rail_roads,outfile,indent='  ')

if __name__ == "__main__":
    main()


import sys
try:  # Literal 3.8
    from typing import Literal
except ImportError:
    input("requires Python 3.9 or higher... press enter to quit")
    exit(1)
import argparse
import time

from subversion_rando.game import CypherItems, GameOptions
from subversion_rando.logic_presets import casual, expert, medium
from subversion_rando.main_generation import generate, write_rom, write_spoiler_file

def commandLineArgs(sys_args: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--casual', action="store_true",
                        help='Casual logic, easy setting matching the vanilla Subversion experience, Default')
    parser.add_argument('-u', '--logicmedium', action="store_true",
                        help='Medium logic, medium setting between casual and expert')
    parser.add_argument('-e', '--expert', action="store_true",
                        help='Expert logic, hard setting comparable to Varia.run Expert difficulty')
    parser.add_argument('-q', '--logiccustom', action="store_true",
                        help='use the gui to customize logic')

    parser.add_argument('-s', '--speedrun', action="store_true",
                        help='Speedrun fill, fast setting comparable to Varia.run Speedrun fill algorithm')
    parser.add_argument('-d', '--assumedfill', action="store_true",
                        help='Assumed fill, standard slower progression fill algorithm, Default')
    parser.add_argument(
        '-m', '--medium', action="store_true",
        help='Medium fill, medium speed setting that places low-power items first for increased exploration'
    )
    parser.add_argument('-mm', '--majorminor', action="store_true",
                        help='Major-Minor fill, using unique majors and locations')
    parser.add_argument('-b', '--majorminorbias', action="store_true",
                        help='unique items have high probability to be in unique item locations')

    parser.add_argument('-a', '--area', action="store_true",
                        help='Area rando shuffles major areas of the game, expert logic only')

    parser.add_argument('-o', '--smallspaceport', action="store_true",
                        help='cuts out some parts of the space port to make it smaller')

    parser.add_argument('-r', '--escapeshortcuts', action="store_true",
                        help='shortens the escape paths - (final escape shortened only if not area rando)')

    parser.add_argument('--daphne_gate', action="store_true",
                        help='???')

    parser.add_argument('--goals', default=0, type=int,
                        help='number of randomized objectives (0 for disabled)')

    parser.add_argument(
        '--skipcrashspaceport', action="store_true",
        help='A second GFS Daphne starts crashed in Lomyr Valley, removing the requirement to crash the Space Port'
    )
    args = parser.parse_args(sys_args)
    # print(args)
    return args

# main program
def Main(argv: list[str]) -> None:
    """ generate from command line """
    workingArgs = commandLineArgs(argv[1:])

    logic = casual  # default
    if workingArgs.expert:
        logic = expert
    elif workingArgs.logicmedium:
        logic = medium

    fillChoice: Literal["M", "MM", "D", "S", "B"]
    if workingArgs.medium:
        fillChoice = "M"
    elif workingArgs.majorminor:
        fillChoice = "MM"
    elif workingArgs.speedrun:
        fillChoice = "S"
    elif workingArgs.majorminorbias:
        fillChoice = "B"
    else:
        fillChoice = "D"

    area_rando = False
    if workingArgs.area:
        area_rando = True
        # if fillChoice == "MM":
        #     fillChoice = "D"
        #     print("Cannot use Major-Minor in Area rando currently. Using assumed fill instead.")

    small_spaceport = False
    if workingArgs.smallspaceport:
        small_spaceport = True

    escape_shortcuts = False
    if workingArgs.escapeshortcuts:
        escape_shortcuts = True

    daphne_gate = False
    if workingArgs.daphne_gate:
        daphne_gate = True

    skip_crash_space_port = False
    if workingArgs.skipcrashspaceport:
        skip_crash_space_port = True

    options = GameOptions(logic, area_rando, fillChoice, small_spaceport,
                          escape_shortcuts, CypherItems.NotRequired,
                          daphne_gate, workingArgs.goals, skip_crash_space_port)
    game = generate(options)
    rom_name = write_rom(game)
    write_spoiler_file(game, rom_name)

if __name__ == "__main__":
    t0 = time.perf_counter()
    Main(sys.argv)
    t1 = time.perf_counter()
    print(f"time taken: {t1 - t0}")


import constants as c

import copy

from cppn import CPPN

import matplotlib.pyplot as plt

from tree import TREE

cppns = {}

def Create_CPPNs():

    for panelIndex in range(1,c.numberOfCPPNs+1):

        cppns[panelIndex] = CPPN()

def Draw_CPPNs():

    fig = Prep_Drawing()

    for panelIndex in range(1,c.numberOfCPPNs+1):

        cppns[panelIndex].Draw(fig,panelIndex)

    plt.show()

def Mutate_CPPNs(choice):

    for panelIndex in range(1,c.numberOfCPPNs+1):

        if panelIndex != choice:

            cppns[panelIndex] = copy.deepcopy(cppns[choice])

            cppns[panelIndex].Mutate()

def Prep_Drawing():

    fig = plt.figure(figsize=(20,10))

    #ax = fig.add_subplot(339)

    #ax.set_xlim( c.vectorFieldMinimum - c.vectorFieldCellWidth  , c.vectorFieldMaximum + c.vectorFieldCellWidth)

    #ax.set_ylim( c.vectorFieldMinimum - c.vectorFieldCellHeight , c.vectorFieldMaximum + c.vectorFieldCellHeight)

    return fig

# -------------- Main function -------------------

Create_CPPNs()

Draw_CPPNs()

while True:

    choice = int( input('Which vector field to you like the best [1-'+str(c.numberOfCPPNs)+']: ') )

    Mutate_CPPNs(choice)

    Draw_CPPNs()

exit()

tree = TREE()

for i in range(10):

    tree.Update_Positions_Starting_At(0,0)

    #tree.Print()
    #tree.Count()
    tree.Draw()

    tree.Grow(cppn)


"""
Created on 5 févr. 2014

@author: inso
"""

import datetime
import logging
import asyncio
from ..tools.decorators import asyncify, once_at_a_time, cancel_once_task
from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, QSortFilterProxyModel, \
    QDateTime, QLocale, QModelIndex

from PyQt5.QtGui import QFont, QColor

class CertsFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, ts_from, ts_to, parent=None):
        super().__init__(parent)
        self.app = None
        self.ts_from = ts_from
        self.ts_to = ts_to

    @property
    def account(self):
        return self.app.current_account

    def set_period(self, ts_from, ts_to):
        """
        Filter table by given timestamps
        """
        logging.debug("Filtering from {0} to {1}".format(
            datetime.datetime.fromtimestamp(ts_from).isoformat(' '),
            datetime.datetime.fromtimestamp(ts_to).isoformat(' '))
        )
        self.ts_from = ts_from
        self.ts_to = ts_to
        self.modelReset.emit()

    def filterAcceptsRow(self, sourceRow, sourceParent):
        def in_period(date_ts):
            return date_ts in range(self.ts_from, self.ts_to)

        source_model = self.sourceModel()
        date_col = source_model.columns_types.index('date')
        source_index = source_model.index(sourceRow, date_col)
        date = source_model.data(source_index, Qt.DisplayRole)
        return in_period(date)

    @property
    def community(self):
        return self.sourceModel().community

    def columnCount(self, parent):
        return self.sourceModel().columnCount(None) - 5

    def setSourceModel(self, sourceModel):
        self.app = sourceModel.app
        super().setSourceModel(sourceModel)

    def lessThan(self, left, right):
        """
        Sort table by given column number.
        """
        source_model = self.sourceModel()
        left_data = source_model.data(left, Qt.DisplayRole)
        right_data = source_model.data(right, Qt.DisplayRole)
        if left_data == "":
            return self.sortOrder() == Qt.DescendingOrder
        elif right_data == "":
            return self.sortOrder() == Qt.AscendingOrder
        return (left_data < right_data)

    def data(self, index, role):
        source_index = self.mapToSource(index)
        model = self.sourceModel()
        source_data = model.data(source_index, role)
        state_col = model.columns_types.index('state')
        state_index = model.index(source_index.row(), state_col)
        state_data = model.data(state_index, Qt.DisplayRole)
        if role == Qt.DisplayRole:
            if source_index.column() == model.columns_types.index('uid'):
                return source_data
            if source_index.column() == model.columns_types.index('date'):
                return QLocale.toString(
                    QLocale(),
                    QDateTime.fromTime_t(source_data).date(),
                    QLocale.dateFormat(QLocale(), QLocale.ShortFormat)
                )
            if source_index.column() == model.columns_types.index('payment') or \
                    source_index.column() == model.columns_types.index('deposit'):
                return source_data

        if role == Qt.FontRole:
            font = QFont()
            return font

        if role == Qt.ForegroundRole:
            if state_data == TransferState.REFUSED:
                return QColor(Qt.red)
            elif state_data == TransferState.TO_SEND:
                return QColor(Qt.blue)

        if role == Qt.TextAlignmentRole:
            if source_index.column() == self.sourceModel().columns_types.index('date'):
                return Qt.AlignCenter

        if role == Qt.ToolTipRole:
            if source_index.column() == self.sourceModel().columns_types.index('date'):
                return QDateTime.fromTime_t(source_data).toString(Qt.SystemLocaleLongDate)
            return None

        return source_data

class HistoryTableModel(QAbstractTableModel):
    """
    A Qt abstract item model to display communities in a tree
    """

    def __init__(self, app, account, community, parent=None):
        """
        Constructor
        """
        super().__init__(parent)
        self.app = app
        self.account = account
        self.community = community
        self.transfers_data = []
        self.refresh_certs()
        self._max_confirmations = 0

        self.columns_types = (
            'date',
            'uid',
            'state',
            'pubkey',
            'block_number'
        )

        self.column_headers = (
            self.tr('Date'),
            self.tr('UID/Public key'),
            'State',
            'Pubkey',
            'Block Number'
        )

    def change_account(self, account):
        cancel_once_task(self, self.refresh_certs)
        self.account = account

    def change_community(self, community):
        cancel_once_task(self, self.refresh_certs)
        self.community = community

    def certifications(self):
        if self.account:
            return self.account.certifications(self.community)
        else:
            return []

    @once_at_a_time
    @asyncify
    async def refresh_certs(self):
        self.beginResetModel()
        self.transfers_data = []
        self.endResetModel()

    def max_confirmations(self):
        return self._max_confirmations

    def rowCount(self, parent):
        return len(self.transfers_data)

    def columnCount(self, parent):
        return len(self.columns_types)

    def headerData(self, section, orientation, role):
        if self.account and self.community:
            if role == Qt.DisplayRole:
                return self.column_headers[section]

    def data(self, index, role):
        row = index.row()
        col = index.column()

        if not index.isValid():
            return QVariant()

        if role == Qt.DisplayRole:
            return self.transfers_data[row][col]

        if role == Qt.ToolTipRole:
            return self.transfers_data[row][col]

    def flags(self, index):
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled


#!/usr/bin/python

# -*- coding: utf-8 -*-
import RPIO
import time
import telebot
import vars
import MySQLdb

bot = telebot.TeleBot(vars.token)
telegram_id = vars.telegramId
input_pins = (18,19,20,21,22,23,24,25,26,27)

def writeDoorState(when, state):
  db = MySQLdb.connect(vars.mysqlHost,vars.mysqUser,vars.mysqlPassword,vars.mysqlDatabase)
  cursor = db.cursor()
  sql = "INSERT INTO door(bool, date, name) VALUES (0, '%s', '%s')" % (when , state)
  try:
     # Execute the SQL command
     cursor.execute(sql)
     # Commit your changes in the database
     db.commit()
  except:
     # Rollback in case there is any error
     db.rollback()
  db.close()

for i in xrange(len(input_pins)):
  RPIO.setup(input_pins[i], RPIO.IN, pull_up_down=RPIO.PUD_DOWN)

def write_door_state(gpio_id, val):
  if val==0:
    bot.send_message(telegram_id, "Openning")
    writeDoorState(time.strftime("%d.%m.%Y %H:%M:%S"), 'Open')
  else:
    bot.send_message(telegram_id, "Closing")
    writeDoorState(time.strftime("%d.%m.%Y %H:%M:%S"), 'Close')

RPIO.add_interrupt_callback(24, write_door_state,pull_up_down=RPIO.PUD_DOWN,threaded_callback=True, debounce_timeout_ms=50)
RPIO.wait_for_interrupts()


import abc
import typing as tp

class Conversation(abc.ABC):
    """
    Inspired by https://github.com/lm-sys/FastChat/blob/main/fastchat/conversation.py
    """

    def __init__(self, system_prompt: str, roles: tp.Tuple[str, str]):
        self.system_prompt = system_prompt
        self.roles = roles
        self.messages: tp.List[tp.Tuple[str, str]] = []

    @abc.abstractmethod
    def get_prompt(self) -> str:
        pass

    def update_last_message(self, text: str) -> None:
        self.messages[-1] = (self.messages[-1][0], text)

    def append_message(self, role: str, text: str) -> None:
        self.messages.append([role, text])

class LlamaConversation(Conversation):
    def __init__(self):
        super().__init__(
            system_prompt="",  # faked for compatibility
            roles=("", ""),
        )

    def get_prompt(self) -> str:
        prompt = self.system_prompt
        for role, text in self.messages:
            if text:
                prompt += f" {role} {text}"
            else:
                prompt += f" {role} "
        return prompt

class SaigaConversation(Conversation):
    def __init__(self):
        super().__init__(
            system_prompt="<s> system\n Ты — Сайга, русскоязычный автоматический ассистент. Ты разговариваешь с людьми и помогаешь им.</s>\n",
            roles=("user", "bot"),
        )

    def get_prompt(self) -> str:
        prompt = self.system_prompt
        for role, text in self.messages:
            if text:
                prompt += f"<s> {role}\n {text} </s>\n"
            else:
                prompt += f"<s> {role} "
        return prompt

class VerbalistConversation(Conversation):
    def __init__(self):
        super().__init__(
            system_prompt="<s> system\n Ты — Буквоед, русскоязычный автоматический ассистент. Ты разговариваешь с людьми и помогаешь им. </s>\n",
            roles=("user", "bot"),
        )

    def get_prompt(self) -> str:
        prompt = self.system_prompt
        for role, text in self.messages:
            if text:
                prompt += f"<s> {role}\n {text} </s>\n"
            else:
                prompt += f"<s> {role} "
        return prompt

class MistralOrcaConversation(Conversation):
    def __init__(self):
        super().__init__(
            system_prompt="<|im_start|> system\n You are MistralOrca, a large language model trained by Alignment Lab AI. <|im_end|>\n",
            roles=("user", "assistant"),
        )

    def get_prompt(self) -> str:
        # prompt = self.system_prompt
        prompt = ""
        for role, text in self.messages:
            if text:
                prompt += f"<|im_start|>{role}\n{text}<|im_end|>\n"
            else:
                prompt += f"<|im_start|>{role}\n"
        return prompt

conversation_classes = {
    "saiga": SaigaConversation,
    "verbalist": VerbalistConversation,
    "llama": LlamaConversation,
    "open_orca_mistral": MistralOrcaConversation,
}

if __name__ == "__main__":
    utterances = [
        "А тот второй парень — он тоже терминатор?",
        "Не совсем. Модель T-1000, усовершенствованный прототип.",
        "То есть, он более современная модель?",
        "Да. Мимикрирующий жидкий металл.",
        "И что это значит?",
    ]
    print("-=-=-=- Saiga -=-=-=-")
    conv = SaigaConversation()
    conv.append_message(conv.roles[0], utterances[0])
    conv.append_message(conv.roles[1], utterances[1])
    conv.append_message(conv.roles[0], utterances[2])
    conv.append_message(conv.roles[1], None)
    print(conv.get_prompt())
    print("-=-=-=-=-=-=-=-=-=-=-")


from PIL import Image
from collections import Counter
import numpy as np

def crop(pilimage, height, width):
    allbrightresults = []
    imgw, imgh = pilimage.size
    adjusth = (imgh // height) * height
    adjustw = (imgw // width) * width
    for i in range(0, adjusth, height):
        for j in range(0, adjustw, width):
            box = (j, i, j + width, i + height)
            a = pilimage.crop(box)
            single_brightness_val = calc_brightness(a)
            allbrightresults.append(single_brightness_val)

    theCounter = Counter(allbrightresults)
    if theCounter['Not Bright'] > 1:
        return 'Invalid'
    else:
        return 'Valid'

def calc_brightness(image):
    gray = image.convert('L')
    hist = gray.histogram()
    pixels = sum(hist)
    brightness = scale = len(hist)

    for i in range(0, scale):
        ratio = hist[i] / pixels
        brightness += ratio * (-scale + i)
    if brightness == 255:
        return "Bright"
    else:
        if brightness / scale > 0.5:
            return "Bright"
        return "Not Bright"

# if __name__ == "__main__":
#     filename = sys.argv[1]
#     image = Image.open(filename)
#     resultbright, resultblur = crop(image, 200, 200, 20)
#     counterbright = Counter(resultbright)
#     counterblur = Counter(resultblur)
#     print(counterbright)
#     print(counterblur)