import time
from RPi import GPIO
import threading
import sys
from subprocess import check_output
import os
from flask_cors import CORS
from flask_socketio import SocketIO, emit, send
from flask import Flask, jsonify, request, render_template, redirect
from repositories.DataRepository import DataRepository
#from classes.mpuClass import MPU6050
from classes.keypadClass import clKeypad
from selenium import webdriver
from classes.TemperatuurClass import TemperatuurClass
from classes.lcdClass import Lcd
from classes.OLEDCLass import OLED
from datetime import datetime
from datetime import date
import hashlib
from classes.clEmail import emailPy
# from selenium import webdriver
# from selenium.webdriver.chrome.options import Options

# Variables
temperatuurSensor = '/sys/bus/w1/devices/28-0417819474ff/w1_slave'
#mpu = MPU6050(0x68)
rijKeypad = [9, 6, 22, 27]
kolumKeypad = [24, 12, 5]
button = 18
lcdPins = [23, 26, 19, 13]
E = 20
RS = 21
lcd = Lcd(E, RS, lcdPins)
oled = OLED(128, 64, 5)
lees = TemperatuurClass(temperatuurSensor)
user = -1
tellerAfzetten = 0
histMode = "basic"
# Code voor Hardware

def setup_gpio():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(button, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    GPIO.add_event_detect(button, GPIO.FALLING, pushed, 200)

def pushed(knop):
    global tellerAfzetten
    print("**** Read keypad THREAD *****")
    thread = threading.Thread(
        target=leesKeypad, args=(), daemon=True)
    thread.start()
    uit = []
    lcd.init_LCD()
    lcd.write_message("Want to close?:#", 0X80)
    lcd.write_message("Don't close?:*", 0XC0)
    while len(uit) != 1:
        inofUit = leesKeypad()
        if inofUit == "#" or inofUit == "*":
            uit.append(inofUit)
        tellerAfzetten += 1
        print(tellerAfzetten)
        if tellerAfzetten == 5000:
            uit.append("*")
            tellerAfzetten = 0
    if uit[0] == "#":
        print("TURNED OFF")
        lcd.write_message("TURNED OFF", 0x80)
        time.sleep(1)
        lcd.init_LCD()
        oled.Clear_oled()
        os.system("sudo shutdown -h now")
        sys.exit()
        # quits the code
    else:
        tellerAfzetten = 0
        schrijfLCD()

def reboot():
    while True:
        datumVandaag = datetime.now()
        if datumVandaag.hour == 0 and datumVandaag.minute == 0 and datumVandaag.second == 0:
            print("UPDATE")
            DataRepository.updateDatums()
            mails = DataRepository.geefmails()
            overdatum = DataRepository.geefOverdatums()
            for email in mails:
                print(email)
                #mail = emailPy(overdatum, email['E-mail'])
        else:
            pass

def geefAantal():
    while True:
        totaalAanwezig = DataRepository.aantal_aanwezigeproducten()
        if totaalAanwezig == -1:
            print(totaalAanwezig)
            return "Geen aanwezige producten"
        else:
            print(totaalAanwezig['totaalAanwezig'])
            getal = totaalAanwezig['totaalAanwezig']
            if getal == None:
                getal = 0
            else:
                getal = int(totaalAanwezig['totaalAanwezig'])
            socketio.emit(
                "B2F_aantal", {'aantal': getal}, broadcast=True)
            return f"Number of products:{getal}"

def showOled():
    while True:
        huidigeTemp = leesTemperatuur()
        print(huidigeTemp["waarde"])

        aanwezig = geefAantal()
        socketio.emit('B2F_temperatuur', {
            'temperatuur': huidigeTemp}, broadcast=True)
        print(aanwezig)
        uitvoerTemp = f"{str(huidigeTemp['waarde'])}°C"
        uitvoerAantal = aanwezig
        oled.Clear_oled()
        oled.draw(uitvoerTemp, uitvoerAantal)
        time.sleep(10)
        oled.Clear_oled()
        pic = oled.tekenFoto()
        if(pic == 1):
            oled.Clear_oled()
            oled.draw(uitvoerTemp, uitvoerAantal)

def converteerListNaarStr(lstString):
    verwijder1haak = lstString.replace("[", "")
    verwijder2haak = verwijder1haak.replace("]", "")
    verwijderkomma = verwijder2haak.replace(",", "")
    getallen = verwijderkomma.replace(" ", "")
    return getallen

def barcodeInput(invoer=""):
    barcode = invoer
    if barcode == "":
        pass
    else:
        print(barcode)
        lcd.init_LCD()
        print("**** Read keypad THREAD *****")
        try:
            thread = threading.Thread(
                target=leesKeypad, args=(), daemon=True)
            thread.start()
            uit = []
            lcd.init_LCD()
            lcd.write_message("Add product:#", 0X80)
            lcd.write_message("Delete product:*", 0xC0)
            while len(uit) != 1:
                inofUit = leesKeypad()
                if inofUit == "#" or inofUit == "*":
                    uit.append(inofUit)

            else:
                lcd.init_LCD()
                DataRepository.write_scan_history(barcode)
                zoek = DataRepository.zoekByBaarcode(barcode)
                if zoek == -1:
                    DataRepository.write_barcode(barcode)
                    lcd.write_message("Change the name", 0X80)
                    lcd.write_message("On the web app", 0xC0)
                    print("nieuwe product ingevoegd")
                    time.sleep(2)
                    lcd.init_LCD()
                    zoek = DataRepository.zoekByBaarcode(barcode)
                if uit[0] == "#":
                    lcd.write_message("Expiry date", 0x80)
                    lstDatum = []

                    while len(lstDatum) != 8:
                        waarde = leesKeypad()
                        if waarde == None or waarde == "#":
                            pass
                        elif waarde == "*":
                            lcd.init_LCD()
                            lcd.write_message("STOPPED", 0x80)
                            time.sleep(2)
                            schrijfLCD()
                            break
                        else:
                            lstDatum.append(waarde)
                            strDatum = str(lstDatum)
                            newStrdatum = converteerListNaarStr(strDatum)
                            finalString = f"{newStrdatum[:2]}-{newStrdatum[2:4]}-{newStrdatum[4:len(newStrdatum)]}"
                            lcd.write_message(finalString, 0XC0)
                    else:
                        eersteGetal = str(lstDatum[0]) + str(lstDatum[1])
                        tweedeGetal = str(lstDatum[2]) + str(lstDatum[3])
                        jaartal = str(
                            lstDatum[4])+str(lstDatum[5])+str(lstDatum[6]) + str(lstDatum[7])
                        datumke = f"{jaartal}-{tweedeGetal}-{eersteGetal}"
                        try:
                            d = datetime.strptime(datumke, '%Y-%m-%d').date()
                            huidigeDatum = date.today()
                            verschil = d-huidigeDatum
                            if verschil.days <= 0:
                                raise(Exception)
                            else:
                                lcd.init_LCD()
                                lcd.write_message("How much?", 0x80)
                                lstAantal = []
                                aantal = ""
                                while aantal != "#":
                                    aantal = leesKeypad()
                                    if aantal == None or aantal == "#" or aantal == "*":
                                        pass
                                    else:
                                        lstAantal.append(aantal)
                                        strAantal = str(lstAantal)
                                        convStrAantal = converteerListNaarStr(
                                            strAantal)
                                        global final
                                        final = f"{convStrAantal}"
                                        lcd.write_message(final, 0XC0)

                                DataRepository.add_product_in_inventory(
                                    zoek['idproduct'], d, verschil.days, int(final))
                                print(d)
                                lcd.init_LCD()
                                lcd.write_message("This is a...", 0x80)
                                lcd.write_message("Succes! :)", 0XC0)
                                time.sleep(3)
                                schrijfLCD()
                        except Exception as ex:
                            print("datum ongeldig")
                            lcd.write_message("date invalid", 0X80)
                            lcd.write_message("rescan barcode", 0xC0)
                            print(ex)
                else:
                    try:
                        zoek_delete = DataRepository.zoek_for_delete_by_barcode(
                            barcode)
                        print(zoek_delete)
                        totaalAantal = zoek_delete['aantal']
                        lcd.write_message("How much?", 0x80)
                        lcd.write_message(">", 0xC0)
                        aantalVerwijderen = []
                        vw = ""
                        final2 = ""
                        while vw != '#':
                            vw = leesKeypad()
                            if vw == "#":
                                if int(final2) > totaalAantal:
                                    vw = ""
                                    aantalVerwijderen = []
                                    final2 = ">"
                                    lcd.write_message("TOO MUCH!", 0XC0)
                                    time.sleep(2)
                                    lcd.write_message(final2, 0XC0)
                            elif vw == "*":
                                lcd.init_LCD()
                                lcd.write_message("STOPPED", 0x80)
                                time.sleep(2)
                                schrijfLCD()
                                break
                            elif vw == None:
                                pass
                            else:
                                aantalVerwijderen.append(vw)
                                strVerwijderen = str(aantalVerwijderen)
                                convStrAantal = converteerListNaarStr(
                                    strVerwijderen)

                                final2 = f"{convStrAantal}"
                                lcd.write_message(final2, 0XC0)
                        else:
                            try:
                                verschilVerwijdern = totaalAantal - int(final2)
                                print(verschilVerwijdern)
                                if verschilVerwijdern == 0:
                                    DataRepository.delete_Product(
                                        zoek_delete['idProduct'], zoek_delete['HoudbaarheidsDatum'])
                                else:
                                    DataRepository.update_Product(
                                        verschilVerwijdern, zoek_delete['idProduct'], zoek_delete['HoudbaarheidsDatum'])
                                lcd.init_LCD()
                                lcd.write_message("DELETE...", 0x80)
                                lcd.write_message("SUCCES!", 0xC0)
                                time.sleep(3)
                                schrijfLCD()
                            except Exception as ex:
                                print(ex)

                    except Exception as ex:
                        print(ex)

        except Exception as ex:
            print(ex)

def schrijfLCD():
    lcd.init_LCD()  # kuis het eerst op
    ips = check_output(
        ['hostname', '--all-ip-addresses']).split()
    # Eerste ip die binnenkomt aka de ip van de ethernetpoort
    eth = ips[0]
    # wlan = ips[1]  # de ip van de raspberry
    lcd.write_message(f"{eth.decode()}", 0x80)  # 0x80 is lijn 1 van de LCD
    # lcd.write_message(f"{wlan.decode()}", 0xC0)  # 0xC0 is lijn 2 van de LCD

# def leesMPU():
 #   while True:
  #      mpu.printAlles()
   #     time.sleep(10)
    # Checken voor open en dicht soon...

def leesTemperatuur():
    while True:
        lees = TemperatuurClass(temperatuurSensor)
        socketio.emit('B2F_temperatuur', {
            'temperatuur': lees.leesTemp()}, broadcast=True)
        return lees.leesTemp()

def meetTemperatuur():
    while True:
        huidigTemp = TemperatuurClass(temperatuurSensor)
        return huidigTemp.meetTemp()

def keypadInputDoorgeven():
    uitvoer = leesKeypad()
    return uitvoer

def leesKeypad():
    while True:
        key = clKeypad(rijKeypad, kolumKeypad)
        toets = None
        if toets == None:
            toets = key.vangToets()
            if toets != None:
                print(toets)
                if toets == "#":
                    DataRepository.write_keypad(12)
                elif toets == "*":
                    DataRepository.write_keypad(10)
                else:
                    DataRepository.write_keypad(toets)
                return toets
            else:
                return None

def leesHistoriek():
    while True:
        global histMode

        socketio.emit("B2F_mode", histMode, broadcast=True)
        #hist = DataRepository.read_historiek()
        #socketio.emit("B2F_history", hist, broadcast=True)
        if histMode == "Day":
            uitvoer = DataRepository.read_by_day()
        if histMode == "Hour":
            uitvoer = DataRepository.read_by_hour()
        if histMode == "Minute":
            uitvoer = DataRepository.read_by_minute()
        if histMode == 'Seconds' or histMode == "basic":
            uitvoer = DataRepository.read_historiek()
        socketio.emit("B2F_hist", uitvoer, broadcast=True)
        time.sleep(1)

def start():
    pass

# Code voor Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'geheim!'
socketio = SocketIO(app, cors_allowed_origins="*", logger=False,
                    engineio_logger=False, ping_timeout=1)

CORS(app)

@ socketio.on_error()        # Handles the default namespace
def error_handler(e):
    print(e)

# API ENDPOINTS
endpoint = '/api/v1'

@ app.route(endpoint + '/historiek/', methods=['GET'])
def get_histo():
    if request.method == 'GET':
        hist = DataRepository.read_historiek()
        return jsonify(historiek=hist), 200

@ app.route('/')
def hallo():
    return render_template("index.html")

@ socketio.on('connect')
def initial_connection():
    print('A new client connect')
    data = DataRepository.geef_alle_producten()
    socketio.emit("B2F_connected", data)
    DataRepository.geefOverdatums()
    socketio.emit('B2F_temperatuur', {
                  'temperatuur': leesTemperatuur()}, broadcast=True)

    # # Send to the client!

@socketio.on("F2B_historyChange")
def changehist(data):
    global histMode
    histMode = data

@ socketio.on("F2B_add_user")
def add_user(data):
    naam = data["naam"]
    voornaam = data['voornaam']
    email = data['email']
    passwoordSalted = f"{email}s@lt#{data['passwoord']}#tl@s"
    print(passwoordSalted)
    hash_object = hashlib.sha512(passwoordSalted.encode())
    hex_dig = hash_object.hexdigest()
    voeg = DataRepository.add_user(naam, voornaam, email, hex_dig)
    if voeg == -1:
        pass
    else:
        print("succes!")

@ socketio.on("F2B_gebruiker")
def connection(data):
    email = data['mail']
    passwoordSalted = f"{email}s@lt#{data['passwoord']}#tl@s"
    hash_object = hashlib.sha512(passwoordSalted.encode())
    hex_dig = hash_object.hexdigest()
    login = DataRepository.user_login(email, hex_dig)
    print(login)
    global user
    user = login

@ socketio.on("F2B_delete_account")
def deleteUser(data):
    try:
        delete = DataRepository.delete_user(data['idgebruiker'])
        if delete != -1:
            user = -1
            socketio.emit("B2F_user_delete", user)
    except Exception as ex:
        print(ex)

@ socketio.on("F2B_loadPage")
def loadpage(data):
    print(data)
    if data == 1:
        socketio.emit("B2F_user", user)

@ socketio.on("F2B_account")
def showAccount(data):
    if data == 1:
        socketio.emit("B2F_account", user)

@ socketio.on("F2B_update_user")
def updateAccount(data):
    print(data)
    id = data['id']
    naam = data["naam"]
    voornaam = data['voornaam']
    email = data['email']
    hex_dig = ""
    global user
    print(user)
    if user['Passwoord'] == data['passwoord']:
        hex_dig = user['Passwoord']
        print(hex_dig)
    else:
        passwoordSalted = f"{email}s@lt#{data['passwoord']}#tl@s"
        hash_object = hashlib.sha512(passwoordSalted.encode())
        hex_dig = hash_object.hexdigest()
        print(hex_dig)

    updateuser = DataRepository.update_user(
        id, naam, voornaam, email, hex_dig)
    if updateuser != -1:
        user = updateuser
        socketio.emit("B2F_updated_user", updateuser)
    else:
        socketio.emit("B2F_updated_user", user)

@ socketio.on("F2B_delete_product")
def delete_product(id):
    print("Start delete")
    verwijder = DataRepository.delete_by_website(id)
    socketio.emit("B2F_deleted", verwijder)

@ socketio.on('F2B_edit')
def edit(data):
    global aanwezigID
    global uitv

    aanwezigID = data
    uitv = DataRepository.zoekbyAanwezigId(data)
    if uitv != None or uitv != -1:
        socketio.emit("B2F_edit", uitv)
        print(uitv)
    else:
        print("Geen data gevonden")

@ socketio.on("F2B_add-product")
def add(data):
    print('new product')
    try:
        print(data)
        d = datetime.strptime(data["datum"], '%Y-%m-%d').date()
        huidigeDatum = date.today()
        verschil = d-huidigeDatum
        print(verschil.days)
        voegtoe = DataRepository.add_product_by_web(
            data["naam"], data["datum"], int(verschil.days), int(data["aantal"]), data['barcode'], data["foto"])
        if voegtoe == -1:
            print("Deze product is al aanwezig")
            socketio.emit("B2F_alAanwezig", {
                          "aanwezig": "-1"})
        else:
            print("nieuwe product ingevoegd")
            socketio.emit("B2F_alAanwezig", voegtoe)
    except Exception as ex:
        print(ex)

@ socketio.on("F2B_edit_product")
def update_product(data):
    try:
        print(data)
        d = datetime.strptime(data["datum"], '%Y-%m-%d').date()
        huidigeDatum = date.today()
        verschil = d-huidigeDatum
        print(verschil)
        if aanwezigID != None or aanwezigID != -1:
            DataRepository.update_by_website_product(
                aanwezigID, data["naam"], data['datum'], data['aantal'], data["barcode"], int(verschil.days), data["foto"])
        else:
            print("Geen data gevonden")

    except Exception as ex:
        print(ex)

@socketio.on("F2B_shutdown")
def shutdown_by_web(json):
    print("TURNED OFF")
    os.system("sudo shutdown -h now")
    sys.exit()
    # quits the code

@ socketio.on("F2B_barcode")
def barOffline(invoer):
    if len(invoer) >= 13:
        print(invoer)
        barcodeInput(invoer)

# Thread

def start_thread():
    print("**** Starting THREAD ****")
    try:
        thread = threading.Thread(target=start, args=(), daemon=True)
        thread.start()
        temperatuur_thread()
        read_temperatuur_thread()
        oled_thread()
        lcd_thread()
        hist_thread()
        barcode_thread()
        # MPU_thread()
        update_thread()
    except Exception as ex:
        print(ex)

def oled_thread():
    try:
        print("**** OLED thread ****")
        thread = threading.Thread(target=showOled, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def barcode_thread():
    try:
        print("**** barcode thread ****")
        thread = threading.Thread(target=barcodeInput, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def hist_thread():
    try:
        print("**** HIST THREAD ****")
        thread = threading.Thread(target=leesHistoriek, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def lcd_thread():
    print("**** LCD DISPLAY ****")
    try:
        thread = threading.Thread(target=schrijfLCD, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def update_thread():
    print("**** UPDATE THREAD ****")
    try:
        thread = threading.Thread(target=reboot, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

# def MPU_thread():
#    print("**** MPU THREAD ****")
#    try:
#        thread = threading.Thread(target=leesMPU, args=(), daemon=True)
#        thread.start()
#    except Exception as ex:
#        print(ex)

def temperatuur_thread():
    print("**** write Temperature THREAD ****")
    try:
        thread = threading.Thread(
            target=meetTemperatuur, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def read_temperatuur_thread():
    print("**** Read Temperature THREAD ****")
    try:
        thread = threading.Thread(target=leesTemperatuur, args=(), daemon=True)
        thread.start()
    except Exception as ex:
        print(ex)

def start_chrome_kiosk():
    import os

    os.environ['DISPLAY'] = ':0.0'
    options = webdriver.ChromeOptions()
    # options.headless = True
    # options.add_argument("--window-size=1920,1080")
    options.add_argument(
        "user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36")
    options.add_argument('--ignore-certificate-errors')
    options.add_argument('--allow-running-insecure-content')
    options.add_argument("--disable-extensions")
    # options.add_argument("--proxy-server='direct://'")
    options.add_argument("--proxy-bypass-list=*")
    options.add_argument("--start-maximized")
    options.add_argument('--disable-gpu')
    # options.add_argument('--disable-dev-shm-usage')
    options.add_argument('--no-sandbox')
    options.add_argument('--kiosk')
    # chrome_options.add_argument('--no-sandbox')
    # options.add_argument("disable-infobars")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)

    driver = webdriver.Chrome(options=options)
    driver.get("http://localhost/barcodeScanner.html")
    while True:
        pass

def start_chrome_thread():
    print("**** Starting CHROME ****")
    chromeThread = threading.Thread(
        target=start_chrome_kiosk, args=(), daemon=True)
    chromeThread.start()

# ANDERE FUNCTIES
if __name__ == '__main__':
    try:
        temp = TemperatuurClass(temperatuurSensor)
        DataRepository.updateDatums()

        setup_gpio()
        start_thread()
        start_chrome_thread()
        print("**** Starting APP ****")
        socketio.run(app, debug=False, host='0.0.0.0')
    except KeyboardInterrupt:
        print('KeyboardInterrupt exception is caught')
    finally:
        # mpu.sluit()
        temp.sluitTemp()
        lcd.init_LCD()
        GPIO.cleanup()


"""Tests for websites API functionality"""
from uuid import UUID

import factory
import pytest
from mitol.common.utils import now_in_utc

from content_sync.constants import VERSION_DRAFT, VERSION_LIVE
from users.factories import UserFactory
from videos.constants import YT_THUMBNAIL_IMG
from websites.api import (
    detect_mime_type,
    fetch_website,
    get_content_warnings,
    get_valid_new_filename,
    get_valid_new_slug,
    get_website_in_root_website_metadata,
    is_ocw_site,
    mail_on_publish,
    update_website_status,
    update_youtube_thumbnail,
    videos_missing_captions,
    videos_with_truncatable_text,
    videos_with_unassigned_youtube_ids,
)
from websites.constants import (
    PUBLISH_STATUS_ERRORED,
    PUBLISH_STATUS_STARTED,
    PUBLISH_STATUS_SUCCEEDED,
    RESOURCE_TYPE_IMAGE,
    RESOURCE_TYPE_VIDEO,
)
from websites.factories import (
    WebsiteContentFactory,
    WebsiteFactory,
    WebsiteStarterFactory,
)
from websites.messages import (
    PreviewOrPublishFailureMessage,
    PreviewOrPublishSuccessMessage,
)
from websites.models import Website

pytestmark = pytest.mark.django_db

EXAMPLE_UUID_STR = "ae6cfe0b-37a7-4fe6-b194-5b7f1e3c349e"

@pytest.mark.parametrize("exclude_content", [True, False])
@pytest.mark.parametrize(
    ("filename_base", "existing_filenames", "exp_result_filename"),
    [
        ["my-title", [], "my-title"],  # noqa: PT007
        ["my-title", ["my-title"], "my-title2"],  # noqa: PT007
        ["my-title", ["my-title", "my-title9"], "my-title10"],  # noqa: PT007
        [  # noqa: PT007
            "my-long-title",
            ["my-long-title", "my-long-title9"],
            "my-long-titl10",
        ],
    ],
)
def test_websitecontent_autogen_filename_unique(
    mocker, filename_base, existing_filenames, exp_result_filename, exclude_content
):
    """
    get_valid_new_filename should return a filename that obeys uniqueness constraints, adding a suffix and
    removing characters from the end of the string as necessary.
    """
    # Set a lower limit for max filename length to test that filenames are truncated appropriately
    mocker.patch("websites.api.CONTENT_FILENAME_MAX_LEN", 14)
    content_type = "page"
    dirpath = "path/to"
    website = WebsiteFactory.create()
    contents = WebsiteContentFactory.create_batch(
        len(existing_filenames),
        website=website,
        type=content_type,
        dirpath=dirpath,
        filename=factory.Iterator(existing_filenames),
    )

    exclude_text_id = contents[0].text_id if exclude_content and contents else None

    assert get_valid_new_filename(
        website_pk=website.pk,
        dirpath=dirpath,
        filename_base=filename_base,
        exclude_text_id=exclude_text_id,
    ) == (exp_result_filename if not exclude_content else filename_base)

@pytest.mark.parametrize(
    ("uuid_str", "filter_value"),
    [
        [  # noqa: PT007
            "05d329fd-05ca-4770-b8b2-77ad711daca9",
            "05d329fd-05ca-4770-b8b2-77ad711daca9",
        ],
        [  # noqa: PT007
            "05d329fd-05ca-4770-b8b2-77ad711daca9",
            "05d329fd05ca4770b8b277ad711daca9",
        ],
    ],
)
def test_fetch_website_by_uuid(uuid_str, filter_value):
    """fetch_website should find a website based on uuid"""
    website = WebsiteFactory.create(uuid=UUID(uuid_str, version=4))
    result_website = fetch_website(filter_value)
    assert website == result_website

@pytest.mark.parametrize(
    ("website_attrs", "filter_value"),
    [
        [{"title": "my test title"}, "my test title"],  # noqa: PT007
        [{"name": "my test name"}, "my test name"],  # noqa: PT007
        [{"short_id": "my.test.name"}, "my.test.name"],  # noqa: PT007
        [  # noqa: PT007
            {"title": "05d329fd-05ca-4770-b8b2-77ad711daca9"},
            "05d329fd-05ca-4770-b8b2-77ad711daca9",
        ],
        [  # noqa: PT007
            {"title": "abcdefg1-2345-6789-abcd-123456789abc"},
            "abcdefg1-2345-6789-abcd-123456789abc",
        ],
    ],
)
def test_fetch_website_by_name_title(website_attrs, filter_value):
    """fetch_website should find a website based on a name or title"""
    website = WebsiteFactory.create(
        uuid=UUID(EXAMPLE_UUID_STR, version=4), **website_attrs
    )
    result_website = fetch_website(filter_value)
    assert website == result_website

def test_fetch_website_not_found():
    """fetch_website should raise if a matching website was not found"""
    WebsiteFactory.create(
        uuid=UUID(EXAMPLE_UUID_STR, version=4),
        title="my title",
        name="my name",
    )
    with pytest.raises(Website.DoesNotExist):
        fetch_website("bad values")

@pytest.mark.parametrize(
    ("existing_slugs", "exp_result_slug"),
    [
        [[], "my-slug"],  # noqa: PT007
        [["my-slug"], "my-slug2"],  # noqa: PT007
        [["my-slug", "my-slug9"], "my-slug10"],  # noqa: PT007
        [  # noqa: PT007
            ["very-very-very-very-long-slug", "very-very-very-very-long-slug9"],
            "very-very-very-very-long-slu10",
        ],
    ],
)
def test_websitestarter_autogen_slug_unique(existing_slugs, exp_result_slug):
    """
    get_valid_new_slug should return a slug that obeys uniqueness constraints, adding a suffix and
    removing characters from the end of the string as necessary.
    """
    slug_base = exp_result_slug if not existing_slugs else existing_slugs[0]
    for slug in existing_slugs:
        WebsiteStarterFactory.create(
            path=f"http://github.com/configs1/{slug}",
            slug=slug,
            source="github",
            name=slug,
            config={"collections": []},
        )
    assert (
        get_valid_new_slug(
            slug_base=slug_base, path=f"http://github.com/configs2/{slug_base}"
        )
        == exp_result_slug
    )

def test_is_ocw_site(settings):
    """is_ocw_site() should return expected bool value for a website"""
    settings.OCW_COURSE_STARTER_SLUG = "ocw-course-v2"
    ocw_site = WebsiteFactory.create(
        starter=WebsiteStarterFactory.create(slug="ocw-course-v2")
    )
    other_site = WebsiteFactory.create(
        starter=WebsiteStarterFactory.create(slug="not-ocw-course")
    )
    assert is_ocw_site(ocw_site) is True
    assert is_ocw_site(other_site) is False

@pytest.mark.parametrize(
    ("youtube_id", "existing_thumb", "overwrite", "expected_thumb"),
    [
        [  # noqa: PT007
            None,
            YT_THUMBNAIL_IMG.format(video_id="fake"),
            True,
            YT_THUMBNAIL_IMG.format(video_id="fake"),
        ],
        [  # noqa: PT007
            "abc123",
            YT_THUMBNAIL_IMG.format(video_id="def456"),
            False,
            YT_THUMBNAIL_IMG.format(video_id="def456"),
        ],
        [  # noqa: PT007
            "abc123",
            "",
            False,
            YT_THUMBNAIL_IMG.format(video_id="abc123"),
        ],
        [  # noqa: PT007
            "abc123",
            None,
            False,
            YT_THUMBNAIL_IMG.format(video_id="abc123"),
        ],
        [  # noqa: PT007
            "abc123",
            YT_THUMBNAIL_IMG.format(video_id="def456"),
            True,
            YT_THUMBNAIL_IMG.format(video_id="abc123"),
        ],
    ],
)
def test_update_youtube_thumbnail(
    mocker, youtube_id, existing_thumb, overwrite, expected_thumb
):
    """The youtube thumbnail field should be set to the specified value if it exists"""
    mocker.patch("websites.api.is_ocw_site", return_value=True)
    website = WebsiteFactory.create()
    metadata = {
        "video_metadata": {"youtube_id": youtube_id},
        "video_files": {"video_thumbnail_file": existing_thumb},
    }
    update_youtube_thumbnail(website.uuid, metadata, overwrite=overwrite)
    assert metadata["video_files"]["video_thumbnail_file"] == expected_thumb

@pytest.mark.parametrize("is_ocw", [True, False])
def test_unassigned_youtube_ids(mocker, is_ocw):
    """videos_with_unassigned_youtube_ids should return WebsiteContent objects for videos with no youtube ids"""
    mocker.patch("websites.api.is_ocw_site", return_value=is_ocw)
    website = WebsiteFactory.create()
    WebsiteContentFactory.create_batch(
        4,
        website=website,
        metadata={
            "resourcetype": RESOURCE_TYPE_VIDEO,
            "video_metadata": {"youtube_id": "abc123"},
        },
    )
    videos_without_ids = []
    videos_without_ids.append(
        WebsiteContentFactory.create(
            website=website,
            metadata={
                "resourcetype": RESOURCE_TYPE_VIDEO,
                "video_metadata": {},
            },
        )
    )
    for yt_id in [None, ""]:
        videos_without_ids.append(  # noqa: PERF401
            WebsiteContentFactory.create(
                website=website,
                metadata={
                    "resourcetype": RESOURCE_TYPE_VIDEO,
                    "video_metadata": {"youtube_id": yt_id},
                },
            )
        )
    WebsiteContentFactory.create(
        website=website,
        metadata={
            "resourcetype": RESOURCE_TYPE_IMAGE,
            "video_metadata": {"youtube_id": "bad_data"},
        },
    )
    unassigned_content = videos_with_unassigned_youtube_ids(website)
    if is_ocw:
        assert len(unassigned_content) == 3
        for content in videos_without_ids:
            assert content in unassigned_content
    else:
        assert len(unassigned_content) == 0

@pytest.mark.parametrize("is_ocw", [True, False])
def test_videos_missing_captions(mocker, is_ocw):
    """videos_missing_captions should return WebsiteContent objects for videos with no captions"""
    mocker.patch("websites.api.is_ocw_site", return_value=is_ocw)
    website = WebsiteFactory.create()
    WebsiteContentFactory.create_batch(
        3,
        website=website,
        metadata={
            "resourcetype": RESOURCE_TYPE_VIDEO,
            "video_files": {"video_captions_file": "abc123"},
        },
    )
    videos_without_captions = []

    for captions in [None, ""]:
        videos_without_captions.append(  # noqa: PERF401
            WebsiteContentFactory.create(
                website=website,
                metadata={
                    "resourcetype": RESOURCE_TYPE_VIDEO,
                    "video_files": {"video_captions_file": captions},
                },
            )
        )
    WebsiteContentFactory.create(
        website=website,
        metadata={
            "resourcetype": RESOURCE_TYPE_IMAGE,
            "video_files": {"video_captions_file": "bad_data"},
        },
    )

    unassigned_content = videos_missing_captions(website)
    if is_ocw:
        assert len(unassigned_content) == 2
        for content in videos_without_captions:
            assert content in unassigned_content
    else:
        assert len(unassigned_content) == 0

@pytest.mark.parametrize("is_ocw", [True, False])
def test_videos_with_truncatable_text(mocker, is_ocw):
    """Videos with titles or descriptions that are too long should be returned"""
    mocker.patch("websites.api.is_ocw_site", return_value=is_ocw)
    website = WebsiteFactory.create()
    title_descs = (
        (" ".join(["TooLongTitle" for _ in range(10)]), "desc"),
        ("title", " ".join(["TooLongDescription" for _ in range(500)])),
        ("title", "desc"),
    )
    resources = []
    for title, desc in title_descs:
        resources.append(
            WebsiteContentFactory.create(
                website=website,
                title=title,
                metadata={
                    "resourcetype": RESOURCE_TYPE_VIDEO,
                    "video_metadata": {"youtube_description": desc},
                    "video_files": {"video_captions_file": "abc123"},
                },
            )
        )
    truncatable_content = videos_with_truncatable_text(website)
    assert len(resources[1].metadata["video_metadata"]["youtube_description"]) > 5000

    if is_ocw:
        assert len(truncatable_content) == 2
        for content in resources[0:2]:
            assert content in truncatable_content
    else:
        assert truncatable_content == []

@pytest.mark.parametrize("success", [True, False])
@pytest.mark.parametrize("version", ["live", "draft"])
def test_mail_on_publish(settings, mocker, success, version, permission_groups):
    """mail_on_publish should send correct email to correct users"""
    settings.OCW_STUDIO_LIVE_URL = "http://test.live.edu/"
    settings.OCW_STUDIO_DRAFT_URL = "http://test.draft.edu"
    mock_get_message_sender = mocker.patch("websites.api.get_message_sender")
    mock_sender = mock_get_message_sender.return_value.__enter__.return_value
    message = (
        PreviewOrPublishSuccessMessage if success else PreviewOrPublishFailureMessage
    )
    website = permission_groups.websites[0]
    user = UserFactory.create()
    mail_on_publish(website.name, version, success, user.id)
    mock_get_message_sender.assert_called_once_with(message)
    mock_sender.build_and_send_message.assert_any_call(
        user,
        {
            "site": {
                "title": website.title,
                "url": f"http://test.{version}.edu/{website.url_path}",
            },
            "version": version,
        },
    )

def test_detect_mime_type(mocker):
    """detect_mime_type should use python-magic to detect the mime type of an uploaded file"""
    chunk = b"chunk"
    chunks_mock = mocker.Mock(return_value=iter([chunk]))
    uploaded_file = mocker.Mock(chunks=chunks_mock)
    mime_type = "image/tiff"
    magic_mock = mocker.patch("websites.api.Magic")
    from_buffer_mock = magic_mock.return_value.from_buffer
    from_buffer_mock.return_value = mime_type

    assert detect_mime_type(uploaded_file) == mime_type
    from_buffer_mock.assert_called_once_with(chunk)
    chunks_mock.assert_called_once_with(chunk_size=2048)
    magic_mock.assert_called_once_with(mime=True)

@pytest.mark.parametrize(
    ("status", "notify"),
    [
        [PUBLISH_STATUS_STARTED, False],  # noqa: PT007
        [PUBLISH_STATUS_SUCCEEDED, True],  # noqa: PT007
        [PUBLISH_STATUS_ERRORED, True],  # noqa: PT007
    ],
)
@pytest.mark.parametrize("has_user", [True, False])
@pytest.mark.parametrize("version", [VERSION_DRAFT, VERSION_LIVE])
def test_update_website_status(mocker, status, notify, has_user, version):
    """update_website_status should update the appropriate website publishing fields"""
    mock_mail = mocker.patch("websites.api.mail_on_publish")
    mock_log = mocker.patch("websites.api.log.error")
    user = UserFactory.create() if has_user else None
    website = WebsiteFactory.create(**{f"{version}_last_published_by": user})
    now = now_in_utc()
    update_website_status(website, version, status, now)
    website.refresh_from_db()
    assert getattr(website, f"{version}_publish_status") == status
    assert getattr(website, f"{version}_publish_status_updated_on") == now
    assert mock_mail.call_count == (1 if has_user and notify else 0)
    assert mock_log.call_count == (1 if status == PUBLISH_STATUS_ERRORED else 0)

@pytest.mark.parametrize("status", [PUBLISH_STATUS_SUCCEEDED, PUBLISH_STATUS_ERRORED])
def test_update_website_unpublish_status(mocker, status):
    """update_website_status should update the appropriate website publishing fields"""
    mock_mail = mocker.patch("websites.api.mail_on_publish")
    mock_log = mocker.patch("websites.api.log.error")
    user = UserFactory.create()
    website = WebsiteFactory.create(last_unpublished_by=user, unpublished=True)
    now = now_in_utc()
    update_website_status(website, VERSION_LIVE, status, now, unpublished=True)
    website.refresh_from_db()
    assert website.live_publish_status is None
    assert website.live_publish_status_updated_on is None
    assert website.unpublish_status_updated_on == now
    assert website.unpublish_status == status
    assert mock_mail.call_count == 0
    assert mock_log.call_count == (1 if status == PUBLISH_STATUS_ERRORED else 0)

@pytest.mark.parametrize(
    "status",
    [
        PUBLISH_STATUS_STARTED,
        PUBLISH_STATUS_SUCCEEDED,
        PUBLISH_STATUS_ERRORED,
    ],
)
@pytest.mark.parametrize("version", [VERSION_DRAFT, VERSION_LIVE])
def test_update_unpublished_website_status(status, version):
    """update_website_status should update an unpublished site appropriately"""
    website = WebsiteFactory.create(not_published=True, draft_publish_date=None)
    now = now_in_utc()
    update_website_status(website, version, status, now)
    website.refresh_from_db()
    assert getattr(website, f"{version}_publish_status") == status
    assert getattr(website, f"{version}_publish_status_updated_on") == now

    publish_date_field = (
        "publish_date" if version == VERSION_LIVE else "draft_publish_date"
    )
    if status == PUBLISH_STATUS_SUCCEEDED:
        assert getattr(website, publish_date_field) == now
        if version == VERSION_LIVE:
            assert website.first_published_to_production == now
    else:
        assert getattr(website, publish_date_field) is None

@pytest.mark.parametrize("has_missing_ids", [True, False])
@pytest.mark.parametrize("has_missing_captions", [True, False])
@pytest.mark.parametrize("has_truncatable_text", [True, False])
@pytest.mark.parametrize("is_draft", [True, False])
def test_get_content_warnings(
    mocker, has_missing_ids, has_missing_captions, has_truncatable_text, is_draft
):
    """get_content_warnings should return expected warning messages"""
    website = WebsiteFactory.create()
    video_content = WebsiteContentFactory.create_batch(3, website=website)
    no_yt_ids = video_content[0:2] if has_missing_ids else []
    no_caps = video_content[1:3] if has_missing_captions else []
    truncatable_vids = [video_content[2]] if has_truncatable_text else []
    # testing only for videos here, but warnings will be the same for any content type
    draft_titles = [video_content[1]] if is_draft else []
    mocker.patch(
        "websites.api.videos_with_truncatable_text", return_value=truncatable_vids
    )
    mocker.patch(
        "websites.api.videos_with_unassigned_youtube_ids",
        return_value=no_yt_ids,
    )
    mocker.patch(
        "websites.api.videos_missing_captions",
        return_value=no_caps,
    )
    mocker.patch(
        "websites.api.draft_content",
        return_value=draft_titles,
    )
    warnings = get_content_warnings(website)
    warnings_len = 0
    if has_missing_ids:
        warnings_len += 1
        for content in no_yt_ids:
            assert content.title in warnings[0]
    if has_missing_captions:
        warnings_len += 1
        for content in no_caps:
            assert content.title in warnings[1 if has_missing_ids else 0]
    if has_truncatable_text:
        warnings_len += 1
        assert (
            video_content[2].title
            in warnings[int(has_missing_ids) + int(has_missing_captions)]
        )
    if is_draft:
        warnings_len += 1
        assert len(warnings) == warnings_len
        assert video_content[1].title in warnings[warnings_len - 1]
    if (
        not has_missing_ids
        and not has_missing_captions
        and not has_truncatable_text
        and not is_draft
    ):
        assert not warnings

@pytest.mark.parametrize(
    ("version", "site_fields", "expected_draft"),
    [
        (
            # Publishing to live, but site previously published live.
            VERSION_LIVE,
            {},
            False,
        ),
        (
            # Publishing to live for first time
            VERSION_LIVE,
            {"publish_date": None},
            False,
        ),
        (
            # Publishing to draft, but site previously published live.
            VERSION_DRAFT,
            {"publish_date": now_in_utc()},
            False,
        ),
        (
            # Site is unbpublished.
            VERSION_DRAFT,
            {"unpublish_status": PUBLISH_STATUS_SUCCEEDED},
            True,
        ),
        (
            # Site is unbpublished.
            VERSION_LIVE,
            {"unpublish_status": PUBLISH_STATUS_SUCCEEDED},
            True,
        ),
        (
            # Site never published. OK to set to draft
            VERSION_DRAFT,
            {"publish_date": None},
            True,
        ),
    ],
)
def test_get_website_in_root_website_metadata(version, site_fields, expected_draft):
    """
    Test that get_website_in_root_website_metadata returns the expected metadata,
    especially in regards to draft status.
    """
    website = WebsiteFactory.create(**site_fields)
    sitemetadata = WebsiteContentFactory.create(
        website=website,
        type="sitemetadata",
        metadata={
            "some": "field",
        },
    )

    expected = {
        **sitemetadata.metadata,
        "url_path": website.url_path,
        "_build": {"list": True, "render": False},
    }
    if expected_draft:
        expected["draft"] = True

    assert get_website_in_root_website_metadata(website, version) == expected


# TODO: Add support for apartments
import googlemaps as gmaps
import numpy
import requests

import concurrent.futures
import time
import os

API_Key = os.environ.get('GMaps_API_Key')

coord1 = (29.44578, -98.52163)
coord2 = (29.45774, -98.54524)

latStart = min(coord1[0],coord2[0])
latEnd = max(coord1[0],coord2[0])
lonStart = min(coord1[1],coord2[1])
lonEnd = max(coord1[1],coord2[1])

#0.01 Low Res || 0.003 Med Res ||| 0.002 High Res
scanRes = 0.002

out = []
CONNECTIONS = 100
TIMEOUT = 5
urls = []

urlpre = "https://fiber.google.com/address?street_address="
urlmid = "&unit_number=&zip_code="
urlend = "&event_category=check%20address&event_action=submit&event_label=hero"
successurl = "https://fiber.google.com/signup/s/"

gmaps = gmaps.Client(key=API_Key)
fileNum=1
fileOutAvail = ""
fileOutAvail = ""
while not fileOutAvail:
    try:
        fileOutAvail  = open("dataAvail"+str(fileNum)+".csv", "x")
    except Exception as e:
        fileNum += 1

try:
    fileOutAvailMain  = open("dataAvail.csv", "a")
except Exception as e:
    print("dataAvail.csv is not available for writing. \nTERMINATING")
    exit()

fileOutAvail.write("Availability,StreetAddress,Zipcode\n")

latChecks = (latEnd - latStart) / scanRes
lonChecks = (lonEnd - lonStart) / scanRes
numChecks = int(lonChecks * latChecks);

cont = input(f"Starting {numChecks} checks. \nContinue? [Y/N]: ")
if 'y' not in cont.lower():
    exit()

currcheck = 0
coordsList = []
availList = []

def get_addr(coords):
    addr = gmaps.reverse_geocode(coords)
    return addr

def hasFiber(addr):
    address = ''
    zipcode = ''
    streetNum = ''
    route = ''
    for addComp in addr[0]['address_components']:
        if 'street_number' in addComp['types']:
            streetNum = addComp['long_name']
        if 'route' in addComp['types']:
            route = addComp['long_name']
        if 'postal_code' in addComp['types']:
            zipcode = addComp['long_name']
    address = streetNum + " " + route
    fullurl = urlpre + address.replace(" ","%20") + urlmid + zipcode + urlend

    r = requests.head(fullurl, allow_redirects=True)
    if successurl in r.url:
        return "AVAILABLE," + str(address) + ","+ str(zipcode)+"\n"
    else:
        return ""

for lat in numpy.arange(latStart, latEnd, scanRes):
    for lon in numpy.arange(lonStart, lonEnd, scanRes):
        coordsList.append((lat,lon))

with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
    future_to_url = (executor.submit(get_addr, coords) for coords in coordsList)
    time1 = time.time()
    for future in concurrent.futures.as_completed(future_to_url):
        try:
            data = future.result()
        except Exception as exc:
            data = str(type(exc))
        finally:
            out.append(data)

            print(str(len(out)),end="\r")

    time2 = time.time()

with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
    future_to_url = (executor.submit(hasFiber, addr) for addr in out)
    for future in concurrent.futures.as_completed(future_to_url):
        try:
            data = future.result()
        except Exception as exc:
            data = str(type(exc))
        finally:
            if data:
                fileOutAvail.write(data)
                fileOutAvailMain.write(data)

            print(str(len(out)),end="\r")

fileOutAvail.close()
fileOutAvailMain.close()


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev, node = None, head
        while node != None:
            # check if duplicated
            if node.next != None and node.val == node.next.val:
                node_to_delete = node.next
                while node != None and node.val == node_to_delete.val:
                    node = node.next

                # check if the node is in front of the list
                if prev != None:
                    prev.next = node
                else:
                    head = node

            # if it's unique just move to the next node
            else:
                prev = node
                node = node.next

        return head

82. Remove Duplicates from Sorted List II.py


import requests

s_date = '1402,8,1'
e_date = '1402,8,30'

data = {
    'start_time': s_date,
    'end_time': e_date
}

url = "http://127.0.0.1:8000/sale_predict/"

response = requests.get(url, json=data)

print(response.status_code)
print(response.json())


from __future__ import absolute_import, division

import re

from ._args import Arguments

# see parse() docstring for example
_parse_rules = r'''
(?:
    ([-&\w]+)
    =
)?
(?:
    ([-\w]+)
    |
    "
    (.*?)
    (?<!\\)"
    |
    '
    (.*?)
    (?<!\\)'
)
'''
_parse_re = re.compile(_parse_rules, re.X)

def parse(input):
    """
    Parse <input> for positional and keyword arguments, with value quoting and
    quotes escaping.

    :param input: can be like: a b c d=e f="g h" i='j k' l="\"m\" n" o='\'p\' q'
    :returns: Argument instance
    """
    ret = Arguments()

    for match in _parse_re.finditer(input):
        key = match.group(1)
        value = (match.group(2) or match.group(3) or match.group(4)).decode('unicode-escape')

        if key:
            ret.keyword[key] = value
        else:
            ret.positional.append(value)

    return ret

_unparse_rules = r'''^[-\w]+$'''
_unparse_re = re.compile(_unparse_rules, re.X)

def unparse(args):
    """
    Generate a argument string from a Argument instance <args>.
    Argument values that need quoting will be quoted.
    Keyword names must never need quoting (would raise ValueError).

    :param args: Argument instance
    :returns: argument unicode object
    """
    ret = []

    for value in args.positional:
        if not _unparse_re.match(value):
            value = u'"' + value.encode('unicode-escape') + u'"'
        ret.append(value)

    keywords = args.keyword.items()
    keywords.sort(key=lambda a: a[0])
    for key, value in keywords:
        if not _unparse_re.match(key):
            raise ValueError("Invalid keyword string")
        if not _unparse_re.match(value):
            value = u'"' + value.encode('unicode-escape') + u'"'
        ret.append(key + u'=' + value)

    return u' '.join(ret)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tests aiofiles and aiojson integration.
"""
import json
import shutil

import asynctest
from libneko import aiojson, filesystem

test_data_obj = {"foo": [1, "bar", None], "baz": False, "bork": {}}

test_data_raw = json.dumps(test_data_obj)

test_file = ".test.json"

class AioJsonTest(asynctest.TestCase):
    @classmethod
    def setUpClass(cls):
        pass

    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(test_file, ignore_errors=True)

    async def test_aiodump(self):
        async with filesystem.aioopen(test_file, "w") as fp:
            await aiojson.aiodump(test_data_obj, fp)

        with open(test_file) as fp:
            data = fp.read()
            self.assertEqual(test_data_raw, data)

    async def test_aioload(self):
        with open(test_file, "w") as fp:
            fp.write(test_data_raw)

        async with filesystem.aioopen(test_file) as fp:
            obj = await aiojson.aioload(fp)

        self.assertEqual(test_data_obj, obj)


import pandas as pd
from Stat_Calculations.yearly_stats import compile_team_yearly_stats, compile_yearly_stats, season_rankings_by_game
from Stat_Calculations.compile_data import create_stats_df
import numpy as np
from sklearn.neighbors import NearestNeighbors
import matplotlib.pyplot as plt
from sklearn import preprocessing
import psycopg2 as pg2
from sqlalchemy import create_engine

def base_stats_df():
    print('#1')
    base_stats_df = create_stats_df()
    base_stats_df = base_stats_df[base_stats_df['season_type'] == 'Regular Season']

    base_stats_df['jacob_absolute_value'] = base_stats_df['points_score'].abs() + base_stats_df['assists_score'].abs() + base_stats_df['orebs_score'].abs() + base_stats_df['drebs_score'].abs() + base_stats_df['to_score'].abs() + base_stats_df['stls_score'].abs() + base_stats_df['blocks_score'].abs() + base_stats_df['ft_score'].abs() + base_stats_df['dfg_score'].abs() + base_stats_df['sast_score'].abs() + base_stats_df['ft_ast_score'].abs() + base_stats_df['missed_fg_score'].abs() + base_stats_df['def_fouls_score'].abs()
    base_stats_df['pts_contr'] = (base_stats_df['points_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['ast_contr'] = (base_stats_df['assists_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['orebs_contr'] = (base_stats_df['orebs_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['drebs_contr'] = (base_stats_df['drebs_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['to_contr'] = (base_stats_df['to_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['stls_contr'] = (base_stats_df['stls_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['blocks_contr'] = (base_stats_df['blocks_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['ft_contr'] = (base_stats_df['ft_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['dfg_contr'] = (base_stats_df['dfg_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['sast_contr'] = (base_stats_df['sast_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['ft_ast_contr'] = (base_stats_df['ft_ast_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['missed_fg_contr'] = (base_stats_df['missed_fg_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df['def_fouls_contr'] = (base_stats_df['def_fouls_score'] / base_stats_df['jacob_absolute_value']) * base_stats_df['wins_contr']
    base_stats_df = base_stats_df[['season_end', 'player_id', 'player_name', 'pts_contr', 'ast_contr', 'orebs_contr', 'drebs_contr', 'to_contr', 'stls_contr', 'blocks_contr', 'ft_contr', 'dfg_contr', 'sast_contr', 'ft_ast_contr', 'missed_fg_contr', 'def_fouls_contr']]

    sum_base_stats_df = base_stats_df.groupby(['player_id','season_end', 'player_name'], as_index=False)
    sum_base_stats_df = sum_base_stats_df.aggregate(np.sum)
    sum_base_stats_df = sum_base_stats_df.rename(columns={"pts_contr": "pts_contr_sum", "ast_contr": "ast_contr_sum",
                                                    "orebs_contr": "orebs_contr_sum", "drebs_contr": "drebs_contr_sum",
                                                    "to_contr": "to_contr_sum", "stls_contr": "stls_contr_sum",
                                                    "blocks_contr": "blocks_contr_sum", "ft_contr": "ft_contr_sum",
                                                    "dfg_contr": "dfg_contr_sum", "sast_contr": "sast_contr_sum",
                                                    "ft_ast_contr": "ft_ast_contr_sum", "missed_fg_contr": "missed_fg_contr_sum",
                                                    "def_fouls_contr": "def_fouls_contr_sum"})

    mean_base_stats_df = base_stats_df.groupby(['player_id','season_end', 'player_name'], as_index=False)
    mean_base_stats_df = mean_base_stats_df.aggregate(np.mean)
    mean_base_stats_df = mean_base_stats_df.rename(columns={"pts_contr": "pts_contr_avg", "ast_contr": "ast_contr_avg",
                                                    "orebs_contr": "orebs_contr_avg", "drebs_contr": "drebs_contr_avg",
                                                    "to_contr": "to_contr_avg", "stls_contr": "stls_contr_avg",
                                                    "blocks_contr": "blocks_contr_avg", "ft_contr": "ft_contr_avg",
                                                    "dfg_contr": "dfg_contr_avg", "sast_contr": "sast_contr_avg",
                                                    "ft_ast_contr": "ft_ast_contr_avg", "missed_fg_contr": "missed_fg_contr_avg",
                                                    "def_fouls_contr": "def_fouls_contr_avg"})

    base_stats_df = sum_base_stats_df.merge(mean_base_stats_df, on=['player_id','season_end', 'player_name'])

    return base_stats_df
# base_stats_df()

def yearly_stats_df():
    print('#2')
    conn = pg2.connect(dbname= "wins_contr", host = "localhost")
    cur = conn.cursor()
    # SQL Pull
    team_yearly_stats = """SELECT *
                        FROM yearly_stats_by_team"""
    yearly_stats = """SELECT *
                        FROM yearly_stats_by_season"""

    team_yearly_stats = pd.read_sql(team_yearly_stats, con=conn)
    team_yearly_stats = team_yearly_stats[team_yearly_stats['season_type'] == 'Regular Season']
    team_yearly_stats = team_yearly_stats.drop(columns=['season_type', 'team_id', 'wins_contr', 'wins_contr_in_loss'])
    # print('team_yearly stats')
    # print(team_yearly_stats[team_yearly_stats['player_id'] == 202355])

    yearly_stats = pd.read_sql(yearly_stats, con=conn)
    yearly_stats = yearly_stats[yearly_stats['season_type'] == 'Regular Season']
    yearly_stats['pct_of_wins'] = yearly_stats['wins_contr'] / yearly_stats['wins']
    yearly_stats = yearly_stats.drop(columns=['wins', 'losses', 'games_played', 'season_type'])
    # print('yearly stats')
    # print(yearly_stats[yearly_stats['player_id'] == 202355])

    conn.close()

    return yearly_stats.merge(team_yearly_stats, on=['player_id','season_end', 'player_name'])

#yearly_stats_df()

def game_rank_df():
    print('#3')
    tc_rank_2014 = season_rankings_by_game(season_end=2014, win_loss='both', season_type='Regular Season')
    tc_rank_2014['season_end'] = 2014
    tc_rank_2015 = season_rankings_by_game(season_end=2015, win_loss='both', season_type='Regular Season')
    tc_rank_2015['season_end'] = 2015
    tc_rank_2016 = season_rankings_by_game(season_end=2016, win_loss='both', season_type='Regular Season')
    tc_rank_2016['season_end'] = 2016
    tc_rank_2017 = season_rankings_by_game(season_end=2017, win_loss='both', season_type='Regular Season')
    tc_rank_2017['season_end'] = 2017
    tc_rank_2018 = season_rankings_by_game(season_end=2018, win_loss='both', season_type='Regular Season')
    tc_rank_2018['season_end'] = 2018
    tc_rank_2019 = season_rankings_by_game(season_end=2019, win_loss='both', season_type='Regular Season')
    tc_rank_2019['season_end'] = 2019

    wc_rank_2014 = season_rankings_by_game(season_end=2014, win_loss=1, season_type='Regular Season')
    wc_rank_2014['season_end'] = 2014
    wc_rank_2015 = season_rankings_by_game(season_end=2015, win_loss=1, season_type='Regular Season')
    wc_rank_2015['season_end'] = 2015
    wc_rank_2016 = season_rankings_by_game(season_end=2016, win_loss=1, season_type='Regular Season')
    wc_rank_2016['season_end'] = 2016
    wc_rank_2017 = season_rankings_by_game(season_end=2017, win_loss=1, season_type='Regular Season')
    wc_rank_2017['season_end'] = 2017
    wc_rank_2018 = season_rankings_by_game(season_end=2018, win_loss=1, season_type='Regular Season')
    wc_rank_2018['season_end'] = 2018
    wc_rank_2019 = season_rankings_by_game(season_end=2019, win_loss=1, season_type='Regular Season')
    wc_rank_2019['season_end'] = 2019

    tc_games_rank = tc_rank_2014.append([tc_rank_2015, tc_rank_2016, tc_rank_2017, tc_rank_2018, tc_rank_2019])
    tc_games_rank = tc_games_rank.rename(columns={"win_rank": "total_rank_games"})
    wc_games_rank = wc_rank_2014.append([wc_rank_2015, wc_rank_2016, wc_rank_2017, wc_rank_2018, wc_rank_2019])
    wc_games_rank = wc_games_rank.rename(columns={"win_rank": "win_rank_games"})
    all_seasons_games_rank = tc_games_rank.merge(wc_games_rank, on=['player_id', 'player_name','season_end'])

    all_seasons_games_rank = all_seasons_games_rank.reset_index(drop=True)

    return all_seasons_games_rank

def clustering_df():

    clustering_df = base_stats_df().merge(yearly_stats_df(), on=['player_id', 'player_name','season_end'])
    clustering_df = clustering_df.merge(game_rank_df(), on=['player_id', 'player_name','season_end'])
    clustering_df = clustering_df.reset_index(drop=True)
    return clustering_df
# clustering_df()

def nearest_neighbors_df(df):
    X = pd.DataFrame()
    y = pd.DataFrame()
    clustering_df = df.replace([np.inf, -np.inf], np.nan)
    clustering_df = clustering_df.fillna(0)
    clustering_df = clustering_df.drop_duplicates()
    # print(clustering_df)
    # print(clustering_df.columns)
    print(clustering_df[clustering_df['player_id'] == 1629011])
    y['player_id'] = clustering_df['player_id']
    y['season_end'] = clustering_df['season_end']
    y['player_name'] = clustering_df['player_name']
    X['pos_offense'] = clustering_df['pts_contr_avg']  + clustering_df['ast_contr_avg'] + clustering_df['ft_ast_contr_avg'] + clustering_df['ft_contr_avg']
    X['neg_offense'] =  clustering_df['missed_fg_contr_avg'] + clustering_df['to_contr_avg']
    X['hustle'] = clustering_df['orebs_contr_avg'] + clustering_df['drebs_contr_avg'] + clustering_df['sast_contr_avg']
    X['defense'] = clustering_df['stls_contr_avg'] + clustering_df['blocks_contr_avg'] + clustering_df['dfg_contr_avg'] + clustering_df['def_fouls_contr_avg']
    X['rank'] = clustering_df['tc_rank_team'] / clustering_df['total_rank_games']
    X['wins'] = clustering_df['wc_lc_ratio'] * clustering_df['pct_of_wins']

    y = y.reset_index(drop=True)
    X = X.reset_index(drop=True)

    X = X.to_numpy()
    min_max_scaler = preprocessing.MinMaxScaler()
    X_scaled = min_max_scaler.fit_transform(X)

    print('the fit has begun')
    neigh = NearestNeighbors(n_neighbors=6, metric='cosine')
    neigh.fit(X_scaled)

    neighbors_df = pd.DataFrame()
    neighbors_df['similar_player_1_id'] = 0
    neighbors_df['similar_player_1_season_end'] = 0
    neighbors_df['similar_player_1_name'] = 0
    neighbors_df['similar_player_1_distance'] = 0
    neighbors_df['similar_player_2_id'] = 0
    neighbors_df['similar_player_2_season_end'] = 0
    neighbors_df['similar_player_2_name'] = 0
    neighbors_df['similar_player_2_distance'] = 0
    neighbors_df['similar_player_3_id'] = 0
    neighbors_df['similar_player_3_season_end'] = 0
    neighbors_df['similar_player_3_name'] = 0
    neighbors_df['similar_player_3_distance'] = 0
    neighbors_df['similar_player_4_id'] = 0
    neighbors_df['similar_player_4_season_end'] = 0
    neighbors_df['similar_player_4_name'] = 0
    neighbors_df['similar_player_4_distance'] = 0
    neighbors_df['similar_player_5_id'] = 0
    neighbors_df['similar_player_5_season_end'] = 0
    neighbors_df['similar_player_5_name'] = 0
    neighbors_df['similar_player_5_distance'] = 0
    similar_player_1_id_list = []
    similar_player_2_id_list = []
    similar_player_3_id_list = []
    similar_player_4_id_list = []
    similar_player_5_id_list = []
    similar_player_1_season_end_list = []
    similar_player_2_season_end_list = []
    similar_player_3_season_end_list = []
    similar_player_4_season_end_list = []
    similar_player_5_season_end_list = []
    similar_player_1_name_list = []
    similar_player_2_name_list = []
    similar_player_3_name_list = []
    similar_player_4_name_list = []
    similar_player_5_name_list = []
    similar_player_1_distance_list = []
    similar_player_2_distance_list = []
    similar_player_3_distance_list = []
    similar_player_4_distance_list = []
    similar_player_5_distance_list = []
    for idx in range(len(X)):
        #print('Neighbors Loop: ' + str(idx) + ' / ' + str(len(X)))
        neighbors = neigh.kneighbors(X_scaled[idx, :].reshape(1, -1))
        distance_list = neighbors[0].tolist()
        for idx, neighbors in enumerate(neighbors[1]):
            neighbors = neighbors[1:]

            for counter, player in enumerate(neighbors):
                if counter == 0:
                    similar_player_1_id_list.append(y.iloc[player, 0])
                    similar_player_1_season_end_list.append(y.iloc[player, 1])
                    similar_player_1_name_list.append(y.iloc[player, 2])
                    similar_player_1_distance_list.append(distance_list[idx][1])
                elif counter == 1:
                    similar_player_2_id_list.append(y.iloc[player, 0])
                    similar_player_2_season_end_list.append(y.iloc[player, 1])
                    similar_player_2_name_list.append(y.iloc[player, 2])
                    similar_player_2_distance_list.append(distance_list[idx][2])
                elif counter == 2:
                    similar_player_3_id_list.append(y.iloc[player, 0])
                    similar_player_3_season_end_list.append(y.iloc[player, 1])
                    similar_player_3_name_list.append(y.iloc[player, 2])
                    similar_player_3_distance_list.append(distance_list[idx][3])
                elif counter == 3:
                    similar_player_4_id_list.append(y.iloc[player, 0])
                    similar_player_4_season_end_list.append(y.iloc[player, 1])
                    similar_player_4_name_list.append(y.iloc[player, 2])
                    similar_player_4_distance_list.append(distance_list[idx][4])
                else:
                    similar_player_5_id_list.append(y.iloc[player, 0])
                    similar_player_5_season_end_list.append(y.iloc[player, 1])
                    similar_player_5_name_list.append(y.iloc[player, 2])
                    similar_player_5_distance_list.append(distance_list[idx][5])

    neighbors_df['positive_offense'] = X[:, 0]
    neighbors_df['negative_offense'] = X[:, 1]
    neighbors_df['hustle_stats'] = X[:, 2]
    neighbors_df['defense_stats'] = X[:, 3]
    neighbors_df['alpha_measure'] = X[:, 4]
    neighbors_df['winning_factor'] = X[:, 5]
    neighbors_df['similar_player_1_id'] = similar_player_1_id_list
    neighbors_df['similar_player_1_season_end'] = similar_player_1_season_end_list
    neighbors_df['similar_player_1_name'] = similar_player_1_name_list
    neighbors_df['similar_player_1_distance'] = similar_player_1_distance_list
    neighbors_df['similar_player_2_id'] = similar_player_2_id_list
    neighbors_df['similar_player_2_season_end'] = similar_player_2_season_end_list
    neighbors_df['similar_player_2_name'] = similar_player_2_name_list
    neighbors_df['similar_player_2_distance'] = similar_player_2_distance_list
    neighbors_df['similar_player_3_id'] = similar_player_3_id_list
    neighbors_df['similar_player_3_season_end'] = similar_player_3_season_end_list
    neighbors_df['similar_player_3_name'] = similar_player_3_name_list
    neighbors_df['similar_player_3_distance'] = similar_player_3_distance_list
    neighbors_df['similar_player_4_id'] = similar_player_4_id_list
    neighbors_df['similar_player_4_season_end'] = similar_player_4_season_end_list
    neighbors_df['similar_player_4_name'] = similar_player_4_name_list
    neighbors_df['similar_player_4_distance'] = similar_player_4_distance_list
    neighbors_df['similar_player_5_id'] = similar_player_5_id_list
    neighbors_df['similar_player_5_season_end'] = similar_player_5_season_end_list
    neighbors_df['similar_player_5_name'] = similar_player_5_name_list
    neighbors_df['similar_player_5_distance'] = similar_player_5_distance_list
    # print('neighbors')
    # print(neighbors_df)
    # print('player info')
    # print(y)
    # print(y[y['player_id']==1629011])
    # print(neighbors_df[neighbors_df['player_id']==1629011])
    neighbors_df = pd.merge(y, neighbors_df, left_index=True, right_index=True)
    # print(neighbors_df)
    # print(neighbors_df.sort_values('positive_offense', ascending= False))

    neighbors_df['positive_offense'] = neighbors_df['positive_offense'].values.astype(int)
    neighbors_df['negative_offense'] = neighbors_df['negative_offense'].values.astype(int)
    neighbors_df['hustle_stats'] = neighbors_df['hustle_stats'].values.astype(int)
    neighbors_df['defense_stats'] = neighbors_df['defense_stats'].values.astype(float)
    neighbors_df['alpha_measure'] = neighbors_df['alpha_measure'].values.astype(float)
    neighbors_df['winning_factor'] = neighbors_df['winning_factor'].values.astype(float)

    similarity_rank_1_df = pd.DataFrame()
    similarity_rank_2_df = pd.DataFrame()
    similarity_rank_3_df = pd.DataFrame()
    similarity_rank_4_df = pd.DataFrame()
    similarity_rank_5_df = pd.DataFrame()

    similarity_rank_1_df['distance'] = neighbors_df['similar_player_1_distance']
    similarity_rank_2_df['distance'] = neighbors_df['similar_player_2_distance']
    similarity_rank_3_df['distance'] = neighbors_df['similar_player_3_distance']
    similarity_rank_4_df['distance'] = neighbors_df['similar_player_4_distance']
    similarity_rank_5_df['distance'] = neighbors_df['similar_player_5_distance']

    similarity_rank_1_df['rank'] = 1
    similarity_rank_2_df['rank'] = 2
    similarity_rank_3_df['rank'] = 3
    similarity_rank_4_df['rank'] = 4
    similarity_rank_5_df['rank'] = 5

    similarity_rank_df = similarity_rank_1_df.append([similarity_rank_2_df, similarity_rank_3_df, similarity_rank_4_df, similarity_rank_5_df]).reset_index()
    #print(similarity_rank_df)
    similarity_rank_df = similarity_rank_df.sort_values('distance')
    similarity_rank_df = similarity_rank_df.reset_index(drop=True).reset_index()
    #print(similarity_rank_df)
    similarity_rank_df = similarity_rank_df.rename(columns={"level_0":"similarity_rank"})
    similarity_rank_df = similarity_rank_df[['index', 'similarity_rank', 'rank']]

    similarity_rank_1_df = similarity_rank_df[similarity_rank_df['rank'] == 1]
    similarity_rank_2_df = similarity_rank_df[similarity_rank_df['rank'] == 2]
    similarity_rank_3_df = similarity_rank_df[similarity_rank_df['rank'] == 3]
    similarity_rank_4_df = similarity_rank_df[similarity_rank_df['rank'] == 4]
    similarity_rank_5_df = similarity_rank_df[similarity_rank_df['rank'] == 5]

    similarity_rank_1_df = similarity_rank_1_df.rename(columns={"similarity_rank": "similarity_rank_1"})
    similarity_rank_2_df = similarity_rank_2_df.rename(columns={"similarity_rank": "similarity_rank_2"})
    similarity_rank_3_df = similarity_rank_3_df.rename(columns={"similarity_rank": "similarity_rank_3"})
    similarity_rank_4_df = similarity_rank_4_df.rename(columns={"similarity_rank": "similarity_rank_4"})
    similarity_rank_5_df = similarity_rank_5_df.rename(columns={"similarity_rank": "similarity_rank_5"})

    #print(similarity_rank_1_df.sort_values('index'))
    #print(similarity_rank_2_df.sort_values('index'))
    neighbors_df = neighbors_df.reset_index()
    #print(neighbors_df)
    #print(len(neighbors_df))

    neighbors_df = pd.merge(neighbors_df, similarity_rank_1_df[['index', 'similarity_rank_1']], on='index')
    neighbors_df = pd.merge(neighbors_df, similarity_rank_2_df[['index', 'similarity_rank_2']], on='index')
    neighbors_df = pd.merge(neighbors_df, similarity_rank_3_df[['index', 'similarity_rank_3']], on='index')
    neighbors_df = pd.merge(neighbors_df, similarity_rank_4_df[['index', 'similarity_rank_4']], on='index')
    neighbors_df = pd.merge(neighbors_df, similarity_rank_5_df[['index', 'similarity_rank_5']], on='index')

    # print(neighbors_df[neighbors_df['player_id']==1629011])
    #print(len(neighbors_df))
    #print(neighbors_df.columns)

    sql_table = 'nearest_neighbors'
    conn = pg2.connect(dbname = 'postgres', host = "localhost")
    conn.autocommit = True
    engine = create_engine('postgresql+psycopg2://jacobpress:bocaj29@localhost/wins_contr')
    neighbors_df.to_sql(sql_table, con = engine, if_exists='replace', index=False)
    conn.close()

    # km = KMeans(n_clusters=5, random_state=0, n_init=10, algorithm='full')
    # y_km = km.fit_predict(X_scaled)
    #kg_predict = y_km.score(kmeans_df[0].iloc[0].to_numpy())
    #print(kg_predict)
    #print(y_km.cluster_centers_)

#calculate distortion for a range of number of cluster
    # distortions = []
    # for i in range(1, 20):
    #     km = KMeans(
    #         n_clusters=i, init='random',
    #         n_init=10, max_iter=300,
    #         tol=1e-04, random_state=0
    #     )
    #     km.fit(X_scaled)
    #     distortions.append(km.inertia_)

    # # plot
    # plt.plot(range(1, 20), distortions, marker='o')
    # plt.xlabel('Number of clusters')
    # plt.ylabel('Distortion')
    # plt.show()

    # plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=y_km, s=50, cmap='viridis')

    # centers = km.cluster_centers_
    # plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5)
    # plt.show()

nearest_neighbors_df(clustering_df())


from __future__ import annotations

from dataclasses import dataclass, field
from typing import List, ClassVar

from backend.city import CITIES, City
from backend.constants import COLORS
from backend.board import Board
from backend.card import Card
from backend import card

@dataclass
class Player:
    ROLES: ClassVar[dict] = {1: "Doctor",
                             2: "Quarentine Expert",
                             3: "Travel Agent",
                             4: "Operations Expert",
                             5: "Scientist",
                             6: "Researcher",
                             7: "Contingency Expert"}
    id_: int
    role: int
    board: Board
    location: City = None
    cards: List[Card] = field(default_factory=list)

    def __post_init__(self):
        if self.location is None:
            self.location = self.board.locations[0]

    def possible_moves(self) -> List[City]:
        possible = list()
        possible += self.location.connections
        if self.location.research_center:
            possible += self.board.get_research_centers()
        if self.location in possible:
            possible.remove(self.location)
        possible = list(set(possible))
        possible.sort()
        return list(possible)

    def possible_travel_from(self) -> bool:
        return self.location in [_card.city for _card in self.cards if
                                 _card.city is not None]

    def possible_travel_to(self) -> List[City]:
        return [_card.city for _card in self.cards if _card.city is not None]

    def move(self, location: City):
        self.location = location

    def add_card(self, card: Card) -> int:
        self.cards.append(card)
        return len(self.cards)

    def serialize(self) -> dict:
        return {'id': self.id_,
                'role': Player.ROLES.get(self.role),
                'location': self.location.id_,
                'possible_moves': [{'name': city.name, 'id': city.id_} for city
                                   in self.possible_moves()],
                'cards': [card.serialize() for card in self.cards]}

    @classmethod
    def deserialize(cls, data: dict, board: Board) -> Player:
        player_id = data['id']
        player_role = {Player.ROLES[key]: key for key in Player.ROLES}[
            data['role']]
        player_location = board.locations[data['location']]
        player_cards = list()
        for serialized_card in data['cards']:
            deserialized_card = Card.deserialize(serialized_card)
            player_cards.append(deserialized_card)
        player = Player(id_=player_id, role=player_role, board=board,
                        location=player_location)
        player.cards = player_cards
        return player


"""
********* IDIOMASH *********

Create idioms by mashing up old ones! Nothing new under the weather.

Input: text file with each candidate on a separate line

Output: `mashups_for_seq_with_lines` returns a set

Usage:
`lines = lines_from_path('some/save/path/here.txt')`
`mashups = mashups_for_seq_with_lines('and', lines)`

You can run `prompt_loop(9)` to start a user input loop
in which you select a candidate sequence and see up to 9 (or
any #) randomly chosen mashups pivoting on that sequence.

"""

import random

# recursively find the start and end indices
# of all occurrences of a sequence in a line of text
def seq_indices_in_text(seq, text, offset=0):
	if seq in text:
		start = text.index(seq) + offset	# start of 1st occurrence
		end = start + len(seq)				# start of 2nd occurrence
		return [(start, end)] + seq_indices_in_text(seq, text[start+1:], offset=offset+start+1)
	else:
		return []

# given a sequence and a list of lines, find all novel
# mashups between two lines that pivot on the sequence
# returns a set
def mashups_for_seq_with_lines(seq, lines):

	befores = set([])
	afters = set([])

	for line in lines:
		seq_indices = seq_indices_in_text(seq, line)
		for start, end in seq_indices:
			befores.add(line[:start])
			afters.add(line[end:])

	# insert seq between all (before, after) combinations
	mashups = set([before + seq + after for before in befores for after in afters])

	return mashups - set(lines)		# exclude reconstructed originals

################ UTILS ################

def lines_from_path(path):
	with open(path) as f:
		lines = [line.strip().lower() for line in f.readlines()]
		return [line for line in lines if len(line) > 0]

def print_random_n(mashups, n=10):
	mashups_list = list(mashups)
	random.shuffle(mashups_list)
	print('\n' +' \n'.join(mashups_list[:n]) + '\n')

################ UX ################

def prompt_loop(n=20):
	lines = lines_from_path('text/idioms.txt')
	while True:
		choice = input('Enter a sequence:\n')
		if choice in ['q','x']:
			break
		mashups = mashups_for_seq_with_lines(choice, lines)
		print_random_n(mashups, n)

################ TESTS ################

def fruit_test():
	lines = lines_from_path('text/idioms.txt')
	fruit_mashups = mashups_for_seq_with_lines('fruit', lines)
	print(fruit_mashups)

def favorites_test():
	lines = lines_from_path('text/idioms.txt')
	favorites = ['you can', 'of the']
	for fav in favorites:
		mashups = mashups_for_seq_with_lines(fav, lines)
		print_random_n(mashups, 10)

################XXXXXXXXX################

if __name__ == '__main__':

	lines = lines_from_path('text/idioms.txt')
	fruit_test()
	prompt_loop(20)


# -*- coding: utf-8 -*-
"""
Created on Wed May  6 00:34:56 2020

@author: alexi
"""

#https://codeforces.com/problemset/problem/1177/A --- Alexis Galvan

def digits_sequence():

    number = int(input())

    if number < 10:
        return number

    string = ''
    temp = 1

    while len(string) < number:
        string += str(temp)
        temp += 1

    return string[number-1]

A = digits_sequence()
print(A)


import sys
import os
import time

import torch
import pytorch_lightning as pl
from pytorch_lightning import Trainer, TrainResult, EvalResult, seed_everything
from pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping  # , LearningRateLogger
from pytorch_lightning.loggers import TensorBoardLogger
from pytorch_lightning.callbacks import Callback

from config import config
from utils import get_logger
from data import DataModule

logger = get_logger(config)

torch.set_printoptions(precision=5)
seed_everything(42)

class TimeCallback(Callback):
    def on_epoch_start(self, trainer, pl_module):
        self.tc = time.perf_counter()

    def on_epoch_end(self, trainer, pl_module):
        logger.debug(f"Elapsed Time {time.perf_counter()-self.tc:.4f}")
        self.tc = time.perf_counter()

class Net(pl.LightningModule):
    def __init__(self, hparams):
        super(Net, self).__init__()
        self.hparams = hparams

        # "init model"
        hp = self.hparams
        if hp.model == "Model0":
            # self.model =
            pass

    def forward(self, x):
        pass

    # def _my_reduce_fx(self, all_train_loss):
    #     # "reducde somehow"
    #     result = None
    #     return result

    def training_step(self, batch, batch_idx):
        loss = None
        result = TrainResult(minimize=loss,
                             early_stop_on=None,
                             checkpoint_on=None,
                             hiddens=None)
        result.log("train/loss",
                   loss,
                   prog_bar=False,
                   logger=True,
                   on_step=True,
                   on_epoch=False,
                   reduce_fx=torch.mean,
                   tbptt_reduce_fx=torch.mean,
                   tbptt_pad_token=0,
                   enable_graph=False,
                   sync_dist=False,
                   sync_dist_op='mean',
                   sync_dist_group=None)
        values = dict(train_acc=None, train_metric=None)
        result.log_dict(values,
                        prog_bar=False,
                        logger=True,
                        on_step=False,
                        on_epoch=True,
                        reduce_fx=torch.mean,
                        tbptt_reduce_fx=torch.mean,
                        tbptt_pad_token=0,
                        enable_graph=False,
                        sync_dist=False,
                        sync_dist_op='mean',
                        sync_dist_group=None)
        return result

    def validation_step(self, batch, batch_idx):
        acc = None
        result = EvalResult(early_stop_on=None,
                            checkpoint_on=acc,
                            hiddens=None)

        result.a_prediction = None

        result.log("val/acc",
                   acc,
                   prog_bar=False,
                   logger=True,
                   on_step=False,
                   on_epoch=True,
                   reduce_fx=torch.mean,
                   tbptt_reduce_fx=torch.mean,
                   tbptt_pad_token=0,
                   enable_graph=False,
                   sync_dist=False,
                   sync_dist_op='mean',
                   sync_dist_group=None)
        values = {"val/metirc": None}
        result.log_dict(values,
                        prog_bar=False,
                        logger=True,
                        on_step=False,
                        on_epoch=True,
                        reduce_fx=torch.mean,
                        tbptt_reduce_fx=torch.mean,
                        tbptt_pad_token=0,
                        enable_graph=False,
                        sync_dist=False,
                        sync_dist_op='mean',
                        sync_dist_group=None)
        return result

    def validation_epoch_end(self, outputs):
        all_a_predictions = outputs.a_prediction
        # "do something with the predictons from all validation_steps"

        return outputs

    def test_step(self, batch, batch_idx):
        result = self.validation_step(batch, batch_idx)
        result.rename_keys({
            "val/acc": "test/acc",
            "val/metric": "test/metric"
        })
        return result

    def test_epoch_end(self, outputs):
        return self.validation_epoch_end(outputs)

    def configure_optimizers(self):
        if self.hparams.opt == "adamw":
            return torch.optim.AdamW(self.parameters(),
                                     lr=self.hparams.lr,
                                     weight_decay=self.hparams.wd)
        elif self.hparams.opt == "adam":
            return torch.optim.Adam(self.parameters(),
                                    lr=self.hparams.lr,
                                    weight_decay=self.hparams.wd)
        elif self.hparams.opt == "sgd":
            return torch.optim.SGD(self.parameters(),
                                   lr=self.hparams.lr,
                                   weight_decay=self.hparams.wd,
                                   momentum=0.9)

def main(config):
    logger.info(" ".join(sys.argv))

    dm = DataModule(config, logger)
    dm.prepare_data()

    logger.info(f"{'='*42} \n\t {config} \n\t {'='*42}")

    def train():
        model = Net(config)

        # "checkpoint"
        checkpoint_callback = ModelCheckpoint(
            filepath=os.path.join(
                os.getcwd(),
                "runs/ckpts/" + config.model + "/{epoch}-{val_acc:.4f}"),
            save_top_k=1,
            verbose=True,
            monitor='val_acc',
            mode='max',
        )

        # "tensorboard logger"
        tb_logger = TensorBoardLogger(
            "runs/logs",
            name=config.model,
        )

        early_stopping = EarlyStopping(monitor='val_acc',
                                       patience=5,
                                       strict=False,
                                       verbose=False,
                                       mode='max')
        if config.tpu:
            gpus = None
        else:
            gpus = config.gpus if not config.nogpu else None

        distributed_backend = None
        if len(config.gpus.split(",")) > 1 or config.gpus == "-1":
            distributed_backend = "ddp"

        trainer = Trainer(
            fast_dev_run=config.dbg,
            gpus=gpus,
            tpu_cores=8 if config.tpu else None,
            max_epochs=config.epos,
            check_val_every_n_epoch=config.check_val,
            checkpoint_callback=checkpoint_callback,
            early_stop_callback=early_stopping,
            num_sanity_val_steps=0,
            progress_bar_refresh_rate=config.pb_rate,
            distributed_backend=distributed_backend,
            logger=tb_logger,
            # callbacks=[TimeCallback()],
            # resume_from_checkpoint="ckpts/foo.ckpt"
        )

        if config.lr_find:
            dm.setup("fit")
            lr_finder = trainer.lr_find(model, dm.dl_trn, dm.dl_val)
            # "Inspect results"
            # fig = lr_finder.plot(); fig.show(); fig.savefig("lr_finder.png")
            suggested_lr = lr_finder.suggestion()
            logger.info("suggested_lr: ", suggested_lr)
        else:
            dm.setup("fit")
            trainer.fit(model, datamodule=dm)
            logger.success("training finish!")
            dm.setup("test")
            trainer.test(datamodule=dm)

    train()

if __name__ == "__main__":
    main(config)


from time import sleep
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from pars_wb.settings import service

from dotenv import load_dotenv

from pars_wb.config import logger

from pars_wb.constants import PAUSE_DURATION_SECONDS, ERROR_SEARCHE_MESSAGE, LOG_DEBAG_PARS_ELEMENT

load_dotenv()

try:
    URL = os.getenv("URL")
except KeyError as error:
    logger.critical(f"Отсутствует обязательная переменная окружения: {error}!")
    quit()

def get_product(articul, product):
    driver = webdriver.Chrome(service=service)
    driver.get(URL)
    driver.maximize_window()
    sleep(PAUSE_DURATION_SECONDS)
    try:
        search_input = driver.find_element(
            By.XPATH, '//div[@class="search-catalog__block"]/input'
        )
    except NoSuchElementException as error:
        logger.critical(f"{LOG_DEBAG_PARS_ELEMENT}. {error}")
    search_input.click()
    search_input.send_keys(product)
    search_input.send_keys(Keys.ENTER)

    sleep(PAUSE_DURATION_SECONDS)
    count_page = 0
    for p in range(101):
        try:
            count_page += 1
            card = driver.find_element(
                By.XPATH,
                (
                    '//div[@class="product-card-list"]'
                    f'//div[@data-popup-nm-id={articul}]'
                ),
            )
            item_number = card.get_attribute("data-card-index")
            card.click()
            if count_page <= 100:
                return (f"Ваш товар находится на странице №{count_page}, "
                        f"на позиции №{int(item_number) + 1}")
            else:
                return ERROR_SEARCHE_MESSAGE
        except NoSuchElementException:
            next_page = driver.find_element(
                By.XPATH, '//a[@class="pagination-next pagination__next"]'
            )
            next_page.click()
        sleep(PAUSE_DURATION_SECONDS)
    driver.quit()


import django_filters
from django import forms

from .models import Standard,Lesson,Subject

class ClassFilter(django_filters.FilterSet):

    category = django_filters.ModelChoiceFilter(
        queryset=Standard.objects.all(),
        empty_label="All Categories",
        label="Categories",
        widget=forms.Select(attrs={'class': 'form-control'}),
        )

    class Meta:
        model = Standard
        fields = ["__all__"]


import asyncio
import threading
import uuid
from datetime import datetime

from Client import Client
from threading import Thread
from multiprocessing.pool import ThreadPool
import sched, time
from Event import Event

class Conversation:
    def __init__(self, conversation_timeout, port):
        pool = ThreadPool(processes=2)

        self.port = port
        self.conversation_timeout = conversation_timeout
        self.id = uuid.uuid4()
        self.on_client_message = Event()
        self.on_conversation_end = Event()
        self.client1 = None
        self.client2 = None
        self.setup_clients()
        self.client1.remote_user = self.client2.local_user
        self.client2.remote_user = self.client1.local_user
        self.is_conversation_running = True
        self.conversation_start_time = datetime.now()
        self.conversation_end_time = None
        self.last_message_time = time.time()
        threading.Timer(1.0, self.check_timeout).start()

    def check_timeout(self):
        if time.time() - self.last_message_time >= self.conversation_timeout:
            self.end_conversation("timeout")
        if self.is_conversation_running:
            threading.Timer(1.0, self.check_timeout).start()

    def send_message_from_console(self):
        message = input()
        self.client1.send_message(message)
        self.client2.send_message(message)
        self.send_message_from_console()

    def setup_clients(self):
        self.client1 = self.setup_client()
        self.client2 = self.setup_client()
        Thread(target=self.client1.start_client).start()
        Thread(target=self.client2.start_client).start()

    def setup_client(self):
        client = Client(asyncio.new_event_loop())
        client.on_message_event.append(self.on_message)
        client.on_disconnect_event.append(self.on_disconnect)
        client.on_conversation_start_event.append(self.on_conversation_start)
        client.on_is_typing_event.append(self.on_is_typing)
        return client

    def on_conversation_start(self):
        if self.client1.cid is not None and self.client2.cid is not  None:
            print("conversation_start")
            if self.client1.cid == self.client2.cid:
                self.end_conversation("Same conversation")

    def on_message(self, message):
        if not message.is_remote:
            print(str(message.author.id)+":"+message.message)
            self.on_client_message(message)
            if message.author.id == self.client1.local_user.id:
                self.client2.compose_message(message.message)
            else:
                self.client1.compose_message(message.message)
            self.last_message_time = time.time()

    def on_is_typing(self, author_id, state):
        if author_id == self.client1.local_user.id:
            self.client2.send_typing_message(state)
        else:
            self.client1.send_typing_message(state)

    def on_disconnect(self, client):
        self.end_conversation("Ended by "+str(client.local_user.id))

    def end_conversation(self, reason):
        self.is_conversation_running = False
        self.client1.end_conversation()
        self.client2.end_conversation()
        self.conversation_end_time = datetime.now()
        self.on_conversation_end(reason, self)


import time, threading
from Operations import available_doc, update_history, check_waiting_queue

problems_dict = {
    "Headache": 300,
    "Fever": 200,
    "Dialysis": 400,
    "Bloodwork": 100,
    "Fracture": 350,
    "Eye-checkup": 450,
}

# Comes from create_patient
def assign_doctor(patient, doctors_list, waiting_queue, patients_list):
    avl_doc = available_doc(doctors_list)
    if avl_doc is not None:
        thread = threading.Thread(
            target=treatment, args=(patient, avl_doc, patients_list, waiting_queue)
        )
        thread.start()

    else:
        with open("results.txt", "a") as f:
            f.write("\nPatient {} added to waiting_queue\n".format(patient.name))
        waiting_queue.append(patient)
        patient.inqueue = True

def treatment(patient, avl_doc, patients_list, waiting_queue):
    avl_doc.isfree = False
    patient.isattending = True
    f = open("results.txt", "a")
    f.write("\nPatient {} is attending".format(patient.name))
    f.write(
        "\n{} on-duty with {} for {}mins.....\n".format(
            avl_doc.name, patient.name, patient.visit_details[-1]["duration"]
        )
    )
    f.close()

    time.sleep(patient.visit_details[-1]["duration"])  # ----------------------Treatment

    this_visit = patient.visit_details[-1]
    this_visit["visited_by_doc"] = avl_doc.name
    this_visit["bill"] = problems_dict[this_visit["problem"]]
    with open("results.txt", "a") as f:
        f.write("\nPatient {} exited, {} is free\n".format(patient.name, avl_doc.name))

    avl_doc.isfree = True
    patient.isattending = False
    patient.inqueue = False
    update_history(patients_list)

    check_waiting_queue(avl_doc, waiting_queue, treatment, patients_list)


from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication,QFrame,QPushButton,QMainWindow,QWidget,QSizeGrip,QGroupBox,QWidget,QHBoxLayout,QVBoxLayout,QLabel,QRadioButton

import sys
from PyQt5 import QtGui
from PyQt5.QtCore import QRect
from PyQt5 import QtCore
from PyQt5.QtGui import  QPixmap

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Frame"
        self.left = 500
        self.top = 200
        self.width = 300
        self.height = 250
        self.iconName = "icon.png"
        self.InitWindow()

    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon(self.iconName))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setStyleSheet('background-color:yellow')
        hbox  = QHBoxLayout()
        btn = QPushButton('Click Me')
        btn.setStyleSheet('color:white')
        btn.setStyleSheet(('background-color:green'))
        frame = QFrame()
        frame.setFrameShape(QFrame.StyledPanel)
        frame.setLineWidth(0.6)
        frame.setStyleSheet('background-color:red')
        hbox.addWidget(frame)
        hbox.addWidget(btn)
        self.setLayout(hbox)
        self.show()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec())