Beispiel #1
0
 def _get_action_controls(self, req, tickets):
     action_controls = []
     ts = TicketSystem(self.env)
     tickets_by_action = {}
     for t in tickets:
         ticket = Ticket(self.env, t['id'])
         actions = ts.get_available_actions(req, ticket)
         for action in actions:
             tickets_by_action.setdefault(action, []).append(ticket)
     sorted_actions = sorted(set(tickets_by_action.keys()))
     for action in sorted_actions:
         first_label = None
         hints = []
         widgets = []
         ticket = tickets_by_action[action][0]
         for controller in self._get_action_controllers(
                 req, ticket, action):
             label, widget, hint = controller.render_ticket_action_control(
                 req, ticket, action)
             if not first_label:
                 first_label = label
             widgets.append(widget)
             hints.append(hint)
         action_controls.append((action, first_label, tag(widgets), hints))
     return action_controls
  def filter_stream(self, req, method, filename, stream, data):
    logger = self.env.log
    if filename == 'ticket.html':
      ticket_sys = TicketSystem(self.env)
      custom_fields = [field.get('name') for field in ticket_sys.get_ticket_fields()\
                       if field.get('custom') is True]

      all_fields = self.default_fields + custom_fields

      # Populate de all_fields array so the javascript code can
      # hide/show them.
      script = "<script>\n"
      for field_name in all_fields:
        script += ("all_fields.push('%s');\n" % field_name)


      i = 1
      while len(self.config.getlist(self.config_section, ('ticket_type_%d' % i))) > 0 :
        ticket_type = self.config.getlist(self.config_section, ('ticket_type_%d' % i))
        visible_fields = self.config.getlist(self.config_section, ('visible_fields_%d' % i))
        required_fields = self.config.getlist(self.config_section, ('required_fields_%d' % i))

        if len(visible_fields) > 0 :
          script += ("types['%s'] = ['%s'];\n" % ("".join(ticket_type), "','".join(visible_fields)))
          script += ("types_required['%s'] = ['%s'];\n" % ("".join(ticket_type), "','".join(required_fields)))
        i += 1
      
      script += "</script>\n"
      stream |= Transformer("//div[@id='footer']").after(MarkupTemplate(script).generate())

    return stream
Beispiel #3
0
    def _get_action_controls(self, req, tickets):
        action_controls = []
        ts = TicketSystem(self.env)
        tickets_by_action = {}
        for t in tickets:
            ticket = Ticket(self.env, t['id'])
            available_actions = ts.get_available_actions(req, ticket)
            for action in available_actions:
                tickets_by_action.setdefault(action, []).append(ticket)

        # Sort the allowed actions by the 'default' key.
        allowed_actions = set(tickets_by_action.keys())
        workflow = ConfigurableTicketWorkflow(self.env)
        all_actions = sorted(
            ((action['default'], name)
             for name, action in workflow.get_all_actions().iteritems()),
            reverse=True)
        sorted_actions = [
            action[1] for action in all_actions if action[1] in allowed_actions
        ]
        for action in sorted_actions:
            first_label = None
            hints = []
            widgets = []
            ticket = tickets_by_action[action][0]
            for controller in self._get_action_controllers(
                    req, ticket, action):
                label, widget, hint = controller.render_ticket_action_control(
                    req, ticket, action)
                if not first_label:
                    first_label = label
                widgets.append(widget)
                hints.append(hint)
            action_controls.append((action, first_label, tag(widgets), hints))
        return action_controls
Beispiel #4
0
 def _get_action_controls(self, req, tickets):
     action_controls = []
     ts = TicketSystem(self.env)
     tickets_by_action = {}
     for t in tickets:
         ticket = Ticket(self.env, t['id'])
         actions = ts.get_available_actions(req, ticket)
         for action in actions:
             tickets_by_action.setdefault(action, []).append(ticket)
     sorted_actions = sorted(set(tickets_by_action.keys()))
     for action in sorted_actions:
         first_label = None
         hints = []
         widgets = []
         ticket = tickets_by_action[action][0]
         for controller in self._get_action_controllers(req, ticket,
                                                        action):
             label, widget, hint = controller.render_ticket_action_control(
                 req, ticket, action)
             if not first_label:
                 first_label = label
             widgets.append(widget)
             hints.append(hint)
         action_controls.append((action, first_label, tag(widgets), hints))
     return action_controls
Beispiel #5
0
    def _generate_form(self, req, data):
        batchFormData = dict(data)
        batchFormData['query_href']= req.session['query_href'] \
                                     or req.href.query()
        batchFormData['notify_enabled'] = self.config.getbool(
            'notification', 'smtp_enabled', False)

        ticketSystem = TicketSystem(self.env)
        fields = []
        for field in ticketSystem.get_ticket_fields():
            if field['name'] not in ('summary', 'reporter', 'description'):
                fields.append(field)
            if field['name'] == 'owner' \
                and hasattr(ticketSystem, 'eventually_restrict_owner'):
                ticketSystem.eventually_restrict_owner(field)
        fields.sort(key=lambda f: f['name'])
        batchFormData['fields'] = fields

        add_script(req, 'batchmod/js/batchmod.js')
        add_stylesheet(req, 'batchmod/css/batchmod.css')
        stream = Chrome(self.env).render_template(req,
                                                  'batchmod.html',
                                                  batchFormData,
                                                  fragment=True)
        return stream.select('//form[@id="batchmod_form"]')
Beispiel #6
0
 def process_request(self, req):
     if req.path_info == '/' and MobileDetect(req).is_mobile():
         req.redict('/mobile')
     else:
         ts = TicketSystem(self.env)
         fields = ts.get_ticket_fields()
         return 'test.html', {"JSON": custom_json, "fields": fields}, None
Beispiel #7
0
    def meta_getTypeDefinition(self, req):
        ticket_system = TicketSystem(self.env)
        fields = ticket_system.get_ticket_fields()

        # remove ticket type as this should be a parameter of the request
        # XXX Trac doesn't support fields based on ticket type
        fields = [field for field in fields if field['name'] != 'type']
        return fields
Beispiel #8
0
 def meta_getTypeDefinition(self, req):
     ticket_system = TicketSystem(self.env)
     fields = ticket_system.get_ticket_fields()
     
     # remove ticket type as this should be a parameter of the request
     # XXX Trac doesn't support fields based on ticket type
     fields = [ field for field in fields 
                if field['name'] != 'type' ]
     return fields
Beispiel #9
0
 def process_request(self, req):
     if req.path_info == '/':
         try:
             from mobiledetect import MobileDetect
             if MobileDetect(
                     useragent=req.get_header('user-agent')).is_mobile():
                 req.redirect('/mobile')
         except ImportError:
             pass
     else:
         ts = TicketSystem(self.env)
         fields = ts.get_ticket_fields()
         return 'test.html', {"JSON": custom_json, "fields": fields}, None
Beispiel #10
0
    def filter_stream(self, req, method, filename, stream, formdata):
        '''Add workflows to query/report output'''
        if filename != 'query.html' and filename != 'report_view.html':
            return stream
        if not (req.perm.has_permission('TICKET_ADMIN')
                or req.perm.has_permission('TICKET_GRID_WORKFLOW')):
            return stream

        ts = TicketSystem(self.env)

        add_script(req, 'gridflow/gridflow.js')

        html = stream.render()
        js = ''
        tickets = []

        copy = genshi.XML(html)
        nodes = genshi.path.Path('//td[contains(@class, "ticket")]//a/text()')
        tickets += [int(a[1][1:]) for a in nodes.select(copy)]

        copy = genshi.XML(html)
        nodes = genshi.path.Path('//td[contains(@class, "id")]//a/text()')
        tickets += [int(a[1][1:]) for a in nodes.select(copy)]

        copy = genshi.XML(html)
        tktDict = {}
        for tno in tickets:
            tktDict[tno] = {'labels': [], 'widgets': [], 'actions': []}
            tkt = trac.ticket.Ticket(self.env, tno)
            actions = ts.get_available_actions(req, tkt)
            for action in actions:
                for controller in self._get_action_controllers(
                        req, tkt, action):
                    (label, widget,
                     hint) = controller.render_ticket_action_control(
                         req, tkt, action)
                    tktDict[tno]['actions'].append(action)
                    tktDict[tno]['labels'].append(label)
                    tktDict[tno]['widgets'].append(widget.generate().render())

        js += 'tktDict = ' + repr(tktDict).replace(", u'", ", '").replace(
            "[u'", "['") + ';\n'
        js += 'baseURL = "%s";\n' % req.base_url

        script = genshi.builder.tag.script(js, type="text/javascript")
        xpath = '//head'
        copy |= Transformer(xpath).append(script)
        return copy
Beispiel #11
0
 def _get_action_controllers(self, req, ticket, action):
     """Generator yielding the controllers handling the given `action`"""
     for controller in TicketSystem(self.env).action_controllers:
         actions = [a for w, a in
                    controller.get_ticket_actions(req, ticket) or []]
         if action in actions:
             yield controller
def get_available_actions(env, action='resolve'):
    # The list should not have duplicates.
    ts = TicketSystem(env)
    for controller in ts.action_controllers:
        if isinstance(controller, ConfigurableTicketWorkflow):
            return controller.actions.get(action)
    return None
Beispiel #13
0
    def _get_options(self, field_name):
        """Return a list of options for the given [dynvars] field:
        
         [dynvars]
         myfield.options = value1|value2|value3
        
        If no [dynvars] field is found, a select field is searched
        and its options returned.  For the milestone field, completed
        milestones are omitted.  If no select field is found, then an
        empty list is returned."""
        # look for [dynvars] field
        for key, val in self.env.config.options('dynvars'):
            if key == field_name + '.options':
                return val.split('|')

        # handle milestone special - skip completed milestones
        if field_name == 'milestone':
            return [''] + [
                m.name
                for m in Milestone.select(self.env, include_completed=False)
            ]

        # lookup select field
        for field in TicketSystem(self.env).get_ticket_fields():
            if field['name'] == field_name and 'options' in field:
                return field['options']
        return []
Beispiel #14
0
    def __init__(self,
                 env,
                 constraints=None,
                 order=None,
                 desc=0,
                 group=None,
                 groupdesc=0,
                 verbose=0):
        self.env = env
        self.constraints = constraints or {}
        self.order = order
        self.desc = desc
        self.group = group
        self.groupdesc = groupdesc
        self.verbose = verbose
        self.fields = TicketSystem(self.env).get_ticket_fields()
        self.cols = []  # lazily initialized

        if self.order != 'id' \
                and self.order not in [f['name'] for f in self.fields]:
            # order by priority by default
            self.order = 'priority'

        if self.group not in [f['name'] for f in self.fields]:
            self.group = None
Beispiel #15
0
	def _process_request(self, req):
		'''Intercept and execute GridFlow AJAX callbacks.'''
		if not req.perm.has_permission('TICKET_ADMIN') and \
		   not req.perm.has_permission('TICKET_GRID_WORKFLOW'):
			raise Exception('Permission denied')

		id = req.args.get('id')
		action = req.args.get('action')
		ticket = Ticket(self.env, id)
		ts = TicketSystem(self.env)

		validActions = ts.get_available_actions(req, ticket)
		if action not in validActions:
			req.send_response(500)
			req.send_header('Content-Type', 'text/plain')
			req.end_headers()
			req.write('"%s" is not a valid action for #%d\n' %
			          (action, id))
			return

		changes, problems = self.get_ticket_changes(req, ticket, action)
		if problems:
			req.send_response(500)
			req.send_header('Content-Type', 'text/plain')
			req.end_headers()
			req.write('Problems: %s\n' % problems)
			return


		self._apply_ticket_changes(ticket, changes)
		valid, reasons = self._validate_ticket(req, ticket)
		if not valid:
			req.send_response(500)
			req.send_header('Content-Type', 'text/plain')
			req.end_headers()
			req.write('Changes not valid:\n')
			for reason in reasons:
				req.write('* ' + reason + '\n')
			return

		self._save(req, ticket, action)
		req.send_response(200)
		req.send_header('Content-Type', 'text/plain')
		req.end_headers()
		req.write('OK')
		return
Beispiel #16
0
 def _add_ticket_fields(self, req):
     for field in TicketSystem(self.env).get_ticket_fields():
         name = field['name']
         del field['name']
         if name in ('summary', 'reporter', 'description', 'type', 'status',
                     'resolution', 'owner'):
             field['skip'] = True
         req.hdf['ticket.fields.' + name] = field
Beispiel #17
0
    def _do_save(self, req, db, ticket):
        if req.perm.has_permission('TICKET_CHGPROP'):
            # TICKET_CHGPROP gives permission to edit the ticket
            if not req.args.get('summary'):
                raise TracError('Tickets must contain summary.')

            if req.args.has_key('description') or req.args.has_key('reporter'):
                req.perm.assert_permission('TICKET_ADMIN')

            ticket.populate(req.args)
        else:
            req.perm.assert_permission('TICKET_APPEND')

        # Mid air collision?
        if int(req.args.get('ts')) != ticket.time_changed:
            raise TracError("Sorry, can not save your changes. "
                            "This ticket has been modified by someone else "
                            "since you started", 'Mid Air Collision')

        # Do any action on the ticket?
        action = req.args.get('action')
        actions = TicketSystem(self.env).get_available_actions(ticket, req.perm)
        if action not in actions:
            raise TracError('Invalid action')

        # TODO: this should not be hard-coded like this
        if action == 'accept':
            ticket['status'] =  'assigned'
            ticket['owner'] = req.authname
        if action == 'resolve':
            ticket['status'] = 'closed'
            ticket['resolution'] = req.args.get('resolve_resolution')
        elif action == 'reassign':
            ticket['owner'] = req.args.get('reassign_owner')
            ticket['status'] = 'new'
        elif action == 'reopen':
            ticket['status'] = 'reopened'
            ticket['resolution'] = ''

        self._validate_ticket(req, ticket)

        now = int(time.time())
        cnum = req.args.get('cnum')        
        replyto = req.args.get('replyto')
        internal_cnum = cnum
        if cnum and replyto: # record parent.child relationship
            internal_cnum = '%s.%s' % (replyto, cnum)
        if ticket.save_changes(get_reporter_id(req, 'author'),
                               req.args.get('comment'), when=now, db=db,
                               cnum=internal_cnum):
            db.commit()

            try:
                tn = TicketNotifyEmail(self.env)
                tn.notify(ticket, newticket=False, modtime=now)
            except Exception, e:
                self.log.exception("Failure sending notification on change to "
                                   "ticket #%s: %s" % (ticket.id, e))
Beispiel #18
0
    def _get_action_controllers(self, req, ticket, action):

        for controller in TicketSystem(self.env).action_controllers:
            actions = [
                action for weight, action in controller.get_ticket_actions(
                    req, ticket)
            ]
            if action in actions:
                yield controller
Beispiel #19
0
    def _generate_form(self, req, data):
        batchFormData = dict(data)
        batchFormData["query_href"] = req.session["query_href"] or req.href.query()

        ticketSystem = TicketSystem(self.env)
        fields = []
        for field in ticketSystem.get_ticket_fields():
            if field["name"] not in ("summary", "reporter", "description"):
                fields.append(field)
            if field["name"] == "owner" and hasattr(ticketSystem, "eventually_restrict_owner"):
                ticketSystem.eventually_restrict_owner(field)
        fields.sort(key=lambda f: f["name"])
        batchFormData["fields"] = fields

        add_script(req, "batchmod/js/batchmod.js")
        add_stylesheet(req, "batchmod/css/batchmod.css")
        stream = Chrome(self.env).render_template(req, "batchmod.html", batchFormData, fragment=True)
        return stream.select('//form[@id="batchmod_form"]')
Beispiel #20
0
	def filter_stream(self, req, method, filename, stream, formdata):
		'''Add workflows to query/report output'''
		if filename != 'query.html' and filename != 'report_view.html':
			return stream
		if not (req.perm.has_permission('TICKET_ADMIN') or
		        req.perm.has_permission('TICKET_GRID_WORKFLOW')):
			return stream

		ts = TicketSystem(self.env)

		add_script(req, 'gridflow/gridflow.js')

		html = stream.render()
		js = ''
		tickets = []

		copy = genshi.XML(html)
		nodes = genshi.path.Path('//td[contains(@class, "ticket")]//a/text()')
		tickets += [int(a[1][1:]) for a in nodes.select(copy)]

		copy = genshi.XML(html)
		nodes = genshi.path.Path('//td[contains(@class, "id")]//a/text()')
		tickets += [int(a[1][1:]) for a in nodes.select(copy)]

		copy = genshi.XML(html);
		tktDict = {}
		for tno in tickets:
			tktDict[tno] = {'labels': [], 'widgets': [], 'actions': []}
			tkt = trac.ticket.Ticket(self.env, tno)
			actions = ts.get_available_actions(req, tkt)
			for action in actions:
				for controller in self._get_action_controllers(req, tkt, action):
					(label, widget, hint) = controller.render_ticket_action_control(req, tkt, action)
					tktDict[tno]['actions'].append(action)
					tktDict[tno]['labels'].append(label)
					tktDict[tno]['widgets'].append(widget.generate().render())

		js += 'tktDict = ' + repr(tktDict).replace(", u'", ", '").replace("[u'", "['") + ';\n'
		js += 'baseURL = "%s";\n' % req.base_url

		script = genshi.builder.tag.script(js, type="text/javascript")
		xpath = '//head'
		copy |= Transformer(xpath).append(script)
		return copy
Beispiel #21
0
    def _process_request(self, req):
        '''Intercept and execute GridFlow AJAX callbacks.'''
        if not req.perm.has_permission('TICKET_ADMIN') and \
           not req.perm.has_permission('TICKET_GRID_WORKFLOW'):
            raise Exception('Permission denied')

        id = req.args.get('id')
        action = req.args.get('action')
        ticket = Ticket(self.env, id)
        ts = TicketSystem(self.env)

        validActions = ts.get_available_actions(req, ticket)
        if action not in validActions:
            req.send_response(500)
            req.send_header('Content-Type', 'text/plain')
            req.end_headers()
            req.write('"%s" is not a valid action for #%d\n' % (action, id))
            return

        changes, problems = self.get_ticket_changes(req, ticket, action)
        if problems:
            req.send_response(500)
            req.send_header('Content-Type', 'text/plain')
            req.end_headers()
            req.write('Problems: %s\n' % problems)
            return

        self._apply_ticket_changes(ticket, changes)
        valid, reasons = self._validate_ticket(req, ticket)
        if not valid:
            req.send_response(500)
            req.send_header('Content-Type', 'text/plain')
            req.end_headers()
            req.write('Changes not valid:\n')
            for reason in reasons:
                req.write('* ' + reason + '\n')
            return

        self._save(req, ticket, action)
        req.send_response(200)
        req.send_header('Content-Type', 'text/plain')
        req.end_headers()
        req.write('OK')
        return
Beispiel #22
0
 def __init__(self, env, tkt_id=None, db=None):
     self.env = env
     self.fields = TicketSystem(self.env).get_ticket_fields()
     self.values = {}
     if tkt_id:
         self._fetch_ticket(tkt_id, db)
     else:
         self._init_defaults(db)
         self.id = self.time_created = self.time_changed = None
     self._old = {}
Beispiel #23
0
 def _get_new_ticket_values(self, req, env):
     """Pull all of the new values out of the post data."""
     values = {}
     for field in TicketSystem(env).get_ticket_fields():
         name = field['name']
         if name not in ('summary', 'reporter', 'description'):
             value = req.args.get('batchmod_value_' + name)
             if value is not None:
                 values[name] = value
     return values
Beispiel #24
0
def _get_groups(env, db, by='component'):
    for field in TicketSystem(env).get_ticket_fields():
        if field['name'] == by:
            if field.has_key('options'):
                return field['options']
            else:
                cursor = db.cursor()
                cursor.execute("SELECT DISTINCT %s FROM ticket ORDER BY %s" %
                               (by, by))
                return [row[0] for row in cursor]
    return []
Beispiel #25
0
    def _render_view(self, req, db, milestone):
        req.hdf['title'] = 'Milestone %s' % milestone.name
        req.hdf['milestone.mode'] = 'view'

        req.hdf['milestone'] = milestone_to_hdf(self.env, db, req, milestone)

        available_groups = []
        component_group_available = False
        for field in TicketSystem(self.env).get_ticket_fields():
            if field['type'] == 'select' and field['name'] != 'milestone' \
                    or field['name'] == 'owner':
                available_groups.append({
                    'name': field['name'],
                    'label': field['label']
                })
                if field['name'] == 'component':
                    component_group_available = True
        req.hdf['milestone.stats.available_groups'] = available_groups

        if component_group_available:
            by = req.args.get('by', 'component')
        else:
            by = req.args.get('by', available_groups[0]['name'])
        req.hdf['milestone.stats.grouped_by'] = by

        tickets = get_tickets_for_milestone(self.env, db, milestone.name, by)
        stats = calc_ticket_stats(tickets)
        req.hdf['milestone.stats'] = stats
        for key, value in get_query_links(req, milestone.name).items():
            req.hdf['milestone.queries.' + key] = value

        groups = _get_groups(self.env, db, by)
        group_no = 0
        max_percent_total = 0
        for group in groups:
            group_tickets = [t for t in tickets if t[by] == group]
            if not group_tickets:
                continue
            prefix = 'milestone.stats.groups.%s' % group_no
            req.hdf['%s.name' % prefix] = group
            percent_total = 0
            if len(tickets) > 0:
                percent_total = float(len(group_tickets)) / float(len(tickets))
                if percent_total > max_percent_total:
                    max_percent_total = percent_total
            req.hdf['%s.percent_total' % prefix] = percent_total * 100
            stats = calc_ticket_stats(group_tickets)
            req.hdf[prefix] = stats
            for key, value in \
                    get_query_links(req, milestone.name, by, group).items():
                req.hdf['%s.queries.%s' % (prefix, key)] = value
            group_no += 1
        req.hdf['milestone.stats.max_percent_total'] = max_percent_total * 100
Beispiel #26
0
class TicketSystemApi(object):
    """Manages interaction with Trac ticket system"""
    def __init__(self, env):
        self.ticket_system = TicketSystem(env)
        self.custom_fields = self.ticket_system.get_custom_fields()

    def get_custom_field_label(self, custom_field_name):
        """For the given custom field name, get the corresponding label."""

        for custom_field in self.custom_fields:
            if custom_field["name"] == custom_field_name:
                return custom_field["label"]
Beispiel #27
0
    def _get_new_ticket_values(self, req):
        """Pull all of the new values out of the post data."""
        values = {}

        for field in TicketSystem(self.env).get_ticket_fields():
            name = field['name']
            if name not in ('id', 'resolution', 'status', 'owner', 'time',
                            'changetime', 'summary', 'reporter',
                            'description') and field['type'] != 'textarea':
                value = req.args.get('batchmod_value_' + name)
                if value is not None:
                    values[name] = value
        return values
Beispiel #28
0
    def _generate_form(self, req, data):
        batchFormData = dict(data)
        batchFormData['query_href']= req.session['query_href'] \
                                     or req.href.query()
        batchFormData['notify_enabled'] = self.config.getbool('notification', 
                                                        'smtp_enabled', False)
        
        ticketSystem = TicketSystem(self.env)
        fields = []
        for field in ticketSystem.get_ticket_fields():
            if field['name'] not in ('summary', 'reporter', 'description'):
                fields.append(field)
            if field['name'] == 'owner' \
                and hasattr(ticketSystem, 'eventually_restrict_owner'):
                ticketSystem.eventually_restrict_owner(field)
        fields.sort(key=lambda f: f['name'])
        batchFormData['fields']=fields

        add_script(req, 'batchmod/js/batchmod.js')
        add_stylesheet(req, 'batchmod/css/batchmod.css')
        stream = Chrome(self.env).render_template(req, 'batchmod.html',
              batchFormData, fragment=True)
        return stream.select('//form[@id="batchmod_form"]')
Beispiel #29
0
 def get_value_and_options(self, req, target, key):
     """Returns the preference value for the given key if configured
     for being set by user preference.  If no user preference has been
     set yet, the target field's default value is returned."""
     value = ''
     options = []
     for field in TicketSystem(self.env).get_ticket_fields():
         if field['name'] == target:
             value = field.get('value', value)
             options = field.get('options', options)
             break
     if key in self._pref_defaults:
         value = req.session.get(PREFIX + key + '.value', value)
     return value, options
Beispiel #30
0
    def _get_constraints(self, req):
        constraints = {}
        ticket_fields = [
            f['name'] for f in TicketSystem(self.env).get_ticket_fields()
        ]

        # A special hack for Safari/WebKit, which will not submit dynamically
        # created check-boxes with their real value, but with the default value
        # 'on'. See also htdocs/query.js#addFilter()
        checkboxes = [k for k in req.args.keys() if k.startswith('__')]
        if checkboxes:
            import cgi
            for checkbox in checkboxes:
                (real_k, real_v) = checkbox[2:].split(':', 2)
                req.args.list.append(cgi.MiniFieldStorage(real_k, real_v))

        # For clients without JavaScript, we remove constraints here if
        # requested
        remove_constraints = {}
        to_remove = [
            k[10:] for k in req.args.keys() if k.startswith('rm_filter_')
        ]
        if to_remove:  # either empty or containing a single element
            match = re.match(r'(\w+?)_(\d+)$', to_remove[0])
            if match:
                remove_constraints[match.group(1)] = int(match.group(2))
            else:
                remove_constraints[to_remove[0]] = -1

        for field in [k for k in req.args.keys() if k in ticket_fields]:
            vals = req.args[field]
            if not isinstance(vals, (list, tuple)):
                vals = [vals]
            vals = map(lambda x: x.value, vals)
            if vals:
                mode = req.args.get(field + '_mode')
                if mode:
                    vals = map(lambda x: mode + x, vals)
                if remove_constraints.has_key(field):
                    idx = remove_constraints[field]
                    if idx >= 0:
                        del vals[idx]
                        if not vals:
                            continue
                    else:
                        continue
                constraints[field] = vals

        return constraints
Beispiel #31
0
    def delete(self, db=None):
        db, handle_ta = self._get_db_for_write(db)
        Attachment.delete_all(self.env, 'ticket', self.id, db)
        cursor = db.cursor()
        cursor.execute("DELETE FROM ticket WHERE id=%s", (self.id, ))
        cursor.execute("DELETE FROM ticket_change WHERE ticket=%s",
                       (self.id, ))
        cursor.execute("DELETE FROM ticket_custom WHERE ticket=%s",
                       (self.id, ))

        if handle_ta:
            db.commit()

        for listener in TicketSystem(self.env).change_listeners:
            listener.ticket_deleted(self)
Beispiel #32
0
    def _get_new_ticket_values(self, req):
        """Pull all of the new values out of the post data."""
        values = {}

        for field in TicketSystem(self.env).get_ticket_fields():
            name = field['name']
            if name not in ('id', 'resolution', 'status', 'owner', 'time',
                            'changetime', 'summary', 'description') + \
                           (('reporter',) if 'TICKET_ADMIN' not in req.perm
                                          else ()) \
                    and field['type'] != 'textarea':
                value = req.args.get('batchmod_value_' + name)
                if value is not None:
                    values[name] = self._parse_field_value(req, field, value)
        return values
Beispiel #33
0
 def get_statistics_source(self, active=None):
     stats_source = [{
         'value': None,
         'label': _('Number of tickets'),
         'active': not active
     }]
     fields = TicketSystem(self.env).get_ticket_fields()
     for field in fields:
         if field['name'] in self._calculate_statistics_on:
             stats_source.append({
                 'value': field['name'],
                 'label': field['label'],
                 'active': field['name'] == active
             })
     return stats_source
Beispiel #34
0
def get_tickets_for_milestone(env, db, milestone, field='component'):
    cursor = db.cursor()
    fields = TicketSystem(env).get_ticket_fields()
    if field in [f['name'] for f in fields if not f.get('custom')]:
        cursor.execute(
            "SELECT id,status,%s FROM ticket WHERE milestone like %%s "
            "ORDER BY %s" % (field, field), (milestone + '%', ))
    else:
        cursor.execute(
            "SELECT id,status,value FROM ticket LEFT OUTER "
            "JOIN ticket_custom ON (id=ticket AND name=%s) "
            "WHERE milestone like %s ORDER BY value", (field, milestone + '%'))
    tickets = []
    for tkt_id, status, fieldval in cursor:
        tickets.append({'id': tkt_id, 'status': status, field: fieldval})
    return tickets
Beispiel #35
0
    def _batch_modify(self, req):
        tickets = req.session['query_tickets'].split(' ')
        comment = req.args.get('comment', '')
        values = {}

        for field in TicketSystem(self.env).get_ticket_fields():
            name = field['name']
            if name not in ('summary', 'reporter', \
                        'description', 'type', 'status',
                        'resolution', 'owner'):
                if req.args.has_key('bm_' + name):
                    values[name] = req.args.get(name)

        for id in tickets:
            t = Ticket(self.env, id)
            t.populate(values)
            t.save_changes(req.authname, comment)
Beispiel #36
0
	def _do_checkdates(self):
		ts = TicketSystem(self.env)
		now = datetime.now(utc)
		hard_mail = []
		soft_mail = []
		with self.env.db_query as db:
			cursor = db.cursor()
			cursor.execute("SELECT id FROM ticket WHERE status != 'closed'")
			for row in cursor:
				ticket_id = row[0]
				t = Ticket(self.env, ticket_id, db)
				t['id'] = ticket_id
				if t['due_date']:
					due_diff = parse_date(t['due_date']) - now
					if due_diff <= self.diff_threshold:
						hard_mail.append(t)
				if t['soft_due_date']:
					soft_due_diff = parse_date(t['soft_due_date']) - now
					if soft_due_diff <= self.soft_diff_threshold:
						soft_mail.append(t)

		if len(hard_mail) == 0 and len(soft_mail) == 0:
			return

		mail_body = "This is your friendly Ticket Due Date Mailer for today.\n\n"

		hard_mail=sorted(hard_mail, key=lambda ticket: ticket['due_date'])
		soft_mail=sorted(soft_mail, key=lambda ticket: ticket['soft_due_date'])

		if len(hard_mail) != 0:
			mail_body += "The following tickets are nearing their HARD DEADLINE:\n"
			mail_body +=         "    id  due_date       component  summary\n"
			for ticket in hard_mail:
				mail_body += "  % 4d  %s  % 12s  %s\n" % (ticket['id'], ticket['due_date'], ticket['component'], ticket['summary'])
			mail_body += "\n"

		if len(soft_mail) != 0:
			mail_body += "The following tickets are nearing their SOFT DEADLINE:\n"
			mail_body +=         "    id  soft_due_date  component  summary\n"
			for ticket in soft_mail:
				mail_body += "  % 4d  %s  % 12s  %s\n" % (ticket['id'], ticket['soft_due_date'], ticket['component'], ticket['summary'])
			mail_body += "\n"

		mail_body += "See you next time!\n"
		mail_body += "Trac Assistent"
		print mail_body.encode('utf-8')
Beispiel #37
0
def grouped_stats_data(env, stats_provider, tickets, by, per_group_stats_data):
    """Get the `tickets` stats data grouped by ticket field `by`.
    
    `per_group_stats_data(gstat, group_name)` should return a data dict to
    include for the group with field value `group_name`.
    """
    group_names = []
    for field in TicketSystem(env).get_ticket_fields():
        if field['name'] == by:
            if 'options' in field:
                group_names = field['options']
                if field.get('optional'):
                    group_names.insert(0, '')
            else:
                group_names = [
                    name for name, in env.db_query("""
                    SELECT DISTINCT COALESCE(%s, '') FROM ticket
                    ORDER BY COALESCE(%s, '')
                    """ % (by, by))
                ]
    max_count = 0
    data = []

    for name in group_names:
        values = (name, ) if name else (None, name)
        group_tickets = [t for t in tickets if t[by] in values]
        if not group_tickets:
            continue

        gstat = get_ticket_stats(stats_provider, group_tickets)
        if gstat.count > max_count:
            max_count = gstat.count

        gs_dict = {'name': name}
        gs_dict.update(per_group_stats_data(gstat, name))
        data.append(gs_dict)

    for gs_dict in data:
        percent = 1.0
        if max_count:
            gstat = gs_dict['stats']
            percent = float(gstat.count) / float(max_count) * 100
        gs_dict['percent_of_max_total'] = percent
    return data
Beispiel #38
0
    def _render_view(self, req, milestone):
        available_groups = []
        component_group_available = False
        project_id = req.data['project_id']
        ticket_fields = TicketSystem(self.env).get_ticket_fields(pid=project_id)

        # collect fields that can be used for grouping
        for name, field in ticket_fields.iteritems():
            if field['type'] == 'select' and name != 'milestone' \
                    or name in ('owner', 'reporter'):
                available_groups.append({'name': name,
                                         'label': field['label']})
                if name == 'component':
                    component_group_available = True

        # determine the field currently used for grouping
        by = None
        if component_group_available:
            by = 'component'
        elif available_groups:
            by = available_groups[0]['name']
        by = req.args.get('by', by)

        db = self.env.get_read_db()
        tickets = get_tickets_for_milestone(self.env, db, milestone, by)
        stat = get_ticket_stats(self.stats_provider, tickets, project_id)
        tstat = get_ticket_stats(self.tickettype_stats_provider, tickets, project_id)

        # Data for milestone and timeline
        data = {
            'milestone': milestone,
            'tickethistory' : [],
            'dates' : [],
            'ticketstat' : {},
            'yui_base_url': self.tm.yui_base_url,
            '_': _,
        }

        data.update(milestone_stats_data(self.env, req, stat, milestone))

        ticketstat = {'name':'ticket type'}
        ticketstat.update(milestone_stats_data(self.env, req, tstat, milestone))
        data['ticketstat'] = ticketstat

        # get list of ticket ids that in the milestone
        everytickets = get_every_tickets_in_milestone(db, project_id, milestone.name)

        if everytickets:
            tkt_history = collect_tickets_status_history(db, everytickets, milestone)
            if tkt_history:

                # Sort the key in the history list
                # returns sorted list of tuple of (key, value)
                sorted_events = sorted(tkt_history.items(), key=lambda(t,events):(t))

                # Get first date that ticket enter the milestone
                min_time = sorted_events[0][0]
                begin_date = to_datetime(min_time, tzinfo=req.tz)

                if milestone.is_completed:
                    end_date = milestone.completed
                else:
                    end_date = None
                end_date = to_datetime(end_date, tzinfo=req.tz)

                dates = list(date_generator(begin_date, end_date))

                #Create a data for the cumulative flow chart.
                date_history = prepare_to_cumulate(sorted_events)
                tkt_cumulative_table = make_cumulative_data(dates, date_history)

                #prepare Yahoo datasource for comulative flow chart
                dscumulative = ''
                for idx, date in enumerate(dates):
                    dscumulative = dscumulative +  '{ date: "%s", enter: %d, leave: %d, finish: %d}, ' \
                          % (format_date(date,tzinfo=utc), tkt_cumulative_table['Enter'][idx], \
                             tkt_cumulative_table['Leave'][idx], tkt_cumulative_table['Finish'][idx])

                data['tickethistory'] = tkt_cumulative_table
                data['dates'] = dates
                data['dscumulative'] = '[ ' + dscumulative + ' ];'

        return 'mdashboard.html', data, None