<commit_before>_tatd ='Trinidad and Tobago Dollar'
_uaed = 'United Arab Emirates Dirham'

def make_ccys(db):
    '''
    Create the currency dictionary
    '''
    dfr = 4
    dollar = '\u0024'
    peso   = '\u20b1'
    kr     = 'kr'
    yen    = '\xa5'

    db.insert('EUR','978','EU',1,       'Euro',                 dfr,'EU', '30/360', 'ACT/360', symbol='\u20ac')
    db.insert('GBP','826','BP',2,       'British Pound',        dfr,'GB', 'ACT/365','ACT/365', symbol='\xa3')
    db.insert('AUD','036','AD',3,       'Australian Dollar',    dfr,'AU', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('NZD','554','ND',4,       'New-Zealand Dollar',   dfr,'NZ', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('USD','840','UD',5,       'US Dollar',            0,  'US', '30/360', 'ACT/360', symbol=dollar)
    db.insert('CAD','124','CD',6,       'Canadian Dollar',      dfr,'CA', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('CHF','756','SF',7,       'Swiss Franc',          dfr,'CH', '30/360', 'ACT/360', symbol='Fr')
    db.insert('NOK','578','NK',8,       'Norwegian Krona',      dfr,'NO', '30/360', 'ACT/360', symbol=kr)
    db.insert('SEK','752','SK',9,       'Swedish Krona',        dfr,'SE', '30/360', 'ACT/360', symbol=kr)
    db.insert('DKK','208','DK',10,      'Danish Krona',         dfr,'DK', '30/360', 'ACT/360', symbol=kr)
    db.insert('JPY','392','JY',10000,   'Japanese Yen',         2,  'JP', 'ACT/365','ACT/360', symbol=yen)

    db.insert('CNY','156','CY',680,     'Chinese Renminbi',     dfr,'CN', 'ACT/365','ACT/365', symbol=yen)
    db.insert('KRW','410','KW',110000,  'South Korean won',     2,  'KR', 'ACT/365','ACT/365', symbol='\u20a9')
    db.insert('SGD','702','SD',15,      'Singapore Dollar',     dfr,'SG', 'ACT/365','ACT/365')
    db.insert('IDR','360','IH',970000,  'Indonesian Rupiah',    0,  'ID', 'ACT/360','ACT/360')
    db.insert('THB','764','TB',3300,    'Thai Baht',            2,  'TH', 'ACT/365','ACT/365', symbol='\u0e3f')
    db.insert('TWD','901','TD',18,      'Taiwan Dollar',        dfr,'TW', 'ACT/365','ACT/365')
    db.insert('HKD','344','HD',19,      'Hong Kong Dollar',     dfr,'HK', 'ACT/365','ACT/365', symbol='\u5713')
    db.insert('PHP','608','PP',4770,    'Philippines Peso',     dfr,'PH', 'ACT/360','ACT/360')
    db.insert('INR','356','IR',4500,    'Indian Rupee',         dfr,'IN', 'ACT/365','ACT/365', symbol='\u20a8')
    db.insert('MYR','458','MR',345,     'Malaysian Ringgit',    dfr,'MY', 'ACT/365','ACT/365')
    db.insert('VND','704','VD',1700000, 'Vietnamese Dong',      0,  'VN', 'ACT/365','ACT/365', symbol='\u20ab')

    db.insert('BRL','986','BC',200,     'Brazilian Real',       dfr,'BR', 'BUS/252','BUS/252', symbol='R$')
    db.insert('PEN','604','PS',220,     'Peruvian New Sol',     dfr,'PE', 'ACT/360','ACT/360', symbol='S/.')
    db.insert('ARS','032','AP',301,     'Argentine Peso',       dfr,'AR', '30/360', 'ACT/360', symbol=dollar)
    db.insert('MXN','484','MP',1330,    'Mexican Peso',         dfr,'MX', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('CLP','152','CH',54500,   'Chilean Peso',         2,  'CL', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('COP','170','CL',190000,  'Colombian Peso',       2,  'CO', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('JMD','388','JD',410,     'Jamaican Dollar',      dfr,'JM', 'ACT/360','ACT/360', symbol=dollar)   ### TODO: Check towletters code and position
    db.insert('TTD','780','TT',410,     _tatd,                  dfr,'TT', 'ACT/360','ACT/360', symbol=dollar)   ### TODO: Check towletters code and position
    db.insert('BMD','060','BD',410,     'BermudIan Dollar',     dfr,'BM', symbol=dollar)   ### TODO: Check towletters code and position

    db.insert('CZK','203','CK',28,      'Czech Koruna',         dfr,'CZ', 'ACT/360','ACT/360')
    db.insert('PLN','985','PZ',29,      'Polish Zloty',         dfr,'PL', 'ACT/ACT','ACT/365')
    db.insert('TRY','949','TY',30,      'Turkish Lira',         dfr,'TR', 'ACT/360','ACT/360', symbol='YTL')
    db.insert('HUF','348','HF',32,      'Hungarian Forint',     dfr,'HU', 'ACT/365','ACT/360', symbol='Ft')
    db.insert('RON','946','RN',34,      'Romanian Leu',         dfr,'RO', 'ACT/360','ACT/360')
    db.insert('RUB','643','RR',36,      'Russian Ruble',        dfr,'RU', 'ACT/ACT','ACT/ACT', symbol='\u0440\u0443\u0431')
    db.insert('HRK','191','HK',410,     'Croatian kuna',        dfr,'HR')                 ### TODO: Check towletters code and position.
    db.insert('EEK','233','EK',410,     'Estonia Kroon',        dfr,'EE')                 ### TODO: Check towletters code and position
    db.insert('KZT','398','KT',410,     'Tenge',                dfr,'KZ')                         ### TODO: Check towletters code and position

    db.insert('ILS','376','IS',410,     'Israeli Shekel',       dfr,'IL', 'ACT/365','ACT/365', symbol='\u20aa')
    db.insert('AED','784','AE',410,     _uaed,                  dfr,'AE')   ### TODO: Check towletters code and position
    db.insert('QAR','634','QA',410,     'Qatari Riyal',         dfr,'QA')                   ### TODO: Check towletters code and position
    db.insert('SAR','682','SR',410,     'Saudi Riyal',          dfr,'SA')                    ### TODO: Check towletters code and position
    db.insert('EGP','818','EP',550,     'Egyptian Pound',       dfr,'EG')
    db.insert('ZAR','710','SA',750,     'South African Rand',   dfr,'ZA', 'ACT/365','ACT/365', symbol='R')

    db.insert('DOP','214','DO',410,     'Dominican Peso',       dfr,'DO', symbol = 'RD$')
    db.insert('UYU','858','UY',410,     'Uruguayan Peso',       dfr,'UY', symbol = '$')
<commit_msg>Adding support for:<commit_after>_tatd ='Trinidad and Tobago Dollar'
_uaed = 'United Arab Emirates Dirham'

def make_ccys(db):
    '''
    Create the currency dictionary
    '''
    dfr = 4
    dollar = '\u0024'
    peso   = '\u20b1'
    kr     = 'kr'
    yen    = '\xa5'

    db.insert('EUR','978','EU',1,       'Euro',                 dfr,'EU', '30/360', 'ACT/360', symbol='\u20ac')
    db.insert('GBP','826','BP',2,       'British Pound',        dfr,'GB', 'ACT/365','ACT/365', symbol='\xa3')
    db.insert('AUD','036','AD',3,       'Australian Dollar',    dfr,'AU', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('NZD','554','ND',4,       'New-Zealand Dollar',   dfr,'NZ', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('USD','840','UD',5,       'US Dollar',            0,  'US', '30/360', 'ACT/360', symbol=dollar)
    db.insert('CAD','124','CD',6,       'Canadian Dollar',      dfr,'CA', 'ACT/365','ACT/365', symbol=dollar)
    db.insert('CHF','756','SF',7,       'Swiss Franc',          dfr,'CH', '30/360', 'ACT/360', symbol='Fr')
    db.insert('NOK','578','NK',8,       'Norwegian Krona',      dfr,'NO', '30/360', 'ACT/360', symbol=kr)
    db.insert('SEK','752','SK',9,       'Swedish Krona',        dfr,'SE', '30/360', 'ACT/360', symbol=kr)
    db.insert('DKK','208','DK',10,      'Danish Krona',         dfr,'DK', '30/360', 'ACT/360', symbol=kr)
    db.insert('JPY','392','JY',10000,   'Japanese Yen',         2,  'JP', 'ACT/365','ACT/360', symbol=yen)

    db.insert('CNY','156','CY',680,     'Chinese Renminbi',     dfr,'CN', 'ACT/365','ACT/365', symbol=yen)
    db.insert('KRW','410','KW',110000,  'South Korean won',     2,  'KR', 'ACT/365','ACT/365', symbol='\u20a9')
    db.insert('SGD','702','SD',15,      'Singapore Dollar',     dfr,'SG', 'ACT/365','ACT/365')
    db.insert('IDR','360','IH',970000,  'Indonesian Rupiah',    0,  'ID', 'ACT/360','ACT/360')
    db.insert('THB','764','TB',3300,    'Thai Baht',            2,  'TH', 'ACT/365','ACT/365', symbol='\u0e3f')
    db.insert('TWD','901','TD',18,      'Taiwan Dollar',        dfr,'TW', 'ACT/365','ACT/365')
    db.insert('HKD','344','HD',19,      'Hong Kong Dollar',     dfr,'HK', 'ACT/365','ACT/365', symbol='\u5713')
    db.insert('PHP','608','PP',4770,    'Philippines Peso',     dfr,'PH', 'ACT/360','ACT/360')
    db.insert('INR','356','IR',4500,    'Indian Rupee',         dfr,'IN', 'ACT/365','ACT/365', symbol='\u20a8')
    db.insert('MYR','458','MR',345,     'Malaysian Ringgit',    dfr,'MY', 'ACT/365','ACT/365')
    db.insert('VND','704','VD',1700000, 'Vietnamese Dong',      0,  'VN', 'ACT/365','ACT/365', symbol='\u20ab')

    db.insert('BRL','986','BC',200,     'Brazilian Real',       dfr,'BR', 'BUS/252','BUS/252', symbol='R$')
    db.insert('PEN','604','PS',220,     'Peruvian New Sol',     dfr,'PE', 'ACT/360','ACT/360', symbol='S/.')
    db.insert('ARS','032','AP',301,     'Argentine Peso',       dfr,'AR', '30/360', 'ACT/360', symbol=dollar)
    db.insert('MXN','484','MP',1330,    'Mexican Peso',         dfr,'MX', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('CLP','152','CH',54500,   'Chilean Peso',         2,  'CL', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('COP','170','CL',190000,  'Colombian Peso',       2,  'CO', 'ACT/360','ACT/360', symbol=dollar)
    db.insert('JMD','388','JD',410,     'Jamaican Dollar',      dfr,'JM', 'ACT/360','ACT/360', symbol=dollar)   ### TODO: Check towletters code and position
    db.insert('TTD','780','TT',410,     _tatd,                  dfr,'TT', 'ACT/360','ACT/360', symbol=dollar)   ### TODO: Check towletters code and position
    db.insert('BMD','060','BD',410,     'BermudIan Dollar',     dfr,'BM', symbol=dollar)   ### TODO: Check towletters code and position

    db.insert('CZK','203','CK',28,      'Czech Koruna',         dfr,'CZ', 'ACT/360','ACT/360')
    db.insert('PLN','985','PZ',29,      'Polish Zloty',         dfr,'PL', 'ACT/ACT','ACT/365')
    db.insert('TRY','949','TY',30,      'Turkish Lira',         dfr,'TR', 'ACT/360','ACT/360', symbol='YTL')
    db.insert('HUF','348','HF',32,      'Hungarian Forint',     dfr,'HU', 'ACT/365','ACT/360', symbol='Ft')
    db.insert('RON','946','RN',34,      'Romanian Leu',         dfr,'RO', 'ACT/360','ACT/360')
    db.insert('RUB','643','RR',36,      'Russian Ruble',        dfr,'RU', 'ACT/ACT','ACT/ACT', symbol='\u0440\u0443\u0431')
    db.insert('HRK','191','HK',410,     'Croatian kuna',        dfr,'HR')                 ### TODO: Check towletters code and position.
    db.insert('EEK','233','EK',410,     'Estonia Kroon',        dfr,'EE')                 ### TODO: Check towletters code and position
    db.insert('KZT','398','KT',410,     'Tenge',                dfr,'KZ')                         ### TODO: Check towletters code and position

    db.insert('ILS','376','IS',410,     'Israeli Shekel',       dfr,'IL', 'ACT/365','ACT/365', symbol='\u20aa')
    db.insert('AED','784','AE',410,     _uaed,                  dfr,'AE')   ### TODO: Check towletters code and position
    db.insert('QAR','634','QA',410,     'Qatari Riyal',         dfr,'QA')                   ### TODO: Check towletters code and position
    db.insert('SAR','682','SR',410,     'Saudi Riyal',          dfr,'SA')                    ### TODO: Check towletters code and position
    db.insert('EGP','818','EP',550,     'Egyptian Pound',       dfr,'EG')
    db.insert('ZAR','710','SA',750,     'South African Rand',   dfr,'ZA', 'ACT/365','ACT/365', symbol='R')

    db.insert('DOP','214','DO',410,     'Dominican Peso',       dfr,'DO', symbol = 'RD$')
    db.insert('UYU','858','UY',410,     'Uruguayan Peso',       dfr,'UY', symbol = '$')
    db.insert('CRC','188','CR',410,     'Costa Rica Colon',     dfr,'CR', symbol = '\u20a1')

    db.insert('KWD','414','KW',410,     'Kuwaiti Dinar',        dfr,'KW', symbol = 'K.D.')
    db.insert('SAR','682','SA',410,     'Saudi Rial',           dfr,'SA', symbol = 'SR')
    db.insert('QAR','634','QA',410,     'Qatar Rial',           dfr,'QA', symbol = 'QR')
    db.insert('AED','784','AE',410,     'Emirates Dirham',      dfr,'AE', symbol = dollar)
    db.insert('OMR','512','OM',410,     'Omani Rial',           dfr,'OM', symbol = dollar)
<|endoftext|>


<commit_before>#!/usr/bin/python
from __future__ import print_function
from pt_data import masses
val = ""
multiples = {}

def add_commas(orig):
    with_Commas = ""
    for i in range(len(orig) - 1):
        with_Commas += orig[i]
        if orig[i + 1].isupper():
            with_Commas += ","
    with_Commas += orig[-1]
    with_Commas += ','
    return with_Commas

# returns the complete coeficcient given the starting number
def find_num(i, val):
    if val[i].isalpha() or val[i] == ',':
        return val[i]
    count = i
    while(count < len(val) and not val[count].isalpha() and val[count] != ","):
        count += 1
    if count >= len(val):
        return val[i:]
    return val[i:count]

# distributes the vnumber to all the numbers in the string
def distribute(val, num):
    build = ""
    sub = 1
    for i in range(len(val)):
        if not val[i].isalpha():
            sub = float(find_num(i, val))
            sub *= num
            build += str(sub)
            i += len(find_num(i, val)) - 1
        elif i+1 >= len(val) or (val[i+1].isupper()):
            sub = 1*num
            build += val[i]
            build += str(sub)
            i += len(find_num(i, val))
            sub = 1
        else:
            build += val[i]
    return build

# distributes the number through parenthesis
def distrib_parenth(val):
    between = ""
    op = val.find('(')
    num = 1
    while(op != -1):
        close = val.find(')', op)
        between = val[op+1:close]
        if close+1 < len(val) and not val[close+1].isalpha() and val[close+1] != '':
            num = float(val[close+1])
            val = val[:op] + distribute(between, num) + val[close+2:]
        else:
            val = val[:op] + distribute(between, num) + val[close+1:]
        op = val.find('(', op+1)
        num = 1
    return val

# adds comma and parenthesis markers so rest of the functions can work
def add_markers(val):
    if "[" in val and "]" in val:
        val = val.replace("[","(")
        val = val.replace("]",")")
        val = distrib_parenth(val)
    if ',' not in val:
        val = add_commas(val)
    return val

# adds coefficients and symbol to the multiples dictionary
def add(mult, symb):
    global multiples
    com = symb.find(",")
    if com >= 0:
        symb = symb[:com]
    if symb not in multiples:
        multiples[symb] = mult
    else:
        multiples[symb] += mult

# returns a string with no coefficients and builds the multiples
# dictionary
def strip_coeff(val):
    build = ""
    symb = ""
    coeff = False
    i = 0
    while i < len(val):
        if not val[i].isalpha() and val[i] != ',':
            num = float(find_num(i, val))
            add(num, symb)
            coeff = True
        if val[i] == ',':
            if val[i].isalpha() or val[i] == ',':
                symb += val[i]
            if not coeff:
                add(1,symb)
            build += symb
            symb = ""
            coeff = False
        elif val[i].isalpha():
            symb += val[i]
        i += len(find_num(i, val))
    if len(build) == 0:
        return ""
    return build

def calc_single_element(val):
    count = 0
    num = float(masses[val][:len(masses[val]) - 3].strip())
    if val in multiples:
        count += num * multiples[val]
    else:
        count += num
    return count

# returns a mass if all elements are real
# otherwise returns the name of the offending element
def calculate(comp):
    build = ""
    count = 0
    finished = []
    for i in range(len(comp)):
        if comp[i] == ",":
            if build in masses and build not in finished:
                count += calc_single_element(build)
                finished.append(build)
            elif build not in masses:
                return build
            build = ""
        elif i == len(comp) - 1:
            build += comp[i]
            if build in masses and build not in finished:
                count += calc_single_element(build)
                finished.append(build)
            elif build not in masses:
                return build
        else:
            build += comp[i]
    return count
<commit_msg>Fix ImportError<commit_after>#!/usr/bin/python
from __future__ import print_function
from calcmass.pt_data import masses
val = ""
multiples = {}

def add_commas(orig):
    with_Commas = ""
    for i in range(len(orig) - 1):
        with_Commas += orig[i]
        if orig[i + 1].isupper():
            with_Commas += ","
    with_Commas += orig[-1]
    with_Commas += ','
    return with_Commas

# returns the complete coeficcient given the starting number
def find_num(i, val):
    if val[i].isalpha() or val[i] == ',':
        return val[i]
    count = i
    while(count < len(val) and not val[count].isalpha() and val[count] != ","):
        count += 1
    if count >= len(val):
        return val[i:]
    return val[i:count]

# distributes the vnumber to all the numbers in the string
def distribute(val, num):
    build = ""
    sub = 1
    for i in range(len(val)):
        if not val[i].isalpha():
            sub = float(find_num(i, val))
            sub *= num
            build += str(sub)
            i += len(find_num(i, val)) - 1
        elif i+1 >= len(val) or (val[i+1].isupper()):
            sub = 1*num
            build += val[i]
            build += str(sub)
            i += len(find_num(i, val))
            sub = 1
        else:
            build += val[i]
    return build

# distributes the number through parenthesis
def distrib_parenth(val):
    between = ""
    op = val.find('(')
    num = 1
    while(op != -1):
        close = val.find(')', op)
        between = val[op+1:close]
        if close+1 < len(val) and not val[close+1].isalpha() and val[close+1] != '':
            num = float(val[close+1])
            val = val[:op] + distribute(between, num) + val[close+2:]
        else:
            val = val[:op] + distribute(between, num) + val[close+1:]
        op = val.find('(', op+1)
        num = 1
    return val

# adds comma and parenthesis markers so rest of the functions can work
def add_markers(val):
    if "[" in val and "]" in val:
        val = val.replace("[","(")
        val = val.replace("]",")")
        val = distrib_parenth(val)
    if ',' not in val:
        val = add_commas(val)
    return val

# adds coefficients and symbol to the multiples dictionary
def add(mult, symb):
    global multiples
    com = symb.find(",")
    if com >= 0:
        symb = symb[:com]
    if symb not in multiples:
        multiples[symb] = mult
    else:
        multiples[symb] += mult

# returns a string with no coefficients and builds the multiples
# dictionary
def strip_coeff(val):
    build = ""
    symb = ""
    coeff = False
    i = 0
    while i < len(val):
        if not val[i].isalpha() and val[i] != ',':
            num = float(find_num(i, val))
            add(num, symb)
            coeff = True
        if val[i] == ',':
            if val[i].isalpha() or val[i] == ',':
                symb += val[i]
            if not coeff:
                add(1,symb)
            build += symb
            symb = ""
            coeff = False
        elif val[i].isalpha():
            symb += val[i]
        i += len(find_num(i, val))
    if len(build) == 0:
        return ""
    return build

def calc_single_element(val):
    count = 0
    num = float(masses[val][:len(masses[val]) - 3].strip())
    if val in multiples:
        count += num * multiples[val]
    else:
        count += num
    return count

# returns a mass if all elements are real
# otherwise returns the name of the offending element
def calculate(comp):
    build = ""
    count = 0
    finished = []
    for i in range(len(comp)):
        if comp[i] == ",":
            if build in masses and build not in finished:
                count += calc_single_element(build)
                finished.append(build)
            elif build not in masses:
                return build
            build = ""
        elif i == len(comp) - 1:
            build += comp[i]
            if build in masses and build not in finished:
                count += calc_single_element(build)
                finished.append(build)
            elif build not in masses:
                return build
        else:
            build += comp[i]
    return count
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2013 Edgewall Software
# Copyright (C) 2007 Eli Carter <retracile@gmail.com>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/.

from genshi.builder import tag

from trac.core import Component, implements
from trac.ticket.api import ITicketActionController, TicketSystem
from trac.perm import IPermissionRequestor

revision = "$Rev$"
url = "$URL$"

class StatusFixerActionController(Component):
    """Provides the admin with a way to correct a ticket's status.

    This plugin is especially useful when you made changes to your workflow,
    and some ticket status are no longer valid. The tickets that are in those
    status can then be set to some valid state.

    Don't forget to add `StatusFixerActionController` to the workflow
    option in the `[ticket]` section in TracIni.
    If there is no other workflow option, the line will look like this:
    {{{
    workflow = ConfigurableTicketWorkflow,StatusFixerActionController
    }}}
    """

    implements(ITicketActionController, IPermissionRequestor)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ['TICKET_STATUSFIX']

    # ITicketActionController methods

    def get_ticket_actions(self, req, ticket):
        actions = []
        if 'TICKET_STATUSFIX' in req.perm(ticket.resource):
            actions.append((0, 'force_status'))
        return actions

    def get_all_status(self):
        """Return all the status that are present in the database,
        so that queries for status no longer in use can be made.
        """
        return [status for status, in
                self.env.db_query("SELECT DISTINCT status FROM ticket")]

    def render_ticket_action_control(self, req, ticket, action):
        # Need to use the list of all status so you can't manually set
        # something to an invalid state.
        selected_value = req.args.get('force_status_value', 'new')
        all_status = TicketSystem(self.env).get_all_status()
        render_control = tag.select(
            [tag.option(x, selected=(x == selected_value and 'selected' or
                                     None)) for x in all_status],
            id='force_status_value', name='force_status_value')
        return ("force status to", render_control,
                "The next status will be the selected one")

    def get_ticket_changes(self, req, ticket, action):
        return {'status': req.args.get('force_status_value')}

    def apply_action_side_effects(self, req, ticket, action):
        pass
<commit_msg>1.2dev: Don't show //force status// action on `/newticket` page<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2013 Edgewall Software
# Copyright (C) 2007 Eli Carter <retracile@gmail.com>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/.

from genshi.builder import tag

from trac.core import Component, implements
from trac.ticket.api import ITicketActionController, TicketSystem
from trac.perm import IPermissionRequestor

revision = "$Rev$"
url = "$URL$"

class StatusFixerActionController(Component):
    """Provides the admin with a way to correct a ticket's status.

    This plugin is especially useful when you made changes to your workflow,
    and some ticket status are no longer valid. The tickets that are in those
    status can then be set to some valid state.

    Don't forget to add `StatusFixerActionController` to the workflow
    option in the `[ticket]` section in TracIni.
    If there is no other workflow option, the line will look like this:
    {{{
    workflow = ConfigurableTicketWorkflow,StatusFixerActionController
    }}}
    """

    implements(ITicketActionController, IPermissionRequestor)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ['TICKET_STATUSFIX']

    # ITicketActionController methods

    def get_ticket_actions(self, req, ticket):
        actions = []
        if ticket.exists and 'TICKET_STATUSFIX' in req.perm(ticket.resource):
            actions.append((0, 'force_status'))
        return actions

    def get_all_status(self):
        """Return all the status that are present in the database,
        so that queries for status no longer in use can be made.
        """
        return [status for status, in
                self.env.db_query("SELECT DISTINCT status FROM ticket")]

    def render_ticket_action_control(self, req, ticket, action):
        # Need to use the list of all status so you can't manually set
        # something to an invalid state.
        selected_value = req.args.get('force_status_value', 'new')
        all_status = TicketSystem(self.env).get_all_status()
        render_control = tag.select(
            [tag.option(x, selected=(x == selected_value and 'selected' or
                                     None)) for x in all_status],
            id='force_status_value', name='force_status_value')
        return ("force status to", render_control,
                "The next status will be the selected one")

    def get_ticket_changes(self, req, ticket, action):
        return {'status': req.args.get('force_status_value')}

    def apply_action_side_effects(self, req, ticket, action):
        pass
<|endoftext|>


<commit_before>from django.conf import settings
from django.db import models, connection, transaction
from tenant_schemas.postgresql_backend.base import _check_identifier
from django.core.management import call_command
from tenant_schemas.signals import post_schema_sync
from tenant_schemas.utils import django_is_in_test_mode, schema_exists
from .utils import get_public_schema_name
from django.db.models.signals import post_delete
from django.dispatch import receiver

class TenantMixin(models.Model):
    auto_drop_schema = False   # USE THIS WITH CAUTION!
                               # set this flag to true on a parent class if
                               # you want the schema to be automatically
                               # removed after tenant remove.

    auto_create_schema = True  # set this flag to false on a parent class if
                               # you dont want the schema to be automatically
                               # created upon save.

    domain_url = models.CharField(max_length=128, unique=True)
    schema_name = models.CharField(max_length=63)

    class Meta:
        abstract = True

    def save(self, verbosity=1, *args, **kwargs):
        if connection.get_schema() != get_public_schema_name():
            raise Exception("Can't update tenant outside the public schema. Current schema is %s."
                            % connection.get_schema())

        is_new = self.pk is None
        super(TenantMixin, self).save(*args, **kwargs)

        if is_new and self.auto_create_schema:
            self.create_schema(check_if_exists=True, verbosity=verbosity)
            post_schema_sync.send(sender=TenantMixin, tenant=self)

        transaction.commit_unless_managed()

    def create_schema(self, check_if_exists=False, sync_schema=True, verbosity=1):
        """
        Creates the schema 'schema_name' for this tenant. Optionally checks if the schema
        already exists before creating it. Returns true if the schema was created, false
        otherwise.
        """

        # safety check
        _check_identifier(self.schema_name)
        cursor = connection.cursor()

        if check_if_exists and schema_exists(self.schema_name):
            return False

        # create the schema
        cursor.execute('CREATE SCHEMA %s' % self.schema_name)

        if sync_schema:
            call_command('sync_schemas',
                         schema_name=self.schema_name,
                         tenant=True,
                         public=False,
                         interactive=False,  # don't ask to create an admin user
                         migrate_all=True,  # migrate all apps directly to last version
                         verbosity=verbosity,
                         )

            # fake all migrations
            if 'south' in settings.INSTALLED_APPS and not django_is_in_test_mode():
                call_command('migrate_schemas', fake=True, schema_name=self.schema_name, verbosity=verbosity)

        return True

@receiver(post_delete, sender=get_tenant_model())
def drop_schema(sender, instance, **kwargs):
    """
    Called in post_delete signal.
    Drops the schema related to the tenant instance. Just drop the schema if the parent
    class model has the attribute auto_drop_schema set to True.
    """
    if schema_exists(instance.schema_name) and instance.auto_drop_schema:
        cursor = connection.cursor()
        cursor.execute('DROP SCHEMA %s CASCADE' % instance.schema_name)
<commit_msg>add missing import<commit_after>from django.conf import settings
from django.db import models, connection, transaction
from tenant_schemas.postgresql_backend.base import _check_identifier
from django.core.management import call_command
from tenant_schemas.signals import post_schema_sync
from tenant_schemas.utils import django_is_in_test_mode, schema_exists, get_tenant_model
from .utils import get_public_schema_name
from django.db.models.signals import post_delete
from django.dispatch import receiver

class TenantMixin(models.Model):
    auto_drop_schema = False   # USE THIS WITH CAUTION!
                               # set this flag to true on a parent class if
                               # you want the schema to be automatically
                               # removed after tenant remove.

    auto_create_schema = True  # set this flag to false on a parent class if
                               # you dont want the schema to be automatically
                               # created upon save.

    domain_url = models.CharField(max_length=128, unique=True)
    schema_name = models.CharField(max_length=63)

    class Meta:
        abstract = True

    def save(self, verbosity=1, *args, **kwargs):
        if connection.get_schema() != get_public_schema_name():
            raise Exception("Can't update tenant outside the public schema. Current schema is %s."
                            % connection.get_schema())

        is_new = self.pk is None
        super(TenantMixin, self).save(*args, **kwargs)

        if is_new and self.auto_create_schema:
            self.create_schema(check_if_exists=True, verbosity=verbosity)
            post_schema_sync.send(sender=TenantMixin, tenant=self)

        transaction.commit_unless_managed()

    def create_schema(self, check_if_exists=False, sync_schema=True, verbosity=1):
        """
        Creates the schema 'schema_name' for this tenant. Optionally checks if the schema
        already exists before creating it. Returns true if the schema was created, false
        otherwise.
        """

        # safety check
        _check_identifier(self.schema_name)
        cursor = connection.cursor()

        if check_if_exists and schema_exists(self.schema_name):
            return False

        # create the schema
        cursor.execute('CREATE SCHEMA %s' % self.schema_name)

        if sync_schema:
            call_command('sync_schemas',
                         schema_name=self.schema_name,
                         tenant=True,
                         public=False,
                         interactive=False,  # don't ask to create an admin user
                         migrate_all=True,  # migrate all apps directly to last version
                         verbosity=verbosity,
                         )

            # fake all migrations
            if 'south' in settings.INSTALLED_APPS and not django_is_in_test_mode():
                call_command('migrate_schemas', fake=True, schema_name=self.schema_name, verbosity=verbosity)

        return True

@receiver(post_delete, sender=get_tenant_model())
def drop_schema(sender, instance, **kwargs):
    """
    Called in post_delete signal.
    Drops the schema related to the tenant instance. Just drop the schema if the parent
    class model has the attribute auto_drop_schema set to True.
    """
    if schema_exists(instance.schema_name) and instance.auto_drop_schema:
        cursor = connection.cursor()
        cursor.execute('DROP SCHEMA %s CASCADE' % instance.schema_name)
<|endoftext|>


<commit_before>#Main page for Apul.py http://github.com/GrautDevelopes/Apul.py/
#Very alpha. Expect bugs!
#Only use redirectors that go to blobar and don't go to clickvalidator.net. Examples:
#http://youtuber.com/ << Redirects to clickvalidator
#http://pete.com/ << The only one that seems to work
#http://youtibe.com/ << Redirects to clickvalidator
# @Thunder when you get script sorted out.
import urllib2
import sys
import re
print("[Apul] Starting...")
print("[Apul] Checking " + sys.argv[1] + " ...")
req = urllib2.Request(sys.argv[1], headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' })
RedirectorResponse = urllib2.urlopen(req)
RedirectorResponseHTML = RedirectorResponse.read()
RedirectorResponse.close()
def clean_text(rgx, text):
    new_text = text
    new_text = re.sub(rgx, '', new_text)
    return new_text
if "http://blobar.org/d/p" in RedirectorResponseHTML:
 print("[Apul] Verifed " + sys.argv[1] + " !")
 while True:
        trim1 = RedirectorResponseHTML[614:]
        req2 = urllib2.Request(clean_text('\'\+\(\(.*', trim1))
        Redirector2Response = urllib2.urlopen(req2)
        Redirecter3url = Redirector2Response.geturl() + "&z=240"
        Redirector2Response.close()
        #print("[Apul] Resolving " + Redirecter3url + " ...") #The blobar url
        req3 = urllib2.Request(Redirecter3url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' })
        Redirector3Response = urllib2.urlopen(req3)
        PopUpHTML = Redirector3Response.read() #TODO: Extract phone number from popup
        PopUpURL = Redirector3Response.geturl()
        num = re.match("(\+|1-|8..).*", PopUpHTML)
        print(PopUpURL)
        Redirector3Response.close()
 else:
	      print("[Apul] The redirector " + sys.argv[1] + " does not use blobar.")
<commit_msg>Blobar url extraction is longer domain relative.<commit_after>#Main page for Apul.py http://github.com/GrautDevelopes/Apul.py/
#Very alpha. Expect bugs!
#Only use redirectors that go to blobar and don't go to clickvalidator.net. Examples:
#http://youtuber.com/ << Redirects to clickvalidator
#http://pete.com/ << The only one that seems to work
#http://youtibe.com/ << Redirects to clickvalidator
# @Thunder when you get script sorted out.
import urllib2
import sys
import re
print("[Apul] Starting...")
print("[Apul] Checking " + sys.argv[1] + " ...")
req = urllib2.Request(sys.argv[1], headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' })
RedirectorResponse = urllib2.urlopen(req)
RedirectorResponseHTML = RedirectorResponse.read()
RedirectorResponse.close()
def clean_text(rgx, text):
    new_text = text
    new_text = re.sub(rgx, '', new_text)
    return new_text
if "http://blobar.org/d/p" in RedirectorResponseHTML:
    print("[Apul] Verifed " + sys.argv[1] + " !")
    while True:
        trim0 = clean_text('.*var u = \'', RedirectorResponseHTML)
        trim1 = clean_text('\'\+\(\(r.*', trim0)
        req2 = urllib2.Request(clean_text('\'\+\(\(.*', trim1))
        Redirector2Response = urllib2.urlopen(req2)
        Redirecter3url = Redirector2Response.geturl() + "&z=240"
        Redirector2Response.close()
        #print("[Apul] Resolving " + Redirecter3url + " ...") #The blobar url
        req3 = urllib2.Request(Redirecter3url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' })
        Redirector3Response = urllib2.urlopen(req3)
        PopUpHTML = Redirector3Response.read() #TODO: Extract phone number from popup
        PopUpURL = Redirector3Response.geturl()
        num = re.search(r"^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})", PopUpHTML)#.group(0) #Much thanks Laurence O'Donnell from regexlib
        Redirector3Response.close()
        if num:
            #print(PopUpURL + " | " + num)
			print(PopUpURL + " | ".format(num.group(0)))
        else:
            print(PopUpURL)
else:
    print("[Apul] The redirector " + sys.argv[1] + " does not use blobar.")
<|endoftext|>


<commit_before># fresh_tomatoes.py

import webbrowser
import os
import re
import entertainment_center

# Styles and scripting for the page
main_page_head = '''

    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="keywords" content="Vishal Lama, Udacity, Movie Trailers">
    <title>Fresh Tomatoes!</title>

    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap-theme.min.css">

'''

# The main page layout and title bar
main_page_content = '''
<!DOCTYPE html>

            Fresh Tomatoes Movie Trailers

        {movie_tiles}

© 2015 Vishal Lama

'''

# A single movie entry html template
movie_tile_content = '''

{movie_title}

{release_year}

{movie_rating}

'''

def create_movie_tiles_content(movies):
    # The HTML content for this section of the page
    content = ''
    for movie in movies:
        # Extract the youtube ID from the url
        youtube_id_match = re.search(r'(?<=v=)[^&#]+', movie.trailer_youtube_url)
        youtube_id_match = youtube_id_match or re.search(r'(?<=be/)[^&#]+', movie.trailer_youtube_url)
        trailer_youtube_id = youtube_id_match.group(0) if youtube_id_match else None

        # Append the tile for the movie with its content filled in
        content += movie_tile_content.format(
            movie_title=movie.title,
            poster_image_url=movie.poster_image_url,
            trailer_youtube_id=trailer_youtube_id,
            movie_rating = movie.mpaa_rating,
            release_year = movie.release_date.split()[-1]
        )
    return content

def open_movies_page(movies):
    # Create or overwrite the output file
    output_file = open('fresh_tomatoes.html', 'w')

    # Replace the placeholder for the movie tiles with the actual dynamically generated content
    rendered_content = main_page_content.format(movie_tiles=create_movie_tiles_content(movies))

    # Output the file
    output_file.write(main_page_head + rendered_content)
    output_file.close()

    # open the output file in the browser
    url = os.path.abspath(output_file.name)
    webbrowser.open('file://' + url, new=2)     # open in a new tab, if possible

def main():
    open_movies_page(entertainment_center.movies)

if __name__ == "__main__":
    main()

<commit_msg>Add footer semantic tag<commit_after># fresh_tomatoes.py

import webbrowser
import os
import re
import entertainment_center

# Styles and scripting for the page
main_page_head = '''

    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="keywords" content="Vishal Lama, Udacity, Movie Trailers">
    <title>Fresh Tomatoes!</title>

    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap-theme.min.css">

'''

# The main page layout and title bar
main_page_content = '''
<!DOCTYPE html>

            Fresh Tomatoes Movie Trailers

        {movie_tiles}

© 2015 Vishal Lama

'''

# A single movie entry html template
movie_tile_content = '''

{movie_title}

{release_year}

{movie_rating}

'''

def create_movie_tiles_content(movies):
    # The HTML content for this section of the page
    content = ''
    for movie in movies:
        # Extract the youtube ID from the url
        youtube_id_match = re.search(r'(?<=v=)[^&#]+', movie.trailer_youtube_url)
        youtube_id_match = youtube_id_match or re.search(r'(?<=be/)[^&#]+', movie.trailer_youtube_url)
        trailer_youtube_id = youtube_id_match.group(0) if youtube_id_match else None

        # Append the tile for the movie with its content filled in
        content += movie_tile_content.format(
            movie_title=movie.title,
            poster_image_url=movie.poster_image_url,
            trailer_youtube_id=trailer_youtube_id,
            movie_rating = movie.mpaa_rating,
            release_year = movie.release_date.split()[-1]
        )
    return content

def open_movies_page(movies):
    # Create or overwrite the output file
    output_file = open('fresh_tomatoes.html', 'w')

    # Replace the placeholder for the movie tiles with the actual dynamically generated content
    rendered_content = main_page_content.format(movie_tiles=create_movie_tiles_content(movies))

    # Output the file
    output_file.write(main_page_head + rendered_content)
    output_file.close()

    # open the output file in the browser
    url = os.path.abspath(output_file.name)
    webbrowser.open('file://' + url, new=2)     # open in a new tab, if possible

def main():
    open_movies_page(entertainment_center.movies)

if __name__ == "__main__":
    main()

<|endoftext|>


<commit_before>import math
from ophyd import (Device, EpicsMotor, Signal, Component as Cpt,
                   PseudoSingle, PseudoPositioner,
                   movr)

from ophyd.pseudopos import (real_position_argument,
                             pseudo_position_argument)

from hxntools.device import NamedDevice

def remove_names_maybe(obj, names):
    for n in names:
        try:
            obj.read_attrs.remove(n)
        except ValueError:
            pass
    return obj

# NOTE: NamedDevice will name components exactly as the 'name' argument
#       specifies. Normally, it would be named based on the parent

class HxnMLLSample(NamedDevice):
    ssx = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssx}Mtr', doc='fine_x')
    ssy = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssy}Mtr', doc='fine_y')
    ssz = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssz}Mtr', doc='fine_z')
    sth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:1-Ax:0}Mtr', doc='theta')

    sxx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', doc='coarse x')
    sy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:0}Mtr', doc='coarse y')
    sx1 = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:1}Mtr', doc='coarse x1')
    sz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', doc='coarse z')

    kill = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1}KillAll-Cmd.PROC', doc='kill all piezos')
    zero = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1}KillZero-Cmd.PROC', doc='zero all piezos')
    # sz1 = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:3}Mtr', doc='coarse z1')
    # sz1 was replaced with vz when controller 2 died

smll = HxnMLLSample('', name='smll')
ssx = smll.ssx
ssy = smll.ssy
ssz = smll.ssz

sth = smll.sth

sx = smll.sxx
sy = smll.sy
sx1 = smll.sx1
sz = smll.sz
# sz1 = smll.sz1

smll = remove_names_maybe(smll, ['kill', 'zero'])

class HxnMLLDiffractionSample(NamedDevice):
    '''MLL diffraction sample scanning stages'''
    dsy = Cpt(EpicsMotor, 'XF:03IDC-ES{MCS:1-Ax:mlldiffy}Mtr', doc='dsy')
    dsx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:2}Mtr', doc='dsx')
    dsz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:3}Mtr', doc='dsz')
    dsth = Cpt(EpicsMotor, 'XF:03IDC-ES{MCS:3-Ax:diffsth}Mtr', doc='dsth')
    sbx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', doc='sx')
    sbz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', doc='sz')
    # dssx and dssz are swapped, due to the installation. (01/17/17, H. Yan)
    dssz = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssx}Mtr', doc='fine_x')
    dssy = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssy}Mtr', doc='fine_y')
    dssx = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssz}Mtr', doc='fine_z')
    kill = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1-Diff}Kill-Cmd.PROC', doc='kill all piezos')

smlld = HxnMLLDiffractionSample('', name='smlld')
dssx = smlld.dssx
dssy = smlld.dssy
dssz = smlld.dssz
dsx = smlld.dsx
dsy = smlld.dsy
dsz = smlld.dsz
dsth = smlld.dsth
sbx = smlld.sbx
sbz = smlld.sbz

smlld = remove_names_maybe(smlld, ['kill','zero'])

class HxnAnc350_3(Device):
    '''3 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')

class HxnAnc350_4(Device):
    '''4 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')
    ax3 = Cpt(EpicsMotor, '-Ax:3}Mtr')

class HxnAnc350_6(Device):
    '''6 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')
    ax3 = Cpt(EpicsMotor, '-Ax:3}Mtr')
    ax4 = Cpt(EpicsMotor, '-Ax:4}Mtr')
    ax5 = Cpt(EpicsMotor, '-Ax:5}Mtr')

# Note that different controllers have different axis counts:
anc350_1 = HxnAnc350_6('XF:03IDC-ES{ANC350:1', name='anc350_1')
# anc350 controller 2 is being sent for repairs
# anc350_2 = HxnAnc350_6('XF:03IDC-ES{ANC350:2', name='anc350_2')
anc350_3 = HxnAnc350_4('XF:03IDC-ES{ANC350:3', name='anc350_3')
anc350_4 = HxnAnc350_6('XF:03IDC-ES{ANC350:4', name='anc350_4')
anc350_5 = HxnAnc350_6('XF:03IDC-ES{ANC350:5', name='anc350_5')
anc350_6 = HxnAnc350_6('XF:03IDC-ES{ANC350:6', name='anc350_6')
anc350_7 = HxnAnc350_3('XF:03IDC-ES{ANC350:7', name='anc350_7')
anc350_8 = HxnAnc350_3('XF:03IDC-ES{ANC350:8', name='anc350_8')

class HxnVerticalMLL(NamedDevice):
    # vx, vy now on controller 5
    vx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:3}Mtr', doc='coarse x')
    vy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:4}Mtr', doc='coarse y')
    # vz is now on controller 3
    vz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:3}Mtr', doc='coarse z')
    # vchi, vth now on controller 8
    vchi = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:3}Mtr', doc='chi')
    vth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:4}Mtr', doc='theta')

vmll = HxnVerticalMLL('', name='vmll')

class HxnHorizontalMLL(NamedDevice):
    # hx now on controller 8
    hx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:5}Mtr', doc='x')
    hy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:0}Mtr', doc='y')
    hz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:1}Mtr', doc='z')
    hth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:4}Mtr', doc='theta')

hmll = HxnHorizontalMLL('', name='hmll')

class HxnMLL_OSA(NamedDevice):
    osax = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:2}Mtr')
    osay = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:3}Mtr')
    osaz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:4}Mtr')

mllosa = HxnMLL_OSA('', name='mllosa')

class HxnMLLBeamStop(NamedDevice):
    bsx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:0}Mtr')
    bsy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:1}Mtr')

mllbs = HxnMLLBeamStop('', name='mllbs')

class PseudoAngleCorrection(PseudoPositioner, NamedDevice):
    '''Pseudo positioner definition for MLL coarse and fine sample positioners
    with angular correction
    '''

    def __init__(self, prefix, **kwargs):
        super().__init__(prefix, **kwargs)

        # if theta changes, update the pseudo position
        self.theta.subscribe(self.parameter_updated)

    def parameter_updated(self, value=None, **kwargs):
        self._update_position()

    @property
    def radian_theta(self):
        return math.radians(self.theta.get())

    @pseudo_position_argument
    def forward(self, position):
        theta = self.radian_theta
        c = math.cos(theta)
        s = math.sin(theta)

        x = c * position.px + s * position.pz
        z = -s * position.px + c * position.pz
        return self.RealPosition(x=x, z=z)

    @real_position_argument
    def inverse(self, position):
        theta = self.radian_theta
        c = math.cos(theta)
        s = math.sin(theta)
        x = c * position.x - s * position.z
        z = s * position.x + c * position.z
        return self.PseudoPosition(px=x, pz=z)

class PseudoMLLFineSample(PseudoAngleCorrection):
    # pseudo axes
    px = Cpt(PseudoSingle, name='pssx')
    pz = Cpt(PseudoSingle, name='pssz')

    # real axes
    x = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssx}Mtr', name='ssx')
    z = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssz}Mtr', name='ssz')

    # configuration settings
    theta = Cpt(Signal, value=15.0, name='pmllf_theta')

class PseudoMLLCoarseSample(PseudoAngleCorrection):
    # pseudo axes
    px = Cpt(PseudoSingle, name='psx')
    pz = Cpt(PseudoSingle, name='psz')

    # real axes
    x = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', name='sx')
    z = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', name='sz')

    # configuration settings
    theta = Cpt(Signal, value=15.0, name='pmllc_theta')

pmllf = PseudoMLLFineSample('', name='pmllf')
pssx = pmllf.px
pssz = pmllf.pz
# To tweak the angle, set pmllf.theta.put(15.1) for example

pmllc = PseudoMLLCoarseSample('', name='pmllc')
psx = pmllc.x
psz = pmllc.z

def movr_hth(angle):
    radian = angle * math.pi / 180.0
    correction = -1. * math.tan(radian) * 34376.6
    movr(hmll.th, angle)
    movr(hmll.coarse_x, correction)
<commit_msg>MNT: imports and whitespace<commit_after>import math
from ophyd import (Device, EpicsMotor, Signal, Component as Cpt,
                   PseudoSingle, PseudoPositioner, EpicsSignal)

from ophyd.pseudopos import (real_position_argument,
                             pseudo_position_argument)

from hxntools.device import NamedDevice

def remove_names_maybe(obj, names):
    for n in names:
        try:
            obj.read_attrs.remove(n)
        except ValueError:
            pass
    return obj

# NOTE: NamedDevice will name components exactly as the 'name' argument
#       specifies. Normally, it would be named based on the parent

class HxnMLLSample(NamedDevice):
    ssx = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssx}Mtr', doc='fine_x')
    ssy = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssy}Mtr', doc='fine_y')
    ssz = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssz}Mtr', doc='fine_z')
    sth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:1-Ax:0}Mtr', doc='theta')

    sxx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', doc='coarse x')
    sy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:0}Mtr', doc='coarse y')
    sx1 = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:1}Mtr', doc='coarse x1')
    sz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', doc='coarse z')

    kill = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1}KillAll-Cmd.PROC',
               doc='kill all piezos')
    zero = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1}KillZero-Cmd.PROC',
               doc='zero all piezos')
    # sz1 = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:3}Mtr', doc='coarse z1')
    # sz1 was replaced with vz when controller 2 died

smll = HxnMLLSample('', name='smll')
ssx = smll.ssx
ssy = smll.ssy
ssz = smll.ssz

sth = smll.sth

sx = smll.sxx
sy = smll.sy
sx1 = smll.sx1
sz = smll.sz
# sz1 = smll.sz1

smll = remove_names_maybe(smll, ['kill', 'zero'])

class HxnMLLDiffractionSample(NamedDevice):
    '''MLL diffraction sample scanning stages'''
    dsy = Cpt(EpicsMotor, 'XF:03IDC-ES{MCS:1-Ax:mlldiffy}Mtr', doc='dsy')
    dsx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:2}Mtr', doc='dsx')
    dsz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:3}Mtr', doc='dsz')
    dsth = Cpt(EpicsMotor, 'XF:03IDC-ES{MCS:3-Ax:diffsth}Mtr', doc='dsth')
    sbx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', doc='sx')
    sbz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', doc='sz')
    # dssx and dssz are swapped, due to the installation. (01/17/17, H. Yan)
    dssz = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssx}Mtr', doc='fine_x')
    dssy = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssy}Mtr', doc='fine_y')
    dssx = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-dssz}Mtr', doc='fine_z')
    kill = Cpt(EpicsSignal, 'XF:03IDC-ES{Ppmac:1-Diff}Kill-Cmd.PROC',
               doc='kill all piezos')

smlld = HxnMLLDiffractionSample('', name='smlld')
dssx = smlld.dssx
dssy = smlld.dssy
dssz = smlld.dssz
dsx = smlld.dsx
dsy = smlld.dsy
dsz = smlld.dsz
dsth = smlld.dsth
sbx = smlld.sbx
sbz = smlld.sbz

smlld = remove_names_maybe(smlld, ['kill', 'zero'])

class HxnAnc350_3(Device):
    '''3 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')

class HxnAnc350_4(Device):
    '''4 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')
    ax3 = Cpt(EpicsMotor, '-Ax:3}Mtr')

class HxnAnc350_6(Device):
    '''6 axis ANC350'''
    ax0 = Cpt(EpicsMotor, '-Ax:0}Mtr')
    ax1 = Cpt(EpicsMotor, '-Ax:1}Mtr')
    ax2 = Cpt(EpicsMotor, '-Ax:2}Mtr')
    ax3 = Cpt(EpicsMotor, '-Ax:3}Mtr')
    ax4 = Cpt(EpicsMotor, '-Ax:4}Mtr')
    ax5 = Cpt(EpicsMotor, '-Ax:5}Mtr')

# Note that different controllers have different axis counts:
anc350_1 = HxnAnc350_6('XF:03IDC-ES{ANC350:1', name='anc350_1')
# anc350 controller 2 is being sent for repairs
# anc350_2 = HxnAnc350_6('XF:03IDC-ES{ANC350:2', name='anc350_2')
anc350_3 = HxnAnc350_4('XF:03IDC-ES{ANC350:3', name='anc350_3')
anc350_4 = HxnAnc350_6('XF:03IDC-ES{ANC350:4', name='anc350_4')
anc350_5 = HxnAnc350_6('XF:03IDC-ES{ANC350:5', name='anc350_5')
anc350_6 = HxnAnc350_6('XF:03IDC-ES{ANC350:6', name='anc350_6')
anc350_7 = HxnAnc350_3('XF:03IDC-ES{ANC350:7', name='anc350_7')
anc350_8 = HxnAnc350_3('XF:03IDC-ES{ANC350:8', name='anc350_8')

class HxnVerticalMLL(NamedDevice):
    # vx, vy now on controller 5
    vx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:3}Mtr', doc='coarse x')
    vy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:4}Mtr', doc='coarse y')
    # vz is now on controller 3
    vz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:3}Mtr', doc='coarse z')
    # vchi, vth now on controller 8
    vchi = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:3}Mtr', doc='chi')
    vth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:4}Mtr', doc='theta')

vmll = HxnVerticalMLL('', name='vmll')

class HxnHorizontalMLL(NamedDevice):
    # hx now on controller 8
    hx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:8-Ax:5}Mtr', doc='x')
    hy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:0}Mtr', doc='y')
    hz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:1}Mtr', doc='z')
    hth = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:6-Ax:4}Mtr', doc='theta')

hmll = HxnHorizontalMLL('', name='hmll')

class HxnMLL_OSA(NamedDevice):
    osax = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:2}Mtr')
    osay = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:3}Mtr')
    osaz = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:4}Mtr')

mllosa = HxnMLL_OSA('', name='mllosa')

class HxnMLLBeamStop(NamedDevice):
    bsx = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:0}Mtr')
    bsy = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:5-Ax:1}Mtr')

mllbs = HxnMLLBeamStop('', name='mllbs')

class PseudoAngleCorrection(PseudoPositioner, NamedDevice):
    '''Pseudo positioner definition for MLL coarse and fine sample positioners
    with angular correction
    '''

    def __init__(self, prefix, **kwargs):
        super().__init__(prefix, **kwargs)

        # if theta changes, update the pseudo position
        self.theta.subscribe(self.parameter_updated)

    def parameter_updated(self, value=None, **kwargs):
        self._update_position()

    @property
    def radian_theta(self):
        return math.radians(self.theta.get())

    @pseudo_position_argument
    def forward(self, position):
        theta = self.radian_theta
        c = math.cos(theta)
        s = math.sin(theta)

        x = c * position.px + s * position.pz
        z = -s * position.px + c * position.pz
        return self.RealPosition(x=x, z=z)

    @real_position_argument
    def inverse(self, position):
        theta = self.radian_theta
        c = math.cos(theta)
        s = math.sin(theta)
        x = c * position.x - s * position.z
        z = s * position.x + c * position.z
        return self.PseudoPosition(px=x, pz=z)

class PseudoMLLFineSample(PseudoAngleCorrection):
    # pseudo axes
    px = Cpt(PseudoSingle, name='pssx')
    pz = Cpt(PseudoSingle, name='pssz')

    # real axes
    x = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssx}Mtr', name='ssx')
    z = Cpt(EpicsMotor, 'XF:03IDC-ES{Ppmac:1-ssz}Mtr', name='ssz')

    # configuration settings
    theta = Cpt(Signal, value=15.0, name='pmllf_theta')

class PseudoMLLCoarseSample(PseudoAngleCorrection):
    # pseudo axes
    px = Cpt(PseudoSingle, name='psx')
    pz = Cpt(PseudoSingle, name='psz')

    # real axes
    x = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:4-Ax:5}Mtr', name='sx')
    z = Cpt(EpicsMotor, 'XF:03IDC-ES{ANC350:3-Ax:2}Mtr', name='sz')

    # configuration settings
    theta = Cpt(Signal, value=15.0, name='pmllc_theta')

pmllf = PseudoMLLFineSample('', name='pmllf')
pssx = pmllf.px
pssz = pmllf.pz
# To tweak the angle, set pmllf.theta.put(15.1) for example

pmllc = PseudoMLLCoarseSample('', name='pmllc')
psx = pmllc.x
psz = pmllc.z

def movr_hth(angle):
    radian = angle * math.pi / 180.0
    correction = -1. * math.tan(radian) * 34376.6
    movr(hmll.th, angle)
    movr(hmll.coarse_x, correction)
<|endoftext|>


<commit_before>import math
import rospy
import tf2_ros as tf2
from geometry_msgs.msg import PointStamped

from bitbots_head_behavior.actions.look_at import AbstractLookAt

class SearchRecentBall(AbstractLookAt):
    """
    This action looks at the last position the ball has been seen
    and starts searching it from this position on.
    """

    def __init__(self, dsd, blackboard, parameters=None):
        super(SearchRecentBall, self).__init__(dsd, blackboard, parameters)
        self._config = self.blackboard.config['search_recent_ball']

        self._pan_speed = self._config['pan_speed']
        self._tilt_speed = self._config['tilt_speed']

        self._ball_time_out = rospy.Duration.from_sec(self._config['ball_search_time'])

        self._offset_pattern = self._config['offset_pattern']

        self._threshold = self.blackboard.config['position_reached_threshold']

        # Get the coresponding motor goals for the ball position
        self._recent_ball_motor_goals = self._get_head_goals_for_recent_ball()

        # Check if a ball exists
        if self._recent_ball_motor_goals is None:
            rospy.loginfo("No ball seen. So we are not able to seaarch for it.", logger_name="search_recent_ball")
            return self.pop()

        # Check if the ball is too old
        if rospy.Time.now() - self.blackboard.world_model.ball_last_seen() > self._ball_time_out):
            rospy.loginfo("Ball is too old to search for it. Let's forget it.", logger_name="search_recent_ball")
            return self.pop()

        # Init pattern index
        self.index = 0

    def _get_head_goals_for_recent_ball(self):
        """
        Returns the head motor goals to look at the most recent ball position.

        :retruns tuple(head_pan, head_tilt): The head motor goals
        """
        # Check if Ball has been seen
        if not self.blackboard.world_model.ball_seen:
            return

         # Get last ball position
        point = self.blackboard.world_model.get_ball_stamped()

        # Transform the points reference frame to be the head
        try:
            point = self.blackboard.head_capsule.tf_buffer.transform(point, self.head_tf_frame, timeout=rospy.Duration(0.9))
        except tf2.LookupException as e:
            rospy.logwarn('The frame {} is not being published (LookupException)'.format(self.head_tf_frame))
            return
        except tf2.ConnectivityException as e:
            rospy.logwarn('The transforms {} and {} are not connected in the TF Tree (ConnectivityException)'.format(point.header.frame_id, self.head_tf_frame))
            return
        except tf2.ExtrapolationException as e:
            rospy.logwarn('The transform {} is currently not available (ExtrapolationException)'.format(self.head_tf_frame))
            return

        motor_goals = self.get_motor_goals_from_point(point.point)
        return motor_goals

    def perform(self, reevaluate=False):
        """
        Call look_at to look at the point which our world-model determines to be the ball

        :param reevaluate: No effect here
        """

        current_head_pan, current_head_tilt = self.blackboard.head_capsule.get_head_position()

        # Add offset pattern to last ball position
        head_motor_goal_pan = self._recent_ball_motor_goals + self._offset_pattern[self.index][0]
        head_motor_goal_tilt = self._recent_ball_motor_goals + self._offset_pattern[self.index][1]

        #TODO add clipping otherwise the robot gets stuck at max positions

        self.blackboard.head_capsule.send_motor_goals(head_motor_goal_pan, head_motor_goal_tilt, pan_speed=self._pan_speed, tilt_speed=self._tilt_speed)

        # Distance between the current and the goal position
        distance = math.sqrt((current_head_pan - head_motor_goal_pan) ** 2 + (current_head_tilt - head_motor_goal_tilt) ** 2)

        # Exit action if pattern is finished
        if self.index >= len(self._offset_pattern):
            return self.pop()

        # Increment index when position is reached
        if distance < math.radians(self._threshold):
            self.index += 1
<commit_msg>Head Bahavior: Fix radians bug<commit_after>import math
import rospy
import tf2_ros as tf2
from geometry_msgs.msg import PointStamped

from bitbots_head_behavior.actions.look_at import AbstractLookAt

class SearchRecentBall(AbstractLookAt):
    """
    This action looks at the last position the ball has been seen
    and starts searching it from this position on.
    """

    def __init__(self, dsd, blackboard, parameters=None):
        super(SearchRecentBall, self).__init__(dsd, blackboard, parameters)
        self._config = self.blackboard.config['search_recent_ball']

        self._pan_speed = self._config['pan_speed']
        self._tilt_speed = self._config['tilt_speed']

        self._ball_time_out = rospy.Duration.from_sec(self._config['ball_search_time'])

        self._offset_pattern = self._config['offset_pattern']

        self._threshold = self.blackboard.config['position_reached_threshold']

        # Get the coresponding motor goals for the ball position
        self._recent_ball_motor_goals = self._get_head_goals_for_recent_ball()

        # Check if a ball exists
        if self._recent_ball_motor_goals is None:
            rospy.loginfo("No ball seen. So we are not able to seaarch for it.", logger_name="search_recent_ball")
            return self.pop()

        # Check if the ball is too old
        if rospy.Time.now() - self.blackboard.world_model.ball_last_seen() > self._ball_time_out):
            rospy.loginfo("Ball is too old to search for it. Let's forget it.", logger_name="search_recent_ball")
            return self.pop()

        # Init pattern index
        self.index = 0

    def _get_head_goals_for_recent_ball(self):
        """
        Returns the head motor goals to look at the most recent ball position.

        :retruns tuple(head_pan, head_tilt): The head motor goals
        """
        # Check if Ball has been seen
        if not self.blackboard.world_model.ball_seen:
            return

         # Get last ball position
        point = self.blackboard.world_model.get_ball_stamped()

        # Transform the points reference frame to be the head
        try:
            point = self.blackboard.head_capsule.tf_buffer.transform(point, self.head_tf_frame, timeout=rospy.Duration(0.9))
        except tf2.LookupException as e:
            rospy.logwarn('The frame {} is not being published (LookupException)'.format(self.head_tf_frame))
            return
        except tf2.ConnectivityException as e:
            rospy.logwarn('The transforms {} and {} are not connected in the TF Tree (ConnectivityException)'.format(point.header.frame_id, self.head_tf_frame))
            return
        except tf2.ExtrapolationException as e:
            rospy.logwarn('The transform {} is currently not available (ExtrapolationException)'.format(self.head_tf_frame))
            return

        motor_goals = self.get_motor_goals_from_point(point.point)
        return motor_goals

    def perform(self, reevaluate=False):
        """
        Call look_at to look at the point which our world-model determines to be the ball

        :param reevaluate: No effect here
        """

        current_head_pan, current_head_tilt = self.blackboard.head_capsule.get_head_position()

        # Add offset pattern to last ball position
        head_motor_goal_pan = self._recent_ball_motor_goals[0] + math.radians(self._offset_pattern[self.index][0])
        head_motor_goal_tilt = self._recent_ball_motor_goals[1] + math.radians(self._offset_pattern[self.index][1])

        #TODO add clipping otherwise the robot gets stuck at max positions

        self.blackboard.head_capsule.send_motor_goals(head_motor_goal_pan, head_motor_goal_tilt, pan_speed=self._pan_speed, tilt_speed=self._tilt_speed)

        # Distance between the current and the goal position
        distance = math.sqrt((current_head_pan - head_motor_goal_pan) ** 2 + (current_head_tilt - head_motor_goal_tilt) ** 2)

        # Exit action if pattern is finished
        if self.index >= len(self._offset_pattern):
            return self.pop()

        # Increment index when position is reached
        if distance < math.radians(self._threshold):
            self.index += 1
<|endoftext|>


<commit_before>#!/usr/bin/env python3

# Python implementations of HALCON's radial distortion models.
# These are intended to work both element-wise for numpy arrays,
# and also for scalars.
# Transcribed from the documentation for the "calibrate_cameras" HALCON operator.
#
# I performed some manual common subexpresson elimination since
# numpy isn't a compiler and won't do CSE.

import numpy
from numpy import sqrt
from numba import jit

halcon_model_types = ['area_scan_division', 'area_scan_polynomial']

def undistort_division(u_tilde, v_tilde, kappa):
    """
    From the HALCON Docs:

    The division model uses one parameter () to model the radial distortions.

    The following equations transform the distorted image plane coordinates into undistorted image plane coordinates if the division model is used """
    r_tilde_squared = u_tilde**2 + v_tilde**2
    scaling = 1.0 / (1.0 + kappa * r_tilde_squared)
    u = scaling * u_tilde
    v = scaling * v_tilde
    return u, v

@jit
def distort_division(u, v, kappa):
    """
    From the HALCON Docs:

    These equations can be inverted analytically, which leads to the following equations that transform undistorted coordinates into distorted coordinates:
    kappa = 0 means no distortion.
    """
    r_squared = u**2 + v**2
    temp = 1.0 - 4.0 * kappa * r_squared
    scaling = 2.0 / (1.0 + sqrt(temp))
    #print('scaling=',scaling)
    u_tilde = scaling * u
    v_tilde = scaling * v
    return u_tilde, v_tilde

@jit
def undistort_halcon_polynomial(u_tilde, v_tilde, k1, k2, k3, p1, p2):
    """
    From the HALCON Docs:
    The polynomial model uses three parameters () to model
    the radial distortions and two parameters () to model
    the decentering distortions.

    The following equations transform the distorted image
    plane coordinates into undistorted image plane coordinates
    if the polynomial model is used:

    These equations cannot be inverted analytically. Therefore,
    distorted image plane coordinates must be calculated from
    undistorted image plane coordinates numerically.

    k1=k2=k3=p1=p2=0 means no distortion.
    """
    u_tilde_to_2 = u_tilde**2
    v_tilde_to_2 = v_tilde**2
    r_to_2 = u_tilde_to_2 + v_tilde_to_2
    r_to_4 = r_to_2**2
    r_to_6 = r_to_4 * r_to_2
    temp1 =  k1 * r_to_2
    temp1 += k2 * r_to_4
    temp1 += k3 * r_to_6
    u = u_tilde + u_tilde * temp1  # the radial part
    v = v_tilde + v_tilde * temp1
    uv_tilde = u_tilde * v_tilde
    u += p1 * (r_to_2 + 2 * u_tilde_to_2) + 2 * p2 * uv_tilde # The tilt part
    v += 2 * p1 * uv_tilde + p2 * (r_to_2 + 2 * v_tilde_to_2)
    return u,v

@jit
def distort_halcon_polynomial(u,v,k1,k2,k3,p1,p2):
    #u_tilde,v_tilde = undistort_polynomial(u, v, k1, k2, k3, p1, p2)
    assert False, "Not implemented yet. Requires iterative solution"
    return u_tilde,v_tilde

def project_and_distort_simple(point_3d, camera_parameters):
    """ Simplified api that takes a 3d point, applies extrinsics, intrinsics,
    and radial distortion to return a 2d image point corresponding to a 3d point.
    This implementation is not for speed critical use; see project_and_distort. """
    R = camera_parameters['R']
    T = camera_parameters['T']
    kappa = camera_parameters['kappa']
    f_mm = camera_parameters['f'] * 1000.0 # Because project_and_distort takes mm
    image_height = camera_parameters['image_height']
    image_width = camera_parameters['image_width']
    pixel_h_mm = camera_parameters['pixel_h'] * 1000.0
    pixel_w_mm = camera_parameters['pixel_w'] * 1000.0
    cx = camera_parameters['cx']
    cy = camera_parameters['cy']
    x,y,z = numpy.dot(R,point_3d) + T
    assert z*1000.0 > f_mm, 'A voxel is behind the image plane of a camera!'
    u, v = project_and_distort(x, y, z, f_mm, image_height, image_width,
                               pixel_h_mm, pixel_w_mm, cx, cy, kappa)
    return u,v

@jit
def project_and_distort(x, y, z, f_mm, sensor_h, sensor_w, pixel_h_mm, pixel_w_mm, cx,
                        cy, kappa):
    """ Project a 3D point into a sensor plane and
    simulate lens distortion to get (sub)pixel coordinates.
    This applies the camera's intrinsic / internal parameters.
    The caller is responsible for first applying the cameras extrinsic / external parameters
    to get the point's location in the camera frame.

    Note that the definition of the projection/distortion process is consistent with HALCON,
    but there exist other conventions, so be careful!. i.e. HALCON's radial distortion is applied
    to coordinates in the image plane (in m); I have seen other groups do radal distortion on the
    pixel coordinates.

    Inputs:
    x,y,z - real world coordinates in the camera frame. Scale doesn't matter.
    f_mm - focal length of the lens in mm! This is to be consistent with giplib.
    sensor_h,sensor_w - height and width of the sensor in pixels
    pixel_h,pixel_w - height and width of a sensor pixel in mm! This is to be consistent with giplib.
    cx,cy - the center of the sensor optical axis in pixels
    kappa - the division model radial distortion parameter as defined by halcon.  """
    f_meters = f_mm * 0.001
    x_projected = x * f_meters / z
    y_projected = y * f_meters / z
    # z_projected = f_meters

    # Apply radial distortion in the image plane
    u = x_projected
    v = y_projected
    #if kappa != 0.0 and kappa != -0.0:
    #print('Non-zero Kappa! Applying radial distortion!')
    #u_tilde, v_tilde = distort_division(u, v, kappa)
    #else:
    #u_tilde, v_tilde = u, v
    u_tilde, v_tilde = distort_division(u, v, kappa)

    # Convert to pixel (sub) coordinates
    u_pixel = u_tilde / (pixel_w_mm * .001) + cx
    v_pixel = v_tilde / (pixel_h_mm * .001) + cy

    #assert False

    return u_pixel, v_pixel

# Check division model invertibility for a specific camera
def check_distortion_model_invertability(intrinsics):
    u_grid = numpy.arange(intrinsics['ImageWidth']).astype(numpy.float64)
    v_grid = numpy.arange(intrinsics['ImageHeight']).astype(numpy.float64)
    u, v= numpy.meshgrid(u_grid,v_grid,sparse=False)
    u -= intrinsics['Cx']
    v -= intrinsics['Cy']
    u *= intrinsics['Sx'] # in the HALCON .dat files it seems the values are in meters
    v *= intrinsics['Sy']
    kappa = intrinsics['Kappa']

    # Distort and undistort
    u_tilde,v_tilde = distort_division(u,v,kappa)
    from numpy import all, abs
    assert not all(abs(u_tilde-u)<intrinsics['Sx']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    assert not all(abs(v_tilde-v)<intrinsics['Sy']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    u2,v2 = undistort_division(u_tilde,v_tilde,kappa)
    eps = .001*intrinsics['Sx'] # a thousandth of a pixel
    assert all(abs(u-u2)<eps) and all(abs(v-v2)<eps), 'Camera intrinsics are not invertible on the image domain!'

    # Undistort then Distort
    u_tilde,v_tilde = undistort_division(u,v,kappa)
    assert not all(abs(u_tilde-u)<intrinsics['Sx']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    assert not all(abs(v_tilde-v)<intrinsics['Sy']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    u2,v2 = distort_division(u_tilde,v_tilde,kappa)
    assert all(abs(u-u2)<eps) and all(abs(v-v2)<eps), 'Camera intrinsics are not invertible on the image domain!'

if __name__=='__main__':
    test_division_model_invertability(1)
    test_division_model_invertability(20)
<commit_msg>Add function for triangulation of 3d from 2d point sets<commit_after>#!/usr/bin/env python3

# Python implementations of HALCON's radial distortion models.
# These are intended to work both element-wise for numpy arrays,
# and also for scalars.
# Transcribed from the documentation for the "calibrate_cameras" HALCON operator.
#
# I performed some manual common subexpresson elimination since
# numpy isn't a compiler and won't do CSE.

import numpy
from numpy import sqrt
from numba import jit

halcon_model_types = ['area_scan_division', 'area_scan_polynomial']

def undistort_division(u_tilde, v_tilde, kappa):
    """
    From the HALCON Docs:

    The division model uses one parameter () to model the radial distortions.

    The following equations transform the distorted image plane coordinates into undistorted image plane coordinates if the division model is used """
    r_tilde_squared = u_tilde**2 + v_tilde**2
    scaling = 1.0 / (1.0 + kappa * r_tilde_squared)
    u = scaling * u_tilde
    v = scaling * v_tilde
    return u, v

@jit
def distort_division(u, v, kappa):
    """
    From the HALCON Docs:

    These equations can be inverted analytically, which leads to the following equations that transform undistorted coordinates into distorted coordinates:
    kappa = 0 means no distortion.
    """
    r_squared = u**2 + v**2
    temp = 1.0 - 4.0 * kappa * r_squared
    scaling = 2.0 / (1.0 + sqrt(temp))
    #print('scaling=',scaling)
    u_tilde = scaling * u
    v_tilde = scaling * v
    return u_tilde, v_tilde

@jit
def undistort_halcon_polynomial(u_tilde, v_tilde, k1, k2, k3, p1, p2):
    """
    From the HALCON Docs:
    The polynomial model uses three parameters () to model
    the radial distortions and two parameters () to model
    the decentering distortions.

    The following equations transform the distorted image
    plane coordinates into undistorted image plane coordinates
    if the polynomial model is used:

    These equations cannot be inverted analytically. Therefore,
    distorted image plane coordinates must be calculated from
    undistorted image plane coordinates numerically.

    k1=k2=k3=p1=p2=0 means no distortion.
    """
    u_tilde_to_2 = u_tilde**2
    v_tilde_to_2 = v_tilde**2
    r_to_2 = u_tilde_to_2 + v_tilde_to_2
    r_to_4 = r_to_2**2
    r_to_6 = r_to_4 * r_to_2
    temp1 =  k1 * r_to_2
    temp1 += k2 * r_to_4
    temp1 += k3 * r_to_6
    u = u_tilde + u_tilde * temp1  # the radial part
    v = v_tilde + v_tilde * temp1
    uv_tilde = u_tilde * v_tilde
    u += p1 * (r_to_2 + 2 * u_tilde_to_2) + 2 * p2 * uv_tilde # The tilt part
    v += 2 * p1 * uv_tilde + p2 * (r_to_2 + 2 * v_tilde_to_2)
    return u,v

@jit
def distort_halcon_polynomial(u,v,k1,k2,k3,p1,p2):
    #u_tilde,v_tilde = undistort_polynomial(u, v, k1, k2, k3, p1, p2)
    assert False, "Not implemented yet. Requires iterative solution"
    return u_tilde,v_tilde

def project_and_distort_simple(point_3d, camera_parameters):
    """ Simplified api that takes a 3d point, applies extrinsics, intrinsics,
    and radial distortion to return a 2d image point corresponding to a 3d point.
    This implementation is not for speed critical use; see project_and_distort. """
    R = camera_parameters['R']
    T = camera_parameters['T']
    kappa = camera_parameters['kappa']
    f_mm = camera_parameters['f'] * 1000.0 # Because project_and_distort takes mm
    image_height = camera_parameters['image_height']
    image_width = camera_parameters['image_width']
    pixel_h_mm = camera_parameters['pixel_h'] * 1000.0
    pixel_w_mm = camera_parameters['pixel_w'] * 1000.0
    cx = camera_parameters['cx']
    cy = camera_parameters['cy']
    x,y,z = numpy.dot(R,point_3d) + T
    assert z*1000.0 > f_mm, 'A voxel is behind the image plane of a camera!'
    u, v = project_and_distort(x, y, z, f_mm, image_height, image_width,
                               pixel_h_mm, pixel_w_mm, cx, cy, kappa)
    return u,v

@jit
def project_and_distort(x, y, z, f_mm, sensor_h, sensor_w, pixel_h_mm, pixel_w_mm, cx,
                        cy, kappa):
    """ Project a 3D point into a sensor plane and
    simulate lens distortion to get (sub)pixel coordinates.
    This applies the camera's intrinsic / internal parameters.
    The caller is responsible for first applying the cameras extrinsic / external parameters
    to get the point's location in the camera frame.

    Note that the definition of the projection/distortion process is consistent with HALCON,
    but there exist other conventions, so be careful!. i.e. HALCON's radial distortion is applied
    to coordinates in the image plane (in m); I have seen other groups do radial distortion on the
    pixel coordinates.

    Inputs:
    x,y,z - real world coordinates in the camera frame. Scale doesn't matter.
    f_mm - focal length of the lens in mm! This is to be consistent with giplib.
    sensor_h,sensor_w - height and width of the sensor in pixels
    pixel_h,pixel_w - height and width of a sensor pixel in mm! This is to be consistent with giplib.
    cx,cy - the center of the sensor optical axis in pixels
    kappa - the division model radial distortion parameter as defined by halcon.  """
    f_meters = f_mm * 0.001
    x_projected = x * f_meters / z
    y_projected = y * f_meters / z
    # z_projected = f_meters

    # Apply radial distortion in the image plane
    u = x_projected
    v = y_projected
    #if kappa != 0.0 and kappa != -0.0:
    #print('Non-zero Kappa! Applying radial distortion!')
    #u_tilde, v_tilde = distort_division(u, v, kappa)
    #else:
    #u_tilde, v_tilde = u, v
    u_tilde, v_tilde = distort_division(u, v, kappa)

    # Convert to pixel (sub) coordinates
    u_pixel = u_tilde / (pixel_w_mm * .001) + cx
    v_pixel = v_tilde / (pixel_h_mm * .001) + cy

    #assert False

    return u_pixel, v_pixel

def triangulate(camera_point_tuples):
    """ Takes (camera parameters, 2d point) tuples and computes the closest
        3d point to the viewing rays.
        This is solves the inverse problem of project_and_distort_simple."""
    points_on_the_lines = []
    direction_vectors = []
    assert(len(camera_point_tuples)) >= 2, 'There are not enough points to triangulate!'
    for camera_parameters,(u_pixel,v_pixel) in camera_point_tuples:
        # Extract two points per ray in world coordinates.
        R = camera_parameters['R'] # R,T, map world coorinates into camera coordinates
        T = camera_parameters['T']
        R_,T_ = R.T,numpy.dot(-R.T,T) # map camera coordinates into world coordinates
        kappa = camera_parameters['kappa']
        f_m = camera_parameters['f'] # Because project_and_distort takes mm
        image_height = camera_parameters['image_height']
        image_width = camera_parameters['image_width']
        pixel_h_m = camera_parameters['pixel_h']
        pixel_w_m = camera_parameters['pixel_w']
        cx = camera_parameters['cx']
        cy = camera_parameters['cy']
        u_tilde = (u_pixel - cx) * pixel_w_m
        v_tilde = (v_pixel - cy) * pixel_h_m
        u,v = undistort_division(u_tilde, v_tilde, kappa)
        #point1_camera = numpy.array([0,0,0])
        point1_world = camera_center = T_ # in world coordinates since R_*[[0],[0],[0]] + T_ = T_
        point2_camera = numpy.array((u,v,f_m))
        point2_world = numpy.dot(R_,point2_camera)+T_
        direction_world = point2_world - point1_world
        points_on_the_lines.append(point1_world)
        direction_vectors.append(direction_world)
    from plaroma3d.camera_geometry import point_closest_to_several_lines
    point_3d_world = point_closest_to_several_lines(points=points_on_the_lines,
                                              directions=direction_vectors)
    point_to_line_distances = []
    for p,d in zip(points_on_the_lines, direction_vectors):
        n = d / numpy.linalg.norm(d) # normalized direction
        point_3d_p = point_3d_world - p # center on a point on the line
        # To compute distance, consider right triangle formed by the projection...
        distance = numpy.sqrt(numpy.dot(point_3d_p,point_3d_p) - (numpy.dot(point_3d_p,n))**2)
        point_to_line_distances.append(distance)

    return point_3d_world, point_to_line_distances

# Check division model invertibility for a specific camera
def check_distortion_model_invertability(intrinsics):
    u_grid = numpy.arange(intrinsics['ImageWidth']).astype(numpy.float64)
    v_grid = numpy.arange(intrinsics['ImageHeight']).astype(numpy.float64)
    u, v= numpy.meshgrid(u_grid,v_grid,sparse=False)
    u -= intrinsics['Cx']
    v -= intrinsics['Cy']
    u *= intrinsics['Sx'] # in the HALCON .dat files it seems the values are in meters
    v *= intrinsics['Sy']
    kappa = intrinsics['Kappa']

    # Distort and undistort
    u_tilde,v_tilde = distort_division(u,v,kappa)
    from numpy import all, abs
    assert not all(abs(u_tilde-u)<intrinsics['Sx']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    assert not all(abs(v_tilde-v)<intrinsics['Sy']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    u2,v2 = undistort_division(u_tilde,v_tilde,kappa)
    eps = .001*intrinsics['Sx'] # a thousandth of a pixel
    assert all(abs(u-u2)<eps) and all(abs(v-v2)<eps), 'Camera intrinsics are not invertible on the image domain!'

    # Undistort then Distort
    u_tilde,v_tilde = undistort_division(u,v,kappa)
    assert not all(abs(u_tilde-u)<intrinsics['Sx']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    assert not all(abs(v_tilde-v)<intrinsics['Sy']), 'Warning: Distortion is at most sub-pixel! Probably a bug!'
    u2,v2 = distort_division(u_tilde,v_tilde,kappa)
    assert all(abs(u-u2)<eps) and all(abs(v-v2)<eps), 'Camera intrinsics are not invertible on the image domain!'

if __name__=='__main__':
    test_division_model_invertability(1)
    test_division_model_invertability(20)
<|endoftext|>