def test_tags_attachments(self):
        source = connection('tags_and_attachments.xml')

        issues = list(get_issues(source, None))
        self.assertEqual(1, len(issues))
        changes = issues[0]
        self.assertEqual(4, len(changes))
        self.assertEqual({'sTitle': 'Some Title', 'attachments': [],
            'tags': set(['jessie', 'rupert', 'sally']),
            'sStatus': 'Closed (Duplicate)',
            'ixPersonAssignedTo': '1', 'sCategory': 'Feature',
            'ixBugParent': '0', 'ixPriority': '1',
            'dt': '2010-04-20T10:19:52Z', 'sProject': 'Some Project',
            'ixPerson': '2', 'ixBug': '5'}, changes[0])
        self.assertEqual({'sTitle': 'Some Title', 'attachments': [],
            'tags': set(['jessie', 'rupert', 'sally']),
            'ixPersonAssignedTo': '1', 'sCategory': 'Feature',
            'ixBugParent': '0', 'ixPriority': '1', 'ixBug': '5',
            'dt': '2010-04-20T09:56:38Z', 'sProject': 'Some Project',
            'ixPerson': '2', 'sStatus': 'Resolved'}, changes[1])
        self.assertEqual({'sTitle': 'Some Title', 'attachments': [],
            'tags': set(['jessie', 'rupert', 'sally']), 'sStatus': 'Active',
            'ixPersonAssignedTo': '3', 'sCategory': 'Bug',
            'ixBugParent': '0', 'ixPriority': '1',
            'dt': '2010-03-13T00:52:11Z', 'sProject': 'Some Project',
            'ixPerson': '3', 'ixBug': '5'}, changes[2])
        self.assertEqual({'sTitle': 'Some Title',
            'attachments': [('VENUES.pdf', 'default.asp?pg=pgDownload&pgType=pgFile&ixBugEvent=15&ixAttachment=3&sFileName=VENUES.pdf&sTicket=')],
            'tags': set([]), 'sStatus': 'Active', 'ixPersonAssignedTo': '3',
            'sCategory': 'Bug', 'ixBugParent': '0', 'ixPriority': u'3',
            'dt': '2010-03-13T00:48:13Z', 'sProject': 'Some Project',
            'ixPerson': '3', 'ixBug': '5'}, changes[3])
    def test_resolve_and_close(self):
        source = connection('resolve_and_close.xml')

        issues = list(get_issues(source, None))
        self.assertEqual(1, len(issues))
        changes = issues[0]
        self.assertEqual(3, len(changes))
        self.assertEqual({'sTitle': 'Realtime ETL process development',
            'attachments': [], 'tags': set([]), 'sStatus': 'Closed (Duplicate)',
            'ixPersonAssignedTo': '1', 'sCategory': 'Feature',
            'ixBugParent': '46', 'ixPriority': '3',
            'dt': '2010-05-14T06:46:25Z', 'sProject': 'USA - Data',
            'ixPerson': '7', 'ixBug': '94'}, changes[0])
        self.assertEqual({'sTitle': 'Realtime ETL process development',
            'attachments': [], 'tags': set([]), 'ixPersonAssignedTo': '1',
            'sCategory': 'Feature', 'ixBugParent': '46', 'ixPriority': '3',
            'ixBug': '94', 'dt': '2010-05-14T06:46:25Z',
            'sProject': 'USA - Data', 'ixPerson': '7', 'sStatus': 'Resolved'},
            changes[1])
        self.assertEqual({'sTitle': 'Realtime ETL process development',
            'attachments': [], 'tags': set([]), 'ixPersonAssignedTo': '7',
            'sCategory': 'Feature', 'ixBugParent': '46', 'ixPriority': '3',
            'ixBug': '94', 'dt': '2010-05-14T06:45:33Z',
            'sProject': 'USA - Data', 'ixPerson': '7', 'sStatus': 'Active'},
            changes[2])
def _get_commands(source, users, projects, search):
    """Returns a list of (cmd, params, files) tuples."""
    for issue in get_issues(source, search):
        cmd = None
        issue.reverse()
        for change in issue:
            status = change.pop('sStatus')
            if cmd is None:
                cmd = 'new'
            elif status == 'Active':
                cmd = 'edit'
            elif status.startswith('Resolved'):
                cmd = 'resolve'
            elif status.startswith('Closed'):
                cmd = 'close'
            else:
                raise ExportError('Unknown status %s!' % status)
            files = change.pop('attachments')
            # There is a bug in the api.xml that escapes the '&' characters
            # in the url, despite being in a CDATA section. Work around this
            # by unescaping it (again).
            files = [(filename, url.replace('&', '&')) for filename, url in files]

            yield (cmd, change, files)