def apply_preset(self, req, tickets, preset=None): if preset is None: return tickets presets = preset and [kw.split('=', 1) for kw in preset.split('&')] or [] fields = dict([(field, value) for field, value in presets]) warn = [] modified_tickets = [] if tickets and presets: db = self.env.get_db_cnx() ticket_module = TicketModule(self.env) action = fields.get('action') for ticket_id in tickets: if 'TICKET_CHGPROP' in req.perm('ticket', ticket_id): ticket = Ticket(self.env, ticket_id, db) ticket.populate(fields) if action: field_changes, problems = ticket_module.get_ticket_changes(req, ticket, action) if problems: for problem in problems: warn.append(problem) ticket_module._apply_ticket_changes(ticket, field_changes) # Apply changes made by the workflow ticket.save_changes(req.authname, None, db=db) modified_tickets.append(ticket_id) else: warn.append(_("You have no permission to modify ticket '%(ticket)s'", ticket=ticket_id)) db.commit() return { 'tickets' : modified_tickets, 'warnings': warn}
def wake_up(self, *args): db = self.env.get_db_cnx() cursor = db.cursor() # find still opened more recent milestone # select ticket whom milestone are due in less than specified delay cursor.execute(""" SELECT m.name FROM milestone m WHERE m.completed is NULL or m.completed = 0 AND m.due not NULL and m.due > 0 ORDER BY m.due ASC LIMIT 1 """) next_milestone = None for name, in cursor: next_milestone = name # select ticket whom milestone are due in less than specified delay cursor.execute(""" SELECT t.id , t.milestone FROM ticket t, milestone m WHERE t.status != 'closed' AND t.milestone = m.name AND m.completed not NULL and m.completed > 0 """) if next_milestone: for id, milestone in cursor: mess = "ticket %s is opened in closed milestone %s. Should postpone this ticket to %s" % ( id, milestone, next_milestone) self.env.log.debug(mess) ticket = Ticket(self.env, id) ticket.populate({'milestone': next_milestone}) ticket.save_changes(self.getId(), mess) else: self.env.log.debug( "No opened milestone found. Cannot postpone tickets")
def test_title_and_description_with_sub_vars_in_sql(self): with self.env.db_transaction: id_ = self._insert_report( 'Tickets on $M for $USER', '-- M=milestone1\r\n' 'SELECT * FROM ticket WHERE milestone=$M AND owner=$USER\r\n', 'Show tickets on $M for $USER') for milestone in ('milestone1', 'milestone2'): ticket = Ticket(self.env) ticket.populate({ 'status': 'new', 'summary': 'Test 1', 'owner': 'joe', 'milestone': milestone }) ticket.insert() req = MockRequest(self.env, path_info='/report/%d' % id_, authname='joe') self.assertTrue(self.report_module.match_request(req)) data = self.report_module.process_request(req)[1] self.assertEqual('{%d} Tickets on milestone1 for joe' % id_, data['title']) self.assertEqual('Show tickets on milestone1 for joe', data['description']) self.assertIsNone(data['message']) self.assertEqual(1, data['numrows'])
def test_populate_ticket(self): data = { "summary": "Hello world", "reporter": "john", "foo": "bar", "checkbox_cbon": "", "cbon": "on", "checkbox_cboff": "", } ticket = Ticket(self.env) ticket.populate(data) # Standard fields self.assertEqual("Hello world", ticket["summary"]) self.assertEqual("john", ticket["reporter"]) # An unknown field assert ticket["bar"] is None # Custom field self.assertEqual("bar", ticket["foo"]) # Custom field of type 'checkbox' self.assertEqual("on", ticket["cbon"]) self.assertEqual("0", ticket["cboff"])
def test_new_component_has_no_owner(self): """Ticket is not disowned when the component is changed to a component with no owner. """ self._add_component('component3', 'cowner3') self._add_component('component4', '') ticket = Ticket(self.env) ticket.populate({ 'reporter': 'reporter1', 'summary': 'the summary', 'component': 'component3', 'owner': 'cowner3', 'status': 'new', }) tkt_id = ticket.insert() req = MockRequest(self.env, method='POST', args={ 'id': tkt_id, 'field_component': 'component4', 'submit': True, 'action': 'leave', 'view_time': str(to_utimestamp(ticket['changetime'])), }) self.assertRaises(RequestDone, self.ticket_module.process_request, req) ticket = Ticket(self.env, tkt_id) self.assertEqual('component4', ticket['component']) self.assertEqual('cowner3', ticket['owner'])
def wake_up(self, *args): db = self.env.get_db_cnx() cursor = db.cursor() # find still opened more recent milestone # select ticket whom milestone are due in less than specified delay cursor.execute(""" SELECT m.name FROM milestone m WHERE m.completed is NULL or m.completed = 0 AND m.due not NULL and m.due > 0 ORDER BY m.due ASC LIMIT 1 """ ) next_milestone = None for name, in cursor: next_milestone = name # select ticket whom milestone are due in less than specified delay cursor.execute(""" SELECT t.id , t.milestone FROM ticket t, milestone m WHERE t.status != 'closed' AND t.milestone = m.name AND m.completed not NULL and m.completed > 0 """ ) if next_milestone: for id, milestone in cursor: mess = "ticket %s is opened in closed milestone %s. Should postpone this ticket to %s" % (id, milestone, next_milestone) self.env.log.debug(mess) ticket = Ticket(self.env, id) ticket.populate({'milestone':next_milestone}) ticket.save_changes(self.getId(),mess) else: self.env.log.debug("No opened milestone found. Cannot postpone tickets")
def add_tickets(self, project, customerrequest, tickets, reporter, notify=False): from trac.ticket.notification import TicketNotifyEmail from trac.util.text import exception_to_unicode from penelope.core.models.dashboard import User settings = get_current_registry().settings tracenvs = settings.get('penelope.trac.envs') request = get_current_request() for trac in project.tracs: for t in tickets: owner = DBSession.query(User).get(t['owner']) ticket = {'summary': t['summary'], 'description': t['description'], 'customerrequest': customerrequest.id, 'reporter': reporter.email, 'type': 'task', 'priority': 'major', 'milestone': 'Backlog', 'owner': owner.email, 'status': 'new'} tracenv = Environment('%s/%s' % (tracenvs, trac.trac_name)) tracenv.abs_href.base = trac.api_uri t = Ticket(tracenv) t.populate(ticket) t.insert() if notify: try: tn = TicketNotifyEmail(tracenv) tn.notify(t, newticket=True) except Exception, e: request.add_message('Failure sending notification on creation ' 'of a ticket #%s: %s' % (t.id, exception_to_unicode(e)), 'error')
def test_populate_ticket(self): data = { 'summary': 'Hello world', 'reporter': 'john', 'foo': 'bar', 'foo': 'bar', 'checkbox_cbon': '', 'cbon': 'on', 'checkbox_cboff': '' } ticket = Ticket(self.env) ticket.populate(data) # Standard fields self.assertEqual('Hello world', ticket['summary']) self.assertEqual('john', ticket['reporter']) # An unknown field self.assertRaises(KeyError, ticket.__getitem__, 'bar') # Custom field self.assertEqual('bar', ticket['foo']) # Custom field of type 'checkbox' self.assertEqual('on', ticket['cbon']) self.assertEqual('0', ticket['cboff'])
def test_title_and_description_with_sub_vars(self): with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( """INSERT INTO report (title,query,description) VALUES (%s,%s,%s)""", ('Tickets on $M for $USER', 'SELECT * FROM ticket WHERE milestone=$M AND owner=$USER', 'Show tickets on $M for $USER')) id_ = db.get_last_id(cursor, 'report') for milestone in ('milestone1', 'milestone2'): ticket = Ticket(self.env) ticket.populate({'status': 'new', 'summary': 'Test 1', 'owner': 'joe', 'milestone': milestone}) ticket.insert() req = MockRequest(self.env, path_info='/report/%d' % id_, authname='joe', args={'M': 'milestone2'}) self.assertTrue(self.report_module.match_request(req)) data = self.report_module.process_request(req)[1] self.assertEqual('{%d} Tickets on milestone2 for joe' % id_, data['title']) self.assertEqual('Show tickets on milestone2 for joe', data['description']) self.assertIsNone(data['message']) self.assertEqual(1, data['numrows'])
def test_component_change(self): """New ticket owner is updated when the component is changed. """ self._add_component('component3', 'cowner3') self._add_component('component4', 'cowner4') ticket = Ticket(self.env) ticket.populate({ 'reporter': 'reporter1', 'summary': 'the summary', 'component': 'component3', 'owner': 'cowner3', 'status': 'new', }) tkt_id = ticket.insert() req = self._create_request(method='POST', args={ 'id': tkt_id, 'field_component': 'component4', 'submit': True, 'action': 'leave', 'view_time': str(to_utimestamp(ticket['changetime'])), }) self.assertRaises(RequestDone, self.ticket_module.process_request, req) ticket = Ticket(self.env, tkt_id) self.assertEqual('component4', ticket['component']) self.assertEqual('cowner4', ticket['owner'])
def test_prop_whitespace_change_is_not_saved(self): ticket = Ticket(self.env) ticket.populate({'summary': 'ticket summary'}) ticket.insert() ticket['summary'] = ' ticket summary ' ticket.save_changes() self.assertEqual(0, len(ticket.get_changelog()))
def _create_ticket_with_change(self, old_props, new_props): """Create a ticket with `old_props` and apply properties in `new_props`. """ t = Ticket(self.env) t.populate(old_props) t.insert() t.populate(new_props) t.save_changes('actor') return t
def ticket_setup(tc): config = tc.env.config config.set('ticket-custom', 'custom1', 'text') config.save() ticket = Ticket(tc.env) ticket.populate({'reporter': 'santa', 'summary': 'This is the summary', 'status': 'new'}) ticket.insert()
def query2_setup(tc): ticket = Ticket(tc.env) ticket.populate({'reporter': 'santa', 'summary': 'This is the summary', 'status': 'new'}) ticket.insert() ticket = Ticket(tc.env) ticket.populate({'reporter': 'claus', 'summary': 'This is another summary', 'status': 'new'}) ticket.insert()
def _save_ticket_changes(self, req, selected_tickets, new_values, comment, action): """Save changes to tickets.""" valid = True for manipulator in self.ticket_manipulators: if hasattr(manipulator, 'validate_comment'): for message in manipulator.validate_comment(req, comment): valid = False add_warning(req, tag_("The ticket %(field)s is invalid: " "%(message)s", field=tag.strong(_('comment')), message=message)) tickets = [] for id_ in selected_tickets: t = Ticket(self.env, id_) values = self._get_updated_ticket_values(req, t, new_values) for ctlr in self._get_action_controllers(req, t, action): values.update(ctlr.get_ticket_changes(req, t, action)) t.populate(values) for manipulator in self.ticket_manipulators: for field, message in manipulator.validate_ticket(req, t): valid = False if field: add_warning(req, tag_("The ticket field %(field)s is " "invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning(req, message) tickets.append(t) if not valid: return when = datetime_now(utc) with self.env.db_transaction: for t in tickets: t.save_changes(req.authname, comment, when=when) for ctlr in self._get_action_controllers(req, t, action): ctlr.apply_action_side_effects(req, t, action) event = BatchTicketChangeEvent(selected_tickets, when, req.authname, comment, new_values, action) try: NotificationSystem(self.env).notify(event) except Exception as e: self.log.error("Failure sending notification on ticket batch" "change: %s", exception_to_unicode(e)) add_warning(req, tag_("The changes have been saved, but an error " "occurred while sending notifications: " "%(message)s", message=to_unicode(e)))
def update_ticket_on_db(self, ticketId, author, comment, req): db = self.env.get_db_cnx() ticket = Ticket(self.env, ticketId, db=db) ticket.populate(req.args) now = int(time.time()) ticket.save_changes(author, comment, when=now, db=db) db.commit() return True
def ticket_setup(tc): config = tc.env.config config.set('ticket-custom', 'custom1', 'text') config.save() ticket = Ticket(tc.env) ticket.populate({ 'reporter': 'santa', 'summary': 'This is the summary', 'status': 'new' }) ticket.insert()
def _create_ticket_with_change(self, old_props, new_props, author='anonymous'): """Create a ticket with `old_props` and apply properties in `new_props`. """ t = Ticket(self.env) t.populate(old_props) t.insert() comment = new_props.pop('comment', None) t.populate(new_props) t.save_changes(author, comment=comment) return t
def test_ignores_other_operations(self): """Ignores operations not defined by ConfigurableTicketWorkflow. """ self.env.config.set('ticket-workflow', 'review', 'assigned -> review') self.env.config.set('ticket-workflow', 'review.operations', 'CodeReview') ctw = ConfigurableTicketWorkflow(self.env) ticket = Ticket(self.env) ticket.populate({'summary': '#13013', 'status': 'assigned'}) ticket.insert() req = MockRequest(self.env) self.assertNotIn((0, 'review'), ctw.get_ticket_actions(req, ticket))
def test_empty_set_resolution(self): config = self.env.config['ticket-workflow'] config.set('resolve.set_resolution', '') self._reload_workflow() ticket = Ticket(self.env) ticket.populate({'summary': '#12882', 'status': 'new'}) ticket.insert() req = MockRequest(self.env, path_info='/ticket/%d' % ticket.id) try: self.ctlr.render_ticket_action_control(req, ticket, 'resolve') self.fail('ConfigurationError not raised') except ConfigurationError as e: self.assertIn('but none is defined', unicode(e))
def comment_setup(tc): ticket1 = Ticket(tc.env) ticket1.populate({'reporter': 'santa', 'summary': 'This is the summary for ticket 1', 'status': 'new'}) ticket1.insert() ticket1.save_changes(comment='This is the comment for ticket 1') ticket2 = Ticket(tc.env) ticket2.populate({'reporter': 'claws', 'summary': 'This is the summary for ticket 2', 'status': 'closed'}) ticket2.insert() ticket2.save_changes(comment='This is the comment for ticket 2')
def add_ticket(self, request, cleaned_data): """Initializes a trac ticket, saves it to the database, and returns the result.""" # Trac's version: trac.ticket.web_ui:375 (_process_newticket_request) data = { 'summary': cleaned_data['summary'], 'description': cleaned_data['description'], 'reporter': self._get_trac_user(request.user), } ticket = Ticket(self.env) ticket.populate(data) ticket_id = ticket.insert() return ticket_id
def fix_issue(self, db, data, author): """This base fix updates a ticket with the field/value pairs in data. The data object can either be a dict or a list of dicts with the 'ticket' field of each identifying the ticket to change.""" if not isinstance(data,list): data = [data] # update each ticket for changes in data: ticket = Ticket(self.env, changes['ticket']) del changes['ticket'] ticket.populate(changes) ticket.save_changes(author=author, comment='') return None
def fix_issue(self, db, data, author): """This base fix updates a ticket with the field/value pairs in data. The data object can either be a dict or a list of dicts with the 'ticket' field of each identifying the ticket to change.""" if not isinstance(data, list): data = [data] # update each ticket for changes in data: ticket = Ticket(self.env, changes['ticket']) del changes['ticket'] ticket.populate(changes) ticket.save_changes(author=author, comment='') return None
def query2_setup(tc): ticket = Ticket(tc.env) ticket.populate({ 'reporter': 'santa', 'summary': 'This is the summary', 'status': 'new' }) ticket.insert() ticket = Ticket(tc.env) ticket.populate({ 'reporter': 'claus', 'summary': 'This is another summary', 'status': 'new' }) ticket.insert()
def test_delete_milestone_retarget_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertEqual(False, milestone.exists) self.assertEqual('Other', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Other', Ticket(self.env, tkt2.id)['milestone'])
def test_update_milestone_update_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() self.assertEqual('Testing', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Testing', Ticket(self.env, tkt2.id)['milestone'])
def display_ticket_page(self, req, ticketId): # This method is based on process_request() in TicketModule. # todo: security check should go here # --- For security, only display ticket if it's req.perm.assert_permission('TICKET_VIEW') action = req.args.get('action', 'view') db = self.env.get_db_cnx() ticket = Ticket(self.env, ticketId, db=db) reporter_id = req.args.get('author') req.hdf['ticket.debug'] = self.debug if req.method == 'POST': if not req.args.has_key('preview'): self.save_ticket_form_data(req, db, ticket) else: # Use user supplied values ticket.populate(req.args) req.hdf['ticket.action'] = action req.hdf['ticket.ts'] = req.args.get('ts') req.hdf['ticket.reassign_owner'] = req.args.get('reassign_owner') \ or req.authname req.hdf['ticket.resolve_resolution'] = req.args.get('resolve_resolution') reporter_id = req.args.get('author') comment = req.args.get('comment') if comment: req.hdf['ticket.comment'] = comment # Wiki format a preview of comment req.hdf['ticket.comment_preview'] = wiki_to_html(comment, self.env, req, db) else: req.hdf['ticket.reassign_owner'] = req.authname # Store a timestamp in order to detect "mid air collisions" req.hdf['ticket.ts'] = ticket.time_changed self.insert_ticket_data_to_hdf(req, db, ticket) add_stylesheet(req, 'common/css/ticket.css') return 'autotracticket.cs', None
def test_delete_milestone_retarget_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertEqual('Other', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Other', Ticket(self.env, tkt2.id)['milestone'])
def comment_setup(tc): ticket1 = Ticket(tc.env) ticket1.populate({ 'reporter': 'santa', 'summary': 'This is the summary for ticket 1', 'status': 'new' }) ticket1.insert() ticket1.save_changes(comment='This is the comment for ticket 1') ticket2 = Ticket(tc.env) ticket2.populate({ 'reporter': 'claws', 'summary': 'This is the summary for ticket 2', 'status': 'closed' }) ticket2.insert() ticket2.save_changes(comment='This is the comment for ticket 2')
def test_delete_milestone_retarget_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({"summary": "Foo", "milestone": "Test"}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({"summary": "Bar", "milestone": "Test"}) tkt2.insert() milestone = Milestone(self.env, "Test") milestone.delete(retarget_to="Other") self.assertEqual(False, milestone.exists) self.assertEqual("Other", Ticket(self.env, tkt1.id)["milestone"]) self.assertEqual("Other", Ticket(self.env, tkt2.id)["milestone"])
def test_update_milestone_update_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() self.assertEqual('Testing', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Testing', Ticket(self.env, tkt2.id)['milestone'])
def test_update_milestone_update_tickets(self): cursor = self.db.cursor() cursor.execute("INSERT INTO milestone (name) VALUES ('Test')") cursor.close() tkt1 = Ticket(self.env) tkt1.populate({"summary": "Foo", "milestone": "Test"}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({"summary": "Bar", "milestone": "Test"}) tkt2.insert() milestone = Milestone(self.env, "Test") milestone.name = "Testing" milestone.update() self.assertEqual("Testing", Ticket(self.env, tkt1.id)["milestone"]) self.assertEqual("Testing", Ticket(self.env, tkt2.id)["milestone"])
def test_undefined_resolutions(self): config = self.env.config['ticket-workflow'] ticket = Ticket(self.env) ticket.populate({'summary': '#12882', 'status': 'new'}) ticket.insert() req = MockRequest(self.env, path_info='/ticket/%d' % ticket.id) config.set('resolve.set_resolution', 'fixed,invalid,wontfix,,duplicate,worksforme,,,,,') self._reload_workflow() self.ctlr.render_ticket_action_control(req, ticket, 'resolve') config.set('resolve.set_resolution', 'undefined,fixed') self._reload_workflow() try: self.ctlr.render_ticket_action_control(req, ticket, 'resolve') self.fail('ConfigurationError not raised') except ConfigurationError as e: self.assertIn('but uses undefined resolutions', unicode(e))
def add_trac_tickets(project, customer_request, settings, users): g = lipsum.Generator() tracname = idnormalizer.normalize(project.name) tracenvs = settings.get('penelope.trac.envs') trac_path = '%s/%s' % (tracenvs, tracname) tracenv = Environment(trac_path) for i in range(50): ticket = Ticket(tracenv) ticket.populate({ 'summary': g.generate_sentence(), 'description': g.generate_paragraph(), # 'project_id': project.id, # 'customerrequest': "%s>%s>%s" % (project.id, customer_request.id, customer_request.name), 'owner': random.choice(users).email, 'customerrequest': "%s" % (customer_request.id), 'status': 'new', }) ticket.insert() yield ticket
def process_request(self, req): """Iterate through all the ticket validation rules and if any them evaluate to true then the fields specified in that rule's "hidden" config will be hidden from the user on the ticket entry form.""" hiddenfields = [] ticket = Ticket(self.env) ticket.populate(req.args) BoolOperator.ticket = ticket for r in [x for x in self.get_rules() if x['enabled']]: result = self._grammar.parseString(r['condition'])[0] b = bool(result) self.log.debug('hidden field rule "%s": %s is %s' % (r['hidden'], str(result), b)) if b: for name in r['hidden']: field = [f for f in ticket.fields if f['name'] == name] hiddenfields.append("field-" + field[0]['name']) data = {'fields' : hiddenfields} return 'ajax_response.xml', data, 'text/xml'
def test_populate_ticket(self): data = {'summary': 'Hello world', 'reporter': 'john', 'foo': 'bar', 'checkbox_cbon': '', 'cbon': 'on', 'checkbox_cboff': ''} ticket = Ticket(self.env) ticket.populate(data) # Standard fields self.assertEqual('Hello world', ticket['summary']) self.assertEqual('john', ticket['reporter']) # An unknown field assert ticket['bar'] is None # Custom field self.assertEqual('bar', ticket['foo']) # Custom field of type 'checkbox' self.assertEqual('on', ticket['cbon']) self.assertEqual('0', ticket['cboff'])
def create_ticket_in_tasklist(cls, self, req): task_list = TaskList.load(self.env, id=req.args['tasklist_id']) ticket = Ticket(self.env) assert "field_summary" in req.args ticket_data = {field.split("field_")[1]: value for field, value in req.args.iteritems() if field.startswith("field_")} ticket_data['status'] = "new" ticket_data['reporter'] = (req.args.get("field_reporter") or get_reporter_id(req, 'author')) ticket.populate(ticket_data) ticket.insert() with self.env.db_transaction as db: # @@TODO assert tasklist with tasklist_id exists # @@TODO assert ticket with id exists # @@TODO assert child ticket with (tasklist_id, id) does not exist db("INSERT INTO task_list_child_ticket " " (task_list, ticket, `order`) VALUES " " (%s, %s, %s)", [req.args['tasklist_id'], ticket.id, req.args['order']]) task = task_list.get_ticket(ticket.id) task.actions = TasklistWorkflowManager(self.env).allowed_actions(task_list, req, task.ticket) data = { "task_list": task_list, "task": task, "workflow_manager": TasklistWorkflowManager(self.env), } list_item = Chrome(self.env).render_template(req, 'show_tasklist_ticket.html', data, 'text/html') return {"ok": "ok", "remove": False, "list_item": list_item, }
def setUp(self): self.env = EnvironmentStub(default_data=True) self.milestone1 = Milestone(self.env) self.milestone1.name = 'Test' self.milestone1.insert() self.milestone2 = Milestone(self.env) self.milestone2.name = 'Test2' self.milestone2.insert() tkt1 = Ticket(self.env) tkt1.populate({ 'summary': 'Foo', 'milestone': 'Test', 'owner': 'foman', 'status': 'new' }) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({ 'summary': 'Bar', 'milestone': 'Test', 'status': 'closed', 'owner': 'barman' }) tkt2.insert() tkt3 = Ticket(self.env) tkt3.populate({ 'summary': 'Sum', 'milestone': 'Test', 'owner': 'suman', 'status': 'reopened' }) tkt3.insert() self.tkt1 = tkt1 self.tkt2 = tkt2 self.tkt3 = tkt3 prov = DefaultTicketGroupStatsProvider(ComponentManager()) prov.env = self.env prov.config = self.env.config self.stats = prov.get_ticket_group_stats([tkt1.id, tkt2.id, tkt3.id])
def _new_ticket(self, env, ticket_dict): ticket = Ticket(env) ticket.populate(ticket_dict) return ticket.insert()
def _insert_ticket(self, when=None, **values): values.setdefault('status', 'new') values.setdefault('type', 'defect') ticket = Ticket(self.env) ticket.populate(values) return ticket.insert(when=when)
def add_trac_to_project(application, smtp_enabled=True, privatecomments=True, sensitivetickets=True, batchmod=True, autocompleteusers=True, customfieldadmin=True, qafields=True, privatetickets=False, tracwysiwyg=True, attachment_max_size=10485760, milestones=[], tickets=[], project_name=u'', ): from penelope.core.models.dashboard import Project project = DBSession.query(Project).get(application.project_id) settings = get_current_registry().settings or application.settings tracenvs = settings.get('penelope.trac.envs') if not os.path.exists(tracenvs): # TODO: logging bootstrap os.mkdir(tracenvs) tracname = None idx = '' while (not tracname): tracname = idnormalizer.normalize("%s%s" % (project.id, idx)) trac_path = '%s/%s' % (tracenvs, tracname) if os.path.exists(trac_path): idx = idx and (idx+1) or 1 tracname = None trac_perm = { 'administrator': ['TRAC_ADMIN', 'EXTRA_TIMEENTRY'], 'customer': ['TICKET_CREATE', 'TICKET_MODIFY', 'TICKET_VIEW', 'WIKI_VIEW', 'XML_RPC', 'CHANGESET_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW', 'REPORT_SQL_VIEW', 'ROADMAP_VIEW', 'SEARCH_VIEW', 'TIMELINE_VIEW'], 'developer': ['TICKET_CREATE', 'TICKET_MODIFY', 'TICKET_VIEW', 'WIKI_VIEW', 'XML_RPC', 'WIKI_CREATE', 'WIKI_MODIFY', 'CHANGESET_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW', 'REPORT_SQL_VIEW', 'ROADMAP_VIEW', 'SEARCH_VIEW', 'TIMELINE_VIEW', 'REPORT_ADMIN'], 'internal_developer': ['developer', 'TRAC_ADMIN', 'TIME_ENTRY_ADD'], 'external_developer': ['developer'], 'project_manager': ['administrator', 'TIME_ENTRY_ADD'], } pordb = str(DBSession.bind.url) run([trac_path, 'initenv "%s" "%s?schema=trac_%s"' % ( tracname, pordb.replace('postgresql://', 'postgres://'), tracname)]) tracenv = Environment(trac_path) # REPORTS cnx = tracenv.get_db_cnx().cnx cursor = cnx.cursor() cursor.executemany(\ "INSERT INTO report (title, description, query) VALUES (%s, %s, %s)", get_reports(project_id=project.id)) cursor.close() cnx.commit() # remove report 2 cursor = cnx.cursor() cursor.execute("DELETE FROM report where id=2;") cursor.close() cnx.commit() # update reports cursor = cnx.cursor() cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, t.id AS ticket, summary, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY CAST(p.value AS int), milestone, t.type, time' where id=1;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, ''Milestone ''||milestone AS __group__, t.id AS ticket, summary, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY (milestone IS NULL),milestone, CAST(p.value AS int), t.type, time' where id=3;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, owner AS __group__, t.id AS ticket, summary, milestone, cr.name AS CR, t.type AS type, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status = ''accepted'' AND tc.name = ''customerrequest'' ORDER BY owner, CAST(p.value AS int), t.type, time' where id=4;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, owner AS __group__, t.id AS ticket, summary, milestone, t.type AS type, cr.name AS CR, time AS created, t.description AS _description_, changetime AS _changetime, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status = ''accepted'' AND tc.name = ''customerrequest'' ORDER BY owner, CAST(p.value AS int), t.type, time' where id=5;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, (CASE status WHEN ''accepted'' THEN ''Accepted'' ELSE ''Owned'' END) AS __group__, t.id AS ticket, summary, milestone, cr.name AS CR, t.type AS type, priority, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE t.status <> ''closed'' AND owner = $USER AND tc.name = ''customerrequest'' ORDER BY (status = ''accepted'') DESC, CAST(p.value AS int), milestone, t.type, time' where id=7;""") cursor.execute("""UPDATE report set query=' SELECT p.value AS __color__, (CASE owner WHEN $USER THEN ''My Tickets'' ELSE ''Active Tickets'' END) AS __group__, t.id AS ticket, summary, milestone, t.type AS type, cr.name AS CR, owner, status, time AS created, changetime AS _changetime, t.description AS _description, reporter AS _reporter FROM ticket t LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority'' LEFT JOIN ticket_custom tc ON t.id = tc.ticket LEFT JOIN public.customer_requests cr ON tc.value = cr.id WHERE status <> ''closed'' AND tc.name = ''customerrequest'' ORDER BY (COALESCE(owner, '''') = $USER) DESC, CAST(p.value AS int), milestone, t.type, time' where id=8;""") cursor.close() cnx.commit() # remove default trac's milestones, components, versions cursor = cnx.cursor() cursor.execute("DELETE FROM milestone;") cursor.close() cnx.commit() cursor = cnx.cursor() cursor.execute("DELETE FROM component;") cursor.close() cnx.commit() cursor = cnx.cursor() cursor.execute("DELETE FROM version;") cursor.close() cnx.commit() # TODO: attualmente il riferimento dal trac al progetto dashboard è il project_id, # considerando un'instalalzzione che condicide lo stesso stack wsgi, # valutare se deve essere una uri (jsnorpc, xmlrpx, ...) # o un dsn che punti al db, o ... tracenv.config.set('por-dashboard', 'project-id', application.project_id) # custom templates templates = settings.get('penelope.trac.templates') masterconfig = settings.get('penelope.trac.masterconfig') if templates: tracenv.config.set('inherit', 'templates_dir', templates) # master config if masterconfig: tracenv.config.set('inherit', 'file', masterconfig) # set name and description tracenv.config.set('project', 'name', project_name) tracenv.config.set('project', 'descr', application.name) tracenv.config.set('notification', 'smtp_enabled', smtp_enabled and 'true' or 'false') tracenv.config.set('notification', 'always_notify_owner', 'true') tracenv.config.set('notification', 'always_notify_reporter', 'true') manager_email = project.manager.email or '' tracenv.config.set('notification', 'smtp_always_cc', manager_email) # manager should always receiv CC tracenv.config.set('attachment', 'max_size', attachment_max_size) tracenv.config.set('components', 'penelope.trac.*', 'enabled') tracenv.config.set('components', 'themeengine.admin.*', 'enabled') tracenv.config.set('components', 'themeengine.api.*', 'enabled') tracenv.config.set('components', 'themeengine.web_ui.*', 'enabled') tracenv.config.set('components', 'ticketrelations.*', 'enabled') tracenv.config.set('components', 'tracopt.perm.config_perm_provider.extrapermissionsprovider', 'enabled') # All the custom permission names are converted to uppercase. # It is not possible to have lowercase and distinguish them from the standard permissions. tracenv.config.set('extra-permissions', 'extra_timeentry', 'TIME_ENTRY_ADD') tracenv.config.set('theme','theme', 'por') # ticket-custom tracenv.config.set('ticket-custom', 'customerrequest', 'select') tracenv.config.set('ticket-custom', 'customerrequest.label', 'Customer Request') tracenv.config.set('ticket-custom', 'blocking', 'text') tracenv.config.set('ticket-custom', 'blocking.label', 'Blocking') tracenv.config.set('ticket-custom', 'blockedby', 'text') tracenv.config.set('ticket-custom', 'blockedby.label', 'Blocked By') # BBB: ii valori di customerrequest vengono caricati ondemand tracenv.config.set('ticket-custom', 'customerrequest.options', '') # see ticket:80 if qafields: tracenv.config.set('ticket-custom', 'issuetype', 'select') tracenv.config.set('ticket-custom', 'issuetype.label', 'Natura del problema') tracenv.config.set('ticket-custom', 'issuetype.options', '|'.join([u"", u"sistemistica", u"funzionalità", u"design (grafica, layout...)", u"prestazioni", u"mi aspettavo che..."])) tracenv.config.set('ticket-custom', 'esogeno', 'checkbox') tracenv.config.set('ticket-custom', 'esogeno.label', 'Ticket aperto dal Cliente') tracenv.config.set('ticket-custom', 'esogeno.value', 'false') tracenv.config.set('ticket-custom', 'stats_exclude', 'checkbox') tracenv.config.set('ticket-custom', 'stats_exclude.label', 'Exclude from report stats') tracenv.config.set('ticket-custom', 'stats_exclude.value', 'false') tracenv.config.set('ticket-custom', 'fasesviluppo', 'select') tracenv.config.set('ticket-custom', 'fasesviluppo.label', 'Fase sviluppo') tracenv.config.set('ticket-custom', 'fasesviluppo.options', '|'.join([u"", u"In lavorazione", u"Per lo staging", u"In staging", u"Per la produzione", u"In produzione"])) tracenv.config.set('ticket', 'restrict_owner', 'true') tracenv.config.set('ticket-custom', 'milestone.invalid_if', '') # WORKFLOW tracenv.config.set('ticket-workflow', 'accept', 'new,reviewing -> assigned') tracenv.config.set('ticket-workflow', 'accept.operations', 'set_owner_to_self') tracenv.config.set('ticket-workflow', 'accept.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'leave', '* -> *') tracenv.config.set('ticket-workflow', 'leave.default', '1') tracenv.config.set('ticket-workflow', 'leave.operations', 'leave_status') tracenv.config.set('ticket-workflow', 'reassign', 'new,assigned,accepted,reopened -> assigned') tracenv.config.set('ticket-workflow', 'reassign.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'reassign.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'reassign_reviewing', 'reviewing -> *') tracenv.config.set('ticket-workflow', 'reassign_reviewing.name', 'reassign review') tracenv.config.set('ticket-workflow', 'reassign_reviewing.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'reassign_reviewing.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'reopen', 'closed -> reopened') tracenv.config.set('ticket-workflow', 'reopen.operations', 'del_resolution') tracenv.config.set('ticket-workflow', 'reopen.permissions', 'TRAC_ADMIN') tracenv.config.set('ticket-workflow', 'resolve', 'new,assigned,reopened,reviewing -> closed') tracenv.config.set('ticket-workflow', 'resolve.operations', 'set_resolution') tracenv.config.set('ticket-workflow', 'resolve.permissions', 'TICKET_MODIFY') tracenv.config.set('ticket-workflow', 'review', 'new,assigned,reopened -> reviewing') tracenv.config.set('ticket-workflow', 'review.operations', 'set_owner') tracenv.config.set('ticket-workflow', 'review.permissions', 'TICKET_MODIFY') tracenv.config.set('milestone-groups', 'closed', 'closed') tracenv.config.set('milestone-groups', 'closed.order', '0') tracenv.config.set('milestone-groups', 'closed.query_args', 'group=resolution') tracenv.config.set('milestone-groups', 'closed.overall_completion', 'true') tracenv.config.set('milestone-groups', 'active', '*') tracenv.config.set('milestone-groups', 'active.order', '1') tracenv.config.set('milestone-groups', 'active.css_class', 'open') tracenv.config.set('milestone-groups', 'new', 'new,reopened') tracenv.config.set('milestone-groups', 'new.order', '2') # navigation tracenv.config.set('metanav', 'logout', 'disabled') # permissions tracenv.config.set('components', 'penelope.trac.users.*', 'enabled') tracenv.config.set('trac', 'permission_store', 'PorPermissionStore') tracenv.config.set('trac', 'show_email_addresses', 'true') # xmlrpc plugin tracenv.config.set('components', 'tracrpc.api.xmlrpcsystem', 'enabled') tracenv.config.set('components', 'tracrpc.xml_rpc.xmlrpcprotocol', 'enabled') tracenv.config.set('components', 'tracrpc.web_ui.rpcweb', 'enabled') tracenv.config.set('components', 'tracrpc.ticket.*', 'enabled') # DynamicFields Plugin tracenv.config.set('components', 'dynfields.rules.*','enabled') tracenv.config.set('components', 'dynfields.web_ui.*','enabled') # User & Roles (ReadOnly !!!) # tracenv.config.set('user_manager', 'user_store', 'PorUserStore') # tracenv.config.set('user_manager', 'attribute_provider', 'PorAttributeProvider') # BatchModifyPlugin if batchmod: tracenv.config.set('components', 'batchmod.web_ui.*', 'enabled') # Traccustomfieldadmin if customfieldadmin: tracenv.config.set('components', 'customfieldadmin.*', 'enabled') # PrivateCommentsPlugin if privatecomments: tracenv.config.set('components', 'privatecomments.privatecomments.*', 'enabled') # PrivateTicketPlugin if privatetickets: tracenv.config.set('components', 'privatetickets.policy.*', 'enabled') tracenv.config.set('trac', 'permission_policies', 'PrivateTicketsPolicy, %s' % tracenv.config.get('trac', 'permission_policies')) trac_perm['customer'].append('TICKET_VIEW_SELF') # SensitiveTicketsPlugin if sensitivetickets: tracenv.config.set('components', 'sensitivetickets.*', 'enabled') tracenv.config.set('trac', 'permission_policies', 'SensitiveTicketsPolicy, %s' % tracenv.config.get('trac', 'permission_policies')) tracenv.config.set('ticket-custom', 'sensitive.show_if_group', 'administrator|developer') # utilizzato se il default e' 1, se il default e' 0 non serve # tracenv.config.set('ticket-custom', 'sensitive.clear_on_hide', 'false') # tracenv.config.set('ticket-custom', 'sensitive.has_permission', 'SENSITIVE_VIEW') tracenv.config.set('ticket-custom', 'sensitive.value', '0') trac_perm['developer'].append('SENSITIVE_VIEW') # tracwysiwyg if tracwysiwyg: tracenv.config.set('components', 'tracwysiwyg.wysiwygmodule', 'enabled') # AutoCompleteUsers if autocompleteusers: tracenv.config.set('components', 'autocompleteusers.autocompleteusers', 'enabled') tracenv.config.set('wiki', 'max_size', 1048576) tracenv.config.set('logging', 'log_file', 'trac.log') tracenv.config.set('logging', 'log_level', 'INFO') tracenv.config.set('logging', 'log_type', 'file') tracenv.config.save() # let's remove notification config - it is set by the global config tracenv.config.remove('notification', 'smtp_from') tracenv.config.remove('notification', 'smtp_from_name') tracenv.config.remove('notification', 'replyto') tracenv.config.remove('notification', 'smtp_replyto') tracenv.config.remove('notification', 'email_sender') tracenv.config.remove('notification', 'smtp_enabled') tracenv.config.remove('notification', 'smtp_host') tracenv.config.remove('notification', 'smtp_port') tracenv.config.remove('notification', 'smtp_password') tracenv.config.remove('notification', 'smtp_username') tracenv.config.remove('trac', 'repository_sync_per_request') tracenv.config.save() run([trac_path, 'upgrade --no-backup']) run([trac_path, 'permission remove anonymous *']) run([trac_path, 'permission remove authenticated *']) for role, perms in trac_perm.items(): for perm in perms: run([trac_path, "permission add %s %s" % (role, perm)]) run([trac_path, "ticket_type add verification"]) # hack to minimize config size run([trac_path, 'config set browser color_scale True']) # add properly milestones milestones.append({'title': 'Backlog', 'due_date': date.today().replace(year=date.today().year+1)}) for milestone in milestones: due = milestone['due_date'] if due: due = milestone['due_date'].strftime('%Y-%m-%d') run([trac_path, 'milestone add "%s" %s' % (milestone['title'], due)]) else: run([trac_path, 'milestone add "%s"' % milestone['title']]) tracenv = Environment(trac_path) for ticket in tickets: # in this moment the customer request has a proper id ticket['status'] = 'new' t = Ticket(tracenv) t.populate(ticket) t.insert() application.api_uri = 'trac://%s' % tracname application.trac_name = tracname
def _get_actions(self, ticket_dict): ts = TicketSystem(self.env) ticket = Ticket(self.env) ticket.populate(ticket_dict) id = ticket.insert() return ts.get_available_actions(self.req, Ticket(self.env, id))
def _create_or_update_ticket(self, runid, testaction, comment, req): """ creates or updates a ticket for an action (default ticket type if failed, enhancement if otherwise (e.g. passed with comment)) update means add a comment to the already created ticket and add keyword if neccesary """ self.dbg('accordion.request._create_or_update_ticket(%s)' % req.args) testrun = Ticket(self.env, tkt_id=runid) display = get_display_states(self.env) # build ticket summary: <todo>: <action> of <testcase>, e.g.: # 'Creator checks in the model of TcCaddocCreate failed.' todo = STATES_DISPLAY[testaction.status] testcase = models.TestCaseQuery(self.env, tcid=testaction.tcid).execute()[0] summary = "%s of %s %s." % ( testaction.title, testcase.wiki, display[todo]) # build description description = "Related test case: %s.\n\n%s" % ( self._build_tc_link(testaction, req), comment ) # check if a similar ticket already exists... existing_tickets = Query.from_string( self.env, "summary=%s" % summary ).execute() if existing_tickets: # if yes return the ticket id t = Ticket(self.env, existing_tickets[0]['id']) tp_title = self._get_testplan_title(testrun) if t['keywords']: kws = t['keywords'].split(',') if not tp_title in kws: t['keywords'] += ',%s' % tp_title else: t['keywords'] = tp_title t.save_changes(author=req.authname, comment=description) return t.id # build the ticket ticket = Ticket(self.env) # determine type of ticket ticket_type = ticket.get_default('type') if testaction.status != FAILED: ticket_type = 'enhancement' data = { 'reporter': req.authname, 'summary': summary, 'type': ticket_type, 'description': description, 'priority': req.args.get('priority', 'major'), 'status': 'new', 'keywords': self._get_testplan_title(testrun), } self.dbg('ticket data: %s' % data) try: ticket.populate(data) tid = ticket.insert() ticket.save_changes() except TracError as e: self.env.log.error(e) raise TracError( safe_unicode( "ticket could not be created: %s" % e.message ) ) return tid
def _import_trackers(self, meta, reset): types = set() components = set() priorities = set() resolutions = set() for tracker in meta['trackers']: types.add(tracker['label']) v = tracker['vocabulary'] for x in v.get('Category', []): components.add(x) for x in v.get('priority', []): priorities.add(x) for x in v.get('Resolution', []): resolutions.add(x) if reset: @self.env.with_transaction() def do_reset_components(db): cursor = db.cursor() cursor.execute("DELETE FROM component") cursor.execute("DELETE FROM milestone") cursor.execute("DELETE FROM enum") cursor.execute("DELETE FROM version") cursor.execute("DELETE FROM ticket") cursor.execute("DELETE FROM ticket_change") @self.env.with_transaction() def do_reset_ticket_meta(db): cursor = db.cursor() for x in sorted(components): cursor.execute( "INSERT INTO component (name, owner, description) VALUES (%s, 'admin', %s)", (x, x)) for i, x in enumerate(sorted(types)): cursor.execute( "INSERT INTO enum (type, name, value) VALUES ('ticket_type', %s, %s)", (x, i + 1)) for i, x in enumerate(sorted(priorities)): cursor.execute( "INSERT INTO enum (type, name, value) VALUES ('priority', %s, %s)", (x, i + 1)) for i, x in enumerate(sorted(resolutions)): cursor.execute( "INSERT INTO enum (type, name, value) VALUES ('resolution', %s, %s)", (x, i + 1)) for tracker in meta['trackers']: label = tracker['label'] for a in tracker['artifacts']: owner = a.get('assigned_to', '') if owner == 'Nobody' or owner == 'No Change': owner = '' ticket = Ticket(self.env) #, a['id']) ticket.populate({ 'summary': a.get('summary', ''), 'reporter': a.get('submitter', ''), 'description': a.get('description', ''), 'type': label, 'priority': a.get('priority', ''), 'component': a.get('Category', ''), 'owner': owner, 'status': a.get('status', ''), 'cc': '', 'keywords': '', }) created = dateutil.parser.parse(a['date']) ticket.insert(created) changes = a['comments'] changes.extend(a['history']) lastcom = None for c in sorted(changes, key=lambda x: x['date']): modified = dateutil.parser.parse(c['date']) if c['class'] == 'COMMENT': author = c.get('submitter', '') comment = c.get('comment', '') if lastcom != None and lastcom == modified: modified += datetime.timedelta(0, 10) lastcom = modified elif c['class'] == 'FIELDCHANGE': author = c['by'] comment = None field = c['field'] value = c['old'] # WTF? if field == 'status': ticket.status = value elif field == 'Resolution': ticket.resolution = value elif field == 'assigned_to': ticket.owner = value elif field == 'close_date': continue ticket.save_changes(author, comment, modified) return 'OK'