from trac.util.text import unicode_urlencode
from trac.ticket.query import Query
import copy


def _encode_trac_query( query_args ):
    return unicode_urlencode(query_args) \
	.replace('%21=', '!=') \
        .replace('%21%7E=', '!~=') \
        .replace('%7E=', '~=') \
        .replace('%5E=', '^=') \
        .replace('%24=', '$=') \
        .replace('%21%5E=', '!^=') \
        .replace('%21%24=', '!$=') \
        .replace('%7C', '|') \
        .replace('+', ' ') \
        .replace('%23', '#') \
        .replace('%28', '(') \
        .replace('%29', ')')


def get_viewable_tickets(env, req, query_args):
    # set maximum number of returned tickets to 0 to get all tickets at once
    query_args = copy.copy( query_args )
    query_args['max'] = 0
    query_string = _encode_trac_query( query_args )
    env.log.debug("query_string: %s", query_string)
    query = Query.from_string(env, query_string)

    tickets = query.execute(req)

    tickets = [t for t in tickets
               if ('TICKET_VIEW' or 'TICKET_VIEW_CC')
               in req.perm('ticket', t['id'])]

    return tickets


def require_ticket_fields( query_args, fieldnames ):
    for f in fieldnames:
        if not f in query_args:
            query_args[ f + "!" ] = "###"


from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
import math
import datetime as dt

def estimate_end( start, now, total, remaining ):
    done = total - remaining
    if done <= 0:
        return None
    hours = (now - start).total_seconds() / 3600
    if hours < 1:
        return None
    perhour = float(done) / hours
    morehours = int(math.ceil(remaining / perhour))

    return (now + dt.timedelta( hours = morehours )).date()


def _workday_diff( start, end ):
    if (end-start).days < 1:
        return 0
    daydiff = end.weekday() - start.weekday()
    days = ((end-start).days - daydiff) / 7 * 5 + min(daydiff,5) - (max(end.weekday() - 4, 0) % 5)
    return days


def estimate_end_workdays( start, now, total, remaining ):
    done = total - remaining
    if done <= 0:
        return None
    # days = (now - start).days
    days = _workday_diff(start, now)
    if days < 1:
        return None
    perday = float(done) / days
    moredays = int(math.ceil(remaining / perday))
    enddate = now + dt.timedelta( days = moredays )
    offdays = moredays - _workday_diff(now, enddate)

    # raise Exception( "days %d, perday %f, moredays %f, enddate %s, offdays %d" % ( days, perday, moredays, enddate, offdays ) )
    return (enddate + dt.timedelta( days = offdays )).date()


@@ -1253,1307 +1253,4 @@
 %22%0A%0A%0A
-from decimal import Decimal, InvalidOperation, ROUND_HALF_UP%0Aimport math%0Aimport datetime as dt%0A%0Adef estimate_end( start, now, total, remaining ):%0A    done = total - remaining%0A    if done %3C= 0:%0A        return None%0A    hours = (now - start).total_seconds() / 3600%0A    if hours %3C 1:%0A        return None%0A    perhour = float(done) / hours%0A    morehours = int(math.ceil(remaining / perhour))%0A%0A    return (now + dt.timedelta( hours = morehours )).date()%0A%0A%0Adef _workday_diff( start, end ):%0A    if (end-start).days %3C 1:%0A        return 0%0A    daydiff = end.weekday() - start.weekday()%0A    days = ((end-start).days - daydiff) / 7 * 5 + min(daydiff,5) - (max(end.weekday() - 4, 0) %25 5)%0A    return days%0A%0A%0Adef estimate_end_workdays( start, now, total, remaining ):%0A    done = total - remaining%0A    if done %3C= 0:%0A        return None%0A    # days = (now - start).days%0A    days = _workday_diff(start, now)%0A    if days %3C 1:%0A        return None%0A    perday = float(done) / days%0A    moredays = int(math.ceil(remaining / perday))%0A    enddate = now + dt.timedelta( days = moredays )%0A    offdays = moredays - _workday_diff(now, enddate)%0A%0A    # raise Exception( %22days %25d, perday %25f, moredays %25f, enddate %25s, offdays %25d%22 %25 ( days, perday, moredays, enddate, offdays ) )%0A    return (enddate + dt.timedelta( days = offdays )).date()%0A%0A

remove workday calculation from dbutils