from __future__ import annotations

import errno
import inspect
import os
import socket as stdlib_socket
import sys
import tempfile
from socket import AddressFamily, SocketKind
from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Union

import attr
import pytest

from .. import _core, socket as tsocket
from .._core._tests.tutil import binds_ipv6, creates_ipv6
from .._highlevel_socket import SocketStream
from .._socket import _NUMERIC_ONLY, SocketType, _SocketType, _try_sync
from ..testing import assert_checkpoints, wait_all_tasks_blocked

if TYPE_CHECKING:
    from typing_extensions import TypeAlias

    GaiTuple: TypeAlias = Tuple[
        AddressFamily,
        SocketKind,
        int,
        str,
        Union[Tuple[str, int], Tuple[str, int, int, int]],
    ]
    getaddrinfoResponse: TypeAlias = List[GaiTuple]
else:
    GaiTuple: object
    getaddrinfoResponse = object

################################################################
# utils
################################################################

class MonkeypatchedGAI:
    def __init__(self, orig_getaddrinfo: Callable[..., getaddrinfoResponse]):
        self._orig_getaddrinfo = orig_getaddrinfo
        self._responses: dict[tuple[Any, ...], getaddrinfoResponse | str] = {}
        self.record: list[tuple[Any, ...]] = []

    # get a normalized getaddrinfo argument tuple
    def _frozenbind(self, *args: Any, **kwargs: Any) -> tuple[Any, ...]:
        sig = inspect.signature(self._orig_getaddrinfo)
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        frozenbound = bound.args
        assert not bound.kwargs
        return frozenbound

    def set(
        self, response: getaddrinfoResponse | str, *args: Any, **kwargs: Any
    ) -> None:
        self._responses[self._frozenbind(*args, **kwargs)] = response

    def getaddrinfo(self, *args: Any, **kwargs: Any) -> getaddrinfoResponse | str:
        bound = self._frozenbind(*args, **kwargs)
        self.record.append(bound)
        if bound in self._responses:
            return self._responses[bound]
        elif bound[-1] & stdlib_socket.AI_NUMERICHOST:
            return self._orig_getaddrinfo(*args, **kwargs)
        else:
            raise RuntimeError(f"gai called with unexpected arguments {bound}")

@pytest.fixture
def monkeygai(monkeypatch: pytest.MonkeyPatch) -> MonkeypatchedGAI:
    controller = MonkeypatchedGAI(stdlib_socket.getaddrinfo)
    monkeypatch.setattr(stdlib_socket, "getaddrinfo", controller.getaddrinfo)
    return controller

async def test__try_sync() -> None:
    with assert_checkpoints():
        async with _try_sync():
            pass

    with assert_checkpoints():
        with pytest.raises(KeyError):
            async with _try_sync():
                raise KeyError

    async with _try_sync():
        raise BlockingIOError

    def _is_ValueError(exc: BaseException) -> bool:
        return isinstance(exc, ValueError)

    async with _try_sync(_is_ValueError):
        raise ValueError

    with assert_checkpoints():
        with pytest.raises(BlockingIOError):
            async with _try_sync(_is_ValueError):
                raise BlockingIOError

################################################################
# basic re-exports
################################################################

def test_socket_has_some_reexports() -> None:
    assert tsocket.SOL_SOCKET == stdlib_socket.SOL_SOCKET
    assert tsocket.TCP_NODELAY == stdlib_socket.TCP_NODELAY
    assert tsocket.gaierror == stdlib_socket.gaierror
    assert tsocket.ntohs == stdlib_socket.ntohs

################################################################
# name resolution
################################################################

async def test_getaddrinfo(monkeygai: MonkeypatchedGAI) -> None:
    def check(got: getaddrinfoResponse, expected: getaddrinfoResponse) -> None:
        # win32 returns 0 for the proto field
        # musl and glibc have inconsistent handling of the canonical name
        # field (https://github.com/python-trio/trio/issues/1499)
        # Neither field gets used much and there isn't much opportunity for us
        # to mess them up, so we don't bother checking them here
        def interesting_fields(
            gai_tup: GaiTuple,
        ) -> tuple[
            AddressFamily,
            SocketKind,
            tuple[str, int] | tuple[str, int, int] | tuple[str, int, int, int],
        ]:
            # (family, type, proto, canonname, sockaddr)
            family, type, proto, canonname, sockaddr = gai_tup
            return (family, type, sockaddr)

        def filtered(
            gai_list: getaddrinfoResponse,
        ) -> list[
            tuple[
                AddressFamily,
                SocketKind,
                tuple[str, int] | tuple[str, int, int] | tuple[str, int, int, int],
            ]
        ]:
            return [interesting_fields(gai_tup) for gai_tup in gai_list]

        assert filtered(got) == filtered(expected)

    # Simple non-blocking non-error cases, ipv4 and ipv6:
    with assert_checkpoints():
        res = await tsocket.getaddrinfo("127.0.0.1", "12345", type=tsocket.SOCK_STREAM)

    check(
        res,
        [
            (
                tsocket.AF_INET,  # 127.0.0.1 is ipv4
                tsocket.SOCK_STREAM,
                tsocket.IPPROTO_TCP,
                "",
                ("127.0.0.1", 12345),
            ),
        ],
    )

    with assert_checkpoints():
        res = await tsocket.getaddrinfo("::1", "12345", type=tsocket.SOCK_DGRAM)
    check(
        res,
        [
            (
                tsocket.AF_INET6,
                tsocket.SOCK_DGRAM,
                tsocket.IPPROTO_UDP,
                "",
                ("::1", 12345, 0, 0),
            ),
        ],
    )

    monkeygai.set("x", b"host", "port", family=0, type=0, proto=0, flags=0)
    with assert_checkpoints():
        res = await tsocket.getaddrinfo("host", "port")
    assert res == "x"
    assert monkeygai.record[-1] == (b"host", "port", 0, 0, 0, 0)

    # check raising an error from a non-blocking getaddrinfo
    with assert_checkpoints():
        with pytest.raises(tsocket.gaierror) as excinfo:
            await tsocket.getaddrinfo("::1", "12345", type=-1)
    # Linux + glibc, Windows
    expected_errnos = {tsocket.EAI_SOCKTYPE}
    # Linux + musl
    expected_errnos.add(tsocket.EAI_SERVICE)
    # macOS
    if hasattr(tsocket, "EAI_BADHINTS"):
        expected_errnos.add(tsocket.EAI_BADHINTS)
    assert excinfo.value.errno in expected_errnos

    # check raising an error from a blocking getaddrinfo (exploits the fact
    # that monkeygai raises if it gets a non-numeric request it hasn't been
    # given an answer for)
    with assert_checkpoints():
        with pytest.raises(RuntimeError):
            await tsocket.getaddrinfo("asdf", "12345")

async def test_getnameinfo() -> None:
    # Trivial test:
    ni_numeric = stdlib_socket.NI_NUMERICHOST | stdlib_socket.NI_NUMERICSERV
    with assert_checkpoints():
        got = await tsocket.getnameinfo(("127.0.0.1", 1234), ni_numeric)
    assert got == ("127.0.0.1", "1234")

    # getnameinfo requires a numeric address as input:
    with assert_checkpoints():
        with pytest.raises(tsocket.gaierror):
            await tsocket.getnameinfo(("google.com", 80), 0)

    with assert_checkpoints():
        with pytest.raises(tsocket.gaierror):
            await tsocket.getnameinfo(("localhost", 80), 0)

    # Blocking call to get expected values:
    host, service = stdlib_socket.getnameinfo(("127.0.0.1", 80), 0)

    # Some working calls:
    got = await tsocket.getnameinfo(("127.0.0.1", 80), 0)
    assert got == (host, service)

    got = await tsocket.getnameinfo(("127.0.0.1", 80), tsocket.NI_NUMERICHOST)
    assert got == ("127.0.0.1", service)

    got = await tsocket.getnameinfo(("127.0.0.1", 80), tsocket.NI_NUMERICSERV)
    assert got == (host, "80")

################################################################
# constructors
################################################################

async def test_from_stdlib_socket() -> None:
    sa, sb = stdlib_socket.socketpair()
    assert not isinstance(sa, tsocket.SocketType)
    with sa, sb:
        ta = tsocket.from_stdlib_socket(sa)
        assert isinstance(ta, tsocket.SocketType)
        assert sa.fileno() == ta.fileno()
        await ta.send(b"x")
        assert sb.recv(1) == b"x"

    # rejects other types
    with pytest.raises(TypeError):
        tsocket.from_stdlib_socket(1)  # type: ignore[arg-type]

    class MySocket(stdlib_socket.socket):
        pass

    with MySocket() as mysock:
        with pytest.raises(TypeError):
            tsocket.from_stdlib_socket(mysock)

async def test_from_fd() -> None:
    sa, sb = stdlib_socket.socketpair()
    ta = tsocket.fromfd(sa.fileno(), sa.family, sa.type, sa.proto)
    with sa, sb, ta:
        assert ta.fileno() != sa.fileno()
        await ta.send(b"x")
        assert sb.recv(3) == b"x"

async def test_socketpair_simple() -> None:
    async def child(sock: SocketType) -> None:
        print("sending hello")
        await sock.send(b"h")
        assert await sock.recv(1) == b"h"

    a, b = tsocket.socketpair()
    with a, b:
        async with _core.open_nursery() as nursery:
            nursery.start_soon(child, a)
            nursery.start_soon(child, b)

@pytest.mark.skipif(not hasattr(tsocket, "fromshare"), reason="windows only")
async def test_fromshare() -> None:
    if TYPE_CHECKING and sys.platform != "win32":  # pragma: no cover
        return
    a, b = tsocket.socketpair()
    with a, b:
        # share with ourselves
        shared = a.share(os.getpid())
        a2 = tsocket.fromshare(shared)
        with a2:
            assert a.fileno() != a2.fileno()
            await a2.send(b"x")
            assert await b.recv(1) == b"x"

async def test_socket() -> None:
    with tsocket.socket() as s:
        assert isinstance(s, tsocket.SocketType)
        assert s.family == tsocket.AF_INET

@creates_ipv6
async def test_socket_v6() -> None:
    with tsocket.socket(tsocket.AF_INET6, tsocket.SOCK_DGRAM) as s:
        assert isinstance(s, tsocket.SocketType)
        assert s.family == tsocket.AF_INET6

@pytest.mark.skipif(not sys.platform == "linux", reason="linux only")
async def test_sniff_sockopts() -> None:
    from socket import AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM

    # generate the combinations of families/types we're testing:
    sockets = []
    for family in [AF_INET, AF_INET6]:
        for type in [SOCK_DGRAM, SOCK_STREAM]:
            sockets.append(stdlib_socket.socket(family, type))
    for socket in sockets:
        # regular Trio socket constructor
        tsocket_socket = tsocket.socket(fileno=socket.fileno())
        # check family / type for correctness:
        assert tsocket_socket.family == socket.family
        assert tsocket_socket.type == socket.type
        tsocket_socket.detach()

        # fromfd constructor
        tsocket_from_fd = tsocket.fromfd(socket.fileno(), AF_INET, SOCK_STREAM)
        # check family / type for correctness:
        assert tsocket_from_fd.family == socket.family
        assert tsocket_from_fd.type == socket.type
        tsocket_from_fd.close()

        socket.close()

################################################################
# _SocketType
################################################################

async def test_SocketType_basics() -> None:
    sock = tsocket.socket()
    with sock as cm_enter_value:
        assert cm_enter_value is sock
        assert isinstance(sock.fileno(), int)
        assert not sock.get_inheritable()
        sock.set_inheritable(True)
        assert sock.get_inheritable()

        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)
        assert not sock.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)
        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, True)
        assert sock.getsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY)
    # closed sockets have fileno() == -1
    assert sock.fileno() == -1

    # smoke test
    repr(sock)

    # detach
    with tsocket.socket() as sock:
        fd = sock.fileno()
        assert sock.detach() == fd
        assert sock.fileno() == -1

    # close
    sock = tsocket.socket()
    assert sock.fileno() >= 0
    sock.close()
    assert sock.fileno() == -1

    # share was tested above together with fromshare

    # check __dir__
    assert "family" in dir(sock)
    assert "recv" in dir(sock)
    assert "setsockopt" in dir(sock)

    # our __getattr__ handles unknown names
    with pytest.raises(AttributeError):
        sock.asdf  # type: ignore[attr-defined]  # noqa: B018

    # type family proto
    stdlib_sock = stdlib_socket.socket()
    sock = tsocket.from_stdlib_socket(stdlib_sock)
    assert sock.type == stdlib_sock.type
    assert sock.family == stdlib_sock.family
    assert sock.proto == stdlib_sock.proto
    sock.close()

async def test_SocketType_setsockopt() -> None:
    sock = tsocket.socket()
    with sock as _:
        setsockopt_tests(sock)

def setsockopt_tests(sock: SocketType | SocketStream) -> None:
    """Extract these out, to be reused for SocketStream also."""
    # specifying optlen. Not supported on pypy, and I couldn't find
    # valid calls on darwin or win32.
    if hasattr(tsocket, "SO_BINDTODEVICE"):
        sock.setsockopt(tsocket.SOL_SOCKET, tsocket.SO_BINDTODEVICE, None, 0)

    # specifying value
    sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False)

    # specifying both
    with pytest.raises(TypeError, match="invalid value for argument 'value'"):
        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, False, 5)  # type: ignore[call-overload]

    # specifying neither
    with pytest.raises(TypeError, match="invalid value for argument 'value'"):
        sock.setsockopt(tsocket.IPPROTO_TCP, tsocket.TCP_NODELAY, None)  # type: ignore[call-overload]

async def test_SocketType_dup() -> None:
    a, b = tsocket.socketpair()
    with a, b:
        a2 = a.dup()
        with a2:
            assert isinstance(a2, tsocket.SocketType)
            assert a2.fileno() != a.fileno()
            a.close()
            await a2.send(b"x")
            assert await b.recv(1) == b"x"

async def test_SocketType_shutdown() -> None:
    a, b = tsocket.socketpair()
    with a, b:
        await a.send(b"x")
        assert await b.recv(1) == b"x"
        assert not a.did_shutdown_SHUT_WR
        assert not b.did_shutdown_SHUT_WR
        a.shutdown(tsocket.SHUT_WR)
        assert a.did_shutdown_SHUT_WR
        assert not b.did_shutdown_SHUT_WR
        assert await b.recv(1) == b""
        await b.send(b"y")
        assert await a.recv(1) == b"y"

    a, b = tsocket.socketpair()
    with a, b:
        assert not a.did_shutdown_SHUT_WR
        a.shutdown(tsocket.SHUT_RD)
        assert not a.did_shutdown_SHUT_WR

    a, b = tsocket.socketpair()
    with a, b:
        assert not a.did_shutdown_SHUT_WR
        a.shutdown(tsocket.SHUT_RDWR)
        assert a.did_shutdown_SHUT_WR

@pytest.mark.parametrize(
    "address, socket_type",
    [
        ("127.0.0.1", tsocket.AF_INET),
        pytest.param("::1", tsocket.AF_INET6, marks=binds_ipv6),
    ],
)
async def test_SocketType_simple_server(
    address: str, socket_type: AddressFamily
) -> None:
    # listen, bind, accept, connect, getpeername, getsockname
    listener = tsocket.socket(socket_type)
    client = tsocket.socket(socket_type)
    with listener, client:
        await listener.bind((address, 0))
        listener.listen(20)
        addr = listener.getsockname()[:2]
        async with _core.open_nursery() as nursery:
            nursery.start_soon(client.connect, addr)
            server, client_addr = await listener.accept()
        with server:
            assert client_addr == server.getpeername() == client.getsockname()
            await server.send(b"x")
            assert await client.recv(1) == b"x"

async def test_SocketType_is_readable() -> None:
    a, b = tsocket.socketpair()
    with a, b:
        assert not a.is_readable()
        await b.send(b"x")
        await _core.wait_readable(a)
        assert a.is_readable()
        assert await a.recv(1) == b"x"
        assert not a.is_readable()

# On some macOS systems, getaddrinfo likes to return V4-mapped addresses even
# when we *don't* pass AI_V4MAPPED.
# https://github.com/python-trio/trio/issues/580
def gai_without_v4mapped_is_buggy() -> bool:  # pragma: no cover
    try:
        stdlib_socket.getaddrinfo("1.2.3.4", 0, family=stdlib_socket.AF_INET6)
    except stdlib_socket.gaierror:
        return False
    else:
        return True

@attr.s
class Addresses:
    bind_all: str = attr.ib()
    localhost: str = attr.ib()
    arbitrary: str = attr.ib()
    broadcast: str = attr.ib()

# Direct thorough tests of the implicit resolver helpers
@pytest.mark.parametrize(
    "socket_type, addrs",
    [
        (
            tsocket.AF_INET,
            Addresses(
                bind_all="0.0.0.0",
                localhost="127.0.0.1",
                arbitrary="1.2.3.4",
                broadcast="255.255.255.255",
            ),
        ),
        pytest.param(
            tsocket.AF_INET6,
            Addresses(
                bind_all="::",
                localhost="::1",
                arbitrary="1::2",
                broadcast="::ffff:255.255.255.255",
            ),
            marks=creates_ipv6,
        ),
    ],
)
async def test_SocketType_resolve(socket_type: AddressFamily, addrs: Addresses) -> None:
    v6 = socket_type == tsocket.AF_INET6

    def pad(addr: tuple[str | int, ...]) -> tuple[str | int, ...]:
        if v6:
            while len(addr) < 4:
                addr += (0,)
        return addr

    def assert_eq(
        actual: tuple[str | int, ...], expected: tuple[str | int, ...]
    ) -> None:
        assert pad(expected) == pad(actual)

    with tsocket.socket(family=socket_type) as sock:
        # testing internal functionality, so we check it against the internal type
        assert isinstance(sock, _SocketType)

        # For some reason the stdlib special-cases "" to pass NULL to
        # getaddrinfo. They also error out on None, but whatever, None is much
        # more consistent, so we accept it too.
        # TODO: this implies that we can send host=None, but what does that imply for the return value, and other stuff?
        for null in [None, ""]:
            got = await sock._resolve_address_nocp((null, 80), local=True)
            assert not isinstance(got, (str, bytes))
            assert_eq(got, (addrs.bind_all, 80))
            got = await sock._resolve_address_nocp((null, 80), local=False)
            assert not isinstance(got, (str, bytes))
            assert_eq(got, (addrs.localhost, 80))

        # AI_PASSIVE only affects the wildcard address, so for everything else
        # local=True/local=False should work the same:
        for local in [False, True]:

            async def res(
                args: tuple[str, int]
                | tuple[str, int, int]
                | tuple[str, int, int, int]
                | tuple[str, str]
                | tuple[str, str, int]
                | tuple[str, str, int, int]
            ) -> Any:
                return await sock._resolve_address_nocp(
                    args,
                    local=local,  # noqa: B023  # local is not bound in function definition
                )

            assert_eq(await res((addrs.arbitrary, "http")), (addrs.arbitrary, 80))
            if v6:
                # Check handling of different length ipv6 address tuples
                assert_eq(await res(("1::2", 80)), ("1::2", 80, 0, 0))
                assert_eq(await res(("1::2", 80, 0)), ("1::2", 80, 0, 0))
                assert_eq(await res(("1::2", 80, 0, 0)), ("1::2", 80, 0, 0))
                # Non-zero flowinfo/scopeid get passed through
                assert_eq(await res(("1::2", 80, 1)), ("1::2", 80, 1, 0))
                assert_eq(await res(("1::2", 80, 1, 2)), ("1::2", 80, 1, 2))

                # And again with a string port, as a trick to avoid the
                # already-resolved address fastpath and make sure we call
                # getaddrinfo
                assert_eq(await res(("1::2", "80")), ("1::2", 80, 0, 0))
                assert_eq(await res(("1::2", "80", 0)), ("1::2", 80, 0, 0))
                assert_eq(await res(("1::2", "80", 0, 0)), ("1::2", 80, 0, 0))
                assert_eq(await res(("1::2", "80", 1)), ("1::2", 80, 1, 0))
                assert_eq(await res(("1::2", "80", 1, 2)), ("1::2", 80, 1, 2))

                # V4 mapped addresses resolved if V6ONLY is False
                sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, False)
                assert_eq(await res(("1.2.3.4", "http")), ("::ffff:1.2.3.4", 80))

            # Check the <broadcast> special case, because why not
            assert_eq(await res(("<broadcast>", 123)), (addrs.broadcast, 123))

            # But not if it's true (at least on systems where getaddrinfo works
            # correctly)
            if v6 and not gai_without_v4mapped_is_buggy():
                sock.setsockopt(tsocket.IPPROTO_IPV6, tsocket.IPV6_V6ONLY, True)
                with pytest.raises(tsocket.gaierror) as excinfo:
                    await res(("1.2.3.4", 80))
                # Windows, macOS
                expected_errnos = {tsocket.EAI_NONAME}
                # Linux
                if hasattr(tsocket, "EAI_ADDRFAMILY"):
                    expected_errnos.add(tsocket.EAI_ADDRFAMILY)
                assert excinfo.value.errno in expected_errnos

            # A family where we know nothing about the addresses, so should just
            # pass them through. This should work on Linux, which is enough to
            # smoke test the basic functionality...
            try:
                netlink_sock = tsocket.socket(
                    family=tsocket.AF_NETLINK, type=tsocket.SOCK_DGRAM
                )
            except (AttributeError, OSError):
                pass
            else:
                assert isinstance(netlink_sock, _SocketType)
                assert (
                    await netlink_sock._resolve_address_nocp("asdf", local=local)
                    == "asdf"
                )
                netlink_sock.close()

            with pytest.raises(ValueError):
                await res("1.2.3.4")  # type: ignore[arg-type]
            with pytest.raises(ValueError):
                await res(("1.2.3.4",))  # type: ignore[arg-type]
            with pytest.raises(ValueError):
                if v6:
                    await res(("1.2.3.4", 80, 0, 0, 0))  # type: ignore[arg-type]
                else:
                    # I guess in theory there could be enough overloads that this could error?
                    await res(("1.2.3.4", 80, 0, 0))

async def test_SocketType_unresolved_names() -> None:
    with tsocket.socket() as sock:
        await sock.bind(("localhost", 0))
        assert sock.getsockname()[0] == "127.0.0.1"
        sock.listen(10)

        with tsocket.socket() as sock2:
            await sock2.connect(("localhost", sock.getsockname()[1]))
            assert sock2.getpeername() == sock.getsockname()

    # check gaierror propagates out
    with tsocket.socket() as sock:
        with pytest.raises(tsocket.gaierror):
            # definitely not a valid request
            await sock.bind(("1.2:3", -1))

# This tests all the complicated paths through _nonblocking_helper, using recv
# as a stand-in for all the methods that use _nonblocking_helper.
async def test_SocketType_non_blocking_paths() -> None:
    a, b = stdlib_socket.socketpair()
    with a, b:
        ta = tsocket.from_stdlib_socket(a)
        b.setblocking(False)

        # cancel before even calling
        b.send(b"1")
        with _core.CancelScope() as cscope:
            cscope.cancel()
            with assert_checkpoints():
                with pytest.raises(_core.Cancelled):
                    await ta.recv(10)
        # immediate success (also checks that the previous attempt didn't
        # actually read anything)
        with assert_checkpoints():
            assert await ta.recv(10) == b"1"
        # immediate failure
        with assert_checkpoints():
            with pytest.raises(TypeError):
                await ta.recv("haha")  # type: ignore[arg-type]
        # block then succeed

        async def do_successful_blocking_recv() -> None:
            with assert_checkpoints():
                assert await ta.recv(10) == b"2"

        async with _core.open_nursery() as nursery:
            nursery.start_soon(do_successful_blocking_recv)
            await wait_all_tasks_blocked()
            b.send(b"2")
        # block then cancelled

        async def do_cancelled_blocking_recv() -> None:
            with assert_checkpoints():
                with pytest.raises(_core.Cancelled):
                    await ta.recv(10)

        async with _core.open_nursery() as nursery:
            nursery.start_soon(do_cancelled_blocking_recv)
            await wait_all_tasks_blocked()
            nursery.cancel_scope.cancel()
        # Okay, here's the trickiest one: we want to exercise the path where
        # the task is signaled to wake, goes to recv, but then the recv fails,
        # so it has to go back to sleep and try again. Strategy: have two
        # tasks waiting on two sockets (to work around the rule against having
        # two tasks waiting on the same socket), wake them both up at the same
        # time, and whichever one runs first "steals" the data from the
        # other:
        tb = tsocket.from_stdlib_socket(b)

        async def t1() -> None:
            with assert_checkpoints():
                assert await ta.recv(1) == b"a"
            with assert_checkpoints():
                assert await tb.recv(1) == b"b"

        async def t2() -> None:
            with assert_checkpoints():
                assert await tb.recv(1) == b"b"
            with assert_checkpoints():
                assert await ta.recv(1) == b"a"

        async with _core.open_nursery() as nursery:
            nursery.start_soon(t1)
            nursery.start_soon(t2)
            await wait_all_tasks_blocked()
            a.send(b"b")
            b.send(b"a")
            await wait_all_tasks_blocked()
            a.send(b"b")
            b.send(b"a")

# This tests the complicated paths through connect
async def test_SocketType_connect_paths() -> None:
    with tsocket.socket() as sock:
        with pytest.raises(ValueError):
            # Should be a tuple
            await sock.connect("localhost")

    # cancelled before we start
    with tsocket.socket() as sock:
        with _core.CancelScope() as cancel_scope:
            cancel_scope.cancel()
            with pytest.raises(_core.Cancelled):
                await sock.connect(("127.0.0.1", 80))

    # Cancelled in between the connect() call and the connect completing
    with _core.CancelScope() as cancel_scope:
        with tsocket.socket() as sock, tsocket.socket() as listener:
            await listener.bind(("127.0.0.1", 0))
            listener.listen()

            # Swap in our weird subclass under the trio.socket._SocketType's
            # nose -- and then swap it back out again before we hit
            # wait_socket_writable, which insists on a real socket.
            class CancelSocket(stdlib_socket.socket):
                def connect(self, *args: Any, **kwargs: Any) -> None:
                    # accessing private method only available in _SocketType
                    assert isinstance(sock, _SocketType)

                    cancel_scope.cancel()
                    sock._sock = stdlib_socket.fromfd(
                        self.detach(), self.family, self.type
                    )
                    sock._sock.connect(*args, **kwargs)
                    # If connect *doesn't* raise, then pretend it did
                    raise BlockingIOError  # pragma: no cover

            # accessing private method only available in _SocketType
            assert isinstance(sock, _SocketType)
            sock._sock.close()
            sock._sock = CancelSocket()

            with assert_checkpoints():
                with pytest.raises(_core.Cancelled):
                    await sock.connect(listener.getsockname())
            assert sock.fileno() == -1

    # Failed connect (hopefully after raising BlockingIOError)
    with tsocket.socket() as sock:
        with pytest.raises(OSError):
            # TCP port 2 is not assigned. Pretty sure nothing will be
            # listening there. (We used to bind a port and then *not* call
            # listen() to ensure nothing was listening there, but it turns
            # out on macOS if you do this it takes 30 seconds for the
            # connect to fail. Really. Also if you use a non-routable
            # address. This way fails instantly though. As long as nothing
            # is listening on port 2.)
            await sock.connect(("127.0.0.1", 2))

# Fix issue #1810
async def test_address_in_socket_error() -> None:
    address = "127.0.0.1"
    with tsocket.socket() as sock:
        try:
            await sock.connect((address, 2))
        except OSError as e:
            assert any(address in str(arg) for arg in e.args)

async def test_resolve_address_exception_in_connect_closes_socket() -> None:
    # Here we are testing issue 247, any cancellation will leave the socket closed
    with _core.CancelScope() as cancel_scope:
        with tsocket.socket() as sock:

            async def _resolve_address_nocp(
                self: Any, *args: Any, **kwargs: Any
            ) -> None:
                cancel_scope.cancel()
                await _core.checkpoint()

            assert isinstance(sock, _SocketType)
            sock._resolve_address_nocp = _resolve_address_nocp  # type: ignore[method-assign, assignment]
            with assert_checkpoints():
                with pytest.raises(_core.Cancelled):
                    await sock.connect("")
            assert sock.fileno() == -1

async def test_send_recv_variants() -> None:
    a, b = tsocket.socketpair()
    with a, b:
        # recv, including with flags
        assert await a.send(b"x") == 1
        assert await b.recv(10, tsocket.MSG_PEEK) == b"x"
        assert await b.recv(10) == b"x"

        # recv_into
        await a.send(b"x")
        buf = bytearray(10)
        await b.recv_into(buf)
        assert buf == b"x" + b"\x00" * 9

        if hasattr(a, "sendmsg"):
            assert await a.sendmsg([b"xxx"], []) == 3
            assert await b.recv(10) == b"xxx"

    a = tsocket.socket(type=tsocket.SOCK_DGRAM)
    b = tsocket.socket(type=tsocket.SOCK_DGRAM)
    with a, b:
        await a.bind(("127.0.0.1", 0))
        await b.bind(("127.0.0.1", 0))

        targets = [b.getsockname(), ("localhost", b.getsockname()[1])]

        # recvfrom + sendto, with and without names
        for target in targets:
            assert await a.sendto(b"xxx", target) == 3
            (data, addr) = await b.recvfrom(10)
            assert data == b"xxx"
            assert addr == a.getsockname()

        # sendto + flags
        #
        # I can't find any flags that send() accepts... on Linux at least
        # passing MSG_MORE to send_some on a connected UDP socket seems to
        # just be ignored.
        #
        # But there's no MSG_MORE on Windows or macOS. I guess send_some flags
        # are really not very useful, but at least this tests them a bit.
        if hasattr(tsocket, "MSG_MORE"):
            await a.sendto(b"xxx", tsocket.MSG_MORE, b.getsockname())
            await a.sendto(b"yyy", tsocket.MSG_MORE, b.getsockname())
            await a.sendto(b"zzz", b.getsockname())
            (data, addr) = await b.recvfrom(10)
            assert data == b"xxxyyyzzz"
            assert addr == a.getsockname()

        # recvfrom_into
        assert await a.sendto(b"xxx", b.getsockname()) == 3
        buf = bytearray(10)
        (nbytes, addr) = await b.recvfrom_into(buf)
        assert nbytes == 3
        assert buf == b"xxx" + b"\x00" * 7
        assert addr == a.getsockname()

        if hasattr(b, "recvmsg"):
            assert await a.sendto(b"xxx", b.getsockname()) == 3
            (data, ancdata, msg_flags, addr) = await b.recvmsg(10)
            assert data == b"xxx"
            assert ancdata == []
            assert msg_flags == 0
            assert addr == a.getsockname()

        if hasattr(b, "recvmsg_into"):
            assert await a.sendto(b"xyzw", b.getsockname()) == 4
            buf1 = bytearray(2)
            buf2 = bytearray(3)
            ret = await b.recvmsg_into([buf1, buf2])
            (nbytes, ancdata, msg_flags, addr) = ret
            assert nbytes == 4
            assert buf1 == b"xy"
            assert buf2 == b"zw" + b"\x00"
            assert ancdata == []
            assert msg_flags == 0
            assert addr == a.getsockname()

        if hasattr(a, "sendmsg"):
            for target in targets:
                assert await a.sendmsg([b"x", b"yz"], [], 0, target) == 3
                assert await b.recvfrom(10) == (b"xyz", a.getsockname())

    a = tsocket.socket(type=tsocket.SOCK_DGRAM)
    b = tsocket.socket(type=tsocket.SOCK_DGRAM)
    with a, b:
        await b.bind(("127.0.0.1", 0))
        await a.connect(b.getsockname())
        # send on a connected udp socket; each call creates a separate
        # datagram
        await a.send(b"xxx")
        await a.send(b"yyy")
        assert await b.recv(10) == b"xxx"
        assert await b.recv(10) == b"yyy"

async def test_idna(monkeygai: MonkeypatchedGAI) -> None:
    # This is the encoding for "faß.de", which uses one of the characters that
    # IDNA 2003 handles incorrectly:
    monkeygai.set("ok faß.de", b"xn--fa-hia.de", 80)
    monkeygai.set("ok ::1", "::1", 80, flags=_NUMERIC_ONLY)
    monkeygai.set("ok ::1", b"::1", 80, flags=_NUMERIC_ONLY)
    # Some things that should not reach the underlying socket.getaddrinfo:
    monkeygai.set("bad", "fass.de", 80)
    # We always call socket.getaddrinfo with bytes objects:
    monkeygai.set("bad", "xn--fa-hia.de", 80)

    assert "ok ::1" == await tsocket.getaddrinfo("::1", 80)
    assert "ok ::1" == await tsocket.getaddrinfo(b"::1", 80)
    assert "ok faß.de" == await tsocket.getaddrinfo("faß.de", 80)
    assert "ok faß.de" == await tsocket.getaddrinfo("xn--fa-hia.de", 80)
    assert "ok faß.de" == await tsocket.getaddrinfo(b"xn--fa-hia.de", 80)

async def test_getprotobyname() -> None:
    # These are the constants used in IP header fields, so the numeric values
    # had *better* be stable across systems...
    assert await tsocket.getprotobyname("udp") == 17
    assert await tsocket.getprotobyname("tcp") == 6

async def test_custom_hostname_resolver(monkeygai: MonkeypatchedGAI) -> None:
    # This intentionally breaks the signatures used in HostnameResolver
    class CustomResolver:
        async def getaddrinfo(
            self, host: str, port: str, family: int, type: int, proto: int, flags: int
        ) -> tuple[str, str, str, int, int, int, int]:
            return ("custom_gai", host, port, family, type, proto, flags)

        async def getnameinfo(
            self, sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int
        ) -> tuple[str, tuple[str, int] | tuple[str, int, int, int], int]:
            return ("custom_gni", sockaddr, flags)

    cr = CustomResolver()

    assert tsocket.set_custom_hostname_resolver(cr) is None  # type: ignore[arg-type]

    # Check that the arguments are all getting passed through.
    # We have to use valid calls to avoid making the underlying system
    # getaddrinfo cranky when it's used for NUMERIC checks.
    for vals in [
        (tsocket.AF_INET, 0, 0, 0),
        (0, tsocket.SOCK_STREAM, 0, 0),
        (0, 0, tsocket.IPPROTO_TCP, 0),
        (0, 0, 0, tsocket.AI_CANONNAME),
    ]:
        assert await tsocket.getaddrinfo("localhost", "foo", *vals) == (
            "custom_gai",
            b"localhost",
            "foo",
            *vals,
        )

    # IDNA encoding is handled before calling the special object
    got = await tsocket.getaddrinfo("föö", "foo")
    expected = ("custom_gai", b"xn--f-1gaa", "foo", 0, 0, 0, 0)
    assert got == expected

    assert await tsocket.getnameinfo("a", 0) == (  # type: ignore[arg-type]
        "custom_gni",
        "a",
        0,
    )

    # We can set it back to None
    assert tsocket.set_custom_hostname_resolver(None) is cr

    # And now Trio switches back to calling socket.getaddrinfo (specifically
    # our monkeypatched version of socket.getaddrinfo)
    monkeygai.set("x", b"host", "port", family=0, type=0, proto=0, flags=0)
    assert await tsocket.getaddrinfo("host", "port") == "x"

async def test_custom_socket_factory() -> None:
    class CustomSocketFactory:
        def socket(
            self, family: AddressFamily, type: SocketKind, proto: int
        ) -> tuple[str, AddressFamily, SocketKind, int]:
            return ("hi", family, type, proto)

    csf = CustomSocketFactory()

    assert tsocket.set_custom_socket_factory(csf) is None  # type: ignore[arg-type]

    assert tsocket.socket() == ("hi", tsocket.AF_INET, tsocket.SOCK_STREAM, 0)
    assert tsocket.socket(1, 2, 3) == ("hi", 1, 2, 3)

    # socket with fileno= doesn't call our custom method
    fd = stdlib_socket.socket().detach()
    wrapped = tsocket.socket(fileno=fd)
    assert hasattr(wrapped, "bind")
    wrapped.close()

    # Likewise for socketpair
    a, b = tsocket.socketpair()
    with a, b:
        assert hasattr(a, "bind")
        assert hasattr(b, "bind")

    assert tsocket.set_custom_socket_factory(None) is csf

async def test_SocketType_is_abstract() -> None:
    with pytest.raises(TypeError):
        tsocket.SocketType()

@pytest.mark.skipif(not hasattr(tsocket, "AF_UNIX"), reason="no unix domain sockets")
async def test_unix_domain_socket() -> None:
    # Bind has a special branch to use a thread, since it has to do filesystem
    # traversal. Maybe connect should too? Not sure.

    async def check_AF_UNIX(path: str | bytes) -> None:
        with tsocket.socket(family=tsocket.AF_UNIX) as lsock:
            await lsock.bind(path)
            lsock.listen(10)
            with tsocket.socket(family=tsocket.AF_UNIX) as csock:
                await csock.connect(path)
                ssock, _ = await lsock.accept()
                with ssock:
                    await csock.send(b"x")
                    assert await ssock.recv(1) == b"x"

    # Can't use tmpdir fixture, because we can exceed the maximum AF_UNIX path
    # length on macOS.
    with tempfile.TemporaryDirectory() as tmpdir:
        path = f"{tmpdir}/sock"
        await check_AF_UNIX(path)

    try:
        cookie = os.urandom(20).hex().encode("ascii")
        await check_AF_UNIX(b"\x00trio-test-" + cookie)
    except FileNotFoundError:
        # macOS doesn't support abstract filenames with the leading NUL byte
        pass

async def test_interrupted_by_close() -> None:
    a_stdlib, b_stdlib = stdlib_socket.socketpair()
    with a_stdlib, b_stdlib:
        a_stdlib.setblocking(False)

        data = b"x" * 99999

        try:
            while True:
                a_stdlib.send(data)
        except BlockingIOError:
            pass

        a = tsocket.from_stdlib_socket(a_stdlib)

        async def sender() -> None:
            with pytest.raises(_core.ClosedResourceError):
                await a.send(data)

        async def receiver() -> None:
            with pytest.raises(_core.ClosedResourceError):
                await a.recv(1)

        async with _core.open_nursery() as nursery:
            nursery.start_soon(sender)
            nursery.start_soon(receiver)
            await wait_all_tasks_blocked()
            a.close()

async def test_many_sockets() -> None:
    total = 5000  # Must be more than MAX_AFD_GROUP_SIZE
    sockets = []
    for _x in range(total // 2):
        try:
            a, b = stdlib_socket.socketpair()
        except OSError as e:  # pragma: no cover
            assert e.errno in (errno.EMFILE, errno.ENFILE)
            break
        sockets += [a, b]
    async with _core.open_nursery() as nursery:
        for s in sockets:
            nursery.start_soon(_core.wait_readable, s)
        await _core.wait_all_tasks_blocked()
        nursery.cancel_scope.cancel()
    for sock in sockets:
        sock.close()
    if _x != total // 2 - 1:  # pragma: no cover
        print(f"Unable to open more than {(_x-1)*2} sockets.")


import requests
import sys, getopt
import time
import os
from bs4 import BeautifulSoup

def main():
    movieDBURL = 'https://imsdb.com/scripts/'
    #Load the movies list
    movie_names = []
    with open('movies.txt') as f:
        movie_names = f.readlines()
    #Replace spaces on the movie names with dashes
    movies = [movie.replace(" ", "-") for movie in movie_names]
    #Remove the new line character
    movies = [movie.replace("\n", "") for movie in movies]
    #Remove Colons
    movies = [movie.replace(":", "") for movie in movies]
    #Remove periods
    movies = [movie.replace(".", "") for movie in movies]
    #Replace / with -
    movies = [movie.replace("/", "-") for movie in movies]
    #Move The to the end of the name
    movies = [movie.replace("The-", "") + ",-The" if movie.startswith("The-") else movie for movie in movies]
    #Add the URL to the movie name
    movies = [movieDBURL + movie + ".html" for movie in movies]
    #Get the script for each movie
    #delete the failed.txt file if it exists
    try:
        os.remove("failed.txt")
    except OSError:
        pass
    for i in range(len(movies)):
        movie = movies[i]
        movie_name = movie_names[i]
        getScript(movie, movie_name)
        #Wait 1 second to avoid overloading the server
        time.sleep(1)

def attemptAddToFailList(movie_name = None):
    if movie_name is not None:
        with open("failed.txt", "a") as f:
            f.write(movie_name)

def getScript(url, folder='raw_scripts', movie_name = None):
    #Get the HTML from the URL
    r = requests.get(url)
    #Save the movie name to a file if the reuquest failed
    if r.status_code != 200:
        print("Failed - " + str(r.status_code))
        attemptAddToFailList(movie_name)
    else:
        #Find the td with class "srctext"
        print("Parsing: " + url)
        soup = BeautifulSoup(r.text, 'html.parser')
        td = soup.find("td", class_="scrtext")
        #Get the child pre tag
        pre = td.findChild("pre")

        #Remove all tags from pre
        for tag in pre.find_all(True):
            tag.replaceWithChildren()

        #Mark as error if text is empty
        if pre.text == "":
            print("Failed - Empty Text")
            attemptAddToFailList(movie_name)
        else:
            #Save the text to a file
            #Get the name of the file from the URL before the .html
            filename = url.split("/")[-1].split(".")[0]
            text = pre.text
            #Removed \ufffd characters
            text = text.replace("\ufffd", "")
            #Try to delete the file if it exists
            try:
                os.remove(folder + "/"+ filename + ".txt")
            except OSError:
                pass
            with open(folder + "/"+ filename + ".txt", "w") as f:
                f.write(text)

if __name__ == "__main__":
   main()


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('organisation', '0011_auto_20180719_1144'),
    ]

    operations = [
        migrations.AlterField(
            model_name='unit',
            name='order',
            field=models.IntegerField(help_text=b'Higher numbers mean higher up in the list', null=True, blank=True),
        ),
        migrations.AlterField(
            model_name='unitmembership',
            name='order',
            field=models.IntegerField(help_text=b'Higher numbers mean higher up in the list', null=True, blank=True),
        ),
    ]


import sys
import numpy as np
import random
import math

class PreprocessData:
    def __init__(self, train_file, test_file):
        self.train_file = train_file
        self.test_file = test_file
        self.load_data(self.train_file, True)
        self.load_data(self.test_file, False)

    def load_data(self, data_file, is_train):
        data = np.loadtxt(data_file)

        if is_train:
            self.train_data = data
            self.attr = data.shape[1] - 1
            unique_data = sorted(np.unique(data[:, [-1]]))
            self.num_class = len(unique_data)
            self.mapping = {}
            self.idx = {}

            for i, j in enumerate(unique_data):
                self.mapping[i] = j
                self.idx[j] = i

        else:
            self.test_data = data

class Forest:
    def __init__(self, num_trees, option, prune, data):

        self.data = data
        self.num_trees = num_trees
        self.trees = [
            DecisionTree(i, option, prune, data).DTL_TopLevel()
            for i in range(1, self.num_trees + 1)
        ]

    def classify(self, data):
        forest_dist = []

        for i in self.trees:
            dist = i.predict(data)
            forest_dist.append(dist)

        forest_dist = np.array(forest_dist)
        return self.data.mapping[int(np.argmax(np.mean(forest_dist, axis=0)))]

    def predictions(self):
        object_id = 1
        count = 0
        curr_val = 0
        for i in self.data.test_data:
            predicted_cal = int(self.classify(i[:-1]))
            true_val = int(i[-1])
            if predicted_cal != true_val:
                accuracy = 0
            else:
                accuracy = 1
            curr_val += accuracy
            count += 1
            print(
                f"ID={object_id:5d}, predicted={predicted_cal:3d}, true={true_val:3d}, accuracy={accuracy:4.2f}\n"
            )
            object_id += 1
        print(f"classification accuracy={curr_val/count:6.4f}")

class Tree:
    def __init__(
        self,
        id,
        tree_gain=None,
        tree_attr=None,
        tree_dist=None,
        threshold_tree=None,
        is_leaf=False,
    ):
        self.id = id
        self.tree_gain = tree_gain
        self.tree_attr = tree_attr
        self.tree_dist = tree_dist
        self.threshold_tree = threshold_tree
        self.is_leaf = is_leaf
        self.left = None
        self.right = None

    def predict(self, data):
        if self.is_leaf:
            return self.tree_dist
        else:
            if data[self.tree_attr] < self.threshold_tree:
                return self.left.predict(data)
            else:
                return self.right.predict(data)

    def print(self):
        queue = []
        queue.append((self, 1))
        while queue:
            count = len(queue)
            while count > 0:
                temp = queue.pop(0)
                if temp[0].is_leaf:
                    feature = -1
                    thr = -1
                    gain = 0
                else:
                    feature = temp[0].tree_attr + 1
                    thr = temp[0].threshold_tree
                    gain = temp[0].tree_gain
                if temp[0].left:
                    queue.append((temp[0].left, 2 * temp[1]))
                if temp[0].right:
                    queue.append((temp[0].right, 2 * temp[1] + 1))
                count -= 1
                print(
                    f"tree={temp[0].id:2d}, node={temp[1]:3d}, feature={feature:2d}, thr={thr:6.2f}, gain={gain}\n"
                )

class DecisionTree:
    def __init__(self, id_, option, prune, data):
        self.data = data
        self.id = id_
        self.option = option
        self.prune = int(prune)
        self.idx = data.idx
        self.mapping = data.mapping
        self.attr = data.attr
        self.num_class = data.num_class

    def if_equal(self, examples):
        values = examples[:, [-1]]
        element = examples[:, [-1]][0]
        for i in values:
            if i != element:
                return False
        return True

    def DTL_TopLevel(self):
        default = self.distribution(self.data.train_data)
        if self.option == "randomized":
            choose_attribute = self.random
        elif self.option == "optimized":
            choose_attribute = self.optimized
        return self.DTL(self.data.train_data, default, choose_attribute)

    def DTL(self, examples, default, choose_attribute):
        if examples.shape[0] == 0 or examples.shape[0] < self.prune:
            return Tree(self.id, is_leaf=True, tree_dist=default)

        elif self.if_equal(examples):
            return Tree(self.id, is_leaf=True, tree_dist=default)

        else:
            best_gain, best_attr, best_threshold = choose_attribute(examples)

            tree = Tree(
                self.id,
                tree_attr=best_attr,
                threshold_tree=best_threshold,
                tree_gain=best_gain,
            )

            left = []
            right = []

            for i in examples:
                idx = best_attr
                if i[idx] < best_threshold:
                    left.append(i)
                elif i[idx] >= best_threshold:
                    right.append(i)

            tree.left = self.DTL(
                np.array(left), self.distribution(examples), choose_attribute
            )
            tree.right = self.DTL(
                np.array(right), self.distribution(examples), choose_attribute
            )
            return tree

    def optimized(self, examples):
        max_gain = -1
        max_attribute = -1
        max_threshold = -1

        for i in range(self.attr):
            values = examples[:, [i]]

            L = np.amin(values)
            M = np.amax(values)

            step = (M - L) / 51
            for k in range(1, 51):
                threshold = (L + k) * step
                gain = self.information_gain(examples, i, threshold)
                if gain > max_gain:
                    max_gain = gain
                    max_attribute = i
                    max_threshold = threshold
        return max_gain, max_attribute, max_threshold

    def random(self, examples):

        max_gain = -1
        max_threshold = -1
        i = random.randint(0, self.attr - 1)
        values = examples[:, [i]]
        L = np.amin(values)
        M = np.amax(values)
        step = (M - L) / 51
        for j in range(1, 51):
            threshold = (L + j) * step
            gain = self.information_gain(examples, i, threshold)
            if gain > max_gain:
                max_gain = gain
                max_threshold = threshold
        return max_gain, i, max_threshold

    def information_gain(self, examples, A, threshold):

        base_entropy = self.entropy(examples)
        N1 = []
        N2 = []
        for i in examples:
            if i[A] < threshold:
                N1.append(i)
            elif i[A] >= threshold:
                N2.append(i)

        N1 = np.array(N1)
        N2 = np.array(N2)

        base_entropy -= (N1.shape[0] / examples.shape[0]) * self.entropy(N1)
        base_entropy -= (N2.shape[0] / examples.shape[0]) * self.entropy(N2)

        return base_entropy

    def entropy(self, examples):

        count = {}
        for i in examples:
            label = int(i[-1])
            count[label] = count.get(label, 1) + 1
        return sum(
            [
                -(j / examples.shape[0]) * np.log2(j / examples.shape[0])
                for j in count.values()
            ]
        )

    def distribution(self, data):
        zeros = np.zeros(self.num_class)
        for i in data:

            label = int(i[-1])
            zeros[self.idx[label]] += 1
            zeros /= data.shape[0]

        return zeros

    def predictions(self, root):
        object_id = int(1)
        total = 0
        curr_val = 0
        for i in self.data.test_data:
            true_val = int(i[-1])
            dist = root.predict(i[:-1])
            predicted_val = int(np.argmax(dist))
            predicted_val = int(self.mapping[int(predicted_val)])

            total += 1

            if true_val != predicted_val:
                accuracy = 0
            else:
                accuracy = 1
            curr_val += accuracy
            print(
                f"ID={object_id:5d}, predicted={predicted_val:3d}, true={true_val:3d}, accuracy={accuracy:4.2f}\n"
            )
            object_id += 1
        print(f"classification accuracy={curr_val/total:6.4f}")

def main():
    data = PreprocessData(sys.argv[1], sys.argv[2])
    if sys.argv[3] == "forest3":
        forest = Forest(3, "randomized", sys.argv[4], data)
        for i in forest.trees:
            i.print()
        forest.predictions()
    elif sys.argv[3] == "forest15":
        forest = Forest(15, "randomized", sys.argv[4], data)
        for i in forest.trees:
            i.print()
        forest.predictions()
    else:
        tree = DecisionTree(1, sys.argv[3], sys.argv[4], data)
        root = tree.DTL_TopLevel()
        root.print()
        tree.predictions(root)

if __name__ == "__main__":
    main()


n, d, k, c = map(int, input().split()) # 접시수/초밥가짓수/연속해서먹는접시수/쿠폰번호
arr = []
ans = 0
for _ in range(n):
    num = int(input())
    arr.append(num)

for i in range(n):
        if i+k > n:  # 한바퀴 돌아?
            check = len(set(arr[i:n] + arr[:(i+k)%n] + [c]))
        else:
            check = len(set(arr[i:i+k]+[c]))
        if ans < check:
            ans = check
print(ans)


import socket
import sys

# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

server_address = ("127.0.0.1", 5005)
msg = b'This is the message. It will be repeated.'

try:
    # Send data
    print('Sending {!r}'.format(msg))
    sent = sock.sendto(msg, server_address)

    # Receive response
    print("Waiting to receive")
    data, server = sock.recvfrom(4096)
    print("Received {!r}".format(data))

finally:
    print('Closing socket')
    sock.close()


from django.conf import settings

from .import views
from django.urls import path,include
from django.conf.urls.static import static
app_name='movieapp'

urlpatterns = [

    path('',views.demo,name='demo'),
    path('movie/<int:movie_id>/',views.detail,name='detail'),
    path('add/',views.add_movie,name='add_movie'),
    path('update/<int:id>/',views.update,name='update'),
    path('delete/<int:id>/',views.delete,name='delete'),
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL,
                          document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)


# from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
from django.core.mail import send_mail
from django.shortcuts import get_object_or_404
from .forms import EmailPostForm, CommentForm, SearchForm
from django.views.generic import ListView
from django.shortcuts import render
from .models import Post

# Template Views

class PostListView(ListView):
    queryset = Post.published.all()
    context_object_name = 'posts'
    paginate_by = 3
    template_name = 'blog/post/post_list.html'

# def post_list(request, tag_slug=None):
#     object_list = Post.published.all()
#     tag = None

#     #  tags
#     from taggit.models import Tag
#     if tag_slug:
#         tag = get_object_or_404(Tag, slug=tag_slug)
#         object_list = object_list.filter(tags__in=[tag])

#     #  paginator
#     from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
#     paginator = Paginator(object_list, 10)
#     page = request.GET.get('page')
#     try:
#         posts = paginator.page(page)
#     except PageNotAnInteger:
#         posts = paginator.page(1)
#     except EmptyPage:
#         posts = paginator.page(paginator.num_pages)

#     context = {'posts': posts, 'page': page, 'tag': tag}
#     return render(request, 'blog/post/post_list.html', context)

def post_detail(request, year, month, day, post):
    post = get_object_or_404(Post, slug=post, status='published', published_date__year=year,
                             published_date__month=month, published_date__day=day)

    #  comments
    comments = post.comments.filter(active=True)
    new_comment = None

    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.post = post
            new_comment.save()
    else:
        comment_form = CommentForm()

    #  list of similar posts
    from django.db.models import Count
    post_tags_ids = post.tags.values_list('id', flat=True)
    similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
    similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-published_date')[:4]

    context = {'post': post, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form,
               'similar_posts': similar_posts}
    return render(request, 'blog/post/post_detail.html', context)

# Forms views

def share_post(request, post_id):
    post = get_object_or_404(Post, id=post_id, status='published')
    mail_is_sent = False

    if request.method == 'POST':
        form = EmailPostForm(request.POST)
        if form.is_valid():
            user = form.cleaned_data
            post_url = request.build_absolute_uri(post.get_absolute_url())
            subject = f"{user['name']} recommends you read  {post.title}"
            message = f"Read the post '{post.title}' at {post_url}\n\n {user['name']}\'s comment {user['comments']}"
            send_mail(subject, message, user['email'], [
                user['to']], fail_silently=False)
            mail_is_sent = True
    else:
        form = EmailPostForm()
        user = EmailPostForm()

    context = {'post': post, 'form': form,
               'mail_is_sent': mail_is_sent, 'user': user}
    return render(request, 'blog/post/share.html', context)

def show_latest_posts(request):
    count = 5
    latest_posts = Post.published.order_by('-published_date')[:count]

    context = {'latest_posts': latest_posts}
    return render(request, 'blog/post/latest_posts.html', context)

def post_search(request):
    form = SearchForm()
    query = None
    results = []

    if 'query' in request.GET:
        form = SearchForm(request.GET)
        if form.is_valid():
            query = form.cleaned_data['query']
            search_vector = SearchVector(
                'title', weight='A') + SearchVector('content', weight='B')
            search_query = SearchQuery(query)
            results = Post.published.annotate(search=search_vector, rank=SearchRank(
                search_vector, search_query)).filter(rank__gte=0.2).order_by('-rank')

    context = {'form': form, 'query': query, 'results': results}
    return render(request, 'blog/post/search.html', context)


from pages.home.login_page import LoginPage
import unittest
from utilities.teststatus import Status
import pytest
import time

@pytest.mark.usefixtures("oneTimeSetUp","setUp")
class LoginTests(unittest.TestCase):

    @pytest.fixture(autouse=True)
    def classSetup(self,oneTimeSetUp):
        self.lp = LoginPage(self.driver)
        self.ts = Status(self.driver)

    @pytest.mark.run(order=2)
    def test_validlogin(self):
        #print("######## execution of correct login")
        self.lp.login('test@email.com', 'abcabc')
        result1 = self.lp.verifyLoginPageTitle("Let's Kode It")
        self.ts.mark(result1,'Title is correct')
        #self.lp.login('test@email.com','abcabc')
        result2 = self.lp.verifyLoginSuccessful()
        self.ts.mark(result2,"Login was successful")
        self.ts.markFinal("Validating Login",result2,"Login was successful")

    @pytest.mark.run(order = 1)
    def test_invalidlogin(self):
         # The class Setup fixture will open the URL the first time
        #print("**********   Execution of wrong login")
        self.lp.logout()
        self.lp.login('test@email.com','abc')
        result = self.lp.verifyLoginFailure()
        self.ts.mark(result,"Couldn't login with wrong userid/password")
     #   assert result == True
    #


"""This runs the GUI for our project"""
import tkinter as tk
import random
import spotify_methods
import computations
import song_graph
import pygame_visualization

###################################
#      FUNCTIONAL DEFINITIONS
###################################

def make_playlist(playlist: list, preferences: dict,
                  graph: song_graph.GenreGraph, all_songs: dict) -> list[song_graph.Song]:
    """This method generates you a new playlist!

    The song vertices currently stored aren't in the graph, they just represent the vertex. Now
    this song MAY have a vertex representation already in the graph or may not, so we need to
    first decide if we need ot add this new vertex in as a new vertex, or use the existing one!

    Then, computations.py handles the playlist generation depending on the mode selected"""
    # print("Making Playlist...")
    song_verts = []
    for song in playlist:
        if not song.information['id'] in all_songs:
            graph.insert_song(song)
            all_songs[song.information['id']] = song.genre
        song_verts.append(graph.get_song(song))
    new_playlist = []
    if song_verts != []:
        if preferences['gen_mode'] == 'level gen':
            new_playlist = computations.bfs_gen(graph, song_verts, 2)
        elif preferences['gen_mode'] == 'custom gen':
            new_playlist = computations.par_gen(graph, song_verts, 2, preferences)
        elif preferences['gen_mode'] == 'artist pref':
            new_playlist = computations.artist_gen(graph, song_verts)
        elif preferences['gen_mode'] == 'new genre':
            if preferences['bias'] is not None:
                new_playlist = computations.explore_new_genres(graph, song_verts,
                                                               preferences['bias'],
                                                               preferences)
        elif preferences['gen_mode'] == 'unique songs':
            new_playlist = computations.find_uniquely_connected(graph, song_verts, preferences)
        elif preferences['gen_mode'] == 'recent songs':
            new_playlist = computations.get_new_songs(graph, song_verts)
        else:
            new_playlist = []

    playname_id = list(range(10))
    random.shuffle(playname_id)
    play_name = preferences['gen_mode'] + " " + "".join([str(x) for x in playname_id])
    if new_playlist == []:
        print('Playlist FAILED, check the input')
        return []

    spotify_methods.generate_playlist(play_name,
                                      [vert.information['id'] for vert in new_playlist])
    print('Playlist Made, Check your Spotify Account! \n \n Playlist Name: ' + str(play_name))
    return new_playlist

###################################
#         EVENT METHODS
###################################
def search_song(fields: tuple[tk.Entry, tk.Entry],
                search_res: tk.Listbox, current_result: list,
                graph: song_graph.GenreGraph, all_songs: dict) -> None:
    """Searches for a song and updates results list. This method calls the
    spotipy_methods.find_track_options and loads the results into a list"""
    song_name = fields[0].get()
    artist_name = fields[1].get()
    if song_name is not None:
        search_results = spotify_methods.find_track_options(song_name, graph,
                                                            all_songs, artist_name)
        current_result.clear()
        search_res.delete(0, search_res.size())
        if len(search_results) > 0:  # found a song
            for i in range(len(search_results)):
                search_res.insert(i, search_results[i].name + ' by '
                                  + search_results[i].information['artists'][0])
                current_result.append(search_results[i])

def add_song(search_res: tk.Listbox,
             playlist_res: tk.Listbox, current_results: list, playlist: list) -> None:
    """This method adds a song to the playlist data and listview. Given a
    selected song from the search results list, you can add the selected song
    to your song list."""
    cur_selection = search_res.curselection()
    if len(cur_selection) == 0:  # nothing selected
        pass
    else:
        selected_index = cur_selection[0]
        selected_song = current_results[selected_index]
        playlist_res.insert('end', selected_song.name + " by "
                            + selected_song.information['artists'][0])
        playlist.append(selected_song)

def rem_song(playlist_res: tk.Listbox, playlist: list) -> None:
    """Removes a song from playlist data and listview. Given a
    selected song from the song list, you can remove the selected song
    sed list."""
    cur_selection = playlist_res.curselection()
    if len(cur_selection) == 0:  # nothing selected
        pass
    else:
        selected_index = cur_selection[0]
        playlist.pop(selected_index)
        playlist_res.delete(selected_index, selected_index)

def pull_playlist(name_entry: tk.Entry, playlist_res: tk.Listbox, playlist: list,
                  graph: song_graph.GenreGraph, all_songs: dict) -> None:
    """Pulls a playlist from tethered account. You can load a whole playlist into the song
    list. This function is for ease of the user."""
    name = name_entry.get()
    if name is None:
        # Error Message
        return
    else:
        track_list = spotify_methods.pull_playlist(name, graph, all_songs)
        if track_list == []:
            return
        for i in range(len(track_list)):
            playlist_res.insert('end', track_list[i].name + ' by '
                                + track_list[i].information['artists'][0])
            playlist.append(track_list[i])

def clear(playlist_res: tk.Listbox, playlist: list) -> None:
    """Clears songs list"""
    playlist.clear()
    playlist_res.delete(0, playlist_res.size())

def visualize(graph: song_graph.GenreGraph) -> None:
    """This method visualizes the given graph"""
    pygame_visualization.run(graph)

def open_pref(preferences: dict) -> None:
    """This method loads the user preferences window. It is triggered by clicking
    'settings' """
    settings_window = tk.Tk(screenName='Settings Window')
    settings_window.config(background=BG_BLACK)
    settings_window.geometry(str(SET_WIN_WIDTH) + 'x' + str(SET_WIN_HEIGHT))

    ###################################
    #           GUI METHODS
    ###################################
    def update_prefs(preferences: dict) -> None:
        """This function updates the user's preferences!"""
        for item in sliders:
            preferences[item] = int(sliders[item].get())
        preferences['gen_mode'] = choice_var.get()
        if len(bias_field.get()) > 0:
            preferences['bias'] = float(bias_field.get())
        else:
            preferences['bias'] = None

    ###################################
    #         GUI ELEMENTS
    ###################################
    settings_header = tk.Label(master=settings_window, text="Preferences",
                               background=HEADER_COL)
    settings_header.config(font=("System", 9), width=40)
    settings_header.place(relx=0.5, rely=0.02, anchor='n')

    sliders = {}
    keys = list(preferences.keys())
    for i in range(6):  # nifty loop to load and store the sliders locally
        new_slider = tk.Scale(master=settings_window, label=keys[i], from_=0, to=100,
                              orient=tk.HORIZONTAL)

        new_slider.place(relx=0.25 + (i % 2) * 0.48,
                         rely=0.17 + (i // 2) * 0.19,
                         anchor='center')
        new_slider.set(preferences[keys[i]])
        sliders[keys[i]] = new_slider

    gen_choices = {'level gen', 'custom gen', 'artist pref',
                   'new genre', 'unique songs', 'recent songs'}
    choice_var = tk.StringVar(settings_window)
    choice_var.set(preferences['gen_mode'])

    choice_label = tk.Label(settings_window, text="Generation Mode",
                            background=HEADER_COL)
    choice_label.config(font=("System", 9), width=20)
    choice_label.place(relx=0.5, rely=0.67, anchor='n')

    choice_menu = tk.OptionMenu(settings_window, choice_var, *gen_choices)
    choice_menu.place(relx=0.5, rely=0.77, anchor='center')

    bias_label = tk.Label(settings_window, text="Bias (new genre only) 0 to 1",
                          background=HEADER_COL)
    bias_label.config(font=("System", 5), width=25)
    bias_label.place(relx=0.5, rely=0.81, anchor='n')

    bias_field = tk.Entry(master=settings_window, width=10)
    bias_field.config(font=("System", 9))
    bias_field.place(relx=0.5, rely=0.89, anchor='center')

    apply_button = tk.Button(master=settings_window, text='Apply',
                             command=lambda: update_prefs(preferences),
                             background=HEADER_COL)
    apply_button.config(font=('System', 9), width=7)
    apply_button.place(relx=0.5, rely=0.99, anchor='s')

    settings_window.mainloop()

###################################
#       GUI DEFINITIONS
###################################
WIN_WIDTH, WIN_HEIGHT = 375, 575
SET_WIN_WIDTH, SET_WIN_HEIGHT = 300, 465
HEADER_COL = "#1DB954"  # <- spotify green
SEC1_COL = "#5ca2b8"  # 94ba5f" <- old green
SEC2_COL = "#ac76cc"
WHITE = "#FFFFFF"
BG_BLACK = '#191414'
SPOT_GREY = '#7c7c7c'

def get_window() -> tk.Tk:
    """This function creates the GUI window and returns it"""
    window = tk.Tk(screenName='Thank you Evan Kanter for a terrific semester!')
    window.config(background=BG_BLACK)
    window.geometry(str(WIN_WIDTH) + 'x' + str(WIN_HEIGHT))

    return window

def set_song_header(window: tk.Tk) -> tk.Label:
    """This is a header method to put the song header"""
    song_header = tk.Label(master=window, text="Songs", background=HEADER_COL)
    song_header.config(font=("System", 9), width=40)
    song_header.place(relx=0.5, rely=0.02, anchor='n')

    return song_header

def set_vis_btn(window: tk.Tk, graph: song_graph.GenreGraph) -> None:
    """This is a header method to set the visualization button"""
    vis_btn = tk.Button(master=window, text='Visualize',
                        command=lambda: visualize(graph=graph))
    vis_btn.config(font=('System', 9), width=7)
    vis_btn.place(relx=0.85, rely=0.06, anchor='s')

def set_playlist_res(window: tk.Tk) -> tk.Listbox:
    """This is a header to set the playlist listbox on the GUI"""
    playlist_res = tk.Listbox(master=window, width=40, height=14)
    playlist_res.config(background=WHITE)
    playlist_res.place(relx=0.5, rely=0.28, anchor='center')
    return playlist_res

def set_song_entry_header(window: tk.Tk) -> tk.Label:
    """This is a header to set the song entry item on the GUI"""
    song_entry_header = tk.Label(master=window, text="Song:", background=SEC1_COL)
    song_entry_header.config(font=("System", 9))
    song_entry_header.place(relx=0.25, rely=0.49, anchor='n')
    return song_entry_header

def set_song_field(window: tk.Tk) -> tk.Entry:
    """This method is a header to set the song entry item on the GUI"""
    song_field = tk.Entry(master=window, width=20)
    song_field.config(font=("System", 9))
    song_field.place(relx=0.62, rely=0.51, anchor='center')
    return song_field

def set_art_entry_header(window: tk.Tk) -> tk.Label:
    """This is a header method to set the artist entry title item on the GUI"""
    art_entry_header = tk.Label(master=window, text="Artist:", background=SEC1_COL)
    art_entry_header.config(font=("System", 9))
    art_entry_header.place(relx=0.25, rely=0.54, anchor='n')
    return art_entry_header

def set_art_field(window: tk.Tk) -> tk.Entry:
    """This is a header method to set the artist entry item on the GUI"""
    art_field = tk.Entry(master=window, width=20)
    art_field.config(font=("System", 9))
    art_field.place(relx=0.62, rely=0.56, anchor='center')
    return art_field

def set_search_btn(window: tk.Tk, fields: tuple[tk.Entry, tk.Entry],
                   search_elements: tuple[tk.Listbox, list],
                   graph: song_graph.GenreGraph, all_songs: dict) -> None:
    """This is a header method to set the search button on the GUI"""
    search_btn = tk.Button(master=window, text='Search',
                           command=lambda: search_song(fields=(fields[0], fields[1]),
                                                       search_res=search_elements[0],
                                                       current_result=search_elements[1],
                                                       graph=graph,
                                                       all_songs=all_songs),
                           background=SEC1_COL)
    search_btn.config(font=('System', 9), width=7)
    search_btn.place(relx=0.5, rely=0.64, anchor='s')

def set_search_res(window: tk.Tk) -> tk.Listbox:
    """This method sets the search result listbox on the GUI"""
    search_res = tk.Listbox(master=window, width=40, height=3)
    search_res.config(font=("System", 9), background=WHITE)
    search_res.place(relx=0.5, rely=0.74, anchor='s')
    return search_res

def set_load_play_header(window: tk.Tk) -> tk.Label:
    """This method sets the load playlist title on the GUI"""
    load_play_header = tk.Label(master=window, text="Load Playlist", background=SEC2_COL)
    load_play_header.config(font=("System", 9), width=40)
    load_play_header.place(relx=0.5, rely=0.79, anchor='s')
    return load_play_header

def set_name_entry(window: tk.Tk) -> tk.Entry:
    """This method sets the playlist name entry on the GUI"""
    name_entry = tk.Entry(master=window, width=22)
    name_entry.config(font=("System", 9))
    name_entry.place(relx=0.5, rely=0.83, anchor='center')
    return name_entry

def set_settings_btn(window: tk.Tk, preferences: dict) -> None:
    """This is a method to set the settings button on the GUI"""
    settings_btn = tk.Button(master=window, text='Settings',
                             command=lambda: open_pref(preferences=preferences))
    settings_btn.config(font=('System', 9), width=7)
    settings_btn.place(relx=0.5, rely=0.92, anchor='s')

def set_clear_btn(window: tk.Tk, playlist_res: tk.Listbox, playlist: list) -> None:
    """This method sets the clear button on the GUI"""
    clear_btn = tk.Button(master=window, text='Clear',
                          command=lambda: clear(playlist_res, playlist))
    clear_btn.config(font=('System', 9), width=10)
    clear_btn.place(relx=0.8, rely=0.92, anchor='s')

def set_add_play(window: tk.Tk, name_entry: tk.Entry,
                 playlist_elements: tuple[tk.Listbox, list],
                 graph: song_graph.GenreGraph, all_songs: dict) -> None:
    """This method sets the add playlist button on the GUI"""
    add_play = tk.Button(master=window, text='Add Playlist',
                         command=lambda:
                         pull_playlist(name_entry,
                                       playlist_elements[0], playlist=playlist_elements[1],
                                       graph=graph, all_songs=all_songs))
    add_play.config(font=('System', 9), width=10)
    add_play.place(relx=0.2, rely=0.92, anchor='s')

def set_add_song(window: tk.Tk, search_res: tk.Listbox, playlist_res: tk.Listbox,
                 current_results: list, playlist: list) -> None:
    """This method sets the add song button on the GUI"""
    add_song_btn = tk.Button(master=window, text='Add Song',
                             command=lambda:
                             add_song(search_res, playlist_res, current_results, playlist))
    add_song_btn.config(font=('System', 9), width=9)
    add_song_btn.place(relx=0.2, rely=0.98, anchor='s')

def set_start_btn(window: tk.Tk, playlist: list, preferences: dict, graph: song_graph.GenreGraph,
                  all_songs: dict) -> None:
    """This method sets the start button on the GUI"""
    start_btn = tk.Button(master=window, text='START!',
                          command=lambda: make_playlist(playlist, preferences, graph,
                                                        all_songs))
    start_btn.config(font=('System', 9), width=7)
    start_btn.place(relx=0.5, rely=0.98, anchor='s')

def set_rem_btn(window: tk.Tk, playlist_res: tk.Listbox, playlist: list) -> None:
    """This method sets the remove button on the GUI"""
    rem_btn = tk.Button(master=window, text='Remove',
                        command=lambda: rem_song(playlist_res, playlist))

    rem_btn.config(font=('System', 9), width=9)
    rem_btn.place(relx=0.8, rely=0.98, anchor='s')

def open_tk() -> None:
    """This method starts the GUI"""
    thresh = float(input("Enter the threshold you would like to work with: "))
    print("Starting Sofware with a threshold of " + str(thresh))
    playlist = []  # song list
    preferences = {'acousticness': 0,  # user preferences
                   'danceability': 0,
                   'energy': 0,
                   'instrumentalness': 0,
                   'key': 0,
                   'liveness': 0,
                   'gen_mode': 'new genre',
                   'bias': 0}
    current_result = []
    graph, all_songs = song_graph.create_genre_graph('Data/data.csv', 'Data/data_w_genres.csv',
                                                     'Data/data_by_genres.csv', thresh)

    window = get_window()
    set_song_header(window)
    set_vis_btn(window, graph=graph)
    playlist_res = set_playlist_res(window)
    set_song_entry_header(window)
    song_field = set_song_field(window)
    set_art_entry_header(window)
    art_field = set_art_field(window)
    search_res = set_search_res(window)
    set_search_btn(window, fields=(song_field, art_field),
                   search_elements=(search_res, current_result), graph=graph, all_songs=all_songs)
    set_load_play_header(window)
    name_entry = set_name_entry(window)
    set_settings_btn(window, preferences=preferences)
    set_clear_btn(window, playlist_res=playlist_res, playlist=playlist)

    set_add_play(window, name_entry=name_entry, playlist_elements=(playlist_res, playlist),
                 graph=graph, all_songs=all_songs)
    set_add_song(window, playlist_res=playlist_res, search_res=search_res,
                 current_results=current_result, playlist=playlist)
    set_start_btn(window, playlist, preferences, graph, all_songs)
    set_rem_btn(window, playlist_res=playlist_res, playlist=playlist)
    window.mainloop()

open_tk()

# if __name__ == '__main__':
#     import python_ta.contracts
#     python_ta.contracts.check_all_contracts()
#
#     import doctest
#     doctest.testmod()
#
#     import python_ta
#     python_ta.check_all(config={
#         'max-line-length': 100,
#         'disable': ['E1136'],
#         'extra-imports': ['pygame', 'networkx', 'pygame_visualization', 'song_graph',
#                           'computations', 'tkinter', 'spotify_methods', 'random', 'main',
#                           'spotipy', 'spotipy.oauth2', 'main', 'graph_visualization', 'datetime',
#                           'csv', 'plotly.graph_objects'],
#         'generated-members': ['pygame.*'],
#         'max-nested-blocks': 4,
#         'allowed-io': ['genres_to_songs', 'load_genres', 'load_artists_to_genres', 'load_songs',
#                        'open_tk', 'make_playlist']
#     })


import boto3
import cv2
import numpy as np
import tensorflow as tf
from pymediainfo import MediaInfo

s3_client = boto3.client('s3')
#model = tf.keras.models.load_model('./model.h5', compile=False)

VIDEO_FPS = 10
MODEL_PATH = 'models/efficientdet_d0_coco17_tpu-32/saved_model/'
LABEL_PATH = 'models/coco-labels-paper.txt'
OUTPUT_WIDTH = 300

# ロード
detection_graph = tf.Graph()
loaded = tf.saved_model.load(MODEL_PATH)
labels = ['blank']
with open(LABEL_PATH,'r') as f:
    for line in f: labels.append(line.rstrip())

colors = [
  (0, 0, 255),
  (0, 64, 255),
  (0, 128, 255),
  (0, 192, 255),
  (0, 255, 255),
  (0, 255, 192),
  (0, 255, 128),
  (0, 255, 64),
  (0, 255, 0),
  (64, 255, 0),
  (128, 255, 0),
  (192, 255, 0),
  (255, 255, 0),
  (255, 192, 0),
  (255, 128, 0),
  (255, 64, 0),
  (255, 0, 0),
  (255, 0, 64),
  (255, 0, 128),
  (255, 0, 192),
  (255, 0, 255),
  (192, 0, 255),
  (128, 0, 255),
  (64, 0, 255),
]

def handler(event, context):
    try:
        for record in event['Records']:
            # アップロードされた画像をダウンロード
            bucket = record['s3']['bucket']['name']
            key = record['s3']['object']['key']
            download_path = '/tmp/target_image'
            s3_client.download_file(bucket, key, download_path)
            splitkey = key.split('/')	#keyのフォルダ名/ファイル名を分離
            print("name:"+splitkey[1])

            # 読み込み用
            cap = cv2.VideoCapture(download_path)

            degree = 0
            if splitkey[1].endswith('.mov') or splitkey[1].endswith('.MOV') or splitkey[1].endswith('.mp4') or splitkey[1].endswith('.MP4'):

                media_info = MediaInfo.parse(download_path)

                # 入力ファイルのサイズ
                w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

                for track in media_info.tracks:
                    if track.rotation == "90.000":
                        degree = 180
                        frame_width, frame_height = w, h
                        break
                    elif track.rotation == "180.000":
                        degree = 0
                        frame_width, frame_height = w, h
                        break
                    elif track.rotation == "270.000":
                        degree = 180
                        frame_width, frame_height = w, h
                        break
                    else:
                        degree = 0
                        frame_width, frame_height = w, h

            print("rotated " + str(degree))

            if splitkey[1].endswith('.mp4') or splitkey[1].endswith('.MP4'):
                # 書き込み用
                original_fps = cap.get(cv2.CAP_PROP_FPS)
                fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
                writer = cv2.VideoWriter("/tmp/" + splitkey[1], fmt, VIDEO_FPS, (frame_width, frame_height))

                thresh = original_fps / VIDEO_FPS

            if splitkey[1].endswith('.mov') or splitkey[1].endswith('.MOV'):
                # 書き込み用
                original_fps = cap.get(cv2.CAP_PROP_FPS)
                fmt = cv2.VideoWriter_fourcc(*'XVID')
                writer = cv2.VideoWriter("/tmp/" + splitkey[1], fmt, VIDEO_FPS, (frame_width, frame_height))

                thresh = original_fps / VIDEO_FPS

            frame_counter = 0

            while True:

                ret, frame = cap.read()
                if not ret: break

                # 画像の入力に対応するため初回のみ1フレームずらす
                if frame_counter == 0 or frame_counter >= thresh:

                    print("loaded!")

                    # movの場合 180度回転
                    if degree == 90:
                        frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
                    elif degree == 180:
                        frame = cv2.rotate(frame, cv2.ROTATE_180)
                    elif degree == 270:
                        frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)

                    frame_height, frame_width, _ = frame.shape

                    # 入力画像を正方形で切り出し
                    crop_len = min(frame_height, frame_width)
                    crop_start = ((frame_height-crop_len) // 2, (frame_width-crop_len) // 2)
                    img_crp = frame[crop_start[0]:crop_start[0]+crop_len, crop_start[1]:crop_start[1]+crop_len]

                    img_bgr = cv2.resize(img_crp, (300, 300))
                    img_np  = img_bgr[:, :, ::-1]
                    img_np  = np.expand_dims(img_np, axis=0)

                    img_tensor = tf.convert_to_tensor(img_np)
                    output_dict = loaded.signatures["serving_default"](img_tensor)

                    output_dict['num_detections'] = int(output_dict['num_detections'][0])
                    output_dict['detection_classes'] = output_dict['detection_classes'][0].numpy()
                    output_dict['detection_boxes'] = output_dict['detection_boxes'][0].numpy()
                    output_dict['detection_scores'] = output_dict['detection_scores'][0].numpy()

                    for i in range(output_dict['num_detections']):
                        class_id = output_dict['detection_classes'][i].astype(np.int)
                        if class_id < len(labels):
                            label = labels[class_id]
                        else:
                            label = 'unknown'

                        detection_score = output_dict['detection_scores'][i]

                        if detection_score > 0.5:

                            h, w, _ = img_crp.shape

                            box = output_dict['detection_boxes'][i] * np.array([h, w,  h, w])
                            box = box.astype(np.int)

                            class_id = class_id % len(colors)
                            color = colors[class_id]

                            cv2.rectangle(frame, (box[1]+crop_start[1], box[0]+crop_start[0]), (box[3]+crop_start[1], box[2]+crop_start[0]), color, 3)

                            information = '%s: %.1f%%' % (label, output_dict['detection_scores'][i] * 100.0)
                            cv2.putText(frame, information, (box[1]+crop_start[1] + 15, box[2]+crop_start[0] - 15), cv2.FONT_HERSHEY_SIMPLEX, int(1*crop_len/300), (255, 255, 255), int(2*crop_len/300), cv2.LINE_AA)
                            cv2.putText(frame, information, (box[1]+crop_start[1] + 15, box[2]+crop_start[0] - 15), cv2.FONT_HERSHEY_SIMPLEX, int(1*crop_len/300), color, int(1*crop_len/300), cv2.LINE_AA)

                    if (splitkey[1].endswith('.jpg') or splitkey[1].endswith('.JPG')):
                        cv2.imwrite("/tmp/" + splitkey[1], frame)

                    elif (splitkey[1].endswith('.jpeg') or splitkey[1].endswith('.JPEG')):
                        cv2.imwrite("/tmp/" + splitkey[1], frame)

                    elif (splitkey[1].endswith('.png') or splitkey[1].endswith('.PNG')):
                        cv2.imwrite("/tmp/" + splitkey[1], frame)

                    elif (splitkey[1].endswith('.mp4') or splitkey[1].endswith('.MP4')):
                        writer.write(frame)

                    elif (splitkey[1].endswith('.MOV') or splitkey[1].endswith('.MOV')):
                        writer.write(frame)

                    frame_counter = 1

                else:
                    #print("skipped!")
                    frame_counter += 1

            print("done!")
            cap.release()

            if (splitkey[1].endswith('.mp4') or splitkey[1].endswith('.MP4')):
                writer.release()

            if (splitkey[1].endswith('.mov') or splitkey[1].endswith('.MOV')):
                writer.release()

            # 画像をアップロード
            newkey = key.replace(splitkey[0], 'result')	# result/ファイル名 のnewkeyを作成
            s3_client.upload_file("/tmp/" + splitkey[1], bucket, newkey)
            print("saved")

        return 'Success'

    except Exception as e:
        print(e)
        return 'Failure'


class Coord:
    POINT = "POINT"
    RECTANGLE = "RECTANGLE"
    COLUMN_DEFAULTS = {
        "name": "Undefined",
        "x1": None,
        "y1": None,
        "x2": None,
        "y2": None,
    }

    def __init__(self, **kwargs):
        """
        Add variables to the object based on their static list.
        Set the variable to the value from kwargs, if given.
        Set to default otherwise.
        """
        [self.__setattr__(key, kwargs.get(key, val)) for key, val in self.COLUMN_DEFAULTS.items()]
        self.type = self.POINT if self.x2 == None and self.y2 == None else self.RECTANGLE

    def column_data(self, key, value=None):
        if not value:
            return self.__getattribute__(key)
        else:
            return self.__setattr__(key, value)

    def row_data(self):
        return [self.__getattribute__(key) or "" for key in self.COLUMN_DEFAULTS.keys()]


import machine
import utime

def control_servos(current_angle_list, target_angle_list, transition_time, steps=100):
    # サーボモータ用のピン設定
    servo_pins = [machine.PWM(machine.Pin(i)) for i in range(16)]

    # PWMの周波数設定 (50Hz -> 20ms周期)
    for pin in servo_pins:
        pin.freq(50)

    # 徐々に変化させるためのステップ数
    step_time = transition_time / steps

    # ステップごとに角度を徐々に変化させる
    for step in range(steps):
        for i in range(16):
            current_angle = current_angle_list[i] + (target_angle_list[i] - current_angle_list[i]) * (step + 1) / steps
            pulse_width = 500 + (2000 - 500) * current_angle / 180
            duty_cycle = pulse_width / 20000 * 100
            servo_pins[i].duty_u16(int(duty_cycle * 65535 / 100))
        utime.sleep_ms(step_time)

# 角度の初期値を設定 (90度)
initial_angle_list = [90] * 16

# 初期値にサーボモータをセット
control_servos(initial_angle_list, initial_angle_list, 0)

# 動作例
target_angle_list = [0, 45, 90, 135, 180, 90, 45, 135, 180, 90, 135, 180, 90, 45, 135, 180]
transition_time = 2000  # ミリ秒
control_servos(initial_angle_list, target_angle_list, transition_time)


#https://projecteuler.net/problem=36

def IsPal(N):
    X = list(str(N))
    Y = X[::-1] #this syntax is so weird
    if(X[0] == "0" or Y[0] == "0"):
        return False
    if(X == Y):
        return True
    return False

def ToBinary(N):
    return int("{0:b}".format(N))

def Main():
    Pals = []
    for i in range(0,1000000):
        if(IsPal(i) and IsPal(ToBinary(i))):
            Pals.append(i)
    print(sum(Pals))

Main()


from typing import Any
from ..decorator import method

@method
def get(data: Any, args: str):
    """
    functon implements $get functionality of parser
    Args:
        data: str , list or any data type where index can be applied
        args: index in double quotes e.g "0"
              range of index can be given as "1","4"

    Returns:
        subset of data in given index or range
    """
    args = args.split(",")
    args = [i.strip('"') for i in args]
    for arg in args:
        if not arg.isdigit():
            ValueError(f"args to function 'get' should be string containing index")

    if len(args) == 1:
        return data[int(args[0])]
    elif len(args) == 2:
        return data[int(args[0]) : int(args[1])]


import pprint

message = "It was a bright cold day in April, and the clocks were stiking thirteen."
count = {} #"r":12

for character in message.upper(): #upper() returns an uppercase form of the string
    count.setdefault(character, 0) #sets the counter to 0
    count[character] = count[character] + 1

pprint.pprint(count)

#Apriltext = pprint.pformat(count)
#print(Apriltext)

# pformat function returns the string (instead of being printed to the screen)


"""
    tablette.bases
    ~~~~~~~~~~~~~~

"""

__all__ = ['Tablette', 'Column']

from math import ceil
from operator import itemgetter
from six import callable, string_types
from termcolor import colored
from .utils import terminal_size

class Column(object):
    """
    Defines a Column.

    :ivar name: name of the column
    :ivar width: initial width of column.
    :ivar template: allows you to format the value field.
                    See `string formatting`_ for the full specification.
    :ivar term: based on termcolor_, it allows to stylizes the displayed field.
                its can be a string, list or a dict.
                Its also mays be a callable that must returns any of the
                previous types. The callable must have this signature::

                     lambda field_value: None

    .. _`string formatting`: http://bit.ly/VS4rQ2
    .. _`termcolor`: http://pypi.python.org/pypi/termcolor
    """

    __slot__ = ['name', 'width', 'template', 'term']

    def __init__(self, name, width, template=None, term=None):
        self.name = name
        self.width = width
        self.template = template or '{:{width}}'
        self.term = term

    def field(self, value):
        return Field(initial=value,
                     width=self.width,
                     template=self.template,
                     term=self.term)

    @property
    def header(self):
        return Header(self.name, self.width)

class Field(object):
    __slot__ = ['initial', 'width', 'template', 'term', 'resolved', 'length']

    def __init__(self, initial, width, template=None, term=None):
        self.initial = initial
        self.width = width
        self.template = template or '{:{width}}'

        if callable(term):
            term = term(initial)

        self.term = term

        self.resolved = self.template.format(self.initial, width=self.width)
        self.length = len(self.resolved)

    def __str__(self):
        return self.resolved

    def __len__(self):
        return self.length

class Header(object):
    __slot__ = ['initial', 'width', 'resolved', 'length']

    def __init__(self, initial, width):
        self.initial = initial
        self.width = width
        self.resolved = '{:{width}}'.format(self.initial, width=self.width)
        self.length = len(self.resolved)

    def __str__(self):
        return self.resolved

    def __len__(self):
        return self.length

class Tablette(object):
    """
    Decorates an iterator with tablette.

    :ivar columns: list of :class:Column
    :ivar data: the iterator
    """

    __slot__ = ['columns', 'data']

    def __init__(self, columns, data=None):
        """
        :param columns: a list where each element will be consumed
                        by :meth:add_column
        :param data: any iterator
        """
        self.data = data or []
        self.columns = []
        for column in columns:
            self.add_column(column)

    def add_column(self, obj):
        """
        Injects a new column.

        :param obj: it can be a string, dict or a Column instance.
        """

        if not isinstance(obj, Column):
            if isinstance(obj, dict):
                name = obj.get('name', '')
                template = obj.get('template', '{:{width}}')
                term = obj.get('term', None)
                if 'width' in obj:
                    width = obj['width']
                elif 'name' in obj:
                    width = len(obj['name'])
                else:
                    width = 1
            else:
                name = obj
                template = '{:{width}}'
                term = None
                width = len('{}'.format(name))
            obj = Column(name=name, width=width, template=template, term=term)
        self.columns.append(obj)

    @property
    def headers(self):
        return [column.header for column in self.columns]

    def __iter__(self):
        for values in self.data:
            fields = []
            for value, column in zip(values, self.columns):
                fields.append(column.field(value))
            yield fields

    def printer(self):
        return Printer(self)

class Printer(object):
    __slot__ = ['tablette', 'height', 'width']

    def __init__(self, tablette):
        self.tablette = tablette
        self.height = 50
        self.width = 50

    def __iter__(self):
        self.width, self.height = terminal_size()
        buffer = []
        for i, fields in enumerate(self.tablette):
            buffer.append(fields)
            if i and (i % self.height) == 0:
                for row in self.resolve_buffer(buffer):
                    yield row
        while buffer:
            for row in self.resolve_buffer(buffer):
                yield row

    def resolve_buffer(self, buffer):
        self.width, self.height = terminal_size()
        headers = self.tablette.headers
        rows = []

        # define optimistic length
        lengths = Length([header.length for header in headers])
        for fields in buffer:
            lengths.register([field.length for field in fields])

        sizes = lengths.caped(self.width)

        # do the heading fu
        rows.append('+-' + '-+-'.join('-' * size for size in sizes) + '-+')

        squares, lines = compartment(headers, sizes)
        for y in range(0, lines + 1):
            row = []
            for x, size in enumerate(sizes):
                row.append('{:{width}}'.format(squares.get((x, y), ' '),
                                               width=size))
            rows.append('| ' + ' | '.join(row) + ' |')
        rows.append('+-' + '-+-'.join('-' * size for size in sizes) + '-+')

        consumed_values = 0
        for fields in buffer:
            block = []
            squares, lines = compartment(fields, sizes)
            for y in range(0, lines + 1):
                row = []
                for x, size in enumerate(sizes):
                    response = '{:{width}}'.format(squares.get((x, y), ' '),
                                                   width=size)
                    term = fields[x].term
                    if isinstance(term, list):
                        response = colored(response, *term)
                    elif isinstance(term, dict):
                        response = colored(response, **term)
                    elif isinstance(term, string_types):
                        response = colored(response, term)
                    row.append(response)
                block.append('| ' + ' | '.join(row) + ' |')

            if len(rows) + len(block) < (self.height - 1):
                rows.extend(block)
                consumed_values += len(block)
            else:
                break
        rows.append('+-' + '-+-'.join('-' * size for size in sizes) + '-+')

        for i in range(len(rows), self.height - 1):
            rows.append('')

        buffer[:consumed_values] = []
        return rows

class Length(object):
    __slots__ = ['buffer']

    def __init__(self, sizes):
        self.buffer = [[size] for size in sizes]

    def register(self, sizes):
        for i, size in enumerate(sizes):
            self.buffer[i].append(size)

    @property
    def max(self):
        return [max(*d) for d in self.buffer]

    def caped(self, width):
        sizes = self.max[:]

        for i in range(10):
            # try to find the best match 10 times
            total = sum(sizes) + (len(sizes) * 2) + len(sizes) + 1
            if total <= width:
                break
            index, value = max(enumerate(sizes), key=itemgetter(1))
            sizes[index] = int(ceil(value / 2))
            total = sum(sizes) + (len(sizes) * 2) + len(sizes) + 1
            if total < width:
                sizes[index] += width - total
                break

        return sizes

def compartment(fields, sizes):
    x, y, lines = 0, 0, 0
    response = {}
    for x, (field, size) in enumerate(zip(fields, sizes)):
        y = 0
        field = str(field)
        while len(field) > size:
            tmp, field = field[:size], field[size:]
            response[(x, y)] = tmp
            y += 1
            lines = max(lines, y)
        field = '{:{width}}'.format(field, width=size)
        response[(x, y)] = '{:{width}}'.format(field, width=size)

    return response, lines