def clear_ticket_system_field_cache(self): # In Trac 0.12, fields will be cached by new functionality and this # cache will be initialized before our custom field configuration was # read so we need to force a reload ticket_system = AgiloTicketSystem(self.env) if not ticket_system.is_trac_012(): return del ticket_system.fields del ticket_system.custom_fields
def create_field(self, field_name, field_type, **field_options): config = AgiloConfig(self.env) ticket_custom = config.get_section('ticket-custom') ticket_custom.change_option(field_name, field_type) for (option_name, option_value) in field_options.items(): key = '%s.%s' % (field_name, option_name) ticket_custom.change_option(key, option_value) config.save() ticket_system = AgiloTicketSystem(self.env) all_known_fields = ticket_system.fieldnames(ticket_system.get_ticket_fields()) assert_contains(field_name, all_known_fields)
def _get_field_names(self, backlog): """Return an ordered collection of all field names which appear in one of the tickets for this backlog.""" ticket_types = set() for bi in backlog: ticket_types.add(bi[Key.TYPE]) field_names = set() ats = AgiloTicketSystem(self.env) for ticket_type in ticket_types: for field in ats.get_ticket_fields(ticket_type): field_names.add(field[Key.NAME]) return list(field_names)
def create_field(self, field_name, field_type, **field_options): config = AgiloConfig(self.env) ticket_custom = config.get_section('ticket-custom') ticket_custom.change_option(field_name, field_type) for (option_name, option_value) in field_options.items(): key = '%s.%s' % (field_name, option_name) ticket_custom.change_option(key, option_value) config.save() ticket_system = AgiloTicketSystem(self.env) all_known_fields = ticket_system.fieldnames( ticket_system.get_ticket_fields()) assert_contains(field_name, all_known_fields)
def convert_owner_field_to_select_if_needed(self, field_dict, backlog): if not AgiloTicketSystem(self.env).restrict_owner: return owner_properties = field_dict.get(Key.OWNER) if owner_properties is None: return sprint_name = None if backlog.is_sprint_backlog(): sprint_name = backlog.sprint().name AgiloTicketSystem(self.env).eventually_restrict_owner( owner_properties, sprint_name=sprint_name)
def reset_db(self, default_data=None): from agilo.test.functional.api import EnvironmentBuilder env = EnvironmentBuilder.get_testenv(self.env_key) from trac.db.api import _parse_db_str scheme, db_prop = _parse_db_str(env.get_db_url()) if scheme != 'sqlite' and not default_data: return super(BetterEnvironmentStub, self).reset_db(default_data) env_for_transaction = env.get_trac_environment() if AgiloTicketSystem.is_trac_1_0(): env_for_transaction = env tables = [] if scheme != 'sqlite': db = self.get_db_cnx() @with_transaction(env_for_transaction, db) def implementation(db): cursor = db.cursor() cursor.execute("update system set value='9999' WHERE name='database_version'") db.commit() tables = super(BetterEnvironmentStub, self).reset_db(default_data) else: from trac import db_default from trac.db_default import schema from trac.db.sqlite_backend import _to_sql # our 'destroy_db' db = self.get_db_cnx() @with_transaction(env_for_transaction, db) def implementation(db): cursor = db.cursor() cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") tables = cursor.fetchall() for table in tables: cursor.execute("DROP TABLE %s" % table) # part of sqlite_backend's init_db for table in schema: for stmt in _to_sql(table): cursor.execute(stmt) # part of reset_db for table, cols, vals in db_default.get_data(db): cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) db.commit() if env.tester.testcase.testtype != 'unittest': try: env._upgrade_environment() env._setup_users_and_permissions() except: # it's possible that this has already happened print "Warning: Exception on post-reset_db tasks" return tables
def wait_if_last_change_was_within_this_second(self, req, ticket_id, args): if AgiloTicketSystem(self.env).is_trac_012(): return # Trac can't save two ticket changes within one second last_changetime = self.get_last_changetime( self.get_ticket_as_json(req, ticket_id)) if last_changetime == self.get_last_changetime(args): time.sleep(1)
def get_enabled_components(self): components = self.super() from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): components += ['agilo.*', 'webadmin.*', 'acct_mgr.*', 'tracopt.versioncontrol.svn.*'] else: components += ['agilo.*', 'webadmin.*', 'acct_mgr.*'] return components
def runTest(self): self.tester.login_as(Usernames.team_member) task_id = self.tester.create_new_agilo_task('Foo') last_change = self.tester.get_time_of_last_change(task_id) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): self.tester.edit_ticket(task_id, summary='fnord', view_time='12') else: self.tester.edit_ticket(task_id, summary='fnord', ts='12') tc.find('This ticket has been modified by someone else since you started') if AgiloTicketSystem.is_trac_1_0(): self.tester.edit_ticket(task_id, summary='fnord', view_time=str(last_change)) else: self.tester.edit_ticket(task_id, summary='fnord', ts=str(last_change)) tc.notfind('This ticket has been modified by someone else since you started') tc.find('fnord')
def has_permission(self, username, action): # DefaultPermissionPolicy will cache permissions for 5 seconds so we # need to reset the cache DefaultPermissionPolicy(self.env).permission_cache = {} if AgiloTicketSystem.is_trac_1_0(): del PermissionSystem(self.env).store._all_permissions return PermissionSystem(self.env).check_permission(action, username)
def __init__(self, env, req, ticket): self.ats = AgiloTicketSystem(env) self.req = req self.ticket = ticket self._old_status_stack = [] self._status_stack = [] self.candidate_queue = None self.checked_status = None
def rename_fields(self, req, ticket_type=None): fields = AgiloTicketSystem(self.env).get_ticket_fields(ticket_type) for field in fields: name = field[Key.NAME] if name in req.args: value = req.args[name] if not isinstance(value, basestring): value = str(value) del req.args[name] req.args['field_' + name] = value
def update_contains_can_edit(self, args): json = self.ticket_as_json(self.task) json.update(args) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp json.update(dict(view_time=str(to_utimestamp(self.task.time_changed)), submit=True)) view = TicketUpdateView(self.env) result = view.do_post(self.req, json) return result['can_edit']
def get_enabled_components(self): components = self.super() from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): components += [ 'agilo.*', 'webadmin.*', 'acct_mgr.*', 'tracopt.versioncontrol.svn.*' ] else: components += ['agilo.*', 'webadmin.*', 'acct_mgr.*'] return components
def _get_action_controllers_for_action(self, action): def _get_actions_for(controller): weighted_actions = controller.get_ticket_actions( self.req, self.ticket) actions = [action for (weight, action) in weighted_actions] return actions controllers = [] ats = AgiloTicketSystem(self.env) for controller in ats.action_controllers: if action in _get_actions_for(controller): controllers.append(controller) return controllers
def update_contains_can_edit(self, args): json = self.ticket_as_json(self.task) json.update(args) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp json.update( dict(view_time=str(to_utimestamp(self.task.time_changed)), submit=True)) view = TicketUpdateView(self.env) result = view.do_post(self.req, json) return result['can_edit']
def runTest(self): self.tester.go_to_product_backlog() story_as_dict = self.story.as_dict() from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp story_as_dict.update({'view_time': str(to_utimestamp(self.story.time_changed))}) if story_as_dict.has_key('description'): del story_as_dict['description'] data = json.dumps(story_as_dict) import trac.util escaped_data = trac.util.escape(data) page = tc.get_browser().get_html() self.assert_(str(escaped_data) in page, "Metadata error")
def __init__(self, project, rev, reponame=None, env=None): """Initialize the class with the project path and the revision""" try: self.env = env or Environment(project) self.tm = AgiloTicketModelManager(self.env) if AgiloTicketSystem(self.env).is_trac_011(): repos = self.env.get_repository() else: repos = self.env.get_repository(reponame=reponame) repos.sync() except Exception, e: print >> sys.stderr, "An Error occurred while opening Trac project: %s => %s" % ( project, to_unicode(e)) sys.exit(1)
def runTest(self): self.tester.go_to_product_backlog() story_as_dict = self.story.as_dict() from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp story_as_dict.update( {'view_time': str(to_utimestamp(self.story.time_changed))}) if story_as_dict.has_key('description'): del story_as_dict['description'] data = json.dumps(story_as_dict) import trac.util escaped_data = trac.util.escape(data) page = tc.get_browser().get_html() self.assert_(str(escaped_data) in page, "Metadata error")
def do_put(self, req, args): trac_type = AgiloTicketSystem(self.env).normalize_type( args.get(Key.TYPE)) if trac_type is None: self.error_response(req, {}, ['Must specify a type.']) success, ticket_id, errors = self._create_ticket(req, args) if not success: self.error_response(req, {}, errors) ticket_resource = Resource('ticket')(id=ticket_id) if not req.perm.has_permission(Action.TICKET_VIEW, ticket_resource): self.error_response(req, {}, ['No permission to see ticket %d' % ticket_id]) return self.get_ticket_as_json(req, ticket_id)
def edit_ticket(self, ticket_id, **kwargs): if 'time_of_last_change' not in kwargs: ticket = AgiloTicket(self.env, ticket_id) kwargs['time_of_last_change'] = to_timestamp(ticket.time_changed) if 'ts' not in kwargs: ticket = AgiloTicket(self.env, ticket_id) kwargs['ts'] = str(ticket.time_changed) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp if 'view_time' not in kwargs: ticket = AgiloTicket(self.env, ticket_id) kwargs['view_time'] = str(to_utimestamp(ticket.time_changed)) if 'submit' not in kwargs: kwargs['submit'] = True return self.server.json.tickets[ticket_id].post(**kwargs)
def allow_link_from_to(self, from_type, to_type, save=None): # All tickets instantiated before this call will have a copy # of their allowed links, so for this call to have an effect, # the objects have to be dropped and recreated. config = AgiloConfig(self.env) assert from_type in config.get_available_types() assert to_type in config.get_available_types() section = config.get_section(AgiloConfig.AGILO_LINKS) allowed_links = section.get_list('allow') allowed_links.append('%s-%s' % (from_type, to_type)) section.change_option('allow', ', '.join(allowed_links), save=save) # Recreate all the worst caches links_configuration = LinksConfiguration(self.env) links_configuration._initialized = False links_configuration.initialize() AgiloTicketSystem(self.env).clear_cached_information()
def _request_for_ticket_change(self, username, **kwargs): # We need to load the ticket again to get the correct time of last # change - otherwise trac will reject the edit... task = AgiloTicket(self.env, self.task.id) args = dict( ticket_id=task.id, time_of_last_change=to_timestamp(task.time_changed), ts=str(task.time_changed), ) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp args.update({'view_time':str(to_utimestamp(task.time_changed)),'submit':True}) args.update(kwargs) req = self.teh.mock_request(username, method='POST') req.args = args return req
def _request_for_ticket_change(self, username, **kwargs): # We need to load the ticket again to get the correct time of last # change - otherwise trac will reject the edit... task = AgiloTicket(self.env, self.task.id) args = dict( ticket_id=task.id, time_of_last_change=to_timestamp(task.time_changed), ts=str(task.time_changed), ) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp args.update({ 'view_time': str(to_utimestamp(task.time_changed)), 'submit': True }) args.update(kwargs) req = self.teh.mock_request(username, method='POST') req.args = args return req
def should_be_skipped(self): return (not AgiloTicketSystem.is_trac_1_0()) or (self.super())
def reset_db(self, default_data=None): from agilo.test.functional.api import EnvironmentBuilder env = EnvironmentBuilder.get_testenv(self.env_key) from trac.db.api import _parse_db_str scheme, db_prop = _parse_db_str(env.get_db_url()) if scheme != 'sqlite' and not default_data: return super(BetterEnvironmentStub, self).reset_db(default_data) env_for_transaction = env.get_trac_environment() if AgiloTicketSystem.is_trac_1_0(): env_for_transaction = env tables = [] if scheme != 'sqlite': db = self.get_db_cnx() @with_transaction(env_for_transaction, db) def implementation(db): cursor = db.cursor() cursor.execute( "update system set value='9999' WHERE name='database_version'" ) db.commit() tables = super(BetterEnvironmentStub, self).reset_db(default_data) else: from trac import db_default from trac.db_default import schema from trac.db.sqlite_backend import _to_sql # our 'destroy_db' db = self.get_db_cnx() @with_transaction(env_for_transaction, db) def implementation(db): cursor = db.cursor() cursor.execute( "SELECT name FROM sqlite_master WHERE type='table'") tables = cursor.fetchall() for table in tables: cursor.execute("DROP TABLE %s" % table) # part of sqlite_backend's init_db for table in schema: for stmt in _to_sql(table): cursor.execute(stmt) # part of reset_db for table, cols, vals in db_default.get_data(db): cursor.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) db.commit() if env.tester.testcase.testtype != 'unittest': try: env._upgrade_environment() env._setup_users_and_permissions() except: # it's possible that this has already happened print "Warning: Exception on post-reset_db tasks" return tables
test.get_dburi = custom_get_dburi def custom_get_db_cnx(self, destroying=False): dbenv = EnvironmentStub.dbenv if not dbenv: dbenv = EnvironmentStub.dbenv = EnvironmentStub() dbenv.config.set('trac', 'database', self.dburi) if not destroying: self.reset_db() # make sure we get rid of previous garbage return DatabaseManager(dbenv).get_connection() if not AgiloTicketSystem.is_trac_1_0(): EnvironmentStub.get_db_cnx = custom_get_db_cnx def suppressed__del__(self): try: self.close() except: # if we got here, it's because the poolable connection was # garbage collected from a thread other than the one where it # was opened. this is not trac's fault, but it should handle # the situation better pass from trac.db.pool import PooledConnection
def is_unknown_status(self, status): known_statuses = AgiloTicketSystem(self.env).valid_ticket_statuses() known_statuses = known_statuses + TicketStatusManipulator.DEFAULT_SIMPLE_STATUSES return not status in known_statuses
return original_get_dburi() testenv = EnvironmentBuilder.get_testenv(LAST_ENV_KEY) return testenv.get_db_url() test.get_dburi = custom_get_dburi def custom_get_db_cnx(self, destroying=False): dbenv = EnvironmentStub.dbenv if not dbenv: dbenv = EnvironmentStub.dbenv = EnvironmentStub() dbenv.config.set('trac', 'database', self.dburi) if not destroying: self.reset_db() # make sure we get rid of previous garbage return DatabaseManager(dbenv).get_connection() if not AgiloTicketSystem.is_trac_1_0(): EnvironmentStub.get_db_cnx = custom_get_db_cnx def suppressed__del__(self): try: self.close() except: # if we got here, it's because the poolable connection was # garbage collected from a thread other than the one where it # was opened. this is not trac's fault, but it should handle # the situation better pass from trac.db.pool import PooledConnection PooledConnection.__del__ = suppressed__del__
def _append_backlog_item_html_to_string(self, file_str, item, level=1, parent=None): item_id = item.ticket.id item_html_id = str(item_id) ticket_type = item.ticket.get_type() status = item.ticket.values['status'] owner = item.ticket.values['owner'] is_container = self.ticket_is_container(item.ticket) has_multiple_parents = self.ticket_has_multiple_parents(item.ticket) if has_multiple_parents: if item_id in self.tickets_with_multiple_parents: if parent is not None and self.backlog_values_as_dict.has_key( parent.ticket.id): item_html_id += "-" + str(parent.ticket.id) else: self.tickets_with_multiple_parents.append(item_id) if is_container and level > 1: file_str.write('<dd class="childcontainer">') if is_container: file_str.write('<dl>') file_str.write('<dt class="container ') else: file_str.write('<dd class="leaf ') if has_multiple_parents: file_str.write('multi-linked-item ') html_text = 'handle level-$level ' +\ 'tickettype-$type '+\ 'ticketstatus-$status" '+\ 'id="ticketID-$html_id" '+\ 'data=\"$data\">' html_template = string.Template(html_text) data_dict = item.ticket.as_dict() if data_dict.has_key('description'): del data_dict['description'] from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp data_dict.update( {'view_time': str(to_utimestamp(item.ticket.time_changed))}) data_dump = json.dumps(data_dict) import trac.util file_str.write( html_template.substitute({ 'level': level, 'type': ticket_type, 'status': status, 'id': item_id, 'html_id': item_html_id, 'owner': owner, 'ts': str(item.ticket.time_changed), 'data': trac.util.escape(data_dump) })) for field in self.column_names: if field == 'id': ticket_url = "" if self.base_url != "" and self.base_url != "/": ticket_url = self.base_url + ticket_url ticket_url = ticket_url + "/ticket/%s" % item_id file_str.write( '<span class="%s numeric" data="{field:\'%s\'}"><a href="%s">%s</a></span>' % (field, field, ticket_url, item_id)) else: actual_field = field if isinstance(field, list): actual_field = field[0] for alternative in field: if alternative in item.fields_for_type or alternative in item.ticket.get_calculated_fields_names( ): actual_field = alternative data_field = actual_field if actual_field not in item.fields_for_type: data_field = '' css_class = actual_field if actual_field in item.ticket.get_calculated_fields_names(): css_class += " numeric" value = item.get(actual_field) if value is None: value = "" try: file_str.write( '<span class="%s" data="{field:\'%s\'}">%g</span>' % (css_class, data_field, value)) except TypeError: file_str.write( '<span class="%s" data="{field:\'%s\'}">%s</span>' % (css_class, data_field, value)) if is_container: file_str.write('</dt>') else: file_str.write('</dd>') children = item.ticket.get_outgoing() def index_for_item(item): if self.backlog_values_as_dict.has_key(item.id): return self.backlog_values.index(item) else: return None sorted_children = sorted(children, key=lambda item: index_for_item(item)) for child in sorted_children: if self.backlog_values_as_dict.has_key(child.id): self._append_backlog_item_html_to_string( file_str, self.backlog_values_as_dict[child.id], level=level + 1, parent=item) if is_container: file_str.write('</dl>') if is_container and level > 1: file_str.write('</dt>')
def _append_backlog_item_html_to_string(self, file_str, item, level=1, parent=None): item_id = item.ticket.id item_html_id = str(item_id) ticket_type = item.ticket.get_type() status = item.ticket.values['status'] owner = item.ticket.values['owner'] is_container = self.ticket_is_container(item.ticket) has_multiple_parents = self.ticket_has_multiple_parents(item.ticket) if has_multiple_parents: if item_id in self.tickets_with_multiple_parents: if parent is not None and self.backlog_values_as_dict.has_key(parent.ticket.id): item_html_id += "-" + str(parent.ticket.id) else: self.tickets_with_multiple_parents.append(item_id) if is_container and level > 1: file_str.write('<dd class="childcontainer">') if is_container: file_str.write('<dl>') file_str.write('<dt class="container ') else: file_str.write('<dd class="leaf ') if has_multiple_parents: file_str.write('multi-linked-item ') html_text = 'handle level-$level ' +\ 'tickettype-$type '+\ 'ticketstatus-$status" '+\ 'id="ticketID-$html_id" '+\ 'data=\"$data\">' html_template = string.Template(html_text) data_dict = item.ticket.as_dict() if data_dict.has_key('description'): del data_dict['description'] from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp data_dict.update({'view_time': str(to_utimestamp(item.ticket.time_changed))}) data_dump = json.dumps(data_dict) import trac.util file_str.write(html_template.substitute({'level': level, 'type': ticket_type, 'status': status, 'id': item_id, 'html_id': item_html_id, 'owner': owner, 'ts': str(item.ticket.time_changed), 'data': trac.util.escape(data_dump) })) for field in self.column_names: if field == 'id': ticket_url = "" if self.base_url != "" and self.base_url != "/": ticket_url = self.base_url + ticket_url ticket_url = ticket_url + "/ticket/%s" % item_id file_str.write('<span class="%s numeric" data="{field:\'%s\'}"><a href="%s">%s</a></span>' %(field, field, ticket_url, item_id)) else: actual_field = field if isinstance(field, list): actual_field = field[0] for alternative in field: if alternative in item.fields_for_type or alternative in item.ticket.get_calculated_fields_names(): actual_field = alternative data_field = actual_field if actual_field not in item.fields_for_type: data_field = '' css_class = actual_field if actual_field in item.ticket.get_calculated_fields_names(): css_class += " numeric" value = item.get(actual_field) if value is None: value = "" try: file_str.write('<span class="%s" data="{field:\'%s\'}">%g</span>' %(css_class, data_field, value)) except TypeError: file_str.write('<span class="%s" data="{field:\'%s\'}">%s</span>' %(css_class, data_field, value)) if is_container: file_str.write('</dt>') else: file_str.write('</dd>') children = item.ticket.get_outgoing() def index_for_item(item): if self.backlog_values_as_dict.has_key(item.id): return self.backlog_values.index(item) else: return None sorted_children = sorted(children, key=lambda item: index_for_item(item)) for child in sorted_children: if self.backlog_values_as_dict.has_key(child.id): self._append_backlog_item_html_to_string(file_str, self.backlog_values_as_dict[child.id], level = level+1, parent=item) if is_container: file_str.write('</dl>') if is_container and level > 1: file_str.write('</dt>')
def _configured_fields(self): for field in AgiloTicketSystem(self.env).get_ticket_fields(): field_name = field['name'] del field['name'] yield field_name, field