<commit_before>#!/usr/bin/env python
#  This file is part of the myhdl library, a Python package for using
#  Python as a Hardware Description Language.
#
#  Copyright (C) 2008 Jan Decaluwe
#
#  The myhdl library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation; either version 2.1 of the
#  License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.

#  You should have received a copy of the GNU Lesser General Public
#  License along with this library; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

""" Run the intbv.signed() unit tests. """

__author__ = "Jan Decaluwe <jan@jandecaluwe.com>"
__revision__ = "$Revision$"
__date__ = "$Date$"

import unittest
from unittest import TestCase

from myhdl._intbv import intbv
from myhdl._concat import concat

class TestIntbvSigned(TestCase):
  '''Test cases to verify the intbv.signed() member function'''

  def testPlainIntbvInstance(self):
    '''Test a plain intbv instance with .signed()

    ----+----+----+----+----+----+----+----
       -3   -2   -1    0    1    2    3

                      min  max
                           min  max
                 min       max
                 min            max
            min            max
            min       max
            min  max
          neither min nor max is set
          only max is set
          only min is set

    '''
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # in the following cases the .signed() function should classify the
    # value of the intbv instance as unsigned and return a value based
    # on the msb.
    #

    # intbv with positive range, pos number, and msb not set, return signed()
    # Expect the number to be returned
    a = intbv(0x3b, min=0, max=0x7c)
    b = a.signed()
    self.assertEqual(b, 0x3b)

    # intbv with positive range, pos number, and msb set, return signed()
    # Expect the number to be converted to a negative number
    a = intbv(7, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -1)

    a = intbv(6, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -2)

    a = intbv(5, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -3)

    # set bit #3 and increase the range that the set bit is considered
    # the sign bit
    # Expect to return -4
    a = intbv(4, min=0, max=5)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=6)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=7)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -4)

    # here it is not the sign bit anymore
    # Expect the value to be 4
    a = intbv(4, min=0, max=9)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with positive range, value = 0, return signed()
    # Expect the number to be returned
    a = intbv(0, min=0, max=0x8)
    b = a.signed()
    self.assertEqual(b, 0)

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # in these cases the .signed() function should classify the
    # value of the intbv instance as signed and return the value as is
    #

    # intbv without range, pos number, return signed()
    # Expect value to be returned as is
    a = intbv(8)
    b = a.signed()
    self.assertEqual(b, 8)

    # intbv without range, neg number, return signed()
    # Expect value to be returned as is
    a = intbv(-8)
    b = a.signed()
    self.assertEqual(b, -8)

    # set bit #3 and increase the range that the set bit is actually the
    # msb, but due to the negative range not considered signed
    # Expect to return 4
    a = intbv(4, min=-1, max=5)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=6)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=7)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with negative range, pos number, and msb set, return signed()
    # Expect the number to returned as is
    a = intbv(7, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 7)

    a = intbv(6, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 6)

    a = intbv(5, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 5)

    # intbv with symmetric (min = -max) range, pos value, msb set
    # return signed()
    # Expect value returned as is
    a = intbv(4, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with symmetric (min = -max) range, neg value,
    # return signed()
    # Expect value returned as is
    a = intbv(-4, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, -4)

    # intbv with symmetric (min=-max) range, value = 0,
    # return signed()
    # Expect value returned as is
    a = intbv(0, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, 0)

  def testSlicedSigned(self):
    '''Test a slice with .signed()

    This test can actually be simplified, as a slice will always have
    min=0 and max > min, which will result in an intbv instance that
    will be considered unsigned by the intbv.signed() function.
    '''
    a = intbv(4, min=-8, max=8)
    b = a[4:]
    self.assertEqual(b, 4)
    b = a[4:].signed()
    self.assertEqual(b, 4)    # msb is not set with a 4 bit slice

    b = a[3:]
    self.assertEqual(b, 4)
    b = a[3:].signed()
    self.assertEqual(b, -4)   # msb is set with 3 bits sliced

  def testSignedConcat(self):
    '''Test the .signed() function in connection with the concatenate
    function
    '''

    # concat 3 bits
    # Expect the signed function to return a negative value
    a = concat(True, True, True).signed()
    self.assertEqual(a, -1)

    # concate a 3 bit intbv with msb set and two bits
    # Expect a negative number
    b = concat(intbv(5,min=0,max=8), True, True).signed()
    self.assertEqual(b, -9)

########################################################################
# main
#
if __name__ == "__main__":
    unittest.main()
<commit_msg>Added further tests with min > 0, that are classified as unsigned by the signed() function<commit_after>#!/usr/bin/env python
#  This file is part of the myhdl library, a Python package for using
#  Python as a Hardware Description Language.
#
#  Copyright (C) 2008 Jan Decaluwe
#
#  The myhdl library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation; either version 2.1 of the
#  License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.

#  You should have received a copy of the GNU Lesser General Public
#  License along with this library; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

""" Run the intbv.signed() unit tests. """

__author__ = "Jan Decaluwe <jan@jandecaluwe.com>"
__revision__ = "$Revision$"
__date__ = "$Date$"

import unittest
from unittest import TestCase

from myhdl._intbv import intbv
from myhdl._concat import concat

class TestIntbvSigned(TestCase):
  '''Test cases to verify the intbv.signed() member function'''

  def testPlainIntbvInstance(self):
    '''Test a plain intbv instance with .signed()

    ----+----+----+----+----+----+----+----
       -3   -2   -1    0    1    2    3

                      min  max
                           min  max
                 min       max
                 min            max
            min            max
            min       max
            min  max
          neither min nor max is set
          only max is set
          only min is set

    '''
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # in the following cases the .signed() function should classify the
    # value of the intbv instance as unsigned and return the 2's
    # complement value of the bits as specified by _nrbits.
    #

    # intbv with positive range, pos number, and msb not set, return signed()
    # Expect the number to be returned
    a = intbv(0x3b, min=0, max=0x7c)
    b = a.signed()
    self.assertEqual(b, 0x3b)

    # intbv with positive range, pos number, and msb set, return signed()
    # test various bit patterns to see that the 2's complement
    # conversion works correct
    # Expect the number to be converted to a negative number
    a = intbv(7, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -1)

    a = intbv(6, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -2)

    a = intbv(5, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -3)

    # set bit #3 and increase the range so that the set bit is considered
    # the sign bit. Here min = 0
    # Expect to return -4
    a = intbv(4, min=0, max=5)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=6)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=7)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=0, max=8)
    b = a.signed()
    self.assertEqual(b, -4)

    # here it is not the sign bit anymore
    # Expect the value to be 4
    a = intbv(4, min=0, max=9)
    b = a.signed()
    self.assertEqual(b, 4)

    # set bit #3 and increase the range so that the set bit is considered
    # the sign bit. Here min > 0
    # Expect to return -4
    a = intbv(4, min=1, max=5)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=2, max=6)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=3, max=7)
    b = a.signed()
    self.assertEqual(b, -4)

    a = intbv(4, min=4, max=8)
    b = a.signed()
    self.assertEqual(b, -4)

    # again with min > 0, here it is not the sign bit anymore
    # Expect the value to be 4
    a = intbv(4, min=2, max=9)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with positive range, value = 0, return signed()
    # Expect the number to be returned
    a = intbv(0, min=0, max=0x8)
    b = a.signed()
    self.assertEqual(b, 0)

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # in these cases the .signed() function should classify the
    # value of the intbv instance as signed and return the value as is
    #

    # intbv without range, pos number, return signed()
    # Expect value to be returned as is
    a = intbv(8)
    b = a.signed()
    self.assertEqual(b, 8)

    # intbv without range, neg number, return signed()
    # Expect value to be returned as is
    a = intbv(-8)
    b = a.signed()
    self.assertEqual(b, -8)

    # set bit #3 and increase the range that the set bit is actually the
    # msb, but due to the negative range not considered signed
    # Expect to return 4
    a = intbv(4, min=-1, max=5)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=6)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=7)
    b = a.signed()
    self.assertEqual(b, 4)

    a = intbv(4, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with negative range, pos number, and msb set, return signed()
    # Expect the number to returned as is
    a = intbv(7, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 7)

    a = intbv(6, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 6)

    a = intbv(5, min=-1, max=8)
    b = a.signed()
    self.assertEqual(b, 5)

    # intbv with symmetric (min = -max) range, pos value, msb set
    # return signed()
    # Expect value returned as is
    a = intbv(4, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, 4)

    # intbv with symmetric (min = -max) range, neg value,
    # return signed()
    # Expect value returned as is
    a = intbv(-4, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, -4)

    # intbv with symmetric (min=-max) range, value = 0,
    # return signed()
    # Expect value returned as is
    a = intbv(0, min=-8, max=8)
    b = a.signed()
    self.assertEqual(b, 0)

  def testSlicedSigned(self):
    '''Test a slice with .signed()

    This test can actually be simplified, as a slice will always have
    min=0 and max > min, which will result in an intbv instance that
    will be considered unsigned by the intbv.signed() function.
    '''
    a = intbv(4, min=-8, max=8)
    b = a[4:]
    self.assertEqual(b, 4)
    b = a[4:].signed()
    self.assertEqual(b, 4)    # msb is not set with a 4 bit slice

    b = a[3:]
    self.assertEqual(b, 4)
    b = a[3:].signed()
    self.assertEqual(b, -4)   # msb is set with 3 bits sliced

  def testSignedConcat(self):
    '''Test the .signed() function in connection with the concatenate
    function
    '''

    # concat 3 bits
    # Expect the signed function to return a negative value
    a = concat(True, True, True).signed()
    self.assertEqual(a, -1)

    # concate a 3 bit intbv with msb set and two bits
    # Expect a negative number
    b = concat(intbv(5,min=0,max=8), True, True).signed()
    self.assertEqual(b, -9)

########################################################################
# main
#
if __name__ == "__main__":
    unittest.main()
<|endoftext|>


<commit_before>#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os

from launchpadlib.launchpad import Launchpad
from launchpadlib.launchpad import uris

class launchpad_client(object):

    def __init__(self, project, bug_id=None):

        lp_cache_dir = os.path.expanduser(
            os.environ.get('LAUNCHPAD_CACHE_DIR',
                           '~/.launchpadlib/cache'))

        lp_creds_filename = os.path.expanduser(
            os.environ.get('LAUNCHPAD_CREDS_FILENAME',
                           '~/.launchpadlib/creds'))

        self.Launchpad = Launchpad.login_with(
            project,
            uris.LPNET_SERVICE_ROOT,
            lp_cache_dir,
            credentials_file=lp_creds_filename)

        self.Project = self.launchpad.projects[project]

        self.Bug = None
        self.Tasks = []
        if bug_id is not None:
            self.Bug = self.bug(bug_id)

            self.Tasks = self.tasks(bug_id)

    @property
    def launchpad(self):
        return self.Launchpad

    @property
    def project(self):
        return self.Project

    def bug(self, bug_id=None):
        return self.Bug if bug_id is None else self.launchpad.bugs[bug_id]

    def tasks(self, bug_id=None):
        if bug_id is not None:
            for task in self.bug(bug_id).bug_tasks:
                if task.bug_target_name == self.project.name:
                    self.Tasks.append(task)
            if not self.Tasks:
                raise Exception("Bug #{} not affected on project '{}'".format(
                    bug_id, self.project))
        return self.Tasks

    def add_comment(self, comment):
        return self.bug().newMessage(content=comment)

    def change_status(self, status, current_status=None):
        for task in self.tasks():
            #if current_status:
            #    if task.status != current_status:
            #        raise Exception('Bug status is not updated because '
            #                        'current value is not up to expectations')
            task.status = status
            task.lp_save()

    def create_bug(self, title, description):
        return self.launchpad.bugs.createBug(target=self.project.self_link,
                                             title=title,
                                             description=description)

def get_argparser():
    statuses_report = ['New',
                       'Confirmed',
                       'Triaged',
                       'In Progress',
                       'Fix Committed',
                       'Fix Released']
    statuses_update = ['Imcomplete',
                       'Opinion',
                       'Invalid',
                       'Won\'t fix']
    statuses_update += statuses_report

    accessibility = ['Public',
                     'Public Security',
                     'Private',
                     'Private Seciruty',
                     'Proprietary']

    importance = ['Undecided',
                  'Critical',
                  'High',
                  'Medium',
                  'Low',
                  'Wishlist']

    parser = argparse.ArgumentParser(prog='lp_cli.py',
                                     description='Command line client '
                                     'for Launchpad')
    parser.add_argument('project',
                        help='Launchpad project\'s name for auth.')
    parser.add_argument('-a', '--affected',
                        nargs='*',
                        default=None,
                        help='Launchpad project\'s name for check that bug is '
                        'affected on it. If specified, bug will be updated '
                        'only in this case.')

    subparsers = parser.add_subparsers(title='Commands')

    parser_comment = subparsers.add_parser('comment')
    parser_comment.set_defaults(func=command_comment)
    parser_comment.add_argument('bug_id', help='Bug id on Launchpad.')
    parser_comment.add_argument('comment', help='Comment body.', nargs='+')

    parser_status = subparsers.add_parser('status')
    parser_status.set_defaults(func=command_status)
    parser_status.add_argument('bug_id', help='Bug id on Launchpad.')
    parser_status.add_argument('-s', '--status',
                               help='New status for a bug.')
    #parser_status.add_argument('-c', '--current-status',
    #                           help='Current status for bug. If specified, '
    #                           'status will be updated only when according.')

    parser_report = subparsers.add_parser('report')
    parser_report.set_defaults(func=command_report)
    parser_report.add_argument('title',
                               nargs='+',
                               help='Bug title.')
    parser_report.add_argument('-d', '--description',
                               nargs='+',
                               default=None,
                               help='Bug description.')
    parser_report.add_argument('-a', '--accessibility',
                               default='Public',
                               choices=accessibility,
                               help='Bug accessibility')

    parser_report.add_argument('-s', '--status',
                               choices=statuses_report,
                               default='New',
                               help='Bug status.')
    parser_report.add_argument('-i', '--importance',
                               choices=importance,
                               default='Undecided',
                               help='Bug importance.')
    parser_report.add_argument('-m', '--milestone',
                               default=None,
                               help='Milestone')
    parser_report.add_argument('-t', '--tags',
                               nargs='+',
                               help='Add specified tags.')
    parser_report.add_argument('-o', '--assign-to',
                               default=None,
                               help='Assign bug to specified user/group.')
    #TODO: add attach subcommand w/ parameters:
    #TODO: "Description for it" --patch(True/False) filename

    parser_update = subparsers.add_parser('update')
    parser_update.set_defaults(func=command_update)
    parser_update.add_argument('title',
                               nargs='+',
                               help='Bug title.')
    parser_update.add_argument('-d', '--description',
                               nargs='+',
                               default=None,
                               help='Bug description.')
    parser_update.add_argument('-a', '--accessibility',
                               default='Public',
                               choices=accessibility,
                               help='Bug accessibility')

    parser_update.add_argument('-s', '--status',
                               choices=statuses_update,
                               default='New',
                               help='Bug status.')
    parser_update.add_argument('-i', '--importance',
                               choices=importance,
                               default='Undecided',
                               help='Bug importance.')
    parser_update.add_argument('-m', '--milestone',
                               default=None,
                               help='Milestone')
    parser_update.add_argument('-t', '--tags',
                               nargs='+',
                               help='Add specified tags.')
    parser_update.add_argument('-o', '--assign-to',
                               default=None,
                               help='Assign bug to specified user/group.')
    #TODO: add attach subcommand w/ parameters:
    #TODO: "Description for it" --patch(True/False) filename

    return parser

def command_comment(launchpad_client, options):
    comment = ' '.join(options.comment).strip()
    print 'Added comment: {}'.format(
        launchpad_client.add_comment(comment).web_link)

def command_status(launchpad_client, options):
    launchpad_client.change_status(options.status)
    print 'Status for bug #{} changed to "{}"'.format(options.bug_id,
                                                      options.status)

def command_report(launchpad_client, options):
    title = ' '.join(options.title).strip()
    description = ' '.join(options.description).strip() or ''
    bug = launchpad_client.create_bug(title, description)
    print 'Reported bug #{} {}'.format(bug.id, bug.web_link)

def main():

    parser = get_argparser()
    options = parser.parse_args()
    #lp = launchpad_client(project=options.project, bug_id=options.bug_id)
    lp = launchpad_client(project=options.project)
    options.func(lp, options)

if __name__ == '__main__':
    main()
<commit_msg>Implemented report, update<commit_after>#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os

from launchpadlib.launchpad import Launchpad
from launchpadlib.launchpad import uris

class launchpad_client(object):

    def __init__(self, project):

        lp_cache_dir = os.path.expanduser(
            os.environ.get('LAUNCHPAD_CACHE_DIR',
                           '~/.launchpadlib/cache'))

        lp_creds_filename = os.path.expanduser(
            os.environ.get('LAUNCHPAD_CREDS_FILENAME',
                           '~/.launchpadlib/creds'))

        self.Launchpad = Launchpad.login_with(
            project.lower(),
            uris.LPNET_SERVICE_ROOT,
            lp_cache_dir,
            credentials_file=lp_creds_filename)

        self.Project = None
        self.Bug = None
        self.Tasks = []

        self.project(project.lower())

    @property
    def launchpad(self):
        return self.Launchpad

    def project(self, project=None):
        if project is not None:
            self.Project = self.launchpad.projects[project]
        return self.Project

    def bug(self, bug_id=None):
        if bug_id is not None:
            if self.Bug is None or self.Bug.id != bug_id:
                self.Bug = self.launchpad.bugs[bug_id]
        return self.Bug

    def tasks(self, bug_id=None):
        if bug_id is not None:
            #TODO: add check for affected projects
            #for task in self.bug(bug_id).bug_tasks:
            #    if task.bug_target_name == self.project().name:
            #        self.Tasks.append(task)
            #if not self.Tasks:
            #    raise Exception("Bug #{} not affected on project '{}'".format(
            #        bug_id, self.project()))
            self.Tasks = self.bug(bug_id).bug_tasks
        return self.Tasks

    def create_bug(self, title, description, **properties):
        bug = self.launchpad.bugs.createBug(target=self.project().self_link,
                                            title=title,
                                            description=description)
        self.update_bug(bug.id, **properties)
        return bug

    def update_bug(self, bug_id=None, **properties):
        bug = self.bug(bug_id)
        updated = False
        for p in ['title', 'description', 'tags']:
            if p in properties:
                #exec('bug.{0} = properties["{0}"]'.format(p))
                setattr(bug, p, properties[p])
                updated = True
        if updated:
            bug.lp_save()

        for task in self.tasks(bug_id):
            self.update_task(task, **properties)

        return bug

    def update_task(self, task, **properties):
        updated = False
        for p in ['status', 'importance', 'milestone', 'assignee']:
            if p in properties:
                #exec('task.{0} = properties["{0}"]'.format(p))
                setattr(task, p, properties[p])
                updated = True
        if updated:
            task.lp_save()

    #TODO: create task for the bug

    def add_comment(self, comment, bug_id=None):
        return self.bug(bug_id).newMessage(content=comment)

    def change_status(self, bug_id, status, current_status=None):
        self.update_bug(bug_id, status=status)

def get_argparser():
    statuses_report = ['New',
                       'Confirmed',
                       'Triaged',
                       'In Progress',
                       'Fix Committed',
                       'Fix Released']
    statuses_update = ['Imcomplete',
                       'Opinion',
                       'Invalid',
                       'Won\'t fix']
    statuses_update += statuses_report

    importance = ['Undecided',
                  'Critical',
                  'High',
                  'Medium',
                  'Low',
                  'Wishlist']

    parser = argparse.ArgumentParser(prog='lp_cli.py',
                                     description='Command line client '
                                     'for Launchpad')
    parser.add_argument('project',
                        help='Launchpad project\'s name for auth.')
    parser.add_argument('-a', '--affected',
                        nargs='*',
                        default=None,
                        help='Launchpad project\'s name for check that bug is '
                        'affected on it. If specified, bug will be updated '
                        'only in this case.')

    subparsers = parser.add_subparsers(title='Commands')

    parser_comment = subparsers.add_parser('comment')
    parser_comment.set_defaults(func=command_comment)
    parser_comment.add_argument('bug_id', help='Bug id on Launchpad.')
    parser_comment.add_argument('comment', help='Comment body.', nargs='+')

    parser_status = subparsers.add_parser('status')
    parser_status.set_defaults(func=command_status)
    parser_status.add_argument('bug_id', help='Bug id on Launchpad.')
    parser_status.add_argument('-s', '--status',
                               choices=statuses_update,
                               help='New status for a bug.')
    #parser_status.add_argument('-c', '--current-status',
    #                           help='Current status for bug. If specified, '
    #                           'status will be updated only when according.')

    parser_report = subparsers.add_parser('report')
    parser_report.set_defaults(func=command_report)
    parser_report.add_argument('title',
                               help='Bug title.')
    parser_report.add_argument('-d', '--description',
                               required=True,
                               help='Bug description.')

    parser_report.add_argument('-s', '--status',
                               choices=statuses_report,
                               default='New',
                               help='Bug status.')
    parser_report.add_argument('-i', '--importance',
                               choices=importance,
                               default='Undecided',
                               help='Bug importance.')
    parser_report.add_argument('-m', '--milestone',
                               default=None,
                               help='Milestone')
    parser_report.add_argument('-t', '--tags',
                               nargs='+',
                               default=[],
                               help='Add specified tags.')
    parser_report.add_argument('-a', '--assignee',
                               default=None,
                               help='Assign bug to specified user/group.')
    #TODO: add attach subcommand w/ parameters:
    #TODO: "Description for it" --patch(True/False) filename

    parser_update = subparsers.add_parser('update')
    parser_update.set_defaults(func=command_update)
    parser_update.add_argument('bug_id', help='Bug id on Launchpad.')
    parser_update.add_argument('-l', '--title',
                               default=None,
                               help='Bug title.')
    parser_update.add_argument('-d', '--description',
                               default=None,
                               help='Bug description.')

    parser_update.add_argument('-s', '--status',
                               choices=statuses_update,
                               default=None,
                               help='Bug status.')
    parser_update.add_argument('-i', '--importance',
                               choices=importance,
                               default=None,
                               help='Bug importance.')
    parser_update.add_argument('-m', '--milestone',
                               default=None,
                               help='Milestone')
    parser_update.add_argument('-t', '--tags',
                               nargs='+',
                               default=[],
                               help='Add specified tags.')
    parser_update.add_argument('-a', '--assignee',
                               default=None,
                               help='Assign bug to specified user/group.')
    #TODO: add attach subcommand w/ parameters:
    #TODO: "Description for it" --patch(True/False) filename

    return parser

def command_comment(launchpad_client, options):
    comment = ' '.join(options.comment).strip()
    print 'Added comment: {}'.format(
        launchpad_client.add_comment(comment=comment,
                                     bug_id=options.bug_id).web_link)

def command_status(launchpad_client, options):
    launchpad_client.change_status(options.bug_id, options.status)
    print 'Status for bug #{} changed to "{}"'.format(options.bug_id,
                                                      options.status)

def command_report(launchpad_client, options):
    title = options.title
    description = options.description
    properties = vars(options)
    properties.pop('title', None)
    properties.pop('description', None)
    for k in properties.keys():
        if properties[k] is None or properties[k] == []:
            properties.pop(k, None)
    bug = launchpad_client.create_bug(title, description, **properties)
    print 'Reported bug #{} {}'.format(bug.id, bug.web_link)

def command_update(launchpad_client, options):
    properties = vars(options)
    for k in properties.keys():
        if properties[k] is None or properties[k] == []:
            properties.pop(k, None)
    bug = launchpad_client.update_bug(**properties)
    print 'Updated bug #{} {}'.format(bug.id, bug.web_link)

def main():

    parser = get_argparser()
    options = parser.parse_args()
    lp = launchpad_client(project=options.project)
    options.func(lp, options)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>import random

from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User

from mzalendo.testing.selenium import MzalendoSeleniumTestCase, TwitterSeleniumTestCase
from mzalendo.core.models import Person
from mzalendo.comments2.models import Comment

class CommentTestCase(MzalendoSeleniumTestCase):

    fixtures = ['test_data']

    def test_leaving_comment(self):
        driver = self.driver

        random_string = str( random.randint(100000, 999999) )

        # Go to the test person
        self.open_url('/')
        self.open_url('/person/joseph-bloggs')

        # Go to the comments, and then login to add one
        driver.find_element_by_id("view_comments_link").click()
        driver.find_element_by_link_text("login to add your own comment").click()

        self.login('superuser')

        # fill in the comment and submit
        driver.find_element_by_id("id_title").clear()
        driver.find_element_by_id("id_title").send_keys("This is a test comment " + random_string)
        driver.find_element_by_id("id_comment").clear()
        driver.find_element_by_id("id_comment").send_keys("Test comment content")
        driver.find_element_by_css_selector("input[type=\"submit\"]").click()

        # check that a comment has been created in the database
        bloggs = Person.objects.get(slug="joseph-bloggs")
        comments = bloggs.comments
        self.assertEqual(comments.count(), 1)
        comment = comments.all()[0]

        # Check that we are on the comment page.
        self.assertTrue( comment.get_absolute_url() in driver.current_url )
        self.assertTrue( comment.title in self.page_source )

class TweetCommentTestCase(TwitterSeleniumTestCase):

    fixtures = ['test_data']

    def test_posting_to_twitter(self):
        driver = self.driver

        # make sure that we are logged in on twitter
        self.twitter_login()

        random_string = str( random.randint(100000, 999999) )

        # create the comment
        bloggs = Person.objects.get(slug="joseph-bloggs")
        superuser = User.objects.get(username="superuser")
        comment = Comment(
            content_type = ContentType.objects.get_for_model(bloggs),
            object_id    = bloggs.id,
            title        = 'test title' + random_string,
            comment      = 'test comment',
            user         = superuser,
            # submit_date  = datetime.datetime.now(),
        )
        comment.save()

        # Go to the test person
        self.open_url( comment.get_absolute_url() )

        # Tweet the comment - not sure that the selector is specific enough
        driver.switch_to_frame(0)
        driver.find_element_by_id("btn").click()

        # go through all the windows and stay with the twitter one
        for h in driver.window_handles:
            driver.switch_to_window(h)
            if 'twitter.com/' in driver.current_url: break

        # Check that the tweet is ready to send
        self.assertTrue( random_string in driver.find_element_by_id("status").text )

        # use this to submit the tweet
        #driver.find_element_by_css_selector("input.button.submit").click()

        driver.close()
        <commit_msg>Fix failing tweet test<commit_after>import random

from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User

from mzalendo.testing.selenium import MzalendoSeleniumTestCase, TwitterSeleniumTestCase
from mzalendo.core.models import Person
from mzalendo.comments2.models import Comment

class CommentTestCase(MzalendoSeleniumTestCase):

    fixtures = ['test_data']

    def test_leaving_comment(self):
        driver = self.driver

        random_string = str( random.randint(100000, 999999) )

        # Go to the test person
        self.open_url('/')
        self.open_url('/person/joseph-bloggs')

        # Go to the comments, and then login to add one
        driver.find_element_by_id("view_comments_link").click()
        driver.find_element_by_link_text("login to add your own comment").click()

        self.login('superuser')

        # fill in the comment and submit
        driver.find_element_by_id("id_title").clear()
        driver.find_element_by_id("id_title").send_keys("This is a test comment " + random_string)
        driver.find_element_by_id("id_comment").clear()
        driver.find_element_by_id("id_comment").send_keys("Test comment content")
        driver.find_element_by_css_selector("input[type=\"submit\"]").click()

        # check that a comment has been created in the database
        bloggs = Person.objects.get(slug="joseph-bloggs")
        comments = bloggs.comments
        self.assertEqual(comments.count(), 1)
        comment = comments.all()[0]

        # Check that we are on the comment page.
        self.assertTrue( comment.get_absolute_url() in driver.current_url )
        self.assertTrue( comment.title in self.page_source )

class TweetCommentTestCase(TwitterSeleniumTestCase):

    fixtures = ['test_data']

    def test_posting_to_twitter(self):
        driver = self.driver

        # make sure that we are logged in on twitter
        self.twitter_login()

        random_string = str( random.randint(100000, 999999) )

        # create the comment
        bloggs = Person.objects.get(slug="joseph-bloggs")
        superuser = User.objects.get(username="superuser")
        comment = Comment(
            content_type = ContentType.objects.get_for_model(bloggs),
            object_id    = bloggs.id,
            title        = 'test title ' + random_string,
            comment      = 'test comment',
            user         = superuser,
            # submit_date  = datetime.datetime.now(),
        )
        comment.save()

        # Go to the test person
        self.open_url( comment.get_absolute_url() )

        # Tweet the comment - not sure that the selector is specific enough
        driver.switch_to_frame(1)
        driver.find_element_by_css_selector("a.btn").click()

        # go through all the windows and stay with the twitter one
        for h in driver.window_handles:
            driver.switch_to_window(h)
            if 'twitter.com/' in driver.current_url: break

        # Check that the tweet is ready to send
        self.assertTrue( random_string in driver.find_element_by_id("status").text )

        # use this to submit the tweet
        #driver.find_element_by_css_selector("input.button.submit").click()

        driver.close()
        <|endoftext|>


<commit_before># lptest.py - Run unit tests against listparser.py
# Copyright (C) 2009 Kurt McKee <contactme@kurtmckee.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import datetime # required by evals
import os
from os.path import abspath, dirname, join, splitext
import threading
import unittest
import BaseHTTPServer
import SimpleHTTPServer
import StringIO

import listparser

class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        status = 200
        location = etag = modified = None
        reply = u''
        end_directives = False
        f = open(dirname(abspath(__file__)) + self.path, 'r')
        for line in f:
            reply += line
            if not end_directives:
                if line.strip() == '-->':
                    end_directives = True
                elif 'Status:' in line:
                    status = int(line.strip()[7:])
                elif 'Location:' in line:
                    location = line.strip()[9:].strip()
                elif 'ETag:' in line:
                    etag = line.strip()[5:].strip()
                    if self.headers.get('if-none-match') == etag:
                        status = 304
                elif 'Modified:' in line:
                    modified = line.strip()[10:].strip()
                    if self.headers.get('if-modified-since') == modified:
                        status = 304
        f.close()
        self.send_response(status)
        if location:
            self.send_header('Location', location)
        if etag:
            self.send_header('ETag', etag)
        if modified:
            self.send_header('Last-Modified', modified)
        self.send_header('Content-type', 'text/xml')
        self.end_headers()
        self.wfile.write(reply)
    def log_request(self, *arg, **karg):
        pass

class ServerThread(threading.Thread):
    def __init__(self, numtests):
        super(ServerThread, self).__init__()
        self.numtests = numtests
    def run(self):
        server = BaseHTTPServer.HTTPServer
        bind_to = ('', 8091)
        reqhandler = Handler
        httpd = server(bind_to, reqhandler)
        while self.numtests:
            httpd.handle_request()
            self.numtests -= 1

class TestCases(unittest.TestCase):
    def setUp(self):
        pass
    def tearDown(self):
        pass
    def testUnparsableObject(self):
        result = listparser.parse(True)
        self.assert_(result['bozo'] == 1)
    def testStringInput(self):
        t = """<?xml version="1.0"?><opml version="2.0"><title>
        String Input Test</title><outline text="node" />
        </opml>"""
        result = listparser.parse(t)
        self.assert_(result['bozo'] == 0)
        self.assert_(result['meta']['title'] == u'String Input Test')
    def testFileishInput(self):
        t = """<?xml version="1.0"?><opml version="2.0"><title>
        Fileish Input Test</title><outline text="node" />
        </opml>"""
        result = listparser.parse(StringIO.StringIO(t))
        self.assert_(result['bozo'] == 0)
        self.assert_(result['meta']['title'] == u'Fileish Input Test')
    def worker(self, evals, testfile, etag, modified):
        result = listparser.parse('http://localhost:8091/tests/' + testfile,
            etag=etag, modified=modified)
        map(self.assert_, map(eval, evals))

def make_testcase(evals, testfile, etag, modified):
    # HACK: Only necessary in order to ensure that `evals` is evaluated
    # for every testcase, not just for the last one in the loop below
    # (where, apparently, `lambda` would cause it to be evaluated only
    # once at the end of the loop, containing the final testcase' values).
    return lambda self: self.worker(evals, testfile, etag, modified)

numtests = 0
testpath = join(dirname(abspath(__file__)), 'tests/')
# files contains a list of relative paths to test files
# HACK: replace() is only being used because os.path.relpath()
# was only added to Python in version 2.6
files = (join(r, f).replace(testpath, '', 1)
            for r, d, files in os.walk(testpath)
            for f in files if f.endswith('.xml'))
for testfile in files:
    if 'http/destination' in testfile:
        # destination.xml is the target of four redirect requests
        numtests += 4
        continue
    elif 'http/http_304-last_modified' in testfile:
        # http_304-last_modified.xml must be called twice:
        # once with `modified` as a string, and again as a datetime
        numtests += 2
    else:
        numtests += 1
    description = ''
    etag = modified = None
    evals = []
    openfile = open(join(testpath, testfile))
    for line in openfile:
        line = line.strip()
        if '-->' in line:
            break
        if 'Description:' in line:
            description = line.split('Description:')[1].strip()
        if 'Eval:' in line:
            evals.append(line.split('Eval:')[1].strip())
        if 'http/http_304' in testfile:
            if 'ETag:' in line:
                etag = line.strip()[5:].strip()
            if 'Modified:' in line:
                modified = line.strip()[9:].strip()
    openfile.close()
    if not description:
        raise ValueError("Description not found in test %s" % testfile)
    if not evals:
        raise ValueError("Eval not found in test %s" % testfile)
    if 'http/http_304-last_modified' in testfile:
        testcase = make_testcase(evals, testfile, etag, modified)
        testcase.__doc__ = '%s: %s [string]' % (testfile, description)
        setattr(TestCases, 'test_%s_1' % splitext(testfile)[0], testcase)
        testcase = make_testcase(evals, testfile, etag, listparser._rfc822(modified))
        testcase.__doc__ = '%s: %s [datetime]' % (testfile, description)
        setattr(TestCases, 'test_%s_2' % splitext(testfile)[0], testcase)
    else:
        testcase = make_testcase(evals, testfile, etag, modified)
        testcase.__doc__ = '%s: %s' % (testfile, description)
        setattr(TestCases, 'test_%s' % splitext(testfile)[0], testcase)

server = ServerThread(numtests)
server.start()

testsuite = unittest.TestLoader().loadTestsFromTestCase(TestCases)
unittest.TextTestRunner(verbosity=1).run(testsuite)
<commit_msg>python3: ensure proper `reply` type to `write()`<commit_after># lptest.py - Run unit tests against listparser.py
# Copyright (C) 2009 Kurt McKee <contactme@kurtmckee.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import datetime # required by evals
import os
from os.path import abspath, dirname, join, splitext
import threading
import unittest
import BaseHTTPServer
import SimpleHTTPServer
import StringIO

import listparser

def bytestr(reply):
    # HACK: force `reply` to the type expected by the webserver's `write()` function
    # Python 2 expects type(basestring)
    # Python 3 expects type(bytes)
    try:
        return bytes(reply, 'utf8')
    except (TypeError, NameError):
        return reply

class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        status = 200
        location = etag = modified = None
        reply = u''
        end_directives = False
        f = open(dirname(abspath(__file__)) + self.path, 'r')
        for line in f:
            reply += line
            if not end_directives:
                if line.strip() == '-->':
                    end_directives = True
                elif 'Status:' in line:
                    status = int(line.strip()[7:])
                elif 'Location:' in line:
                    location = line.strip()[9:].strip()
                elif 'ETag:' in line:
                    etag = line.strip()[5:].strip()
                    if self.headers.get('if-none-match') == etag:
                        status = 304
                elif 'Modified:' in line:
                    modified = line.strip()[10:].strip()
                    if self.headers.get('if-modified-since') == modified:
                        status = 304
        f.close()
        self.send_response(status)
        if location:
            self.send_header('Location', location)
        if etag:
            self.send_header('ETag', etag)
        if modified:
            self.send_header('Last-Modified', modified)
        self.send_header('Content-type', 'text/xml')
        self.end_headers()
        self.wfile.write(bytestr(reply))
    def log_request(self, *arg, **karg):
        pass

class ServerThread(threading.Thread):
    def __init__(self, numtests):
        super(ServerThread, self).__init__()
        self.numtests = numtests
    def run(self):
        server = BaseHTTPServer.HTTPServer
        bind_to = ('', 8091)
        reqhandler = Handler
        httpd = server(bind_to, reqhandler)
        while self.numtests:
            httpd.handle_request()
            self.numtests -= 1

class TestCases(unittest.TestCase):
    def setUp(self):
        pass
    def tearDown(self):
        pass
    def testUnparsableObject(self):
        result = listparser.parse(True)
        self.assert_(result['bozo'] == 1)
    def testStringInput(self):
        t = """<?xml version="1.0"?><opml version="2.0"><title>
        String Input Test</title><outline text="node" />
        </opml>"""
        result = listparser.parse(t)
        self.assert_(result['bozo'] == 0)
        self.assert_(result['meta']['title'] == u'String Input Test')
    def testFileishInput(self):
        t = """<?xml version="1.0"?><opml version="2.0"><title>
        Fileish Input Test</title><outline text="node" />
        </opml>"""
        result = listparser.parse(StringIO.StringIO(t))
        self.assert_(result['bozo'] == 0)
        self.assert_(result['meta']['title'] == u'Fileish Input Test')
    def worker(self, evals, testfile, etag, modified):
        result = listparser.parse('http://localhost:8091/tests/' + testfile,
            etag=etag, modified=modified)
        map(self.assert_, map(eval, evals))

def make_testcase(evals, testfile, etag, modified):
    # HACK: Only necessary in order to ensure that `evals` is evaluated
    # for every testcase, not just for the last one in the loop below
    # (where, apparently, `lambda` would cause it to be evaluated only
    # once at the end of the loop, containing the final testcase' values).
    return lambda self: self.worker(evals, testfile, etag, modified)

numtests = 0
testpath = join(dirname(abspath(__file__)), 'tests/')
# files contains a list of relative paths to test files
# HACK: replace() is only being used because os.path.relpath()
# was only added to Python in version 2.6
files = (join(r, f).replace(testpath, '', 1)
            for r, d, files in os.walk(testpath)
            for f in files if f.endswith('.xml'))
for testfile in files:
    if 'http/destination' in testfile:
        # destination.xml is the target of four redirect requests
        numtests += 4
        continue
    elif 'http/http_304-last_modified' in testfile:
        # http_304-last_modified.xml must be called twice:
        # once with `modified` as a string, and again as a datetime
        numtests += 2
    else:
        numtests += 1
    description = ''
    etag = modified = None
    evals = []
    openfile = open(join(testpath, testfile))
    for line in openfile:
        line = line.strip()
        if '-->' in line:
            break
        if 'Description:' in line:
            description = line.split('Description:')[1].strip()
        if 'Eval:' in line:
            evals.append(line.split('Eval:')[1].strip())
        if 'http/http_304' in testfile:
            if 'ETag:' in line:
                etag = line.strip()[5:].strip()
            if 'Modified:' in line:
                modified = line.strip()[9:].strip()
    openfile.close()
    if not description:
        raise ValueError("Description not found in test %s" % testfile)
    if not evals:
        raise ValueError("Eval not found in test %s" % testfile)
    if 'http/http_304-last_modified' in testfile:
        testcase = make_testcase(evals, testfile, etag, modified)
        testcase.__doc__ = '%s: %s [string]' % (testfile, description)
        setattr(TestCases, 'test_%s_1' % splitext(testfile)[0], testcase)
        testcase = make_testcase(evals, testfile, etag, listparser._rfc822(modified))
        testcase.__doc__ = '%s: %s [datetime]' % (testfile, description)
        setattr(TestCases, 'test_%s_2' % splitext(testfile)[0], testcase)
    else:
        testcase = make_testcase(evals, testfile, etag, modified)
        testcase.__doc__ = '%s: %s' % (testfile, description)
        setattr(TestCases, 'test_%s' % splitext(testfile)[0], testcase)

server = ServerThread(numtests)
server.start()

testsuite = unittest.TestLoader().loadTestsFromTestCase(TestCases)
unittest.TextTestRunner(verbosity=1).run(testsuite)
<|endoftext|>


<commit_before>import abc
from nala.structures.data import Label
import re
from nala.utils import MUT_CLASS_ID

class Labeler:
    """
    Abstract class for generating labels for each token in the dataset.
    Subclasses that inherit this class should:
    * Be named [Name]Labeler
    * Implement the abstract method label
    * Append new items to the list field "original_labels" of each Token in the dataset
    """

    @abc.abstractmethod
    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        return

class BIOLabeler(Labeler):
    """
    Implements a simple labeler using the annotations of the dataset
    using the BIO (beginning, inside, outside) format. Creates labels
    based on the class_id value in the Annotation object. That is:
    * B-[class_id]
    * I-[class_id]
    * O

    Requires the list field "annotations" to be previously set.
    Implements the abstract class Labeler.
    """

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            for sentence in part.sentences:
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start:
                            token.original_labels[0].value = 'B-{}'.format(ann.class_id)
                            break
                        elif start < token.start < end:
                            token.original_labels[0].value = 'I-{}'.format(ann.class_id)
                            break

class TmVarLabeler(Labeler):
    """
    Implements a labeler using the annotations of the dataset
    based on the labeling scheme used by tmVar (http://www.ncbi.nlm.nih.gov/CBBresearch/Lu/Demo/tmTools/#tmVar)

    This is however a close approximation of their lableing scheme and not an exact replica.

    The following labels are possible:
    * A - Reference sequence
    * T - Mutation type
    * F - Frame shift
    * R - SNP
    * M - Mutant
    * W - Wild type
    * S - Frame shift position
    * P - Mutation position
    * I - Other inside mutation tokens
    * O - Outside

    Requires the list field "annotations" to be previously set.
    Requires the TmVarTokenizer to be used.
    Implements the abstract class Labeler.
    """

    def __init__(self):
        # A
        self.label_reference_sequence = re.compile('(^[cgrmp]$)|(^(ivs|ex|orf)$)')
        # T
        self.label_type = re.compile('(del|ins|dup|tri|qua|con|delins|indel)')
        # F
        self.label_frameshift = re.compile('^(fs|fsX|fsx)$')
        # R
        self.label_snip = re.compile('^(rs|RS|Rs)$')

        # W or M (wild type or mutant)
        self.dna_symbols = re.compile('^[ATCGUatcgu]+$')
        self.protein_symbols = re.compile('(glutamine|glutamic|leucine|valine|isoleucine|lysine|alanine|glycine|'
                                          'aspartate|methionine|threonine|histidine|aspartic|asparticacid|arginine|'
                                          'asparagine|tryptophan|proline|phenylalanine|cysteine|serine|glutamate|'
                                          'tyrosine|stop|frameshift)|(^(cys|ile|ser|gln|met|asn|pro|lys|asp|thr|phe|'
                                          'ala|gly|his|leu|arg|trp|val|glu|tyr|fs|fsx)$)|(^[cisqmnpkdtfaghlrwveyx]$)')

        # P or S (mutation_position or frameshift_position)
        self.position = re.compile('^[0-9]+$')

    def _match_regex_label(self, previous_token, token):
        if self.label_reference_sequence.search(token.word):
            token.original_labels[0].value = 'A'  # Reference sequence
        elif self.label_type.search(token.word):
            token.original_labels[0].value = 'T'  # Mutation type
        elif self.label_frameshift.search(token.word):
            token.original_labels[0].value = 'F'  # Frame shift
        elif previous_token is not None and previous_token.original_labels[0].value is 'F' and token.word is 'X':
            token.original_labels[0].value = 'F'  # Frame shift
        elif self.label_snip.search(token.word):
            token.original_labels[0].value = 'R'  # SNP
        elif self.dna_symbols.search(token.word) or self.protein_symbols.search(token.word.lower()):
           token.original_labels[0].value = '*'   # temporary for W or M (wild type or mutant)
        elif self.position.search(token.word):
            if previous_token is not None and previous_token.original_labels[0].value == 'F':
                token.original_labels[0].value = 'S'  # Frame shift position
            else:
                token.original_labels[0].value = 'P'  # Mutation position
        else:
            token.original_labels[0].value = 'I'  # Other inside mutation tokens

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            previous_token = None
            for sentence in part.sentences:
                alternate = 'W'
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start or start < token.start < end:
                            if ann.class_id == MUT_CLASS_ID:
                                self._match_regex_label(previous_token, token)
                                previous_token = token

                                # replace temporary label with W or M
                                if token.original_labels[0].value == '*':
                                    token.original_labels[0].value = alternate
                                    alternate = 'W' if alternate == 'M' else 'M'
                                # reset the alternation to W since we reached end
                                if token.start + len(token.word) == end:
                                    alternate = 'W'
                                break

                # iterate a sliding window of 3
                # when you find 'P I P' labels replace them with 'P P P'
                for previous, current, next in zip(sentence, sentence[1:], sentence[2:]):
                    if previous.original_labels[0].value == 'P' and next.original_labels[0].value == 'P':
                        if current.original_labels[0].value == 'I':
                            current.original_labels[0].value = 'P'

class BIEOLabeler(Labeler):
    """
    Implements a simple labeler using the annotations of the dataset
    using the BIEO (beginning, inside, ending, outside) format. Creates labels
    based on the class_id value in the Annotation object. That is:
    * B-[class_id]
    * I-[class_id]
    * E-[class_id]
    * O

    Requires the list field "annotations" to be previously set.
    Implements the abstract class Labeler.
    """

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            for sentence in part.sentences:
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start:
                            token.original_labels[0].value = 'B-{}'.format(ann.class_id)
                            break
                        elif start < token.start < token.end < end:
                            token.original_labels[0].value = 'I-{}'.format(ann.class_id)
                            break
                        elif token.end == end:
                            token.original_labels[0].value = 'E-{}'.format(ann.class_id)
                            break
<commit_msg>[#82] Use new end field<commit_after>import abc
from nala.structures.data import Label
import re
from nala.utils import MUT_CLASS_ID

class Labeler:
    """
    Abstract class for generating labels for each token in the dataset.
    Subclasses that inherit this class should:
    * Be named [Name]Labeler
    * Implement the abstract method label
    * Append new items to the list field "original_labels" of each Token in the dataset
    """

    @abc.abstractmethod
    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        return

class BIOLabeler(Labeler):
    """
    Implements a simple labeler using the annotations of the dataset
    using the BIO (beginning, inside, outside) format. Creates labels
    based on the class_id value in the Annotation object. That is:
    * B-[class_id]
    * I-[class_id]
    * O

    Requires the list field "annotations" to be previously set.
    Implements the abstract class Labeler.
    """

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            for sentence in part.sentences:
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start:
                            token.original_labels[0].value = 'B-{}'.format(ann.class_id)
                            break
                        elif start < token.start < end:
                            token.original_labels[0].value = 'I-{}'.format(ann.class_id)
                            break

class TmVarLabeler(Labeler):
    """
    Implements a labeler using the annotations of the dataset
    based on the labeling scheme used by tmVar (http://www.ncbi.nlm.nih.gov/CBBresearch/Lu/Demo/tmTools/#tmVar)

    This is however a close approximation of their lableing scheme and not an exact replica.

    The following labels are possible:
    * A - Reference sequence
    * T - Mutation type
    * F - Frame shift
    * R - SNP
    * M - Mutant
    * W - Wild type
    * S - Frame shift position
    * P - Mutation position
    * I - Other inside mutation tokens
    * O - Outside

    Requires the list field "annotations" to be previously set.
    Requires the TmVarTokenizer to be used.
    Implements the abstract class Labeler.
    """

    def __init__(self):
        # A
        self.label_reference_sequence = re.compile('(^[cgrmp]$)|(^(ivs|ex|orf)$)')
        # T
        self.label_type = re.compile('(del|ins|dup|tri|qua|con|delins|indel)')
        # F
        self.label_frameshift = re.compile('^(fs|fsX|fsx)$')
        # R
        self.label_snip = re.compile('^(rs|RS|Rs)$')

        # W or M (wild type or mutant)
        self.dna_symbols = re.compile('^[ATCGUatcgu]+$')
        self.protein_symbols = re.compile('(glutamine|glutamic|leucine|valine|isoleucine|lysine|alanine|glycine|'
                                          'aspartate|methionine|threonine|histidine|aspartic|asparticacid|arginine|'
                                          'asparagine|tryptophan|proline|phenylalanine|cysteine|serine|glutamate|'
                                          'tyrosine|stop|frameshift)|(^(cys|ile|ser|gln|met|asn|pro|lys|asp|thr|phe|'
                                          'ala|gly|his|leu|arg|trp|val|glu|tyr|fs|fsx)$)|(^[cisqmnpkdtfaghlrwveyx]$)')

        # P or S (mutation_position or frameshift_position)
        self.position = re.compile('^[0-9]+$')

    def _match_regex_label(self, previous_token, token):
        if self.label_reference_sequence.search(token.word):
            token.original_labels[0].value = 'A'  # Reference sequence
        elif self.label_type.search(token.word):
            token.original_labels[0].value = 'T'  # Mutation type
        elif self.label_frameshift.search(token.word):
            token.original_labels[0].value = 'F'  # Frame shift
        elif previous_token is not None and previous_token.original_labels[0].value is 'F' and token.word is 'X':
            token.original_labels[0].value = 'F'  # Frame shift
        elif self.label_snip.search(token.word):
            token.original_labels[0].value = 'R'  # SNP
        elif self.dna_symbols.search(token.word) or self.protein_symbols.search(token.word.lower()):
           token.original_labels[0].value = '*'   # temporary for W or M (wild type or mutant)
        elif self.position.search(token.word):
            if previous_token is not None and previous_token.original_labels[0].value == 'F':
                token.original_labels[0].value = 'S'  # Frame shift position
            else:
                token.original_labels[0].value = 'P'  # Mutation position
        else:
            token.original_labels[0].value = 'I'  # Other inside mutation tokens

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            previous_token = None
            for sentence in part.sentences:
                alternate = 'W'
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start or start < token.start < end:
                            if ann.class_id == MUT_CLASS_ID:
                                self._match_regex_label(previous_token, token)
                                previous_token = token

                                # replace temporary label with W or M
                                if token.original_labels[0].value == '*':
                                    token.original_labels[0].value = alternate
                                    alternate = 'W' if alternate == 'M' else 'M'
                                # reset the alternation to W since we reached end
                                if token.end == end:
                                    alternate = 'W'
                                break

                # iterate a sliding window of 3
                # when you find 'P I P' labels replace them with 'P P P'
                for previous, current, next in zip(sentence, sentence[1:], sentence[2:]):
                    if previous.original_labels[0].value == 'P' and next.original_labels[0].value == 'P':
                        if current.original_labels[0].value == 'I':
                            current.original_labels[0].value = 'P'

class BIEOLabeler(Labeler):
    """
    Implements a simple labeler using the annotations of the dataset
    using the BIEO (beginning, inside, ending, outside) format. Creates labels
    based on the class_id value in the Annotation object. That is:
    * B-[class_id]
    * I-[class_id]
    * E-[class_id]
    * O

    Requires the list field "annotations" to be previously set.
    Implements the abstract class Labeler.
    """

    def label(self, dataset):
        """
        :type dataset: nala.structures.data.Dataset
        """
        for part in dataset.parts():
            for sentence in part.sentences:
                for token in sentence:
                    token.original_labels = [Label('O')]

                    for ann in part.annotations:
                        start = ann.offset
                        end = ann.offset + len(ann.text)
                        if start == token.start:
                            token.original_labels[0].value = 'B-{}'.format(ann.class_id)
                            break
                        elif start < token.start < token.end < end:
                            token.original_labels[0].value = 'I-{}'.format(ann.class_id)
                            break
                        elif token.end == end:
                            token.original_labels[0].value = 'E-{}'.format(ann.class_id)
                            break
<|endoftext|>