import data_util
from tensorflow.keras.utils import to_categorical
import numpy as np
# this only works for binary classification

def model_evaluation(clf, x_val, y_val, cardio_dict, gender_dict):
    from sklearn.metrics import accuracy_score
    from sklearn.metrics import precision_score
    from sklearn.metrics import recall_score
    from sklearn.metrics import f1_score
    y_pred = clf.predict(x_val)

    y_pred_cardio = y_pred[:, :2]
    y_pred_gender = y_pred[:, 2:]

    y_preds = [y_pred_cardio, y_pred_gender]

    y_val_cardio = y_val[:, :2]
    y_val_gender = y_val[:, 2:]

    y_vals = [y_val_cardio, y_val_gender]

    acc = []
    precision = []
    recall = []
    f_score = []

    for i in range(len(y_preds)):
        y_pred = y_preds[i]
        y_val = y_vals[i]
        if i == 0:
           label_dict = cardio_dict
        else:
           label_dict = gender_dict

        # wrong_instances = []
        y_predict_classes_onthot = []
        y_val_classes_onthot = []
        if label_dict != None:
            for i in range(len(y_pred)):
                maxElement = np.where(y_pred[i] == np.amax(y_pred[i]))[0][0]
                temp = '['
                temp += '0. ' * (maxElement)
                temp += '1.'
                temp += ' 0.' * (2-maxElement-1)
                temp += ']'
                y_predict_classes_onthot.append(label_dict[temp])
                y_val_classes_onthot.append(label_dict[np.array2string(y_val[i])])
                # print(y_predict_classes_onthot)

            y_pred = y_predict_classes_onthot
            y_val = y_val_classes_onthot

        # for i in range(len(y_pred)):
        #     if y_pred[i] != y_val[i]:
        #         wrong_instances.append(backtrack_dict[i])

        # print(y_val)
        # print(y_pred)

        acc       += [accuracy_score (y_val, y_pred)]
        precision += [precision_score(y_val, y_pred)]
        recall    += [recall_score   (y_val, y_pred)]
        f_score   += [f1_score       (y_val, y_pred)]
    return (acc, precision, recall, f_score)

# def where_i_did_wrong():

# http://scikit.ml/api/skmultilearn.model_selection.iterative_stratification.html
def cross_validation(clfs, X, y_true, cardio_dict, gender_dict, num_fold = 10):
    from sklearn.model_selection import StratifiedKFold
    from prettytable import PrettyTable
    from tensorflow.keras.utils import to_categorical
    from skmultilearn.model_selection import IterativeStratification

    skf = IterativeStratification(n_splits=num_fold, random_state=10)
    wrong_instances_clf = {}
    for clf_name in clfs:

        avg_matrics_cardio = [0,0,0,0]
        avg_matrics_gender = [0,0,0,0]

        print('we are trying classifier: ', clf_name)
        clf = clfs[clf_name]
        best_f_score = 0
        best_clf = None
        i = 1

        t_cardio = PrettyTable(['Fold', 'acc', 'precision', 'recall', 'f_score'])
        # t_cardio.title = 'cardio'

        t_gender = PrettyTable(['Fold', 'acc', 'precision', 'recall', 'f_score'])
        # t_gender.title = 'gender'

        wrong_instances = []
        for train_index, val_index in skf.split(X, y_true):

            X_train, X_val = X[train_index], X[val_index]
            y_train, y_val = y_true[train_index], y_true[val_index]

            backtrack_dict = {}
            for index in range(len(val_index)):
                backtrack_dict[index] = val_index[index]

            clf.fit(X_train, y_train,
                  verbose = 0,
                  epochs=100,
                  batch_size=8192)
            acc, precision, recall, f_score = model_evaluation(clf, X_val, y_val, cardio_dict, gender_dict)
            data_util.reset_weights(clf)

            # wrong_instances.extend(wrong_instances_fold)

            # print(' '+str(i)+' ', acc, precision, recall, f_score)
            t_cardio.add_row([' '+str(i)+' ', '{0:.3f}'.format(acc[0]), '{0:.3f}'.format(precision[0]), '{0:.3f}'.format(recall[0]), '{0:.3f}'.format(f_score[0])])
            t_gender.add_row([' '+str(i)+' ', '{0:.3f}'.format(acc[1]), '{0:.3f}'.format(precision[1]), '{0:.3f}'.format(recall[1]), '{0:.3f}'.format(f_score[1])])

            avg_matrics_cardio [0] += acc[0]
            avg_matrics_cardio [1] += precision[0]
            avg_matrics_cardio [2] += recall[0]
            avg_matrics_cardio [3] += f_score[0]
            avg_matrics_gender [0] += acc[1]
            avg_matrics_gender [1] += precision[1]
            avg_matrics_gender [2] += recall[1]
            avg_matrics_gender [3] += f_score[1]
            i += 1
        i-=1
        t_cardio.add_row(['avg','{0:.3f}'.format(avg_matrics_cardio[0]/i),'{0:.3f}'.format(avg_matrics_cardio[1]/i),'{0:.3f}'.format(avg_matrics_cardio[2]/i),'{0:.3f}'.format(avg_matrics_cardio[3]/i)])
        t_gender.add_row(['avg','{0:.3f}'.format(avg_matrics_gender[0]/i),'{0:.3f}'.format(avg_matrics_gender[1]/i),'{0:.3f}'.format(avg_matrics_gender[2]/i),'{0:.3f}'.format(avg_matrics_cardio[3]/i)])

        # print('avg',avg_matrics[0]/i,avg_matrics[1]/i,avg_matrics[2]/i,avg_matrics[3]/i)
        # wrong_instances_clf[clf_name] = wrong_instances
        print('+--------------------------------------------+')
        print('|                  cardio                    |')
        print('+--------------------------------------------+')
        print(t_cardio)
        print('+--------------------------------------------+')
        print('|                   gender                   |')
        print('+--------------------------------------------+')
        print(t_gender)

    return

def main():
    from tensorflow.keras import optimizers
    X, y_true, feature_names = data_util.preprocess_cardio_dataset(
    normalization = True,
    exclusive_all_did_wrong=True,
    is_clean_blood_pressure = True,
    save = False, new_filename = None,
    PCA = True,
    is_using_onehot = True,
    include_gender = True,
    secondary_label = 'gender')

    y_true_cardio = y_true[0]
    y_true_gender = y_true[1]

    y_true_gender[y_true_gender == 2] = 0

    mlp = data_util.getMLP(X.shape[-1], num_class = 2)

    mlp.compile(loss='binary_crossentropy',
              optimizer=optimizers.Adam(lr = 0.001),
              metrics=['accuracy'])

    clfs = {'MLP':mlp}
    print('we are trying to classify label \'gender\'')
    print('+--------------------------------------------+')
    print('|                   gender                   |')
    print('+--------------------------------------------+')
    data_util.cross_validation(clfs, X, y_true_gender, num_fold = 10)

    mlp = data_util.getMLP(X.shape[-1], num_class = 2)

    mlp.compile(loss='binary_crossentropy',
              optimizer=optimizers.Adam(lr = 0.001),
              metrics=['accuracy'])

    clfs = {'MLP':mlp}
    print('we are trying to classify label \'cardio\'')
    print('+--------------------------------------------+')
    print('|                  cardio                    |')
    print('+--------------------------------------------+')
    data_util.cross_validation(clfs, X, y_true_cardio, num_fold = 10)

    y_true_cardio_onehot = to_categorical(y_true_cardio)
    y_true_gender_onehot = to_categorical(y_true_gender)

    unique, counts = np.unique(y_true_gender_onehot, return_counts=True)

    cardio_dict = data_util.get_label_dict(y_true_cardio, y_true_cardio_onehot, 2)
    gender_dict = data_util.get_label_dict(y_true_gender, y_true_gender_onehot, 2)

    y_true = np.concatenate((y_true_cardio_onehot, y_true_gender_onehot), axis=1)

    mlp = data_util.getMLP(X.shape[-1], num_class = 4)

    mlp.compile(loss='binary_crossentropy',
              optimizer=optimizers.Adam(lr = 0.001),
              metrics=['accuracy'])
    print('let the game start')
    clfs = {'MLP_multilabel':mlp}
    cross_validation(clfs, X, y_true, cardio_dict, gender_dict, num_fold = 10)

if __name__ == "__main__":
    # execute only if run as a script
    main()


from datetime import date

from django.db.models import Q
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.status import (
    HTTP_200_OK,
    HTTP_201_CREATED,
    HTTP_204_NO_CONTENT,
    HTTP_400_BAD_REQUEST,
    HTTP_401_UNAUTHORIZED
)

from tax.models import Tax
from tax.serializers import (
    TaxHistorySerializers,
    TaxPaymentSerializers,
    TaxSerializers
)
from user.models import User
from user.utils import (
    IsAdmin,
    IsAdminOrTaxAccountant,
    IsTaxAccountant,
    IsTaxPayer
)

# yyyy/mm/dd
def set_date(sdate):
    var = list(map(int, sdate.split('-')))
    return date(var[0], var[1], var[2])

@swagger_auto_schema(method='post', request_body=TaxSerializers)
@api_view(['POST'])
@permission_classes([IsAuthenticated, IsTaxAccountant])
def request_tax(request, *args, **kwargs):
    """
    Create tax by only tax-accountant type user. From point 2.
    """
    tax_accountant = request.user
    data = request.data
    try:
        if data.get('tax_payer'):
            tax_payer = User.taxpayermanager.filter(
                Q(username=data.get('tax_payer')) | Q(email=data.get('tax_payer'))).first()
            if tax_payer.state is None:
                message = {
                    'detail': f'{tax_payer.username} state is not selected'
                }
                return Response(message, status=HTTP_400_BAD_REQUEST)
        tax = Tax()
        tax.income = int(data.get('income'))
        tax.tax_payer = tax_payer
        tax.tax_accountant = tax_accountant
        if data.get('deadline'):
            deadline = set_date(data.get('deadline'))
            tax.deadline = deadline
        tax.save()
        serializer = TaxSerializers(tax)
        return Response(serializer.data, status=HTTP_201_CREATED)
    except Exception as e:
        message = {'detail': 'Invalid Parameters.'}
        return Response(message, status=HTTP_400_BAD_REQUEST)

@swagger_auto_schema(method='put', request_body=TaxSerializers)
@api_view(['PUT'])
@permission_classes([IsAuthenticated, IsTaxAccountant])
def edit_tax(request, id, *args, **kwargs):
    """
    Edit tax by only tax_accountant type user. From point 4.
    """
    tax_accountant = request.user
    data = request.data
    try:
        tax = Tax.objects.get(pk=id)
        if tax_accountant == tax.tax_accountant:
            if tax.status != 'PAID':
                tax_payer = User.taxpayermanager.filter(
                    Q(username=data.get('tax_payer')) | Q(email=data.get('tax_payer'))).first()
                if data.get('income'):
                    tax.income = int(data.get('income'))
                if data.get('status'):
                    tax.status = data.get('status').upper()
                if data.get('deadline'):
                    tax.deadline = set_date(data.get('deadline'))
                if tax_payer:
                    tax.tax_payer = tax_payer
                tax.save()
                serializer = TaxSerializers(tax)
                return Response(serializer.data)
            else:
                message = {'detail': 'Tax is already paid.'}
                return Response(message, status=HTTP_400_BAD_REQUEST)
        else:
            message = {'detail': 'You don\'t have right to edit.'}
            return Response(message, status=HTTP_400_BAD_REQUEST)
    except Exception as e:
        message = {'detail': 'Invalid Parameters.'}
        return Response(message, status=HTTP_400_BAD_REQUEST)

created_at = openapi.Parameter(
    'created_at',
    openapi.IN_QUERY,
    description="created_at",
    type=openapi.FORMAT_DATETIME
)
updated_at = openapi.Parameter(
    'updated_at',
    openapi.IN_QUERY,
    description="updated_at",
    type=openapi.FORMAT_DATETIME
)
status = openapi.Parameter(
    'status',
    openapi.IN_QUERY,
    description="Status",
    type=openapi.FORMAT_SLUG
)

@swagger_auto_schema(
    method='get',
    manual_parameters=[status, created_at, updated_at]
)
@api_view(['GET'])
@permission_classes([IsAuthenticated, AllowAny])
def list_tax(request, *args, **kwargs):
    """
    Get list of tax according to hierarchy of users. From point 2.
    """
    try:
        user = request.user
        status = request.query_params.get('status')
        created_at = request.query_params.get('created_at')
        updated_at = request.query_params.get('updated_at')

        if user.user_type == 'admin':
            tax = Tax.objects.all().order_by('updated_at')
        if user.user_type == 'tax-accountant':
            tax = user.user_tax_accountant.all().order_by('updated_at')
        if user.user_type == 'tax-payer':
            tax = user.user_tax_payer.all().order_by('updated_at')

        if status:
            tax = tax.filter(status=status.upper())
        if created_at:
            dt = set_date(created_at)
            tax = tax.filter(
                created_at__year=dt.year,
                created_at__month=dt.month,
                created_at__day=dt.day
            )
        if updated_at:
            dt = set_date(updated_at)
            tax = tax.filter(
                updated_at__year=dt.year,
                updated_at__month=dt.month,
                updated_at__day=dt.day
            )

        serializer = TaxSerializers(tax, many=True)
        return Response(serializer.data, status=HTTP_200_OK)
    except Exception as e:
        message = {'detail': 'Error'}
        print(e)
        return Response(message, status=HTTP_400_BAD_REQUEST)

@swagger_auto_schema(method='get')
@api_view(['GET'])
@permission_classes([IsAuthenticated, IsAdminOrTaxAccountant])
def tax_history(request, id, *args, **kwargs):
    """
    Get record for tax history or changes, only for admin and tax-accountant. From point 2.
    """
    user = request.user
    tax = Tax.objects.get(pk=id)
    try:
        if user.user_type == 'admin' or user.user_type == 'tax-accountant':
            if user.user_type == 'tax-accountant' and user == tax.tax_accountant:
                tax_hist = TaxHistorySerializers(tax)
                return Response(tax_hist.data, status=HTTP_200_OK)
            elif user.user_type == 'admin':
                tax_hist = TaxHistorySerializers(tax)
                return Response(tax_hist.data, status=HTTP_200_OK)
        else:
            return Response(
                'User of tax-payer type not allowed.',
                status=HTTP_401_UNAUTHORIZED
            )
    except Exception as e:
        message = {'detail': 'Tax with id not found.'}
        return Response(message, status=HTTP_400_BAD_REQUEST)

@swagger_auto_schema(method='post', request_body=TaxPaymentSerializers)
@api_view(['POST'])
@permission_classes([IsAuthenticated, IsTaxPayer])
def tax_payment(request, id, *args, **kwargs):
    """
    Payment for tax, only for tax-payer. From point 2.
    """
    user = request.user
    data = request.data
    try:
        tax = Tax.objects.get(pk=id)
    except Exception as e:
        message = {'detail': 'Tax with id not found.'}
        return Response(message, status=HTTP_400_BAD_REQUEST)
    if tax.tax_payer == user:
        serializers = TaxPaymentSerializers(data)
        if (serializers.data.get('payment') == tax.total_amount or tax.total_amount == 0) and tax.status != 'PAID':
            tax.payment()
            message = {
                'message': f'Payment of Rs. {tax.total_amount} is success.'}
            return Response(message, status=HTTP_204_NO_CONTENT)
        else:
            message = {'message': f'Amount to be paid Rs.{tax.total_amount}'}
            return Response(message, status=HTTP_204_NO_CONTENT)
    else:
        message = {'detail': 'Tax with id not for current user.'}
        return Response(message, status=HTTP_400_BAD_REQUEST)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Author: Milan Ondrasovic <milan.ondrasovic@gmail.com>
#
# MIT License
#
# Copyright (c) 2021 Milan Ondrašovič
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import Iterable, List, Optional

import numpy as np

from datagen.position import FixedPositionGen, PositionGenerator
from datagen.shape import ShapeGenerator

def build_grid_layout(
        n_rows: int, n_cols: int, *, x_border: float = 0.2,
        y_border: float = 0.2
) -> List[PositionGenerator]:
    assert n_rows > 0 and n_cols > 0
    assert 0 < x_border < 0.5 and 0 < y_border < 0.5

    x_seps = np.linspace(x_border, 1 - x_border, n_cols)
    y_seps = np.linspace(y_border, 1 - y_border, n_rows)
    xs, ys = np.meshgrid(x_seps, y_seps)

    return [FixedPositionGen(x, y) for x, y in zip(xs.flatten(), ys.flatten())]

class ShapesLayoutGenerator:
    """Generates vertices of given shape anchored in a specific layout.
    """

    def __init__(
            self, img_width: int, img_height: int, shape_gen: ShapeGenerator,
            position_gens: Iterable[PositionGenerator],
            n_shapes: Optional[int] = None
    ) -> None:
        self.img_width: int = img_width
        self.img_height: int = img_height

        self.shape_gen: ShapeGenerator = shape_gen
        self.position_gens: Iterable[PositionGenerator] = position_gens
        self.n_shapes: Optional[int] = n_shapes

    @property
    def shape_name(self) -> str:
        return self.shape_gen.shape_name

    def generate_points(self) -> np.ndarray:
        points = map(
            self.shape_gen.generate_points,
            self._generate_center_points()
        )
        points = np.array(tuple(points), dtype=np.float)

        # Select only a random subset of the points.
        if self.n_shapes is not None:
            shapes_num = min(len(points), self.n_shapes)
            selector = np.array(
                [True] * shapes_num + [False] * (len(points) - shapes_num)
            )
            np.random.shuffle(selector)
            points = points[selector]

        return points

    def _generate_center_points(self) -> Iterable[np.ndarray]:
        return map(
            lambda g: g.generate_absolute_position(
                self.img_width, self.img_height),
            self.position_gens
        )


import operator

from functools import reduce

from Verilog_VCD.Verilog_VCD import parse_vcd

class VCDEngine(object):
    trace_element_mapping = [
        (["instruction"], 0, 32),
        (["address"], 32, 64),
        (["pass_through"], 64, 65),
        (["if_data", "time_start"], 65, 97),
        (["if_data", "time_end"], 97, 129),
        (["if_data", "mem_access_req", "time_start"], 129, 161),
        (["if_data", "mem_access_req", "time_end"], 161, 193),
        (["if_data", "mem_access_res", "time_start"], 193, 225),
        (["if_data", "mem_access_res", "time_end"], 225, 257),
        (["id_data", "time_start"], 257, 289),
        (["id_data", "time_end"], 289, 321),
        (["ex_data", "time_start"], 321, 353),
        (["ex_data", "time_end"], 353, 385),
        (["ex_data", "mem_addr"], 385, 417),
        (["ex_data", "mem_access_req", "time_start"], 417, 449),
        (["ex_data", "mem_access_req", "time_end"], 449, 481),
        (["wb_data", "time_start"], 481, 513),
        (["wb_data", "time_end"], 513, 545),
        (["wb_data", "mem_access_res", "time_start"], 545, 577),
        (["wb_data", "mem_access_res", "time_end"], 577, 609)
    ]

    def extract_tracing_information(self, vcd_file, testbench_module_name):
        # Grab the required signal trace from the VCD file
        parsed = [
            v['tv'] for _, v in
            parse_vcd(str(vcd_file),
                      siglist=["{0}.tracer.trace_data_o[31:0]".format(testbench_module_name)],
                      opt_timescale="ns").items()
        ][0]
        # Reformat the data so that it's an object containing each data
        # item rather than one large bit string.
        processed_data = [(time, TraceElement(bitstring, [["pass_through"]],
                                              self.trace_element_mapping))
                          for (time, bitstring) in parsed[1:]]
        return processed_data

class TraceElement(object):

    instruction = ""
    address = ""
    if_data = {}
    id_data = {}
    ex_data = {}
    wb_data = {}

    def __init__(self, bitstring, fields_to_ignore, trace_element_mapping):
        self.if_data = {
            "time_start": 0,
            "time_end": 0,
            "mem_access_req": {
                "time_start": 0,
                "time_end": 0
            },
            "mem_access_res": {
                "time_start": 0,
                "time_end": 0
            }
        }
        self.id_data = {
            "time_start": 0,
            "time_end": 0
        }
        self.ex_data = {
            "time_start": 0,
            "time_end": 0,
            "mem_access_req": {
                "time_start": 0,
                "time_end": 0
            }
        }
        self.wb_data = {
            "time_start": 0,
            "time_end": 0,
            "mem_access_res": {
                "time_start": 0,
                "time_end": 0
            }
        }
        extended_bitstring = bitstring
        # Since SystemVerilog seems to strip out leading zeroes we have to add
        # them back in
        if len(bitstring) < trace_element_mapping[-1][2]:
            extended_bitstring = \
                "0" * (trace_element_mapping[-1][2] - len(bitstring)) + \
                bitstring
        # Iterate over the individual elements of a Trace Element
        for (field, start, end) in trace_element_mapping:
            # If the field we're looking at is in the fields to ignore
            if field not in fields_to_ignore:
                # If the field list is of length then we have a top level field
                if len(field) == 1:
                    setattr(self, field[0],
                            self.fixed_length_hex(extended_bitstring[start:end],
                                                  32))
                else:
                    self.set_in_dict(
                        getattr(self, field[0]), field[1:],
                        self.fixed_length_hex(extended_bitstring[start:end], 32)
                    )

    @staticmethod
    def get_from_dict(data_dict, map_list):
        return reduce(operator.getitem, map_list, data_dict)

    def set_in_dict(self, data_dict, map_list, value):
        self.get_from_dict(data_dict, map_list[:-1])[map_list[-1]] = value

    @staticmethod
    def fixed_length_hex(bit_string, required_bit_length):
        interim_hex = hex(int(bit_string, base=2))
        if required_bit_length // 4 != (len(interim_hex) - 2):
            return \
                "0x" + \
                "0" * ((required_bit_length // 4) - len(interim_hex) + 2) + \
                interim_hex[2:]
        else:
            return interim_hex

if __name__ == "__main__":
    vcd_engine = VCDEngine()
    results = vcd_engine.extract_tracing_information(
        "../working/125718_24012018.vcd")
    from joushi.display_engine import DisplayEngine
    display_engine = DisplayEngine()
    display_engine.process_and_display_data(results)


import argparse
import logging

import numpy as np
from skimage.morphology import convex_hull_image

from src.utils import load_image, center_of_mass, get_cutout, draw_type
from src.segmentation import SEGMENTATION_TECHNIQUES
from src.form_detection import (
    find_rectangle_candidates,
    find_best_rectangle,
    classify_points,
    get_object_description,
    get_boundaries_and_corners,
)

# uncomment to disable loggint
# logging.basicConfig()
logging.basicConfig(level=logging.INFO)

def main(args):
    logging.info("Started loading image.")
    image = load_image(args.input_file)
    logging.info("Finished loading image.")
    segmentation_function = SEGMENTATION_TECHNIQUES[args.segmentation]
    logging.info("Started image segmentation.")
    segmented_image = segmentation_function(image)
    logging.info("Finished image segmentation.")
    num_segments = np.max(segmented_image)
    print(f"Number of detected jigsaw pieces: {num_segments}.")
    for i in range(1, num_segments + 1):
        logging.info(f"Start processing segment {i}.")
        subimage = segmented_image == i

        cutout = get_cutout(subimage)
        total_space = np.sum(cutout)
        best_space = 0
        space_target = 0.8
        while best_space <= space_target * total_space:
            boundaries, corners = get_boundaries_and_corners(
                cutout, num_candidates=args.corner_candidates
            )
            space_target -= 0.05
            possible_rectangles = find_rectangle_candidates(
                cutout, corners, candidate_limit=10
            )
            best_rectangle = find_best_rectangle(possible_rectangles)
            zeros = np.zeros_like(cutout)
            for p in best_rectangle:
                zeros[p[0], p[1]] = 1
            best_space = np.sum(convex_hull_image(zeros) * cutout)
            logging.info(
                f"Current space is {best_space} which is {best_space / total_space} of total space. "
                f"Required space is {space_target}."
            )
        boundary_points = np.argwhere(boundaries == 1)
        new_boundary_points, classes = classify_points(best_rectangle, boundary_points)
        description = get_object_description(
            best_rectangle,
            new_boundary_points,
            classes,
            center_of_mass(cutout),
            None,
            center_thr=12,
            max_thr=75,
        )
        print(f"Figure_{i}'s code is {description}.")
        center = center_of_mass(subimage)
        logging.info(f"Saving image to output.png")
        draw_type(image, center, description)
        image.save("output.png")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--input-file", type=str, required=True, help="Input image.")
    parser.add_argument(
        "--segmentation", type=str, required=True, help="Segmentation type."
    )
    parser.add_argument(
        "--corner-candidates",
        type=int,
        help="Number of candidate corners for best rectangle " "approximation.",
    )
    args = parser.parse_args()
    main(args)


#对不同的split生成openmatch的文件
import json
def get_q_d():
	for t in ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', \
	'17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31']:
		query_file=open('/home/dihe/Projects/data/QG/queries'+t,'r').readlines()
		doc_file=open('/home/dihe/Projects/data/QG/corpus'+t,'r').readlines()
		w=open('/home/dihe/Projects/data/QG/q_d'+str(t)+'.jsonl','w', encoding="utf-8")

		for line1,line2 in zip(query_file,doc_file):
			temp_data={}
			line1=json.loads(line1)
			line2=json.loads(line2)
			assert line1["pos_doc_id"]==line2['_id']
			temp_data['query']=line1['text']
			temp_data['doc']=line2["text"]
			temp_data['query_id']=line1["_id"]
			temp_data['doc_id']=line1["pos_doc_id"]
			temp_data['retrieval_score']=0

			w.write("{}\n".format(json.dumps(temp_data)))
	w.close()

def get_filter_queries():
	#qd_score={}
	#rewrite queries file
	pass
if __name__ == '__main__':
	get_q_d()


import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np

def PlotTimeSeries(y,offset,**kwargs):
    plt.plot(np.arange(y.size)+offset,y,**kwargs)

def PlotPrediction(x,w,offset,**kwargs):
    yHat = np.dot(x,w)
    PlotTimeSeries(yHat,offset,**kwargs)

def DensityPlot(data,**kwargs):
    from scipy.stats import gaussian_kde
    density = gaussian_kde(data)
    d = np.max(data)-np.min(data)

    xs = np.linspace(np.min(data)-d*0.2,np.max(data)+d*0.2,200)
    density._compute_covariance()
    y = density(xs)
    y /= np.abs(y).max()
    plt.plot(xs,y,**kwargs)

def AddLegends(colors,names):
    patches=[]
    for it in zip(names,colors):
        patches.append(mpatches.Patch(color=it[1], label=it[0]))
    plt.legend(handles=patches)


from __future__ import division

import blockdiag.drawer
from blockdiag.utils import XY, Box

from nwdiag.metrics import DiagramMetrics

class DiagramDraw(blockdiag.drawer.DiagramDraw):
    def create_metrics(self, *args, **kwargs):
        return DiagramMetrics(*args, **kwargs)

    def __init__(self, _format, diagram, filename=None, **kwargs):
        super(DiagramDraw, self).__init__(_format, diagram, filename, **kwargs)
        self.drawer.set_options(jump_forward='vertical',
                                jump_radius=self.metrics.jump_radius,
                                jump_shift=self.metrics.jump_shift)

    @property
    def groups(self):
        return self.diagram.groups

    def pagesize(self, scaled=False):
        return super(DiagramDraw, self).pagesize(scaled).to_integer_point()

    def _draw_background(self):
        super(DiagramDraw, self)._draw_background()
        self.trunklines_shadow()

    def trunklines_shadow(self):
        for network in self.diagram.networks:
            if network.hidden is False and network.color != 'none':
                self.trunkline(network, shadow=True)

    def trunklines(self):
        metrics = self.metrics
        for network in self.diagram.networks:
            if network.hidden is False:
                self.trunkline(network)

                if (self.diagram.external_connector and
                   (network == self.diagram.networks[0])):
                    r = metrics.trunk_diameter // 2

                    pt = metrics.network(network).top
                    pt0 = XY(pt.x, pt.y - metrics.span_height * 2 // 3)
                    pt1 = XY(pt.x, pt.y - r)

                    self.drawer.line([pt0, pt1], fill=network.linecolor)

    def trunkline(self, network, shadow=False):
        metrics = self.metrics
        m = metrics.network(network)
        r = metrics.trunk_diameter // 2

        pt1, pt2 = m.trunkline
        box = Box(pt1.x, pt1.y - r, pt2.x, pt2.y + r)

        if shadow:
            xdiff = self.metrics.shadow_offset.x
            ydiff = self.metrics.shadow_offset.y // 2

            box = Box(pt1.x + xdiff, pt1.y - r + ydiff,
                      pt2.x + xdiff, pt2.y + r + ydiff)

        if self.format == 'SVG':
            from blockdiag.imagedraw.simplesvg import pathdata

            path = pathdata(box[0], box[1])
            path.line(box[2], box[1])
            path.ellarc(r // 2, r, 0, 0, 1, box[2], box[3])
            path.line(box[0], box[3])
            path.ellarc(r // 2, r, 0, 0, 1, box[0], box[1])

            if shadow:
                self.drawer.path(path, fill=self.shadow, filter='blur')
            else:
                self.drawer.path(path, fill=network.color,
                                 outline=network.linecolor)

                path = pathdata(box[2], box[3])
                path.ellarc(r // 2, r, 0, 0, 1, box[2], box[1])
                self.drawer.path(path, fill='none', outline=network.linecolor)

                # for edge jumping
                line = (XY(box[0], box[1]), XY(box[2], box[1]))
                self.drawer.line(line, fill='none', jump=True)
        else:
            lsection = Box(box[0] - r // 2, box[1], box[0] + r // 2, box[3])
            rsection = Box(box[2] - r // 2, box[1], box[2] + r // 2, box[3])

            if shadow:
                color = self.shadow
                _filter = 'blur'
            else:
                color = network.color
                _filter = None

            # fill background
            self.drawer.rectangle(box, outline=color,
                                  fill=color, filter=_filter)
            self.drawer.ellipse(lsection, outline=color,
                                fill=color, filter=_filter)
            self.drawer.ellipse(rsection, outline=color,
                                fill=color, filter=_filter)

            if not shadow:
                upper = (XY(box[0], box[1]), XY(box[2], box[1]))
                self.drawer.line(upper, fill=network.linecolor, jump=True)

                bottom = (XY(box[0], box[3]), XY(box[2], box[3]))
                self.drawer.line(bottom, fill=network.linecolor, jump=True)

                self.drawer.arc(lsection, 90, 270, fill=network.linecolor)
                self.drawer.ellipse(rsection, outline=network.linecolor,
                                    fill=network.color)

    def _draw_elements(self):
        self.trunklines()

        for network in self.diagram.networks:
            self.trunkline_label(network)

        super(DiagramDraw, self)._draw_elements()

    def trunkline_label(self, network):
        if network.display_label:
            m = self.metrics.network(network)
            self.drawer.textarea(m.textbox, network.display_label,
                                 self.metrics.font_for(network),
                                 fill=network.textcolor, halign="right")

    def node(self, node, **kwargs):
        m = self.metrics

        for connector in m.node(node).connectors:
            self.draw_connector(connector)
            if hasattr(connector, 'subject'):
                network = connector.subject.network
            else:
                network = connector.network

            if network in node.address:
                label = node.address[network]
                self.drawer.textarea(connector.textbox, label,
                                     self.metrics.font_for(node),
                                     fill=node.textcolor, halign="left")

        super(DiagramDraw, self).node(node, **kwargs)

    def draw_connector(self, connector):
        if hasattr(connector, 'subject'):
            linecolor = connector.subject.network.linecolor
        else:
            linecolor = connector.network.linecolor
        self.drawer.line(connector.line, fill=linecolor, jump=True)

    def group_label(self, group):
        if group.label:
            m = self.metrics.cell(group)
            self.drawer.textarea(m.grouplabelbox, group.label,
                                 self.metrics.font_for(group),
                                 valign='top', fill=group.textcolor)


import servers
import database
from bot import bot
import uuid

class Match:
    def __init__(self, team1_name, team1_players, team2_name, team2_players, map, location):
        self.team1_name = team1_name
        self.team1_players = team1_players
        self.team2_name = team2_name
        self.team2_players = team2_players
        self.map = map
        self.location = location
        self.id = uuid.uuid1()

def queue_match(match):
    t1p = ""
    for i in range(len(match.team1_players)):
        t1p += str(match.team1_players[i])
        if i < len(match.team1_players) - 1:
            t1p += ", "
    t2p = ""
    for i in range(len(match.team2_players)):
        t2p += str(match.team2_players[i])
        if i < len(match.team2_players) - 1:
            t2p += ", "
    print(t1p)
    print(t2p)
    database.cur.execute("INSERT INTO matchtable (id, team1name, team1players, team2name, team2players, map, location, finished) VALUES ('%s', '%s', '{%s}', '%s', '{%s}', '%s', '%s', false);" % (match.id, match.team1_name, t1p, match.team2_name, t2p, match.map, match.location))
    database.conn.commit()

def start_match(match):
    serverid = servers.find_open_server()
    if serverid == None:
        return None
    #move the server and start it
    scode = servers.edit_server(serverid, {'location':match.location})
    if scode != 200:
        return None
    scode = servers.start_server(serverid)
    if scode != 200:
        return None
    #tell the server to request the match config from CGL
    scode = servers.put_console(serverid, "get5_loadmatch_url \"https://cgl-discord-bot.herokuapp.com/config/%s\"" % match.id)
    if scode != 200:
        return None
    return servers.server_info(serverid)[0]

async def check_matches():
    database.cur.execute("SELECT team1name, team1players, team2name, team2players, map, team1score, team2score FROM matchtable WHERE finished=true;")
    completed_matches = database.cur.fetchall()
    for team1_name, team1_players, team2_name, team2_players, map, team1_score, team2_score in completed_matches:
        print("match completed")
        t1p = *match.team1_players, sep=", "
        t2p = *match.team2_players, sep=", "
        await bot.appinfo.owner.send("%s (%s) - **%s**\nvs\n%s (%s) - **%s**\n%s" % (team1_name, t1p, team1_score, team2_name, t2p, team2_score, map))
    database.cur.execute("DELETE FROM matchtable WHERE finished=true;")
    database.conn.commit()


# import necessary libraries
from flask import Flask, render_template, redirect
from flask_pymongo import PyMongo
# import refugee_news

# create instance of Flask app
app = Flask(__name__)

# Use flask_pymongo to set up mongo connection
app.config["MONGO_URI"] = "mongodb://localhost:27017/refugee_app"
mongo = PyMongo(app)

# Create route that finds documents from Mongo and renders index.html template
@app.route("/")
def index():

    # Find data
    refugeeNews = mongo.db.refugeeNews.items.find_one()

    # return template and data
    return render_template("index.html", refugeeNews = refugeeNews)

# Run app
if __name__ == "__main__":
    app.run(debug=True)


## camera : 0 phone :2
import numpy as np
import glob
## 1 -> 2

def process_file(input_filename, output_filename):
    lines_to_keep = []
    with open(input_filename, 'r') as file:
        for line in file:
            class_label, *values = line.split()
            class_label = int(class_label)

            # 클래스가 0인 경우 또는 클래스가 2인 경우를 1로 변경하여 lines_to_keep 리스트에 추가
            if class_label == 0:
                lines_to_keep.append('0 ' + ' '.join(values) + '\n')
            elif class_label == 1:
                lines_to_keep.append('2 ' + ' '.join(values) + '\n')
            # 클래스가 1 또는 3인 경우에는 건너뜀
            # elif class_label == 2 or class_label == 3:
                # continue
            else :
                print('what?')

    # 수정된 내용을 새로운 파일로 저장
    with open(output_filename, 'w') as file:
        file.writelines(lines_to_keep)

# 여러 파일에 대해 작업을 반복하려면 파일 이름을 반복문으로 지정
# 예를 들어, 파일명이 data1.txt, data2.txt, data3.txt인 경우:
# file_names = ['data1.txt', 'data2.txt', 'data3.txt']
file_names = glob.glob('./data/save_img_copy/**/**.txt',recursive=True)
print(len(file_names))

for input_file in file_names:
    output_file = input_file  # 수정된 파일명을 동일하게 유지하려면 input_file과 동일하게 설정
    process_file(input_file, output_file)


#!/usr/bin/python3
import RPi.GPIO as GPIO
from time import *
from signal import *

mypin_out_38  = 38
#编码器信号1
mypin_in_36 = 36
#编码器信号2
mypin_in_32 = 32

GPIO.setwarnings(False)

GPIO.setmode(GPIO.BOARD)
GPIO.setup(mypin_out_38,GPIO.OUT)
GPIO.setup(mypin_in_36, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(mypin_in_32, GPIO.IN, pull_up_down=GPIO.PUD_UP)

index = 0
# 清除树莓派引脚状态赋值
def destroy():
    GPIO.cleanup()  # 释放数据

def my_callback(channel):
    global  index
    index = index +1
    print('检测到低电平，端口是: %s'%channel,index)
    if GPIO.input(mypin_in_32) == GPIO.HIGH:
        print('电机正转~~~~~~~~~~~~~~~~~~')
    else:
        print('电机逆转*******')

try:
    GPIO.add_event_detect(mypin_in_36, GPIO.FALLING, callback=my_callback)

    while True:
        GPIO.output(mypin_out_38, GPIO.HIGH)
        sleep(1)
        GPIO.output(mypin_out_38, GPIO.LOW)
        sleep(1)

    pause()

except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child function destroy() will be  executed.
    destroy()


# -*- coding: utf-8 -*-
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response, redirect
from django.core.urlresolvers import reverse
from django.core.mail import send_mail, EmailMultiAlternatives
from django.conf import settings
from django.contrib import messages
from django.template import RequestContext
from django.template.loader import render_to_string
from django.db import transaction

from common.fields import emails_list
from cart import Cart
from cart.models import Order
from cart.forms import OrderForm

def show_cart(request):
    return render_to_response('cart.html', {},
        context_instance=RequestContext(request))

@transaction.commit_on_success
def order_cart(request):
    if request.method == 'POST':
        form = OrderForm(request.POST)
        if form.is_valid():
            subject = u'Заказ с сайта'
            recipients = []
            try:
                recipients.extend(emails_list(request.settings.email))
            except:
                recipients.append(settings.ADMINS[0][1])
            cart = Cart(request, init=True)
            # Order insertion
            if cart.objects:
                order = Order.objects.create(fio=form.cleaned_data['fio'],
                                             address='',
                                             phones=form.cleaned_data['phones'],
                                             email=form.cleaned_data['email'],
                                             comment=form.cleaned_data['comment'])
                for item in cart.objects:
                    order.items.create(label=item.label,
                                       amount=item.amount,
                                       price=item.price)
            subject = u'Поступил заказ с сайта %s' % settings.PROJECT_TITLE
            letter_context = form.cleaned_data
            letter_context.update({'site': settings.PROJECT_TITLE,
                                   'cart': cart, 'subject': subject})

            text_content = render_to_string('letters/order_text.txt', letter_context)
            html_content = render_to_string('letters/order_html.html', letter_context)

            msg = EmailMultiAlternatives(subject, text_content,
                recipients[0], recipients)
            msg.attach_alternative(html_content, "text/html")
            msg.send()
            cart.delete_all()
            success_message = request.infoblock.get('order_send_message', u"""
            Благодарим за то, что Вы воспользовались услугами нашего Интернет-магазина.
            Ваша заявка принята в обработку, и наш менеджер свяжется с Вами в ближайшее время для уточнения деталей.
            """)
            messages.add_message(request, messages.SUCCESS, success_message)
            return redirect(reverse('message_list'))
    else:
        form = OrderForm()
    return render_to_response('cart_order.html', {'form': form},
        context_instance=RequestContext(request))

def add_cart(request):
    cart = Cart(request)
    product = int(request.POST.get('product', 0))
    if product > 0:
        request = cart.add(product)
    return HttpResponse(cart.serialize())

def update_cart(request):
    cart = Cart(request)
    product = int(request.POST.get('product', '0'))
    amount = int(request.POST.get('amount', '0'))
    i = int(request.POST.get('i', '0'))
    data = None

    if product > 0 and amount >= 0:
        if amount == 0:
            cart.delete(product)
        else:
            cart.update({'product': product, 'amount': amount})
        return HttpResponse(cart.serialize())
    else:
        raise Http404

def delete_cart(request, id):
    cart = Cart(request)
    cart.delete(id)
    if request.META.get('HTTP_X_REQUESTED_WITH', '') == 'XMLHttpRequest':
        return HttpResponse(cart.serialize())
    else:
        return redirect(reverse('show_cart'))

def empty_cart(request):
    cart = Cart(request)
    cart.delete_all()
    return redirect('/')


def KSA(key):
    key_length = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def PRGA(S, n):
    i = 0
    j = 0
    key = []
    while n>0:
        n = n-1
        i = (i+1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i]+S[j]) % 256]
        key.append(K)
    return key

key=str(input("INTRODUZCA LA CLAVE: "))
plaintext = str(input("INTRODUZCA EL MENSAJE: "))

def preparing_key_array(s):
    return [ord(c) for c in s]

key = preparing_key_array(key)

import numpy as np
S = KSA(key)

keystream = np.array(PRGA(S, len(plaintext)))
print("EL MENSAJE CIFRADO ES: ")
print(keystream)


import sys
from cactus.utils.filesystem import fileList
try:
    import sass
except:
    sys.exit("Could not find libsass, please install: sudo easy_install libsass")

CSS_PATH = 'static/css'

def postBuild(site):
    for path in fileList(CSS_PATH):
        if not path.endswith('.scss'):
            continue

        with open(path, 'rw') as f:
            data = f.read()

        css, map = sass.compile(filename=path,
                                source_map_filename=path.replace('.scss', '.css') + ".map",
        )
        with open(path.replace('.scss', '.css'), 'w') as f:
            f.write(css)
        with open(path.replace('.scss', '.css') + ".map", "wb") as mapsock:
            mapsock.write(map.encode('utf8'))


import sys
from os import path
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget, \
    QPushButton, QCheckBox, QGridLayout, QDesktopWidget, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import pyqtSlot

class ResultsTable(QMainWindow):
    def __init__(self, model):
        super().__init__()
        self.model = model
        self.initUI()

    def initUI(self):
        self.setMinimumSize(500,500)
        self.tableview = QTableView(self)
        self.tableview.setModel(self.model)
        self.tableview.setFixedSize(400, 400)
        self.setWindowTitle('Lexical Results')
        self.center_window()

        # size policy
        self.tableview.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
        #tableview.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) # ---
        self.tableview.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)# ++

    def center_window(self):

        # centering window
        qtRectangle = self.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())

class TableView(QAbstractTableModel):

    def __init__(self, tokens, parent = None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        #super().__init__()

        self.__rows = []
        self.__headers = ['Token', 'Type']
        for token_obj in tokens:
            self.__rows.append((token_obj.value(), token_obj.type()))

    def init_ui(self):
        pass
    def rowCount(self, parent):
        return len(self.__rows)

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

    def data(self, index, role):
        if role != Qt.DisplayRole:
            return QVariant()
        # What's the value of the cell at the given index?
        return self.__rows[index.row()][index.column()]

    def headerData(self, section, orientation, role):
        if role != Qt.DisplayRole or orientation != Qt.Horizontal:
            return QVariant()
        return self.__headers[section]

'''
if __name__ == '__main__':
    app = QApplication([])
    model = ResultsTable()
    view = QTableView()
    view.setModel(model)
    view.setFixedSize(500, 500)
    view.show()
    app.exec_()
'''


import airsim
import time
import airsim.utils
import numpy as np
from record_data import camera_car_data as camera
client = airsim.CarClient()
client.confirmConnection()
client.enableApiControl(True)
controls = airsim.CarControls()
controls.handbrake = True
client.setCarControls(controls)
print(client.isApiControlEnabled())
object_name = 'ChessBoard'
if len(client.simListSceneObjects(object_name)) != 0:
    print("object is find ")
else:
    raise IOError("object not find")

print(client.simListSceneObjects('ChessBoard'))
chess_board_pos = client.simGetObjectPose(object_name).position
# Ось напралвена вниз!!!!
print("camera\n", client.simGetCameraInfo("front_center").pose)
print("chess_board_pos_init", chess_board_pos)
t = 0
pose_o = airsim.Pose()
pose_o_c = airsim.Pose()
pose_o.position = chess_board_pos
pose_o_c.position = pose_o.position
dx = 0.1  # ->1 см
dy = 0.1  # ->1 см
dz = 0.1  # ->5 см

ddx = [0*i * dx for i in range(-2, 20, 1)]
ddy = [i * dy for i in range(-15, 0, 1)]
ddz = [i * dz for i in range(-6,-2, 1)]
X = np.array([pose_o.position.x_val]*5)#[pose_o.position.x_val for i in range(-15, 0, 1)]
Y = pose_o.position.y_val +np.random.random(5)#[pose_o.position.y_val+ i * dy for i in range(-15, 0, 1)]
Z = pose_o.position.z_val -np.random.random(5)#[pose_o.position.z_val + i * dz for i in range(-15,0, 1)]
# client.startRecording()
cameraTest = camera.CameraRecord(client)
client.simGetLidarSegmentation()
cameraTest.start_recording()
for z in Z:
    for y in Y:
        # pose_o.position.x_val
        pose_o.position.y_val = y
        client.simSetObjectPose(object_name, pose_o)
        cameraTest.record(airsim.ImageType.Scene)
        time.sleep(1)
    pose_o.position.y_val = 0
    pose_o.position.z_val = z
print("Конец цикла")
#client.stopRecording()
print("Запись данных в файл")
f = open(f"{object_name}_pos.dat", 'w')
f.write(f"1\n")
for z in Z:
    for y in Y:
        f.write(f"{X[0]} {y} {z}\n")
f.close()


CLASSES = {
    "male_soldier": {
        "health": 100,
        "energy_gain": 50,
        "energy_spend": 26,
        "healing": 22,
        "shots_num": 2,
        "shot_snd": "rifle_shot1",
    },
    "female_soldier": {
        "health": 80,
        "energy_gain": 40,
        "energy_spend": 26,
        "healing": 22,
        "shots_num": 2,
        "shot_snd": "rifle_shot1",
    },
    "male_raider": {
        "health": 100,
        "energy_gain": 35,
        "energy_spend": 20,
        "healing": 14,
        "shots_num": 1,
        "shot_snd": "shotgun_shot1",
    },
    "female_raider": {
        "health": 80,
        "energy_gain": 30,
        "energy_spend": 20,
        "healing": 14,
        "shots_num": 1,
        "shot_snd": "shotgun_shot1",
    },
    "male_anarchist": {
        "health": 100,
        "energy_gain": 50,
        "energy_spend": 26,
        "healing": 22,
        "shots_num": 3,
        "shot_snd": "pistol_shot1",
    },
    "female_anarchist": {
        "health": 80,
        "energy_gain": 40,
        "energy_spend": 26,
        "healing": 22,
        "shots_num": 2,
        "shot_snd": "pistol_shot1",
    },
}

NAMES = {
    "male": (
        "Aaron",
        "Adam",
        "Aidan",
        "Alex",
        "Alexis",
        "Antony",
        "Armin",
        "Arnold",
        "Ben",
        "Bruce",
        "Chris",
        "Clint",
        "Cody",
        "Cory",
        "Craig",
        "Daren",
        "Donnie",
        "Ed",
        "Elijah",
        "Eric",
        "Felix",
        "Finn",
        "Frank",
        "Hans",
        "James",
        "Jerome",
        "Jordan",
        "Josh",
        "Justin",
        "Kenneth",
        "Lewis",
        "Mathew",
        "Max",
        "Mike",
        "Nathan",
        "Neill",
        "Paul",
        "Peter",
        "Philip",
        "Roy",
        "Shawn",
        "Sid",
        "Steven",
        "Tim",
        "Thomas",
        "Travis",
        "Tyler",
        "Werner",
    ),
    "female": (
        "Adriana",
        "Andrea",
        "Angelina",
        "Ann",
        "Barbara",
        "Casey",
        "Charlotte",
        "Christine",
        "Claudia",
        "Clara",
        "Cobie",
        "Dolores",
        "Elizabeth",
        "Emily",
        "Emma",
        "Eva",
        "Gillian",
        "Helena",
        "Isabela",
        "Jennifer",
        "Jessica",
        "Julia",
        "Karen",
        "Kate",
        "Laura",
        "Lea",
        "Luisa",
        "Maeve",
        "Megan",
        "Melissa",
        "Mia",
        "Miranda",
        "Monica",
        "Naomi",
        "Nele",
        "Olivia",
        "Paula",
        "Rachael",
        "Radha",
        "Rhonda",
        "Samara",
        "Sara",
        "Scarlett",
        "Sofia",
        "Stephanie",
        "Teresa",
        "Vanessa",
        "Victoria",
    ),
}


from search import *
from heapq import *
import re, math, time

class RoutingGraph(Graph):
    """This is a graph that represents a map used for calculating routing"""
    def __init__(self, map_str):
        """Initialises an explicit graph.
        Keyword arguments:
        map_str -- the textual representation of the map
        """
        self.routing_map, self.goal_node, self.agents = self.process_text_map(map_str)

    def process_text_map(self, map_str):
        """Processes the text representation of a graph into a workable data
           structure"""
        goal_node = (None, None)
        agents = []

        map_list = []
        map_str.strip()
        map_list_temp = map_str.splitlines()

        for row, line in enumerate(map_list_temp):
            newLine = []
            line = line.strip()

            for col, char in enumerate(line):
                if char == "G":
                    goal_node = (row, col)
                elif char == "S":
                    agents.append((row, col, math.inf))
                elif char.isdigit():
                    agents.append((row, col, int(char)))
                elif char in ["+", "-", "|"]:
                    char = "X"
                newLine.append(char)
            map_list.append(newLine)

        return map_list, goal_node, agents

    def is_goal(self, node):
        """Returns true if the given node is a goal state, false otherwise."""
        row, col, _ = node
        return (row, col) == self.goal_node

    def starting_nodes(self):
        """Returns a sequence of starting nodes. Often there is only one
        starting node but even then the function returns a sequence
        with one element. It can be implemented as an iterator if
        needed."""
        #for starting_node in self.starting_nodes:
        #    yield starting_node
        return self.agents

    def outgoing_arcs(self, tail_node):
        """Given a node it returns a sequence of arcs (Arc objects)
        which correspond to the actions that can be taken in that
        state (node)."""
        directions = [('N' , -1, 0), ('NE', -1, 1), ('E' ,  0, 1), ('SE',  1, 1),
                      ('S' ,  1, 0), ('SW',  1, -1), ('W' ,  0, -1), ('NW', -1, -1)]
        row, col, fuel = tail_node
        fuel_stations = []
        if fuel > 0:
            for label, move_x, move_y in directions:
                new_row = row + move_x
                new_col = col + move_y
                new_pos = self.routing_map[new_row][new_col]
                if new_pos != "X": #If it is not an obstacle
                    head = (new_row, new_col, fuel - 1)
                    cost = 2 #For readability
                    yield Arc(tail_node, head, label, cost)

        if self.routing_map[row][col] == "F" and fuel < 9:
            head = (row, col, 9)
            cost = 5
            yield Arc(tail_node, head, "Fuel up", cost)

    def estimated_cost_to_goal(self, node):
        """Return the estimated cost to a goal node from the given
        state. This function is usually implemented when there is a
        single goal state. The function is used as a heuristic in
        search. The implementation should make sure that the heuristic
        meets the required criteria for heuristics."""
        return 0

class AStarFrontier(Frontier):
    """Implements a frontier appropriate for A* search."""

    def __init__(self, routing_graph):
        """The constructor takes no argument. It initialises the
        container to an empty list."""
        self.container = []
        self.item_count = 0
        self.routing_graph = routing_graph

    def add(self, path):
        """Adds a new path to the frontier. A path is a sequence (tuple) of
        Arc objects. You should override this method.
        """
        cost = 0
        for arc in path:
            cost += arc[-1]
        est_cost = self.routing_graph.estimated_cost_to_goal(path[-1][0])
        total_cost = cost + est_cost
        to_add = (total_cost, self.item_count, path)
        self.item_count += 1
        heappush(self.container, to_add)

    def __iter__(self):
        """Returns a generator. The generator selects and removes a path from
        the frontier and returns it. A path is a sequence (tuple) of
        Arc objects. Override this method according to the desired
        search strategy.
        """
        while self.container:
            yield heappop(self.container)[-1]

def main():
    print("Test 1:")
    map_str = """\
    +-------+
    |   G   |
    |       |
    |   S   |
    +-------+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)

    print("\n\nTest 2:")
    map_str = """\
    +-------+
    |     XG|
    |X XXX  |
    | S     |
    +-------+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)

    print("\n\nTest 3:")
    map_str = """\
    +-------+
    |  F  XG|
    |X XXXX |
    | 2     |
    +-------+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)

    print("\n\nTest 4:")
    map_str = """\
    +--+
    |GS|
    +--+
    """
    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)

    print("\n\nTest 5:")
    map_str = """\
    +---+
    |GF2|
    +---+
    """
    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)

    print("\n\nTest 6:")
    start_time = time.time()
    map_str = """\
    +----+
    | S  |
    | SX |
    | X G|
    +----+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)
    print("--- %s seconds ---" % (time.time() - start_time))

    print("\n\nTest 7:")
    start_time = time.time()
    map_str = """\
    +---------+
    |         |
    |    G    |
    |         |
    +---------+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)
    print("--- %s seconds ---" % (time.time() - start_time))

    print("\n\nTest 8")
    start_time = time.time()
    map_str = """\
    +----------+
    |    X     |
    | S  X  G  |
    |    X     |
    +----------+
    """

    map_graph = RoutingGraph(map_str)
    frontier = AStarFrontier(map_graph)
    solution = next(generic_search(map_graph, frontier), None)
    print_actions(solution)
    print("--- %s seconds ---" % (time.time() - start_time))

if __name__ == "__main__":
    main()

Superquiz (A* routing)/Question 2 saved.py


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

print('\n\n')
print(' ---------------- START ---------------- \n')

#-------------------------------- API-FOOTBALL --------------------------------

import pickle
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import KFold
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn.neighbors import KNeighborsRegressor
import lime
import lime.lime_tabular
import warnings
from sklearn.metrics import mean_squared_error
from sklearn.metrics import make_scorer
from sklearn.model_selection import cross_val_score, cross_val_predict

from sklearn.model_selection import StratifiedKFold

plt.close('all')

#------------------------------- ML MODEL BUILD -------------------------------

with open('2019_prem_generated_clean/2019_prem_df_for_ml_5.txt', 'rb') as myFile:
    df_ml_5 = pickle.load(myFile)

with open('2019_prem_generated_clean/2019_prem_df_for_ml_10.txt', 'rb') as myFile:
    df_ml_10 = pickle.load(myFile)

#----------------------------- ML MODEL EVALUATION ----------------------------

#this section contains a series of functions which may be used for model evaluation

def pred_proba_plot(clf, x, y, cv=4, no_iter=5, no_bins=25, x_min=0.5, x_max=1, output_progress=True, classifier=''):
    y_pred_values = []

    for i in range(no_iter):
        if output_progress:
            if i % 2 == 0:
                print(f'completed {i} iterations')
        kf = KFold(n_splits=cv, shuffle=False)
        y_pred_iter = []  # Store predictions for each iteration separately
        for train_index, test_index in kf.split(x, y):
            x_train, x_test = x.iloc[train_index], x.iloc[test_index]
            y_train, y_test = y.iloc[train_index], y.iloc[test_index]
            clf.fit(x_train, y_train)
            y_pred_cv = clf.predict(x_test)
            y_pred_iter.extend(y_pred_cv)  # Store predictions for each fold
        y_pred_values.append(y_pred_iter)  # Store predictions for each iteration

    # Flatten the list of lists
    y_pred_values_flat = [item for sublist in y_pred_values for item in sublist]

    bins = np.linspace(x_min, x_max, no_bins)
    fig, ax = plt.subplots()
    ax.hist(y_pred_values_flat, bins, alpha=0.5, edgecolor='#1E212A', color='blue', label='Predicted Values')
    ax.legend()
    fig.suptitle(f'{classifier} - Iterated {no_iter} Times', y=0.96, fontsize=16, fontweight='bold')
    ax.set(ylabel='Number of Occurrences',
           xlabel='Predicted Values')
    return fig

def lime_explanation(clf, x, y, index, feature_names):
    explainer = lime.lime_tabular.LimeTabularExplainer(x.values,
                                                      mode='regression',
                                                      feature_names=[str(name) for name in feature_names],
                                                      class_names=['Team AV Corneres'],
                                                      discretize_continuous=True)
    exp = explainer.explain_instance(x.iloc[index].values, clf.predict, num_features=len(feature_names))
    exp.show_in_notebook(show_table=True)

def plot_residuals(clf, x_test, y_test):
    residuals = y_test - clf.predict(x_test)
    plt.scatter(x_test.index, residuals)
    plt.axhline(y=0, color='r', linestyle='--')
    plt.title('Residual Plot')
    plt.xlabel('Data Points')
    plt.ylabel('Residuals')
    plt.show()

def plot_feature_importance(clf, feature_names):
    feature_importance = clf.feature_importances_
    sorted_idx = feature_importance.argsort()[::-1]
    plt.bar(range(len(feature_importance)), feature_importance[sorted_idx], align="center")
    plt.xticks(range(len(feature_importance)), feature_names[sorted_idx], rotation='vertical')
    plt.title('Feature Importance')
    plt.show()

def plot_results(clf, x_train, y_train, x_test, y_test):
    # Plotting for training data
    plt.figure(figsize=(12, 6))
    plt.subplot(1, 2, 1)
    plt.scatter(x_train.index, y_train, label='Actual')
    plt.scatter(x_train.index, clf.predict(x_train), label='Predicted')
    plt.title('Training Data: Actual vs Predicted')
    plt.xlabel('Data Points')
    plt.ylabel('Target Variable')
    plt.legend()

    # Plotting for test data
    plt.subplot(1, 2, 2)
    plt.scatter(x_test.index, y_test, label='Actual')
    plt.scatter(x_test.index, clf.predict(x_test), label='Predicted')
    plt.title('Test Data: Actual vs Predicted')
    plt.xlabel('Data Points')
    plt.ylabel('Target Variable')
    plt.legend()

    plt.tight_layout()
    plt.show()

    return clf

# Define a custom scorer function
def custom_scorer(estimator, X, y):
    feature_names = X.columns if hasattr(X, 'columns') else None
    return estimator.score(X, y), feature_names

x_10 = df_ml_10.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
y_10 = df_ml_10['Team Av Corners'].astype(float)

x_5 = df_ml_5.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
y_5 = df_ml_5['Team Av Corners'].astype(float)

print('\nRANDOM FOREST\n')
#------------------------------- RANDOM FOREST --------------------------------

def rand_forest_train(df):
    # create features matrix
    x = df.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
    y = df['Team Av Corners'].astype(float)  # Use float for continuous target variable

    # instantiate the random forest regressor
    clf = RandomForestRegressor()  # Use RandomForestRegressor

    # split into training data and test data
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
    warnings.filterwarnings('ignore')
    # train the model
    clf.fit(x_train, y_train)
    y_pred = clf.predict(x_test)
    mse = mean_squared_error(y_test, y_pred) ** 0.5
    print(mse)
    # training data
    train_data_score = round(clf.score(x_train, y_train) * 100, 1)
    print(f'Training data score = {train_data_score}%')

    # test data
    test_data_score = round(clf.score(x_test, y_test) * 100, 1)
    print(f'Test data score = {test_data_score}% \n')
    explainer = lime.lime_tabular.LimeTabularExplainer(x_train.values, feature_names=x_train.columns.values.tolist(),
                                                       class_names=['Team Av Corners'], verbose=True, mode='regression')
    j = 10
    exp = explainer.explain_instance(x_test.values[j], clf.predict, num_features=3)
    exp.show_in_notebook(show_table=True)
    exp.save_to_file("LIME.html")
    return clf
print("10 games avg")
ml_10_rand_forest = rand_forest_train(df_ml_10)
print("5 games avg")
ml_5_rand_forest = rand_forest_train(df_ml_5)
original_feature_names = x_10.columns.tolist()

with open('ml_model_build_random_forest/ml_models/random_forest_model_5.pk1', 'wb') as myFile:
    pickle.dump(ml_5_rand_forest, myFile)

with open('ml_model_build_random_forest/ml_models/random_forest_model_10.pk1', 'wb') as myFile:
    pickle.dump(ml_10_rand_forest, myFile)

ml_10_rand_forest = rand_forest_train(df_ml_10)
ml_5_rand_forest = rand_forest_train(df_ml_5)

# Extracting x_train, y_train, x_test, y_test from the dataframes
x_train_10, x_test_10, y_train_10, y_test_10 = train_test_split(df_ml_10.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1), df_ml_10['Team Av Corners'].astype(float), test_size=0.2)
x_train_5, x_test_5, y_train_5, y_test_5 = train_test_split(df_ml_5.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1), df_ml_5['Team Av Corners'].astype(float), test_size=0.2)

plot_results(ml_10_rand_forest, x_train_10, y_train_10, x_test_10, y_test_10)
plot_results(ml_5_rand_forest, x_train_5, y_train_5, x_test_5, y_test_5)

plot_residuals(ml_10_rand_forest, x_test_10, y_test_10)
plot_residuals(ml_5_rand_forest, x_test_5, y_test_5)

feature_names = x_10.columns  # Assuming x is your feature matrix
plot_feature_importance(ml_10_rand_forest, feature_names)
plot_feature_importance(ml_5_rand_forest, feature_names)

#---------- MODEL EVALUATION ----------

# Use the custom scorer in cross-validation
cv_score_av_10, feature_names_10 = custom_scorer(ml_10_rand_forest, x_10, y_10)
print('Cross-Validation Accuracy Score ML10: ', round(cv_score_av_10 * 100, 1), '%\n')

cv_score_av_5, feature_names_5 = custom_scorer(ml_5_rand_forest, x_5, y_5)
print('Cross-Validation Accuracy Score ML5: ', round(cv_score_av_5 * 100, 1), '%\n')

#prediction probability plots
#fig = pred_proba_plot(ml_10_rand_forest, x_10, y_10, no_iter=50, no_bins=36, x_min=0.3, classifier='Random Forest (ml_10)')
#fig.savefig('figures/random_forest_pred_values_ml10_50iter.png')

#fig = pred_proba_plot(ml_5_rand_forest, x_5, y_5, no_iter=50, no_bins=35, x_min=0.3, classifier='Random Forest (ml_5)')
#fig.savefig('figures/random_forest_pred_values_ml5_50iter.png')

# --------------- TESTING C PARAM ---------------

expo_iter = np.square(np.arange(0.1, 10, 0.1))

def testing_c_parms(df, iterable):
    training_score_li = []
    test_score_li = []
    for c in iterable:
        x = df.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
        y = df['Team Av Corners'].astype(int)
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1)
        clf = svm.SVC(kernel='rbf', C=c)
        clf.fit(x_train, y_train)
        train_data_score = round(clf.score(x_train, y_train) * 100, 1)
        test_data_score = round(clf.score(x_test, y_test) * 100, 1)
        training_score_li.append(train_data_score)
        test_score_li.append(test_data_score)
    return training_score_li, test_score_li

training_score_li, test_score_li = testing_c_parms(df_ml_10, expo_iter)

# from the plot below we can see that a c of around 3 is likely to be more optimal than 1
fig, ax = plt.subplots()
ax.plot(expo_iter, test_score_li)

print('\nSUPPORT VECTOR MACHINES\n')
#--------------------------- SUPPORT VECTOR MACHINE ---------------------------

from sklearn.preprocessing import LabelEncoder

def svm_train(df):

    # create features matrix
    x = df.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)

    le = LabelEncoder()
    df['Team Av Corners'] = le.fit_transform(df['Team Av Corners'])  # Encode categorical labels to numerical

    # Target variable for classification
    y = df['Team Av Corners']

    # split into training data and test data
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

    # Testing C Parameters
    expo_iter = np.square(np.arange(0.1, 10, 0.1))
    training_score_li, test_score_li = testing_c_parms(df, expo_iter)

    # Identify the optimal C value (you can customize this logic)
    optimal_c_value = expo_iter[np.argmax(test_score_li)]

    # Instantiate the SVM class with the optimal C value
    clf = svm.SVR(C=optimal_c_value)

    # train the model
    clf.fit(x_train, y_train)

    # training data
    train_data_score = round(clf.score(x_train, y_train) * 100, 1)
    print(f'Training data score = {train_data_score}%')

    # test data
    test_data_score = round(clf.score(x_test, y_test) * 100, 1)
    print(f'Test data score = {test_data_score}% \n')

    return clf

ml_10_svm = svm_train(df_ml_10)
ml_5_svm = svm_train(df_ml_5)

with open('ml_model_build_support_vector_machine/ml_models/svm_model_5.pk1', 'wb') as myFile:
    pickle.dump(ml_5_svm, myFile)

with open('ml_model_build_support_vector_machine/ml_models/svm_model_10.pk1', 'wb') as myFile:
    pickle.dump(ml_10_svm, myFile)

#---------- MODEL EVALUATION ----------

#cross validation
cv_score_av = round(np.mean(cross_val_score(ml_10_svm, x_10, y_10, cv=5))*100,1)
print('Cross-Validation Accuracy Score ML10: ', cv_score_av, '%\n')

cv_score_av = round(np.mean(cross_val_score(ml_5_svm, x_5, y_5, cv=5))*100,1)
print('Cross-Validation Accuracy Score ML5: ', cv_score_av, '%\n')

#prediction probability plots
#fig = pred_proba_plot(ml_10_svm, x_10, y_10, no_iter=50, no_bins=35, x_min=0.3, classifier='Support Vector Machine (ml_10)')
#fig.savefig('figures/svm_pred_proba_ml10_50iter.png')

#fig = pred_proba_plot(ml_5_svm, x_5, y_5, no_iter=50, no_bins=35, x_min=0.3, classifier='Support Vector Machine (ml_5)')
#fig.savefig('figures/svm_pred_proba_ml5_50iter.png')

print('\nK NEAREST NEIGHBORS\n')
#----------------------------- K NEAREST NEIGHBORS ----------------------------

def k_nearest_neighbor_train(df):
    # create features matrix
    x = df.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
    y = df['Team Av Corners'].astype(int)

    # split into training data and test data
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

    # instantiate the K Nearest Neighbor class
    clf = KNeighborsRegressor(n_neighbors=28)

    # train the model
    clf.fit(x_train, y_train)

    # training data
    train_data_score = round(clf.score(x_train, y_train) * 100, 1)
    print(f'Training data score = {train_data_score}%')

    # test data
    test_data_score = round(clf.score(x_test, y_test) * 100, 1)
    print(f'Test data score = {test_data_score}% \n')

    return clf

ml_10_knn = k_nearest_neighbor_train(df_ml_10)
ml_5_knn = k_nearest_neighbor_train(df_ml_5)

with open('ml_model_build_nearest_neighbor/ml_models/knn_model_5.pk1', 'wb') as myFile:
    pickle.dump(ml_5_knn, myFile)

with open('ml_model_build_nearest_neighbor/ml_models/knn_model_10.pk1', 'wb') as myFile:
    pickle.dump(ml_10_knn, myFile)

# --------------- TESTING N_NEIGHBORS PARAM ---------------

df = df_ml_10
x = df.drop(['Target Fixture ID', 'Team Av Corners', 'Opponent Av Corners'], axis=1)
y = df['Team Av Corners'].astype(int)
#split into training data and test data
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

test_accuracy = []
for n in range(1, 50, 1):
    clf = KNeighborsRegressor(n_neighbors=n)
    clf.fit(x_train, y_train)
    test_accuracy.append(round(clf.score(x_test, y_test) * 100, 1))

fig, ax = plt.subplots()
ax.plot(range(1, 50, 1), test_accuracy)

#---------- MODEL EVALUATION ----------

#cross validation
kf = KFold(n_splits=4, shuffle=True, random_state=42)

cv_score_av = round(np.mean(cross_val_score(ml_10_knn, x_10, y_10, cv=kf))*100,1)
print('Cross-Validation Accuracy Score ML10: ', cv_score_av, '%\n')

cv_score_av = round(np.mean(cross_val_score(ml_5_knn, x_5, y_5, cv=kf))*100,1)
print('Cross-Validation Accuracy Score ML5: ', cv_score_av, '%\n')

#prediction probability plots
#fig = pred_proba_plot(ml_10_knn, x_10, y_10, no_iter=50, no_bins=18, x_min=0.3, classifier='Nearest Neighbor (ml_10)')
#fig.savefig('figures/knn_pred_proba_ml10_50iter.png')

#fig = pred_proba_plot(ml_5_knn, x_5, y_5, no_iter=50, no_bins=18, x_min=0.3, classifier='Nearest Neighbor (ml_5)')
#fig.savefig('figures/knn_pred_proba_ml5_50iter.png')

# ----------------------------------- END -------------------------------------

print(' ----------------- END ----------------- ')
print('\n')


import socket
import sys

class ISKServer():
    def __init__(self, size=1024, address = 'localhost', source_address = 81):
        self.size = size
        self.address = address
        self.source_address = source_address

    def main_loop(self):
        # Set up a TCP/IP server
        tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Bind the socket to server address and port 81
        server_address = (self.address, self.source_address)
        tcp_socket.bind(server_address)

        # Listen on port 81
        tcp_socket.listen(1)

        while True:
            print("Waiting for connection")
            connection, client = tcp_socket.accept()

            try:
                print("Connected to client IP: {}".format(client))

                # Receive and print data 32 bytes at a time, as long as the client is sending something
                while True:
                    data = connection.recv(self.size).decode()
                    print("Data: "+data)

                    if not data:
                        break

            finally:
                connection.close()

if __name__ == '__main__':
    address = input("Enter address: ")
    port = int(input("Enter port: "))
    size = int(input("Enter expected size of messages: "))

    server = ISKServer(size, address, port)
    server.main_loop()


"""
This module defines Pydantic models for representing reservation-related data.
"""

from __future__ import annotations

from typing import Optional
from datetime import datetime

from pydantic import BaseModel as PydanticBase, Field, Extra, root_validator
from fastapi import Query
from sqlalchemy import select, and_, func

from ...util import format_description_with_example
from ...db.manager import SessionFacade
from ...db.models import Reservation as ReservationModel, \
    Restaurant as RestaurantModel, \
    Table as TableModel, \
    BusinessHour as BusinessHourModel

session = SessionFacade()

# Messages explaining the error-codes returned by the validation-methods.
# Used by the /reservations/validate-endpoints in restaurants and tables.
VALIDATION_ERRORS_RESTAURANT = {
    -1: "Reservation is not inside open-to-close timeframe and/or the restaurant does not accept "
        "reservations at the desired time.",
    -2: "There is no table with enough space for all the guests of the given reservation and/or other potential "
        "tables can only be reserved with more guests.",
    -3: "Reservation conflicts with other existing reservations on all potential tables."
}
VALIDATION_ERRORS_TABLE = {
    -1: "Reservation timeframe not inside open-to-close timeframe and/or the restaurant does not accept "
        "reservations at the desired time.",
    -2: "Not enough space for all the guests of the given reservation on the given table and/or the given table "
        "can only be reserved with more guests.",
    -3: "Reservation conflicts with other existing reservations on the table."
}

class ReservationNew(PydanticBase):
    """
    Represents a new reservation to be created.
    """
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int = Field(gt=0)
    customer_phone: Optional[str]
    additional_information: Optional[str]

    @root_validator(skip_on_failure=True)
    def validate_times(cls, values):
        if values['reserved_from'] > values['reserved_until']:
            raise ValueError("Reservation can't end before it has started.")
        return values

    class Config:
        schema_extra = {
            "example": {
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!"
            }
        }
        extra = Extra.forbid

    def cast_to_model(self, table_id: int) -> ReservationModel:
        """
        Convert a new reservation object to a SQLAlchemy model object.

        Args:
            table_id (int): The ID of the table to which the new reservation belongs.

        Returns:
            ReservationModel: The SQLAlchemy model object created from the new reservation object.
        """
        reservation_model = ReservationModel(
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information,
            table_id=table_id
        )
        return reservation_model

    def cast_to_put(self, reservation_id: int) -> ReservationPut:
        """
        Creates a new `ReservationPut` instance using the data from a `ReservationNew` instance and a reservation ID.

        Args:
            reservation_id (int): The ID of the reservation to be updated (primary key).

        Returns:
            ReservationPut: A new `ReservationPut` instance with the same reservation data as the `ReservationNew`
            instance.
        """
        reservation_put = ReservationPut(
            id=reservation_id,
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information,
        )
        return reservation_put

    def is_inside_business_hour_timeframe_for_restaurant(self, restaurant: RestaurantModel) -> bool:
        """
        Check if the reservation falls within the business hours of a restaurant.

        Args:
            restaurant (Restaurant): The SQLAlchemy restaurant.

        Returns:
            bool: True if the reservation is inside the business hours, False otherwise.
        """
        # Get business-hour entry for reservation.
        # E.g.: Monday 10-13, Monday 14-18, Reservation Monday 15-16 --> Monday 14-18
        qry = select(BusinessHourModel).where(and_(
            BusinessHourModel.restaurant_id == restaurant.id,
            BusinessHourModel.weekday == self.reserved_from.weekday(),
            BusinessHourModel.close_time >= self.reserved_until.time(),
            BusinessHourModel.open_time <= self.reserved_from.time(),
            BusinessHourModel.open_for_reservation_until >= self.reserved_from.time()
        ))
        business_hour = session.scalars(qry).first()
        return business_hour is not None

    def is_conflicting_with_existing_reservations_of_table(self, table: TableModel) -> bool:
        """
        Check if the reservation conflicts with existing reservations for a specific table.

        Args:
            table (Table): The SQLAlchemy table.

        Returns:
            bool: True if there is a conflict with existing reservations, False otherwise.
        """
        get_same_day_reservations_qry = select(ReservationModel).where(and_(
            ReservationModel.table_id == table.id,
            func.date(ReservationModel.reserved_from) == self.reserved_from.date()
        )).order_by(ReservationModel.reserved_from.asc())
        same_day_reservations = list(session.scalars(get_same_day_reservations_qry).all())

        # Find space of new reservation between same-day-reservations.
        # Add <self> to the list and sort by reserved_from (Pydantic- and SQLAlchemy-model
        # both have this attribute).
        # After that find the index <self> and validate from- and until-time with its
        # neighbours.
        is_conflicting = False
        if same_day_reservations:
            same_day_reservations.append(self)
            same_day_reservations.sort(key=lambda reservation: reservation.reserved_from)
            self_index = same_day_reservations.index(self)
            if self_index == 0:
                is_conflicting = self.reserved_until > same_day_reservations[self_index + 1].reserved_from
            elif self_index == len(same_day_reservations) - 1:
                is_conflicting = self.reserved_from < same_day_reservations[self_index - 1].reserved_until
            else:
                is_conflicting = (self.reserved_until > same_day_reservations[self_index + 1].reserved_from or
                                  self.reserved_from < same_day_reservations[self_index - 1].reserved_until)
        return is_conflicting

    def validate_for_restaurant(self, restaurant: RestaurantModel) -> int:
        """
        Validate the reservation for a specific restaurant.

        Args:
            restaurant (Restaurant): The SQLAlchemy restaurant.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: No tables available for the required number of guests
                  -3: Reservation conflicts with existing reservations on every potential table
        """
        if not self.is_inside_business_hour_timeframe_for_restaurant(restaurant):
            return -1

        get_potential_tables_qry = select(TableModel).where(and_(
            TableModel.restaurant_id == restaurant.id,
            TableModel.min_guests_required_for_reservation <= self.guest_amount,
            TableModel.seats >= self.guest_amount,
        ))
        potential_tables = session.scalars(get_potential_tables_qry).all()
        if not potential_tables:
            return -2

        potential_tables = [potential_table for potential_table in potential_tables
                            if not self.is_conflicting_with_existing_reservations_of_table(potential_table)]
        if not potential_tables:
            return -3

        # Prioritize tables by sorting the list by specified criteria
        potential_tables.sort(key=lambda potential_table: potential_table.seats - self.guest_amount)

        return potential_tables[0].id

    def validate_for_table(self, table: TableModel) -> int:
        """
        Validate the reservation for a specific table.

        Args:
            table (Table): The SQLAlchemy table.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: Specified table cannot accommodate all guests
                  -3: Reservation conflicts with existing reservations on the table
        """
        if not self.is_inside_business_hour_timeframe_for_restaurant(table.restaurant):
            return -1

        if (table.min_guests_required_for_reservation > self.guest_amount or
                table.seats < self.guest_amount):
            return -2

        if self.is_conflicting_with_existing_reservations_of_table(table):
            return -3

        return table.id

class Reservation(PydanticBase):
    """
    Represents a reservation.
    """
    id: int
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int
    customer_phone: Optional[str]
    additional_information: Optional[str]

    table_id: int

    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!",
                "table_id": 1
            }
        }
        extra = Extra.forbid

    @staticmethod
    def cast_from_model(reservation_model: ReservationModel) -> Reservation:
        """
        Convert a SQLAlchemy model object to a reservation object.

        Args:
            reservation_model (ReservationModel): The SQLAlchemy model object to be converted.

        Returns:
            Reservation: The reservation object created from the SQLAlchemy model object.
        """
        reservation = Reservation(
            id=reservation_model.id,
            customer_name=reservation_model.customer_name,
            customer_email=reservation_model.customer_email,
            reserved_from=reservation_model.reserved_from,
            reserved_until=reservation_model.reserved_until,
            guest_amount=reservation_model.guest_amount,
            customer_phone=reservation_model.customer_phone,
            additional_information=reservation_model.additional_information,
            table_id=reservation_model.table_id
        )
        return reservation

class ReservationPut(PydanticBase):
    """
    Represents a reservation to be updated.
    """
    id: int = Field(gt=0)
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int = Field(gt=0)
    customer_phone: Optional[str]
    additional_information: Optional[str]

    @root_validator(skip_on_failure=True)
    def validate_times(cls, values):
        if values['reserved_from'] > values['reserved_until']:
            raise ValueError("Reservation can't end before it has started.")
        return values

    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!"
            }
        }
        extra = Extra.forbid

    def cast_to_model(self) -> ReservationModel:
        """
        Convert a reservation update object to a SQLAlchemy model object.

        Returns:
            ReservationModel: The SQLAlchemy model object created from the reservation update object.
        """
        get_existing_reservation_model_qry = select(ReservationModel).where(ReservationModel.id == self.id)
        reservation_model = session.scalars(get_existing_reservation_model_qry).first()
        # Update existing model
        reservation_model.customer_name = self.customer_name
        reservation_model.customer_email = self.customer_email
        reservation_model.reserved_from = self.reserved_from
        reservation_model.reserved_until = self.reserved_until
        reservation_model.guest_amount = self.guest_amount
        reservation_model.customer_phone = self.customer_phone
        reservation_model.additional_information = self.additional_information
        return reservation_model

    def cast_to_new(self) -> ReservationNew:
        """
        Convert a ReservationPut object to a ReservationNew object.

        This instance can be used during the validation process for a reservation update.

        Returns:
            ReservationNew: The ReservationNew object created from the ReservationPut object.
        """
        reservation_new = ReservationNew(
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information
        )
        return reservation_new

    def validate_update(self) -> int:
        """
        Validate the reservation update for its table.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: Specified table cannot accommodate all guests
                  -3: Reservation conflicts with existing reservations on the table
        """
        qry = select(ReservationModel).where(ReservationModel.id == self.id)
        old_reservation_model: ReservationModel = session.scalars(qry).first()
        table_model = old_reservation_model.table

        # Remove the old state of the reservation from the session so
        # validation runs without considering it.
        session.delete(old_reservation_model)
        session.flush()

        reservation_new = self.cast_to_new()
        valid_table_id = reservation_new.validate_for_table(table_model)

        # Add back the old state object to the session as the update will
        # only be validated and not persistent in this method.
        session.make_transient(old_reservation_model)
        session.add(old_reservation_model)
        session.flush()

        return valid_table_id

class ReservationQuery(PydanticBase):
    """
    Represents a set of query parameters used to search for reservations in the system.
    \f
    Attributes:
        restaurant_id (Optional[int]): The ID of the restaurant to search for reservations at.
        table_id (Optional[int]): The ID of the table to search for reservations for.
        customer_name (Optional[str]): The name of the customer to search for reservations for.
        customer_email (Optional[str]): The email of the customer to search for reservations for.
        customer_phone (Optional[str]): The phone number of the customer to search for reservations for.
        starting_after (Optional[datetime]): The earliest time to search for reservations from.
        starting_at (Optional[datetime]): The exact start time to search for reservations for.
        ending_before (Optional[datetime]): The latest time to search for reservations until.
        ending_at (Optional[datetime]): The exact end time to search for reservations for.
        guest_amount (Optional[int]): The exact number of guests to search for reservations for.
        min_guest_amount (Optional[int]): The minimum number of guests to search for reservations for.
        max_guest_amount (Optional[int]): The maximum number of guests to search for reservations for.
    """
    restaurant_id: Optional[int] = Field(Query(None,
                                               description=format_description_with_example(
                                                   "Get all reservations for a restaurant with the given ID.", 1)))
    table_id: Optional[int] = Field(Query(None,
                                          description=format_description_with_example(
                                              "Get all reservations for a table with the given ID.", 1)))
    customer_name: Optional[str] = Field(Query(None,
                                               description=format_description_with_example(
                                                   "Get all reservations for customers with the given name.",
                                                   "Versuchskaninchen")))
    customer_email: Optional[str] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations for customers with the given email.",
                                                    "verena.versuchskaninchen@mail.de")))
    customer_phone: Optional[str] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations for customers with the given phone-number.",
                                                    "+49-176-111-12345")))
    starting_after: Optional[datetime] = Field(Query(None,
                                                     description=format_description_with_example(
                                                         "Get all reservations starting after that time.",
                                                         "2023-05-10 16:30:00.00000")))
    starting_at: Optional[datetime] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations starting at that exact time.",
                                                      "2023-05-10 17:00:00.00000")))
    ending_before: Optional[datetime] = Field(Query(None,
                                                    description=format_description_with_example(
                                                        "Get all reservations ending before that time.",
                                                        "2023-05-10 20:00:00.00000")))
    ending_at: Optional[datetime] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations ending at that exact time.",
                                                    "2023-05-10 19:30:00.00000")))
    guest_amount: Optional[int] = Field(Query(None,
                                              description=format_description_with_example(
                                                  "Get all reservations with an exact amount of guests.", 5)))
    min_guest_amount: Optional[int] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations that have at least the amount of guests.",
                                                      3)))
    max_guest_amount: Optional[int] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations that have at most the "
                                                      "amount of guests.", 7)))