<commit_before>import enum
import functools
import logging
import typing
import warnings

from asyncqlio import db as md_db
from asyncqlio.backends.base import BaseTransaction
from asyncqlio.orm import inspection as md_inspection, query as md_query
from asyncqlio.orm.schema import table as md_table
from asyncqlio.sentinels import NO_DEFAULT, NO_VALUE

logger = logging.getLogger(__name__)

class SessionState(enum.Enum):
    NOT_READY = 0
    READY = 1
    CLOSED = 2

# decorators
def enforce_open(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        if self._state is not SessionState.READY:
            raise RuntimeError("Session is not ready or closed")
        else:
            return func(self, *args, **kwargs)

    return wrapper

class Session(object):
    """
    Sessions act as a temporary window into the database. They are responsible for creating queries,
    inserting and updating rows, etc.

    Sessions are bound to a :class:`.DatabaseInterface` instance which they use to get a transaction
    and execute queries in.

    .. code-block:: python
        # get a session from our db interface
        sess = db.get_session()
    """

    def __init__(self, bind: 'md_db.DatabaseInterface'):
        """
        :param bind: The :class:`.DatabaseInterface` instance we are bound to.
        """
        self.bind = bind

        #: The current state for the session.
        self._state = SessionState.NOT_READY

        #: The current :class:`.BaseTransaction` this Session is associated with.
        #: The transaction is used for making queries and inserts, etc.
        self.transaction = None  # type: BaseTransaction

    async def __aenter__(self) -> 'Session':
        await self.start()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        try:
            if exc_type is None:
                await self.commit()
            else:
                await self.rollback()
        except Exception:
            # error in committing, etc
            await self.rollback()
            raise
        finally:
            await self.close()

        return False

    def __del__(self):
        if self._state == SessionState.READY:
            warnings.warn("Session was destroyed without being closed!", stacklevel=2)

    # Query builders
    @property
    def select(self) -> 'md_query.SelectQuery':
        """
        Creates a new SELECT query that can be built upon.

        :return: A new :class:`.SelectQuery`.
        """
        q = md_query.SelectQuery(self)
        return q

    @property
    def insert(self) -> 'md_query.InsertQuery':
        """
        Creates a new INSERT INTO query that can be built upon.

        :return: A new :class:`.InsertQuery`.
        """
        return md_query.InsertQuery(self)

    @property
    def update(self) -> 'md_query.BulkUpdateQuery':
        """
        Creates a new bulk UPDATE query that can be built upon.

        :return: A new :class:`.BulkUpdateQuery`.
        """
        return md_query.BulkUpdateQuery(self)

    @property
    def delete(self) -> 'md_query.BulkDeleteQuery':
        """
        Creates a new bulk DELETE query that can be built upon.

        :return: A new :class:`.BulkDeleteQuery`.
        """
        return md_query.BulkDeleteQuery(self)

    async def start(self) -> 'Session':
        """
        Starts the session, acquiring a transaction connection which will be used to modify the DB.
        This **must** be called before using the session.

        .. code-block:: python
            sess = db.get_session()
            await sess.start()

        .. note::
            When using ``async with``, this is automatically called.
        """
        if self._state is not SessionState.NOT_READY:
            raise RuntimeError("Session must not be ready or closed")

        logger.debug("Acquiring new transaction, and beginning")
        self.transaction = self.bind.get_transaction()
        await self.transaction.begin()

        self._state = SessionState.READY
        return self

    @enforce_open
    async def commit(self):
        """
        Commits the current session, running inserts/updates/deletes.

        This will **not** close the session; it can be re-used after a commit.
        """
        logger.debug("Committing transaction")
        await self.transaction.commit()
        return self

    @enforce_open
    async def rollback(self, checkpoint: str = None):
        """
        Rolls the current session back.
        This is useful if an error occurs inside your code.

        :param checkpoint: The checkpoint to roll back to, if applicable.
        """
        logger.debug("Rolling back session to checkpoint {}".format(checkpoint))
        await self.transaction.rollback(checkpoint=checkpoint)
        return self

    @enforce_open
    async def close(self):
        """
        Closes the current session.

        .. warning::
            This will **NOT COMMIT ANY DATA**. Old data will die.
        """
        await self.transaction.close()
        self._state = SessionState.CLOSED
        del self.transaction

    @enforce_open
    async def fetch(self, sql: str, params=None):
        """
        Fetches a single row.
        """
        cur = await self.transaction.cursor(sql, params)
        next = await cur.fetch_row()
        await cur.close()
        return next

    @enforce_open
    async def execute(self, sql: str, params: typing.Union[typing.Mapping[str, typing.Any],
                                                           typing.Iterable[typing.Any]] = None):
        """
        Executes SQL inside the current session.

        This is part of the **low-level API.**

        :param sql: The SQL to execute.
        :param params: The parameters to use inside the query.
        """
        return await self.transaction.execute(sql, params)

    @enforce_open
    async def cursor(self, sql: str, params: typing.Union[typing.Mapping[str, typing.Any],
                                                          typing.Iterable[typing.Any]] = None):
        """
        Executes SQL inside the current session, and returns a new :class:`.BaseResultSet.`

        :param sql: The SQL to execute.
        :param params: The parameters to use inside the query.
        """
        return await self.transaction.cursor(sql, params)

    @enforce_open
    async def insert_now(self, row: 'md_table.Table') -> typing.Any:
        """
        Inserts a row NOW.

        .. warning::
            This will only generate the INSERT statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

            Also, tables with auto-incrementing fields will only have their first field filled in
            outside of Postgres databases.

        :param row: The :class:`.Table` instance to insert.
        :return: The row, with primary key included.
        """
        # this just creates a new query
        # and calls _do_insert_query
        # to actually run the query
        q = md_query.InsertQuery(self)
        q.add_row(row)
        result = await self.run_insert_query(q)
        try:
            return result[0]
        except IndexError:
            return None

    @enforce_open
    async def update_now(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Updates a row NOW.

        .. warning::
            This will only generate the UPDATE statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

        :param row: The :class:`.Table` instance to update.
        :return: The :class:`.Table` instance that was updated.
        """
        q = md_query.RowUpdateQuery(self)
        q.add_row(row)

        await self.run_update_query(q)
        return row

    @enforce_open
    async def delete_now(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Deletes a row NOW.
        """
        q = md_query.RowDeleteQuery(self)
        q.add_row(row)

        await self.run_delete_query(q)
        return row

    async def run_select_query(self, query: 'md_query.SelectQuery'):
        """
        Executes a select query.

        .. warning::
            Unlike the other `run_*_query` methods, this method should not be used without a good
            reason; it creates a special class that is used for the query.

            Use :class:`.SelectQuery.first` or :class:`.SelectQuery.all`.

        :param query: The :class:`.SelectQuery` to use.
        :return: A :class:`._ResultGenerator` for this query.
        """
        gen = md_query._ResultGenerator(query)
        sql, params = query.generate_sql()
        cursor = await self.cursor(sql, params)
        # set the cursor on the result generator
        gen._results = cursor
        return gen

    async def run_insert_query(self, query: 'md_query.InsertQuery'):
        """
        Executes an insert query.

        :param query: The :class:`.InsertQuery` to use.
        :return: The list of rows that were inserted.
        """
        queries = query.generate_sql()
        results = []

        for row, (sql, params) in zip(query.rows_to_insert, queries):
            if md_inspection._get_mangled(row, "deleted"):
                raise RuntimeError("Row '{}' is marked as deleted".format(row))

            # this needs to be a cursor
            # since postgres uses RETURNING
            cur = await self.cursor(sql, params)
            # some drivers don't execute until this is done
            # (asyncpg, apparently)
            # so always fetch a row now
            # this is None if it doesnt support returning, so
            returned_row = await cur.fetch_row()

            # if we have returns, we can store the column values directly
            if self.bind.dialect.has_returns:
                for colname, value in returned_row.items():
                    column = row.table.get_column(colname)
                    if column is None:
                        # what
                        continue
                    row.store_column_value(column, value, track_history=False)
                    await cur.close()
            else:
                if sum(1 for x in row.table.iter_columns() if x.auto_increment) == 1:
                    # we can load the last value easily
                    lquery = "SELECT {};".format(self.bind.dialect.lastval_method)
                    cursor = await self.cursor(lquery)
                    async with cursor:
                        lval_row = await cursor.fetch_row()
                        # there should only be one value here
                        value = list(lval_row.values())[0]
                    column = next(
                        filter(lambda x: x.auto_increment, row.table.iter_columns()), None
                    )
                    row.store_column_value(column, value)

                for column in row.table.iter_columns():
                    if column.default is not NO_DEFAULT \
                            and row.get_column_value(column, return_default=False) is NO_VALUE:
                        row.store_column_value(column, column.default)

                await cur.close()

            md_inspection._set_mangled(row, "deleted", False)
            md_inspection._set_mangled(row, "existed", True)
            results.append(row)

        return results

    async def run_update_query(self, query: 'md_query.BaseQuery'):
        """
        Executes an update query.

        :param query: The :class:`.RowUpdateQuery` or :class:`.BulkUpdateQuery` to execute.
        """
        if isinstance(query, md_query.RowUpdateQuery):
            for row, (sql, params) in zip(query.rows_to_update, query.generate_sql()):
                if md_inspection._get_mangled(row, "deleted"):
                    raise RuntimeError("Row '{}' is marked as deleted".format(row))

                if sql is None and params is None:
                    continue

                await self.execute(sql, params)
                # copy the history of the row
                row._previous_values = row._values
        elif isinstance(query, md_query.BulkUpdateQuery):
            sql, params = query.generate_sql()
            await self.execute(sql, params)
        else:
            raise TypeError("Type {0.__class__.__name__} is not an update query".format(query))

        return query

    async def run_delete_query(self, query: 'md_query.RowDeleteQuery'):
        """
        Executes a delete query.

        :param query: The :class:`.RowDeleteQuery` or :class:`.BulkDeleteQuery` to execute.
        """
        if isinstance(query, md_query.RowDeleteQuery):
            for row, (sql, params) in zip(query.rows_to_delete, query.generate_sql()):
                if md_inspection._get_mangled(row, "deleted"):
                    raise RuntimeError("Row '{}' is already marked as deleted".format(row))

                if sql is None and params is None:
                    continue

                await self.execute(sql, params)
                md_inspection._set_mangled(row, "deleted", True)
        elif isinstance(query, md_query.BulkDeleteQuery):
            sql, params = query.generate_sql()
            await self.execute(sql, params)
        else:
            raise TypeError("Type {0.__class__.__name__} is not a delete query".format(query))

        return query

    async def add(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Adds a row to the current transaction. This will emit SQL that will generate an INSERT or
        UPDATE statement, and then update the primary key of this row.

        .. warning::
            This will only generate the INSERT statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

        :param row: The :class:`.Table` instance object to add to the transaction.
        :return: The :class:`.Table` instance with primary key filled in, if applicable.
        """
        # it already existed in our session, so emit a UPDATE
        if md_inspection._get_mangled(row, "existed"):
            return await self.update_now(row)
        # otherwise, emit an INSERT
        else:
            return await self.insert_now(row)

    async def merge(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Merges a row with a row that already exists in the database.

        This should be used for rows that have a primary key, but were not returned from
        :meth:`.select`.

        :param row: The :class:`.Table` instance to merge.
        :return: The :class:`.Table` instance once updated.
        """
        return await self.update_now(row)

    async def remove(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Removes a row from the database.

        :param row: The :class:`.Table` instance to remove.
        """
        return await self.delete_now(row)
<commit_msg>Add checkpoint and uncheckpoint methods to the Session.<commit_after>import enum
import functools
import logging
import typing
import warnings

from asyncqlio import db as md_db
from asyncqlio.backends.base import BaseTransaction
from asyncqlio.orm import inspection as md_inspection, query as md_query
from asyncqlio.orm.schema import table as md_table
from asyncqlio.sentinels import NO_DEFAULT, NO_VALUE

logger = logging.getLogger(__name__)

class SessionState(enum.Enum):
    NOT_READY = 0
    READY = 1
    CLOSED = 2

# decorators
def enforce_open(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        if self._state is not SessionState.READY:
            raise RuntimeError("Session is not ready or closed")
        else:
            return func(self, *args, **kwargs)

    return wrapper

class Session(object):
    """
    Sessions act as a temporary window into the database. They are responsible for creating queries,
    inserting and updating rows, etc.

    Sessions are bound to a :class:`.DatabaseInterface` instance which they use to get a transaction
    and execute queries in.

    .. code-block:: python
        # get a session from our db interface
        sess = db.get_session()
    """

    def __init__(self, bind: 'md_db.DatabaseInterface'):
        """
        :param bind: The :class:`.DatabaseInterface` instance we are bound to.
        """
        self.bind = bind

        #: The current state for the session.
        self._state = SessionState.NOT_READY

        #: The current :class:`.BaseTransaction` this Session is associated with.
        #: The transaction is used for making queries and inserts, etc.
        self.transaction = None  # type: BaseTransaction

    async def __aenter__(self) -> 'Session':
        await self.start()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        try:
            if exc_type is None:
                await self.commit()
            else:
                await self.rollback()
        except Exception:
            # error in committing, etc
            await self.rollback()
            raise
        finally:
            await self.close()

        return False

    def __del__(self):
        if self._state == SessionState.READY:
            warnings.warn("Session was destroyed without being closed!", stacklevel=2)

    # Query builders
    @property
    def select(self) -> 'md_query.SelectQuery':
        """
        Creates a new SELECT query that can be built upon.

        :return: A new :class:`.SelectQuery`.
        """
        q = md_query.SelectQuery(self)
        return q

    @property
    def insert(self) -> 'md_query.InsertQuery':
        """
        Creates a new INSERT INTO query that can be built upon.

        :return: A new :class:`.InsertQuery`.
        """
        return md_query.InsertQuery(self)

    @property
    def update(self) -> 'md_query.BulkUpdateQuery':
        """
        Creates a new bulk UPDATE query that can be built upon.

        :return: A new :class:`.BulkUpdateQuery`.
        """
        return md_query.BulkUpdateQuery(self)

    @property
    def delete(self) -> 'md_query.BulkDeleteQuery':
        """
        Creates a new bulk DELETE query that can be built upon.

        :return: A new :class:`.BulkDeleteQuery`.
        """
        return md_query.BulkDeleteQuery(self)

    async def start(self) -> 'Session':
        """
        Starts the session, acquiring a transaction connection which will be used to modify the DB.
        This **must** be called before using the session.

        .. code-block:: python
            sess = db.get_session()
            await sess.start()

        .. note::
            When using ``async with``, this is automatically called.
        """
        if self._state is not SessionState.NOT_READY:
            raise RuntimeError("Session must not be ready or closed")

        logger.debug("Acquiring new transaction, and beginning")
        self.transaction = self.bind.get_transaction()
        await self.transaction.begin()

        self._state = SessionState.READY
        return self

    @enforce_open
    async def checkpoint(self, checkpoint_name: str):
        """
        Sets a new checkpoint.

        :param checkpoint_name: The name of the checkpoint to use.
        """
        return await self.transaction.create_savepoint(checkpoint_name)

    @enforce_open
    async def uncheckpoint(self, checkpoint_name: str):
        """
        Releases a checkpoint.

        :param checkpoint_name: The name of the checkpoint to release.
        """
        return await self.transaction.release_savepoint(checkpoint_name)

    @enforce_open
    async def commit(self):
        """
        Commits the current session, running inserts/updates/deletes.

        This will **not** close the session; it can be re-used after a commit.
        """
        logger.debug("Committing transaction")
        await self.transaction.commit()
        return self

    @enforce_open
    async def rollback(self, checkpoint: str = None):
        """
        Rolls the current session back.
        This is useful if an error occurs inside your code.

        :param checkpoint: The checkpoint to roll back to, if applicable.
        """
        logger.debug("Rolling back session to checkpoint {}".format(checkpoint))
        await self.transaction.rollback(checkpoint=checkpoint)
        return self

    @enforce_open
    async def close(self):
        """
        Closes the current session.

        .. warning::
            This will **NOT COMMIT ANY DATA**. Old data will die.
        """
        await self.transaction.close()
        self._state = SessionState.CLOSED
        del self.transaction

    @enforce_open
    async def fetch(self, sql: str, params=None):
        """
        Fetches a single row.
        """
        cur = await self.transaction.cursor(sql, params)
        next = await cur.fetch_row()
        await cur.close()
        return next

    @enforce_open
    async def execute(self, sql: str, params: typing.Union[typing.Mapping[str, typing.Any],
                                                           typing.Iterable[typing.Any]] = None):
        """
        Executes SQL inside the current session.

        This is part of the **low-level API.**

        :param sql: The SQL to execute.
        :param params: The parameters to use inside the query.
        """
        return await self.transaction.execute(sql, params)

    @enforce_open
    async def cursor(self, sql: str, params: typing.Union[typing.Mapping[str, typing.Any],
                                                          typing.Iterable[typing.Any]] = None):
        """
        Executes SQL inside the current session, and returns a new :class:`.BaseResultSet.`

        :param sql: The SQL to execute.
        :param params: The parameters to use inside the query.
        """
        return await self.transaction.cursor(sql, params)

    @enforce_open
    async def insert_now(self, row: 'md_table.Table') -> typing.Any:
        """
        Inserts a row NOW.

        .. warning::
            This will only generate the INSERT statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

            Also, tables with auto-incrementing fields will only have their first field filled in
            outside of Postgres databases.

        :param row: The :class:`.Table` instance to insert.
        :return: The row, with primary key included.
        """
        # this just creates a new query
        # and calls _do_insert_query
        # to actually run the query
        q = md_query.InsertQuery(self)
        q.add_row(row)
        result = await self.run_insert_query(q)
        try:
            return result[0]
        except IndexError:
            return None

    @enforce_open
    async def update_now(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Updates a row NOW.

        .. warning::
            This will only generate the UPDATE statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

        :param row: The :class:`.Table` instance to update.
        :return: The :class:`.Table` instance that was updated.
        """
        q = md_query.RowUpdateQuery(self)
        q.add_row(row)

        await self.run_update_query(q)
        return row

    @enforce_open
    async def delete_now(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Deletes a row NOW.
        """
        q = md_query.RowDeleteQuery(self)
        q.add_row(row)

        await self.run_delete_query(q)
        return row

    async def run_select_query(self, query: 'md_query.SelectQuery'):
        """
        Executes a select query.

        .. warning::
            Unlike the other `run_*_query` methods, this method should not be used without a good
            reason; it creates a special class that is used for the query.

            Use :class:`.SelectQuery.first` or :class:`.SelectQuery.all`.

        :param query: The :class:`.SelectQuery` to use.
        :return: A :class:`._ResultGenerator` for this query.
        """
        gen = md_query._ResultGenerator(query)
        sql, params = query.generate_sql()
        cursor = await self.cursor(sql, params)
        # set the cursor on the result generator
        gen._results = cursor
        return gen

    async def run_insert_query(self, query: 'md_query.InsertQuery'):
        """
        Executes an insert query.

        :param query: The :class:`.InsertQuery` to use.
        :return: The list of rows that were inserted.
        """
        queries = query.generate_sql()
        results = []

        for row, (sql, params) in zip(query.rows_to_insert, queries):
            if md_inspection._get_mangled(row, "deleted"):
                raise RuntimeError("Row '{}' is marked as deleted".format(row))

            # this needs to be a cursor
            # since postgres uses RETURNING
            cur = await self.cursor(sql, params)
            # some drivers don't execute until this is done
            # (asyncpg, apparently)
            # so always fetch a row now
            # this is None if it doesnt support returning, so
            returned_row = await cur.fetch_row()

            # if we have returns, we can store the column values directly
            if self.bind.dialect.has_returns:
                for colname, value in returned_row.items():
                    column = row.table.get_column(colname)
                    if column is None:
                        # what
                        continue
                    row.store_column_value(column, value, track_history=False)
                    await cur.close()
            else:
                if sum(1 for x in row.table.iter_columns() if x.auto_increment) == 1:
                    # we can load the last value easily
                    lquery = "SELECT {};".format(self.bind.dialect.lastval_method)
                    cursor = await self.cursor(lquery)
                    async with cursor:
                        lval_row = await cursor.fetch_row()
                        # there should only be one value here
                        value = list(lval_row.values())[0]
                    column = next(
                        filter(lambda x: x.auto_increment, row.table.iter_columns()), None
                    )
                    row.store_column_value(column, value)

                for column in row.table.iter_columns():
                    if column.default is not NO_DEFAULT \
                            and row.get_column_value(column, return_default=False) is NO_VALUE:
                        row.store_column_value(column, column.default)

                await cur.close()

            md_inspection._set_mangled(row, "deleted", False)
            md_inspection._set_mangled(row, "existed", True)
            results.append(row)

        return results

    async def run_update_query(self, query: 'md_query.BaseQuery'):
        """
        Executes an update query.

        :param query: The :class:`.RowUpdateQuery` or :class:`.BulkUpdateQuery` to execute.
        """
        if isinstance(query, md_query.RowUpdateQuery):
            for row, (sql, params) in zip(query.rows_to_update, query.generate_sql()):
                if md_inspection._get_mangled(row, "deleted"):
                    raise RuntimeError("Row '{}' is marked as deleted".format(row))

                if sql is None and params is None:
                    continue

                await self.execute(sql, params)
                # copy the history of the row
                row._previous_values = row._values
        elif isinstance(query, md_query.BulkUpdateQuery):
            sql, params = query.generate_sql()
            await self.execute(sql, params)
        else:
            raise TypeError("Type {0.__class__.__name__} is not an update query".format(query))

        return query

    async def run_delete_query(self, query: 'md_query.RowDeleteQuery'):
        """
        Executes a delete query.

        :param query: The :class:`.RowDeleteQuery` or :class:`.BulkDeleteQuery` to execute.
        """
        if isinstance(query, md_query.RowDeleteQuery):
            for row, (sql, params) in zip(query.rows_to_delete, query.generate_sql()):
                if md_inspection._get_mangled(row, "deleted"):
                    raise RuntimeError("Row '{}' is already marked as deleted".format(row))

                if sql is None and params is None:
                    continue

                await self.execute(sql, params)
                md_inspection._set_mangled(row, "deleted", True)
        elif isinstance(query, md_query.BulkDeleteQuery):
            sql, params = query.generate_sql()
            await self.execute(sql, params)
        else:
            raise TypeError("Type {0.__class__.__name__} is not a delete query".format(query))

        return query

    async def add(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Adds a row to the current transaction. This will emit SQL that will generate an INSERT or
        UPDATE statement, and then update the primary key of this row.

        .. warning::
            This will only generate the INSERT statement for the row now. Only :meth:`.commit` will
            actually commit the row to storage.

        :param row: The :class:`.Table` instance object to add to the transaction.
        :return: The :class:`.Table` instance with primary key filled in, if applicable.
        """
        # it already existed in our session, so emit a UPDATE
        if md_inspection._get_mangled(row, "existed"):
            return await self.update_now(row)
        # otherwise, emit an INSERT
        else:
            return await self.insert_now(row)

    async def merge(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Merges a row with a row that already exists in the database.

        This should be used for rows that have a primary key, but were not returned from
        :meth:`.select`.

        :param row: The :class:`.Table` instance to merge.
        :return: The :class:`.Table` instance once updated.
        """
        return await self.update_now(row)

    async def remove(self, row: 'md_table.Table') -> 'md_table.Table':
        """
        Removes a row from the database.

        :param row: The :class:`.Table` instance to remove.
        """
        return await self.delete_now(row)
<|endoftext|>


<commit_before># gamestate = {
#     "tournament_id":"550d1d68cd7bd10003000003",     # Id of the current tournament
#
#     "game_id":"550da1cb2d909006e90004b1",           # Id of the current sit'n'go game. You can use this to link a
#                                                     # sequence of game states together for logging purposes, or to
#                                                     # make sure that the same strategy is played for an entire game
#
#     "round":0,                                      # Index of the current round within a sit'n'go
#
#     "bet_index":0,                                  # Index of the betting opportunity within a round
#
#     "small_blind": 10,                              # The small blind in the current round. The big blind is twice the
#                                                     #     small blind
#
#     "current_buy_in": 80,                          # The amount of the largest current bet from any one player
#
#     "pot": 400,                                     # The size of the pot (sum of the player bets)
#
#     "minimum_raise": 240,                           # Minimum raise amount. To raise you have to return at least:
#                                                     #     current_buy_in - players[in_action][bet] + minimum_raise
#
#     "dealer": 1,                                    # The index of the player on the dealer button in this round
#                                                     #     The first player is (dealer+1)%(players.length)
#
#     "orbits": 7,                                    # Number of orbits completed. (The number of times the dealer
#                                                     #     button returned to the same player.)
#
#     "in_action": 1,                                 # The index of your player, in the players array
#
#     "players": [                                    # An array of the players. The order stays the same during the
#         {                                           #     entire tournament
#
#             "id": 0,                                # Id of the player (same as the index)
#
#             "name": "Albert",                       # Name specified in the tournament config
#
#             "status": "active",                     # Status of the player:
#                                                     #   - active: the player can make bets, and win the current pot
#                                                     #   - folded: the player folded, and gave up interest in
#                                                     #       the current pot. They can return in the next round.
#                                                     #   - out: the player lost all chips, and is out of this sit'n'go
#
#             "version": "Default random player",     # Version identifier returned by the player
#
#             "stack": 1010,                          # Amount of chips still available for the player. (Not including
#                                                     #     the chips the player bet in this round.)
#
#             "bet": 0                              # The amount of chips the player put into the pot
#         },
#         {
#             "id": 1,                                # Your own player looks similar, with one extension.
#             "name": "Glorious Ape",
#             "status": "active",
#             "version": "Default random player",
#             "stack": 1590,
#             "bet": 80,
#             "hole_cards": [                         # The cards of the player. This is only visible for your own player
#                                                     #     except after showdown, when cards revealed are also included.
#                 {
#                     "rank": "K",                    # Rank of the card. Possible values are numbers 2-10 and J,Q,K,A
#                     "suit": "hearts"                # Suit of the card. Possible values are: clubs,spades,hearts,diamonds
#                 },
#                 {
#                     "rank": "K",
#                     "suit": "spades"
#                 }
#             ]
#         },
#         {
#             "id": 2,
#             "name": "Chuck",
#             "status": "out",
#             "version": "Default random player",
#             "stack": 0,
#             "bet": 0
#         }
#     ],
#     "community_cards": [                            # Finally the array of community cards.
#         {
#             "rank": "10",
#             "suit": "spades"
#         },
#         {
#
#             "rank": "A",
#
#
#             "suit": "hearts"
#         },
#         {
#             "rank": "8",
#             "suit": "clubs"
#         }
#     ]
# }

class Player:
    VERSION = "destroying team orange HAHAHAHA"

    def betRequest(self, game_state):
        # if len(game_state['community_cards']) == 0:
        #     if self.preflop(game_state) == "highcards" or "pairinhand":
        #         return 20
        # elif len(game_state['community_cards']) > 3:
        if self.if_drill(game_state) == "drill":
            # print("drill")
            return 1000
        elif self.two_pairs(game_state) == "twopair":
            # print("two pairs")
            return 1000
        elif self.ifpair(game_state) == "pair":
            # print("pair")
            return 1000
        elif self.ifpairhand(game_state) == "pairinhand":
            # print("pairinhand")
            return 1000
        elif self.ifhighcards(game_state) == "high":
            # print("high")
            return int(self.player(game_state)['stack'])/2
        # elif self.ifhighcards(game_state) == "10":
        #     print("10")
        #     return 200
        else:
            # print("nothing")
            return 0

    def showdown(self, game_state):
        pass

    def preflop(self, game_state):
        list = ['10', 'J', 'Q', 'K', 'A']
        hand = self.hand(game_state)
        if self.ifpairhand(game_state) == "pairinhand":
            return "pairinhand"
        elif hand[0]['rank'] in list and hand[1]['rank'] in list:
            return "highcards"
        else:
            return "fold"

    def ifhighcards(self, game_state):
        high = ['J', 'Q', 'K', 'A']
        hand = self.hand(game_state)
        if hand[0]['rank'] in high and hand[1]['rank'] in high:
            return "high"
        else:
            return "fold"

    def ifpairhand(self, game_state):
        hand = self.hand(game_state)
        if hand[0]['rank'] == hand[1]['rank']:
            return "pairinhand"
        else:
            return "fold"

    def ifpair(self, game_state):
        hand = self.hand(game_state)
        comm = self.community_cards(game_state)
        list = []
        for i in comm:
            list.append(i['rank'])
        for i in list:
            if hand[0]["rank"] == i or hand[1]["rank"] == i:
                return 'pair'

    def if_drill(self, game_state):
        hand = self.hand(game_state)
        comm = self.community_cards(game_state)
        list = []
        for i in comm:
            list.append(i['rank'])
        count = 0
        count2 = 0
        for i in list:
            if hand[0]['rank'] == i:
                count += 1
        if self.ifpairhand(game_state) == "pairinhand":
            for i in list:
                if hand[0]['rank'] == i:
                    count2 += 1
        if count == 2 or count2 == 1:
            return 'drill'
        else:
            return 'fold'

    def player(self, game_state):
        for player in game_state['players']:
            if player['name'] == 'Glorious Ape':
                return player

    def hand(self, game_state):
        player = self.player(game_state)
        return player['hole_cards']

    def community_cards(self, game_state):
        return game_state['community_cards']

    def two_pairs(self, game_state):
        hand = self.hand(game_state)
        comm_card = self.community_cards(game_state)
        if hand[0]['rank'] != hand[1]['rank']:
            comm_card_values = set([])
            for card in comm_card:
                comm_card_values.add(card["rank"])
            if hand[0]['rank'] in comm_card_values and hand[1]['rank'] in comm_card_values:
                return "twopair"
            else:
                return "fold"

# x = Player()
# x.betRequest(gamestate)
<commit_msg>pleeeease<commit_after># gamestate = {
#     "tournament_id":"550d1d68cd7bd10003000003",     # Id of the current tournament
#
#     "game_id":"550da1cb2d909006e90004b1",           # Id of the current sit'n'go game. You can use this to link a
#                                                     # sequence of game states together for logging purposes, or to
#                                                     # make sure that the same strategy is played for an entire game
#
#     "round":0,                                      # Index of the current round within a sit'n'go
#
#     "bet_index":0,                                  # Index of the betting opportunity within a round
#
#     "small_blind": 10,                              # The small blind in the current round. The big blind is twice the
#                                                     #     small blind
#
#     "current_buy_in": 80,                          # The amount of the largest current bet from any one player
#
#     "pot": 400,                                     # The size of the pot (sum of the player bets)
#
#     "minimum_raise": 240,                           # Minimum raise amount. To raise you have to return at least:
#                                                     #     current_buy_in - players[in_action][bet] + minimum_raise
#
#     "dealer": 1,                                    # The index of the player on the dealer button in this round
#                                                     #     The first player is (dealer+1)%(players.length)
#
#     "orbits": 7,                                    # Number of orbits completed. (The number of times the dealer
#                                                     #     button returned to the same player.)
#
#     "in_action": 1,                                 # The index of your player, in the players array
#
#     "players": [                                    # An array of the players. The order stays the same during the
#         {                                           #     entire tournament
#
#             "id": 0,                                # Id of the player (same as the index)
#
#             "name": "Albert",                       # Name specified in the tournament config
#
#             "status": "active",                     # Status of the player:
#                                                     #   - active: the player can make bets, and win the current pot
#                                                     #   - folded: the player folded, and gave up interest in
#                                                     #       the current pot. They can return in the next round.
#                                                     #   - out: the player lost all chips, and is out of this sit'n'go
#
#             "version": "Default random player",     # Version identifier returned by the player
#
#             "stack": 1010,                          # Amount of chips still available for the player. (Not including
#                                                     #     the chips the player bet in this round.)
#
#             "bet": 0                              # The amount of chips the player put into the pot
#         },
#         {
#             "id": 1,                                # Your own player looks similar, with one extension.
#             "name": "Glorious Ape",
#             "status": "active",
#             "version": "Default random player",
#             "stack": 1590,
#             "bet": 80,
#             "hole_cards": [                         # The cards of the player. This is only visible for your own player
#                                                     #     except after showdown, when cards revealed are also included.
#                 {
#                     "rank": "8",                    # Rank of the card. Possible values are numbers 2-10 and J,Q,K,A
#                     "suit": "hearts"                # Suit of the card. Possible values are: clubs,spades,hearts,diamonds
#                 },
#                 {
#                     "rank": "4",
#                     "suit": "spades"
#                 }
#             ]
#         },
#         {
#             "id": 2,
#             "name": "Chuck",
#             "status": "out",
#             "version": "Default random player",
#             "stack": 0,
#             "bet": 0
#         }
#     ],
#     "community_cards": [                            # Finally the array of community cards.
#
#     ]
# }
# {
#     "rank": "10",
#     "suit": "spades"
# },
# {
#
#     "rank": "A",
#     "suit": "hearts"
# },
# {
#     "rank": "8",
#     "suit": "clubs"
# }
class Player:
    VERSION = "destroying team orange HAHAHAHA"

    def betRequest(self, game_state):
        if len(game_state['community_cards']) == 0:
            if self.preflop(game_state) == "highcards" or self.preflop(game_state) == "pairinhand":
                return 20
            elif self.preflop(game_state) == "fold":
                return 0
            else:
                return 0
        elif len(game_state['community_cards']) > 3:
            if self.if_drill(game_state) == "drill":
                # print("drill")
                return 1000
            elif self.two_pairs(game_state) == "twopair":
                # print("two pairs")
                return 1000
            elif self.ifpair(game_state) == "pair":
                # print("pair")
                return 1000
            elif self.ifpairhand(game_state) == "pairinhand":
                # print("pairinhand")
                return 1000
            elif self.ifhighcards(game_state) == "high":
                # print("high")
                return int(self.player(game_state)['stack'])/2
            # elif self.ifhighcards(game_state) == "10":
            #     print("10")
            #     return 200
            else:
                # print("nothing")
                return 0

    def showdown(self, game_state):
        pass

    def preflop(self, game_state):
        list = ['10', 'J', 'Q', 'K', 'A']
        hand = self.hand(game_state)
        if self.ifpairhand(game_state) == "pairinhand":
            return "pairinhand"
        elif hand[0]['rank'] in list and hand[1]['rank'] in list:
            return "highcards"
        else:
            return "fold"

    def ifhighcards(self, game_state):
        high = ['J', 'Q', 'K', 'A']
        hand = self.hand(game_state)
        if hand[0]['rank'] in high and hand[1]['rank'] in high:
            return "high"
        else:
            return "fold"

    def ifpairhand(self, game_state):
        hand = self.hand(game_state)
        if hand[0]['rank'] == hand[1]['rank']:
            return "pairinhand"
        else:
            return "fold"

    def ifpair(self, game_state):
        hand = self.hand(game_state)
        comm = self.community_cards(game_state)
        list = []
        for i in comm:
            list.append(i['rank'])
        for i in list:
            if hand[0]["rank"] == i or hand[1]["rank"] == i:
                return 'pair'

    def if_drill(self, game_state):
        hand = self.hand(game_state)
        comm = self.community_cards(game_state)
        list = []
        for i in comm:
            list.append(i['rank'])
        count = 0
        count2 = 0
        for i in list:
            if hand[0]['rank'] == i:
                count += 1
        if self.ifpairhand(game_state) == "pairinhand":
            for i in list:
                if hand[0]['rank'] == i:
                    count2 += 1
        if count == 2 or count2 == 1:
            return 'drill'
        else:
            return 'fold'

    def player(self, game_state):
        for player in game_state['players']:
            if player['name'] == 'Glorious Ape':
                return player

    def hand(self, game_state):
        player = self.player(game_state)
        return player['hole_cards']

    def community_cards(self, game_state):
        return game_state['community_cards']

    def two_pairs(self, game_state):
        hand = self.hand(game_state)
        comm_card = self.community_cards(game_state)
        if hand[0]['rank'] != hand[1]['rank']:
            comm_card_values = set([])
            for card in comm_card:
                comm_card_values.add(card["rank"])
            if hand[0]['rank'] in comm_card_values and hand[1]['rank'] in comm_card_values:
                return "twopair"
            else:
                return "fold"

# x = Player()
# x.betRequest(gamestate)
<|endoftext|>


<commit_before>import csv

class Student:
  def __init__(self, fields):
    self.fields = fields # hash table of user fields

students = []
pairs = []
language_prof = {1:"Elementary proficiency", 2:"Limited proficiency", 3:"Intermediate proficiency", 4:"Nearly-full professional proficiency"}

# setup function to read the user csv
# and initialize hash table of student objects
def setup():

  with open("script/newsheet.csv", "r") as users:
    reader = csv.DictReader(users)
    for row in reader:
      fields = {
          'name': row['first_name'] + row['last_name'],
          'gender': row['gender'],
          'academic_title': row['academic_title'],
          'residency': row['residency'],
          'gender_preference': row['gender_preference'],
          'fluent_languages': row['fluent_languages'],
          'lang_additional_info': row['lang_additional_info'],
          'first_lang_preference': row['first_lang_preference'],
          'first_lang_proficiency': row['first_lang_proficiency'],
          'second_lang_preference': row['second_lang_preference'],
          'second_lang_proficiency': row['second_lang_proficiency'],
          'time_preference': row['time_preference'],
          'hours_per_week': row['hours_per_week'],
          'sid': row['sid']
      }
      s = Student(fields)
      students.append(s)

    users.close()

'''
for line in users:
    newline = line.split(",")
    students.append(newline)
    for word in range(len(newline)):
        if newline[word] == "Elementary proficiency":
            newline[word] = 1
        elif newline[word] == "Limited proficiency":
            newline[word] = 2
        elif newline[word] == "Intermediate proficiency":
            newline[word] = 3
        elif newline[word] == "Nearly-full professional proficiency":
            newline[word] = 4
'''

# a score is a tuple of (gender, gender_preference)
def gender_score(score1, score2):
  if score1[1] == "Indifferent" and score2[1] == 'Indifferent':
    return 1
  if score1[1] == score2[0] and score2[1] == score1[0]:
    return 5
  if (score1[1] == score2[0] and score2[1] == 'Indifferent') or (score1[0] == score2[1] and score1[1] == 'Indifferent'):
    return 3
  else:
    return 0

# languages = list of tuples: (language_preference, language_proficiency)
def data_clean(languages):
  cleaned = []
  ls = []
  for (l, p) in languages:
    if type(l) == list:
      for i in xrange(len(l)):
        if l[i] != '' and l[i] not in ls:
          cleaned.append((l[i], p[i]))
          ls.append(l[i])
    else:
      if l != '' and l not in ls:
        if p !='':
          cleaned.append((l, p))
          ls.append(l)
  return cleaned

def get_profi(student):
    na = student[9].split(",")
    sn = []
    for nai in na:
        sni = 5
        if nai == student[14]:
            if student[15] != '':
                sni = student[15]
        elif nai == student[16]:
            if student[17] != '':
                sni = student[17]
        sn.append(sni)
    return sn

def mutual_interest(sn, si, pn, pi):
    total = 0
    st, pt = False, False
    for (sl, spro) in sn:
        for (pl, ppro) in pi:
            if sl == pl:
                st = True
                total -= abs(spro-ppro)*0.25
    for (pl, ppro) in pn:
        for (sl, spro) in si:
            if sl == pl:
                pt = True
                total -= abs(spro-ppro)*0.25
    if st and pt:
        total+=15
        return (True, total)
    return (False, 0)

def same_interest(si, pi):
    total = 0
    languages = []
    for (sl, sp) in si:
        for (pl, pp) in pi:
            if sl == pl:
                languages.append(sl)
                total += 5 - abs(sp-pp)*0.5
    return total

def one_interest(sn, si, pn, pi):
  total = 0
  for (s1_lang, s1_proficiency) in sn:
    for (s2_lang, s2_proficiency) in pi:
      if s1_lang == s2_lang:
        total += 3 - abs(s1_proficiency-s2_proficiency)*0.5
  for (s2_lang, s2_proficiency) in pn:
    for (s1_lang, s1_proficiency) in si:
      if s1_lang == s2_lang:
        total += 3 - abs(s1_proficiency-s2_proficiency)*0.5
  return total

def language_score(student, potential):
    final = 0
    sn, si = student
    pn, pi = potential
    if sn[0] != pn[0]:
        final += 1
    mi, mi_score = mutual_interest(sn, si, pn, pi)
    si_score = same_interest(si, pi)
    if not mi:
        final += one_interest(sn, si, pn, pi)
    final += mi_score + si_score
    return final

# assumes each day...fck this method.
def time_score(s1_time, s2_time, s1_hour, s2_hour):
  total = 0.0
  for s1_day in s1_time:
    for s2_day in s2_time:
      if s1_day == s2_day:
        total += 1
  if s1_hour == s2_hour:
    total += 0.5
  return total

def meetup(s1_time, s2_time):
  t = ''
  week = {0:"Monday: ", 1:"Tuesday: ", 2:"Wednesday: ", 3:"Thursday: ", 4:"Friday: "}
  for i in xrange(5):
    check = True
    for s1_day in s1_time:
      for s2_day in s2_time:
        if s1_day == s2_day:
          if check:
            t += s1_day
            check = False
          else:
            t += ", " + s1_day
  return t

def language_detection(best_pair):
    s1, s2 = best_pair
    sn1, si1 = s1
    sn2, si2 = s2
    languages = ''
    visited = []
    for (l1, p1) in sn1:
        for (l2, p2) in si2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    for (l1, p1) in si1:
        for (l2, p2) in sn2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    for (l1, p1) in si1:
        for (l2, p2) in si2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    languages = languages[:(len(languages)-2)]
    return languages

setup()
pairs = open('script/final_pairs.csv', 'w')
fields = ['partner1', 'partner2', 'languages(s)', 'possible meetup time', 'stability']
writer = csv.DictWriter(pairs, fieldnames=fields)
writer.writeheader()
#pairs.write("partner1\tpartner2\tlanguage(s)\tPossible Meetup time\tstability\n")

# attempt to pair every student with another student
while len(students) != 0:
    print(len(students))
    if len(students) == 1:
        student = students.keys()[0]
        pairs.writerow({'partner1': student.fields['name']})
        students.remove(student)
        break
    final = 0.0
    for student1 in students:
        s1_academic = student1.fields['academic_title']
        s1_residency = student1.fields['residency']
        s1_gender = (student1.fields['gender'], student1.fields['gender_preference'])
        fluent_lang = student1.fields['fluent_languages'].split(',')
        s1_lang_to_teach = []
        for lang in fluent_lang:
          s1_lang_to_teach.append((lang, 4))
        s1_lang_to_learn = data_clean([(student1.fields['first_lang_preference'], student1.fields['first_lang_proficiency']),
              (student1.fields['second_lang_preference'], student1.fields['second_lang_proficiency'])])
        s1_time = student1.fields['time_preference']
        s1_hour = student1.fields['hours_per_week']
        highest = 0.0

        for student2 in students:
            total = 0.0
            if student2 != student1:
                s2_academic = student2.fields['residency']
                s2_residency = student2.fields['residency']
                s2_gender = (student2.fields['gender'], student2.fields['gender_preference'])
                fluent_lang = student2.fields['fluent_languages'].split(',')
                s2_lang_to_teach = []
                for lang in fluent_lang:
                  s2_lang_to_teach.append((lang, 4))
                s2_lang_to_learn = data_clean([(student2.fields['first_lang_preference'], student2.fields['first_lang_proficiency']),
                    (student2.fields['second_lang_preference'], student2.fields['second_lang_proficiency'])])
                s2_time = student2.fields['time_preference']
                s2_hour = student2.fields['hours_per_week']
                #if sacademic == pacademic:
                    #total += 0.5
                if s1_residency != s2_residency:
                    total += 0.5
                total += gender_score(s1_gender, s2_gender)
                total += language_score((s1_lang_to_teach, s1_lang_to_learn), (s2_lang_to_teach, s2_lang_to_learn))
                total += time_score(s1_time, s2_time, s1_hour, s2_hour)
                total += abs(int(s1_hour)+int(s2_hour))*0.5 - abs(int(s1_hour)-int(s2_hour))*0.5
                if highest < total:
                    highest = total
                    best = student2
                    best_language = (s2_lang_to_teach, s2_lang_to_learn)
        if final < highest:
            final = highest
            best_pair = (student1, best)
            best_language = ((s1_lang_to_teach, s1_lang_to_learn), best_language)
    partner1 = best_pair[0]
    partner2 = best_pair[1]
    p1_name = partner1.fields['name']
    p2_name = partner2.fields['name']
    language = language_detection(best_language)
    hours = meetup(s1_time, s2_time)
    #line = p1_name+"("+partner1[3]+")"+"\t"+p2_name+"("+partner2[3]+")\t"+language+"\t"+hours+"\t"+str(final)+"\n"
    #pairs.write(line)
    writer.writerow({'partner1': p1_name+'('+partner1.fields['sid']+')', 'partner2': p2_name+'('+partner2.fields['sid']+')', 'language(s)': language, 'possible meetup time': hours, 'stability': str(final)})
    students.remove(partner1)
    students.remove(partner2)

#need to check if the student put the right ID number

<commit_msg>refactored algorithm<commit_after>import csv

class Student:
  def __init__(self, fields):
    self.fields = fields # hash table of user fields

students = []
pairs = []
language_prof = {"elementary": 1, "limited": 2, "intermediate": 3, "nearly_proficient": 4}

def lookup(proficiency):
  return language_prof[proficiency]

# setup function to read the user csv
# and initialize hash table of student objects
def setup():

  with open("script/newsheet.csv", "r") as users:
    reader = csv.DictReader(users)
    for row in reader:
      fields = {
          'name': row['first_name'] + row['last_name'],
          'gender': row['gender'],
          'academic_title': row['academic_title'],
          'residency': row['residency'],
          'gender_preference': row['gender_preference'],
          'fluent_languages': row['fluent_languages'],
          'lang_additional_info': row['lang_additional_info'],
          'first_lang_preference': row['first_lang_preference'],
          'first_lang_proficiency': lookup(row['first_lang_proficiency']),
          'second_lang_preference': row['second_lang_preference'],
          'second_lang_proficiency': lookup(row['second_lang_proficiency']),
          'time_preference': row['time_preference'],
          'hours_per_week': row['hours_per_week'],
          'sid': row['sid']
      }
      s = Student(fields)
      students.append(s)

    users.close()

'''
for line in users:
    newline = line.split(",")
    students.append(newline)
    for word in range(len(newline)):
        if newline[word] == "Elementary proficiency":
            newline[word] = 1
        elif newline[word] == "Limited proficiency":
            newline[word] = 2
        elif newline[word] == "Intermediate proficiency":
            newline[word] = 3
        elif newline[word] == "Nearly-full professional proficiency":
            newline[word] = 4
'''

# a score is a tuple of (gender, gender_preference)
def gender_score(score1, score2):
  if score1[1] == "Indifferent" and score2[1] == 'Indifferent':
    return 1
  if score1[1] == score2[0] and score2[1] == score1[0]:
    return 5
  if (score1[1] == score2[0] and score2[1] == 'Indifferent') or (score1[0] == score2[1] and score1[1] == 'Indifferent'):
    return 3
  else:
    return 0

# languages = list of tuples: (language_preference, language_proficiency)
def data_clean(languages):
  cleaned = []
  ls = []
  for (l, p) in languages:
    if type(l) == list:
      for i in xrange(len(l)):
        if l[i] != '' and l[i] not in ls:
          cleaned.append((l[i], p[i]))
          ls.append(l[i])
    else:
      if l != '' and l not in ls:
        if p !='':
          cleaned.append((l, p))
          ls.append(l)
  return cleaned

def get_profi(student):
    na = student[9].split(",")
    sn = []
    for nai in na:
        sni = 5
        if nai == student[14]:
            if student[15] != '':
                sni = student[15]
        elif nai == student[16]:
            if student[17] != '':
                sni = student[17]
        sn.append(sni)
    return sn

def mutual_interest(sn, si, pn, pi):
    total = 0
    st, pt = False, False
    for (sl, spro) in sn:
        for (pl, ppro) in pi:
            if sl == pl:
                st = True
                total -= abs(spro-ppro)*0.25
    for (pl, ppro) in pn:
        for (sl, spro) in si:
            if sl == pl:
                pt = True
                total -= abs(spro-ppro)*0.25
    if st and pt:
        total+=15
        return (True, total)
    return (False, 0)

def same_interest(si, pi):
    total = 0
    languages = []
    for (sl, sp) in si:
        for (pl, pp) in pi:
            if sl == pl:
                languages.append(sl)
                total += 5 - abs(sp-pp)*0.5
    return total

def one_interest(sn, si, pn, pi):
  total = 0
  for (s1_lang, s1_proficiency) in sn:
    for (s2_lang, s2_proficiency) in pi:
      if s1_lang == s2_lang:
        total += 3 - abs(s1_proficiency-s2_proficiency)*0.5
  for (s2_lang, s2_proficiency) in pn:
    for (s1_lang, s1_proficiency) in si:
      if s1_lang == s2_lang:
        total += 3 - abs(s1_proficiency-s2_proficiency)*0.5
  return total

def language_score(student, potential):
    final = 0
    sn, si = student
    pn, pi = potential
    if sn[0] != pn[0]:
        final += 1
    mi, mi_score = mutual_interest(sn, si, pn, pi)
    si_score = same_interest(si, pi)
    if not mi:
        final += one_interest(sn, si, pn, pi)
    final += mi_score + si_score
    return final

# assumes each day...fck this method.
def time_score(s1_time, s2_time, s1_hour, s2_hour):
  total = 0.0
  for s1_day in s1_time:
    for s2_day in s2_time:
      if s1_day == s2_day:
        total += 1
  if s1_hour == s2_hour:
    total += 0.5
  return total

def meetup(s1_time, s2_time):
  t = ''
  week = {0:"Monday: ", 1:"Tuesday: ", 2:"Wednesday: ", 3:"Thursday: ", 4:"Friday: "}
  #for i in xrange(5):
  #print 's1_time: ', s1_time
  check = True
  for s1_day in s1_time:
    #print 's1_time, again: ', s1_day
    for s2_day in s2_time:
      if s1_day == s2_day:
        if check:
          t += s1_day
          check = False
        else:
          t += ", " + s1_day
  return t

def language_detection(best_pair):
    s1, s2 = best_pair
    print "s1:", s1
    print "s2:", s2
    sn1, si1 = s1[0]
    sn2, si2 = s2[0]
    languages = ''
    visited = []
    for (l1, p1) in sn1:
        for (l2, p2) in si2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    for (l1, p1) in si1:
        for (l2, p2) in sn2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    for (l1, p1) in si1:
        for (l2, p2) in si2:
            if l1 == l2 and l1 not in visited:
                languages+=l1+"("+language_prof[min(p1, p2)]+"), "
                visited.append(l1)
    languages = languages[:(len(languages)-2)]
    return languages

setup()
pairs = open('script/final_pairs.csv', 'w')
fields = ['partner1', 'partner2', 'language(s)', 'possible meetup time', 'stability']
writer = csv.DictWriter(pairs, fieldnames=fields)
writer.writeheader()
#pairs.write("partner1\tpartner2\tlanguage(s)\tPossible Meetup time\tstability\n")

# attempt to pair every student with another student
while len(students) != 0:
    print(len(students))
    if len(students) == 1:
        student = students.keys()[0]
        pairs.writerow({'partner1': student.fields['name']})
        students.remove(student)
        break
    final = 0.0
    for student1 in students:
        s1_academic = student1.fields['academic_title']
        s1_residency = student1.fields['residency']
        s1_gender = (student1.fields['gender'], student1.fields['gender_preference'])
        fluent_lang = student1.fields['fluent_languages'].split(',')
        s1_lang_to_teach = []
        for lang in fluent_lang:
          s1_lang_to_teach.append((lang, 4))
        s1_lang_to_learn = data_clean([(student1.fields['first_lang_preference'], student1.fields['first_lang_proficiency']),
              (student1.fields['second_lang_preference'], student1.fields['second_lang_proficiency'])])
        s1_time = student1.fields['time_preference']
        s1_hour = student1.fields['hours_per_week']
        highest = 0.0

        for student2 in students:
            total = 0.0
            if student2 != student1:
                s2_academic = student2.fields['residency']
                s2_residency = student2.fields['residency']
                s2_gender = (student2.fields['gender'], student2.fields['gender_preference'])
                fluent_lang = student2.fields['fluent_languages'].split(',')
                s2_lang_to_teach = []
                for lang in fluent_lang:
                  s2_lang_to_teach.append((lang, 4))
                s2_lang_to_learn = data_clean([(student2.fields['first_lang_preference'], student2.fields['first_lang_proficiency']),
                    (student2.fields['second_lang_preference'], student2.fields['second_lang_proficiency'])])
                s2_time = student2.fields['time_preference']
                s2_hour = student2.fields['hours_per_week']
                #if sacademic == pacademic:
                    #total += 0.5
                if s1_residency != s2_residency:
                    total += 0.5
                total += gender_score(s1_gender, s2_gender)
                total += language_score((s1_lang_to_teach, s1_lang_to_learn), (s2_lang_to_teach, s2_lang_to_learn))
                total += time_score(s1_time, s2_time, s1_hour, s2_hour)
                total += abs(int(s1_hour)+int(s2_hour))*0.5 - abs(int(s1_hour)-int(s2_hour))*0.5
                if highest < total:
                    highest = total
                    best = student2
                    best_language = (s2_lang_to_teach, s2_lang_to_learn)
        if final < highest:
            final = highest
            best_pair = (student1, best)
            best_language = ((s1_lang_to_teach, s1_lang_to_learn), best_language)
    partner1 = best_pair[0]
    partner2 = best_pair[1]
    p1_name = partner1.fields['name']
    p2_name = partner2.fields['name']
    #language = language_detection(best_language)
    language = partner1.fields['first_lang_preference']
    hours = meetup(s1_time, s2_time)
    #line = p1_name+"("+partner1[3]+")"+"\t"+p2_name+"("+partner2[3]+")\t"+language+"\t"+hours+"\t"+str(final)+"\n"
    #pairs.write(line)
    print language
    writer.writerow({'partner1': p1_name+'('+partner1.fields['sid']+')', 'partner2': p2_name+'('+partner2.fields['sid']+')', 'language(s)': language, 'possible meetup time': hours, 'stability': str(final)})
    students.remove(partner1)
    students.remove(partner2)

#need to check if the student put the right ID number

<|endoftext|>


<commit_before>from itertools import chain
from ConfigParser import ConfigParser

import yaml

from springboard.utils import parse_repo_name
from springboard.tools.commands.bootstrap import BootstrapTool
from springboard.tools.commands.base import (
    SpringboardToolCommand, CommandArgument)

class ImportContentTool(BootstrapTool):

    command_name = 'import'
    command_help_text = 'Clone and import a content repository locally.'
    command_arguments = SpringboardToolCommand.command_arguments + (
        CommandArgument(
            'repo_url',
            metavar='repo_url',
            help='The URL of the Git content repository to clone.'),
        CommandArgument(
            '-i', '--ini',
            dest='ini_config',
            default='development.ini',
            help='The paste ini file to update.'),
        CommandArgument(
            '-s', '--ini-section',
            dest='ini_section',
            default='app:main',
            help='The paste ini section to update'),
        CommandArgument(
            '-u', '--update-config',
            dest='update_config',
            default=True,
            help='Add the repository to the config files?',
            action='store_false'),
        CommandArgument(
            '-n', '--name',
            dest='repo_name',
            help='Give the repository a custom name on disk.'),
    )

    def run(self, config, verbose, clobber, repo_dir, repo_url,
            ini_config, ini_section, update_config, repo_name):
        config_file, config_data = config
        repo_name = repo_name or parse_repo_name(repo_url)
        workdir, _ = self.clone_repo(repo_name=repo_name,
                                     repo_url=repo_url,
                                     repo_dir=repo_dir,
                                     clobber=clobber,
                                     verbose=verbose)
        self.bootstrap(
            workdir,
            config_data.get('models', {}).items(),
            clobber=clobber,
            verbose=verbose)

        if not update_config:
            return

        repositories = config_data.setdefault('repositories', {})

        if repo_name not in repositories:
            repositories[repo_name] = repo_url

            with open(config_file, 'w') as fp:
                yaml.safe_dump(config_data,
                               stream=fp, default_flow_style=False)
            self.emit('Added %s to the %s config file.' % (
                repo_name, config_file))

        config_key = 'unicore.content_repo_urls'

        cp = ConfigParser()
        cp.read(ini_config)
        if not cp.has_section(ini_section):
            cp.add_section(ini_section)

        existing_repo_urls = (cp.get(ini_section, config_key)
                              if cp.has_option(ini_section, config_key)
                              else '')
        existing_repo_urls = existing_repo_urls.strip().split('\n')

        if repo_url not in existing_repo_urls:
            cp.set(ini_section, config_key,
                   '\n'.join(chain(existing_repo_urls, [repo_url])))
            with open(ini_config, 'w') as fp:
                cp.write(fp)
            self.emit(
                'Updated unicore.content_repo_urls in %s.' % (ini_config,))
<commit_msg>update to use config_list<commit_after>from itertools import chain
from ConfigParser import ConfigParser

import yaml

from springboard.utils import parse_repo_name, config_list
from springboard.tools.commands.bootstrap import BootstrapTool
from springboard.tools.commands.base import (
    SpringboardToolCommand, CommandArgument)

class ImportContentTool(BootstrapTool):

    command_name = 'import'
    command_help_text = 'Clone and import a content repository locally.'
    command_arguments = SpringboardToolCommand.command_arguments + (
        CommandArgument(
            'repo_url',
            metavar='repo_url',
            help='The URL of the Git content repository to clone.'),
        CommandArgument(
            '-i', '--ini',
            dest='ini_config',
            default='development.ini',
            help='The paste ini file to update.'),
        CommandArgument(
            '-s', '--ini-section',
            dest='ini_section',
            default='app:main',
            help='The paste ini section to update'),
        CommandArgument(
            '-u', '--update-config',
            dest='update_config',
            default=True,
            help='Add the repository to the config files?',
            action='store_false'),
        CommandArgument(
            '-n', '--name',
            dest='repo_name',
            help='Give the repository a custom name on disk.'),
    )

    def run(self, config, verbose, clobber, repo_dir, repo_url,
            ini_config, ini_section, update_config, repo_name):
        config_file, config_data = config
        repo_name = repo_name or parse_repo_name(repo_url)
        workdir, _ = self.clone_repo(repo_name=repo_name,
                                     repo_url=repo_url,
                                     repo_dir=repo_dir,
                                     clobber=clobber,
                                     verbose=verbose)
        self.bootstrap(
            workdir,
            config_data.get('models', {}).items(),
            clobber=clobber,
            verbose=verbose)

        if not update_config:
            return

        repositories = config_data.setdefault('repositories', {})

        if repo_name not in repositories:
            repositories[repo_name] = repo_url

            with open(config_file, 'w') as fp:
                yaml.safe_dump(config_data,
                               stream=fp, default_flow_style=False)
            self.emit('Added %s to the %s config file.' % (
                repo_name, config_file))

        config_key = 'unicore.content_repo_urls'

        cp = ConfigParser()
        cp.read(ini_config)
        if not cp.has_section(ini_section):
            cp.add_section(ini_section)

        existing_repo_urls = config_list(
            cp.get(ini_section, config_key)
            if cp.has_option(ini_section, config_key)
            else '')

        if repo_url not in existing_repo_urls:
            cp.set(ini_section, config_key,
                   '\n'.join(chain(existing_repo_urls, [repo_url])))
            with open(ini_config, 'w') as fp:
                cp.write(fp)
            self.emit(
                'Updated unicore.content_repo_urls in %s.' % (ini_config,))
<|endoftext|>


<commit_before># coding: UTF-8
#
# TileCutter debugger window
#

# Copyright  2008-2010 Timothy Baldock. All Rights Reserved.

# To use:
# import logger
# debug = logger.Log()
#
# Then debug(<string to output>)
# Time/newline added automatically

import config
config = config.Config()

from datetime import datetime as datetime

import codecs

class Log(object):
    """Debug/log output to file"""
    file = None
    def __init__(self, file=None):
        """"""
        if Log.file == None:
            if file == None:
                # Appends this session's log info to the logging file
                Log.file = codecs.open(config.logfile, "a", "UTF-8")
            else:
                Log.file = codecs.open(file, "a", "UTF-8")
    def __call__(self, s):
        """Calls self.write()"""
        self.out(s)
    def out(self, s):
        """Write a string to file, stripping newlines and reformatting"""
        s = s.replace(u"\r", u"")
        splits = s.split(u"\n")
        writedate = True
        for k in splits:
            outline = datetime.now().replace(microsecond=0).isoformat(" ") + " | " + k + "\n"
            Log.file.write(outline, date=writedate)
            writedate = False
        Log.file.flush()
    def write(self, s, date=False):
        """Write a string to file, preserving newlines fed in"""
        # Check string, if it's entirely whitespace or just a newline, or blank do nothing
        # Prepend current time/date, append newline
        dt = datetime.now().replace(microsecond=0).isoformat(" ")
        # Check if next line will need the date prepending
        if date:
            outline = dt + u" | " + s
        else:
            outline = u" " * len(dt) + u" | " + s
        Log.file.write(outline)
        Log.file.flush()

<commit_msg>Better logging module<commit_after># coding: UTF-8
#
# TileCutter debugger window
#

# Copyright  2008-2010 Timothy Baldock. All Rights Reserved.

# To use:
# import logger
# debug = logger.Log()
#
# Then debug(<string to output>)
# Time/newline added automatically

import config
config = config.Config()

from datetime import datetime as datetime

import codecs

class Log(object):
    """Debug/log output to file"""
    file = None
    def __init__(self, file=None):
        """"""
        if Log.file == None:
            if file == None:
                # Appends this session's log info to the logging file
                Log.file = codecs.open(config.logfile, "a", "UTF-8")
            else:
                Log.file = codecs.open(file, "a", "UTF-8")
    def __call__(self, s):
        """Calls self.write()"""
        self.out(s)
    def out(self, s):
        """Write a string to file, stripping newlines and reformatting"""
        s = s.replace(u"\r", u"")
        splits = s.split(u"\n")
        writedate = True
        for k in splits:
            outline = k + "\n"
            self.write(outline, date=writedate)
            writedate = False
        Log.file.flush()
    def write(self, s, date=False):
        """Write a string to file, preserving newlines fed in"""
        # Check string, if it's entirely whitespace or just a newline, or blank do nothing
        # Prepend current time/date, append newline
        dt = datetime.now().replace(microsecond=0).isoformat(" ")
        # Check if next line will need the date prepending
        if date:
            outline = dt + u" | " + s
        else:
            outline = u" " * len(dt) + u" | " + s
        Log.file.write(outline)
        Log.file.flush()

<|endoftext|>