<commit_before>###
# Copyright (c) 2013, <NAME>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

###

import re
import os
import sys
import datetime

import supybot.utils as utils
import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('Aka')

try:
    import sqlalchemy
    import sqlalchemy.ext
    import sqlalchemy.ext.declarative
except ImportError:
    raise callbacks.error('You have to install python-sqlalchemy in order '
            'to load this plugin.')

if sqlalchemy:

    Base = sqlalchemy.ext.declarative.declarative_base()
    class Alias(Base):
        __tablename__ = 'aliases'

        id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
        name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
        alias = sqlalchemy.Column(sqlalchemy.String, nullable=False)

        locked = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False)
        locked_by = sqlalchemy.Column(sqlalchemy.String, nullable=True)
        locked_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True)

        def __init__(self, name, alias):
            self.name = name
            self.alias = alias
            self.locked = False
            self.locked_by = None
            self.locked_at = None
        def __repr__(self):
            return "<Alias('%r', '%r')>" % (self.name, self.alias)

    # TODO: Add table for usage statistics

    class SqlAlchemyAkaDB(object):
        def __init__(self, filename):
            self.engines = ircutils.IrcDict()
            self.filename = filename
            self.sqlalchemy = sqlalchemy

        def close(self):
            self.dbs.clear()

        def get_db(self, channel):
            if channel in self.engines:
                engine = self.engines[channel]
            else:
                filename = plugins.makeChannelFilename(self.filename, channel)
                exists = os.path.exists(filename)
                engine = sqlalchemy.create_engine('sqlite:///' + filename)
                if not exists:
                    Base.metadata.create_all(engine)
                self.engines[channel] = engine
            assert engine.execute("select 1").scalar() == 1
            Session = sqlalchemy.orm.sessionmaker()
            Session.configure(bind=engine)
            return Session()


        def has_aka(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            count = self.get_db(channel).query(Alias) \
                    .filter(Alias.name == name) \
                    .count()
            return bool(count)
        def get_aka_list(self, channel):
            list_ = list(self.get_db(channel).query(Alias.name))
            return list_

        def get_alias(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            try:
                return self.get_db(channel).query(Alias.alias) \
                        .filter(Alias.name == name).one()[0]
            except sqlalchemy.orm.exc.NoResultFound:
                return None

        def add_aka(self, channel, name, alias):
            name = callbacks.canonicalName(name)
            if self.has_aka(channel, name):
                raise AkaError(_('This Aka already exists.'))
            if sys.version_info[0] < 3:
                if isinstance(name, str):
                    name = name.decode('utf8')
                if isinstance(alias, str):
                    alias = alias.decode('utf8')
            db = self.get_db(channel)
            db.add(Alias(name, alias))
            db.commit()

        def remove_aka(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            db.query(Alias).filter(Alias.name == name).delete()
            db.commit()

        def lock_aka(self, channel, name, by):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            try:
                aka = db.query(Alias) \
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))
            if aka.locked:
                raise AkaError(_('This Aka is already locked.'))
            aka.locked = True
            aka.locked_by = by
            aka.locked_at = datetime.datetime.now()
            db.commit()

        def unlock_aka(self, channel, name, by):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            try:
                aka = db.query(Alias) \
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))
            if not aka.locked:
                raise AkaError(_('This Aka is already unlocked.'))
            aka.locked = False
            aka.locked_by = by
            aka.locked_at = datetime.datetime.now()
            db.commit()

        def get_aka_lock(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            try:
                return self.get_db(channel) \
                        .query(Alias.locked, Alias.locked_by, Alias.locked_at)\
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))


def getArgs(args, required=1, optional=0, wildcard=0):
    if len(args) < required:
        raise callbacks.ArgumentError
    if len(args) < required + optional:
        ret = list(args) + ([''] * (required + optional - len(args)))
    elif len(args) >= required + optional:
        if not wildcard:
            ret = list(args[:required + optional - 1])
            ret.append(' '.join(args[required + optional - 1:]))
        else:
            ret = list(args)
    return ret

class AkaError(Exception):
    pass

class RecursiveAlias(AkaError):
    pass

dollarRe = re.compile(r'\$(\d+)')
def findBiggestDollar(alias):
    dollars = dollarRe.findall(alias)
    dollars = map(int, dollars)
    dollars.sort()
    if dollars:
        return dollars[-1]
    else:
        return 0

atRe = re.compile(r'@(\d+)')
def findBiggestAt(alias):
    ats = atRe.findall(alias)
    ats = map(int, ats)
    ats.sort()
    if ats:
        return ats[-1]
    else:
        return 0

AkaDB = plugins.DB('Aka', {'sqlalchemy': SqlAlchemyAkaDB})

class Aka(callbacks.Plugin):
    """Add the help for "@plugin help Aka" here
    This should describe *how* to use this plugin."""

    def __init__(self, irc):
        self.__parent = super(Aka, self)
        self.__parent.__init__(irc)
        self._db = AkaDB()

    def isCommandMethod(self, name):
        if sys.version_info[0] < 3 and isinstance(name, str):
            name = name.decode('utf8')
        args = name.split(' ')
        if len(args) > 1 and \
                callbacks.canonicalName(args[0]) != self.canonicalName():
            for cb in dynamic.irc.callbacks: # including this plugin
                if cb.getCommand(args[0:-1]):
                    return False
        channel = dynamic.channel or 'global'
        return self._db.has_aka(channel, name) or \
                self._db.has_aka('global', name) or \
                self.__parent.isCommandMethod(name)
    isCommand = isCommandMethod

    def listCommands(self):
        channel = dynamic.channel or 'global'
        return list(set(map(callbacks.formatCommand,
                            self._db.get_aka_list(channel) +
                            self._db.get_aka_list('global')) +
                ['add', 'remove', 'lock', 'unlock', 'importaliasdatabase']))

    def getCommand(self, args):
        canonicalName = callbacks.canonicalName
        # All the code from here to the 'for' loop is copied from callbacks.py
        assert args == map(canonicalName, args)
        first = args[0]
        for cb in self.cbs:
            if first == cb.canonicalName():
                return cb.getCommand(args)<commit_msg>Aka: Fix encoding issue with Python 2.<commit_after>###
# Copyright (c) 2013, <NAME>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

###

import re
import os
import sys
import datetime

import supybot.utils as utils
import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('Aka')

try:
    import sqlalchemy
    import sqlalchemy.ext
    import sqlalchemy.ext.declarative
except ImportError:
    raise callbacks.error('You have to install python-sqlalchemy in order '
            'to load this plugin.')

if sqlalchemy:

    Base = sqlalchemy.ext.declarative.declarative_base()
    class Alias(Base):
        __tablename__ = 'aliases'

        id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
        name = sqlalchemy.Column(sqlalchemy.String, unique=True, nullable=False)
        alias = sqlalchemy.Column(sqlalchemy.String, nullable=False)

        locked = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False)
        locked_by = sqlalchemy.Column(sqlalchemy.String, nullable=True)
        locked_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True)

        def __init__(self, name, alias):
            self.name = name
            self.alias = alias
            self.locked = False
            self.locked_by = None
            self.locked_at = None
        def __repr__(self):
            return "<Alias('%r', '%r')>" % (self.name, self.alias)

    # TODO: Add table for usage statistics

    class SqlAlchemyAkaDB(object):
        def __init__(self, filename):
            self.engines = ircutils.IrcDict()
            self.filename = filename
            self.sqlalchemy = sqlalchemy

        def close(self):
            self.dbs.clear()

        def get_db(self, channel):
            if channel in self.engines:
                engine = self.engines[channel]
            else:
                filename = plugins.makeChannelFilename(self.filename, channel)
                exists = os.path.exists(filename)
                engine = sqlalchemy.create_engine('sqlite:///' + filename)
                if not exists:
                    Base.metadata.create_all(engine)
                self.engines[channel] = engine
            assert engine.execute("select 1").scalar() == 1
            Session = sqlalchemy.orm.sessionmaker()
            Session.configure(bind=engine)
            return Session()


        def has_aka(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            count = self.get_db(channel).query(Alias) \
                    .filter(Alias.name == name) \
                    .count()
            return bool(count)
        def get_aka_list(self, channel):
            list_ = list(self.get_db(channel).query(Alias.name))
            return list_

        def get_alias(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            try:
                return self.get_db(channel).query(Alias.alias) \
                        .filter(Alias.name == name).one()[0]
            except sqlalchemy.orm.exc.NoResultFound:
                return None

        def add_aka(self, channel, name, alias):
            name = callbacks.canonicalName(name)
            if self.has_aka(channel, name):
                raise AkaError(_('This Aka already exists.'))
            if sys.version_info[0] < 3:
                if isinstance(name, str):
                    name = name.decode('utf8')
                if isinstance(alias, str):
                    alias = alias.decode('utf8')
            db = self.get_db(channel)
            db.add(Alias(name, alias))
            db.commit()

        def remove_aka(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            db.query(Alias).filter(Alias.name == name).delete()
            db.commit()

        def lock_aka(self, channel, name, by):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            try:
                aka = db.query(Alias) \
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))
            if aka.locked:
                raise AkaError(_('This Aka is already locked.'))
            aka.locked = True
            aka.locked_by = by
            aka.locked_at = datetime.datetime.now()
            db.commit()

        def unlock_aka(self, channel, name, by):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            db = self.get_db(channel)
            try:
                aka = db.query(Alias) \
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))
            if not aka.locked:
                raise AkaError(_('This Aka is already unlocked.'))
            aka.locked = False
            aka.locked_by = by
            aka.locked_at = datetime.datetime.now()
            db.commit()

        def get_aka_lock(self, channel, name):
            name = callbacks.canonicalName(name)
            if sys.version_info[0] < 3 and isinstance(name, str):
                name = name.decode('utf8')
            try:
                return self.get_db(channel) \
                        .query(Alias.locked, Alias.locked_by, Alias.locked_at)\
                        .filter(Alias.name == name).one()
            except sqlalchemy.orm.exc.NoResultFound:
                raise AkaError(_('This Aka does not exist'))


def getArgs(args, required=1, optional=0, wildcard=0):
    if len(args) < required:
        raise callbacks.ArgumentError
    if len(args) < required + optional:
        ret = list(args) + ([''] * (required + optional - len(args)))
    elif len(args) >= required + optional:
        if not wildcard:
            ret = list(args[:required + optional - 1])
            ret.append(' '.join(args[required + optional - 1:]))
        else:
            ret = list(args)
    return ret

class AkaError(Exception):
    pass

class RecursiveAlias(AkaError):
    pass

dollarRe = re.compile(r'\$(\d+)')
def findBiggestDollar(alias):
    dollars = dollarRe.findall(alias)
    dollars = map(int, dollars)
    dollars.sort()
    if dollars:
        return dollars[-1]
    else:
        return 0

atRe = re.compile(r'@(\d+)')
def findBiggestAt(alias):
    ats = atRe.findall(alias)
    ats = map(int, ats)
    ats.sort()
    if ats:
        return ats[-1]
    else:
        return 0

AkaDB = plugins.DB('Aka', {'sqlalchemy': SqlAlchemyAkaDB})

class Aka(callbacks.Plugin):
    """Add the help for "@plugin help Aka" here
    This should describe *how* to use this plugin."""

    def __init__(self, irc):
        self.__parent = super(Aka, self)
        self.__parent.__init__(irc)
        self._db = AkaDB()

    def isCommandMethod(self, name):
        args = name.split(' ')
        if len(args) > 1 and \
                callbacks.canonicalName(args[0]) != self.canonicalName():
            for cb in dynamic.irc.callbacks: # including this plugin
                if cb.getCommand(args[0:-1]):
                    return False
        if sys.version_info[0] < 3 and isinstance(name, str):
            name = name.decode('utf8')
        channel = dynamic.channel or 'global'
        return self._db.has_aka(channel, name) or \
                self._db.has_aka('global', name) or \
                self.__parent.isCommandMethod(name)
    isCommand = isCommandMethod

    def listCommands(self):
        channel = dynamic.channel or 'global'
        return list(set(map(callbacks.formatCommand,
                            self._db.get_aka_list(channel) +
                            self._db.get_aka_list('global')) +
                ['add', 'remove', 'lock', 'unlock', 'importaliasdatabase']))

    def getCommand(self, args):
        canonicalName = callbacks.canonicalName
        # All the code from here to the 'for' loop is copied from callbacks.py
        assert args == map(canonicalName, args)
        first = args[0]
        for cb in self.cbs:
            if first == cb.canonicalName():
                return cb.getCommand(args)
{'fraction_non_alphanumeric': 0.09946353487023343, 'fraction_numerical': 0.004688028611473587, 'mean_word_length': 2.1143889223359422, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5769204', 'n_tokens_mistral': 5994, 'n_tokens_neox': 5605, 'n_words': 1623}