def setUp(self): self.super() config = AgiloConfig(self.env) # Adding properties for multiple calculated property testing # The problem is that at this point the linkConfiguration as been # already initialized so we will need to do it again manually config.change_option('actual_time', 'text', section=AgiloConfig.TICKET_CUSTOM) config.change_option(Type.TASK, 'sprint, remaining_time, actual_time, estimated_time, owner, drp_resources', section=AgiloConfig.AGILO_TYPES) config.change_option('story.calculate', 'total_remaining_time=sum:get_outgoing.remaining_time, total_actual_time=sum:get_outgoing.actual_time', section=AgiloConfig.AGILO_LINKS) config.save() self.assert_true(config.is_agilo_enabled) self.assert_true('actual_time' in config.get_list(Type.TASK, section=AgiloConfig.AGILO_TYPES)) self.assert_true('actual_time' in config.get_fields_for_type().get(Type.TASK)) self.assert_equals(config.get('story.calculate', section=AgiloConfig.AGILO_LINKS), 'total_remaining_time=sum:get_outgoing.remaining_time, total_actual_time=sum:get_outgoing.actual_time') self._reset_links_configuration() # Creates tickets self.t1 = self.teh.create_ticket(Type.USER_STORY) self.t2 = self.teh.create_ticket(Type.TASK, props={Key.REMAINING_TIME: u'20', Key.RESOURCES: u'Tim, Tom'}) self.t3 = self.teh.create_ticket(Type.TASK, props={Key.REMAINING_TIME: u'10', Key.RESOURCES: u'Tim, Tom'}) # Now actual_time should be a valid field for Task... self.assert_not_none(self.t2.get_field('actual_time'))
def runTest(self): env = self._testenv.get_trac_environment() config = AgiloConfig(env).get_section(AgiloConfig.AGILO_LINKS) option_name = '%s.calculate' % Type.REQUIREMENT configured_properties = config.get_list(option_name) broken_definition = 'sum:get_outgoing.blubber' configured_properties.append(broken_definition) config.change_option(option_name, ', '.join(configured_properties)) config.save() self._tester.login_as(Usernames.admin) page_url = self._tester.url + '/admin/agilo/types/%s' % Type.REQUIREMENT tc.go(page_url) tc.code(200) html = tc.show() assert "sum:get_outgoing.rd_points|type=story|story_priority=Mandatory" in html assert 'blubber' not in html
def initialize(self): """Initialize the links configuration from the give config key""" self._currently_initializing = True # Prevent recursive imports (AgiloConfig needs to import the ticket # module) from agilo.utils.config import AgiloConfig if self._initialized and AgiloConfig(self.env).is_agilo_enabled: return links = AgiloConfig(self.env).get_section(AgiloConfig.AGILO_LINKS) for pair in links.get_list(LinkOption.ALLOW): if pair.find('-') == -1: continue self._parse_option(pair, links) # Set to initialized self._initialized = True self._currently_initializing = False
class TypesAdminPanel(AgiloAdminPanel): """ Administration panel for different ticket types and their fields. Needs to get imported in agilo/admin/__init__.py in order to appear on the web interface. """ _type = 'types' _label = ('Types', _('Types')) def __init__(self): self.agilo_config = AgiloConfig(self.env) def _get_field_labels(self): """Returns a dictionary of fields and their labels.""" labels = {} for f_name, label in self.agilo_config.LABELS.items(): if f_name not in MANDATORY_FIELDS: labels[f_name] = label return labels def _get_fields(self, type_name=None): """ If type_name is not set, return a dictionary of ticket types and the corresponding fields as a list. If a type_name gets passed, return a list of fields corresponding to this ticket type. """ fields = self.agilo_config.get_available_types(with_fields=True) if type_name: fields = fields.get(type_name) return fields def detail_save_view(self, req, cat, page, ticket_type): """Save the detail panel view""" # The types will be stored in lowercase and the space is not a # valid character for the config file key ticket_type = normalize_ticket_type(ticket_type) alias = req.args.get(Key.ALIAS) if alias: self.agilo_config.change_option('%s.%s' % (ticket_type, Key.ALIAS), alias, section=AgiloConfig.AGILO_TYPES, save=False) # save fields as string or comma-separated list of values # FIXME: (AT) after going crazy, it came out that some types are not # saved because there is no specific field assigned and the config # won't store the property in the trac.ini. So the agilo type will also # not be loaded, even if the alias would be set. fields = req.args.get(Key.FIELDS, '') # We have to save strings not lists if isinstance(fields, list): fields = ', '.join(fields) self.agilo_config.change_option(ticket_type, fields, section=AgiloConfig.AGILO_TYPES, save=False) calc = [] for res, func in zip(req.args.getlist('result'), req.args.getlist('function')): if res and func: configstring = u'%s=%s' % (res.strip(), func.strip()) parsed = parse_calculated_field(configstring) if parsed == None: msg = u"Wrong format for calculated property '%s'" raise TracError(_(msg) % res) calc.append(configstring) calc = ','.join(calc) if calc: self.agilo_config.change_option( '%s.%s' % (ticket_type, LinkOption.CALCULATE), calc, section=AgiloConfig.AGILO_LINKS, save=False) self.agilo_config.save() # on 0.12 we need to reset the ticket fields explicitely as the synchronization # is not done with the trac.ini anymore if AgiloTicketSystem(self.env).is_trac_012(): AgiloTicketSystem(self.env).reset_ticket_fields() return req.redirect(req.href.admin(cat, page)) def detail_view(self, req, cat, page, ticket_type): # All keys are saved lower-cased, but this is not done # automatically for retrieval calc_prop = self.agilo_config.get_list( '%s.%s' % (ticket_type, LinkOption.CALCULATE), section=AgiloConfig.AGILO_LINKS) calculated_properties = [] if len(calc_prop) > 0: for definition in calc_prop: parts = definition.split('=', 1) if len(parts) == 2: property_name, formula = parts calculated_properties.append( (property_name.strip(), formula.strip())) else: message = u"Ignoring broken definition for " \ "calculated property: %s" % definition warning(self, message) data = { 'calculate': calculated_properties, 'view': 'detail', 'type': ticket_type, 'alias': self.agilo_config.ALIASES.get(ticket_type, ''), 'type_fields': self._get_fields(ticket_type), 'labels': self._get_field_labels(), } return 'agilo_admin_types.html', data def list_view(self, req, cat, page): data = { 'view': 'list', 'fields': self._get_fields(), 'aliases': self.agilo_config.ALIASES, 'labels': self._get_field_labels(), } return 'agilo_admin_types.html', data
class TypesAdminPanel(AgiloAdminPanel): """ Administration panel for different ticket types and their fields. Needs to get imported in agilo/admin/__init__.py in order to appear on the web interface. """ _type = 'types' _label = ('Types', _('Types')) def __init__(self): self.agilo_config = AgiloConfig(self.env) def _get_field_labels(self): """Returns a dictionary of fields and their labels.""" labels = {} for f_name, label in self.agilo_config.LABELS.items(): if f_name not in MANDATORY_FIELDS: labels[f_name] = label return labels def _get_fields(self, type_name=None): """ If type_name is not set, return a dictionary of ticket types and the corresponding fields as a list. If a type_name gets passed, return a list of fields corresponding to this ticket type. """ fields = self.agilo_config.get_available_types(with_fields=True) if type_name: fields = fields.get(type_name) return fields def detail_save_view(self, req, cat, page, ticket_type): """Save the detail panel view""" # The types will be stored in lowercase and the space is not a # valid character for the config file key ticket_type = normalize_ticket_type(ticket_type) alias = req.args.get(Key.ALIAS) if alias: self.agilo_config.change_option('%s.%s' % (ticket_type, Key.ALIAS), alias, section=AgiloConfig.AGILO_TYPES, save=False) # save fields as string or comma-separated list of values # FIXME: (AT) after going crazy, it came out that some types are not # saved because there is no specific field assigned and the config # won't store the property in the trac.ini. So the agilo type will also # not be loaded, even if the alias would be set. fields = req.args.get(Key.FIELDS, '') # We have to save strings not lists if isinstance(fields, list): fields = ', '.join(fields) self.agilo_config.change_option(ticket_type, fields, section=AgiloConfig.AGILO_TYPES, save=False) calc = [] for res, func in zip(req.args.getlist('result'), req.args.getlist('function')): if res and func: configstring = u'%s=%s' % (res.strip(), func.strip()) parsed = parse_calculated_field(configstring) if parsed == None: msg = u"Wrong format for calculated property '%s'" raise TracError(_(msg) % res) calc.append(configstring) calc = ','.join(calc) if calc: self.agilo_config.change_option('%s.%s' % (ticket_type, LinkOption.CALCULATE), calc, section=AgiloConfig.AGILO_LINKS, save=False) self.agilo_config.save() # on 0.12 we need to reset the ticket fields explicitely as the synchronization # is not done with the trac.ini anymore if AgiloTicketSystem(self.env).is_trac_012(): AgiloTicketSystem(self.env).reset_ticket_fields() return req.redirect(req.href.admin(cat, page)) def detail_view(self, req, cat, page, ticket_type): # All keys are saved lower-cased, but this is not done # automatically for retrieval calc_prop = self.agilo_config.get_list('%s.%s' % (ticket_type, LinkOption.CALCULATE), section=AgiloConfig.AGILO_LINKS) calculated_properties = [] if len(calc_prop) > 0: for definition in calc_prop: parts = definition.split('=', 1) if len(parts) == 2: property_name, formula = parts calculated_properties.append((property_name.strip(), formula.strip())) else: message = u"Ignoring broken definition for " \ "calculated property: %s" % definition warning(self, message) data = { 'calculate' : calculated_properties, 'view': 'detail', 'type': ticket_type, 'alias' : self.agilo_config.ALIASES.get(ticket_type, ''), 'type_fields' : self._get_fields(ticket_type), 'labels' : self._get_field_labels(), } return 'agilo_admin_types.html', data def list_view(self, req, cat, page): data = { 'view': 'list', 'fields': self._get_fields(), 'aliases' : self.agilo_config.ALIASES, 'labels' : self._get_field_labels(), } return 'agilo_admin_types.html', data