diff --git a/0.11/talm_importer/importer.py b/0.11/talm_importer/importer.py index f63a549..fe7b6f7 100644 --- a/0.11/talm_importer/importer.py +++ b/0.11/talm_importer/importer.py @@ -191,6 +191,12 @@ class ImportModule(Component): commentfield = None lowercaseimportedfields = [f.lower() for f in importedfields] + # Fields which contain relative ticket numbers to update after import + relativeticketfields = [f for f in columns if f.startswith('#')] + if relativeticketfields != []: + notimportedfields = [f for f in notimportedfields if f not in relativeticketfields] + relativeticketfields = [f[1:].lower() for f in relativeticketfields] + idcolumn = None if 'ticket' in lowercaseimportedfields and 'id' in lowercaseimportedfields: @@ -241,6 +247,8 @@ class ImportModule(Component): processor.start(importedfields, ownercolumn != None, commentfield) missingfields = [f for f in computedfields if f not in lowercaseimportedfields] + if relativeticketfields != []: + missingfields = [f for f in missingfields if f not in relativeticketfields] missingemptyfields = [ f for f in missingfields if computedfields[f] == None or computedfields[f]['value'] == ''] missingdefaultedfields = [ f for f in missingfields if f not in missingemptyfields] @@ -300,6 +308,7 @@ class ImportModule(Component): duplicate_summaries = [] + relativeticketvalues = [] row_idx = 0 for row in rows: @@ -350,16 +359,23 @@ class ImportModule(Component): if commentfield: processor.process_comment(row[commentfield]) + relativeticketvalues.append([row[c] for c in row if c.startswith('#')]) + processor.end_process_row() row_idx += 1 + # All the rows have been processed. Handle global stuff + if newvalues != {} and reduce(lambda x, y: x == [] and y or x, newvalues.values()) != []: processor.process_new_lookups(newvalues) if newusers != []: processor.process_new_users(newusers) + if relativeticketfields != []: + processor.process_relativeticket_fields(relativeticketvalues, relativeticketfields) + return processor.end_process(row_idx) diff --git a/0.11/talm_importer/processors.py b/0.11/talm_importer/processors.py index 8b35e21..ac0d1d0 100644 --- a/0.11/talm_importer/processors.py +++ b/0.11/talm_importer/processors.py @@ -31,6 +31,8 @@ class ImportProcessor(object): self.computedfields = None def start(self, importedfields, reconciliate_by_owner_also, has_comments): + # Index by row index, returns ticket id + self.crossref = [] pass def process_missing_fields(self, missingfields, missingemptyfields, missingdefaultedfields, computedfields): @@ -113,6 +115,7 @@ class ImportProcessor(object): self.ticket.save_changes(get_reporter_id(self.req), message, when=tickettime, db=self.db) # TODO: handle cnum, cnum = ticket.values['cnum'] + 1) self.modified[self.ticket.id] = 1 + self.crossref.append(self.ticket.id) self.ticket = None def process_new_lookups(self, newvalues): @@ -140,6 +143,47 @@ class ImportProcessor(object): def process_new_users(self, newusers): pass + + # Rows is an array of lists. + # Each list is in the same order as relativeticketfields. + def process_relativeticket_fields(self, rows, relativeticketfields): + from ticket import PatchedTicket + + row_idx = 0 + for row in rows: + id = self.crossref[row_idx] + + # Get the ticket (added or updated in the main loop + ticket = PatchedTicket(self.env, tkt_id=id, db=self.db) + + col_idx = 0 + for f in relativeticketfields: + # Get the value of the relative field column (e.g., "2,3") + v = row[col_idx] + # If it's not empty, process the contents + s = [] + if len(v) > 0: + for r in v.split(","): + # Make the string value an integer + r = int(r) + + # The relative ticket dependencies are 1-based, + # array indices are 0-based. Convert and look up + # the new ticket ID of the other ticket. + i = self.crossref[r-1] + + # TODO check that i != id + + s.append(str(i)) + + # Empty or not, use it to update the ticket + ticket[f] = ', '.join(s) + + col_idx += 1 + + ticket.save_changes(get_reporter_id(self.req), '', db=self.db) + row_idx += 1 + def end_process(self, numrows): self.db.commit() @@ -213,6 +257,9 @@ class PreviewProcessor(object): def process_notimported_fields(self, notimportedfields): self.message += ' * Some fields will not be imported because they don\'t exist in Trac: ' + ', '.join([x and x or "''(empty name)''" for x in notimportedfields]) + '.\n' + def process_relativeticket_fields(self, rows, relativeticketfields): + self.message += ' * Relative ticket numbers will be processed in: ' + ', '.join([x and x or "''(empty name)''" for x in relativeticketfields]) + '.\n' + def process_comment_field(self, comment): self.message += ' * The field "%s" will be used as comment when modifying tickets, and appended to the description for new tickets.\n' % comment diff --git a/0.11/talm_importer/templates/importer.html b/0.11/talm_importer/templates/importer.html index 18f5a52..c68a62f 100644 --- a/0.11/talm_importer/templates/importer.html +++ b/0.11/talm_importer/templates/importer.html @@ -80,6 +80,28 @@ or:
+If a field name starts with #, the contents of the field will +be treated as row numbers and be replaced by the ticket numbers for +the tickets in the corresponding row. For example: +
+summary | #blockedby |
Design schematic | |
Layout board | 1 |
Check board | 2 |
Manufacture prototypes | 3 |
Verify prototypes | 4 |
Once imported, this might become:
+id | summary | blockedby |
3000 | Design schematic | |
3001 | Layout board | 3000 |
3003 | Check board | 3001 |
3004 | Manufacture prototypes | 3003 |
3005 | Verify prototypes | 3004 |
First, you will be shown a preview of the changes, and then you will be able to execute the import. The first step (uploading and previewing) will not modify the database.