try:
                while self.running:
                    # Advance simulation time in delayed event
                    self.runUntilCurrent()
                    t2 = self.timeout()
                    t = self.running and t2
                    self.doIteration(t)
            except:
                log.msg("Unexpected error in main loop.")
                log.deferr()
            else:
                log.msg('Main loop terminated.')

"""Posix reactor base class

API Stability: stable

Maintainer: U{Itamar Shtull-Trauring<mailto:example@nnthink.com>}
"""

import warnings
import socket
import errno
import os
from zope.interface import implements, classImplements

from twisted.internet.interfaces import IReactorUNIX, IReactorUNIXDatagram
from twisted.internet.interfaces import IReactorTCP, IReactorUDP, IReactorSSL, IReactorArbitrary
from twisted.internet.interfaces import IReactorProcess, IReactorMulticast
from twisted.internet.interfaces import IHalfCloseableDescriptor
from twisted.internet import error
from twisted.internet import tcp, udp

from twisted.python import log, threadable, failure, components, util
from twisted.persisted import styles
from twisted.python.runtime import platformType, platform

from twisted.internet.base import ReactorBase

try:
    from twisted.internet import ssl
    sslEnabled = True
except ImportError:
    sslEnabled = False

try:
    from twisted.internet import unix
    unixEnabled = True
except ImportError:
    unixEnabled = False

processEnabled = False
if platformType == 'posix':
    from twisted.internet import fdesc
    import process
    processEnabled = True

if platform.isWindows():
    try:
        import win32process
        processEnabled = True
    except ImportError:
        win32process = None

class _Win32Waker(log.Logger, styles.Ephemeral):
    """I am a workaround for the lack of pipes on win32.

    I am a pair of connected sockets which can wake up the main loop
    from another thread.
    """
    disconnected = 0

    def __init__(self, reactor):
        """Initialize.
        """
        self.reactor = reactor
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.setsockopt(socket.IPPROTO_TCP, 1, 1)
        server.bind(('127.0.0.1', 0))
        server.listen(1)
        client.connect(server.getsockname())
        reader, clientaddr = server.accept()
        client.setblocking(0)
        reader.setblocking(0)
        self.r = reader
        self.w = client
        self.fileno = self.r.fileno

    def wakeUp(self):
        """Send a byte to my connection.
        """
        try:
            util.untilConcludes(self.w.send, 'x')
        except socket.error, (err, msg):
            if err != errno.WSAEWOULDBLOCK:
                raise

    def doRead(self):
        """Read some data from my connection.
        """
        try:
            self.r.recv(8192)
        except socket.error:
            pass

    def connectionLost(self, reason):
        self.r.close()
        self.w.close()
        self.reactor.waker = None

class _UnixWaker(log.Logger, styles.Ephemeral):
    """This class provides a simple interface to wake up the event loop.

    This is used by threads or signals to wake up the event loop.
    """
    disconnected = 0

    i = None
    o = None

    def __init__(self, reactor):
        """Initialize.
        """
        self.reactor = reactor
        self.i, self.o = os.pipe()
        fdesc.setNonBlocking(self.i)
        fdesc.setNonBlocking(self.o)
        self.fileno = lambda: self.i

    def doRead(self):
        """Read some bytes from the pipe.
        """
        fdesc.readFromFD(self.fileno(), lambda data: None)

    def wakeUp(self):
        """Write one byte to the pipe, and flush it.
        """
        if self.o is not None:
            try:
                util.untilConcludes(os.write, self.o, 'x')
            except OSError, e:
                if e.errno != errno.EAGAIN:
                    raise

    def connectionLost(self, reason):
        """Close both ends of my pipe.
        """
        if not hasattr(self, "o"):
            return
        for fd in self.i, self.o:
            try:
                os.close(fd)
            except IOError:
                pass
        del self.i, self.o
        self.reactor.waker = None

if platformType == 'posix':
    _Waker = _UnixWaker
elif platformType == 'win32':
    _Waker = _Win32Waker

class PosixReactorBase(ReactorBase):
    """A basis for reactors that use file descriptors.
    """
    implements(IReactorArbitrary, IReactorTCP, IReactorUDP, IReactorMulticast)

    def __init__(self):
        ReactorBase.__init__(self)
        if self.usingThreads or platformType == "posix":
            self.installWaker()

    def _handleSignals(self):
        """Install the signal handlers for the Twisted event loop."""
        try:
            import signal
        except ImportError:
            log.msg("Warning: signal module unavailable -- not installing signal handlers.")
            return

        if signal.getsignal(signal.SIGINT) == signal.default_int_handler:
            # only handle if there isn't already a handler, e.g. for Pdb.
            signal.signal(signal.SIGINT, self.sigInt)
        signal.signal(signal.SIGTERM, self.sigTerm)

        # Catch Ctrl-Break in windows
        if hasattr(signal, "SIGBREAK"):
            signal.signal(signal.SIGBREAK, self.sigBreak)

        if platformType == 'posix':
            signal.signal(signal.SIGCHLD, self._handleSigchld)

    def _handleSigchld(self, signum, frame, _threadSupport=platform.supportsThreads()):
        """Reap all processes on SIGCHLD.

        This gets called on SIGCHLD. We do no processing inside a signal
        handler, as the calls we make here could occur between any two
        python bytecode instructions. Deferring processing to the next
        eventloop round prevents us from violating the state constraints
        of arbitrary classes.
        """
        if _threadSupport:
            self.callFromThread(process.reapAllProcesses)
        else:
            self.callLater(0, process.reapAllProcesses)

    def startRunning(self, installSignalHandlers=1):
        # Just in case we're started on a different thread than
        # we're made on
        threadable.registerAsIOThread()

        self.fireSystemEvent('startup')
        if installSignalHandlers:
            self._handleSignals()
        self.running = 1

    def run(self, installSignalHandlers=1):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.mainLoop()

    def mainLoop(self):
        while self.running:

    def _disconnectSelectable(self, selectable, why, isRead, faildict={
        error.ConnectionDone: failure.Failure(error.ConnectionDone()),
        error.ConnectionLost: failure.Failure(error.ConnectionLost())
        }):
        """Utility function for disconnecting a selectable.

        Supports half-close notification, isRead should be boolean indicating
        whether error resulted from doRead().
        """
        self.removeReader(selectable)
        f = faildict.get(why.__class__)
        if f:
            if (isRead and why.__class__ ==  error.ConnectionDone
                and IHalfCloseableDescriptor.providedBy(selectable)):
                selectable.readConnectionLost(f)
            else:
                self.removeWriter(selectable)
                selectable.connectionLost(f)
        else:
            self.removeWriter(selectable)
            selectable.connectionLost(failure.Failure(why))

    def installWaker(self):
        """Install a `waker' to allow threads and signals to wake up the IO thread.

        We use the self-pipe trick (http://example.com) to wake
        the reactor. On Windows we use a pair of sockets.
        """
        if not self.waker:
            self.waker = _Waker(self)
            self.addReader(self.waker)

    # IReactorProcess

    def spawnProcess(self, processProtocol, executable, args=(),
                     env={}, path=None,
                     uid=None, gid=None, usePTY=0, childFDs=None):
        if platformType == 'posix':
            if usePTY:
                if childFDs is not None:
                    raise ValueError("Using childFDs is not supported with usePTY=True.")
                return process.PTYProcess(self, executable, args, env, path,
                                          processProtocol, uid, gid, usePTY)
            else:
                return process.Process(self, executable, args, env, path,
                                       processProtocol, uid, gid, childFDs)
        elif platformType == "win32":
            if uid is not None or gid is not None:
                raise ValueError("The uid and gid parameters are not supported on Windows.")
            if usePTY:
                raise ValueError("The usePTY parameter is not supported on Windows.")
            if childFDs:
                raise ValueError("Customizing childFDs is not supported on Windows.")

            if win32process:
                from twisted.internet._dumbwin32proc import Process
                return Process(self, processProtocol, executable, args, env, path)
            else:
                raise NotImplementedError, "spawnProcess not available since pywin32 is not installed."
        else:
            raise NotImplementedError, "spawnProcess only available on Windows or POSIX."

    # IReactorUDP

    def listenUDP(self, port, protocol, interface='', maxPacketSize=8192):
        """Connects a given L{DatagramProtocol} to the given numeric UDP port.

        @returns: object conforming to L{IListeningPort}.
        """
        p = udp.Port(port, protocol, interface, maxPacketSize, self)
        p.startListening()
        return p

    def connectUDP(self, remotehost, remoteport, protocol, localport=0,
                  interface='', maxPacketSize=8192):
        """DEPRECATED.

        Connects a L{ConnectedDatagramProtocol} instance to a UDP port.
        """
        warnings.warn("use listenUDP and then transport.connect().", DeprecationWarning, stacklevel=2)
        p = udp.ConnectedPort((remotehost, remoteport), localport, protocol, interface, maxPacketSize, self)
        p.startListening()
        return p

    # IReactorMulticast

    def listenMulticast(self, port, protocol, interface='', maxPacketSize=8192, listenMultiple=False):
        """Connects a given DatagramProtocol to the given numeric UDP port.

        EXPERIMENTAL.

        @returns: object conforming to IListeningPort.
        """
        p = udp.MulticastPort(port, protocol, interface, maxPacketSize, self, listenMultiple)
        p.startListening()
        return p

    # IReactorUNIX

    def connectUNIX(self, address, factory, timeout=30, checkPID=0):
        """@see: twisted.internet.interfaces.IReactorUNIX.connectUNIX
        """
        assert unixEnabled, "UNIX support is not present"
        c = unix.Connector(address, factory, timeout, self, checkPID)
        c.connect()
        return c

    def listenUNIX(self, address, factory, backlog=50, mode=0666, wantPID=0):
        """@see: twisted.internet.interfaces.IReactorUNIX.listenUNIX
        """
        assert unixEnabled, "UNIX support is not present"
        p = unix.Port(address, factory, backlog, mode, self, wantPID)
        p.startListening()
        return p

    # IReactorUNIXDatagram

    def listenUNIXDatagram(self, address, protocol, maxPacketSize=8192, mode=0666):
        """Connects a given L{DatagramProtocol} to the given path.

        EXPERIMENTAL.

        @returns: object conforming to L{IListeningPort}.
        """
        assert unixEnabled, "UNIX support is not present"
        p = unix.DatagramPort(address, protocol, maxPacketSize, mode, self)
        p.startListening()
        return p

    def connectUNIXDatagram(self, address, protocol, maxPacketSize=8192, mode=0666, bindAddress=None):
        """Connects a L{ConnectedDatagramProtocol} instance to a path.

        EXPERIMENTAL.
        """
        assert unixEnabled, "UNIX support is not present"
        p = unix.ConnectedDatagramPort(address, protocol, maxPacketSize, mode, bindAddress, self)
        p.startListening()
        return p

    # IReactorTCP

    def listenTCP(self, port, factory, backlog=50, interface=''):
        """@see: twisted.internet.interfaces.IReactorTCP.listenTCP
        """
        p = tcp.Port(port, factory, backlog, interface, self)
        p.startListening()
        return p

    def connectTCP(self, host, port, factory, timeout=30, bindAddress=None):
        """@see: twisted.internet.interfaces.IReactorTCP.connectTCP
        """
        c = tcp.Connector(host, port, factory, timeout, bindAddress, self)
        c.connect()
        return c

    def connectSSL(self, host, port, factory, contextFactory, timeout=30, bindAddress=None):
        """@see: twisted.internet.interfaces.IReactorSSL.connectSSL
        """
        assert sslEnabled, "SSL support is not present"
        c = ssl.Connector(host, port, factory, contextFactory, timeout, bindAddress, self)
        c.connect()
        return c

    def listenSSL(self, port, factory, contextFactory, backlog=50, interface=''):
        """@see: twisted.internet.interfaces.IReactorSSL.listenSSL
        """
        assert sslEnabled, "SSL support is not present"
        p = ssl.Port(port, factory, contextFactory, backlog, interface, self)
        p.startListening()
        return p

    # IReactorArbitrary
    def listenWith(self, portType, *args, **kw):
        kw['reactor'] = self
        p = portType(*args, **kw)
        p.startListening()
        return p

    def connectWith(self, connectorType, *args, **kw):
        kw['reactor'] = self
        c = connectorType(*args, **kw)
        c.connect()
        return c

    def _removeAll(self, readers, writers):
        """
        Remove all readers and writers, and return list of Selectables.

        Meant for calling from subclasses, to implement removeAll, like::

          def removeAll(self):
              return self._removeAll(reads, writes)

        where C{reads} and C{writes} are iterables.
        """
        readers = [reader for reader in readers if
                   reader is not self.waker]

        readers_dict = {}
        for reader in readers:
            readers_dict[reader] = 1

        for reader in readers:
            self.removeReader(reader)
            self.removeWriter(reader)

        writers = [writer for writer in writers if
                   writer not in readers_dict]
        for writer in writers:
            self.removeWriter(writer)

        return readers+writers

if sslEnabled:
    classImplements(PosixReactorBase, IReactorSSL)
if unixEnabled:
    classImplements(PosixReactorBase, IReactorUNIX, IReactorUNIXDatagram)
if processEnabled:
    classImplements(PosixReactorBase, IReactorProcess)

__all__ = ["PosixReactorBase"]


from vulture import core

from . import check

def check_ignore(code, ignore_names, ignore_decorators, expected):
    v = core.Vulture(
        verbose=True,
        ignore_names=ignore_names,
        ignore_decorators=ignore_decorators,
    )
    v.scan(code)
    check(v.get_unused_code(), expected)

def test_var():
    code = """\
fio = 1
fao = 2
bar = 2
ftobar = 3
baz = 10000
funny = True
"""
    check_ignore(code, ["f?o*", "ba[rz]"], [], ["funny"])

def test_function():
    code = """\
def foo_one():
    pass
def foo_two():
    pass
def foo():
    pass
def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_async_function():
    code = """\
async def foobar():
    pass
async def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_class():
    code = """\
class Foo:
    def __init__(self):
        pass
"""
    check_ignore(code, ["Foo"], [], [])

def test_class_ignore():
    code = """\
@bar
class Foo:
    pass

class Bar:
    pass
"""
    check_ignore(code, [], [], ["Foo", "Bar"])

def test_property():
    code = """\
class Foo:
    @property
    def some_property(self, a):
        return a

    @property
    @bar
    def foo_bar(self):
        return 'bar'
"""
    check_ignore(code, ["Foo"], ["@property"], [])
    check_ignore(code, ["Foo"], [], ["some_property", "foo_bar"])

def test_attribute():
    code = """\
class Foo:
    def __init__(self, attr_foo, attr_bar):
        self._attr_foo = attr_foo
        self._attr_bar = attr_bar
"""
    check_ignore(code, ["foo", "*_foo"], [], ["Foo", "_attr_bar"])

def test_decorated_functions():
    code = """\
def decor():
    return help

class FooBar:
    def foobar(self):
        return help

    @property
    def prop_one(self):
        pass

f = FooBar()

@decor()
def bar():
    pass

@f.foobar
def foo():
    pass

@bar
@foo
@f.foobar()
def barfoo():
    pass
"""
    check_ignore(code, [], ["@decor", "*@f.foobar"], ["prop_one"])
    check_ignore(code, [], ["*decor", "@*f.foobar"], ["prop_one"])

code = """\
@app.route('something')
@foobar
async def async_function():
    pass

@a.b.c
async def foo():
    pass
"""
    check_ignore(code, [], ["@app.route", "@a.b"], ["foo"])

def test_decorated_property():
    code = """\
@bar
@property
def foo():
    pass
"""
    check_ignore(code, [], ["@bar"], [])
    check_ignore(code, [], ["@baz"], ["foo"])
    check_ignore(code, [], ["@property"], [])

def test_decorated_property_reversed():
    code = """\
@property
@bar
def foo():
    pass
"""
    check_ignore(code, [], ["@bar"], [])
    check_ignore(code, [], ["@property"], [])
    check_ignore(code, [], ["@b*r"], [])
    check_ignore(code, [], ["@barfoo"], ["foo"])

def test_decorated_class():
    code = """\
@barfoo
@foo.bar('foo')
class Bar:
    def __init__(self):
        pass
"""
    check_ignore(code, [], [], ["Bar"])
    check_ignore(code, [], ["@bar*"], [])


code = """\
@property
@bar
def foo():
    pass
"""
    check_ignore(code, [], ["@bar"], [])
    check_ignore(code, [], ["@property"], [])
    check_ignore(code, [], ["@b*r"], [])
    check_ignore(code, [], ["@barfoo"], ["foo"])

from vulture import core

from . import check

def check_ignore(code, ignore_names, ignore_decorators, expected):
    v = core.Vulture(
        verbose=True,
        ignore_names=ignore_names,
        ignore_decorators=ignore_decorators,
    )
    v.scan(code)
    check(v.get_unused_code(), expected)

def test_var():
    code = """\
fio = 1
fao = 2
bar = 2
ftobar = 3
baz = 10000
funny = True
"""
    check_ignore(code, ["f?o*", "ba[rz]"], [], ["funny"])

def test_function():
    code = """\
def foo_one():
    pass
def foo_two():
    pass
def foo():
    pass
def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_async_function():
    code = """\
async def foobar():
    pass
async def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_class():
    code = """\
class Foo:
    def __init__(self):
        pass
"""
    check_ignore(code, ["Foo"], [], [])

def test_class_ignore():
    code = """\
@bar
class Foo:
    pass

class Bar:
    pass
"""
    check_ignore(code, [], [], ["Foo", "Bar"])

def test_property():
    code = """\
class Foo:
    @property
    def some_property(self, a):
        return a

    @property
    @bar
    def foo_bar(self):
        return 'bar'
"""
    check_ignore(code, ["Foo"], ["@property"], [])
    check_ignore(code, ["Foo"], [], ["some_property", "foo_bar"])

def test_attribute():
    code = """\
class Foo:
    def __init__(self, attr_foo, attr_bar):
        self._attr_foo = attr_foo
        self._attr_bar = attr_bar
"""
    check_ignore(code, ["foo", "*_foo"], [], ["Foo", "_attr_bar"])

def test_decorated_functions():
    code = """\
def decor():
    return help

class FooBar:
    def foobar(self):
        return help

    @property
    def prop_one(self):
        pass

f = FooBar()

@decor()
def bar():
    pass

@f.foobar
def foo():
    pass

@bar
@foo
@f.foobar()
def barfoo():
    pass
"""
    check_ignore(code, [], ["@decor", "*@f.foobar"], ["prop_one"])
    check_ignore(code, [], ["*decor", "@*f.foobar"], ["prop_one"])

def test_decorated_async_functions():
    code = """\
@app.route('something')
@foobar
async def async_function():
    pass

@a.b.c
async def foo():
    pass
"""
    check_ignore(code, [], ["@app.route", "@a.b"], ["foo"])

def test_decorated_property():
    code = """\
@bar
@property
def foo():
    pass
"""
    check_ignore(code, [], ["@bar"], [])
    check_ignore(code, [], ["@baz"], ["foo"])
    check_ignore(code, [], ["@property"], [])

def test_decorated_property_reversed():

def test_decorated_class():
    code = """\
@barfoo
@foo.bar('foo')
class Bar:
    def __init__(self):
        pass
"""
    check_ignore(code, [], [], ["Bar"])
    check_ignore(code, [], ["@bar*"], [])


from vulture import core

from . import check

def check_ignore(code, ignore_names, ignore_decorators, expected):
    v = core.Vulture(
        verbose=True,
        ignore_names=ignore_names,
        ignore_decorators=ignore_decorators,
    )
    v.scan(code)
    check(v.get_unused_code(), expected)

def test_var():
    code = """\
fio = 1
fao = 2
bar = 2
ftobar = 3
baz = 10000
funny = True
"""
    check_ignore(code, ["f?o*", "ba[rz]"], [], ["funny"])

def test_function():
    code = """\
def foo_one():
    pass
def foo_two():
    pass
def foo():
    pass
def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_async_function():
    code = """\
async def foobar():
    pass
async def bar():
    pass
"""
    check_ignore(code, ["foo*"], [], ["bar"])

def test_class():
    code = """\
class Foo:
    def __init__(self):
        pass
"""
    check_ignore(code, ["Foo"], [], [])

def test_class_ignore():
    code = """\
@bar
class Foo:
    pass

class Bar:
    pass
"""
    check_ignore(code, [], [], ["Foo", "Bar"])

def test_property():
    code = """\
class Foo:
    @property
    def some_property(self, a):
        return a

    @property
    @bar
    def foo_bar(self):
        return 'bar'
"""
    check_ignore(code, ["Foo"], ["@property"], [])
    check_ignore(code, ["Foo"], [], ["some_property", "foo_bar"])

def test_attribute():
    code = """\
class Foo:
    def __init__(self, attr_foo, attr_bar):
        self._attr_foo = attr_foo
        self._attr_bar = attr_bar
"""
    check_ignore(code, ["foo", "*_foo"], [], ["Foo", "_attr_bar"])

def test_decorated_functions():
    code = """\
def decor():
    return help

class FooBar:
    def foobar(self):
        return help

    @property
    def prop_one(self):
        pass

f = FooBar()

@decor()
def bar():
    pass

@f.foobar
def foo():
    pass

@bar
@foo
@f.foobar()
def barfoo():
    pass
"""
    check_ignore(code, [], ["@decor", "*@f.foobar"], ["prop_one"])
    check_ignore(code, [], ["*decor", "@*f.foobar"], ["prop_one"])

def test_decorated_async_functions():
    code = """\
@app.route('something')
@foobar
async def async_function():
    pass

@a.b.c
async def foo():
    pass
"""
    check_ignore(code, [], ["@app.route", "@a.b"], ["foo"])

def test_decorated_property():
    code = """\
@bar
@property
def foo():
    pass
"""
    check_ignore(code, [

    check_ignore(code, [], ["@baz"], ["foo"])
    check_ignore(code, [], ["@property"], [])

def test_decorated_property_reversed():
    code = """\
@property
@bar
def foo():
    pass
"""
    check_ignore(code, [], ["@bar"], [])
    check_ignore(code, [], ["@property"], [])
    check_ignore(code, [], ["@b*r"], [])
    check_ignore(code, [], ["@barfoo"], ["foo"])

def test_decorated_class():
    code = """\
@barfoo
@foo.bar('foo')
class Bar:
    def __init__(self):
        pass
"""
    check_ignore(code, [], [], ["Bar"])
    check_ignore(code, [], ["@bar*"], [])


# pylint: disable=missing-docstring

import attr
import six
from django.utils.http import urlencode
from six.moves.urllib.parse import parse_qs  # pylint: disable=import-error, ungrouped-imports

from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings

DEFAULT_CAMPAIGN_SOURCE = 'ace'
DEFAULT_CAMPAIGN_MEDIUM = 'email'

@attr.s
class CampaignTrackingInfo(object):
    """
    A struct for storing the set of UTM parameters that are recognized by tracking tools when included in URLs.
    """
    source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE)
    medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM)
    campaign = attr.ib(default=None)
    term = attr.ib(default=None)
    content = attr.ib(default=None)

    def to_query_string(self, existing_query_string=None):
        """
        Generate a query string that includes the tracking parameters in addition to any existing parameters.

        Note that any existing UTM parameters will be overridden by the values in this instance of CampaignTrackingInfo.

        Args:
            existing_query_string (str): An existing query string that needs to be updated to include this tracking
                information.

        Returns:
            str: The URL encoded string that should be used as the query string in the URL.
        """
        parameters = {}
        if existing_query_string is not None:
            parameters = parse_qs(existing_query_string)

        for attribute, value in six.iteritems(attr.asdict(self)):
            if value is not None:
                parameters['utm_' + attribute] = [value]
        return urlencode(parameters, doseq=True)

@attr.s
class GoogleAnalyticsTrackingPixel(object):
    """
    Implementation of the Google Analytics measurement protocol for email tracking.

    See this document for more info: http://example.com
    """
    ANONYMOUS_USER_CLIENT_ID = 555

    site = attr.ib(default=None)
    course_id = attr.ib(default=None)

    version = attr.ib(default=1, metadata={'param_name': 'v'})
    hit_type = attr.ib(default='event', metadata={'param_name': 't'})

    campaign_source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE, metadata={'param_name': 'cs'})
    campaign_medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM, metadata={'param_name': 'cm'})
    campaign_name = attr.ib(default=None, metadata={'param_name': 'cn'})
    campaign_content = attr.ib(default=None, metadata={'param_name': 'cc'})

    event_category = attr.ib(default='email', metadata={'param_name': 'ec'})
    event_action = attr.ib(default='edx.bi.email.opened', metadata={'param_name': 'ea'})
    event_label = attr.ib(default=None, metadata={'param_name': 'el'})

    document_path = attr.ib(default=None, metadata={'param_name': 'dp'})
    document_host = attr.ib(default=None, metadata={'param_name': 'dh'})

    user_id = attr.ib(default=None, metadata={'param_name': 'uid'})
    client_id = attr.ib(default=ANONYMOUS_USER_CLIENT_ID, metadata={'param_name': 'cid'})

"""
        A URL to a clear image that can be embedded in HTML documents to track email open events.

        The query string of this URL is used to capture data about the email and visitor.
        """
        parameters = {}
        fields = attr.fields(self.__class__)
        for attribute in fields:
            value = getattr(self, attribute.name, None)
            if value is not None and 'param_name' in attribute.metadata:
                parameter_name = attribute.metadata['param_name']
                parameters[parameter_name] = str(value)

        tracking_id = self._get_tracking_id()
        if tracking_id is None:
            return None

        parameters['tid'] = tracking_id

        user_id_dimension = get_config_value_from_site_or_settings(
            "GOOGLE_ANALYTICS_USER_ID_CUSTOM_DIMENSION",
            site=self.site,
        )
        if user_id_dimension is not None and self.user_id is not None:
            parameter_name = 'cd{0}'.format(user_id_dimension)
            parameters[parameter_name] = self.user_id

        if self.course_id is not None and self.event_label is None:
            param_name = fields.event_label.metadata['param_name']
            parameters[param_name] = six.text_type(self.course_id)

        return u"https://www.google-analytics.com/collect?{params}".format(params=urlencode(parameters))

    def _get_tracking_id(self):
        tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_ACCOUNT", site=self.site)
        if tracking_id is None:
            tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_TRACKING_ID", site=self.site)
        return tracking_id


tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_ACCOUNT", site=self.site)
        if tracking_id is None:
            tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_TRACKING_ID", site=self.site)
        return tracking_id

# pylint: disable=missing-docstring

import attr
import six
from django.utils.http import urlencode
from six.moves.urllib.parse import parse_qs  # pylint: disable=import-error, ungrouped-imports

from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings

DEFAULT_CAMPAIGN_SOURCE = 'ace'
DEFAULT_CAMPAIGN_MEDIUM = 'email'

@attr.s
class CampaignTrackingInfo(object):
    """
    A struct for storing the set of UTM parameters that are recognized by tracking tools when included in URLs.
    """
    source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE)
    medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM)
    campaign = attr.ib(default=None)
    term = attr.ib(default=None)
    content = attr.ib(default=None)

    def to_query_string(self, existing_query_string=None):
        """
        Generate a query string that includes the tracking parameters in addition to any existing parameters.

        Note that any existing UTM parameters will be overridden by the values in this instance of CampaignTrackingInfo.

        Args:
            existing_query_string (str): An existing query string that needs to be updated to include this tracking
                information.

        Returns:
            str: The URL encoded string that should be used as the query string in the URL.
        """
        parameters = {}
        if existing_query_string is not None:
            parameters = parse_qs(existing_query_string)

        for attribute, value in six.iteritems(attr.asdict(self)):
            if value is not None:
                parameters['utm_' + attribute] = [value]
        return urlencode(parameters, doseq=True)

@attr.s
class GoogleAnalyticsTrackingPixel(object):
    """
    Implementation of the Google Analytics measurement protocol for email tracking.

    See this document for more info: http://example.com
    """
    ANONYMOUS_USER_CLIENT_ID = 555

    site = attr.ib(default=None)
    course_id = attr.ib(default=None)

    version = attr.ib(default=1, metadata={'param_name': 'v'})
    hit_type = attr.ib(default='event', metadata={'param_name': 't'})

    campaign_source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE, metadata={'param_name': 'cs'})
    campaign_medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM, metadata={'param_name': 'cm'})
    campaign_name = attr.ib(default=None, metadata={'param_name': 'cn'})
    campaign_content = attr.ib(default=None, metadata={'param_name': 'cc'})

    event_category = attr.ib(default='email', metadata={'param_name': 'ec'})
    event_action = attr.ib(default='edx.bi.email.opened', metadata={'param_name': 'ea'})
    event_label = attr.ib(default=None, metadata={'param_name': 'el'})

    document_path = attr.ib(default=None, metadata={'param_name': 'dp'})
    document_host = attr.ib(default=None, metadata={'param_name': 'dh'})

    user_id = attr.ib(default=None, metadata={'param_name': 'uid'})
    client_id = attr.ib(default=ANONYMOUS_USER_CLIENT_ID, metadata={'param_name': 'cid'})

    def generate_image_url(self):
        """
        A URL to a clear image that can be embedded in HTML documents to track email open events.

        The query string of this URL is used to capture data about the email and visitor.
        """
        parameters = {}
        fields = attr.fields(self.__class__)
        for attribute in fields:
            value = getattr(self, attribute.name, None)
            if value is not None and 'param_name' in attribute.metadata:
                parameter_name = attribute.metadata['param_name']
                parameters[parameter_name] = str(value)

        tracking_id = self._get_tracking_id()
        if tracking_id is None:
            return None

        parameters['tid'] = tracking_id

        user_id_dimension = get_config_value_from_site_or_settings(
            "GOOGLE_ANALYTICS_USER_ID_CUSTOM_DIMENSION",
            site=self.site,
        )
        if user_id_dimension is not None and self.user_id is not None:
            parameter_name = 'cd{0}'.format(user_id_dimension)
            parameters[parameter_name] = self.user_id

        if self.course_id is not None and self.event_label is None:
            param_name = fields.event_label.metadata['param_name']
            parameters[param_name] = six.text_type(self.course_id)

        return u"https://www.google-analytics.com/collect?{params}".format(params=urlencode(parameters))

    def _get_tracking_id(self):


# pylint: disable=missing-docstring

import attr
import six
from django.utils.http import urlencode
from six.moves.urllib.parse import parse_qs  # pylint: disable=import-error, ungrouped-imports

from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings

DEFAULT_CAMPAIGN_SOURCE = 'ace'
DEFAULT_CAMPAIGN_MEDIUM = 'email'

@attr.s
class CampaignTrackingInfo(object):
    """
    A struct for storing the set of UTM parameters that are recognized by tracking tools when included in URLs.
    """
    source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE)
    medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM)
    campaign = attr.ib(default=None)
    term = attr.ib(default=None)
    content = attr.ib(default=None)

    def to_query_string(self, existing_query_string=None):
        """
        Generate a query string that includes the tracking parameters in addition to any existing parameters.

        Note that any existing UTM parameters will be overridden by the values in this instance of CampaignTrackingInfo.

        Args:
            existing_query_string (str): An existing query string that needs to be updated to include this tracking
                information.

        Returns:
            str: The URL encoded string that should be used as the query string in the URL.
        """
        parameters = {}
        if existing_query_string is not None:
            parameters = parse_qs(existing_query_string)

        for attribute, value in six.iteritems(attr.asdict(self)):
            if value is not None:
                parameters['utm_' + attribute] = [value]
        return urlencode(parameters, doseq=True)

@attr.s
class GoogleAnalyticsTrackingPixel(object):
    """
    Implementation of the Google Analytics measurement protocol for email tracking.

    See this document for more info: http://example.com
    """
    ANONYMOUS_USER_CLIENT_ID = 555

    site = attr.ib(default=None)
    course_id = attr.ib(default=None)

    version = attr.ib(default=1, metadata={'param_name': 'v'})
    hit_type = attr.ib(default='event', metadata={'param_name': 't'})

    campaign_source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE, metadata={'param_name': 'cs'})
    campaign_medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM, metadata={'param_name': 'cm'})
    campaign_name = attr.ib(default=None, metadata={'param_name': 'cn'})
    campaign_content = attr.ib(default=None, metadata={'param_name': 'cc'})

    event_category = attr.ib(default='email', metadata={'param_name': 'ec'})
    event_action = attr.ib(default='edx.bi.email.opened', metadata={'param_name': 'ea'})
    event_label = attr.ib(default=None, metadata={'param_name': 'el'})

    document_path = attr.ib(default=None, metadata={'param_name': 'dp'})
    document_host = attr.ib(default=None, metadata={'param_name': 'dh'})

    user_id = attr.ib(default=None, metadata={'param_name': 'uid'})
    client_id = attr.ib(default=ANONYMOUS_USER_CLIENT_ID, metadata={'param_name': 'cid'})

    def generate_image_url(self):
        """
        A URL to a clear image that can be embedded in HTML documents to track email open events.

        The query string of this URL is used to capture data about the email and visitor.
        """
        parameters = {}
        fields = attr.fields(self.__class__)
        for attribute in fields:
            value = getattr(self, attribute.name, None)
            if value is not None and 'param_name' in attribute.metadata:
                parameter_name = attribute.metadata['param_name']
                parameters[parameter_name] = str(value)

        tra

        if tracking_id is None:
            return None

        parameters['tid'] = tracking_id

        user_id_dimension = get_config_value_from_site_or_settings(
            "GOOGLE_ANALYTICS_USER_ID_CUSTOM_DIMENSION",
            site=self.site,
        )
        if user_id_dimension is not None and self.user_id is not None:
            parameter_name = 'cd{0}'.format(user_id_dimension)
            parameters[parameter_name] = self.user_id

        if self.course_id is not None and self.event_label is None:
            param_name = fields.event_label.metadata['param_name']
            parameters[param_name] = six.text_type(self.course_id)

        return u"https://www.google-analytics.com/collect?{params}".format(params=urlencode(parameters))

    def _get_tracking_id(self):
        tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_ACCOUNT", site=self.site)
        if tracking_id is None:
            tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_TRACKING_ID", site=self.site)
        return tracking_id


        parameters = {}
        if existing_query_string is not None:
            parameters = parse_qs(existing_query_string)

        for attribute, value in six.iteritems(attr.asdict(self)):
            if value is not None:
                parameters['utm_' + attribute] = [value]
        return urlencode(parameters, doseq=True)

# pylint: disable=missing-docstring

import attr
import six
from django.utils.http import urlencode
from six.moves.urllib.parse import parse_qs  # pylint: disable=import-error, ungrouped-imports

from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings

DEFAULT_CAMPAIGN_SOURCE = 'ace'
DEFAULT_CAMPAIGN_MEDIUM = 'email'

@attr.s
class CampaignTrackingInfo(object):
    """
    A struct for storing the set of UTM parameters that are recognized by tracking tools when included in URLs.
    """
    source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE)
    medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM)
    campaign = attr.ib(default=None)
    term = attr.ib(default=None)
    content = attr.ib(default=None)

    def to_query_string(self, existing_query_string=None):
        """
        Generate a query string that includes the tracking parameters in addition to any existing parameters.

        Note that any existing UTM parameters will be overridden by the values in this instance of CampaignTrackingInfo.

        Args:
            existing_query_string (str): An existing query string that needs to be updated to include this tracking
                information.

        Returns:
            str: The URL encoded string that should be used as the query string in the URL.
        """

@attr.s
class GoogleAnalyticsTrackingPixel(object):
    """
    Implementation of the Google Analytics measurement protocol for email tracking.

    See this document for more info: http://example.com
    """
    ANONYMOUS_USER_CLIENT_ID = 555

    site = attr.ib(default=None)
    course_id = attr.ib(default=None)

    version = attr.ib(default=1, metadata={'param_name': 'v'})
    hit_type = attr.ib(default='event', metadata={'param_name': 't'})

    campaign_source = attr.ib(default=DEFAULT_CAMPAIGN_SOURCE, metadata={'param_name': 'cs'})
    campaign_medium = attr.ib(default=DEFAULT_CAMPAIGN_MEDIUM, metadata={'param_name': 'cm'})
    campaign_name = attr.ib(default=None, metadata={'param_name': 'cn'})
    campaign_content = attr.ib(default=None, metadata={'param_name': 'cc'})

    event_category = attr.ib(default='email', metadata={'param_name': 'ec'})
    event_action = attr.ib(default='edx.bi.email.opened', metadata={'param_name': 'ea'})
    event_label = attr.ib(default=None, metadata={'param_name': 'el'})

    document_path = attr.ib(default=None, metadata={'param_name': 'dp'})
    document_host = attr.ib(default=None, metadata={'param_name': 'dh'})

    user_id = attr.ib(default=None, metadata={'param_name': 'uid'})
    client_id = attr.ib(default=ANONYMOUS_USER_CLIENT_ID, metadata={'param_name': 'cid'})

    def generate_image_url(self):
        """
        A URL to a clear image that can be embedded in HTML documents to track email open events.

        The query string of this URL is used to capture data about the email and visitor.
        """
        parameters = {}
        fields = attr.fields(self.__class__)
        for attribute in fields:
            value = getattr(self, attribute.name, None)
            if value is not None and 'param_name' in attribute.metadata:
                parameter_name = attribute.metadata['param_name']
                parameters[parameter_name] = str(value)

        tracking_id = self._get_tracking_id()
        if tracking_id is None:
            return None

        parameters['tid'] = tracking_id

        user_id_dimension = get_config_value_from_site_or_settings(
            "GOOGLE_ANALYTICS_USER_ID_CUSTOM_DIMENSION",
            site=self.site,
        )
        if user_id_dimension is not None and self.user_id is not None:
            parameter_name = 'cd{0}'.format(user_id_dimension)
            parameters[parameter_name] = self.user_id

        if self.course_id is not None and self.event_label is None:
            param_name = fields.event_label.metadata['param_name']
            parameters[param_name] = six.text_type(self.course_id)

        return u"https://www.google-analytics.com/collect?{params}".format(params=urlencode(parameters))

    def _get_tracking_id(self):
        tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_ACCOUNT", site=self.site)
        if tracking_id is None:
            tracking_id = get_config_value_from_site_or_settings("GOOGLE_ANALYTICS_TRACKING_ID", site=self.site)
        return tracking_id


import datetime

import requests
import requests_cache
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from api.commonresponses import DOWNSTREAM_ERROR_RESPONSE
from api.modules.github import constants
from api.modules.github.github_response import ContributorResponse, IssueResponse

requests_cache.install_cache(expire_after=datetime.timedelta(days=7))

@api_view(['GET'])
def get_contributors(request, project):
    """
    Return list of people contributed
    :param request:
    :param project:
    :return: 503 if github api fails
    :return: 200 successful
    """
    try:
        api_response = requests.get(
            constants.GITHUB_API_GET_CONTRIBUTORS_URL.format(project_name=project)
        )
        api_response_json = api_response.json()
        # if authentication fails
        if api_response.status_code == 401:
            raise Exception("Authentication fails. Invalid github access token.")
        response = []
        for contributor in api_response_json:
            if contributor['type'] != 'User':
                continue
            result = ContributorResponse(
                username=contributor['login'],
                url=contributor['html_url'],
                avatar_url=contributor['avatar_url'],
                contributions=contributor['contributions'],
                repository_name=project,
            )
            result_as_json = result.to_json()
            response.append(result_as_json)
    except Exception:
        return DOWNSTREAM_ERROR_RESPONSE
    return Response(response)

@api_view(['GET'])
def get_all_contributors(request):
    """
    Return list of people contributed
    :param request:
    :return: 503 if github api fails
    :return: 200 successful
    """
    response_dict = {}
    for project in constants.ACTIVE_REPOSITORIES:
        try:
            api_response = requests.get(
                constants.GITHUB_API_GET_CONTRIBUTORS_URL.format(project_name=project)
            )
            api_response_json = api_response.json()
            # if authentication fails
            if api_response.status_code == 401:
                raise Exception("Authentication fails. Invalid github access token.")
            for contributor in api_response_json:
                if contributor['type'] != 'User':
                    continue
                result = ContributorResponse(
                    username=contributor['login'],
                    url=contributor['html_url'],
                    avatar_url=contributor['avatar_url'],
                    contributions=contributor['contributions'],
                    repository_name=[project],
                )
                if result.username in response_dict.keys():
                    response_dict[result.username]['contributions'] += result.contributions
                    response_dict[result.username]['repository_name'].append(project)
                else:
                    response_dict[result.username] = result.to_json()
        except Exception:
            return DOWNSTREAM_ERROR_RESPONSE
    response = sorted(response_dict.values(), key=lambda x: x['contributions'], reverse=True)
    return Response(response)

@api_view(['GET'])

"""
        Return list of issues
        :param request:
        :param project:
        :return: 503 if github api fails
        :return: 200 successful
        """

    try:
        api_response = requests.get(constants.GITHUB_API_GET_ISSUES_URL.format(project_name=project))
        api_response_json = api_response.json()
        if api_response.status_code == 404:
            error_message = "Repository does not exist"
            return Response(error_message, status=status.HTTP_404_NOT_FOUND)
        if api_response.status_code == 401:
            raise Exception("Authentication fails. Invalid github access token.")
        response = []
        for issue in api_response_json:
            labels_length = len(issue['labels'])
            tags = []
            # Making custom dictionary for tags
            for i in range(0, labels_length):
                # Searching inside "labels" key for tag_name
                for tag, tag_name in issue["labels"][i].items():
                    if tag in ["name"]:
                        label = tag_name
                tags.append(label)
            result = IssueResponse(
                title=issue['title'],
                created_at=issue['created_at'],
                comments=issue['comments'],
                issue_number=issue['number'],
                repository_url=issue['repository_url'],
                labels=tags
            )
            result_as_json = result.to_json()
            response.append(result_as_json)

    except Exception:
        return DOWNSTREAM_ERROR_RESPONSE

    return Response(response)


import datetime

import requests
import requests_cache
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from api.commonresponses import DOWNSTREAM_ERROR_RESPONSE
from api.modules.github import constants
from api.modules.github.github_response import ContributorResponse, IssueResponse

requests_cache.install_cache(expire_after=datetime.timedelta(days=7))

@api_view(['GET'])
def get_contributors(request, project):
    """
    Return list of people contributed
    :param request:
    :param project:
    :return: 503 if github api fails
    :return: 200 successful
    """
    try:
        api_response = requests.get(
            constants.GITHUB_API_GET_CONTRIBUTORS_URL.format(project_name=project)
        )
        api_response_json = api_response.json()
        # if authentication fails
        if api_response.status_code == 401:
            raise Exception("Authentication fails. Invalid github access token.")
        response = []
        for contributor in api_response_json:
            if contributor['type'] != 'User':
                continue
            result = ContributorResponse(
                username=contributor['login'],
                url=contributor['html_url'],
                avatar_url=contributor['avatar_url'],
                contributions=contributor['contributions'],
                repository_name=project,
            )
            result_as_json = result.to_json()
            response.append(result_as_json)
    except Exception:
        return DOWNSTREAM_ERROR_RESPONSE
    return Response(respons

@api_view(['GET'])
def get_all_contributors(request):
    """
    Return list of people contributed
    :param request:
    :return: 503 if github api fails
    :return: 200 successful
    """
    response_dict = {}
    for project in constants.ACTIVE_REPOSITORIES:
        try:
            api_response = requests.get(
                constants.GITHUB_API_GET_CONTRIBUTORS_URL.format(project_name=project)
            )
            api_response_json = api_response.json()
            # if authentication fails
            if api_response.status_code == 401:
                raise Exception("Authentication fails. Invalid github access token.")
            for contributor in api_response_json:
                if contributor['type'] != 'User':
                    continue
                result = ContributorResponse(
                    username=contributor['login'],
                    url=contributor['html_url'],
                    avatar_url=contributor['avatar_url'],
                    contributions=contributor['contributions'],
                    repository_name=[project],
                )
                if result.username in response_dict.keys():
                    response_dict[result.username]['contributions'] += result.contributions
                    response_dict[result.username]['repository_name'].append(project)
                else:
                    response_dict[result.username] = result.to_json()
        except Exception:
            return DOWNSTREAM_ERROR_RESPONSE
    response = sorted(response_dict.values(), key=lambda x: x['contributions'], reverse=True)
    return Response(response)

@api_view(['GET'])
def get_issues(request, project):
    """
        Return list of issues
        :param request:
        :param project:
        :return: 503 if github api fails
        :return: 200 successful
        """

    try:
        api_response = requests.get(constants.GITHUB_API_GET_ISSUES_URL.format(project_name=project))
        api_response_json = api_response.json()
        if api_response.status_code == 404:
            error_message = "Repository does not exist"
            return Response(error_message, status=status.HTTP_404_NOT_FOUND)
        if api_response.status_code == 401:
            raise Exception("Authentication fails. Invalid github access token.")
        response = []
        for issue in api_response_json:
            labels_length = len(issue['labels'])
            tags = []
            # Making custom dictionary for tags
            for i in range(0, labels_length):
                # Searching inside "labels" key for tag_name
                for tag, tag_name in issue["labels"][i].items():
                    if tag in ["name"]:
                        label = tag_name
                tags.append(label)
            result = IssueResponse(
                title=issue['title'],
                created_at=issue['created_at'],
                comments=issue['comments'],
                issue_number=issue['number'],
                repository_url=issue['repository_url'],
                labels=tags
            )
            result_as_json = result.to_json()
            response.append(result_as_json)

    except Exception:
        return DOWNSTREAM_ERROR_RESPONSE

    return Response(response)


def __init__(
        self, plotly_name="exponentformat", parent_name="bar.marker.colorbar", **kwargs
    ):

import _plotly_utils.basevalidators

class ExponentformatValidator(_plotly_utils.basevalidators.EnumeratedValidator):

super(ExponentformatValidator, self).__init__(
            plotly_name=plotly_name,
            parent_name=parent_name,
            edit_type=kwargs.pop("edit_type", "colorbars"),
            role=kwargs.pop("role", "style"),
            values=kwargs.pop("values", ["none", "e", "E", "power", "SI", "B"]),
            **kwargs
        )


super(ExponentformatValidator, self).__init__(
            plotly_name=plotly_name,
            parent_name=parent_name,
            edit_type=kwargs.pop("edit_type", "colorbars"),
            role=kwargs.pop("role", "style"),
            values=kwargs.pop("values", ["none", "e", "E", "power", "SI", "B"]),
            **kwargs
        )

import _plotly_utils.basevalidators

class ExponentformatValidator(_plotly_utils.basevalidators.EnumeratedValidator):
    def __init__(
        self, plotly_name="exponentformat", parent_name="bar.marker.colorbar", **kwargs
    ):


import math, pickle
import numpy as np
import os.path as path
from utils import util_methods
import torch, torch.nn as nn, torch.nn.functional as F
from pdb import set_trace as stop
from tqdm import tqdm

if split == 'train':
        ChromeModel.train()
    else:
        ChromeModel.eval()

    all_preds = torch.Tensor().cpu()
    all_targets = torch.Tensor().cpu()

    total_loss = 0

    if opt.adj_type in ['hic','both']:
        graph_file = path.join(opt.graph_root,split+'_graphs_'+opt.hicsize+'_'+opt.hicnorm+'norm.pkl')
        print(graph_file)
        split_adj_dict = pickle.load(open(graph_file, "rb" ))
    else:
        split_adj_dict = None

    chrom_count = len(chrom_feature_dict)

    for chrom in tqdm(chrom_feature_dict, mininterval=0.5,desc='('+split+')', leave=False):
        x_f = chrom_feature_dict[chrom]['forward'].cuda()
        x_r = chrom_feature_dict[chrom]['backward'].cuda()
        targets = chrom_feature_dict[chrom]['target'].cuda()
        x_f.requires_grad = True
        x_r.requires_grad = True

        split_adj = util_methods.process_graph(opt.adj_type,split_adj_dict,x_f.size(0),chrom).cuda()

        if split=='train':
            optimizer.zero_grad()

        _,pred_f,_,z_f = ChromeModel(x_f,split_adj,None)
        _,pred_r,_,z_r = ChromeModel(x_r,split_adj,None)
        pred = (pred_f+pred_r)/2

        loss = F.binary_cross_entropy_with_logits(pred, targets.float())

        if split=='train':
            loss.backward()
            optimizer.step()

        total_loss += loss.sum().item()
        all_preds  = torch.cat((all_preds,F.sigmoid(pred).cpu().data),0)
        all_targets  = torch.cat((all_targets,targets.cpu().data),0)

        # Compare to CNN Preds

    return all_preds,all_targets,total_loss


import math, pickle
import numpy as np
import os.path as path
from utils import util_methods
import torch, torch.nn as nn, torch.nn.functional as F
from pdb import set_trace as stop
from tqdm import tqdm

def finetune(WindowModel,ChromeModel,chrom_feature_dict,crit,optimizer,epoch,data_dict,opt,split):
    if split == 'train':
        ChromeModel.train()
    else:
        ChromeModel.eval()

    all_preds = torch.Tensor()

    all_targets = torch.Tensor().cpu()

    total_loss = 0

    if opt.adj_type in ['hic','both']:
        graph_file = path.join(opt.graph_root,split+'_graphs_'+opt.hicsize+'_'+opt.hicnorm+'norm.pkl')
        print(graph_file)
        split_adj_dict = pickle.load(open(graph_file, "rb" ))
    else:
        split_adj_dict = None

    chrom_count = len(chrom_feature_dict)

    for chrom in tqdm(chrom_feature_dict, mininterval=0.5,desc='('+split+')', leave=False):
        x_f = chrom_feature_dict[chrom]['forward'].cuda()
        x_r = chrom_feature_dict[chrom]['backward'].cuda()
        targets = chrom_feature_dict[chrom]['target'].cuda()
        x_f.requires_grad = True
        x_r.requires_grad = True

        split_adj = util_methods.process_graph(opt.adj_type,split_adj_dict,x_f.size(0),chrom).cuda()

        if split=='train':
            optimizer.zero_grad()

        _,pred_f,_,z_f = ChromeModel(x_f,split_adj,None)
        _,pred_r,_,z_r = ChromeModel(x_r,split_adj,None)
        pred = (pred_f+pred_r)/2

        loss = F.binary_cross_entropy_with_logits(pred, targets.float())

        if split=='train':
            loss.backward()
            optimizer.step()

        total_loss += loss.sum().item()
        all_preds  = torch.cat((all_preds,F.sigmoid(pred).cpu().data),0)
        all_targets  = torch.cat((all_targets,targets.cpu().data),0)

        # Compare to CNN Preds

    return all_preds,all_targets,total_loss


'''
    File name: test_gin_rummy_game.py
    Author: William Hale
    Date created: 3/11/2020
'''

import unittest
import numpy as np

import rlcard.games.gin_rummy.judge as judge
import rlcard.games.gin_rummy.utils.utils as utils

from rlcard.games.gin_rummy.dealer import GinRummyDealer
from rlcard.games.gin_rummy.game import GinRummyGame as Game
from rlcard.games.gin_rummy.player import GinRummyPlayer
from rlcard.games.gin_rummy.utils.action_event import score_player_1_action_id
from rlcard.games.gin_rummy.utils.action_event import draw_card_action_id, pick_up_discard_action_id
from rlcard.games.gin_rummy.utils.action_event import declare_dead_hand_action_id
from rlcard.games.gin_rummy.utils.action_event import gin_action_id, discard_action_id, knock_action_id
from rlcard.games.gin_rummy.utils.melding import get_all_set_melds, get_all_run_melds, get_meld_clusters
from rlcard.games.gin_rummy.utils.settings import Setting, Settings
from rlcard.games.gin_rummy.utils.thinker import Thinker

discard_action_ids = list(range(discard_action_id, discard_action_id + 52))
knock_action_ids = list(range(knock_action_id, knock_action_id + 52))
put_action_ids = [gin_action_id] + discard_action_ids + knock_action_ids
get_action_ids = [draw_card_action_id, pick_up_discard_action_id, declare_dead_hand_action_id]

class TestGinRummyGame(unittest.TestCase):

    def test_get_num_players(self):
        game = Game()
        num_players = game.get_num_players()
        self.assertEqual(num_players, 2)

    def test_get_num_actions(self):
        game = Game()
        num_actions = game.get_num_actions()
        self.assertEqual(num_actions, 110)

    def test_init_game(self):
        game = Game()
        state, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        self.assertEqual(len(game.round.move_sheet), 1)
        self.assertIn(current_player, [0, 1])
        self.assertIn(game.round.dealer_id, [0, 1])
        self.assertEqual(len(game.actions), 0)
        self.assertEqual(opponent_player, game.round.dealer_id)  # opponent_player is dealer
        self.assertEqual(len(game.round.players[opponent_player].hand), 10)  # dealer has 10 cards
        self.assertEqual(len(game.round.players[current_player].hand), 11)  # current_player has 11 cards
        self.assertEqual(len(game.round.dealer.shuffled_deck), 52)
        self.assertEqual(len(game.round.dealer.stock_pile), 31)
        self.assertEqual(state['player_id'], current_player)
        self.assertEqual(len(state['hand']), 11)

game = Game()
        _, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        action = np.random.choice(game.judge.get_legal_actions())
        self.assertIn(action.action_id, put_action_ids)  # should be a put action
        _, next_player = game.step(action)
        if not game.is_over():
            self.assertEqual(next_player, opponent_player)
        if not game.is_over():
            action = np.random.choice(game.judge.get_legal_actions())
            self.assertIn(action.action_id, get_action_ids)  # should be a get action
            _, next_player = game.step(action)
            self.assertEqual(next_player, opponent_player)  # keep turn to put card

    def test_proceed_game(self):
        game = Game()
        game.init_game()
        while not game.is_over():
            legal_actions = game.judge.get_legal_actions()
            action = np.random.choice(legal_actions)
            _, _ = game.step(action)
        self.assertEqual(game.actions[-1].action_id, score_player_1_action_id)

    def test_get_state(self):
        game = Game()
        state, _ = game.init_game()
        self.assertEqual(len(state), 6)

    def test_gin_rummy_dealer(self):
        dealer = GinRummyDealer(np.random.RandomState())
        current_deck = utils.get_deck()
        deck_card_ids = [utils.get_card_id(card) for card in current_deck]
        self.assertEqual(deck_card_ids, list(range(52)))
        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        dealer.deal_cards(player=player, num=10)
        self.assertEqual(len(dealer.shuffled_deck), 52)
        self.assertEqual(len(dealer.stock_pile), 42)
        self.assertEqual(len(current_deck), 52)
        self.assertEqual(len(utils.get_deck()), 52)
        # Pop top_card from current_deck.
        top_card = current_deck.pop(-1)
        self.assertEqual(str(top_card), "KC")
        self.assertEqual(len(current_deck), 51)
        self.assertEqual(len(utils.get_deck()), 52)

    def test_knocking(self):
        hand_text = ['JS', 'JH', 'JD', '8C', '7S', '7H', '7D', '4S', '3D', '2S', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        knock_cards, _ = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=10)
        self.assertEqual(set(knock_cards), set([utils.card_from_text(x) for x in ['8C']]))

    def test_melding(self):
        hand_text = ['9H', 'AC', 'TH', '3C', '3D', '7C', 'QH', '3H', '8C', '8D',
                     '4D', '7H', '8S', '5H', '4H', 'AS', 'TD', '3S', '2S', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]

        # check
        all_set_melds_text = [['3C', '3D', '3H', '3S'], ['8C', '8D', '8S'], ['AC', 'AS', 'AH'], ['3D', '3H', '3S'],
                              ['3C', '3H', '3S'], ['3C', '3D', '3S'], ['3C', '3D', '3H']]
        all_set_melds_text = set([frozenset(x) for x in all_set_melds_text])
        all_set_melds = get_all_set_melds(hand)
        self.assertEqual(all_set_melds_text,
                         set([frozenset([str(card) for card in meld_pile]) for meld_pile in all_set_melds]))

        # check
        all_run_melds_text = [['AS', '2S', '3S'], ['3H', '4H', '5H']]
        all_run_melds = get_all_run_melds(hand)
        self.assertEqual(all_run_melds_text, [[str(card) for card in meld_pile] for meld_pile in all_run_melds])

        # check
        meld_clusters = get_meld_clusters(hand=hand)
        self.assertEqual(len(meld_clusters), 36)

    def test_melding_2(self):  # 2020-Apr-19
        hand_text = ['8D', '5S', '5H', '5D', '4H', '4C', '3H', '2H', '2D', '2C', 'AD']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])
        self.assertEqual(knock_cards, [utils.card_from_text('8D')])
        self.assertEqual(gin_cards, [])

    def test_melding_3(self):  # 2020-Apr-19
        hand_text = ['7H', '6H', '5H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])

        correct_knock_cards = [utils.card_from_text(x) for x in ['7H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']]
        self.assertEqual(set(knock_cards), set(correct_knock_cards))
        self.assertEqual(gin_cards, [])

    def test_corrected_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        corrected_config = Settings.get_config_with_invalid_settings_set_to_default_value(config=config)
        self.assertEqual(corrected_config[Setting.max_drawn_card_count], 10)
        self.assertEqual(corrected_config[Setting.stockpile_dead_card_count],
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(corrected_config[Setting.is_allowed_pick_up_discard],
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_change_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        settings = Settings()
        settings.change_settings(config=config)
        self.assertEqual(settings.max_drawn_card_count, 10)
        self.assertEqual(settings.stockpile_dead_card_count,
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(settings.is_allowed_pick_up_discard,
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_decode_cards(self):
        deck = utils.get_deck()
        encoded_cards = utils.encode_cards(deck)
        decoded_cards = utils.decode_cards(encoded_cards)
        for i in range(52):
            card = deck[i]
            decoded_card = decoded_cards[i]
            self.assertEqual(card, decoded_card)

    def test_get_meld_piles_with_discard_card(self):
        hand_text = ['8D', '7D', '6S', '5H', '5C', '4C', '4S', '2S', 'AC', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]
        discard_card = utils.card_from_text('6D')
        thinker = Thinker(hand=hand)
        meld_piles_with_discard_card = thinker.get_meld_piles_with_discard_card(discard_card=discard_card)
        result_as_set = frozenset([frozenset(meld_pile) for meld_pile in meld_piles_with_discard_card])
        correct_result = [[utils.card_from_text(x) for x in ['8D', '7D', '6D']]]
        correct_result_as_set = frozenset([frozenset(meld_pile) for meld_pile in correct_result])
        self.assertEqual(result_as_set, correct_result_as_set)

if __name__ == '__main__':
    unittest.main()


game = Game()
        num_actions = game.get_num_actions()
        self.assertEqual(num_actions, 110)

'''
    File name: test_gin_rummy_game.py
    Author: William Hale
    Date created: 3/11/2020
'''

import unittest
import numpy as np

import rlcard.games.gin_rummy.judge as judge
import rlcard.games.gin_rummy.utils.utils as utils

from rlcard.games.gin_rummy.dealer import GinRummyDealer
from rlcard.games.gin_rummy.game import GinRummyGame as Game
from rlcard.games.gin_rummy.player import GinRummyPlayer
from rlcard.games.gin_rummy.utils.action_event import score_player_1_action_id
from rlcard.games.gin_rummy.utils.action_event import draw_card_action_id, pick_up_discard_action_id
from rlcard.games.gin_rummy.utils.action_event import declare_dead_hand_action_id
from rlcard.games.gin_rummy.utils.action_event import gin_action_id, discard_action_id, knock_action_id
from rlcard.games.gin_rummy.utils.melding import get_all_set_melds, get_all_run_melds, get_meld_clusters
from rlcard.games.gin_rummy.utils.settings import Setting, Settings
from rlcard.games.gin_rummy.utils.thinker import Thinker

discard_action_ids = list(range(discard_action_id, discard_action_id + 52))
knock_action_ids = list(range(knock_action_id, knock_action_id + 52))
put_action_ids = [gin_action_id] + discard_action_ids + knock_action_ids
get_action_ids = [draw_card_action_id, pick_up_discard_action_id, declare_dead_hand_action_id]

class TestGinRummyGame(unittest.TestCase):

    def test_get_num_players(self):
        game = Game()
        num_players = game.get_num_players()
        self.assertEqual(num_players, 2)

    def test_get_num_actions(self):

    def test_init_game(self):
        game = Game()
        state, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        self.assertEqual(len(game.round.move_sheet), 1)
        self.assertIn(current_player, [0, 1])
        self.assertIn(game.round.dealer_id, [0, 1])
        self.assertEqual(len(game.actions), 0)
        self.assertEqual(opponent_player, game.round.dealer_id)  # opponent_player is dealer
        self.assertEqual(len(game.round.players[opponent_player].hand), 10)  # dealer has 10 cards
        self.assertEqual(len(game.round.players[current_player].hand), 11)  # current_player has 11 cards
        self.assertEqual(len(game.round.dealer.shuffled_deck), 52)
        self.assertEqual(len(game.round.dealer.stock_pile), 31)
        self.assertEqual(state['player_id'], current_player)
        self.assertEqual(len(state['hand']), 11)

    def test_step(self):
        game = Game()
        _, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        action = np.random.choice(game.judge.get_legal_actions())
        self.assertIn(action.action_id, put_action_ids)  # should be a put action
        _, next_player = game.step(action)
        if not game.is_over():
            self.assertEqual(next_player, opponent_player)
        if not game.is_over():
            action = np.random.choice(game.judge.get_legal_actions())
            self.assertIn(action.action_id, get_action_ids)  # should be a get action
            _, next_player = game.step(action)
            self.assertEqual(next_player, opponent_player)  # keep turn to put card

    def test_proceed_game(self):
        game = Game()
        game.init_game()
        while not game.is_over():
            legal_actions = game.judge.get_legal_actions()
            action = np.random.choice(legal_actions)
            _, _ = game.step(action)
        self.assertEqual(game.actions[-1].action_id, score_player_1_action_id)

    def test_get_state(self):
        game = Game()
        state, _ = game.init_game()
        self.assertEqual(len(state), 6)

    def test_gin_rummy_dealer(self):
        dealer = GinRummyDealer(np.random.RandomState())
        current_deck = utils.get_deck()
        deck_card_ids = [utils.get_card_id(card) for card in current_deck]
        self.assertEqual(deck_card_ids, list(range(52)))
        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        dealer.deal_cards(player=player, num=10)
        self.assertEqual(len(dealer.shuffled_deck), 52)
        self.assertEqual(len(dealer.stock_pile), 42)
        self.assertEqual(len(current_deck), 52)
        self.assertEqual(len(utils.get_deck()), 52)
        # Pop top_card from current_deck.
        top_card = current_deck.pop(-1)
        self.assertEqual(str(top_card), "KC")
        self.assertEqual(len(current_deck), 51)
        self.assertEqual(len(utils.get_deck()), 52)

    def test_knocking(self):
        hand_text = ['JS', 'JH', 'JD', '8C', '7S', '7H', '7D', '4S', '3D', '2S', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        knock_cards, _ = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=10)
        self.assertEqual(set(knock_cards), set([utils.card_from_text(x) for x in ['8C']]))

    def test_melding(self):
        hand_text = ['9H', 'AC', 'TH', '3C', '3D', '7C', 'QH', '3H', '8C', '8D',
                     '4D', '7H', '8S', '5H', '4H', 'AS', 'TD', '3S', '2S', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]

        # check
        all_set_melds_text = [['3C', '3D', '3H', '3S'], ['8C', '8D', '8S'], ['AC', 'AS', 'AH'], ['3D', '3H', '3S'],
                              ['3C', '3H', '3S'], ['3C', '3D', '3S'], ['3C', '3D', '3H']]
        all_set_melds_text = set([frozenset(x) for x in all_set_melds_text])
        all_set_melds = get_all_set_melds(hand)
        self.assertEqual(all_set_melds_text,
                         set([frozenset([str(card) for card in meld_pile]) for meld_pile in all_set_melds]))

        # check
        all_run_melds_text = [['AS', '2S', '3S'], ['3H', '4H', '5H']]
        all_run_melds = get_all_run_melds(hand)
        self.assertEqual(all_run_melds_text, [[str(card) for card in meld_pile] for meld_pile in all_run_melds])

        # check
        meld_clusters = get_meld_clusters(hand=hand)
        self.assertEqual(len(meld_clusters), 36)

    def test_melding_2(self):  # 2020-Apr-19
        hand_text = ['8D', '5S', '5H', '5D', '4H', '4C', '3H', '2H', '2D', '2C', 'AD']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])
        self.assertEqual(knock_cards, [utils.card_from_text('8D')])
        self.assertEqual(gin_cards, [])

    def test_melding_3(self):  # 2020-Apr-19
        hand_text = ['7H', '6H', '5H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])

        correct_knock_cards = [utils.card_from_text(x) for x in ['7H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']]
        self.assertEqual(set(knock_cards), set(correct_knock_cards))
        self.assertEqual(gin_cards, [])

    def test_corrected_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        corrected_config = Settings.get_config_with_invalid_settings_set_to_default_value(config=config)
        self.assertEqual(corrected_config[Setting.max_drawn_card_count], 10)
        self.assertEqual(corrected_config[Setting.stockpile_dead_card_count],
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(corrected_config[Setting.is_allowed_pick_up_discard],
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_change_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        settings = Settings()
        settings.change_settings(config=config)
        self.assertEqual(settings.max_drawn_card_count, 10)
        self.assertEqual(settings.stockpile_dead_card_count,
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(settings.is_allowed_pick_up_discard,
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_decode_cards(self):
        deck = utils.get_deck()
        encoded_cards = utils.encode_cards(deck)
        decoded_cards = utils.decode_cards(encoded_cards)
        for i in range(52):
            card = deck[i]
            decoded_card = decoded_cards[i]
            self.assertEqual(card, decoded_card)

    def test_get_meld_piles_with_discard_card(self):
        hand_text = ['8D', '7D', '6S', '5H', '5C', '4C', '4S', '2S', 'AC', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]
        discard_card = utils.card_from_text('6D')
        thinker = Thinker(hand=hand)
        meld_piles_with_discard_card = thinker.get_meld_piles_with_discard_card(discard_card=discard_card)
        result_as_set = frozenset([frozenset(meld_pile) for meld_pile in meld_piles_with_discard_card])
        correct_result = [[utils.card_from_text(x) for x in ['8D', '7D', '6D']]]
        correct_result_as_set = frozenset([frozenset(meld_pile) for meld_pile in correct_result])
        self.assertEqual(result_as_set, correct_result_as_set)

if __name__ == '__main__':
    unittest.main()


'''
    File name: test_gin_rummy_game.py
    Author: William Hale
    Date created: 3/11/2020
'''

import unittest
import numpy as np

import rlcard.games.gin_rummy.judge as judge
import rlcard.games.gin_rummy.utils.utils as utils

from rlcard.games.gin_rummy.dealer import GinRummyDealer
from rlcard.games.gin_rummy.game import GinRummyGame as Game
from rlcard.games.gin_rummy.player import GinRummyPlayer
from rlcard.games.gin_rummy.utils.action_event import score_player_1_action_id
from rlcard.games.gin_rummy.utils.action_event import draw_card_action_id, pick_up_discard_action_id
from rlcard.games.gin_rummy.utils.action_event import declare_dead_hand_action_id
from rlcard.games.gin_rummy.utils.action_event import gin_action_id, discard_action_id, knock_action_id
from rlcard.games.gin_rummy.utils.melding import get_all_set_melds, get_all_run_melds, get_meld_clusters
from rlcard.games.gin_rummy.utils.settings import Setting, Settings
from rlcard.games.gin_rummy.utils.thinker import Thinker

discard_action_ids = list(range(discard_action_id, discard_action_id + 52))
knock_action_ids = list(range(knock_action_id, knock_action_id + 52))
put_action_ids = [gin_action_id] + discard_action_ids + knock_action_ids
get_action_ids = [draw_card_action_id, pick_up_discard_action_id, declare_dead_hand_action_id]

class TestGinRummyGame(unittest.TestCase):

    def test_get_num_players(self):
        game = Game()
        num_players = game.get_num_players()
        self.assertEqual(num_player

    def test_get_num_actions(self):
        game = Game()
        num_actions = game.get_num_actions()
        self.assertEqual(num_actions, 110)

    def test_init_game(self):
        game = Game()
        state, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        self.assertEqual(len(game.round.move_sheet), 1)
        self.assertIn(current_player, [0, 1])
        self.assertIn(game.round.dealer_id, [0, 1])
        self.assertEqual(len(game.actions), 0)
        self.assertEqual(opponent_player, game.round.dealer_id)  # opponent_player is dealer
        self.assertEqual(len(game.round.players[opponent_player].hand), 10)  # dealer has 10 cards
        self.assertEqual(len(game.round.players[current_player].hand), 11)  # current_player has 11 cards
        self.assertEqual(len(game.round.dealer.shuffled_deck), 52)
        self.assertEqual(len(game.round.dealer.stock_pile), 31)
        self.assertEqual(state['player_id'], current_player)
        self.assertEqual(len(state['hand']), 11)

    def test_step(self):
        game = Game()
        _, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        action = np.random.choice(game.judge.get_legal_actions())
        self.assertIn(action.action_id, put_action_ids)  # should be a put action
        _, next_player = game.step(action)
        if not game.is_over():
            self.assertEqual(next_player, opponent_player)
        if not game.is_over():
            action = np.random.choice(game.judge.get_legal_actions())
            self.assertIn(action.action_id, get_action_ids)  # should be a get action
            _, next_player = game.step(action)
            self.assertEqual(next_player, opponent_player)  # keep turn to put card

    def test_proceed_game(self):
        game = Game()
        game.init_game()
        while not game.is_over():
            legal_actions = game.judge.get_legal_actions()
            action = np.random.choice(legal_actions)
            _, _ = game.step(action)
        self.assertEqual(game.actions[-1].action_id, score_player_1_action_id)

    def test_get_state(self):
        game = Game()
        state, _ = game.init_game()
        self.assertEqual(len(state), 6)

    def test_gin_rummy_dealer(self):
        dealer = GinRummyDealer(np.random.RandomState())
        current_deck = utils.get_deck()
        deck_card_ids = [utils.get_card_id(card) for card in current_deck]
        self.assertEqual(deck_card_ids, list(range(52)))
        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        dealer.deal_cards(player=player, num=10)
        self.assertEqual(len(dealer.shuffled_deck), 52)
        self.assertEqual(len(dealer.stock_pile), 42)
        self.assertEqual(len(current_deck), 52)
        self.assertEqual(len(utils.get_deck()), 52)
        # Pop top_card from current_deck.
        top_card = current_deck.pop(-1)
        self.assertEqual(str(top_card), "KC")
        self.assertEqual(len(current_deck), 51)
        self.assertEqual(len(utils.get_deck()), 52)

    def test_knocking(self):
        hand_text = ['JS', 'JH', 'JD', '8C', '7S', '7H', '7D', '4S', '3D', '2S', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        knock_cards, _ = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=10)
        self.assertEqual(set(knock_cards), set([utils.card_from_text(x) for x in ['8C']]))

    def test_melding(self):
        hand_text = ['9H', 'AC', 'TH', '3C', '3D', '7C', 'QH', '3H', '8C', '8D',
                     '4D', '7H', '8S', '5H', '4H', 'AS', 'TD', '3S', '2S', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]

        # check
        all_set_melds_text = [['3C', '3D', '3H', '3S'], ['8C', '8D', '8S'], ['AC', 'AS', 'AH'], ['3D', '3H', '3S'],
                              ['3C', '3H', '3S'], ['3C', '3D', '3S'], ['3C', '3D', '3H']]
        all_set_melds_text = set([frozenset(x) for x in all_set_melds_text])
        all_set_melds = get_all_set_melds(hand)
        self.assertEqual(all_set_melds_text,
                         set([frozenset([str(card) for card in meld_pile]) for meld_pile in all_set_melds]))

        # check
        all_run_melds_text = [['AS', '2S', '3S'], ['3H', '4H', '5H']]
        all_run_melds = get_all_run_melds(hand)
        self.assertEqual(all_run_melds_text, [[str(card) for card in meld_pile] for meld_pile in all_run_melds])

        # check
        meld_clusters = get_meld_clusters(hand=hand)
        self.assertEqual(len(meld_clusters), 36)

    def test_melding_2(self):  # 2020-Apr-19
        hand_text = ['8D', '5S', '5H', '5D', '4H', '4C', '3H', '2H', '2D', '2C', 'AD']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])
        self.assertEqual(knock_cards, [utils.card_from_text('8D')])
        self.assertEqual(gin_cards, [])

    def test_melding_3(self):  # 2020-Apr-19
        hand_text = ['7H', '6H', '5H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])

        correct_knock_cards = [utils.card_from_text(x) for x in ['7H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']]
        self.assertEqual(set(knock_cards), set(correct_knock_cards))
        self.assertEqual(gin_cards, [])

    def test_corrected_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        corrected_config = Settings.get_config_with_invalid_settings_set_to_default_value(config=config)
        self.assertEqual(corrected_config[Setting.max_drawn_card_count], 10)
        self.assertEqual(corrected_config[Setting.stockpile_dead_card_count],
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(corrected_config[Setting.is_allowed_pick_up_discard],
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_change_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        settings = Settings()
        settings.change_settings(config=config)
        self.assertEqual(settings.max_drawn_card_count, 10)
        self.assertEqual(settings.stockpile_dead_card_count,
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(settings.is_allowed_pick_up_discard,
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_decode_cards(self):
        deck = utils.get_deck()
        encoded_cards = utils.encode_cards(deck)
        decoded_cards = utils.decode_cards(encoded_cards)
        for i in range(52):
            card = deck[i]
            decoded_card = decoded_cards[i]
            self.assertEqual(card, decoded_card)

    def test_get_meld_piles_with_discard_card(self):
        hand_text = ['8D', '7D', '6S', '5H', '5C', '4C', '4S', '2S', 'AC', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]
        discard_card = utils.card_from_text('6D')
        thinker = Thinker(hand=hand)
        meld_piles_with_discard_card = thinker.get_meld_piles_with_discard_card(discard_card=discard_card)
        result_as_set = frozenset([frozenset(meld_pile) for meld_pile in meld_piles_with_discard_card])
        correct_result = [[utils.card_from_text(x) for x in ['8D', '7D', '6D']]]
        correct_result_as_set = frozenset([frozenset(meld_pile) for meld_pile in correct_result])
        self.assertEqual(result_as_set, correct_result_as_set)

if __name__ == '__main__':
    unittest.main()


while not game.is_over():
            legal_actions = game.judge.get_legal_actions()
            action = np.random.choice(legal_actions)
            _, _ = game.step(action)

'''
    File name: test_gin_rummy_game.py
    Author: William Hale
    Date created: 3/11/2020
'''

import unittest
import numpy as np

import rlcard.games.gin_rummy.judge as judge
import rlcard.games.gin_rummy.utils.utils as utils

from rlcard.games.gin_rummy.dealer import GinRummyDealer
from rlcard.games.gin_rummy.game import GinRummyGame as Game
from rlcard.games.gin_rummy.player import GinRummyPlayer
from rlcard.games.gin_rummy.utils.action_event import score_player_1_action_id
from rlcard.games.gin_rummy.utils.action_event import draw_card_action_id, pick_up_discard_action_id
from rlcard.games.gin_rummy.utils.action_event import declare_dead_hand_action_id
from rlcard.games.gin_rummy.utils.action_event import gin_action_id, discard_action_id, knock_action_id
from rlcard.games.gin_rummy.utils.melding import get_all_set_melds, get_all_run_melds, get_meld_clusters
from rlcard.games.gin_rummy.utils.settings import Setting, Settings
from rlcard.games.gin_rummy.utils.thinker import Thinker

discard_action_ids = list(range(discard_action_id, discard_action_id + 52))
knock_action_ids = list(range(knock_action_id, knock_action_id + 52))
put_action_ids = [gin_action_id] + discard_action_ids + knock_action_ids
get_action_ids = [draw_card_action_id, pick_up_discard_action_id, declare_dead_hand_action_id]

class TestGinRummyGame(unittest.TestCase):

    def test_get_num_players(self):
        game = Game()
        num_players = game.get_num_players()
        self.assertEqual(num_players, 2)

    def test_get_num_actions(self):
        game = Game()
        num_actions = game.get_num_actions()
        self.assertEqual(num_actions, 110)

    def test_init_game(self):
        game = Game()
        state, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        self.assertEqual(len(game.round.move_sheet), 1)
        self.assertIn(current_player, [0, 1])
        self.assertIn(game.round.dealer_id, [0, 1])
        self.assertEqual(len(game.actions), 0)
        self.assertEqual(opponent_player, game.round.dealer_id)  # opponent_player is dealer
        self.assertEqual(len(game.round.players[opponent_player].hand), 10)  # dealer has 10 cards
        self.assertEqual(len(game.round.players[current_player].hand), 11)  # current_player has 11 cards
        self.assertEqual(len(game.round.dealer.shuffled_deck), 52)
        self.assertEqual(len(game.round.dealer.stock_pile), 31)
        self.assertEqual(state['player_id'], current_player)
        self.assertEqual(len(state['hand']), 11)

    def test_step(self):
        game = Game()
        _, current_player = game.init_game()
        opponent_player = (current_player + 1) % 2
        action = np.random.choice(game.judge.get_legal_actions())
        self.assertIn(action.action_id, put_action_ids)  # should be a put action
        _, next_player = game.step(action)
        if not game.is_over():
            self.assertEqual(next_player, opponent_player)
        if not game.is_over():
            action = np.random.choice(game.judge.get_legal_actions())
            self.assertIn(action.action_id, get_action_ids)  # should be a get action
            _, next_player = game.step(action)
            self.assertEqual(next_player, opponent_player)  # keep turn to put card

    def test_proceed_game(self):
        game = Game()
        game.init_game()

        self.assertEqual(game.actions[-1].action_id, score_player_1_action_id)

    def test_get_state(self):
        game = Game()
        state, _ = game.init_game()
        self.assertEqual(len(state), 6)

    def test_gin_rummy_dealer(self):
        dealer = GinRummyDealer(np.random.RandomState())
        current_deck = utils.get_deck()
        deck_card_ids = [utils.get_card_id(card) for card in current_deck]
        self.assertEqual(deck_card_ids, list(range(52)))
        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        dealer.deal_cards(player=player, num=10)
        self.assertEqual(len(dealer.shuffled_deck), 52)
        self.assertEqual(len(dealer.stock_pile), 42)
        self.assertEqual(len(current_deck), 52)
        self.assertEqual(len(utils.get_deck()), 52)
        # Pop top_card from current_deck.
        top_card = current_deck.pop(-1)
        self.assertEqual(str(top_card), "KC")
        self.assertEqual(len(current_deck), 51)
        self.assertEqual(len(utils.get_deck()), 52)

    def test_knocking(self):
        hand_text = ['JS', 'JH', 'JD', '8C', '7S', '7H', '7D', '4S', '3D', '2S', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        knock_cards, _ = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=10)
        self.assertEqual(set(knock_cards), set([utils.card_from_text(x) for x in ['8C']]))

    def test_melding(self):
        hand_text = ['9H', 'AC', 'TH', '3C', '3D', '7C', 'QH', '3H', '8C', '8D',
                     '4D', '7H', '8S', '5H', '4H', 'AS', 'TD', '3S', '2S', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]

        # check
        all_set_melds_text = [['3C', '3D', '3H', '3S'], ['8C', '8D', '8S'], ['AC', 'AS', 'AH'], ['3D', '3H', '3S'],
                              ['3C', '3H', '3S'], ['3C', '3D', '3S'], ['3C', '3D', '3H']]
        all_set_melds_text = set([frozenset(x) for x in all_set_melds_text])
        all_set_melds = get_all_set_melds(hand)
        self.assertEqual(all_set_melds_text,
                         set([frozenset([str(card) for card in meld_pile]) for meld_pile in all_set_melds]))

        # check
        all_run_melds_text = [['AS', '2S', '3S'], ['3H', '4H', '5H']]
        all_run_melds = get_all_run_melds(hand)
        self.assertEqual(all_run_melds_text, [[str(card) for card in meld_pile] for meld_pile in all_run_melds])

        # check
        meld_clusters = get_meld_clusters(hand=hand)
        self.assertEqual(len(meld_clusters), 36)

    def test_melding_2(self):  # 2020-Apr-19
        hand_text = ['8D', '5S', '5H', '5D', '4H', '4C', '3H', '2H', '2D', '2C', 'AD']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])
        self.assertEqual(knock_cards, [utils.card_from_text('8D')])
        self.assertEqual(gin_cards, [])

    def test_melding_3(self):  # 2020-Apr-19
        hand_text = ['7H', '6H', '5H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']
        hand = [utils.card_from_text(x) for x in hand_text]
        going_out_deadwood_count = 10
        knock_cards, gin_cards = judge.get_going_out_cards(hand=hand, going_out_deadwood_count=going_out_deadwood_count)

        player = GinRummyPlayer(player_id=0, np_random=np.random.RandomState())
        player.hand = hand
        player.did_populate_hand()
        meld_clusters = player.get_meld_clusters()
        alpha, beta = judge._get_going_out_cards(meld_clusters=meld_clusters,
                                                 hand=hand,
                                                 going_out_deadwood_count=going_out_deadwood_count)
        self.assertEqual(set(alpha), set(knock_cards))
        self.assertEqual(beta, [])

        correct_knock_cards = [utils.card_from_text(x) for x in ['7H', '4S', '4H', '3H', '2S', 'AS', 'AH', 'AD', 'AC']]
        self.assertEqual(set(knock_cards), set(correct_knock_cards))
        self.assertEqual(gin_cards, [])

    def test_corrected_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        corrected_config = Settings.get_config_with_invalid_settings_set_to_default_value(config=config)
        self.assertEqual(corrected_config[Setting.max_drawn_card_count], 10)
        self.assertEqual(corrected_config[Setting.stockpile_dead_card_count],
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(corrected_config[Setting.is_allowed_pick_up_discard],
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_change_settings(self):
        default_setting = Setting.default_setting()
        config = {Setting.max_drawn_card_count: 10,
                  Setting.stockpile_dead_card_count: 4.5,
                  Setting.is_allowed_pick_up_discard: 0}
        settings = Settings()
        settings.change_settings(config=config)
        self.assertEqual(settings.max_drawn_card_count, 10)
        self.assertEqual(settings.stockpile_dead_card_count,
                         default_setting[Setting.stockpile_dead_card_count])
        self.assertEqual(settings.is_allowed_pick_up_discard,
                         default_setting[Setting.is_allowed_pick_up_discard])

    def test_decode_cards(self):
        deck = utils.get_deck()
        encoded_cards = utils.encode_cards(deck)
        decoded_cards = utils.decode_cards(encoded_cards)
        for i in range(52):
            card = deck[i]
            decoded_card = decoded_cards[i]
            self.assertEqual(card, decoded_card)

    def test_get_meld_piles_with_discard_card(self):
        hand_text = ['8D', '7D', '6S', '5H', '5C', '4C', '4S', '2S', 'AC', 'AH']
        hand = [utils.card_from_text(x) for x in hand_text]
        discard_card = utils.card_from_text('6D')
        thinker = Thinker(hand=hand)
        meld_piles_with_discard_card = thinker.get_meld_piles_with_discard_card(discard_card=discard_card)
        result_as_set = frozenset([frozenset(meld_pile) for meld_pile in meld_piles_with_discard_card])
        correct_result = [[utils.card_from_text(x) for x in ['8D', '7D', '6D']]]
        correct_result_as_set = frozenset([frozenset(meld_pile) for meld_pile in correct_result])
        self.assertEqual(result_as_set, correct_result_as_set)

if __name__ == '__main__':
    unittest.main()