<commit_before>#Copyright (c) 2014 David Harwood

#Licensed under the terms of the MIT license
#http://opensource.org/licenses/MIT

#Twisted imports
from twisted.internet import ssl, reactor, protocol
from twisted.words.protocols import irc

#system imports
import time, sys, argparse, re
from ConfigParser import RawConfigParser

class AchievementHandler:
    """
    The class that handles the actual achievements, who gets them, who doesn't, etc.
    """

    achievefile = 'achievements'
    userfile = 'users'
    admins = ''
    saynotice = 'grant_success'

    def __init__(self, config):
        for setting in config:
            setattr(self, setting[0], setting[1])
        self.achievestruct = self.read_achievements()
        self.userstruct = self.read_users()

    def command(self, user, channel, msg):
        """
        The function to parse and handle the command given to Achievebot
        """
        try:
            parse = msg.strip().split(None, 1)
            return getattr(self, parse[0])(user, channel, parse[1] if len(parse) > 1 else None)
        except:
            return (self._saypick('command_fail'), 'What?') #command_fail

    def _saypick(self, msg):
        """
        Determine the how Achievebot says something
        """
        if msg in self.saynotice:
            return 'notice'
        else:
            return 'msg'

    def read_achievements(self):
        """
        Read the achievements saved to the achievements file and put them in a dict of Achievement objects
        """
        #an achievement has several fields: name, description, restricted, permissions
        out = dict()
        with open(self.achievefile, 'r') as ach:
            for line in ach:
                out[line.partition(' : ')[0].lower()] = self._ach_make(*line.split(' : '))
        return out

    def read_users(self):
        """
        Read the user info from the users file and put them in a dict of lists of achievement names
        """
        out = dict()
        with open(self.userfile, 'r') as users:
            for line in users:
                parts = line.partition(' -> ')
                out[parts[0].lower()] = parts[2].strip().split(';')
        return out

    def write_achievements(self):
        """
        Write all of the Achievement objects out to file
        """
        out = []
        for name in self.achievestruct.keys():
            out.append(' : '.join((name.name, name.description, name.restricted, name.perms)))
        with open(self.achievefile, 'w') as ach:
            ach.write('\n'.join(out))

    def write_users(self):
        """
        Write all of the user information out to file
        """
        with open(self.userfile, 'w') as users:
            users.write('\n'.join([' -> '.join((name, ';'.join(self.userstruct[name]))) for name in self.userstruct.keys()]))

    def append_achievement(self, achievement):
        """
        Append a new achievement to the achievements file
        """
        with open(self.achievefile, 'a') as ach:
            ach.write(' : '.join((achievement.name, achievement.description, achievement.restricted, achievement.perms)) + '\n')

    def _ach_make(self, name, description, restricted='False', perms=''):
        """
        Create and return a new Achievement object
        """
        class Achievement:
            pass
        ach = Achievement()
        ach.name = name.strip()
        ach.description = description.strip()
        ach.restricted = restricted.strip()
        ach.perms = perms.strip()
        return ach

    def grant(self, granter, channel, grant_block):
        """
        Grant an achievement to a user
        """
        user, achievement = grant_block.split(None, 1)
        if achievement.lower() in self.achievestruct:
            if achievement.lower() in self.userstruct[user.lower()]:
                return (self._saypick('grant_earned'), 'Achievement already earned!') #grant_earned
            if self.achievestruct[achievement.lower()].restricted in ['True', 'true', 'yes', 'restricted']:
                perms = self.achievestruct[achievement.lower()].perms.split()
                if granter in self.admins or granter in perms:
                    return self._grant(user, self.achievestruct[achievement.lower()].name)
                else:
                    return (self._saypick('grant_permissions'), 'This achievement is restricted.') #grant_permissions
            else:
                return self._grant(user, self.achievestruct[achievement.lower()].name)
        else:
            return (self._saypick('grant_notfound'), 'Achievement not found!') #grant_notfound

    def _grant(self, user, achievement):
        if user not in self.userstruct.keys():
            self.userstruct[user.lower()] = []
        self.userstruct[user.lower()].append(achievement)
        self.write_users()
        return (self._saypick('grant_success'), 'Achievement unlocked! %s has earned the achievement %s!' % (user, achievement)) #grant_success

    def earned(self, asker, channel, user):
        """
        Display the achievements a user has earned
        """
        earned = ', '.join(self.userstruct[user.lower()])
        return (self._saypick('earned'), 'User %s has earned %s' % (user, earned)) #earned

    def add(self, adder, channel, achieve_block):
        """
        Add a new achievement
        """
        parts = achieve_block.split(' : ')
        if len(parts) < 2:
            return (self._saypick('add_nodesc'), 'Achievement not added: I need a name and a description') #add_nodesc
        if parts[0].lower() in self.achievestruct:
            return (self._saypick('add_exists'), 'Achievement not added: Achievement with that name already exists!') #add_exists
        ach = self._ach_make(*parts)
        if adder not in self.admins and ach.restricted in ['True', 'true', 'restricted', 'yes']:
            return (self._saypick('add_perms'), 'You must be an admin to add a restricted achievement') #adminadd_perms
        self.achievestruct[parts[0].lower()] = ach
        self.append_achievement(self.achievestruct[parts[0].lower()])
        return (self._saypick('add_success'), 'Added new achievement: %s' % (parts[0])) #add_success

    def listachieve(self, *args):
        """
        List all available achievements
        """
        achievements = ', '.join([self.achievestruct[ach].name for ach in self.achievestruct.keys()])
        return (self._saypick('listachieve'), 'List of achievements: %s' % (achievements)) #listachieve

    def info(self, asker, channel, achievement):
        """
        Display more information on one achievement
        """
        if achievement.lower() not in self.achievestruct:
            return (self._saypick('info_notfound'), 'Achievement not found!') #info_notfound
        ach = self.achievestruct[achievement.lower()]
        if asker in self.admins and ach.restricted in ['True', 'true', 'yes', 'restricted']:
            return (self._saypick('info_perms'), '%s: %s (restricted, allowed users: %s)' % (ach.name, ach.description, ach.perms)) #info_perms
        else:
            return (self._saypick('info_success'), '%s: %s' % (ach.name, ach.description)) #info_success

    def help(self, *args):
        """
        Display the help for Achievebot
        """
        script = ['I am Achievebot, made to track IRC achievements',
                'Commands:',
                'grant <user> <achievement> -> Grant achievement to user',
                'ungrant <user> <achievement> -> Remove an achievement from a user',
                'earned <user> -> Display all of the achievements the user has earned',
                'listachieve -> List all available achievements',
                'add <name> : <description> -> Add a new achievement to the system',
                'info <achievement> -> Show the full block of info on the specified achievement',
                'help -> Display this help',
                'join <channel> -> Join the specified channel',
                'leave <channel> -> Leave the specified channel',
                'quit -> Quit IRC',
                'More information and source code can be found at https://github.com/dharwood/Achievebot']
        return (self._saypick('help'), '\n'.join(script)) #help

    def ungrant(self, asker, channel, ungrant_block):
        """
        Remove a granted achievement from a user
        """
        user, achievement = ungrant_block.split(None, 1)
        if achievement.lower() not in map(str.lower, self.userstruct[user.lower()]):
            return (self._saypick('ungrant_unearned'), 'User %s has not earned that achievement' % user) #ungrant_unearned
        self.userstruct[user.lower()].remove(self.achievestruct[achievement.lower()].name)
        self.write_users()
        return (self._saypick('ungrant_success'), "Achievement %s has been removed from %s" % (self.achievestruct[achievement.lower()].name, user)) #ungrant_success

    def reload(self, config):
        """
        Reload the configuration settings and reread the achievements and users files
        """
        for setting in config:
            setattr(self, setting[0], setting[1])
        self.achievestruct = self.read_achievements()
        self.userstruct = self.read_users()

class AchieveBot(irc.IRCClient):
    """
    An IRC bot to grant and keep track of achievements users earn.
    """

    nickname = "achievebot"
    lineRate = 0.2
    nickpass = None
    channels = ''

    def __init__(self, ircopts):
        for setting in ircopts:
            setattr(self, setting[0], setting[1])

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.achieve = AchievementHandler(self.factory.appopts)
        for chan in self.channels.split(','):
            if chan == '':
                continue
            self.join(*chan.split())

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)

    def privmsg(self, user, channel, msg):
        user = user.split('!', 1)[0]
        if channel == self.nickname:
            self.command(user, user, msg)
        elif msg.startswith(self.nickname):
            self.command(user, channel, msg.split(None, 1)[1])

    def command(self, user, channel, msg):
        if msg.startswith("quit"):
            if user in self.achieve.admins:
                self.quit(message="I have been told to leave")
            else:
                self.msg(channel, "%s: No. And you can't make me." % (user))
        elif msg.startswith("join"):
            if user in self.achieve.admins:
                parts = msg.split()
                if len(parts) > 2:
                    self.join(parts[1], key=parts[2])
                else:
                    self.join(parts[1])
            else:
                self.msg(channel, 'No.')
        elif msg.startswith("leave"):
            if user in self.achieve.admins:
                self.leave(msg.split()[1], reason="I've been told to part")
            else:
                self.msg(channel, 'Not gonna happen')
        elif msg.startswith('reload'):
            if user in self.achieve.admins:
                self.reload()
                self.msg(channel, 'Reload complete')
            else:
                self.msg(channel, 'I change myself only for admins')
        else:
            vol, output = self.achieve.command(user, channel, msg)
            getattr(self, vol)(channel, output)

    def reload(self):
        self.factory.reload()
        for setting in self.factory.ircopts:
            if setting[0] == 'nickname' and setting[1] != self.nickname:
                self.setNick(setting[1])
            setattr(self, setting[0], setting[1])
        self.achieve.reload(self.factory.appopts)

class AchieveBotFactory(protocol.ClientFactory):
    """
    A factory for AchieveBots.
    """

    def __init__(self, config, ircopts, appopts):
        self.config = config
        self.ircopts = ircopts
        self.appopts = appopts

    def buildProtocol(self, addr):
        p = AchieveBot(self.ircopts)
        p.factory = self
        return p

    def clientConnectionLost(self, connector, reason):
        reactor.stop()

    def clientConnectionFailed(self, connector, reason):
        reactor.stop()

    def reload(self):
        conf = RawConfigParser()
        conf.read(self.config)
        self.ircopts = conf.items('IRC Options')
        self.appopts = conf.items('Achievement Options')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Bot for IRC achievements')
    parser.add_argument('-c', '--config', default='abot.conf', metavar='FILE', help='Config file to use. If it is missing, a default configuration file will be generated at the same path.')
    parser.add_argument('-s', '--server', help='Server to connect to (overrides config file)')
    parser.add_argument('-p', '--port', type=int, help='Port to connect to (overrides config file)')
    parser.add_argument('--ssl', action='store_true', help='Connect using SSL (overrides config file)')
    args = parser.parse_args()

    conf = RawConfigParser()
    try:
        conf.read(args.config)
        serv = conf.get('Connection', 'server')
        port = conf.getint('Connection', 'port')
        usessl = conf.getboolean('Connection', 'usessl')
    except:
        print("Configuration file can't be read. Generating.")
        conf.add_section('Connection')
        conf.set('Connection', 'server', 'INSERT VALUE HERE')
        conf.set('Connection', 'port', '6667')
        conf.set('Connection', 'usessl', 'no')
        conf.add_section('IRC Options')
        conf.add_section('Achievement Options')
        conf.write(open(args.config, 'wb'))
        print("Add IRC server address before using.")
        sys.exit()

    if args.server is not None:
        serv = args.server
    if args.port is not None:
        port = args.port
    if args.ssl:
        usessl = True

    f = AchieveBotFactory(args.config, conf.items('IRC Options'), conf.items('Achievement Options'))
    if usessl:
        reactor.connectSSL(serv, port, f, ssl.CertificateOptions())
    else:
        reactor.connectTCP(serv, port, f)
    reactor.run()

<commit_msg>Regressed to repeating achievements<commit_after>#Copyright (c) 2014 David Harwood

#Licensed under the terms of the MIT license
#http://opensource.org/licenses/MIT

#Twisted imports
from twisted.internet import ssl, reactor, protocol
from twisted.words.protocols import irc

#system imports
import time, sys, argparse, re
from ConfigParser import RawConfigParser

class AchievementHandler:
    """
    The class that handles the actual achievements, who gets them, who doesn't, etc.
    """

    achievefile = 'achievements'
    userfile = 'users'
    admins = ''
    saynotice = 'grant_success'

    def __init__(self, config):
        for setting in config:
            setattr(self, setting[0], setting[1])
        self.achievestruct = self.read_achievements()
        self.userstruct = self.read_users()

    def command(self, user, channel, msg):
        """
        The function to parse and handle the command given to Achievebot
        """
        try:
            parse = msg.strip().split(None, 1)
            return getattr(self, parse[0])(user, channel, parse[1] if len(parse) > 1 else None)
        except:
            return (self._saypick('command_fail'), 'What?') #command_fail

    def _saypick(self, msg):
        """
        Determine the how Achievebot says something
        """
        if msg in self.saynotice:
            return 'notice'
        else:
            return 'msg'

    def read_achievements(self):
        """
        Read the achievements saved to the achievements file and put them in a dict of Achievement objects
        """
        #an achievement has several fields: name, description, restricted, permissions
        out = dict()
        with open(self.achievefile, 'r') as ach:
            for line in ach:
                out[line.partition(' : ')[0].lower()] = self._ach_make(*line.split(' : '))
        return out

    def read_users(self):
        """
        Read the user info from the users file and put them in a dict of lists of achievement names
        """
        out = dict()
        with open(self.userfile, 'r') as users:
            for line in users:
                parts = line.partition(' -> ')
                out[parts[0].lower()] = parts[2].strip().split(';')
        return out

    def write_achievements(self):
        """
        Write all of the Achievement objects out to file
        """
        out = []
        for name in self.achievestruct.keys():
            out.append(' : '.join((name.name, name.description, name.restricted, name.perms)))
        with open(self.achievefile, 'w') as ach:
            ach.write('\n'.join(out))

    def write_users(self):
        """
        Write all of the user information out to file
        """
        with open(self.userfile, 'w') as users:
            users.write('\n'.join([' -> '.join((name, ';'.join(self.userstruct[name.lower()]))) for name in self.userstruct.keys()]))

    def append_achievement(self, achievement):
        """
        Append a new achievement to the achievements file
        """
        with open(self.achievefile, 'a') as ach:
            ach.write(' : '.join((achievement.name, achievement.description, achievement.restricted, achievement.perms)) + '\n')

    def _ach_make(self, name, description, restricted='False', perms=''):
        """
        Create and return a new Achievement object
        """
        class Achievement:
            pass
        ach = Achievement()
        ach.name = name.strip()
        ach.description = description.strip()
        ach.restricted = restricted.strip()
        ach.perms = perms.strip()
        return ach

    def grant(self, granter, channel, grant_block):
        """
        Grant an achievement to a user
        """
        user, achievement = grant_block.split(None, 1)
        if achievement.lower() in self.achievestruct:
            if self.achievestruct[achievement.lower()].name in self.userstruct[user.lower()]:
                return (self._saypick('grant_earned'), 'Achievement already earned!') #grant_earned
            if self.achievestruct[achievement.lower()].restricted in ['True', 'true', 'yes', 'restricted']:
                perms = self.achievestruct[achievement.lower()].perms.split()
                if granter in self.admins or granter in perms:
                    return self._grant(user, self.achievestruct[achievement.lower()].name)
                else:
                    return (self._saypick('grant_permissions'), 'This achievement is restricted.') #grant_permissions
            else:
                return self._grant(user, self.achievestruct[achievement.lower()].name)
        else:
            return (self._saypick('grant_notfound'), 'Achievement not found!') #grant_notfound

    def _grant(self, user, achievement):
        if user not in self.userstruct.keys():
            self.userstruct[user.lower()] = []
        self.userstruct[user.lower()].append(achievement)
        self.write_users()
        return (self._saypick('grant_success'), 'Achievement unlocked! %s has earned the achievement %s!' % (user, achievement)) #grant_success

    def earned(self, asker, channel, user):
        """
        Display the achievements a user has earned
        """
        earned = ', '.join(self.userstruct[user.lower()])
        return (self._saypick('earned'), 'User %s has earned %s' % (user, earned)) #earned

    def add(self, adder, channel, achieve_block):
        """
        Add a new achievement
        """
        parts = achieve_block.split(' : ')
        if len(parts) < 2:
            return (self._saypick('add_nodesc'), 'Achievement not added: I need a name and a description') #add_nodesc
        if parts[0].lower() in self.achievestruct:
            return (self._saypick('add_exists'), 'Achievement not added: Achievement with that name already exists!') #add_exists
        ach = self._ach_make(*parts)
        if adder not in self.admins and ach.restricted in ['True', 'true', 'restricted', 'yes']:
            return (self._saypick('add_perms'), 'You must be an admin to add a restricted achievement') #adminadd_perms
        self.achievestruct[parts[0].lower()] = ach
        self.append_achievement(self.achievestruct[parts[0].lower()])
        return (self._saypick('add_success'), 'Added new achievement: %s' % (parts[0])) #add_success

    def listachieve(self, *args):
        """
        List all available achievements
        """
        achievements = ', '.join([self.achievestruct[ach].name for ach in self.achievestruct.keys()])
        return (self._saypick('listachieve'), 'List of achievements: %s' % (achievements)) #listachieve

    def info(self, asker, channel, achievement):
        """
        Display more information on one achievement
        """
        if achievement.lower() not in self.achievestruct:
            return (self._saypick('info_notfound'), 'Achievement not found!') #info_notfound
        ach = self.achievestruct[achievement.lower()]
        if asker in self.admins and ach.restricted in ['True', 'true', 'yes', 'restricted']:
            return (self._saypick('info_perms'), '%s: %s (restricted, allowed users: %s)' % (ach.name, ach.description, ach.perms)) #info_perms
        else:
            return (self._saypick('info_success'), '%s: %s' % (ach.name, ach.description)) #info_success

    def help(self, *args):
        """
        Display the help for Achievebot
        """
        script = ['I am Achievebot, made to track IRC achievements',
                'Commands:',
                'grant <user> <achievement> -> Grant achievement to user',
                'ungrant <user> <achievement> -> Remove an achievement from a user',
                'earned <user> -> Display all of the achievements the user has earned',
                'listachieve -> List all available achievements',
                'add <name> : <description> -> Add a new achievement to the system',
                'info <achievement> -> Show the full block of info on the specified achievement',
                'help -> Display this help',
                'join <channel> -> Join the specified channel',
                'leave <channel> -> Leave the specified channel',
                'quit -> Quit IRC',
                'More information and source code can be found at https://github.com/dharwood/Achievebot']
        return (self._saypick('help'), '\n'.join(script)) #help

    def ungrant(self, asker, channel, ungrant_block):
        """
        Remove a granted achievement from a user
        """
        user, achievement = ungrant_block.split(None, 1)
        if achievement.lower() not in map(str.lower, self.userstruct[user.lower()]):
            return (self._saypick('ungrant_unearned'), 'User %s has not earned that achievement' % user) #ungrant_unearned
        self.userstruct[user.lower()].remove(self.achievestruct[achievement.lower()].name)
        self.write_users()
        return (self._saypick('ungrant_success'), "Achievement %s has been removed from %s" % (self.achievestruct[achievement.lower()].name, user)) #ungrant_success

    def reload(self, config):
        """
        Reload the configuration settings and reread the achievements and users files
        """
        for setting in config:
            setattr(self, setting[0], setting[1])
        self.achievestruct = self.read_achievements()
        self.userstruct = self.read_users()

class AchieveBot(irc.IRCClient):
    """
    An IRC bot to grant and keep track of achievements users earn.
    """

    nickname = "achievebot"
    lineRate = 0.2
    nickpass = None
    channels = ''

    def __init__(self, ircopts):
        for setting in ircopts:
            setattr(self, setting[0], setting[1])

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.achieve = AchievementHandler(self.factory.appopts)
        for chan in self.channels.split(','):
            if chan == '':
                continue
            self.join(*chan.split())

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)

    def privmsg(self, user, channel, msg):
        user = user.split('!', 1)[0]
        if channel == self.nickname:
            self.command(user, user, msg)
        elif msg.startswith(self.nickname):
            self.command(user, channel, msg.split(None, 1)[1])

    def command(self, user, channel, msg):
        if msg.startswith("quit"):
            if user in self.achieve.admins:
                self.quit(message="I have been told to leave")
            else:
                self.msg(channel, "%s: No. And you can't make me." % (user))
        elif msg.startswith("join"):
            if user in self.achieve.admins:
                parts = msg.split()
                if len(parts) > 2:
                    self.join(parts[1], key=parts[2])
                else:
                    self.join(parts[1])
            else:
                self.msg(channel, 'No.')
        elif msg.startswith("leave"):
            if user in self.achieve.admins:
                self.leave(msg.split()[1], reason="I've been told to part")
            else:
                self.msg(channel, 'Not gonna happen')
        elif msg.startswith('reload'):
            if user in self.achieve.admins:
                self.reload()
                self.msg(channel, 'Reload complete')
            else:
                self.msg(channel, 'I change myself only for admins')
        else:
            vol, output = self.achieve.command(user, channel, msg)
            getattr(self, vol)(channel, output)

    def reload(self):
        self.factory.reload()
        for setting in self.factory.ircopts:
            if setting[0] == 'nickname' and setting[1] != self.nickname:
                self.setNick(setting[1])
            setattr(self, setting[0], setting[1])
        self.achieve.reload(self.factory.appopts)

class AchieveBotFactory(protocol.ClientFactory):
    """
    A factory for AchieveBots.
    """

    def __init__(self, config, ircopts, appopts):
        self.config = config
        self.ircopts = ircopts
        self.appopts = appopts

    def buildProtocol(self, addr):
        p = AchieveBot(self.ircopts)
        p.factory = self
        return p

    def clientConnectionLost(self, connector, reason):
        reactor.stop()

    def clientConnectionFailed(self, connector, reason):
        reactor.stop()

    def reload(self):
        conf = RawConfigParser()
        conf.read(self.config)
        self.ircopts = conf.items('IRC Options')
        self.appopts = conf.items('Achievement Options')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Bot for IRC achievements')
    parser.add_argument('-c', '--config', default='abot.conf', metavar='FILE', help='Config file to use. If it is missing, a default configuration file will be generated at the same path.')
    parser.add_argument('-s', '--server', help='Server to connect to (overrides config file)')
    parser.add_argument('-p', '--port', type=int, help='Port to connect to (overrides config file)')
    parser.add_argument('--ssl', action='store_true', help='Connect using SSL (overrides config file)')
    args = parser.parse_args()

    conf = RawConfigParser()
    try:
        conf.read(args.config)
        serv = conf.get('Connection', 'server')
        port = conf.getint('Connection', 'port')
        usessl = conf.getboolean('Connection', 'usessl')
    except:
        print("Configuration file can't be read. Generating.")
        conf.add_section('Connection')
        conf.set('Connection', 'server', 'INSERT VALUE HERE')
        conf.set('Connection', 'port', '6667')
        conf.set('Connection', 'usessl', 'no')
        conf.add_section('IRC Options')
        conf.add_section('Achievement Options')
        conf.write(open(args.config, 'wb'))
        print("Add IRC server address before using.")
        sys.exit()

    if args.server is not None:
        serv = args.server
    if args.port is not None:
        port = args.port
    if args.ssl:
        usessl = True

    f = AchieveBotFactory(args.config, conf.items('IRC Options'), conf.items('Achievement Options'))
    if usessl:
        reactor.connectSSL(serv, port, f, ssl.CertificateOptions())
    else:
        reactor.connectTCP(serv, port, f)
    reactor.run()

<|endoftext|>


<commit_before>from ppp_datamodel import Intersection, First, Last

superlativeNoun = {
    # associate relevant predicate to each uperlative
    'biggest'   : 'size',
    'broadest'  : 'width',
    'coldest'   : 'temperature',
    'deepest'   : 'depth',
    'densest'   : 'density',
    'furthest'  : 'distance',
    'farthest'  : 'distance',
    'fattest'   : 'weight',
    'hardest'   : 'difficulty',
    'heaviest'  : 'weight',
    'highest'   : 'height',
    'hottest'   : 'temperature',
    'largest'   : 'width',
    'longest'   : 'length',
    'loudest'   : 'sound',
    'lowest'    : 'height',
    'noisiest'  : 'sound',
    'oldest'    : 'age',
    'eldest'    : 'age',
    'quickest'  : 'speed',
    'shortest'  : 'length',
    'slimmest'  : 'weight',
    'slowest'   : 'speed',
    'strongest' : 'strength',
    'thickest'  : 'thickness',
    'warmest'   : 'temperature',
    'wettest'   : 'humidity',
    'widest'    : 'width',
    'youngest'  : 'age',
    #
    'most recent'    : 'date',
    'most expensive' : 'cost'
}

superlativeOrder = {
    # associate sorting order to each uperlative
    'first'     : First,
    'last'      : Last,
    'biggest'   : Last,
    'broadest'  : Last,
    'coldest'   : First,
    'deepest'   : Last,
    'densest'   : Last,
    'furthest'  : Last,
    'farthest'  : Last,
    'fattest'   : Last,
    'hardest'   : Last,
    'heaviest'  : Last,
    'highest'   : Last,
    'hottest'   : Last,
    'largest'   : Last,
    'longest'   : Last,
    'loudest'   : Last,
    'lowest'    : First,
    'noisiest'  : Last,
    'oldest'    : Last,
    'eldest'    : Last,
    'quickest'  : Last,
    'shortest'  : First,
    'slimmest'  : First,
    'slowest'   : First,
    'strongest' : Last,
    'thickest'  : Last,
    'warmest'   : Last,
    'wettest'   : Last,
    'widest'    : Last,
    'youngest'  : First,
    #
    'most recent'    : Last,
    'most expensive' : Last
}
<commit_msg>Add superlatives.<commit_after>from ppp_datamodel import Intersection, First, Last

superlativeNoun = {
    # associate relevant predicate to each uperlative
    'biggest'   : 'size',
    'broadest'  : 'width',
    'coldest'   : 'temperature',
    'deepest'   : 'depth',
    'densest'   : 'density',
    'furthest'  : 'distance',
    'farthest'  : 'distance',
    'fattest'   : 'weight',
    'hardest'   : 'difficulty',
    'heaviest'  : 'weight',
    'highest'   : 'height',
    'hottest'   : 'temperature',
    'largest'   : 'width',
    'longest'   : 'length',
    'loudest'   : 'sound',
    'lowest'    : 'height',
    'noisiest'  : 'sound',
    'oldest'    : 'age',
    'eldest'    : 'age',
    'quickest'  : 'speed',
    'shortest'  : 'length',
    'slimmest'  : 'weight',
    'slowest'   : 'speed',
    'strongest' : 'strength',
    'thickest'  : 'thickness',
    'warmest'   : 'temperature',
    'wettest'   : 'humidity',
    'widest'    : 'width',
    'youngest'  : 'age',
    'cheapest'  : 'cost',
    #
    'most recent'    : 'date',
    'least recent'   : 'date',
    'most expensive' : 'cost',
    'least expensive': 'cost',
}

superlativeOrder = {
    # associate sorting order to each uperlative
    'first'     : First,
    'last'      : Last,
    'biggest'   : Last,
    'broadest'  : Last,
    'coldest'   : First,
    'deepest'   : Last,
    'densest'   : Last,
    'furthest'  : Last,
    'farthest'  : Last,
    'fattest'   : Last,
    'hardest'   : Last,
    'heaviest'  : Last,
    'highest'   : Last,
    'hottest'   : Last,
    'largest'   : Last,
    'longest'   : Last,
    'loudest'   : Last,
    'lowest'    : First,
    'noisiest'  : Last,
    'oldest'    : Last,
    'eldest'    : Last,
    'quickest'  : Last,
    'shortest'  : First,
    'slimmest'  : First,
    'slowest'   : First,
    'strongest' : Last,
    'thickest'  : Last,
    'warmest'   : Last,
    'wettest'   : Last,
    'widest'    : Last,
    'youngest'  : First,
    'cheapest'  : First,
    #
    'most recent'    : Last,
    'least recent'   : First,
    'most expensive' : Last,
    'least expensive': First,
}
<|endoftext|>


<commit_before>from teafacto.blocks.rnn import MakeRNU
from teafacto.blocks.attention import Attention, LinearGateAttentionGenerator, WeightedSumAttCon
from teafacto.blocks.basic import MatDot as Lin, Softmax
from teafacto.blocks.basic import VectorEmbed, IdxToOneHot, MatDot
from teafacto.blocks.rnn import RecStack, SeqDecoder, BiRNU, SeqEncoder, MaskSetMode, MaskMode
from teafacto.blocks.rnu import GRU
from teafacto.core.base import Block, tensorops as T, Val
from teafacto.core.stack import stack
from teafacto.blocks.memory import MemoryStack, MemoryBlock, DotMemAddr, GeneralDotMemAddr, LinearGateMemAddr
from teafacto.util import issequence

class SeqEncDec(Block):
    def __init__(self, enc, dec, statetrans=None, **kw):
        super(SeqEncDec, self).__init__(**kw)
        self.enc = enc
        self.dec = dec
        if isinstance(statetrans, Block):
            self.statetrans = lambda x, y: statetrans(x)
        elif statetrans is True:
            self.statetrans = lambda x, y: x
        else:
            self.statetrans = statetrans

    def apply(self, inpseq, outseq, maskseq=None):
        if maskseq is None:
            mask = "auto"
        else:
            mask = maskseq
        enco, allenco = self.enc(inpseq, mask=mask)
        mask = None
        if self.statetrans is not None:
            topstate = self.statetrans(enco, allenco)
            deco = self.dec(allenco, outseq, initstates=[topstate], mask=mask)
        else:
            deco = self.dec(allenco, outseq, mask=mask)      # no state transfer
        return deco

    def get_init_info(self, inpseq, batsize, maskseq=None):     # TODO: must evaluate enc here, in place, without any side effects
        enco, allenco = self.enc.predict(inpseq, mask=maskseq)
        if self.statetrans is not None:
            topstate = self.statetrans.predict(enco, allenco)
            initstates = [topstate]
        else:
            initstates = batsize
        return self.dec.get_init_info(Val(allenco), None, Val(initstates))

    def rec(self, x_t, *states):
        return self.dec.rec(x_t, *states)

class SeqEncDecAtt(SeqEncDec):
    def __init__(self, enclayers, declayers, attgen, attcon, decinnerdim, inconcat, outconcat, statetrans=None, **kw):
        enc = SeqEncoder(*enclayers).with_outputs.maskoption(MaskSetMode.ZERO)
        dec = SeqDecoder(
            declayers,
            attention=Attention(attgen, attcon),
            innerdim=decinnerdim,
            outconcat=outconcat,
            inconcat=inconcat
        )
        super(SeqEncDecAtt, self).__init__(enc, dec, statetrans=statetrans, **kw)

class SimpleSeqEncDecAtt(SeqEncDecAtt):
    def __init__(self,
                 inpvocsize=400,
                 inpembdim=None,
                 outvocsize=100,
                 outembdim=None,
                 encdim=100,
                 decdim=100,
                 attdim=100,
                 bidir=False,
                 rnu=GRU,
                 outconcat=True,
                 inconcat=False,
                 statetrans=None,
                 **kw):
        encinnerdim = [encdim] if not issequence(encdim) else encdim
        decinnerdim = [decdim] if not issequence(decdim) else decdim

        # encoder stack
        if inpembdim is None:
            inpemb = IdxToOneHot(inpvocsize)
            inpembdim = inpvocsize
        else:
            inpemb = VectorEmbed(indim=inpvocsize, dim=inpembdim)
        encrnus = []
        dims = [inpembdim] + encinnerdim
        i = 1
        lastencinnerdim = dims[-1] if not bidir else dims[-1]*2
        while i < len(dims):
            if bidir:
                newrnu = BiRNU.fromrnu(rnu, dim=dims[i-1], innerdim=dims[i])
            else:
                newrnu = rnu(dim=dims[i-1], innerdim=dims[i])
            encrnus.append(newrnu)
            i += 1
        enclayers = [inpemb] + encrnus

        # attention
        lastdecinnerdim = decinnerdim[-1]
        attgen = LinearGateAttentionGenerator(indim=lastencinnerdim + lastdecinnerdim, attdim=attdim)
        attcon = WeightedSumAttCon()

        # decoder
        if outembdim is None:
            outemb = IdxToOneHot(outvocsize)
            outembdim = outvocsize
        else:
            outemb = VectorEmbed(indim=outvocsize, dim=outembdim)
        decrnus = []
        firstdecdim = outembdim if inconcat is False else outembdim + encinnerdim
        dims = [firstdecdim] + decinnerdim
        i = 1
        while i < len(dims):
            decrnus.append(rnu(dim=dims[i-1], innerdim=dims[i]))
            i += 1
        declayers = [outemb] + decrnus
        argdecinnerdim = lastdecinnerdim if outconcat is False else lastencinnerdim + lastdecinnerdim

        if statetrans is True:
            if lastencinnerdim != lastdecinnerdim:  # state shape mismatch
                statetrans = MatDot(lastencinnerdim, lastdecinnerdim)

        super(SimpleSeqEncDecAtt, self).__init__(enclayers, declayers, attgen, attcon, argdecinnerdim, inconcat, outconcat, statetrans=statetrans, **kw)

class SeqTransducer(Block):
    def __init__(self, embedder, *layers, **kw):
        """ layers must have an embedding layers first, final softmax layer is added automatically"""
        assert("smodim" in kw and "outdim" in kw)
        self.embedder = embedder
        smodim = kw["smodim"]
        outdim = kw["outdim"]
        del kw["smodim"]; del kw["outdim"]
        super(SeqTransducer, self).__init__(**kw)
        self.block = RecStack(*(layers + (Lin(indim=smodim, dim=outdim), Softmax())))

    def apply(self, inpseq, maskseq=None):    # inpseq: idx^(batsize, seqlen), maskseq: f32^(batsize, seqlen)
        embseq = self.embedder(inpseq)
        res = self.block(embseq, mask=maskseq)            # f32^(batsize, seqlen, outdim)
        ret = self.applymask(res, maskseq=maskseq)
        return ret

    @classmethod
    def applymask(cls, xseq, maskseq=None):
        if maskseq is None:
            ret = xseq
        else:
            mask = T.tensordot(maskseq, T.ones((xseq.shape[2],)), 0)  # f32^(batsize, seqlen, outdim) -- maskseq stacked
            masker = T.concatenate([T.ones((xseq.shape[0], xseq.shape[1], 1)), T.zeros((xseq.shape[0], xseq.shape[1], xseq.shape[2] - 1))], axis=2)  # f32^(batsize, seqlen, outdim) -- gives 100% prob to output 0
            ret = xseq * mask + masker * (1.0 - mask)
        return ret

class SimpleSeqTransducer(SeqTransducer):
    def __init__(self, indim=400, embdim=50, innerdim=100, outdim=50, **kw):
        self.emb = VectorEmbed(indim=indim, dim=embdim)
        if not issequence(innerdim):
            innerdim = [innerdim]
        innerdim = [embdim] + innerdim
        self.rnn = self.getrnnfrominnerdim(innerdim)
        super(SimpleSeqTransducer, self).__init__(self.emb, *self.rnn, smodim=innerdim[-1], outdim=outdim, **kw)

    @classmethod
    def getrnnfrominnerdim(self, innerdim, rnu=GRU):
        assert(len(innerdim) >= 2)
        initdim = innerdim[0]
        otherdim = innerdim[1:]
        return MakeRNU.make(initdim, otherdim, rnu=rnu)[0]

class SeqTransDec(Block):
    def __init__(self, *layers, **kw):
        """ first two layers must be embedding layers. Final softmax is added automatically"""
        assert("smodim" in kw and "outdim" in kw)
        smodim = kw["smodim"]
        outdim = kw["outdim"]
        del kw["smodim"]; del kw["outdim"]
        super(SeqTransDec, self).__init__(**kw)
        self.inpemb = layers[0]
        self.outemb = layers[1]
        self.block = RecStack(*(layers[2:] + (Lin(indim=smodim, dim=outdim), Softmax())))

    def apply(self, inpseq, outseq, maskseq=None):
        # embed with the two embedding layers
        emb = self._get_emb(inpseq, outseq)
        res = self.block(emb)
        ret = SeqTransducer.applymask(res, maskseq=maskseq)
        return ret

    def _get_emb(self, inpseq, outseq):
        iemb = self.inpemb(inpseq)     # (batsize, seqlen, inpembdim)
        oemb = self.outemb(outseq)     # (batsize, seqlen, outembdim)
        emb = T.concatenate([iemb, oemb], axis=iemb.ndim-1)                       # (batsize, seqlen, inpembdim+outembdim)
        return emb

    def rec(self, inpa, inpb, *states):
        emb = self._get_emb(inpa, inpb)
        return self.block.rec(emb, *states)

    def get_init_info(self, initstates):
        return self.block.get_init_info(initstates)

class SimpleSeqTransDec(SeqTransDec):
    def __init__(self, indim=400, outdim=50, inpembdim=50, outembdim=50, innerdim=100, **kw):
        self.inpemb = VectorEmbed(indim=indim, dim=inpembdim)
        self.outemb = VectorEmbed(indim=outdim, dim=outembdim)
        self.rnn = []
        if not issequence(innerdim):
            innerdim = [innerdim]
        innerdim = [inpembdim+outembdim] + innerdim
        self.rnn = SimpleSeqTransducer.getrnnfrominnerdim(innerdim)
        super(SimpleSeqTransDec, self).__init__(self.inpemb, self.outemb, *self.rnn, smodim=innerdim[-1], outdim=outdim, **kw)

# BASIC SEQ TO IDX
# specify by  enc and out
class Seq2Idx(Block):
    def __init__(self, seq2vec, vec2idx, **kw):
        self.enc = seq2vec
        self.out = vec2idx
        super(Seq2Idx, self).__init__(**kw)

    def apply(self, x, mask=None):         # x: idx^(batsize, seqlen)
        enco = self.enc(x, mask=mask)      # (batsize, innerdim)
        out = self.out(enco)    # (batsize, probs)
        return out

# specify by layers
class LayerSeq2Idx(Seq2Idx):
    def __init__(self, inpemb, enclayers, outlayers, maskid=0, **kw):
        enc = Seq2Vec(inpemb, enclayers, maskid)
        out = Vec2Idx(outlayers)
        super(LayerSeq2Idx, self).__init__(enc, out, **kw)

# specify by dims
class SimpleSeq2Idx(Seq2Idx):
    def __init__(self, indim=400, outdim=100, inpembdim=50, innerdim=100, maskid=0, bidir=False, **kw):
        enc = SimpleSeq2Vec(indim=indim, inpembdim=inpembdim, innerdim=innerdim, maskid=0, bidir=bidir)
        out = SimpleVec2Idx(indim=enc.outdim, outdim=outdim)
        super(SimpleSeq2Idx, self).__init__(enc, out, **kw)

# components:
# seq2vec
# specify by layers
class Seq2Vec(Block):
    def __init__(self, inpemb, enclayers, maskid=0, **kw):
        super(Seq2Vec, self).__init__(**kw)
        self.maskid = maskid
        if not issequence(enclayers):
            enclayers = [enclayers]
        self.enc = SeqEncoder(inpemb, *enclayers).maskoptions(maskid, MaskMode.AUTO)

    def apply(self, x, mask=None):
        ret = self.enc(x, mask=mask)
        ret.name = "seq2vec-out"
        return ret

# specify by dims
class SimpleSeq2Vec(Seq2Vec):
    def __init__(self, indim=400, inpembdim=50, innerdim=100, maskid=0, bidir=False, **kw):
        if inpembdim is None:
            inpemb = IdxToOneHot(indim)
            inpembdim = indim
        else:
            inpemb = VectorEmbed(indim=indim, dim=inpembdim)
        rnn, lastdim = self.makernu(inpembdim, innerdim, bidir=bidir)
        self.outdim = lastdim
        super(SimpleSeq2Vec, self).__init__(inpemb, rnn, maskid, **kw)

    @staticmethod
    def makernu(inpembdim, innerdim, bidir=False):
        return MakeRNU.make(inpembdim, innerdim, bidir=bidir)

# vec2idx:
# specify by layers
class Vec2Idx(Block):
    def __init__(self, outlayers, **kw):
        super(Vec2Idx, self).__init__(**kw)
        if isinstance(outlayers, MemoryStack):
            out = outlayers
        else:
            if not issequence(outlayers):
                outlayers = [outlayers]
            if type(outlayers[-1]) is not Softmax:
                outlayers.append(Softmax())
            out = stack(*outlayers)
        self.out = out

    def apply(self, x, *args):
        return self.out(x, *args)

# specify by dims
class SimpleVec2Idx(Vec2Idx):
    def __init__(self, indim=100, outdim=100, **kw):
        outl = MatDot(indim=indim, dim=outdim)
        super(SimpleVec2Idx, self).__init__(outl, **kw)

class MemVec2Idx(Vec2Idx):
    def __init__(self, memenc, memdata, memaddr=DotMemAddr, memdim=None, memattdim=100, **kw):
        assert(memenc is not None)
        memblock = MemoryBlock(memenc, memdata, indim=memdata.shape[0], outdim=memdim)
        memstack = MemoryStack(memblock, memaddr, memattdim=memattdim)
        super(MemVec2Idx, self).__init__(memstack, **kw)

class DynMemVec2Idx(MemVec2Idx):
    def __init__(self, memenc, memaddr=DotMemAddr, memdim=None, memattdim=100, **kw):
        super(self, DynMemVec2Idx).__init__(memenc, None, memaddr=memaddr, memdim=memdim, memattdim=memattdim, **kw)
<commit_msg>* refactor to OpBlock<commit_after>from teafacto.blocks.rnn import MakeRNU
from teafacto.blocks.attention import Attention, LinearGateAttentionGenerator, WeightedSumAttCon
from teafacto.blocks.basic import MatDot as Lin, Softmax
from teafacto.blocks.basic import VectorEmbed, IdxToOneHot, MatDot
from teafacto.blocks.rnn import RecStack, SeqDecoder, BiRNU, SeqEncoder, MaskSetMode, MaskMode
from teafacto.blocks.rnu import GRU
from teafacto.core.base import Block, tensorops as T, Val
from teafacto.core.stack import stack
from teafacto.blocks.memory import MemoryStack, MemoryBlock, DotMemAddr, GeneralDotMemAddr, LinearGateMemAddr
from teafacto.util import issequence

class SeqEncDec(Block):
    def __init__(self, enc, dec, statetrans=None, **kw):
        super(SeqEncDec, self).__init__(**kw)
        self.enc = enc
        self.dec = dec
        if isinstance(statetrans, Block):
            self.statetrans = lambda x, y: statetrans(x)
        elif statetrans is True:
            self.statetrans = lambda x, y: x
        else:
            self.statetrans = statetrans

    def apply(self, inpseq, outseq, maskseq=None):
        if maskseq is None:
            mask = "auto"
        else:
            mask = maskseq
        enco, allenco = self.enc(inpseq, mask=mask)
        mask = None
        if self.statetrans is not None:
            topstate = self.statetrans(enco, allenco)
            deco = self.dec(allenco, outseq, initstates=[topstate], mask=mask)
        else:
            deco = self.dec(allenco, outseq, mask=mask)      # no state transfer
        return deco

    def get_init_info(self, inpseq, batsize, maskseq=None):     # TODO: must evaluate enc here, in place, without any side effects
        enco, allenco = self.enc.predict(inpseq, mask=maskseq)
        if self.statetrans is not None:
            topstate = self.statetrans.predict(enco, allenco)
            initstates = [topstate]
        else:
            initstates = batsize
        return self.dec.get_init_info(Val(allenco), None, Val(initstates))

    def rec(self, x_t, *states):
        return self.dec.rec(x_t, *states)

class SeqEncDecAtt(SeqEncDec):
    def __init__(self, enclayers, declayers, attgen, attcon, decinnerdim, inconcat, outconcat, statetrans=None, **kw):
        enc = SeqEncoder(*enclayers).with_outputs.maskoption(MaskSetMode.ZERO)
        dec = SeqDecoder(
            declayers,
            attention=Attention(attgen, attcon),
            innerdim=decinnerdim,
            outconcat=outconcat,
            inconcat=inconcat
        )
        super(SeqEncDecAtt, self).__init__(enc, dec, statetrans=statetrans, **kw)

class SimpleSeqEncDecAtt(SeqEncDecAtt):
    def __init__(self,
                 inpvocsize=400,
                 inpembdim=None,
                 outvocsize=100,
                 outembdim=None,
                 encdim=100,
                 decdim=100,
                 attdim=100,
                 bidir=False,
                 rnu=GRU,
                 outconcat=True,
                 inconcat=False,
                 statetrans=None,
                 **kw):
        encinnerdim = [encdim] if not issequence(encdim) else encdim
        decinnerdim = [decdim] if not issequence(decdim) else decdim

        # encoder stack
        if inpembdim is None:
            inpemb = IdxToOneHot(inpvocsize)
            inpembdim = inpvocsize
        else:
            inpemb = VectorEmbed(indim=inpvocsize, dim=inpembdim)
        encrnus = []
        dims = [inpembdim] + encinnerdim
        i = 1
        lastencinnerdim = dims[-1] if not bidir else dims[-1]*2
        while i < len(dims):
            if bidir:
                newrnu = BiRNU.fromrnu(rnu, dim=dims[i-1], innerdim=dims[i])
            else:
                newrnu = rnu(dim=dims[i-1], innerdim=dims[i])
            encrnus.append(newrnu)
            i += 1
        enclayers = [inpemb] + encrnus

        # attention
        lastdecinnerdim = decinnerdim[-1]
        attgen = LinearGateAttentionGenerator(indim=lastencinnerdim + lastdecinnerdim, attdim=attdim)
        attcon = WeightedSumAttCon()

        # decoder
        if outembdim is None:
            outemb = IdxToOneHot(outvocsize)
            outembdim = outvocsize
        else:
            outemb = VectorEmbed(indim=outvocsize, dim=outembdim)
        decrnus = []
        firstdecdim = outembdim if inconcat is False else outembdim + encinnerdim
        dims = [firstdecdim] + decinnerdim
        i = 1
        while i < len(dims):
            decrnus.append(rnu(dim=dims[i-1], innerdim=dims[i]))
            i += 1
        declayers = [outemb] + decrnus
        argdecinnerdim = lastdecinnerdim if outconcat is False else lastencinnerdim + lastdecinnerdim

        if statetrans is True:
            if lastencinnerdim != lastdecinnerdim:  # state shape mismatch
                statetrans = MatDot(lastencinnerdim, lastdecinnerdim)

        super(SimpleSeqEncDecAtt, self).__init__(enclayers, declayers, attgen, attcon, argdecinnerdim, inconcat, outconcat, statetrans=statetrans, **kw)

class SeqTransducer(Block):
    def __init__(self, embedder, *layers, **kw):
        """ layers must have an embedding layers first, final softmax layer is added automatically"""
        assert("smodim" in kw and "outdim" in kw)
        self.embedder = embedder
        smodim = kw["smodim"]
        outdim = kw["outdim"]
        del kw["smodim"]; del kw["outdim"]
        super(SeqTransducer, self).__init__(**kw)
        self.block = RecStack(*(layers + (Lin(indim=smodim, dim=outdim), Softmax())))

    def apply(self, inpseq, maskseq=None):    # inpseq: idx^(batsize, seqlen), maskseq: f32^(batsize, seqlen)
        embseq = self.embedder(inpseq)
        res = self.block(embseq, mask=maskseq)            # f32^(batsize, seqlen, outdim)
        ret = self.applymask(res, maskseq=maskseq)
        return ret

    @classmethod
    def applymask(cls, xseq, maskseq=None):
        if maskseq is None:
            ret = xseq
        else:
            mask = T.tensordot(maskseq, T.ones((xseq.shape[2],)), 0)  # f32^(batsize, seqlen, outdim) -- maskseq stacked
            masker = T.concatenate([T.ones((xseq.shape[0], xseq.shape[1], 1)), T.zeros((xseq.shape[0], xseq.shape[1], xseq.shape[2] - 1))], axis=2)  # f32^(batsize, seqlen, outdim) -- gives 100% prob to output 0
            ret = xseq * mask + masker * (1.0 - mask)
        return ret

class SimpleSeqTransducer(SeqTransducer):
    def __init__(self, indim=400, embdim=50, innerdim=100, outdim=50, **kw):
        self.emb = VectorEmbed(indim=indim, dim=embdim)
        if not issequence(innerdim):
            innerdim = [innerdim]
        innerdim = [embdim] + innerdim
        self.rnn = self.getrnnfrominnerdim(innerdim)
        super(SimpleSeqTransducer, self).__init__(self.emb, *self.rnn, smodim=innerdim[-1], outdim=outdim, **kw)

    @classmethod
    def getrnnfrominnerdim(self, innerdim, rnu=GRU):
        assert(len(innerdim) >= 2)
        initdim = innerdim[0]
        otherdim = innerdim[1:]
        return MakeRNU.make(initdim, otherdim, rnu=rnu)[0]

class SeqTransDec(Block):
    def __init__(self, *layers, **kw):
        """ first two layers must be embedding layers. Final softmax is added automatically"""
        assert("smodim" in kw and "outdim" in kw)
        smodim = kw["smodim"]
        outdim = kw["outdim"]
        del kw["smodim"]; del kw["outdim"]
        super(SeqTransDec, self).__init__(**kw)
        self.inpemb = layers[0]
        self.outemb = layers[1]
        self.block = RecStack(*(layers[2:] + (Lin(indim=smodim, dim=outdim), Softmax())))

    def apply(self, inpseq, outseq, maskseq=None):
        # embed with the two embedding layers
        emb = self._get_emb(inpseq, outseq)
        res = self.block(emb)
        ret = SeqTransducer.applymask(res, maskseq=maskseq)
        return ret

    def _get_emb(self, inpseq, outseq):
        iemb = self.inpemb(inpseq)     # (batsize, seqlen, inpembdim)
        oemb = self.outemb(outseq)     # (batsize, seqlen, outembdim)
        emb = T.concatenate([iemb, oemb], axis=iemb.ndim-1)                       # (batsize, seqlen, inpembdim+outembdim)
        return emb

    def rec(self, inpa, inpb, *states):
        emb = self._get_emb(inpa, inpb)
        return self.block.rec(emb, *states)

    def get_init_info(self, initstates):
        return self.block.get_init_info(initstates)

class SimpleSeqTransDec(SeqTransDec):
    def __init__(self, indim=400, outdim=50, inpembdim=50, outembdim=50, innerdim=100, **kw):
        self.inpemb = VectorEmbed(indim=indim, dim=inpembdim)
        self.outemb = VectorEmbed(indim=outdim, dim=outembdim)
        self.rnn = []
        if not issequence(innerdim):
            innerdim = [innerdim]
        innerdim = [inpembdim+outembdim] + innerdim
        self.rnn = SimpleSeqTransducer.getrnnfrominnerdim(innerdim)
        super(SimpleSeqTransDec, self).__init__(self.inpemb, self.outemb, *self.rnn, smodim=innerdim[-1], outdim=outdim, **kw)

# BASIC SEQ TO IDX
# specify by  enc and out
class Seq2Idx(Block):
    def __init__(self, seq2vec, vec2idx, **kw):
        self.enc = seq2vec
        self.out = vec2idx
        super(Seq2Idx, self).__init__(**kw)

    def apply(self, x, mask=None):         # x: idx^(batsize, seqlen)
        enco = self.enc(x, mask=mask)      # (batsize, innerdim)
        out = self.out(enco)    # (batsize, probs)
        return out

# specify by layers
class LayerSeq2Idx(Seq2Idx):
    def __init__(self, inpemb, enclayers, outlayers, maskid=0, **kw):
        enc = Seq2Vec(inpemb, enclayers, maskid)
        out = Vec2Idx(outlayers)
        super(LayerSeq2Idx, self).__init__(enc, out, **kw)

# specify by dims
class SimpleSeq2Idx(Seq2Idx):
    def __init__(self, indim=400, outdim=100, inpembdim=50, innerdim=100, maskid=0, bidir=False, **kw):
        enc = SimpleSeq2Vec(indim=indim, inpembdim=inpembdim, innerdim=innerdim, maskid=0, bidir=bidir)
        out = SimpleVec2Idx(indim=enc.outdim, outdim=outdim)
        super(SimpleSeq2Idx, self).__init__(enc, out, **kw)

# components:
# seq2vec
# specify by layers
class Seq2Vec(Block):
    def __init__(self, inpemb, enclayers, maskid=0, **kw):
        super(Seq2Vec, self).__init__(**kw)
        self.maskid = maskid
        if not issequence(enclayers):
            enclayers = [enclayers]
        self.enc = SeqEncoder(inpemb, *enclayers).maskoptions(maskid, MaskMode.AUTO)

    def apply(self, x, mask=None):
        x.name = "seq2vec-inp"
        ret = self.enc(x, mask=mask)
        ret.name = "seq2vec-out"
        return ret

# specify by dims
class SimpleSeq2Vec(Seq2Vec):
    def __init__(self, indim=400, inpembdim=50, innerdim=100, maskid=0, bidir=False, **kw):
        if inpembdim is None:
            inpemb = IdxToOneHot(indim)
            inpembdim = indim
        else:
            inpemb = VectorEmbed(indim=indim, dim=inpembdim)
        rnn, lastdim = self.makernu(inpembdim, innerdim, bidir=bidir)
        self.outdim = lastdim
        super(SimpleSeq2Vec, self).__init__(inpemb, rnn, maskid, **kw)

    @staticmethod
    def makernu(inpembdim, innerdim, bidir=False):
        return MakeRNU.make(inpembdim, innerdim, bidir=bidir)

# vec2idx:
# specify by layers
class Vec2Idx(Block):
    def __init__(self, outlayers, **kw):
        super(Vec2Idx, self).__init__(**kw)
        if isinstance(outlayers, MemoryStack):
            out = outlayers
        else:
            if not issequence(outlayers):
                outlayers = [outlayers]
            if type(outlayers[-1]) is not Softmax:
                outlayers.append(Softmax())
            out = stack(*outlayers)
        self.out = out

    def apply(self, x, *args):
        return self.out(x, *args)

# specify by dims
class SimpleVec2Idx(Vec2Idx):
    def __init__(self, indim=100, outdim=100, **kw):
        outl = MatDot(indim=indim, dim=outdim)
        super(SimpleVec2Idx, self).__init__(outl, **kw)

class MemVec2Idx(Vec2Idx):
    def __init__(self, memenc, memdata, memaddr=DotMemAddr, memdim=None, memattdim=100, **kw):
        assert(memenc is not None)
        memblock = MemoryBlock(memenc, memdata, indim=memdata.shape[0], outdim=memdim)
        memstack = MemoryStack(memblock, memaddr, memattdim=memattdim)
        super(MemVec2Idx, self).__init__(memstack, **kw)

class DynMemVec2Idx(MemVec2Idx):
    def __init__(self, memenc, memaddr=DotMemAddr, memdim=None, memattdim=100, **kw):
        super(self, DynMemVec2Idx).__init__(memenc, None, memaddr=memaddr, memdim=memdim, memattdim=memattdim, **kw)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# ###
# Copyright (c) 2013, Rice University
# This software is subject to the provisions of the GNU Affero General
# Public License version 3 (AGPLv3).
# See LICENCE.txt for details.
# ###
"""Web application programming interface (API)"""
from pyramid.view import view_config
from pyramid.response import Response
from pyramid.httpexceptions import HTTPBadRequest
from pyramid.threadlocal import get_current_registry

from amqplib import client_0_8 as amqp
import jsonpickle
import pybit
from pybitweb.controller import Controller as BaseController
from pybitweb.db import Database
from pybit.models import Transport, BuildRequest

class Controller(BaseController):
    """Overrides of pybitweb.controller.Controller"""

    def process_job(self, dist, architectures, version, name,
                    suite, pkg_format, transport, build_environment=None):
        """Overridden to return the build request ID"""
        current_package = self.process_package(name, version)
        if not current_package.id:
            raise Exception("Huh?")
            ##return False

        current_suite = self.db.get_suite_byname(suite)[0]
        current_dist = self.db.get_dist_byname(dist)[0]
        current_format = self.db.get_format_byname(pkg_format)[0]

        # FIXME The use of 'any' is evil. Let's kill this behavior.
        build_env_suite_arch = self.process_build_environment_architectures(
                current_suite, architectures, build_environment)

        if len(build_env_suite_arch) == 0:
            raise Exception("Unable to submit job to PyBit")
        else:
            current_build_env = build_env_suite_arch[0].buildenv
            master_flag = True

        chan = self.get_amqp_channel()
        jobs = []
        for build_env_suite_arch in build_env_suite_arch:
            current_arch = build_env_suite_arch.suitearch.arch
            if current_build_env and current_build_env.name != build_env_suite_arch.get_buildenv_name() : #FIXME
                #first packageinstance for each build environment should have master flag set
                master_flag = True
            current_build_env = build_env_suite_arch.buildenv
            current_packageinstance = self.process_packageinstance(current_build_env, current_arch, current_package, current_dist, current_format, current_suite, master_flag)
            if current_packageinstance.id is None:
                raise Exception("Package instance not found...")

            new_job = self.db.put_job(current_packageinstance,None)
            if new_job is None:
                raise Exception("Job not created...")

            self.cancel_superceded_jobs(new_job)
            build_request_obj = BuildRequest(new_job,transport,
                    "%s:%s" % (self.settings['web']['hostname'],
                               self.settings['web']['port']))
            build_req = jsonpickle.encode(build_request_obj)
            self.db.log_buildRequest(build_request_obj)
            msg = amqp.Message(build_req)
            msg.properties["delivery_mode"] = 2
            routing_key = pybit.get_build_route_name(
                new_job.packageinstance.get_distribution_name(),
                new_job.packageinstance.get_arch_name(),
                new_job.packageinstance.get_suite_name(),
                new_job.packageinstance.get_format_name())
            build_queue = pybit.get_build_queue_name(
                new_job.packageinstance.get_distribution_name(),
                new_job.packageinstance.get_arch_name(),
                new_job.packageinstance.get_suite_name(),
                new_job.packageinstance.get_format_name())
            self.add_message_queue(build_queue, routing_key, chan)

            chan.basic_publish(msg, exchange=pybit.exchange_name,
                               routing_key=routing_key, mandatory=True)

            jobs.append(new_job.id)
        return jobs

def _acquire_pybit_settings():
    """Find and produce the PyBit settings as PyBit would normally
    know and use them.

    """
    pybit_config_path = get_current_registry().settings['pybit-config-path']
    settings = pybit.load_settings(pybit_config_path)[0]
    return settings

def _submit_to_pybit(type, id, version, url):
    pybit_settings = _acquire_pybit_settings()
    # Grab all the sent data. And make sure it's all here.
    dist, arch, suite, format = type.split('.')

    # Pass to controller to queue up
    pybit_db = Database(pybit_settings['db'])
    transport = Transport(None, '', url, '')
    controller = Controller(pybit_settings, pybit_db)
    jobs = controller.process_job(dist, arch, version, id,
                                  suite, format, transport)
    return jobs

@view_config(route_name='new', request_method='POST')
def post_job(request):
    """Post a new job"""
    job_type = request.params['job-type']
    content_id = request.params['id']
    content_version = request.params['version']
    content_url = request.params['url']
    callback_url = request.params.get('callback-url', None)

    # Then content will come in as either raw data or a URL.
    content_url = request.params.get('content-url', None)
    content_body = request.params.get('content-body', None)
    if content_url is None and content_body is None:
        # ??? Shall we default to knowing the structure of the
        #     content-url using the id, version and url?
        return HTTPBadRequest("Missing either a content-url or content-body")

    # Submit the job...
    job_ids = _submit_to_pybit(job_type, content_id, content_version,
                               content_url)
    # TODO There is nowhere to put a callback url at this time...

    urls = [request.route_url('status', id=id) for id in job_ids]
    return Response(', '.join(urls))

@view_config(route_name='status', request_method='GET')
def status(request):
    """Retrieve the status of the job."""
    status_info = request.matchdict['id']
    return Response(status_info)
<commit_msg>Job status route that delivers as much information as possible at this time. This uses the PyBit job history and status objects. More information will become available after PyBit is factored out.<commit_after># -*- coding: utf-8 -*-
# ###
# Copyright (c) 2013, Rice University
# This software is subject to the provisions of the GNU Affero General
# Public License version 3 (AGPLv3).
# See LICENCE.txt for details.
# ###
"""Web application programming interface (API)"""
from pyramid.view import view_config
from pyramid.response import Response
from pyramid.httpexceptions import (
    HTTPOk, HTTPAccepted,
    HTTPBadRequest, HTTPInternalServerError,
    )
from pyramid.threadlocal import get_current_registry

from amqplib import client_0_8 as amqp
import jsonpickle
import pybit
from pybitweb.controller import Controller as BaseController
from pybitweb.db import Database
from pybit.models import Transport, BuildRequest

class Controller(BaseController):
    """Overrides of pybitweb.controller.Controller"""

    def process_job(self, dist, architectures, version, name,
                    suite, pkg_format, transport, build_environment=None):
        """Overridden to return the build request ID"""
        current_package = self.process_package(name, version)
        if not current_package.id:
            raise Exception("Huh?")
            ##return False

        current_suite = self.db.get_suite_byname(suite)[0]
        current_dist = self.db.get_dist_byname(dist)[0]
        current_format = self.db.get_format_byname(pkg_format)[0]

        # FIXME The use of 'any' is evil. Let's kill this behavior.
        build_env_suite_arch = self.process_build_environment_architectures(
                current_suite, architectures, build_environment)

        if len(build_env_suite_arch) == 0:
            raise Exception("Unable to submit job to PyBit")
        else:
            current_build_env = build_env_suite_arch[0].buildenv
            master_flag = True

        chan = self.get_amqp_channel()
        jobs = []
        for build_env_suite_arch in build_env_suite_arch:
            current_arch = build_env_suite_arch.suitearch.arch
            if current_build_env and current_build_env.name != build_env_suite_arch.get_buildenv_name() : #FIXME
                #first packageinstance for each build environment should have master flag set
                master_flag = True
            current_build_env = build_env_suite_arch.buildenv
            current_packageinstance = self.process_packageinstance(current_build_env, current_arch, current_package, current_dist, current_format, current_suite, master_flag)
            if current_packageinstance.id is None:
                raise Exception("Package instance not found...")

            new_job = self.db.put_job(current_packageinstance,None)
            if new_job is None:
                raise Exception("Job not created...")

            self.cancel_superceded_jobs(new_job)
            build_request_obj = BuildRequest(new_job,transport,
                    "%s:%s" % (self.settings['web']['hostname'],
                               self.settings['web']['port']))
            build_req = jsonpickle.encode(build_request_obj)
            self.db.log_buildRequest(build_request_obj)
            msg = amqp.Message(build_req)
            msg.properties["delivery_mode"] = 2
            routing_key = pybit.get_build_route_name(
                new_job.packageinstance.get_distribution_name(),
                new_job.packageinstance.get_arch_name(),
                new_job.packageinstance.get_suite_name(),
                new_job.packageinstance.get_format_name())
            build_queue = pybit.get_build_queue_name(
                new_job.packageinstance.get_distribution_name(),
                new_job.packageinstance.get_arch_name(),
                new_job.packageinstance.get_suite_name(),
                new_job.packageinstance.get_format_name())
            self.add_message_queue(build_queue, routing_key, chan)

            chan.basic_publish(msg, exchange=pybit.exchange_name,
                               routing_key=routing_key, mandatory=True)

            jobs.append(new_job.id)
        return jobs

def _acquire_pybit_settings():
    """Find and produce the PyBit settings as PyBit would normally
    know and use them.

    """
    pybit_config_path = get_current_registry().settings['pybit-config-path']
    settings = pybit.load_settings(pybit_config_path)[0]
    return settings

def _get_pybit_database():
    settings = _acquire_pybit_settings()
    return Database(settings['db'])

def _submit_to_pybit(type, id, version, url):
    pybit_settings = _acquire_pybit_settings()
    # Grab all the sent data. And make sure it's all here.
    dist, arch, suite, format = type.split('.')

    # Pass to controller to queue up
    pybit_db = _get_pybit_database()
    transport = Transport(None, '', url, '')
    controller = Controller(pybit_settings, pybit_db)
    jobs = controller.process_job(dist, arch, version, id,
                                  suite, format, transport)
    return jobs

def _get_job_history(job_id):
    pybit_db = _get_pybit_database()
    return pybit_db.get_job_statuses(job_id)

@view_config(route_name='new', request_method='POST')
def post_job(request):
    """Post a new job"""
    job_type = request.params['job-type']
    content_id = request.params['id']
    content_version = request.params['version']
    content_url = request.params['url']
    callback_url = request.params.get('callback-url', None)

    # Then content will come in as either raw data or a URL.
    content_url = request.params.get('content-url', None)
    content_body = request.params.get('content-body', None)
    if content_url is None and content_body is None:
        # ??? Shall we default to knowing the structure of the
        #     content-url using the id, version and url?
        return HTTPBadRequest("Missing either a content-url or content-body")

    # Submit the job...
    job_ids = _submit_to_pybit(job_type, content_id, content_version,
                               content_url)
    # TODO There is nowhere to put a callback url at this time...

    urls = [request.route_url('status', id=id) for id in job_ids]
    return Response(', '.join(urls))

@view_config(route_name='status', request_method='GET', renderer='json')
def status(request):
    """Retrieve the status of the job."""
    job_id = request.matchdict['id']
    job_history = _get_job_history(job_id)

    # XXX Not ideal to be checking for a particular status string...
    latest = job_history[-1]
    if latest.status == 'Done':
        request.response_status = '200 Ok'
        return ''
    elif latest.status in ('Blocked', 'Failed',):
        raise HTTPInternalServerError()

    tasks_completed = 0
    tasks_total = 1
    messages = [{'type': m.status, 'timestamp': m.time, 'message': ''}
                for m in job_history]
    data = {'tasks-completed': tasks_completed, 'tasks-total': tasks_total,
            'messages': messages, 'last-modified': latest.time,
            }
    request.response_status = '202 Accepted'
    return data
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from odoo import models, fields, api
from odoo.tools import html_escape as escape
from odoo.exceptions import Warning as UserError
from odoo.tools.translate import _

SUBTASK_STATES = {'done': 'Done',
                  'todo': 'TODO',
                  'waiting': 'Waiting',
                  'cancelled': 'Cancelled'}

class ProjectTaskSubtask(models.Model):
    _name = "project.task.subtask"
    _inherit = ['ir.needaction_mixin']
    state = fields.Selection([(k, v) for k, v in SUBTASK_STATES.items()],
                             'Status', required=True, copy=False, default='todo')
    name = fields.Char(required=True, string="Description")
    reviewer_id = fields.Many2one('res.users', 'Reviewer', readonly=True, default=lambda self: self.env.user)
    project_id = fields.Many2one("project.project", related='task_id.project_id', store=True)
    user_id = fields.Many2one('res.users', 'Assigned to', required=True)
    task_id = fields.Many2one('project.task', 'Task', ondelete='cascade', required=True, index="1")
    task_state = fields.Char(string='Task state', related='task_id.stage_id.name', readonly=True)
    hide_button = fields.Boolean(compute='_compute_hide_button')
    recolor = fields.Boolean(compute='_compute_recolor')

    @api.multi
    def _compute_recolor(self):
        for record in self:
            if self.env.user == record.user_id and record.state == 'todo':
                record.recolor = True

    @api.multi
    def _compute_hide_button(self):
        for record in self:
            if self.env.user not in [record.reviewer_id, record.user_id]:
                record.hide_button = True

    @api.multi
    def _compute_reviewer_id(self):
        for record in self:
            record.reviewer_id = record.create_uid

    @api.model
    def _needaction_domain_get(self):
        if self._needaction:
            return [('state', '=', 'todo'), ('user_id', '=', self.env.uid)]
        return []

    @api.multi
    def write(self, vals):
        old_names = dict(zip(self.mapped('id'), self.mapped('name')))
        result = super(ProjectTaskSubtask, self).write(vals)
        for r in self:
            if vals.get('state'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id)
                if self.env.user != r.reviewer_id and self.env.user != r.user_id:
                    raise UserError(_('Only users related to that subtask can change state.'))
            if vals.get('name'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id, old_name=old_names[r.id])
                if self.env.user != r.reviewer_id and self.env.user != r.user_id:
                    raise UserError(_('Only users related to that subtask can change state.'))
            if vals.get('user_id'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id)
        return result

    @api.model
    def create(self, vals):
        result = super(ProjectTaskSubtask, self).create(vals)
        vals = self._add_missing_default_values(vals)
        task = self.env['project.task'].browse(vals.get('task_id'))
        task.send_subtask_email(vals['name'], vals['state'], vals['reviewer_id'], vals['user_id'])
        return result

    @api.multi
    def change_state_done(self):
        for record in self:
            record.state = 'done'

    @api.multi
    def change_state_todo(self):
        for record in self:
            record.state = 'todo'

    @api.multi
    def change_state_cancelled(self):
        for record in self:
            record.state = 'cancelled'

    @api.multi
    def change_state_waiting(self):
        for record in self:
            record.state = 'waiting'

class Task(models.Model):
    _inherit = "project.task"
    subtask_ids = fields.One2many('project.task.subtask', 'task_id', 'Subtask')
    kanban_subtasks = fields.Text(compute='_compute_kanban_subtasks')
    default_user = fields.Many2one('res.users', compute='_compute_default_user')

    @api.multi
    def _compute_default_user(self):
        for record in self:
            if self.env.user != record.user_id and self.env.user != record.create_uid:
                record.default_user = record.user_id
            else:
                if self.env.user != record.user_id:
                    record.default_user = record.user_id
                elif self.env.user != record.create_uid:
                    record.default_user = record.create_uid
                elif self.env.user == record.create_uid and self.env.user == record.user_id:
                    record.default_user = self.env.user

    @api.multi
    def _compute_kanban_subtasks(self):
        for record in self:
            result_string1 = ''
            result_string2 = ''
            result_string3 = ''
            for subtask in record.subtask_ids:
                bounding_length = 25
                tmp_list = (subtask.name).split()
                for index in range(len(tmp_list)):
                    if len(tmp_list[index]) > bounding_length:
                        tmp_list[index] = tmp_list[index][:bounding_length] + '...'
                tmp_subtask_name = " ".join(tmp_list)
                if subtask.state == 'todo' and record.env.user == subtask.user_id and record.env.user == subtask.reviewer_id:
                    tmp_string3 = escape(u': {0}'.format(tmp_subtask_name))
                    result_string3 += u'
TODO{}
'.format(tmp_string3)
                elif subtask.state == 'todo' and record.env.user == subtask.user_id:
                    tmp_string1_1 = escape(u'{0}'.format(subtask.reviewer_id.name))
                    tmp_string1_2 = escape(u'{0}'.format(tmp_subtask_name))
                    result_string1 += u'
TODO from {0}: {1}
'.format(tmp_string1_1, tmp_string1_2)
                elif subtask.state == 'todo' and record.env.user == subtask.reviewer_id:
                    tmp_string2_1 = escape(u'{0}'.format(subtask.user_id.name))
                    tmp_string2_2 = escape(u'{0}'.format(tmp_subtask_name))
                    result_string2 += u'
TODO for {0}: {1}
'.format(tmp_string2_1, tmp_string2_2)
            record.kanban_subtasks = '
' + result_string1 + result_string3 + result_string2 + '
'

    @api.multi
    def send_subtask_email(self, subtask_name, subtask_state, subtask_reviewer_id, subtask_user_id, old_name=None):
        for r in self:
            body = ''
            reviewer = self.env["res.users"].browse(subtask_reviewer_id)
            user = self.env["res.users"].browse(subtask_user_id)
            state = SUBTASK_STATES[subtask_state]
            if subtask_state == 'done':
                state = '' + state + ''
            if subtask_state == 'todo':
                state = '' + state + ''
            if subtask_state == 'cancelled':
                state = '' + state + ''
            if subtask_state == 'waiting':
                state = '' + state + ''
            partner_ids = []
            subtype = 'project_task_subtask.subtasks_subtype'
            if user == self.env.user and reviewer == self.env.user:
                body = '
' + '' + state + ': ' + escape(subtask_name)
                subtype = False
            elif self.env.user == reviewer:
                body = '
' + escape(user.name) + ',
' + state + ': ' + escape(subtask_name)
                partner_ids = [user.partner_id.id]
            elif self.env.user == user:
                body = '
' + escape(reviewer.name) + ', I updated checklist item assigned to me:
' + state + ': ' + escape(subtask_name)
                partner_ids = [reviewer.partner_id.id]
            else:
                body = '
' + escape(user.name) + ', ' + escape(reviewer.name) + ', I updated checklist item, now its assigned to ' + escape(user.name) + ':
' + state + ': ' + escape(subtask_name)
                partner_ids = [user.partner_id.id, reviewer.partner_id.id]
            if old_name:
                body = body + '
Updated from
' + state + ': ' + escape(old_name) + '
'
            else:
                body = body + '
'
            r.message_post(type='comment',
                           subtype=subtype,
                           body=body,
                           reviewer_ids=reviewer_ids)
<commit_msg>[FIX] project_task_subtask: NameError: global name 'reviewer_ids' is not defined<commit_after># -*- coding: utf-8 -*-

from odoo import models, fields, api
from odoo.tools import html_escape as escape
from odoo.exceptions import Warning as UserError
from odoo.tools.translate import _

SUBTASK_STATES = {'done': 'Done',
                  'todo': 'TODO',
                  'waiting': 'Waiting',
                  'cancelled': 'Cancelled'}

class ProjectTaskSubtask(models.Model):
    _name = "project.task.subtask"
    _inherit = ['ir.needaction_mixin']
    state = fields.Selection([(k, v) for k, v in SUBTASK_STATES.items()],
                             'Status', required=True, copy=False, default='todo')
    name = fields.Char(required=True, string="Description")
    reviewer_id = fields.Many2one('res.users', 'Reviewer', readonly=True, default=lambda self: self.env.user)
    project_id = fields.Many2one("project.project", related='task_id.project_id', store=True)
    user_id = fields.Many2one('res.users', 'Assigned to', required=True)
    task_id = fields.Many2one('project.task', 'Task', ondelete='cascade', required=True, index="1")
    task_state = fields.Char(string='Task state', related='task_id.stage_id.name', readonly=True)
    hide_button = fields.Boolean(compute='_compute_hide_button')
    recolor = fields.Boolean(compute='_compute_recolor')

    @api.multi
    def _compute_recolor(self):
        for record in self:
            if self.env.user == record.user_id and record.state == 'todo':
                record.recolor = True

    @api.multi
    def _compute_hide_button(self):
        for record in self:
            if self.env.user not in [record.reviewer_id, record.user_id]:
                record.hide_button = True

    @api.multi
    def _compute_reviewer_id(self):
        for record in self:
            record.reviewer_id = record.create_uid

    @api.model
    def _needaction_domain_get(self):
        if self._needaction:
            return [('state', '=', 'todo'), ('user_id', '=', self.env.uid)]
        return []

    @api.multi
    def write(self, vals):
        old_names = dict(zip(self.mapped('id'), self.mapped('name')))
        result = super(ProjectTaskSubtask, self).write(vals)
        for r in self:
            if vals.get('state'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id)
                if self.env.user != r.reviewer_id and self.env.user != r.user_id:
                    raise UserError(_('Only users related to that subtask can change state.'))
            if vals.get('name'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id, old_name=old_names[r.id])
                if self.env.user != r.reviewer_id and self.env.user != r.user_id:
                    raise UserError(_('Only users related to that subtask can change state.'))
            if vals.get('user_id'):
                r.task_id.send_subtask_email(r.name, r.state, r.reviewer_id.id, r.user_id.id)
        return result

    @api.model
    def create(self, vals):
        result = super(ProjectTaskSubtask, self).create(vals)
        vals = self._add_missing_default_values(vals)
        task = self.env['project.task'].browse(vals.get('task_id'))
        task.send_subtask_email(vals['name'], vals['state'], vals['reviewer_id'], vals['user_id'])
        return result

    @api.multi
    def change_state_done(self):
        for record in self:
            record.state = 'done'

    @api.multi
    def change_state_todo(self):
        for record in self:
            record.state = 'todo'

    @api.multi
    def change_state_cancelled(self):
        for record in self:
            record.state = 'cancelled'

    @api.multi
    def change_state_waiting(self):
        for record in self:
            record.state = 'waiting'

class Task(models.Model):
    _inherit = "project.task"
    subtask_ids = fields.One2many('project.task.subtask', 'task_id', 'Subtask')
    kanban_subtasks = fields.Text(compute='_compute_kanban_subtasks')
    default_user = fields.Many2one('res.users', compute='_compute_default_user')

    @api.multi
    def _compute_default_user(self):
        for record in self:
            if self.env.user != record.user_id and self.env.user != record.create_uid:
                record.default_user = record.user_id
            else:
                if self.env.user != record.user_id:
                    record.default_user = record.user_id
                elif self.env.user != record.create_uid:
                    record.default_user = record.create_uid
                elif self.env.user == record.create_uid and self.env.user == record.user_id:
                    record.default_user = self.env.user

    @api.multi
    def _compute_kanban_subtasks(self):
        for record in self:
            result_string1 = ''
            result_string2 = ''
            result_string3 = ''
            for subtask in record.subtask_ids:
                bounding_length = 25
                tmp_list = (subtask.name).split()
                for index in range(len(tmp_list)):
                    if len(tmp_list[index]) > bounding_length:
                        tmp_list[index] = tmp_list[index][:bounding_length] + '...'
                tmp_subtask_name = " ".join(tmp_list)
                if subtask.state == 'todo' and record.env.user == subtask.user_id and record.env.user == subtask.reviewer_id:
                    tmp_string3 = escape(u': {0}'.format(tmp_subtask_name))
                    result_string3 += u'
TODO{}
'.format(tmp_string3)
                elif subtask.state == 'todo' and record.env.user == subtask.user_id:
                    tmp_string1_1 = escape(u'{0}'.format(subtask.reviewer_id.name))
                    tmp_string1_2 = escape(u'{0}'.format(tmp_subtask_name))
                    result_string1 += u'
TODO from {0}: {1}
'.format(tmp_string1_1, tmp_string1_2)
                elif subtask.state == 'todo' and record.env.user == subtask.reviewer_id:
                    tmp_string2_1 = escape(u'{0}'.format(subtask.user_id.name))
                    tmp_string2_2 = escape(u'{0}'.format(tmp_subtask_name))
                    result_string2 += u'
TODO for {0}: {1}
'.format(tmp_string2_1, tmp_string2_2)
            record.kanban_subtasks = '
' + result_string1 + result_string3 + result_string2 + '
'

    @api.multi
    def send_subtask_email(self, subtask_name, subtask_state, subtask_reviewer_id, subtask_user_id, old_name=None):
        for r in self:
            body = ''
            reviewer = self.env["res.users"].browse(subtask_reviewer_id)
            user = self.env["res.users"].browse(subtask_user_id)
            state = SUBTASK_STATES[subtask_state]
            if subtask_state == 'done':
                state = '' + state + ''
            if subtask_state == 'todo':
                state = '' + state + ''
            if subtask_state == 'cancelled':
                state = '' + state + ''
            if subtask_state == 'waiting':
                state = '' + state + ''
            partner_ids = []
            subtype = 'project_task_subtask.subtasks_subtype'
            if user == self.env.user and reviewer == self.env.user:
                body = '
' + '' + state + ': ' + escape(subtask_name)
                subtype = False
            elif self.env.user == reviewer:
                body = '
' + escape(user.name) + ',
' + state + ': ' + escape(subtask_name)
                partner_ids = [user.partner_id.id]
            elif self.env.user == user:
                body = '
' + escape(reviewer.name) + ', I updated checklist item assigned to me:
' + state + ': ' + escape(subtask_name)
                partner_ids = [reviewer.partner_id.id]
            else:
                body = '
' + escape(user.name) + ', ' + escape(reviewer.name) + ', I updated checklist item, now its assigned to ' + escape(user.name) + ':
' + state + ': ' + escape(subtask_name)
                partner_ids = [user.partner_id.id, reviewer.partner_id.id]
            if old_name:
                body = body + '
Updated from
' + state + ': ' + escape(old_name) + '
'
            else:
                body = body + '
'
            r.message_post(type='comment',
                           subtype=subtype,
                           body=body,
                           partner_ids=partner_ids)
<|endoftext|>