import re
from datetime import date, datetime, time, timedelta, timezone

import pytest
from dirty_equals import HasRepr

from pydantic import BaseModel, FutureDate, PastDate, ValidationError, condate

from .conftest import Err

def create_tz(minutes):
    return timezone(timedelta(minutes=minutes))

@pytest.fixture(scope='module', name='DateModel')
def date_model_fixture():
    class DateModel(BaseModel):
        d: date

    return DateModel

@pytest.mark.parametrize(
    'value,result',
    [
        # Valid inputs
        (1_493_942_400, date(2017, 5, 5)),
        (1_493_942_400_000, date(2017, 5, 5)),
        (0, date(1970, 1, 1)),
        ('2012-04-23', date(2012, 4, 23)),
        (b'2012-04-23', date(2012, 4, 23)),
        (date(2012, 4, 9), date(2012, 4, 9)),
        (datetime(2012, 4, 9, 0, 0), date(2012, 4, 9)),
        # Invalid inputs
        (datetime(2012, 4, 9, 12, 15), Err('Datetimes provided to dates should have zero time - e.g. be exact dates')),
        ('x20120423', Err('Input should be a valid date or datetime, input is too short')),
        ('2012-04-56', Err('Input should be a valid date or datetime, day value is outside expected range')),
        (19_999_958_400, date(2603, 10, 11)),  # just before watershed
        (20000044800, Err('type=date_from_datetime_inexact,')),  # just after watershed
        (1_549_238_400, date(2019, 2, 4)),  # nowish in s
        (1_549_238_400_000, date(2019, 2, 4)),  # nowish in ms
        (1_549_238_400_000_000, Err('Input should be a valid date or datetime, dates after 9999')),  # nowish in μs
        (1_549_238_400_000_000_000, Err('Input should be a valid date or datetime, dates after 9999')),  # nowish in ns
        ('infinity', Err('Input should be a valid date or datetime, input is too short')),
        (float('inf'), Err('Input should be a valid date or datetime, dates after 9999')),
        (int('1' + '0' * 100), Err('Input should be a valid date or datetime, dates after 9999')),
        (1e1000, Err('Input should be a valid date or datetime, dates after 9999')),
        (float('-infinity'), Err('Input should be a valid date or datetime, dates before 1600')),
        (float('nan'), Err('Input should be a valid date or datetime, NaN values not permitted')),
    ],
)
def test_date_parsing(DateModel, value, result):
    if isinstance(result, Err):
        with pytest.raises(ValidationError, match=result.message_escaped()):
            DateModel(d=value)
    else:
        assert DateModel(d=value).d == result

@pytest.fixture(scope='module', name='TimeModel')
def time_model_fixture():
    class TimeModel(BaseModel):
        d: time

    return TimeModel

@pytest.mark.parametrize(
    'value,result',
    [
        # Valid inputs
        ('09:15:00', time(9, 15)),
        ('10:10', time(10, 10)),
        ('10:20:30.400', time(10, 20, 30, 400_000)),
        (b'10:20:30.400', time(10, 20, 30, 400_000)),
        (time(4, 8, 16), time(4, 8, 16)),
        (3610, time(1, 0, 10)),
        (3600.5, time(1, 0, 0, 500000)),
        (86400 - 1, time(23, 59, 59)),
        # Invalid inputs
        ('4:8:16', Err('Input should be in a valid time format, invalid character in hour [type=time_parsing,')),
        (86400, Err('Input should be in a valid time format, numeric times may not exceed 86,399 seconds')),
        ('xxx', Err('Input should be in a valid time format, input is too short [type=time_parsing,')),
        ('091500', Err('Input should be in a valid time format, invalid time separator, expected `:`')),
        (b'091500', Err('Input should be in a valid time format, invalid time separator, expected `:`')),
        ('09:15:90', Err('Input should be in a valid time format, second value is outside expected range of 0-59')),
        ('11:05:00Y', Err('Input should be in a valid time format, unexpected extra characters at the end of the inp')),
        # # https://github.com/pydantic/speedate/issues/10
        # ('11:05:00-05:30', time(11, 5, 0, tzinfo=create_tz(-330))),
        # ('11:05:00-0530', time(11, 5, 0, tzinfo=create_tz(-330))),
        # ('11:05:00Z', time(11, 5, 0, tzinfo=timezone.utc)),
        # ('11:05:00+00', time(11, 5, 0, tzinfo=timezone.utc)),
        # ('11:05-06', time(11, 5, 0, tzinfo=create_tz(-360))),
        # ('11:05+06', time(11, 5, 0, tzinfo=create_tz(360))),
        # ('11:05:00-25:00', errors.TimeError),
    ],
)
def test_time_parsing(TimeModel, value, result):
    if isinstance(result, Err):
        with pytest.raises(ValidationError, match=result.message_escaped()):
            TimeModel(d=value)
    else:
        assert TimeModel(d=value).d == result

@pytest.fixture(scope='module', name='DatetimeModel')
def datetime_model_fixture():
    class DatetimeModel(BaseModel):
        dt: datetime

    return DatetimeModel

@pytest.mark.parametrize(
    'value,result',
    [
        # Valid inputs
        # values in seconds
        (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309)),
        (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24)),
        # values in ms
        (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24)),
        ('2012-04-23T09:15:00', datetime(2012, 4, 23, 9, 15)),
        ('2012-04-23T09:15:00Z', datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)),
        ('2012-04-23T10:20:30.400+02:30', datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))),
        ('2012-04-23T10:20:30.400+02:00', datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))),
        ('2012-04-23T10:20:30.400-02:00', datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
        (b'2012-04-23T10:20:30.400-02:00', datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
        (datetime(2017, 5, 5), datetime(2017, 5, 5)),
        (0, datetime(1970, 1, 1, 0, 0, 0)),
        # # Invalid inputs
        ('1494012444.883309', Err('Input should be a valid datetime, invalid date separator')),
        ('1494012444', Err('Input should be a valid datetime, invalid date separator')),
        (b'1494012444', Err('Input should be a valid datetime, invalid date separator')),
        ('1494012444000.883309', Err('Input should be a valid datetime, invalid date separator')),
        ('-1494012444000.883309', Err('Input should be a valid datetime, invalid character in year')),
        ('2012-4-9 4:8:16', Err('Input should be a valid datetime, invalid character in month')),
        ('x20120423091500', Err('Input should be a valid datetime, invalid character in year')),
        ('2012-04-56T09:15:90', Err('Input should be a valid datetime, day value is outside expected range')),
        ('2012-04-23T11:05:00-25:00', Err('Input should be a valid datetime, timezone offset must be less than 24 ho')),
        (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19)),  # just before watershed
        (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000)),  # just after watershed
        (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0)),  # nowish in s
        (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000)),  # nowish in ms
        (1_549_316_052_104_324, Err('Input should be a valid datetime, dates after 9999')),  # nowish in μs
        (1_549_316_052_104_324_096, Err('Input should be a valid datetime, dates after 9999')),  # nowish in ns
        ('infinity', Err('Input should be a valid datetime, input is too short')),
        (float('inf'), Err('Input should be a valid datetime, dates after 9999')),
        (float('-inf'), Err('Input should be a valid datetime, dates before 1600')),
        (1e50, Err('Input should be a valid datetime, dates after 9999')),
        (float('nan'), Err('Input should be a valid datetime, NaN values not permitted')),
    ],
)
def test_datetime_parsing(DatetimeModel, value, result):
    if isinstance(result, Err):
        with pytest.raises(ValidationError, match=result.message_escaped()):
            DatetimeModel(dt=value)
    else:
        assert DatetimeModel(dt=value).dt == result

@pytest.fixture(scope='module', name='TimedeltaModel')
def timedelta_model_fixture():
    class TimedeltaModel(BaseModel):
        d: timedelta

    return TimedeltaModel

@pytest.mark.parametrize(
    'delta',
    [
        timedelta(days=4, minutes=15, seconds=30, milliseconds=100),  # fractions of seconds
        timedelta(hours=10, minutes=15, seconds=30),  # hours, minutes, seconds
        timedelta(days=4, minutes=15, seconds=30),  # multiple days
        timedelta(days=1, minutes=00, seconds=00),  # single day
        timedelta(days=-4, minutes=15, seconds=30),  # negative durations
        timedelta(minutes=15, seconds=30),  # minute & seconds
        timedelta(seconds=30),  # seconds
    ],
)
def test_parse_python_format(TimedeltaModel, delta):
    assert TimedeltaModel(d=delta).d == delta
    # assert TimedeltaModel(d=str(delta)).d == delta

@pytest.mark.parametrize(
    'value,result',
    [
        # seconds
        (timedelta(seconds=30), timedelta(seconds=30)),
        (30, timedelta(seconds=30)),
        (30.1, timedelta(seconds=30, milliseconds=100)),
        (9.9e-05, timedelta(microseconds=99)),
        # minutes seconds
        ('00:15:30', timedelta(minutes=15, seconds=30)),
        ('00:05:30', timedelta(minutes=5, seconds=30)),
        # hours minutes seconds
        ('10:15:30', timedelta(hours=10, minutes=15, seconds=30)),
        ('01:15:30', timedelta(hours=1, minutes=15, seconds=30)),
        # ('100:200:300', timedelta(hours=100, minutes=200, seconds=300)),
        # days
        ('4d,00:15:30', timedelta(days=4, minutes=15, seconds=30)),
        ('4d,10:15:30', timedelta(days=4, hours=10, minutes=15, seconds=30)),
        # fractions of seconds
        ('00:15:30.1', timedelta(minutes=15, seconds=30, milliseconds=100)),
        ('00:15:30.01', timedelta(minutes=15, seconds=30, milliseconds=10)),
        ('00:15:30.001', timedelta(minutes=15, seconds=30, milliseconds=1)),
        ('00:15:30.0001', timedelta(minutes=15, seconds=30, microseconds=100)),
        ('00:15:30.00001', timedelta(minutes=15, seconds=30, microseconds=10)),
        ('00:15:30.000001', timedelta(minutes=15, seconds=30, microseconds=1)),
        (b'00:15:30.000001', timedelta(minutes=15, seconds=30, microseconds=1)),
        # negative
        ('-4d,00:15:30', timedelta(days=-4, minutes=-15, seconds=-30)),
        (-172800, timedelta(days=-2)),
        ('-00:15:30', timedelta(minutes=-15, seconds=-30)),
        ('-01:15:30', timedelta(hours=-1, minutes=-15, seconds=-30)),
        (-30.1, timedelta(seconds=-30, milliseconds=-100)),
        # iso_8601
        ('30', Err('Input should be a valid timedelta, "day" identifier')),
        ('P4Y', timedelta(days=1460)),
        ('P4M', timedelta(days=120)),
        ('P4W', timedelta(days=28)),
        ('P4D', timedelta(days=4)),
        ('P0.5D', timedelta(hours=12)),
        ('PT5H', timedelta(hours=5)),
        ('PT5M', timedelta(minutes=5)),
        ('PT5S', timedelta(seconds=5)),
        ('PT0.000005S', timedelta(microseconds=5)),
        (b'PT0.000005S', timedelta(microseconds=5)),
    ],
)
def test_parse_durations(TimedeltaModel, value, result):
    if isinstance(result, Err):
        with pytest.raises(ValidationError, match=result.message_escaped()):
            TimedeltaModel(d=value)
    else:
        assert TimedeltaModel(d=value).d == result

@pytest.mark.parametrize(
    'field, value, error_message',
    [
        ('dt', [], 'Input should be a valid datetime'),
        ('dt', {}, 'Input should be a valid datetime'),
        ('dt', object, 'Input should be a valid datetime'),
        ('d', [], 'Input should be a valid date'),
        ('d', {}, 'Input should be a valid date'),
        ('d', object, 'Input should be a valid date'),
        ('t', [], 'Input should be a valid time'),
        ('t', {}, 'Input should be a valid time'),
        ('t', object, 'Input should be a valid time'),
        ('td', [], 'Input should be a valid timedelta'),
        ('td', {}, 'Input should be a valid timedelta'),
        ('td', object, 'Input should be a valid timedelta'),
    ],
)
def test_model_type_errors(field, value, error_message):
    class Model(BaseModel):
        dt: datetime = None
        d: date = None
        t: time = None
        td: timedelta = None

    with pytest.raises(ValidationError) as exc_info:
        Model(**{field: value})
    assert len(exc_info.value.errors()) == 1
    error = exc_info.value.errors()[0]
    assert error['msg'] == error_message

@pytest.mark.parametrize('field', ['dt', 'd', 't', 'dt'])
def test_unicode_decode_error(field):
    class Model(BaseModel):
        dt: datetime = None
        d: date = None
        t: time = None
        td: timedelta = None

    with pytest.raises(ValidationError) as exc_info:
        Model(**{field: b'\x81\x81\x81\x81\x81\x81\x81\x81'})
    assert exc_info.value.error_count() == 1
    # errors vary

def test_nan():
    class Model(BaseModel):
        dt: datetime
        d: date

    with pytest.raises(ValidationError) as exc_info:
        Model(dt=float('nan'), d=float('nan'))
    # insert_assert(exc_info.value.errors())
    assert exc_info.value.errors() == [
        {
            'type': 'datetime_parsing',
            'loc': ('dt',),
            'msg': 'Input should be a valid datetime, NaN values not permitted',
            'input': HasRepr('nan'),
            'ctx': {'error': 'NaN values not permitted'},
        },
        {
            'type': 'date_from_datetime_parsing',
            'loc': ('d',),
            'msg': 'Input should be a valid date or datetime, NaN values not permitted',
            'input': HasRepr('nan'),
            'ctx': {'error': 'NaN values not permitted'},
        },
    ]

@pytest.mark.parametrize(
    'constraint,msg,ok_value,error_value',
    [
        ('gt', 'greater than', date(2020, 1, 2), date(2019, 12, 31)),
        ('gt', 'greater than', date(2020, 1, 2), date(2020, 1, 1)),
        ('ge', 'greater than or equal to', date(2020, 1, 2), date(2019, 12, 31)),
        ('ge', 'greater than or equal to', date(2020, 1, 1), date(2019, 12, 31)),
        ('lt', 'less than', date(2019, 12, 31), date(2020, 1, 2)),
        ('lt', 'less than', date(2019, 12, 31), date(2020, 1, 1)),
        ('le', 'less than or equal to', date(2019, 12, 31), date(2020, 1, 2)),
        ('le', 'less than or equal to', date(2020, 1, 1), date(2020, 1, 2)),
    ],
)
def test_date_constraints(constraint, msg, ok_value, error_value):
    class Model(BaseModel):
        a: condate(**{constraint: date(2020, 1, 1)})

    assert Model(a=ok_value).dict() == {'a': ok_value}

    with pytest.raises(ValidationError, match=re.escape(f'Input should be {msg} 2020-01-01')):
        Model(a=error_value)

@pytest.mark.parametrize(
    'value,result',
    (
        ('1996-01-22', date(1996, 1, 22)),
        (date(1996, 1, 22), date(1996, 1, 22)),
    ),
)
def test_past_date_validation_success(value, result):
    class Model(BaseModel):
        foo: PastDate

    assert Model(foo=value).foo == result

@pytest.mark.parametrize(
    'value',
    (
        date.today(),
        date.today() + timedelta(1),
        '2064-06-01',
    ),
)
def test_past_date_validation_fails(value):
    class Model(BaseModel):
        foo: PastDate

    with pytest.raises(ValidationError) as exc_info:
        Model(foo=value)
    # insert_assert(exc_info.value.errors())
    assert exc_info.value.errors() == [
        {
            'type': 'date_past',
            'loc': ('foo',),
            'msg': 'Date should be in the past',
            'input': value,
        }
    ]

@pytest.mark.parametrize(
    'value,result',
    (
        (date.today() + timedelta(1), date.today() + timedelta(1)),
        ('2064-06-01', date(2064, 6, 1)),
    ),
)
def test_future_date_validation_success(value, result):
    class Model(BaseModel):
        foo: FutureDate

    assert Model(foo=value).foo == result

@pytest.mark.parametrize(
    'value',
    (
        date.today(),
        date.today() - timedelta(1),
        '1996-01-22',
    ),
)
def test_future_date_validation_fails(value):
    class Model(BaseModel):
        foo: FutureDate

    with pytest.raises(ValidationError) as exc_info:
        Model(foo=value)
    # insert_assert(exc_info.value.errors())
    assert exc_info.value.errors() == [
        {
            'type': 'date_future',
            'loc': ('foo',),
            'msg': 'Date should be in the future',
            'input': value,
        }
    ]


import logging

from .base import Phase
from ..items import State

LOG = logging.getLogger("pubtools.pulp")

class Update(Phase):
    """Update phase.

    This phase ensures any per-item mutable fields in Pulp are set to their
    desired values.

    Input queue:
    - items which exist in Pulp but might have some mutable fields which do
      not match desired values.

    Output queue:
    - items which exist in Pulp and have all mutable fields matching their
      desired values.

    Side-effects:
    - mutates unit fields (under pulp_user_metadata) in Pulp.
    """

    def __init__(self, context, pulp_client, in_queue, **_):
        super(Update, self).__init__(
            context, in_queue=in_queue, name="Update items in Pulp"
        )
        self.pulp_client = pulp_client

    def run(self):
        no_update_needed = 0
        update_needed = 0

        for item in self.iter_input():
            if item.pulp_state not in State.NEEDS_UPDATE:
                # This item is already up-to-date in Pulp (or just doesn't support
                # being updated)
                no_update_needed += 1
                self.put_output(item)
            else:
                # This item needs an update.
                update_needed += 1
                self.put_future_output(item.ensure_uptodate(self.pulp_client))

        LOG.info(
            "Update: %s item(s) already up-to-date, %s updating",
            no_update_needed,
            update_needed,
        )


import math
import pygame

# it's messy but gets the job done
# performing calculations on 2160 objects is as hefty of a task as it might seem.

SCREENWIDTH, SCREENHEIGHT = 900,770

WAVECOUNT = 2

EQUILIBRIUM_LINE_1_YCOORD = SCREENHEIGHT//7
EQUILIBRIUM_LINE_2_YCOORD = 3*(SCREENHEIGHT//7)
EQUILIBRIUM_LINE_3_YCOORD = 11*(SCREENHEIGHT//14)

white = (255,255,255)
black = (0,0,0)

class Point(pygame.sprite.Sprite):
    def __init__(self,x,y,vel, group):
        super().__init__()
        self.image = pygame.Surface([3,3])
        self.rect = self.image.get_rect()
        self.image.fill(white)

        self.rect.centerx = x
        self.rect.centery = y
        self.dx = vel

        group.add(self)

    def update(self):
        if self in progressive:
            self.rect.x -= self.dx
            if self.rect.x < 90:
                self.rect.x = SCREENWIDTH-92
            if self.rect.x > SCREENWIDTH-92:
                self.rect.x = 90

        if self in stationary:
            alist = [f.rect.centery for f in progressive if f.rect.centerx == self.rect.centerx]
            if len(alist) == 2:
                coord1 = EQUILIBRIUM_LINE_2_YCOORD-alist[0]
                coord2 = EQUILIBRIUM_LINE_1_YCOORD-alist[1]
                coord3 = coord1+coord2
                self.rect.centery = EQUILIBRIUM_LINE_3_YCOORD-coord3

pygame.init()
screen = pygame.display.set_mode([SCREENWIDTH,SCREENHEIGHT])

progressive = pygame.sprite.Group()
stationary = pygame.sprite.Group()
clock = pygame.time.Clock()

for f in range(int(WAVECOUNT*360)):
    Point(f+90,(2*(SCREENHEIGHT//14))-int((80*(math.sin(math.radians(f))))), 1, progressive)
    Point(f+90,(6*(SCREENHEIGHT//14))-int((80*(math.sin(math.radians(f))))), -1, progressive)
    Point(f+90,11*(SCREENHEIGHT//14), 0, stationary)

done = False
while not done:
    clock.tick(120)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                done = True

    progressive.update()
    stationary.update()

    screen.fill(black)

    pygame.draw.line(screen, white, (90,EQUILIBRIUM_LINE_1_YCOORD), (SCREENWIDTH-92,EQUILIBRIUM_LINE_1_YCOORD), 1)
    pygame.draw.line(screen, white, (90,EQUILIBRIUM_LINE_2_YCOORD), (SCREENWIDTH-92,EQUILIBRIUM_LINE_2_YCOORD), 1)
    pygame.draw.line(screen, white, (90,EQUILIBRIUM_LINE_3_YCOORD), (SCREENWIDTH-92,EQUILIBRIUM_LINE_3_YCOORD), 1)

    progressive.draw(screen)
    stationary.draw(screen)
    pygame.display.flip()
pygame.quit()


from tensorflow.keras.models import load_model
import nltk
from nltk.stem import WordNetLemmatizer
import random
import json
import pickle
import numpy as np

nltk.download('punkt', quiet=True)
nltk.download('wordnet', quiet=True)

words = []
classes = []
intent_methods = {}
model = load_model(f'test_model.h5')
lemmatizer = WordNetLemmatizer()
words = pickle.load(open(f'test_model_words.pkl', 'rb'))
classes = pickle.load(open(f'test_model_classes.pkl', 'rb'))
intents = json.loads(open(f'intents.json').read())

def _clean_up_sentence( sentence):
    sentence_words = nltk.word_tokenize(sentence)
    sentence_words = [lemmatizer.lemmatize(word.lower()) for word in sentence_words]
    return sentence_words

def _bag_of_words( sentence, words):
    sentence_words = _clean_up_sentence(sentence)
    bag = [0] * len(words)
    for s in sentence_words:
        for i, word in enumerate(words):
            if word == s:
                bag[i] = 1
    return np.array(bag)

def _predict_class( sentence):
    p = _bag_of_words(sentence, words)
    res = model.predict(np.array([p]))[0]
    ERROR_THRESHOLD = 0.1
    results = [[i, r] for i, r in enumerate(res) if r > ERROR_THRESHOLD]

    results.sort(key=lambda x: x[1], reverse=True)
    return_list = []
    for r in results:
        return_list.append({'intent': classes[r[0]], 'probability': str(r[1])})
    return return_list

def _get_response( ints, intents_json):
    try:
        tag = ints[0]['intent']
        list_of_intents = intents_json['intents']
        for i in list_of_intents:
            if i['tag']  == tag:
                result = random.choice(i['responses'])
                break
    except IndexError:
        result = "I don't understand!"
    return result

def requested( message):
    ints = _predict_class(message)
    if ints[0]['intent'] in intent_methods.keys():
        intent_methods[ints[0]['intent']]()
    else:
        return _get_response(ints, intents)


import sys
from openpyxl import Workbook
import NXOpen
import NXOpen.CAM

theSession = NXOpen.Session.GetSession()
workPart = theSession.Parts.Work
theUI = NXOpen.UI.GetUI()
tools = []

def main():
    rootGroup = workPart.CAMSetup.GetRoot(NXOpen.CAM.CAMSetup.View.ProgramOrder)
    obj = NXOpen.CAM.NCGroup.GetMembers(rootGroup)
    parsing_group(obj)
    wb = Workbook()

    ws =  wb.active
    ws.title = "Changed Sheet"

    ws = wb.worksheets[0]
    for i , tool in enumerate(tools):
        ws[f'A{i+1}'] = tool
        #lw(tool)
    wb.save(filename = r'C:\Temp\sample_book.xlsx')

def parsing_group(obj):
    for tagged in obj:
        if not tagged == 'NONE':
            if workPart.CAMSetup.IsGroup(tagged):
                group = NXOpen.CAM.NCGroup.GetMembers(tagged)
                parsing_group(group)
            else:
                add_tool(tagged)

def add_tool(operation):
    cutting_tool = operation.GetParent(NXOpen.CAM.CAMSetup.View.MachineTool)
    cutting_tool = cutting_tool.Name
    if not cutting_tool in tools:
        tools.append(cutting_tool)
    pass

def notinuse():
    num = theUI.SelectionManager.GetNumSelectedObjects()
    objects1 = [NXOpen.CAM.CAMObject.Null] * num

    for i in range(num):

        objects1[i] = theUI.SelectionManager.GetSelectedTaggedObject(i)
        name_op = objects1[i].Name
        cutting_tool = objects1[i].GetParent(NXOpen.CAM.CAMSetup.View.MachineTool)
        cutting_tool_list = cutting_tool.Name

    # Create a Workbook
    wb = Workbook()

    ws =  wb.active
    ws.title = "Changed Sheet"

    ws = wb.worksheets[0]
    ws['A1'] = name_op
    ws['A2'] = cutting_tool_list

    wb.save(filename = r'C:\Temp\sample_book.xlsx')

if __name__ == '__main__':
    main()


from tornado.web import RequestHandler, url
from tornado.escape import utf8, json_encode

from .flash import FlashMessagesMixin

class Handler(RequestHandler, FlashMessagesMixin):
    """Medoly Request Handler class

    Extends:
        RequestHandler
        FlashMessagesMixin
    """

    def prepare(self):
        """Perpare request process
        """
        self.hooks.run('on_start_request', self)
        self.on_start_request()

    def on_start_request(self):
        """Process hook after ``on_start_request''
        """
        pass

    def get_template_namespace(self):
        """Returns a dictionary to be used as the default template namespace.
        May be overridden by subclasses to add or modify values.
        The results of this method will be combined with additional
        defaults in the `tornado.template` module and keyword arguments
        to `render` or `render_string`.
        """
        namespace = dict(
            handler=self,
            flash=self.get_flashed_messages,
            request=self.request,
            current_user=self.current_user,
            locale=self.locale,
            _=self.locale.translate,
            pgettext=self.locale.pgettext,
            static_url=self.static_url,
            xsrf_form_html=self.xsrf_form_html,
            reverse_url=self.reverse_url
        )
        return namespace

    def jsonify(self, json_obj):
        """Json data write

        Arguments:
            json_obj {object} -- the data can json encoding

        Raises:
            RuntimeError --  run time exception when  request had finshed
        """
        if self._finished:
            raise RuntimeError("Cannot jsonify() after finish().  May be caused "
                               "by using async operations without the "
                               "@asynchronous decorator.")
        chunk = json_encode(json_obj)
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        chunk = utf8(chunk)
        self._write_buffer.append(chunk)

    def render(self, template_name, **kwargs):
        """Renders the template with the given arguments as the response."""
        html = self.render_string(template_name, **kwargs)
        self.finish(html)

    def on_finish(self):
        """Hook pointer process  on request finshing
        """
        self.on_end_request()
        self.hooks.run('on_end_request', self)

    def on_end_request(self):
        """Custom request handler hook on end request
        """
        pass

class RenderHandler(Handler):
    """Render Handler class """

    def initialize(self, template):
        """Initialize and bind tempalte

        Arguments:
            template {str} -- tempalte path
        """
        self.template = template

    def get(self, *args):
        """Render template
        """
        self.render(self.template)


import numpy as np
import matplotlib.pyplot as plt

##### 	Fonction permettant d'afficher un jeu de donnee ####
#
#
def plotData(X,y):
	plt.scatter(X, y, c="g", alpha=0.8, marker='o')
	plt.ylabel("y")
	plt.xlabel("x")

	plt.show(block=False)

def plotData2D(X,y):
	plt.scatter(X[:,0],X[:,1], c=y, alpha=0.8, marker='o')
	plt.ylabel("x_0")
	plt.xlabel("x_1")
	plt.axis([-15, 15, -15, 15])

	plt.show(block=False)


# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:light
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.0
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# + [markdown] Collapsed="false" tags=[] jp-MarkdownHeadingCollapsed=true
# # Analysis of the hyperparameter simulations

# + Collapsed="false" tags=[]
import numpy as np
#import lmoments_rm as lr
import scipy
import matplotlib.pyplot as plt
import itertools
import xarray as xr
import pandas as pd

# +
import sys

def is_interactive():
    import __main__ as main
    return not hasattr(main, '__file__')

from sys import argv

if is_interactive():
    params = [0]
else:
    print(argv)
    ss=argv[1:]
    print(ss)
    params = [float(i) for i in ss]

print(params)
print(is_interactive())

# + [markdown] tags=[]
# # Load the data on the fit

# +
#
data1 = pd.read_csv('bhis1_mse.txt',  delim_whitespace=True)
data2 = pd.read_csv('tt2.txt',  delim_whitespace=True)
print(data1['a'][0])

n1 = data2['b'].array
n2=n1[0:150]

# sort data according to cost value
err=data1['g'].array

#i=np.argsort(n2,kind='stable')
#j=np.sort(n2,kind='stable')

i1=np.argsort(err,kind='stable')
j1=np.sort(err,kind='stable')

# list best 20 fits
i1s=i1[0:20]

layers=data1[['d','e','f']]
alayers=layers.to_numpy()

for m in range(0,20):
    print(alayers[i1[m]],j1[m],n2[i1[m]],data1['h'][m],data1['i'][m])
# -

plt.plot(j1[:],n2[i1[:]],'o')

# +
#
data1 = pd.read_csv('bhis0_mse.txt',  delim_whitespace=True)
data2 = pd.read_csv('tt2.txt',  delim_whitespace=True)
print(data1['a'][0])

n1 = data2['b'].array
n2=n1[0:150]

# sort data according to cost value
err=data1['g'].array

#i=np.argsort(n2,kind='stable')
#j=np.sort(n2,kind='stable')

i1=np.argsort(err,kind='stable')
j1=np.sort(err,kind='stable')

# list best 20 fits
i1s=i1[0:20]

layers=data1[['d','e','f']]
alayers=layers.to_numpy()

for m in range(0,20):
    print(alayers[i1[m]],j1[m],n2[i1[m]],data1['h'][m],data1['i'][m])
# -

plt.plot(j1[:],n2[i1[:]],'o')


import pandas as pd
import numpy as np
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, confusion_matrix

# Load the dataset
url = 'https://raw.githubusercontent.com/SK7here/Movie-Review-Sentiment-Analysis/master/IMDB-Dataset.csv'
data = pd.read_csv(url, nrows=250)

# Download required NLTK resources
nltk.download('stopwords')
nltk.download('punkt')
nltk.download('wordnet')

# Initialize the lemmatizer
lemmatizer = WordNetLemmatizer()

# Define a function to preprocess the text
def preprocess_text(text):

    # Tokenize the text
    tokens = word_tokenize(text)

    # Remove stopwords and non-alphabetic tokens
    words = [lemmatizer.lemmatize(token.lower()) for token in tokens if token.isalpha() and token.lower() not in stopwords.words('english')]

    # Join the words back into a single string
    preprocessed_text = ' '.join(words)

    return preprocessed_text

# Preprocess the movie reviews
data['preprocessed_review'] = data['review'].apply(preprocess_text)

# Initialize the count vectorizer
vectorizer = CountVectorizer(max_features=5000)

# Fit and transform the preprocessed reviews
X = vectorizer.fit_transform(data['preprocessed_review']).toarray()

# Get the sentiment labels
y = data['sentiment']

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Initialize the Naive Bayes classifier
clf = MultinomialNB()

# Train the classifier
clf.fit(X_train, y_train)

# Make predictions on the test set
y_pred = clf.predict(X_test)

# Calculate the accuracy of the classifier
accuracy = accuracy_score(y_test, y_pred)
print(f'Accuracy: {accuracy:.2f}')

# Display the confusion matrix
cm = confusion_matrix(y_test, y_pred)
print(f'Confusion Matrix:\n{cm}')

def analyze_sentiment(text, classifier, vectorizer):
    # Preprocess the input text
    preprocessed_text = preprocess_text(text)

    # Convert the preprocessed text to a feature vector
    X_input = vectorizer.transform([preprocessed_text]).toarray()

    # Predict the sentiment using the classifier
    sentiment = classifier.predict(X_input)[0]

    return sentiment

# Example usage of the analyze_sentiment function
new_review = 'A wonderful way to spend time on a too hot summer weekend. This is probably my all-time favorite movie!'
predicted_sentiment = analyze_sentiment(new_review, clf, vectorizer)
print(f'Predicted sentiment: {predicted_sentiment}')


#!/Users/tomar/Anaconda3/python.exe
"""
Created on Sat May 27 16:34:21 2017 by tomar
#!/usr/bin/python3
#!/Users/tomar/Anaconda3/python.exe
"""
import sys
import urllib
try:
	import gUtils
	import Users
except:
	sys.path.append("..")
	import gUtils
	import Users
import cgi
import random
import Songs

def renderHtml(x):
	# for greek
	if sys.stdout.encoding == 'UTF_8':
		print(x.encode('UTF_8', 'xmlcharrefreplace').decode('utf8'))
	else:
		print(x.encode('ascii', 'xmlcharrefreplace').decode('utf8'))
def encodeHtml(x):
	return urllib.parse.unquote(x, encoding='utf-8', errors='replace')
print("Content-type: text/html \n")
print('''
<title>SongBook</title>
<LINK REL='StyleSheet' HREF='/python/songs.css?{}'  TYPE='text/css' TITLE='ToMarStyle' MEDIA='screen'>
<LINK REL='StyleSheet' HREF='/python/tomar.css?{}'  TYPE='text/css' TITLE='ToMarStyle' MEDIA='screen'>
<link href='//fonts.googleapis.com/css?family=Didact Gothic' rel='stylesheet'>

<meta name="google-signin-client_id" content="932688745244-i4vfeap5jgu8id5dagrc49786vvs0qrf.apps.googleusercontent.com">

'''.format(random.randrange(9999), random.randrange(9999)))
print('''

    ×

filler

	''')
form = cgi.FieldStorage() # instantiate only once!
gid = form.getvalue('gId', '')	#remove default
name = form.getvalue('gName', '')	#remove default
gMail = form.getvalue('gMail', '')	#remove default
gImg = form.getvalue('gImage', '')	#remove default
oper = form.getvalue('oper','')
#gid = '106932376942135580175'
gUtils.toggleDivFunction()
print('''
<form name="gForm" method="POST" action="#">
<input type="hidden" name="gId" value="{}">
<input type="hidden" name="gName" value="{}">
<input type="hidden" name="gMail" value="{}">
<input type="hidden" name="gImage" value="{}">
<input type="hidden" name="oper">
</form>
'''.format(gid, name, gMail, gImg))
if gid == '':
	gUtils.googleSignIn()
else:
	users = Users.Users()
	authS = users.authenticate(gid, name, gMail, gImg, users.SONGBOOK)		# gets you into songbook public tags
	authA = users.authenticate(gid, name, gMail, gImg, users.ADMIN)			# gets you notes
	authT = users.authenticate(gid, name, gMail, gImg, users.TEA)			# gets you all tags and media
	authF = users.authenticate(gid, name, gMail, gImg, users.FF)			# gets you all tags and media
	if authA[0] == '1':
		perm = 3
	elif authT[0] == '1' and authF[0] == '1':
		perm = 3
	elif authT[0] == '1' or authF[0] == '1':
		perm = 2
	elif authS[0] == '1':
		perm = 1
	else:
		perm = 0
	#perm = 1						#uncomment to test permissions
	print(authS[1])
	if perm > 0:
		print("")
		songbook = Songs.Songs()
		print("")
		renderHtml(songbook.jsFunctions(perm))
		print("
")
		#print("in index, oper is {}, perm is {}
".format(oper, perm))
		if (oper[0] == "N"):
			renderHtml(songbook.displaySong(encodeHtml(oper[1:]), perm))
		else:
			if (oper[0] == "T"):
				results = songbook.displaySongList(songbook.tagDict[oper[1:2]][encodeHtml(oper[2:])], perm)
			elif (oper[0] == "D"):
				results = songbook.displaySongList(songbook.getSongsByDeck(encodeHtml(oper[1:])), perm)
			elif (oper[0] == "M"):
				results = songbook.displaySongList(songbook.marked, perm)
			renderHtml(''.format(results[3]))
			renderHtml(results[0])
	else:
		print('''
Welcome to ToMarGames Friends and Family!

It looks like you've landed on a page you don't have permission to access.
				''')
print('')


import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, roc_auc_score
import pandas as pd
import numpy as np
from sklearn.utils import shuffle
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LassoCV
from sklearn.model_selection import train_test_split,cross_val_score,KFold,RepeatedKFold,GridSearchCV
from scipy.stats import pearsonr,ttest_ind,levene
from sklearn.ensemble import RandomForestClassifier
from sklearn import svm

fp = '/Users/mac/Documents/clinical.xlsx'
data = pd.read_excel(fp)
data = shuffle(data)
data = data.fillna(0)
data_1 = data.loc[data['Label'].isin([0])] #筛选出Label值为0的行
data_2 = data.loc[data['Label'].isin([1])]

X = data[data.columns[1:]]
y = data['Label']
X = StandardScaler().fit_transform(X)
X = pd.DataFrame(X)
colNames = X.columns #
'''t检验特征筛选'''
index = []
for colName in data.columns[1:]:
    if levene(data_1[colName],data_2[colName])[1] > 0.05:
        if ttest_ind(data_1[colName],data_2[colName])[1]<0.05:
            index.append(colName)
    else:
        if ttest_ind(data_1[colName],data_2[colName],equal_var=False)[1]<0.05:
            index.append(colName)
print(len(index))

'''lasso特征筛选'''
if 'Label' not in index:index = ['Label']+index
data_1 = data_1[index]
data_2 = data_2[index]
data = pd.concat([data_1,data_2])
data = shuffle(data)
data.index = range(len(data)) #打乱后重新标号
X= data[data.columns[1:]]
y = data['Label']
X = X.apply(pd.to_numeric, errors='ignore') #将数据类型转化为数值型
colNames = X.columns
X = X.fillna(0)
X = X.astype(np.float64)
X = StandardScaler().fit_transform(X)
X = pd.DataFrame(X)
X.columns = colNames

alphas = np.logspace(-3,1,50)
model_lassoCV = LassoCV(alphas = alphas,cv=10,max_iter=100000).fit(X,y)
print(model_lassoCV.alpha_)
coef = pd.Series(model_lassoCV.coef_,index = X.columns)
print("Lasso picked "+str(sum(coef!=0)) + "variables and eliminated the other "+str(sum(coef==0)))

index = coef[coef !=0 ].index
X = X[index]
print(coef[coef != 0])  #查看经过t检验和lasso的特征值

'''randomForest分类'''
#将筛选出的特征分为训练集和测试集，其中，测试集占30%
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)
#模型训练
model_rf = RandomForestClassifier().fit(X_train,y_train)
#效果查看
score_rf = model_rf.score(X_test,y_test)
print(score_rf)

'''svm支持向量机分类（参数优化前）'''
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)
model_svm = svm.SVC(kernel='rbf',gamma='auto',probability=True).fit(X_train,y_train)
score_svm = model_svm.score(X_test,y_test)
print(score_svm)
'''参数优化params opt:svm'''
#一堆C，在2^-1到2^3中取10个（base不写默认底为10）
Cs = np.logspace(-1,3,10,base = 2)
#一堆γ
gammas = np.logspace(-4,1,50,base = 2)
param_grid = dict(C = Cs, gamma = gammas)
#cv=10  10则交叉验证，可以省略，函数会自动给一个值
grid = GridSearchCV(svm.SVC(kernel='rbf'),param_grid = param_grid,cv=10).fit(X,y)
print(grid.best_params_)
C = grid.best_params_['C']
gamma = grid.best_params_['gamma']
'''svm支持向量机分类（参数优化后）'''
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)
model_svm = svm.SVC(kernel='rbf',gamma=0.08901572837528345,C = 0.9258747122872903,probability=True).fit(X_train,y_train)
score_svm = model_svm.score(X_test,y_test)
print(score_svm)

'''roc'''
y_probs = model_svm.predict_proba(X) #prob概率，预测X中每个病例属于前一种和后一种的概率
#threshold是阈值  ；y的真实值，y的预测值（第1列（人看是第2列）），把阳性的标签定为1
fpr,tpr,thresholds = roc_curve(y,y_probs[:,1],pos_label=1)

#画图
plt.plot(fpr,tpr,marker = 'o')#以原点来标记点（具体的什么符号代表什么图形差资料）
plt.xlabel('fpr')#横坐标名字
plt.ylabel('tpr')
plt.show()
#auc：roc曲线下面积
auc_score = roc_auc_score(y,model_svm.predict(X))
print(auc_score)


from fastapi import APIRouter
from .schemas import RequestRegisterCashboxCharacter, ResponseRegisterCashboxCharacter, \
    ResponseGetSystemID
from .doc_kwargs import doc_register_character, doc_get_sys_id
from .functions import register_cashbox_character, get_sys_id

router = APIRouter()

@router.post('/register_character', **doc_register_character)
async def register_character(character: RequestRegisterCashboxCharacter):
    kwargs = {'valid_schema_data': character.dict()}
    data = await register_cashbox_character(**kwargs)
    return ResponseRegisterCashboxCharacter(**data)

# @router.post('/register_fiscal_cashier', **doc_register_fiscal_cashier)
# async def register_cashier(cashier_data: RequestRegisterFiscalCashier):
#     kwargs = {'valid_schema_data': cashier_data.dict()}
#     data = await register_fiscal_cashier(**kwargs)
#     return ResponseRegisterFiscalCashier(**data)
#

@router.get('/get_system_id', **doc_get_sys_id)
async def get_system_id():
    data = await get_sys_id()
    return ResponseGetSystemID(**data)


# -*- coding: utf-8 -*-
# 创建时间：2019年7月30日
# 直方图均衡化测试

import cv2
import numpy as np
from matplotlib import pyplot as plt
from time import clock
import sys

'''
# 进行图像均衡化demo
img_1 = cv2.imread('../images/alphabets/A.jpg', 0)
print(img_1)
equ_1 = cv2.equalizeHist(img_1)
res = np.hstack((img_1,equ_1))
res = cv2.resize(res,(1280,640))
cv2.imshow('hist_test',res)
cv2.waitKey(0)
'''
'''
# 画2D直方图
img_1 = cv2.imread('../images/test_rectangle.jpg')
img_1 = cv2.resize(img_1, (1280, 720))
hsv_1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2HSV)
hist_1 = cv2.calcHist([hsv_1], [0, 1], None, [180, 256], [0, 180, 0, 256])
cv2.imshow('rectangle', img_1)
plt.imshow(hist_1,interpolation = 'nearest')
plt.show()
cv2.waitKey(0)
'''
# 颜色直方图编码
if __name__ == '__main__':
    # 构建 HSV 颜色地图
    hsv_map = np.zeros((180, 256, 3), np.uint8)

    h, s = np.indices(hsv_map.shape[:2])
    hsv_map[:,:,0] = h
    hsv_map[:,:,1] = s
    hsv_map[:,:,2] = 255
    hsv_map = cv2.cvtColor(hsv_map, cv2.COLOR_HSV2BGR)
    cv2.imshow('hsv_map', hsv_map)
    cv2.namedWindow('hist', 0)

    hist_scale = 10
    def set_scale(val):
        global hist_scale
        hist_scale = val
    cv2.createTrackbar('scale', 'hist', hist_scale, 32, set_scale)
    # cap = cv2.VideoCapture('../video/test_rectangle_1.mp4')
    cap = cv2.VideoCapture(1)
    while True:
        flag, frame = cap.read()
        cv2.imshow('camera', frame)

        small = cv2.pyrDown(frame)
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        # 去除孤立的点
        dark = hsv[:, :, 2] < 32
        hsv[dark] = 0
        h = cv2.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )
        h = np.clip(h*0.005*hist_scale, 0, 1)

        # 从一维变成三维
        # 将得到的直方图和颜色直方图相乘
        vis = hsv_map*h[:, :, np.newaxis] / 255.0
        cv2.imshow('hist', vis)
        ch = 0xFF & cv2.waitKey(1)
        if ch == 27:
            break
cv2.destroyAllWindows()


import argparse
import getpass
import logging
import socket
import sys
from datetime import datetime

from simplekit import email

from recipe.utils import OptionParser
from recipe.utils import config_logging

EMAIL_API = 'http://10.16.75.24:3000/framework/v1/mail'

class Command(object):
    """this is the base class for all commands

    """
    name = None
    SNAP = {}
    parser = argparse.ArgumentParser(description='recipe')

    def __init__(self, options):
        self.options = options
        config_logging(options)
        self.config = OptionParser()
        email.settings.URL_EMAIL = self.config.get('email', 'api', EMAIL_API)
        self.logger = logging.getLogger('recipe')

    @staticmethod
    def register(cmd_class=None):
        """register the command

        :param cmd_class: `class`:`str`
        :return:
        """
        if cmd_class is None:
            parser = Command.parser = argparse.ArgumentParser(description='recipe')
            parser.add_argument('--verbose', '-v', action='count', default=2,
                                help='Give more output, Options is additive, and can be used up to 3 times.')
            parser.add_argument('--log', dest='log', default=None, help='Path to a verbose appending log')
            Command.sub_parser = parser.add_subparsers(help='Available commands', dest='command')
        else:
            if cmd_class.name not in Command.SNAP:
                cmd_class.register(Command.sub_parser)
                Command.SNAP[cmd_class.name] = cmd_class

    @staticmethod
    def parse(args=None):
        if args is None:
            args = sys.argv[1:]
        parser = Command.parser
        options = parser.parse_args(args)
        cmd = options.command
        if cmd in Command.SNAP:
            return Command.SNAP[cmd](options)

    def run(self):
        """ run this command

        :return:
        """
        raise NotImplementedError

    def execute(self):
        try:
            self.run()
        except Exception as e:
            self.logger.exception(e)
            # send email
            sender = 'Recipe@newegg.com'
            receiver = self.config.get('email', 'receiver', 'benjamin.c.yan@newegg.com')
            cc = self.config.get('email', 'cc')
            subject = "(Info) Recipe Error"
            body = "Hi, \n Recipe occur unknown error with login {0} on {1} at {2}".format(getpass.getuser(),
                                                                                socket.gethostname(),
                                                                                datetime.now().strftime(
                                                                                '%a %b %d %H:%M:%S %Y'))
            email.send_email(sender, receiver, subject, body, cc=cc, files=[self.options.log])
            sys.exit(3)


import numpy as np
import cv2
import csv
import os
import torch
from os import path, listdir
import gaze.gaze_heatmap as gh
import time

from baselines.common.trex_utils import normalize_state

import torch.nn.functional as F
import torch

cv2.ocl.setUseOpenCL(False)

# def normalize_state(obs):
#     return obs / 255.0

# def normalize(obs, max_val):
#     # TODO: discard frames with no gaze
#     if(max_val != 0):
#         norm_map = obs/float(max_val)
#     else:
#         norm_map = obs
#     return norm_map

# need to grayscale and warp to 84x84
def GrayScaleWarpImage(image):
    """Warp frames to 84x84 as done in the Nature paper and later work."""
    width = 84
    height = 84
    frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
    frame = cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)
    #frame = np.expand_dims(frame, -1)
    return frame

def MaxSkipAndWarpFrames(trajectory_dir, img_dirs, frames, actions):
    """take a trajectory file of frames and max over every 3rd and 4th observation"""
    num_frames = len(frames)
    assert(len(frames)==len(actions))
    # print('total images:', num_frames)
    skip = 4

    sample_pic = np.random.choice(
        listdir(path.join(trajectory_dir, img_dirs[0])))
    image_path = path.join(trajectory_dir, img_dirs[0], sample_pic)
    pic = cv2.imread(image_path)
    obs_buffer = np.zeros((2,)+pic.shape, dtype=np.uint8)
    max_frames = []
    skipped_actions = []
    for i in range(num_frames):
        # TODO: check that i should max before warping.
        img_name = frames[i] + ".png"
        img_dir = img_dirs[i]

        if i % skip == skip - 2:
            obs = cv2.imread(path.join(trajectory_dir, img_dir, img_name))

            obs_buffer[0] = obs
        if i % skip == skip - 1:
            obs = cv2.imread(path.join(trajectory_dir, img_dir, img_name))
            obs_buffer[1] = obs
            # Take the action of every 4th frame
            skipped_actions.append(actions[i])

            # warp max to 80x80 grayscale
            image = obs_buffer.max(axis=0)
            warped = GrayScaleWarpImage(image)
            max_frames.append(warped)

    assert(len(max_frames)==len(skipped_actions))
    return max_frames, skipped_actions

def StackFrames(frames, actions):
    import copy
    """stack every four frames to make an observation (84,84,4)"""
    stacked = []
    stacked_obs = np.zeros((84, 84, 4))
    stacked_actions = []
    for i in range(len(frames)):
        if i >= 3:
            stacked_obs[:, :, 0] = frames[i-3]
            stacked_obs[:, :, 1] = frames[i-2]
            stacked_obs[:, :, 2] = frames[i-1]
            stacked_obs[:, :, 3] = frames[i]
            stacked.append(np.expand_dims(copy.deepcopy(stacked_obs), 0))
            stacked_actions.append(actions[i-3]) #action for first frame in stack
    return stacked, stacked_actions

def MaxSkipGaze(gaze, heatmap_size):
    """take a list of gaze coordinates and max over every 3rd and 4th observation"""
    num_frames = len(gaze)
    skip = 4

    obs_buffer = np.zeros((2,)+(heatmap_size, heatmap_size), dtype=np.float32)
    max_frames = []
    for i in range(num_frames):
        g = gaze[i]
        g = np.squeeze(g)
        if i % skip == skip - 2:
            obs_buffer[0] = g
        if i % skip == skip - 1:
            obs_buffer[1] = g
            image = obs_buffer.max(axis=0)
            max_frames.append(image)
    if np.isnan(max_frames).any():
        print('nan max gaze map created')
        exit(1)

    return max_frames

def CollapseGaze(gaze_frames, heatmap_size):
    import copy
    """combine every four frames to make an observation (84,84)"""
    stacked = []
    stacked_obs = np.zeros((heatmap_size, heatmap_size))
    for i in range(len(gaze_frames)):
        if i >= 3:
            # Sum over the gaze frequency counts across four frames
            stacked_obs = gaze_frames[i-3]
            stacked_obs = stacked_obs + gaze_frames[i-2]
            stacked_obs = stacked_obs + gaze_frames[i-1]
            stacked_obs = stacked_obs + gaze_frames[i]

            # Normalize the gaze mask
            max_gaze_freq = np.amax(stacked_obs)
            #TODO: normalize gaze with softmax
            # stacked_obs = normalize(stacked_obs, max_gaze_freq)
            stacked_obs = torch.tensor(stacked_obs)
            norm_map = F.softmax(stacked_obs.view(1,-1), dim=1).view(heatmap_size,heatmap_size)
            norm_map = norm_map.cpu().detach().numpy()

            stacked.append(np.expand_dims(
                copy.deepcopy(stacked_obs), 0))  # shape: (1,7,7)

    return stacked

def MaxSkipReward(rewards):
    """take a list of rewards and max over every 3rd and 4th observation"""
    num_frames = len(rewards)
    skip = 4
    max_frames = []
    obs_buffer = np.zeros((2,))
    for i in range(num_frames):
        r = rewards[i]
        if i % skip == skip - 2:

            obs_buffer[0] = r
        if i % skip == skip - 1:

            obs_buffer[1] = r
            rew = obs_buffer.max(axis=0)
            max_frames.append(rew)
    return max_frames

def StackReward(rewards):
    import copy
    """combine every four frames to make an observation"""
    stacked = []
    stacked_obs = np.zeros((1,))
    for i in range(len(rewards)):
        if i >= 3:
            # Sum over the rewards across four frames
            stacked_obs = rewards[i-3]
            stacked_obs = stacked_obs + rewards[i-2]
            stacked_obs = stacked_obs + rewards[i-1]
            stacked_obs = stacked_obs + rewards[i]

            stacked.append(np.expand_dims(copy.deepcopy(stacked_obs), 0))
    return stacked

def get_sorted_traj_indices(env_name, dataset, use_gaze=False):
    # need to pick out a subset of demonstrations based on desired performance
    # first let's sort the demos by performance, we can use the trajectory number to index into the demos so just
    # need to sort indices based on 'score'
    game = env_name
    # Note, I'm also going to try only keeping the full demonstrations that end in terminal
    traj_indices = []
    traj_scores = []
    traj_dirs = []
    traj_rewards = []
    traj_gaze = []
    traj_frames = []
    traj_actions = []
    print('traj length: ', len(dataset.trajectories[game]))
    for t in dataset.trajectories[game]:
        traj_indices.append(t)
        traj_scores.append(dataset.trajectories[game][t][-1]['score'])
        # a separate img_dir defined for every frame of the trajectory as two different trials could comprise an episode
        traj_dirs.append([dataset.trajectories[game][t][i]['img_dir']
                          for i in range(len(dataset.trajectories[game][t]))])
        traj_rewards.append([dataset.trajectories[game][t][i]['reward']
                             for i in range(len(dataset.trajectories[game][t]))])
        if use_gaze:
            traj_gaze.append([dataset.trajectories[game][t][i]['gaze_positions']
                            for i in range(len(dataset.trajectories[game][t]))])
        traj_frames.append([dataset.trajectories[game][t][i]['frame']
                            for i in range(len(dataset.trajectories[game][t]))])
        traj_actions.append([dataset.trajectories[game][t][i]['action']
                            for i in range(len(dataset.trajectories[game][t]))])

    sorted_traj_indices = [x for _, x in sorted(
        zip(traj_scores, traj_indices), key=lambda pair: pair[0])]
    sorted_traj_scores = sorted(traj_scores)
    sorted_traj_dirs = [x for _, x in sorted(
        zip(traj_scores, traj_dirs), key=lambda pair: pair[0])]
    sorted_traj_rewards = [x for _, x in sorted(
        zip(traj_scores, traj_rewards), key=lambda pair: pair[0])]
    if use_gaze:
        sorted_traj_gaze = [x for _, x in sorted(
            zip(traj_scores, traj_gaze), key=lambda pair: pair[0])]
    else:
        sorted_traj_gaze = []
    sorted_traj_frames = [x for _, x in sorted(
        zip(traj_scores, traj_frames), key=lambda pair: pair[0])]
    sorted_traj_actions = [x for _, x in sorted(
        zip(traj_scores, traj_actions), key=lambda pair: pair[0])]

    print("Max human score", max(sorted_traj_scores))
    print("Min human score", min(sorted_traj_scores))

    # so how do we want to get demos? how many do we have if we remove duplicates?
    seen_scores = set()
    non_duplicates = []
    if use_gaze:
        for i, s, d, r, g, f, a in zip(sorted_traj_indices, sorted_traj_scores, sorted_traj_dirs, sorted_traj_rewards, sorted_traj_gaze, sorted_traj_frames, sorted_traj_actions):
            if s not in seen_scores:
                seen_scores.add(s)
                non_duplicates.append((i, s, d, r, g, f, a))
    else:
        for i, s, d, r, f, a in zip(sorted_traj_indices, sorted_traj_scores, sorted_traj_dirs, sorted_traj_rewards, sorted_traj_frames, sorted_traj_actions):
            if s not in seen_scores:
                seen_scores.add(s)
                non_duplicates.append((i, s, d, r, f, a))
    print("num non duplicate scores", len(seen_scores))
    if env_name == "spaceinvaders":
        start = 0
        skip = 3
    elif env_name == "revenge":
        start = 0
        skip = 1
    elif env_name == "qbert":
        start = 0
        skip = 3
    elif env_name == "mspacman":
        start = 0
        skip = 1
    else:   # TODO: confirm best logic for all games
        start = 0
        skip = 3
    # num_demos = 12
    # demos = non_duplicates[start:num_demos*skip + start:skip]
    demos = non_duplicates  # don't skip any demos
    return demos

def get_preprocessed_trajectories(env_name, dataset, data_dir, use_gaze=False):
    """returns an array of trajectories corresponding to what you would get running checkpoints from PPO
       demonstrations are grayscaled, maxpooled, stacks of 4 with normalized values between 0 and 1 and
       top section of screen is masked
    """

    demos = get_sorted_traj_indices(env_name, dataset, use_gaze)
    human_scores = []
    human_demos = []
    human_rewards = []
    human_gaze = []

    # print('len demos: ', len(demos))
    for data in demos:
        if use_gaze:
            indx, score, img_dir, rew, gaze, frame, actions = data
        else:
            indx, score, img_dir, rew, frame, actions = data
        human_scores.append(score)

        # traj_dir = path.join(data_dir, 'screens', env_name, str(indx))
        traj_dir = path.join(data_dir, env_name)
        maxed_traj, actions = MaxSkipAndWarpFrames(traj_dir, img_dir, frame, actions)
        stacked_traj, actions = StackFrames(maxed_traj, actions)

        # demo_norm_mask = []
        demo_norm = []
        # normalize values to be between 0 and 1 and have top part masked
        for ob in stacked_traj:
            # print(env_name) #normalizing
            # demo_norm_mask.append(preprocess(ob, env_name)[0])  # masking
            demo_norm.append(normalize_state(ob))  # normalizing

        # DONE: don't mask here! (masking effects gaze prediction)

        print(len(frame), len(actions))
        print(len(demo_norm), len(actions))
        assert(len(demo_norm)==len(actions))
        # sa = [(demo_norm_mask[i], actions[i]) for i in range(len(demo_norm_mask))]
        sa = [(demo_norm[i], actions[i]) for i in range(len(actions))]
        human_demos.append(sa)

        # skip and stack reward
        maxed_reward = MaxSkipReward(rew)
        stacked_reward = StackReward(maxed_reward)
        human_rewards.append(stacked_reward)

        if use_gaze:
            # generate gaze heatmaps as per Ruohan's algorithm
            h = gh.DatasetWithHeatmap()
            # if gaze_conv_layer == 1:
            #     conv_size = 26
            # elif gaze_conv_layer == 2:
            #     conv_size = 11
            # elif gaze_conv_layer == 3:
            #     conv_size = 9
            # elif gaze_conv_layer == 4:
            #     conv_size = 7
            # else:
            #     print('Invalid Gaze conv layer. Must be between 1-4.')
            #     exit(1)
            conv_size = 84 # original image size
            g = h.createGazeHeatmap(gaze, conv_size)  # TODO: this heatmap is not normalized with softmax

            maxed_gaze = MaxSkipGaze(g, conv_size)
            stacked_gaze = CollapseGaze(maxed_gaze, conv_size)
            human_gaze.append(stacked_gaze)
            # print('stacked gaze: ', stacked_gaze[0].shape)

    # if(use_gaze):
    #     print(len(human_demos[0]), len(human_rewards[0]), len(human_gaze[0]))
    #     print(len(human_demos), len(human_rewards), len(human_gaze))

            return human_demos, human_scores, human_rewards, human_gaze

        else:
            return human_demos, human_scores, human_rewards


import requests, json, urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class AlphaVantage:

    def __init__(self, key):
        self.api_key = key

    def get_intraday(self, stock_symbol, time_interval_min, api_key):
        url = f"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=" \
              f"{stock_symbol}&interval={time_interval_min}min&apikey={api_key}"
        raw_payload = requests.get(url, verify=False)
        if raw_payload.status_code == 200:
            return json.dumps(raw_payload.json(), indent=4)
        else:
            return f"HTTP Error: {raw_payload.status_code}"

    def get_daily_ts(self, stock_symbol, api_key):

        url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol={stock_symbol}&apikey={api_key}"
        raw_payload = requests.get(url, verify=False)

        # Parsing raw API return values into clean ones
        # .rsplit method used to separate the numbers from datatype "1. open" --> "open"
        ts_data_payload = dict(raw_payload.json()["Time Series (Daily)"])
        for date, datapoints in ts_data_payload.items():
            for datapoint_type in list(datapoints):
                datapoints[datapoint_type.rsplit(". ")[-1].capitalize()] = datapoints[datapoint_type]
                del datapoints[datapoint_type]

        return json.dumps(raw_payload.json(), indent=4), {"target": stock_symbol, "payload": ts_data_payload}

if __name__ == '__main__':
    pass


from fractions import gcd
import heapq

def lcm(a, b):
    return (a * b) // gcd(a, b)

def solve_simulate(N, barber_times):
    last_barber_cut_index = 0
    available_barbers = [(index, value) for index, value in enumerate(barber_times)]
    heapq.heapify(available_barbers)
    unavailable_barbers = []
    for i in range(N + 1):
        if len(available_barbers) > 0:
            barber_index_time = heapq.heappop(available_barbers)
            heapq.heappush(unavailable_barbers, (barber_index_time[1], barber_index_time[0]))
            last_barber_cut_index = barber_index_time[0]
        else:
            min_time = unavailable_barbers[0][0]
            while len(unavailable_barbers) > 0 and unavailable_barbers[0][0] == min_time:
                now_available = heapq.heappop(unavailable_barbers)
                barber_index = now_available[1]
                heapq.heappush(available_barbers, (barber_index, barber_times[barber_index]))
            for i in range(len(unavailable_barbers)):
                unavailable_barbers[i] = (unavailable_barbers[i][0] - min_time, unavailable_barbers[i][1])
            heapq.heapify(unavailable_barbers)
            barber_index_time = heapq.heappop(available_barbers)
            heapq.heappush(unavailable_barbers, (barber_index_time[1], barber_index_time[0]))
            last_barber_cut_index = barber_index_time[0]
    return last_barber_cut_index

def solve_small(N, barber_times):
    max_lcm = 1
    for barber_time in barber_times:
        max_lcm = lcm(max_lcm, barber_time)
    cycle_length = 0
    for barber_time in barber_times:
        cycle_length += (max_lcm // barber_time)
    new_N = (N % cycle_length)
    return solve_simulate(new_N, barber_times) + 1

if __name__ == "__main__":
    with open("input.txt", "r") as input, open("output.txt", "w") as output:
        T = int(input.readline())
        for t in range(1, T + 1):
            B_N_tokens = input.readline().split(" ")
            B = int(B_N_tokens[0])
            N = int(B_N_tokens[1]) - 1
            Mk_tokens = input.readline().split(" ")
            barber_times = list(map(int, Mk_tokens))
            small_answer = solve_small(N, barber_times)
            print("Case #{0}: {1}".format(t, small_answer), file=output)


import graphene
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ValidationError

from core.gql.gql_mutations.base_mutation import BaseMutation, BaseCreateMutationMixin, BaseUpdateMutationMixin, \
    BaseHistoryModelCreateMutationMixin, BaseHistoryModelUpdateMutationMixin, BaseHistoryModelDeleteMutationMixin
from core.schema import OpenIMISMutation
from invoice.apps import InvoiceConfig
from invoice.gql.input_types import CreateBillPaymentInputType, UpdateBillPaymentInputType
from invoice.models import BillPayment, BillPaymentMutation

class CreateBillPaymentMutation(BaseHistoryModelCreateMutationMixin, BaseMutation):
    _mutation_class = "CreateBillPaymentMutation"
    _mutation_module = "invoice"
    _model = BillPayment

    @classmethod
    def _mutate(cls, user, **data):
        client_mutation_id = data.get("client_mutation_id")
        if "client_mutation_id" in data:
            data.pop('client_mutation_id')
        if "client_mutation_label" in data:
            data.pop('client_mutation_label')
        p = cls.create_object(user=user, object_data=data)
        if p:
            BillPaymentMutation.object_mutated(
                user,
                client_mutation_id=client_mutation_id,
                bill_payment=p
            )

    @classmethod
    def _validate_mutation(cls, user, **data):
        if type(user) is AnonymousUser or not user.id or not user.has_perms(
                InvoiceConfig.gql_bill_payment_create_perms):
            raise ValidationError("mutation.authentication_required")

    class Input(CreateBillPaymentInputType):
        pass

class UpdateBillPaymentMutation(BaseHistoryModelUpdateMutationMixin, BaseMutation):
    _mutation_class = "UpdateBillPaymentMutation"
    _mutation_module = "invoice"
    _model = BillPayment

    @classmethod
    def _validate_mutation(cls, user, **data):
        if type(user) is AnonymousUser or not user.id or not user.has_perms(
                InvoiceConfig.gql_bill_payment_update_perms):
            raise ValidationError("mutation.authentication_required")

    class Input(UpdateBillPaymentInputType):
        pass

class DeleteBillPaymentMutation(BaseHistoryModelDeleteMutationMixin, BaseMutation):
    _mutation_class = "DeleteBillPaymentMutation"
    _mutation_module = "invoice"
    _model = BillPayment

    @classmethod
    def _validate_mutation(cls, user, **data):
        if type(user) is AnonymousUser or not user.id or not user.has_perms(
                InvoiceConfig.gql_bill_payment_delete_perms):
            raise ValidationError("mutation.authentication_required")

    class Input(OpenIMISMutation.Input):
        uuids = graphene.List(graphene.UUID)


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import xlrd

arq = open("Resultado.xls","w")
workbook = xlrd.open_workbook(r'C:\Repositorio projeto-automacao\projeto-automacao\CNPJ_FIs.xls')
sheet = workbook.sheet_by_name('CNPJ_FI')
rows = sheet.nrows
columns = sheet.ncols

options = webdriver.EdgeOptions()
options.add_argument('--disable-logging')
options.add_argument('--log-level=3')

driver = webdriver.Edge("C:\Repositorio projeto-automacao\projeto-automacao\msedgedriver.exe", options= options)
driver.get ("https://web.cvm.gov.br/app/fundosweb/#/consultaPublica")

for curr_row in range(0,rows):
    x = sheet.cell_value(curr_row,0)
    time.sleep(10)
    #BuscaFundo = driver.find_element(By.LINK_TEXT,'https://sistemas.cvm.gov.br/port/fundos/consultafundos.asp')
    time.sleep(3)
    Pesquisa = driver.find_element(By.ID,"txtCnpj")
    time.sleep(3)
    Pesquisa.clear()
    time.sleep(4)
    Pesquisa.send_keys(x)
    time.sleep(5)
    #Pesquisa.send_keys(Keys.RETURN)
    time.sleep(3)
    click_pesquisa = driver.find_element(By.XPATH, '//*[@id="panelFiltros"]/div/div/a[1]/span' )
    click_pesquisa.click()
    time.sleep(3)
    visualizar_pesquisa = driver.find_element(By.XPATH,'//*[@id="containerView"]/div[2]/div[3]/table/tbody/tr/td[9]/a[1]')
    visualizar_pesquisa.click()
    time.sleep(3)
    click_participantes = driver.find_element(By.XPATH,'//*[@id="tabFundo"]/ul/li[2]')
    click_participantes.click()
    time.sleep(3)
    Dados_Distribuidor = driver.find_element(By.XPATH,'//*[@id="tabPanelVisualizarParticipante"]/div/div[2]/div[21]/div/fieldset/div[2]/div/div/table[1]/tbody/tr/td[3]')

    texto = 'O FI %s %s\n' %(x,Dados_Distribuidor.accessible_name)
    time.sleep(1)

    Boton_Return = driver.find_element(By.XPATH,'//*[@id="containerView"]/div[2]/div[2]/div[1]/a/span[2]')
    Boton_Return.click()
    arq.write(texto)
    time.sleep(2)
arq.close()


import os

import eidolon
from eidolon import vec3, color, enum, first, fillList, Camera2DView, DrawContourMixin, DrawLineMixin, PolyHandle2D

from ..ui import QtWidgets, QtCore, Qt, Ui_Measure2DView, Ui_MeasureObjProp
from .SegmentPlugin import triangulateContour, generateContoursFromMask
from .PlotPlugin import TimePlotWidget
import itertools

# names of parameters for the measurement data file
DatafileParams=enum('name','title','srcimage','tracksrc','trackdata')

# types of measurements
MeasureType=enum('point','line','contour')

# extension of measurement files
measureExt='.measure'

defaultNumNodes=16 # default number of contour nodes
defaultColor=color(1,0,0) # default handle colour

# default colours for points
PointColors=(color(1,0,0),color(0,1,0),color(0,0,1),color(1,1,0),color(1,0,1),color(0,1,1),color(1,1,1))

def calculateLength(measure,objs):
    return [v[0].distTo(v[1]) for v in measure.values]

def calculateDisplacement(measure,objs):
    return [measure.values[0][0].distTo(v[0]) for v in measure.values]

def calculateCircumference(measure,objs):
    results=[]
    for v in measure.values:
        results.append(sum(a.distTo(b) for a,b in eidolon.successive(v,cyclic=True)))

    return results

def calculateArea(measure,objs):
    results=[]
    for v in measure.values:
        triinds=triangulateContour(v,True)
        results.append(sum(v[a].triArea(v[b],v[c]) for a,b,c in triinds))

    return results

def calculateImageVolume(measure,objs):
    area=calculateArea(measure,objs)
    img=first(o for o in objs if isinstance(o,eidolon.ImageSceneObject))
    if img:
        area*=img.getVoxelSize().z()

    return area

# List of metric calculation functions to apply to specific measurement types, values are (description, MeasureType, function)
# The function should accept two arguments, a Measurement object and a list of objects representing the main image object followed
# by secondary objects. The Measurement object is expected to be on one of the planes of the main image, and the secondary
# objects are assumed to be the secondary visible objects in a 2D view.
MetricFuncs=enum(
    ('length','Length',MeasureType._line,calculateLength),
    ('disp','Displacement',MeasureType._point,calculateDisplacement),
    ('circum','Circumference',MeasureType._contour,calculateCircumference),
    ('area','Area',MeasureType._contour,calculateArea),
    #('volume','Volume',MeasureType._contour,calculateImageVolume),
    doc='Types of metrics to apply to measurements over time'
)

class Measurement(object):
    def __init__(self,name,mtype,col=(1,1,1),timesteps=[],values=[]):
        self.name=name
        self.mtype=mtype
        self.col=color(*col)
        self.timesteps=list(timesteps)
        self.values=[list(itertools.starmap(vec3,v)) for v in values] # list of lists, store values as vec3

        assert len(self.values)==len(self.timesteps)
        assert all(len(v)==len(self.values[0]) for v in self.values)
        assert mtype in MeasureType

    def clone(self):
        return Measurement(self.name,self.mtype,self.col,self.timesteps,self.values)

    def setHandlePoints(self,handle,timestep,margin=0):
        curtimeind=eidolon.minmaxIndices(abs(ts-timestep) for ts in self.timesteps)[0]
        assert 0<=curtimeind<len(self.values)

        for i,n in enumerate(self.values[curtimeind]):
            handle.setNode(i,n)

        handle.col=self.col

    def setHandleVisible(self,handle,timestep,margin):
        handle.setVisible(handle.isVisible() and (not margin or min(abs(ts-timestep) for ts in self.timesteps)<=margin))

    def updateValue(self,handle,timestep):
        curtimeind=eidolon.minmaxIndices(abs(ts-timestep) for ts in self.timesteps)[0]
        assert 0<=curtimeind<len(self.values)
        nodes=handle.getNodes()

        if nodes!=self.values[curtimeind]: # if the nodes are changed, update
            self.values[curtimeind]=nodes
            return True
        else:
            return False

    def addTimestep(self,timestep,values):
        self.timesteps.append(timestep)
        self.values.append(list(values))

        # resort timestep and values
        sortinds=eidolon.sortIndices(self.timesteps)
        self.timesteps=eidolon.indexList(sortinds,self.timesteps)
        self.values=eidolon.indexList(sortinds,self.values)

    def setTimesteps(self,timesteps,copyFirstValues=True):
        self.timesteps=timesteps
        if copyFirstValues or len(timesteps)!=len(self.values):
            self.values=[list(self.values[0]) for ii in timesteps]

    def __repr__(self):
        col=tuple(self.col)
        values=[list(map(tuple,v)) for v in self.values]
        return repr((self.name,self.mtype,col,self.timesteps,values))

class MeasureSceneObject(eidolon.DatafileSceneObject):
    def __init__(self,name,filename,plugin,**kwargs):
        filename=eidolon.ensureExt(filename,measureExt)
        datamap={
            DatafileParams.name: name,
            DatafileParams.title: name,
            DatafileParams.srcimage:'',
            DatafileParams.tracksrc:'',
            DatafileParams.trackdata:''
        }

        eidolon.DatafileSceneObject.__init__(self,name,filename,datamap,plugin,**kwargs)

    def getWidget(self):
        '''Get the docked widget for this object, or None if the user hasn't created one.'''
        return self.plugin.getObjectWidget(self)

    def getObjectNames(self):
        return [n for n in self.datamap if n not in DatafileParams]

    def addObject(self,sceneobj):
        basename=sceneobj.name or sceneobj.mtype
        sceneobj.name=eidolon.uniqueStr(basename,[sceneobj.mtype]+self.getObjectNames())
        self.datamap[sceneobj.name]=sceneobj
        return sceneobj.name

    def enumObjects(self):
        for name,val in self.datamap.items():
            if name not in DatafileParams:
                yield val

    def clearObjects(self):
        for n in list(self.datamap):
            if n not in DatafileParams:
                self.datamap.pop(n)

class MeasurePropertyWidget(QtWidgets.QWidget,Ui_MeasureObjProp):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self,parent)
        self.setupUi(self)

class Measure2DWidget(QtWidgets.QWidget,Ui_Measure2DView):
    def __init__(self,parent=None):
        QtWidgets.QWidget.__init__(self,parent)
        self.setupUi(self)

class LabelPolyHandle2D(PolyHandle2D):
    def __init__(self,widg2D,pts,isClosed=False,col=color(1,0,0,1),etype=None,radius=None):
        PolyHandle2D.__init__(self,widg2D,pts,isClosed,col,etype,radius)
        self.label=None

    def setVisible(self,isVisible):
        PolyHandle2D.setVisible(self,isVisible)
        if self.label:
            self.label.setVisible(isVisible and self.isActive())

    def updatePositions(self):
        PolyHandle2D.updatePositions(self)

        if self.isVisible():
            self.label.setPosition(self.widg2D.getOrthoPosition(eidolon.avg(self.pts)))

            if len(self.pts)==2:
                text='Len: %g'%self.pts[0].distTo(self.pts[1])
            else:
                circ=sum(a.distTo(b) for a,b in eidolon.successive(self.pts,cyclic=True))
                triinds=triangulateContour(self.pts,True)
                area=sum(self.pts[a].triArea(self.pts[b],self.pts[c]) for a,b,c in triinds)

                text='Circ: %g\nArea:%g'%(circ,area)
                self.label.setHAlign(eidolon.H_CENTER)
                self.label.setVAlign(eidolon.V_CENTER)

            self.label.setText(text)

    def updateRepr(self):
        PolyHandle2D.updateRepr(self)

        if not self.label:
            self.label=self.widg2D.createFigure('label',eidolon.FT_TEXT)
            self.label.setTextHeight(20)
            self.label.setOverlay(True)
            self.label.setVisible(False)

        self.figs.append(self.label)

class MeasurementView(DrawLineMixin,DrawContourMixin,Camera2DView):

    measurementChanged=QtCore.pyqtSignal(int)

    def __init__(self,mgr,camera,parent=None):
        Camera2DView.__init__(self,mgr,camera,parent)
        DrawLineMixin.__init__(self)
        DrawContourMixin.__init__(self,defaultNumNodes)

        self.plugin=self.mgr.getPlugin('Measure')
        self.handlecol=defaultColor
        self.handleradius=5.0
        self.planeMargin=eidolon.Handle2D.defaultPlaneMargin

        self.lastUpdateTime=-1.0

        self.sceneobj=None

        self.handleNames={} # index -> Measurement object for each index in self.handles which is a measuring handle (not all handles are such)

        self.uiobj=Measure2DWidget()
        self.uiobj.measureBox.setParent(None)
        self.mainLayout.addWidget(self.uiobj.measureBox)

        self.uiobj.numCtrlBox.setValue(self.numNodes)
        self.uiobj.numCtrlBox.valueChanged.connect(self.setNumNodes)

        self.uiobj.cloneButton.clicked.connect(self._cloneObject)
        self.uiobj.delButton.clicked.connect(self._deleteObject)
        self.uiobj.saveButton.clicked.connect(self.save)
        self.uiobj.setPlaneButton.clicked.connect(self._setObjectPlane)
        self.uiobj.setStartButton.clicked.connect(self._setStartTime)
        self.uiobj.copyStepsButton.clicked.connect(self._copySteps)
        self.uiobj.trackButton.clicked.connect(self._motionTrack)

        self.uiobj.addPointButton.clicked.connect(self._addPointButton)
        self.uiobj.addLineButton.clicked.connect(self._addLineButton)
        self.uiobj.addContourButton.clicked.connect(self._addContourButton)

        self.uiobj.show3DBox.clicked.connect(lambda:self.setVisible3D(self.isVisible3D()))

        self.uiobj.objectList.itemSelectionChanged.connect(self._selectObject)

        self.measurementChanged.connect(lambda i:self.updateSceneObject())

        eidolon.setCollapsibleGroupbox(self.uiobj.measureBox,True)

#       self.uiobj.trackGroup.setVisible(False) # TODO: hide the track box until figured out how to track without close coupling

    def setSceneObject(self,sceneobj):
        self.sceneobj=sceneobj

        for i,ind in enumerate(sorted(self.handleNames)): # remove objects only, leave other handles alone
            self.removeHandle(ind-i)

        self.handleNames={}

        for mobj in tuple(self.sceneobj.enumObjects()):
            if isinstance(mobj,tuple):
                mobj=Measurement(*mobj)

            self.addMeasurement(mobj)

        self.setActiveObject('') # select nothing

    def updateSceneObject(self):
        if self.sceneobj:
            self.sceneobj.clearObjects()
            for i,v in self.handleNames.items():
                self.sceneobj.addObject(v)

    def save(self):
        if self.sceneobj:
            self.updateSceneObject()

            if not os.path.isfile(self.sceneobj.filename):
                self.sceneobj.filename=eidolon.ensureExt(self.sceneobj.plugin.getFilename(False),measureExt)
            self.sceneobj.save()

    def setNumNodes(self,n):
        if n>=4:
            self.numNodes=n
            with eidolon.signalBlocker(self.uiobj.numCtrlBox):
                self.uiobj.numCtrlBox.setValue(n)

        self._repaintDelay()

    def addMeasurement(self,mobj):
        '''Add the Measurement object `mobj' to the list of measurements and create an appropriate handle.'''
        if mobj.mtype==MeasureType.point:
            h=eidolon.PointHandle2D(self,mobj.values[0][0],mobj.col)
        elif mobj.mtype==MeasureType.line:
            h=LabelPolyHandle2D(self,mobj.values[0][:2],False,mobj.col,eidolon.ElemType.Line1NL,self.handleradius)
        elif mobj.mtype==MeasureType.contour:
            h=LabelPolyHandle2D(self,mobj.values[0],True,mobj.col,eidolon.ElemType.Line1PCR,self.handleradius)

        i=len(self.handles)
        self.addHandle(h)
        self.handleNames[i]=mobj
        self.measurementChanged.emit(i)

    def createMeasurement(self,mtype,col,values,name=None,timesteps=None):
        '''Create and add a new Measurement object of type `mtype', color `col', with points `values'.'''

        existingnames=list(map(first,MeasureType))+[n.name for n in self.handleNames.values()] # ensures names are unique and always numbered
        name=eidolon.uniqueStr(name or mtype,existingnames)
        timesteps=[self.mgr.timestep] if timesteps==None else timesteps
        col=col or self.handlecol
        if not isinstance(values[0],list):
            values=[values]

        mobj=Measurement(name,mtype,col,timesteps,values)
        self.addMeasurement(mobj)
        self.setActiveObject(mobj.name)

        return mobj

    def getMeasurement(self,i):
        return self.handleNames[i]

    def addPoint(self,point,col=None,name=None,timestep=None):
        return self.createMeasurement(MeasureType.point,col,[point],name,timestep)

    def addLine(self,start,end,col=None,name=None,timestep=None):
        return self.createMeasurement(MeasureType.line,col,[start,end],name,timestep)

    def addContour(self,nodes,col=None,name=None,timestep=None):
        return self.createMeasurement(MeasureType.contour,col,nodes,name,timestep)

    def setActiveObject(self,name):
        for i,v in self.handleNames.items():
            self.handles[i].setActive(v.name==name)

        listitem=first(self.uiobj.objectList.findItems(name+' @ ',Qt.MatchStartsWith))
        if listitem:
            with eidolon.signalBlocker(self.uiobj.objectList):
                self.uiobj.objectList.setCurrentItem(listitem)

        self._repaintDelay()

    def handleSelected(self,handle):
        self.setActiveObject(self.handleNames[self.handles.index(handle)].name)

    def getActiveIndex(self):
        return first(i for i in self.handleNames if i<len(self.handles) and self.handles[i].isActive())

    def removeHandle(self,index):
        if index!=None:
            for i in sorted(self.handleNames.keys()):
                if i==index: # remove the entry for this handle
                    self.handleNames.pop(index)
                elif i>index: # move entries indexed above this handle down 1
                    self.handleNames[i-1]=self.handleNames.pop(i)

            Camera2DView.removeHandle(self,index)

    def removeHandles(self):
        Camera2DView.removeHandles(self)
        self.handleNames={}

    def updateView(self):
        self.fillLineFig()
        self.fillContourFig()

        handles=sorted(self.handleNames.items())

        # fill the handles list, selecting the active handle
        objects=['%s @ %.2f->%.2f (%s)'%(mobj.name,mobj.timesteps[0],mobj.timesteps[-1],mobj.mtype) for i,mobj in handles]
        selected=self.getActiveIndex()
        fillList(self.uiobj.objectList,objects,selected if selected!=None else -1,None,True)

        curtime=self.mgr.timestep
        rep=self.mgr.findObject(self.sourceName)
        tslist=rep.getTimestepList() if rep else []
        tsmargin=eidolon.avgspan(tslist)/2 if len(tslist)>=2 else 0
        #mintimeind=minmaxIndices(abs(ts-curtime) for ts in tslist)[0] if rep else -1

        # set the handle for each measurement object to the data for the current timestep
        if curtime!=self.lastUpdateTime:
            self.lastUpdateTime=curtime
            for i,mobj in handles:
                mobj.setHandlePoints(self.handles[i],curtime,tsmargin)

        Camera2DView.updateView(self)

        for i,mobj in handles:
            mobj.setHandleVisible(self.handles[i],curtime,tsmargin)

        fillList(self.uiobj.trackDataBox,sorted(list(self.plugin.trackSrcs)))

    def mousePress(self,e):
        if DrawLineMixin.drawMousePress(self,e) or DrawContourMixin.drawMousePress(self,e):
            return

        Camera2DView.mousePress(self,e)

    def mouseRelease(self,e):
        self.uiobj.addContourButton.setStyleSheet('')
        self.uiobj.addLineButton.setStyleSheet('')

        if DrawLineMixin.drawMouseRelease(self,e):
            self.addLine(self.lineStart,self.lineEnd)
            self._repaintDelay()
        elif DrawContourMixin.drawMouseRelease(self,e):
            self.addContour(self.contour)
            self._repaintDelay()
        else:
            i=self.getActiveIndex()
            if i!=None and self.handles[i].isVisible():
                if self.handleNames[i].updateValue(self.handles[i],self.mgr.timestep):
                    self.measurementChanged.emit(i)

            Camera2DView.mouseRelease(self,e)

    def mouseDrag(self,e,dx,dy):
        if DrawLineMixin.drawMouseDrag(self,e,dx,dy) or DrawContourMixin.drawMouseDrag(self,e,dx,dy):
            self._repaintDelay()
        else:
            Camera2DView.mouseDrag(self,e,dx,dy)

    def isVisible3D(self):
        return self.uiobj.show3DBox.isChecked()

    def setVisible3D(self,isVisible):
        eidolon.setChecked(isVisible,self.uiobj.show3DBox)

        for i in self.handleNames:
            self.handles[i].setVisible3D(isVisible)

        self.mgr.repaint(False)

    def _selectObject(self):
        if self.uiobj.objectList.currentItem():
            text=str(self.uiobj.objectList.currentItem().text())
            self.setActiveObject(text.split('@')[0].strip())

    def _addPointButton(self):
        p=self.getWorldPosition(0.5,0.5,False)
        self.addPoint(p,self.handlecol)

    def _addLineButton(self):
        if not self.drawingContour:
            self.startLineDraw()
            self.uiobj.addLineButton.setStyleSheet('border: 1px solid rgb(255,0,0);')

    def _addContourButton(self):
        if not self.drawingLine:
            self.startContourDraw()
            self.uiobj.addContourButton.setStyleSheet('border: 1px solid rgb(255,0,0);')

    def _deleteObject(self):
        self.removeHandle(self.getActiveIndex())

    def _setObjectPlane(self):
        i=self.getActiveIndex()
        if i!=None:
#           h=self.handles[i]
#           nodes=h.getNodes()
#           for n in range(len(nodes)):
#               x,y,_=self.getScreenPosition(nodes[n])
#               h.setNode(n,self.getWorldPosition(x,y))
#
#           if self.handleNames[i].updateValue(h,self.mgr.timestep):
#               self.measurementChanged.emit(i)

            m=self.handleNames[i]

            for ts in m.values:
                for j,n in enumerate(ts):
                    x,y,_=self.getScreenPosition(n)
                    ts[j]=self.getWorldPosition(x,y)

            self.measurementChanged.emit(i)
            self._repaintDelay()

    def _setStartTime(self):
        i=self.getActiveIndex()
        if i!=None:
            m=self.handleNames[i]
            m.timesteps=[self.mgr.timestep]
            m.values=[m.values[0]]
            self.measurementChanged.emit(i)

    def _cloneObject(self):
        i=self.getActiveIndex()
        if i!=None:
            #m=self.handleNames[i].clone()
            #m.name=uniqueStr(m.mtype,[n.name for n in self.handleNames.values()]) # ensure name uniqueness
            #self.addMeasurement(m)
            m=self.handleNames[i]
            self.createMeasurement(m.mtype,m.col,m.values,None,m.timesteps)

    def _copySteps(self):
        i=self.getActiveIndex()
        rep=self.mgr.findObject(self.sourceName)
        if i!=None and rep!=None:
            m=self.handleNames[i]
            m.setTimesteps(rep.getTimestepList())
            self.measurementChanged.emit(i)

    def _motionTrack(self):
        i=self.getActiveIndex()
        rep=self.mgr.findObject(self.sourceName)
        trackdata=str(self.uiobj.trackDataBox.currentText())

        if not trackdata:
            self.mgr.showMsg('No source for motion track data','No Track Data')
        elif i!=None and rep!=None:
            m=self.handleNames[i]
            m.timesteps=rep.getTimestepList()

            result=self.plugin.trackSrcs[trackdata](trackdata,m.values[0])
            m.values=[]

            for ts in m.timesteps:
                _,pts=min((abs(ts-t),p) for t,p in result.items())
                m.values.append(pts)

            self.measurementChanged.emit(i)

class MeasurementPlotWidget(TimePlotWidget):
    def __init__(self,plugin,parent=None):
        TimePlotWidget.__init__(self,plugin,parent)
        self.measureCache={} # maps (measurement name, metric name) to (is visible, timestep,line data) tuples
        self.setObjectName('Measurement Plot')

    def isLineVisible(self,measure,metric):
        k=(measure.name,metric)
        return k in self.measureCache and self.measureCache[k][0]

    def setLineVisible(self,measure,metric,isVisible):
        k=(measure.name,metric)
        if k in self.measureCache:
            self.measureCache[k]=(isVisible,measure.timesteps,self.measureCache[k][-1])
            self.updatePlot()

    def setMeasurement(self,measure,metric,data):
        self.measureCache[(measure.name,metric)]=(True,measure.timesteps,data)
        self.updatePlot()

    def updatePlot(self):
        @self.plugin.mgr.callThreadSafe
        def _update():
            data=[]
            timesteps=[]
            labels=[]

            for mname,metric in self.measureCache:
                isVis,timestep,linedata=self.measureCache[(mname,metric)]
                if isVis:
                    labels.append('%s (%s)'%(mname,MetricFuncs[metric][0]))
                    timesteps.append(timestep)
                    data.append(linedata)

            self.setData(data,timesteps,labels)
            self.repaint()

class MeasureSplitView(QtWidgets.QSplitter):
    def __init__(self,mgr,camera,parent=None):
        QtWidgets.QSplitter.__init__(self)
        self.mgr=mgr
        self.setOrientation(Qt.Horizontal)
        self.setChildrenCollapsible(True)
        self.measure=MeasurementView(mgr,camera,self)

        self.measure.measurementChanged.connect(self.updateMeasurement)

        self.plotwidg=QtWidgets.QWidget(self)
        self.plotLayout=QtWidgets.QVBoxLayout(self.plotwidg)

        self.plot=MeasurementPlotWidget(mgr.getPlugin('Plot'),self.plotwidg)

        self.plotctrls=QtWidgets.QGroupBox('Choose Plot Measurements',self.plotwidg)
        self.plotCtrlLayout=QtWidgets.QHBoxLayout(self.plotctrls)

        self.chooseMenu=QtWidgets.QMenu(self)
        self.plotChooseButton=QtWidgets.QToolButton(self.plotctrls)
        self.plotChooseButton.setText('Choose Measurements')
        self.plotChooseButton.setMenu(self.chooseMenu)
        self.plotChooseButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)

        self.plotCtrlLayout.addWidget(self.plotChooseButton)

        self.plotLayout.addWidget(self.plot)
        self.plotLayout.addWidget(self.plotctrls)

    def setSceneObject(self,sceneobj):
        self.measure.setSceneObject(sceneobj)

    def parentClosed(self,e):
        self.measure.parentClosed(e)
        self.plot.parentClosed(e)

    def fillChooseMenu(self):
        def _addAction(measure,metric):
            '''Use a subroutine to ensure `measure' and `metric' are fresh variables for each call to connect().'''
            action=self.chooseMenu.addAction('%s (%s)'%(measure.name,MetricFuncs[metric][0]))
            action.setCheckable(True)
            action.setChecked(self.plot.isLineVisible(measure,metric))
            action.toggled.connect(lambda b:self.setLineVisible(measure,metric,b))

        self.chooseMenu.clear()
        for m in self.measure.sceneobj.enumObjects():
            for mf in MetricFuncs:
                if mf[2]==m.mtype:
                    _addAction(m,mf[0])

    @eidolon.taskmethod('Calculating Plot')
    def setMeasurement(self,measure,metric,objs,task):
        linedata=MetricFuncs[metric][2](measure,objs)
        self.plot.setMeasurement(measure,metric,linedata)
        return linedata

    def setLineVisible(self,measure,metric,isVisible):
        if isVisible and (measure.name,metric) not in self.plot.measureCache:
            objs=[self.mgr.findObject(n).parent for n in self.measure.getObjectNames() if n]
            f=self.setMeasurement(measure,metric,objs)
            #self.mgr.checkFutureResult(f)
        else:
            self.plot.setLineVisible(measure,metric,isVisible)

    def updateMeasurement(self,i):
        self.fillChooseMenu()
        m=self.measure.getMeasurement(i)
        objs=[self.mgr.findObject(n).parent for n in self.measure.getObjectNames() if n]

        for k in list(self.plot.measureCache):
            if k[0]==m.name:
                if not self.plot.measureCache[k][0]:
                    self.plot.measureCache.pop(k)
                else:
                    self.setMeasurement(m,k[1],objs)

class MeasurePlugin(eidolon.ScenePlugin):
    def __init__(self):
        eidolon.ScenePlugin.__init__(self,'Measure')
        self.dockmap={}
        # maps source name/directory to function accepting (name, points list) and returns mapping from timesteps to list of points tracked to that time
        self.trackSrcs={}

    def init(self,plugid,win,mgr):
        eidolon.ScenePlugin.init(self,plugid,win,mgr)
        if win:
            win.addMenuItem('Create','NewMeasure'+str(plugid),'&Measurement Object',self._createMeasure)
            win.addMenuItem('Import','ImportMeasure'+str(plugid),'&Measurements',self._importMeasure)

        if mgr.conf.hasValue('args','--measure'):
            @eidolon.taskroutine('Loading Measurement File(s)')
            def _loadTask(filenames,task=None):
                for f in filenames:
                    obj=self.loadObject(f)
                    self.mgr.addSceneObject(obj)

            self.mgr.runTasks(_loadTask(mgr.conf.get('args','--measure').split(',')))

    def _importMeasure(self):
        filename=self.getFilename()
        if filename:
            obj=self.loadObject(filename)
            self.mgr.addSceneObject(obj)

    def _createMeasure(self):
        obj=self.createMeasurementObject('','Measurement')
        self.mgr.addSceneObject(obj)

    def getMenu(self,obj):
        return [obj.getName(),'Show Measurement View'],self.objectMenuItem

    def getFilename(self,isOpen=True):
        return self.mgr.win.chooseFileDialog('Choose Measurement filename',filterstr='Measurement Files (*%s)'%measureExt,isOpen=isOpen)

    def getObjectWidget(self,obj):
        return first(w for w in self.win.dockWidgets if id(w)==self.dockmap.get(obj.getName(),-1))

    def objectMenuItem(self,obj,item):
        if item=='Show Measurement View':
            self.mgr.addFuncTask(lambda:obj.createRepr(None))

    def createObjPropBox(self,obj):
        prop=MeasurePropertyWidget()

        prop.createContButton.clicked.connect(lambda:self._createContoursButton(prop,obj))
        prop.showButton.clicked.connect(lambda:self.mgr.addFuncTask(lambda:obj.createRepr(None)))
        prop.srcBox.activated.connect(lambda i:obj.set(DatafileParams.srcimage,str(prop.srcBox.itemText(i))))

        return prop

    def updateObjPropBox(self,obj,prop):
        if not prop.isVisible():
            return

        prop.ctrlsBox.setValue(defaultNumNodes)

        imgnames=[o.getName() for o in self.mgr.objs if isinstance(o,eidolon.ImageSceneObject)]

        eidolon.fillTable(obj.getPropTuples(),prop.propTable)
        eidolon.fillList(prop.srcBox,imgnames,obj.get(DatafileParams.srcimage))

        eidolon.fillList(prop.maskImgBox,imgnames)

    def acceptFile(self,filename):
        return eidolon.splitPathExt(filename)[2].lower() == measureExt

    def checkFileOverwrite(self,obj,dirpath,name=None):
        outfile=os.path.join(dirpath,name or obj.getName())+measureExt
        if os.path.exists(outfile):
            return [outfile]
        else:
            return []

    def renameObjFiles(self,obj,oldname,overwrite=False):
        assert isinstance(obj,eidolon.SceneObject) and obj.plugin==self
        if os.path.isfile(obj.filename):
            obj.filename=eidolon.renameFile(obj.filename,obj.getName(),overwriteFile=overwrite)

    def getObjFiles(self,obj):
        return [obj.filename] if obj.filename else []

    def copyObjFiles(self,obj,sdir,overwrite=False):
        assert os.path.isfile(obj.filename),'Nonexistent filename: %r'%obj.filename

        newfilename=os.path.join(sdir,os.path.basename(obj.filename))
        if not overwrite and os.path.exists(newfilename):
            raise IOError('File already exists: %r'%newfilename)

        obj.filename=newfilename
        obj.save()

    def getScriptCode(self,obj,**kwargs):
        configSection=kwargs.get('configSection',False)
        namemap=kwargs.get('namemap',{})
        convertpath=kwargs['convertPath']
        script=''
        args={'varname':namemap[obj], 'objname':obj.name}

        if not configSection:
            args['filename']=convertpath(obj.filename)
            script+='%(varname)s = Measure.loadObject(%(filename)s,%(objname)r)\n'

        return eidolon.setStrIndent(script % args).strip()+'\n'

    def createMeasurementObject(self,filename,name):
        return MeasureSceneObject(name,filename,self)

    @eidolon.taskmethod('Creating Segmentation View')
    def createRepr(self,obj,reprtype,refine=0,task=None,**kwargs):
        sobj=self.mgr.findObject(obj.get(DatafileParams.srcimage))

        # make a representation of the source object visible if one doesn't already exist
        if isinstance(sobj,eidolon.ImageSceneObject) and not len(sobj.reprs):
            isEmpty=first(self.mgr.enumSceneObjectReprs())==None
            r=sobj.createRepr(eidolon.ReprType._imgtimestack if sobj.isTimeDependent else eidolon.ReprType._imgstack)
            self.mgr.addSceneObjectRepr(r)
            if isEmpty:
                self.mgr.setCameraSeeAll()
                self.mgr.repaint()

        return self.getMeasurementDock(obj)

    def getMeasurementDock(self,obj,w=400,h=400):
        @self.mgr.proxyThreadSafe
        def createWidget():
            widg=self.mgr.create2DView(obj.get(DatafileParams.name),MeasureSplitView)#MeasurementView)
            self.dockmap[obj.getName()]=id(widg)
            widg.setSceneObject(obj)
            return widg

        if self.win:
            self.win.sync()
            widg=first(d for d in self.win.dockWidgets if id(d)==self.dockmap.get(obj.getName(),-1))
            return widg or createWidget()

    def loadObject(self,filename,name=None,**kwargs):
        obj=self.createMeasurementObject(filename,name or eidolon.splitPathExt(filename)[1])
        obj.load()
        return obj

    def saveObject(self,obj,path,overwrite=False,setFilenames=False,**kwargs):
        if not isinstance(obj,MeasureSceneObject) or not isinstance(obj.plugin,MeasurePlugin):
            raise ValueError('Can only save measurement objects with MeasurePlugin')

        if os.path.isdir(path):
            path=os.path.join(path,eidolon.getValidFilename(obj.getName()))

        path=eidolon.ensureExt(path,measureExt)
        oldpath=obj.filename
        obj.filename=path
        obj.save()

        if not setFilenames: # set the filename back to the original if we're not supposed to change it
            obj.filename=oldpath

        return path

    def addTrackSource(self,name,func):
        '''
        Add the tracking source for the name/directory `name'. The `func' callable must accept 2 arguments, the name
        it was keyed to and a list of vec3 points, and return a dict mapping timesteps to the motion tracked data
        at that time.
        '''
        self.trackSrcs[name]=func

    def removeTrackSource(self,name_or_func):
        '''Remove all tracking sources using the given name or function.'''
        for n,f in list(self.trackSrcs.items()):
            if name_or_func in (n,f):
                del self.trackSrcs[n]

    @eidolon.taskmethod('Add segmentations from mask image')
    def addMaskSegmentations(self,obj,mask,numctrls,innerseg,minSegSize=100,refine=2,task=None):
        contours=generateContoursFromMask(mask.images,numctrls,innerseg,minSegSize,2,task)

        for ts,nodes in contours:
            mobj=Measurement('contour',MeasureType.contour,defaultColor,[ts],[nodes])
            obj.addObject(mobj)

        widg=obj.getWidget()
        if widg:
            self.mgr.callThreadSafe(widg.setSceneObject,obj)

    def _createContoursButton(self,prop,obj):
        maskname=str(prop.maskImgBox.currentText())
        numctrls=prop.ctrlsBox.value()
        innerseg=prop.innerContBox.isChecked()
        mask=self.findObject(maskname)

        f=self.addMaskSegmentations(obj,mask,numctrls,innerseg)
        self.mgr.checkFutureResult(f)

eidolon.addPlugin(MeasurePlugin())