<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2004-2006 Edgewall Software
# Copyright (C) 2004-2005 <NAME> <<EMAIL>>
# Copyright (C) 2006 <NAME> <<EMAIL>>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>

from datetime import datetime
import re
from time import localtime, strftime, time

from trac import __version__
from trac.core import *
from trac.perm import IPermissionRequestor
from trac.util import sorted
from trac.util.datefmt import parse_date, utc, to_timestamp, \
                              get_date_format_hint, get_datetime_format_hint
from trac.util.html import html, unescape, Markup
from trac.util.text import shorten_line, CRLF, to_unicode
from trac.ticket import Milestone, Ticket, TicketSystem
from trac.ticket.query import Query
from trac.timeline.api import ITimelineEventProvider, TimelineEvent
from trac.web import IRequestHandler
from trac.web.chrome import add_link, add_stylesheet, INavigationContributor
from trac.wiki.api import IWikiSyntaxProvider, Context
from trac.config import ExtensionOption

class ITicketGroupStatsProvider(Interface):
    def get_ticket_group_stats(self, ticket_ids):
        """ Gather statistics on a group of tickets.

        This method returns a valid TicketGroupStats object.
        """

class TicketGroupStats(object):
    """Encapsulates statistics on a group of tickets."""

    def __init__(self, title, unit):
        """Creates a new TicketGroupStats object.
        
        `title` is the display name of this group of stats (e.g.
          'ticket status').
        `unit` is the display name of the units for these stats (e.g. 'hour').
        """
        self.title = title
        self.unit = unit
        self.count = 0
        self.qry_args = {}
        self.intervals = []
        self.done_percent = 0
        self.done_count = 0

    def add_interval(self, title, count, qry_args, css_class, countsToProg=0):
        """Adds a division to this stats' group's progress bar.

        `title` is the display name (eg 'closed', 'spent effort') of this
        interval that will be displayed in front of the unit name.
        `count` is the number of units in the interval.
        `qry_args` is a dict of extra params that will yield the subset of
          tickets in this interval on a query.
        `css_class` is the css class that will be used to display the division.
        `countsToProg` can be set to true to make this interval count towards
          overall completion of this group of tickets.
        """
        self.intervals.append({
            'title': title,
            'count': count,
            'qry_args': qry_args,
            'css_class': css_class,
            'percent': None,
            'countsToProg': countsToProg
        })
        self.count = self.count + count
        self._refresh_calcs()

    def _refresh_calcs(self):
        if self.count < 1:
            return
        total_percent = 0
        self.done_percent = 0
        self.done_count = 0
        for interval in self.intervals:
            interval['percent'] = round(float(interval['count'] / 
                                        float(self.count) * 100))
            total_percent = total_percent + interval['percent']
            if interval['countsToProg']:
                self.done_percent += interval['percent']
                self.done_count += interval['count']

        if self.done_count and total_percent != 100:
            fudge_int = [i for i in self.intervals if i['countsToProg']][0]
            fudge_amt = 100 - total_percent
            fudge_int['percent'] += fudge_amt
            self.done_percent += fudge_amt

class DefaultTicketGroupStatsProvider(Component):
    implements(ITicketGroupStatsProvider)

    def get_ticket_group_stats(self, ticket_ids):
        total_cnt = len(ticket_ids)
        if total_cnt:
            cursor = self.env.get_db_cnx().cursor()
            str_ids = [str(x) for x in sorted(ticket_ids)]
            active_cnt = cursor.execute("SELECT count(1) FROM ticket "
                                        "WHERE status <> 'closed' AND id IN "
                                        "(%s)" % ",".join(str_ids))
            active_cnt = 0
            for cnt, in cursor:
                active_cnt = cnt
        else:
            active_cnt = 0

        closed_cnt = total_cnt - active_cnt

        stat = TicketGroupStats('ticket status', 'ticket')
        stat.add_interval('closed', closed_cnt, {'status': 'closed'},
                          'closed', True)
        stat.add_interval('active', active_cnt,
                          {'status': ['new', 'assigned', 'reopened']},
                          'open', False)
        return stat


def get_ticket_stats(provider, tickets):
    return provider.get_ticket_group_stats([t['id'] for t in tickets])

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=%%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=%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

def milestone_stats_data(req, stat, name, grouped_by='component', group=None):
    def query_href(extra_args):
        args = {'milestone': name, grouped_by: group, 'group': 'status'}
        args.update(extra_args)
        return req.href.query(args)
    return {'stats': stat,
            'stats_href': query_href(stat.qry_args),
            'interval_hrefs': [query_href(interval['qry_args'])<commit_msg>Minor improvement to the `DefaultTicketGroupStatsProvider`: only call refresh_calcs once.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2004-2006 Edgewall Software
# Copyright (C) 2004-2005 <NAME> <<EMAIL>>
# Copyright (C) 2006 <NAME> <<EMAIL>>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>

from datetime import datetime
import re
from time import localtime, strftime, time

from trac import __version__
from trac.core import *
from trac.perm import IPermissionRequestor
from trac.util import sorted
from trac.util.datefmt import parse_date, utc, to_timestamp, \
                              get_date_format_hint, get_datetime_format_hint
from trac.util.html import html, unescape, Markup
from trac.util.text import shorten_line, CRLF, to_unicode
from trac.ticket import Milestone, Ticket, TicketSystem
from trac.ticket.query import Query
from trac.timeline.api import ITimelineEventProvider, TimelineEvent
from trac.web import IRequestHandler
from trac.web.chrome import add_link, add_stylesheet, INavigationContributor
from trac.wiki.api import IWikiSyntaxProvider, Context
from trac.config import ExtensionOption

class ITicketGroupStatsProvider(Interface):
    def get_ticket_group_stats(self, ticket_ids):
        """ Gather statistics on a group of tickets.

        This method returns a valid TicketGroupStats object.
        """

class TicketGroupStats(object):
    """Encapsulates statistics on a group of tickets."""

    def __init__(self, title, unit):
        """Creates a new TicketGroupStats object.
        
        `title` is the display name of this group of stats (e.g.
          'ticket status').
        `unit` is the display name of the units for these stats (e.g. 'hour').
        """
        self.title = title
        self.unit = unit
        self.count = 0
        self.qry_args = {}
        self.intervals = []
        self.done_percent = 0
        self.done_count = 0

    def add_interval(self, title, count, qry_args, css_class, countsToProg=0):
        """Adds a division to this stats' group's progress bar.

        `title` is the display name (eg 'closed', 'spent effort') of this
        interval that will be displayed in front of the unit name.
        `count` is the number of units in the interval.
        `qry_args` is a dict of extra params that will yield the subset of
          tickets in this interval on a query.
        `css_class` is the css class that will be used to display the division.
        `countsToProg` can be set to true to make this interval count towards
          overall completion of this group of tickets.
        """
        self.intervals.append({
            'title': title,
            'count': count,
            'qry_args': qry_args,
            'css_class': css_class,
            'percent': None,
            'countsToProg': countsToProg
        })
        self.count = self.count + count

    def refresh_calcs(self):
        if self.count < 1:
            return
        total_percent = 0
        self.done_percent = 0
        self.done_count = 0
        for interval in self.intervals:
            interval['percent'] = round(float(interval['count'] / 
                                        float(self.count) * 100))
            total_percent = total_percent + interval['percent']
            if interval['countsToProg']:
                self.done_percent += interval['percent']
                self.done_count += interval['count']

        if self.done_count and total_percent != 100:
            fudge_int = [i for i in self.intervals if i['countsToProg']][0]
            fudge_amt = 100 - total_percent
            fudge_int['percent'] += fudge_amt
            self.done_percent += fudge_amt

class DefaultTicketGroupStatsProvider(Component):
    implements(ITicketGroupStatsProvider)

    def get_ticket_group_stats(self, ticket_ids):
        total_cnt = len(ticket_ids)
        if total_cnt:
            cursor = self.env.get_db_cnx().cursor()
            str_ids = [str(x) for x in sorted(ticket_ids)]
            active_cnt = cursor.execute("SELECT count(1) FROM ticket "
                                        "WHERE status <> 'closed' AND id IN "
                                        "(%s)" % ",".join(str_ids))
            active_cnt = 0
            for cnt, in cursor:
                active_cnt = cnt
        else:
            active_cnt = 0

        closed_cnt = total_cnt - active_cnt

        stat = TicketGroupStats('ticket status', 'ticket')
        stat.add_interval('closed', closed_cnt, {'status': 'closed'},
                          'closed', True)
        stat.add_interval('active', active_cnt,
                          {'status': ['new', 'assigned', 'reopened']},
                          'open', False)
        stat.refresh_calcs()
        return stat


def get_ticket_stats(provider, tickets):
    return provider.get_ticket_group_stats([t['id'] for t in tickets])

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=%%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=%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

def milestone_stats_data(req, stat, name, grouped_by='component', group=None):
    def query_href(extra_args):
        args = {'milestone': name, grouped_by: group, 'group': 'status'}
        args.update(extra_args)
        return req.href.query(args)
    return {'stats': stat,
            'stats_href': query_href(stat.qry_args),
            'interval_hrefs': [query_href(interval['qry_args'])
{'fraction_non_alphanumeric': 0.09769038701622972, 'fraction_numerical': 0.006554307116104869, 'mean_word_length': 2.4678030303030303, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1270426', 'n_tokens_mistral': 3702, 'n_tokens_neox': 3520, 'n_words': 1131}