<s> from StringIO import StringIO <EOL> from datetime import datetime , timedelta <EOL> import re <EOL> from genshi . builder import tag <EOL> from trac . attachment import Attachment , AttachmentModule <EOL> from trac . config import ConfigSection , ExtensionOption , Option <EOL> from trac . core import * <EOL> from trac . notification . api import NotificationSystem <EOL> from trac . perm import IPermissionRequestor <EOL> from trac . resource import * <EOL> from trac . search import ISearchSource , search_to_regexps , shorten_result <EOL> from trac . util import as_bool , partition <EOL> from trac . util . datefmt import ( datetime_now , format_date , format_datetime , <EOL> from_utimestamp , get_datetime_format_hint , <EOL> parse_date , pretty_timedelta , to_datetime , <EOL> user_time , utc ) <EOL> from trac . util . text import CRLF , exception_to_unicode , to_unicode <EOL> from trac . util . translation import _ , tag_ <EOL> from trac . ticket . api import TicketSystem <EOL> from trac . ticket . notification import BatchTicketChangeEvent <EOL> from trac . ticket . model import Milestone , MilestoneCache , Ticket <EOL> from trac . timeline . api import ITimelineEventProvider <EOL> from trac . web . api import HTTPBadRequest , IRequestHandler , RequestDone <EOL> from trac . web . chrome import ( Chrome , INavigationContributor , <EOL> add_link , add_notice , add_script , add_stylesheet , <EOL> add_warning , auth_link , prevnext_nav , web_context ) <EOL> from trac . wiki . api import IWikiSyntaxProvider <EOL> from trac . wiki . formatter import format_to <EOL> class ITicketGroupStatsProvider ( Interface ) : <EOL> def get_ticket_group_stats ( ticket_ids ) : <EOL> """<STR_LIT>""" <EOL> class TicketGroupStats ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , title , unit ) : <EOL> """<STR_LIT>""" <EOL> self . title = title <EOL> self . unit = unit <EOL> self . count = <NUM_LIT:0> <EOL> self . qry_args = { } <EOL> self . intervals = [ ] <EOL> self . done_percent = <NUM_LIT:0> <EOL> self . done_count = <NUM_LIT:0> <EOL> def add_interval ( self , title , count , qry_args , css_class , <EOL> overall_completion = None ) : <EOL> """<STR_LIT>""" <EOL> self . intervals . append ( { <EOL> '<STR_LIT:title>' : title , <EOL> '<STR_LIT:count>' : count , <EOL> '<STR_LIT>' : qry_args , <EOL> '<STR_LIT>' : css_class , <EOL> '<STR_LIT>' : None , <EOL> '<STR_LIT>' : overall_completion , <EOL> } ) <EOL> self . count = self . count + count <EOL> def refresh_calcs ( self ) : <EOL> if self . count < <NUM_LIT:1> : <EOL> return <EOL> total_percent = <NUM_LIT:0> <EOL> self . done_percent = <NUM_LIT:0> <EOL> self . done_count = <NUM_LIT:0> <EOL> for interval in self . intervals : <EOL> interval [ '<STR_LIT>' ] = round ( float ( interval [ '<STR_LIT:count>' ] / <EOL> float ( self . count ) * <NUM_LIT:100> ) ) <EOL> total_percent = total_percent + interval [ '<STR_LIT>' ] <EOL> if interval [ '<STR_LIT>' ] : <EOL> self . done_percent += interval [ '<STR_LIT>' ] <EOL> self . done_count += interval [ '<STR_LIT:count>' ] <EOL> if self . done_count and total_percent != <NUM_LIT:100> : <EOL> fudge_amt = <NUM_LIT:100> - total_percent <EOL> fudge_int = [ i for i in sorted ( self . intervals , <EOL> key = lambda k : k [ '<STR_LIT>' ] , <EOL> reverse = ( fudge_amt < <NUM_LIT:0> ) ) <EOL> if i [ '<STR_LIT>' ] ] [ <NUM_LIT:0> ] <EOL> fudge_int [ '<STR_LIT>' ] += fudge_amt <EOL> self . done_percent += fudge_amt <EOL> class DefaultTicketGroupStatsProvider ( Component ) : <EOL> """<STR_LIT>""" <EOL> implements ( ITicketGroupStatsProvider ) <EOL> milestone_groups_section = ConfigSection ( '<STR_LIT>' , <EOL> """<STR_LIT>""" ) <EOL> default_milestone_groups = [ <EOL> { '<STR_LIT:name>' : '<STR_LIT>' , '<STR_LIT:status>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : '<STR_LIT>' , '<STR_LIT>' : '<STR_LIT:true>' } , <EOL> { '<STR_LIT:name>' : '<STR_LIT>' , '<STR_LIT:status>' : '<STR_LIT:*>' , '<STR_LIT>' : '<STR_LIT>' } <EOL> ] <EOL> def _get_ticket_groups ( self ) : <EOL> """<STR_LIT>""" <EOL> if '<STR_LIT>' in self . config : <EOL> groups = { } <EOL> order = <NUM_LIT:0> <EOL> for groupname , value in self . milestone_groups_section . options ( ) : <EOL> qualifier = '<STR_LIT:status>' <EOL> if '<STR_LIT:.>' in groupname : <EOL> groupname , qualifier = groupname . split ( '<STR_LIT:.>' , <NUM_LIT:1> ) <EOL> group = groups . setdefault ( groupname , { '<STR_LIT:name>' : groupname , <EOL> '<STR_LIT>' : order } ) <EOL> group [ qualifier ] = value <EOL> order = max ( order , int ( group [ '<STR_LIT>' ] ) ) + <NUM_LIT:1> <EOL> return [ group for group in sorted ( groups . values ( ) , <EOL> key = lambda g : int ( g [ '<STR_LIT>' ] ) ) ] <EOL> else : <EOL> return self . default_milestone_groups <EOL> def get_ticket_group_stats ( self , ticket_ids ) : <EOL> total_cnt = len ( ticket_ids ) <EOL> all_statuses = set ( TicketSystem ( self . env ) . get_all_status ( ) ) <EOL> status_cnt = { } <EOL> for s in all_statuses : <EOL> status_cnt [ s ] = <NUM_LIT:0> <EOL> if total_cnt : <EOL> for status , count in self . env . db_query ( """<STR_LIT>""" % "<STR_LIT:U+002C>" . join ( str ( x ) for x in sorted ( ticket_ids ) ) ) : <EOL> status_cnt [ status ] = count <EOL> stat = TicketGroupStats ( _ ( '<STR_LIT>' ) , _ ( '<STR_LIT>' ) ) <EOL> remaining_statuses = set ( all_statuses ) <EOL> groups = self . _get_ticket_groups ( ) <EOL> catch_all_group = None <EOL> for group in groups : <EOL> status_str = group [ '<STR_LIT:status>' ] . strip ( ) <EOL> if status_str == '<STR_LIT:*>' : <EOL> if catch_all_group : <EOL> raise TracError ( _ ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> group1 = group [ '<STR_LIT:name>' ] , group2 = catch_all_group [ '<STR_LIT:name>' ] ) ) <EOL> catch_all_group = group <EOL> else : <EOL> group_statuses = set ( [ s . strip ( ) <EOL> for s in status_str . split ( '<STR_LIT:U+002C>' ) ] ) & all_statuses <EOL> if group_statuses - remaining_statuses : <EOL> raise TracError ( _ ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> groupname = group [ '<STR_LIT:name>' ] , <EOL> status = '<STR_LIT:U+002CU+0020>' . join ( group_statuses - remaining_statuses ) ) ) <EOL> else : <EOL> remaining_statuses -= group_statuses <EOL> group [ '<STR_LIT>' ] = group_statuses <EOL> if catch_all_group : <EOL> catch_all_group [ '<STR_LIT>' ] = remaining_statuses <EOL> for group in groups : <EOL> group_cnt = <NUM_LIT:0> <EOL> query_args = { } <EOL> for s , cnt in status_cnt . iteritems ( ) : <EOL> if s in group [ '<STR_LIT>' ] : <EOL> group_cnt += cnt <EOL> query_args . setdefault ( '<STR_LIT:status>' , [ ] ) . append ( s ) <EOL> for arg in [ kv for kv in group . get ( '<STR_LIT>' , '<STR_LIT>' ) . split ( '<STR_LIT:U+002C>' ) <EOL> if '<STR_LIT:=>' in kv ] : <EOL> k , v = [ a . strip ( ) for a in arg . split ( '<STR_LIT:=>' , <NUM_LIT:1> ) ] <EOL> query_args . setdefault ( k , [ ] ) . append ( v ) <EOL> stat . add_interval ( group . get ( '<STR_LIT:label>' , group [ '<STR_LIT:name>' ] ) , <EOL> group_cnt , query_args , <EOL> group . get ( '<STR_LIT>' , group [ '<STR_LIT:name>' ] ) , <EOL> as_bool ( group . get ( '<STR_LIT>' ) ) ) <EOL> stat . refresh_calcs ( ) <EOL> return stat <EOL> def get_ticket_stats ( provider , tickets ) : <EOL> return provider . get_ticket_group_stats ( [ t [ '<STR_LIT:id>' ] for t in tickets ] ) <EOL> def get_tickets_for_milestone ( env , milestone = None , field = '<STR_LIT>' ) : <EOL> """<STR_LIT>""" <EOL> fields = TicketSystem ( env ) . get_ticket_fields ( ) <EOL> if field in [ f [ '<STR_LIT:name>' ] for f in fields if not f . get ( '<STR_LIT>' ) ] : <EOL> sql = """<STR_LIT>""" % ( field , field ) <EOL> args = ( milestone , ) <EOL> else : <EOL> sql = """<STR_LIT>""" <EOL> args = ( field , milestone ) <EOL> return [ { '<STR_LIT:id>' : tkt_id , '<STR_LIT:status>' : status , field : fieldval } <EOL> for tkt_id , status , fieldval in env . db_query ( sql , args ) ] <EOL> def apply_ticket_permissions ( env , req , tickets ) : <EOL> """<STR_LIT>""" <EOL> return [ t for t in tickets <EOL> if '<STR_LIT>' in req . perm ( '<STR_LIT>' , t [ '<STR_LIT:id>' ] ) ] <EOL> def milestone_stats_data ( env , req , stat , name , grouped_by = '<STR_LIT>' , <EOL> group = None ) : <EOL> from trac . ticket . query import QueryModule <EOL> has_query = env [ QueryModule ] is not None <EOL> def query_href ( extra_args ) : <EOL> if not has_query : <EOL> return None <EOL> args = { '<STR_LIT>' : name , grouped_by : group , '<STR_LIT>' : '<STR_LIT:status>' } <EOL> args . update ( extra_args ) <EOL> return req . href . query ( args ) <EOL> return { '<STR_LIT>' : stat , <EOL> '<STR_LIT>' : query_href ( stat . qry_args ) , <EOL> '<STR_LIT>' : [ query_href ( interval [ '<STR_LIT>' ] ) <EOL> for interval in stat . intervals ] } <EOL> def grouped_stats_data ( env , stats_provider , tickets , by , per_group_stats_data ) : <EOL> """<STR_LIT>""" <EOL> group_names = [ ] <EOL> for field in TicketSystem ( env ) . get_ticket_fields ( ) : <EOL> if field [ '<STR_LIT:name>' ] == by : <EOL> if '<STR_LIT>' in field : <EOL> group_names = field [ '<STR_LIT>' ] <EOL> if field . get ( '<STR_LIT>' ) : <EOL> group_names . insert ( <NUM_LIT:0> , '<STR_LIT>' ) <EOL> elif field . get ( '<STR_LIT>' ) : <EOL> group_names = [ name for name , in env . db_query ( """<STR_LIT>""" , ( by , ) ) ] <EOL> if '<STR_LIT>' not in group_names : <EOL> group_names . insert ( <NUM_LIT:0> , '<STR_LIT>' ) <EOL> else : <EOL> group_names = [ name for name , in env . db_query ( """<STR_LIT>""" % ( by , by ) ) ] <EOL> max_count = <NUM_LIT:0> <EOL> data = [ ] <EOL> for name in group_names : <EOL> values = ( name , ) if name else ( None , name ) <EOL> group_tickets = [ t for t in tickets if t [ by ] in values ] <EOL> if not group_tickets : <EOL> continue <EOL> gstat = get_ticket_stats ( stats_provider , group_tickets ) <EOL> if gstat . count > max_count : <EOL> max_count = gstat . count <EOL> gs_dict = { '<STR_LIT:name>' : name } <EOL> gs_dict . update ( per_group_stats_data ( gstat , name ) ) <EOL> data . append ( gs_dict ) <EOL> for gs_dict in data : <EOL> percent = <NUM_LIT:1.0> <EOL> if max_count : <EOL> gstat = gs_dict [ '<STR_LIT>' ] <EOL> percent = float ( gstat . count ) / float ( max_count ) * <NUM_LIT:100> <EOL> gs_dict [ '<STR_LIT>' ] = percent <EOL> return data <EOL> def group_milestones ( milestones , include_completed ) : <EOL> """<STR_LIT>""" <EOL> def category ( m ) : <EOL> return <NUM_LIT:1> if m . is_completed else <NUM_LIT:2> if m . due else <NUM_LIT:3> <EOL> open_due_milestones , open_not_due_milestones , closed_milestones = partition ( [ ( m , category ( m ) ) <EOL> for m in milestones ] , ( <NUM_LIT:2> , <NUM_LIT:3> , <NUM_LIT:1> ) ) <EOL> groups = [ <EOL> ( _ ( "<STR_LIT>" ) , open_due_milestones ) , <EOL> ( _ ( "<STR_LIT>" ) , open_not_due_milestones ) , <EOL> ] <EOL> if include_completed : <EOL> groups . append ( ( _ ( '<STR_LIT>' ) , closed_milestones ) ) <EOL> return groups <EOL> class RoadmapModule ( Component ) : <EOL> """<STR_LIT>""" <EOL> implements ( INavigationContributor , IPermissionRequestor , IRequestHandler ) <EOL> stats_provider = ExtensionOption ( '<STR_LIT>' , '<STR_LIT>' , <EOL> ITicketGroupStatsProvider , <EOL> '<STR_LIT>' , <EOL> """<STR_LIT>""" ) <EOL> def get_active_navigation_item ( self , req ) : <EOL> return '<STR_LIT>' <EOL> def get_navigation_items ( self , req ) : <EOL> if '<STR_LIT>' in req . perm : <EOL> yield ( '<STR_LIT>' , '<STR_LIT>' , <EOL> tag . a ( _ ( '<STR_LIT>' ) , href = req . href . roadmap ( ) , accesskey = <NUM_LIT:3> ) ) <EOL> def get_permission_actions ( self ) : <EOL> actions = [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' , '<STR_LIT>' ] <EOL> return [ '<STR_LIT>' ] + [ ( '<STR_LIT>' , actions ) ] <EOL> def match_request ( self , req ) : <EOL> return req . path_info == '<STR_LIT>' <EOL> def process_request ( self , req ) : <EOL> req . perm . require ( '<STR_LIT>' ) <EOL> show = req . args . getlist ( '<STR_LIT>' ) <EOL> if '<STR_LIT:all>' in show : <EOL> show = [ '<STR_LIT>' ] <EOL> milestones = Milestone . select ( self . env , '<STR_LIT>' in show ) <EOL> if '<STR_LIT>' in show : <EOL> milestones = [ m for m in milestones <EOL> if m . due is not None or m . completed ] <EOL> milestones = [ m for m in milestones <EOL> if '<STR_LIT>' in req . perm ( m . resource ) ] <EOL> stats = [ ] <EOL> queries = [ ] <EOL> for milestone in milestones : <EOL> tickets = get_tickets_for_milestone ( <EOL> self . env , milestone = milestone . name , field = '<STR_LIT>' ) <EOL> tickets = apply_ticket_permissions ( self . env , req , tickets ) <EOL> stat = get_ticket_stats ( self . stats_provider , tickets ) <EOL> stats . append ( milestone_stats_data ( self . env , req , stat , <EOL> milestone . name ) ) <EOL> if req . args . get ( '<STR_LIT>' ) == '<STR_LIT>' : <EOL> self . _render_ics ( req , milestones ) <EOL> return <EOL> username = None <EOL> if req . authname and req . authname != '<STR_LIT>' : <EOL> username = req . authname <EOL> icshref = req . href . roadmap ( show = show , user = username , format = '<STR_LIT>' ) <EOL> add_link ( req , '<STR_LIT>' , auth_link ( req , icshref ) , _ ( '<STR_LIT>' ) , <EOL> '<STR_LIT>' , '<STR_LIT>' ) <EOL> data = { <EOL> '<STR_LIT>' : milestones , <EOL> '<STR_LIT>' : stats , <EOL> '<STR_LIT>' : queries , <EOL> '<STR_LIT>' : show , <EOL> } <EOL> add_stylesheet ( req , '<STR_LIT>' ) <EOL> return '<STR_LIT>' , data , None <EOL> def _render_ics ( self , req , milestones ) : <EOL> req . send_response ( <NUM_LIT:200> ) <EOL> req . send_header ( '<STR_LIT:Content-Type>' , '<STR_LIT>' ) <EOL> buf = StringIO ( ) <EOL> from trac . ticket import Priority <EOL> priorities = { } <EOL> for priority in Priority . select ( self . env ) : <EOL> priorities [ priority . name ] = float ( priority . value ) <EOL> def get_priority ( ticket ) : <EOL> value = priorities . get ( ticket [ '<STR_LIT>' ] ) <EOL> if value : <EOL> return int ( ( len ( priorities ) + <NUM_LIT:8> * value - <NUM_LIT:9> ) / <EOL> ( len ( priorities ) - <NUM_LIT:1> ) ) <EOL> def get_status ( ticket ) : <EOL> status = ticket [ '<STR_LIT:status>' ] <EOL> if status == '<STR_LIT>' or status == '<STR_LIT>' and not ticket [ '<STR_LIT>' ] : <EOL> return '<STR_LIT>' <EOL> elif status in ( '<STR_LIT>' , '<STR_LIT>' ) : <EOL> return '<STR_LIT>' <EOL> elif status == '<STR_LIT>' : <EOL> if ticket [ '<STR_LIT>' ] == '<STR_LIT>' : <EOL> return '<STR_LIT>' <EOL> else : <EOL> return '<STR_LIT>' <EOL> else : return '<STR_LIT>' <EOL> def escape_value ( text ) : <EOL> s = '<STR_LIT>' . join ( map ( lambda c : '<STR_LIT:\\>' + c if c in '<STR_LIT>' else c , text ) ) <EOL> return '<STR_LIT>' . join ( re . split ( r'<STR_LIT>' , s ) ) <EOL> def write_prop ( name , value , params = { } ) : <EOL> text = '<STR_LIT:;>' . join ( [ name ] + [ k + '<STR_LIT:=>' + v for k , v in params . items ( ) ] ) + '<STR_LIT::>' + escape_value ( value ) <EOL> firstline = <NUM_LIT:1> <EOL> while text : <EOL> if not firstline : <EOL> text = '<STR_LIT:U+0020>' + text <EOL> else : <EOL> firstline = <NUM_LIT:0> <EOL> buf . write ( text [ : <NUM_LIT> ] + CRLF ) <EOL> text = text [ <NUM_LIT> : ] <EOL> def write_date ( name , value , params = { } ) : <EOL> params [ '<STR_LIT>' ] = '<STR_LIT>' <EOL> write_prop ( name , format_date ( value , '<STR_LIT>' , req . tz ) , params ) <EOL> def write_utctime ( name , value , params = { } ) : <EOL> write_prop ( name , format_datetime ( value , '<STR_LIT>' , utc ) , <EOL> params ) <EOL> host = req . base_url [ req . base_url . find ( '<STR_LIT>' ) + <NUM_LIT:3> : ] <EOL> user = req . args . get ( '<STR_LIT:user>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' <EOL> % self . env . trac_version ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , <EOL> self . env . project_name + '<STR_LIT>' + _ ( '<STR_LIT>' ) ) <EOL> write_prop ( '<STR_LIT>' , self . env . project_description ) <EOL> write_prop ( '<STR_LIT>' , str ( req . tz ) ) <EOL> for milestone in milestones : <EOL> uid = '<STR_LIT>' % ( req . base_path , milestone . name , <EOL> host ) <EOL> if milestone . due : <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , uid ) <EOL> write_utctime ( '<STR_LIT>' , milestone . due ) <EOL> write_date ( '<STR_LIT>' , milestone . due ) <EOL> write_prop ( '<STR_LIT>' , _ ( '<STR_LIT>' , <EOL> name = milestone . name ) ) <EOL> write_prop ( '<STR_LIT>' , req . abs_href . milestone ( milestone . name ) ) <EOL> if milestone . description : <EOL> write_prop ( '<STR_LIT>' , milestone . description ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> tickets = get_tickets_for_milestone ( <EOL> self . env , milestone = milestone . name , field = '<STR_LIT>' ) <EOL> tickets = apply_ticket_permissions ( self . env , req , tickets ) <EOL> for tkt_id in [ ticket [ '<STR_LIT:id>' ] for ticket in tickets <EOL> if ticket [ '<STR_LIT>' ] == user ] : <EOL> ticket = Ticket ( self . env , tkt_id ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' % ( req . base_path , <EOL> tkt_id , host ) ) <EOL> if milestone . due : <EOL> write_prop ( '<STR_LIT>' , uid ) <EOL> write_date ( '<STR_LIT>' , milestone . due ) <EOL> write_prop ( '<STR_LIT>' , _ ( '<STR_LIT>' , <EOL> num = ticket . id , <EOL> summary = ticket [ '<STR_LIT>' ] ) ) <EOL> write_prop ( '<STR_LIT>' , req . abs_href . ticket ( ticket . id ) ) <EOL> write_prop ( '<STR_LIT>' , ticket [ '<STR_LIT:description>' ] ) <EOL> priority = get_priority ( ticket ) <EOL> if priority : <EOL> write_prop ( '<STR_LIT>' , unicode ( priority ) ) <EOL> write_prop ( '<STR_LIT>' , get_status ( ticket ) ) <EOL> if ticket [ '<STR_LIT:status>' ] == '<STR_LIT>' : <EOL> for time , in self . env . db_query ( """<STR_LIT>""" , ( ticket . id , ) ) : <EOL> write_utctime ( '<STR_LIT>' , from_utimestamp ( time ) ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> write_prop ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> ics_str = buf . getvalue ( ) . encode ( '<STR_LIT:utf-8>' ) <EOL> req . send_header ( '<STR_LIT>' , len ( ics_str ) ) <EOL> req . end_headers ( ) <EOL> req . write ( ics_str ) <EOL> raise RequestDone <EOL> class MilestoneModule ( Component ) : <EOL> """<STR_LIT>""" <EOL> implements ( INavigationContributor , IPermissionRequestor , IRequestHandler , <EOL> IResourceManager , ISearchSource , ITimelineEventProvider , <EOL> IWikiSyntaxProvider ) <EOL> realm = '<STR_LIT>' <EOL> stats_provider = ExtensionOption ( '<STR_LIT>' , '<STR_LIT>' , <EOL> ITicketGroupStatsProvider , <EOL> '<STR_LIT>' , <EOL> """<STR_LIT>""" ) <EOL> default_retarget_to = Option ( '<STR_LIT>' , '<STR_LIT>' , <EOL> doc = """<STR_LIT>""" ) <EOL> default_group_by = Option ( '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> """<STR_LIT>""" ) <EOL> def get_active_navigation_item ( self , req ) : <EOL> return '<STR_LIT>' <EOL> def get_navigation_items ( self , req ) : <EOL> return [ ] <EOL> def get_permission_actions ( self ) : <EOL> actions = [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' ] <EOL> return actions + [ ( '<STR_LIT>' , actions ) ] <EOL> def get_timeline_filters ( self , req ) : <EOL> if '<STR_LIT>' in req . perm : <EOL> yield ( '<STR_LIT>' , _ ( '<STR_LIT>' ) ) <EOL> def get_timeline_events ( self , req , start , stop , filters ) : <EOL> if '<STR_LIT>' in filters : <EOL> milestone_realm = Resource ( self . realm ) <EOL> for name , due , completed , description in MilestoneCache ( self . env ) . milestones . itervalues ( ) : <EOL> if completed and start <= completed <= stop : <EOL> milestone = milestone_realm ( id = name ) <EOL> if '<STR_LIT>' in req . perm ( milestone ) : <EOL> yield ( '<STR_LIT>' , completed , '<STR_LIT>' , <EOL> ( milestone , description ) ) <EOL> for event in AttachmentModule ( self . env ) . get_timeline_events ( <EOL> req , milestone_realm , start , stop ) : <EOL> yield event <EOL> def render_timeline_event ( self , context , field , event ) : <EOL> milestone , description = event [ <NUM_LIT:3> ] <EOL> if field == '<STR_LIT:url>' : <EOL> return context . href . milestone ( milestone . id ) <EOL> elif field == '<STR_LIT:title>' : <EOL> return tag_ ( '<STR_LIT>' , <EOL> name = tag . em ( milestone . id ) ) <EOL> elif field == '<STR_LIT:description>' : <EOL> return format_to ( self . env , None , context . child ( resource = milestone ) , <EOL> description ) <EOL> def match_request ( self , req ) : <EOL> match = re . match ( r'<STR_LIT>' , req . path_info ) <EOL> if match : <EOL> if match . group ( <NUM_LIT:1> ) : <EOL> req . args [ '<STR_LIT:id>' ] = match . group ( <NUM_LIT:1> ) <EOL> return True <EOL> def process_request ( self , req ) : <EOL> milestone_id = req . args . get ( '<STR_LIT:id>' ) <EOL> req . perm ( self . realm , milestone_id ) . require ( '<STR_LIT>' ) <EOL> add_link ( req , '<STR_LIT>' , req . href . roadmap ( ) , _ ( '<STR_LIT>' ) ) <EOL> action = req . args . get ( '<STR_LIT:action>' , '<STR_LIT>' ) <EOL> try : <EOL> milestone = Milestone ( self . env , milestone_id ) <EOL> except ResourceNotFound : <EOL> if '<STR_LIT>' not in req . perm ( self . realm , milestone_id ) : <EOL> raise <EOL> milestone = Milestone ( self . env ) <EOL> milestone . name = milestone_id <EOL> action = '<STR_LIT>' <EOL> if req . method == '<STR_LIT:POST>' : <EOL> if '<STR_LIT>' in req . args : <EOL> if milestone . exists : <EOL> req . redirect ( req . href . milestone ( milestone . name ) ) <EOL> else : <EOL> req . redirect ( req . href . roadmap ( ) ) <EOL> elif action == '<STR_LIT>' : <EOL> return self . _do_save ( req , milestone ) <EOL> elif action == '<STR_LIT>' : <EOL> self . _do_delete ( req , milestone ) <EOL> else : <EOL> raise HTTPBadRequest ( _ ( "<STR_LIT>" ) ) <EOL> elif action in ( '<STR_LIT>' , '<STR_LIT>' ) : <EOL> return self . _render_editor ( req , milestone ) <EOL> elif action == '<STR_LIT>' : <EOL> return self . _render_confirm ( req , milestone ) <EOL> if not milestone . name : <EOL> req . redirect ( req . href . roadmap ( ) ) <EOL> return self . _render_view ( req , milestone ) <EOL> def get_default_due ( self , req ) : <EOL> """<STR_LIT>""" <EOL> now = datetime_now ( req . tz ) <EOL> default_due = datetime ( now . year , now . month , now . day , <NUM_LIT> ) <EOL> if now . hour > <NUM_LIT> : <EOL> default_due += timedelta ( days = <NUM_LIT:1> ) <EOL> return to_datetime ( default_due , req . tz ) <EOL> def save_milestone ( self , req , milestone ) : <EOL> warnings = [ ] <EOL> def warn ( msg ) : <EOL> add_warning ( req , msg ) <EOL> warnings . append ( msg ) <EOL> milestone . description = req . args . get ( '<STR_LIT:description>' , '<STR_LIT>' ) <EOL> if '<STR_LIT>' in req . args : <EOL> duedate = req . args . get ( '<STR_LIT>' ) <EOL> milestone . due = user_time ( req , parse_date , duedate , <EOL> hint = '<STR_LIT>' ) if duedate else None <EOL> else : <EOL> milestone . due = None <EOL> if '<STR_LIT>' in req . args : <EOL> completed = req . args . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> completed = user_time ( req , parse_date , completed , <EOL> hint = '<STR_LIT>' ) if completed else None <EOL> if completed and completed > datetime_now ( utc ) : <EOL> warn ( _ ( '<STR_LIT>' ) ) <EOL> else : <EOL> completed = None <EOL> milestone . completed = completed <EOL> new_name = req . args . get ( '<STR_LIT:name>' ) <EOL> try : <EOL> new_milestone = Milestone ( self . env , new_name ) <EOL> except ResourceNotFound : <EOL> milestone . name = new_name <EOL> else : <EOL> if new_milestone . name != milestone . name : <EOL> if new_milestone . name : <EOL> warn ( _ ( '<STR_LIT>' <EOL> '<STR_LIT>' , name = new_milestone . name ) ) <EOL> else : <EOL> warn ( _ ( "<STR_LIT>" ) ) <EOL> if warnings : <EOL> return False <EOL> if milestone . exists : <EOL> milestone . update ( author = req . authname ) <EOL> if completed and '<STR_LIT>' in req . args : <EOL> comment = req . args . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> retarget_to = req . args . get ( '<STR_LIT:target>' ) or None <EOL> retargeted_tickets = milestone . move_tickets ( retarget_to , req . authname , <EOL> comment , exclude_closed = True ) <EOL> add_notice ( req , _ ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' , <EOL> name = milestone . name , retarget = retarget_to ) ) <EOL> new_values = { '<STR_LIT>' : retarget_to } <EOL> comment = comment or _ ( "<STR_LIT>" ) <EOL> event = BatchTicketChangeEvent ( retargeted_tickets , None , <EOL> req . authname , comment , <EOL> new_values , None ) <EOL> try : <EOL> NotificationSystem ( self . env ) . notify ( event ) <EOL> except Exception as e : <EOL> self . log . error ( "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> exception_to_unicode ( e ) ) <EOL> add_warning ( req , tag_ ( "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> message = to_unicode ( e ) ) ) <EOL> add_notice ( req , _ ( "<STR_LIT>" ) ) <EOL> else : <EOL> milestone . insert ( ) <EOL> add_notice ( req , _ ( '<STR_LIT>' , <EOL> name = milestone . name ) ) <EOL> return True <EOL> _default_retarget_to = default_retarget_to <EOL> @ property <EOL> def default_retarget_to ( self ) : <EOL> if self . _default_retarget_to and not any ( self . _default_retarget_to == m . name <EOL> for m in Milestone . select ( self . env ) ) : <EOL> self . log . warn ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' , self . _default_retarget_to ) <EOL> return self . _default_retarget_to <EOL> def _do_delete ( self , req , milestone ) : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> retarget_to = req . args . get ( '<STR_LIT:target>' ) or None <EOL> retargeted_tickets = milestone . move_tickets ( retarget_to , req . authname , <EOL> "<STR_LIT>" ) <EOL> milestone . delete ( author = req . authname ) <EOL> add_notice ( req , _ ( '<STR_LIT>' , <EOL> name = milestone . name ) ) <EOL> if retargeted_tickets : <EOL> add_notice ( req , _ ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' , name = milestone . name , <EOL> retarget = retarget_to ) ) <EOL> new_values = { '<STR_LIT>' : retarget_to } <EOL> comment = _ ( "<STR_LIT>" ) <EOL> event = BatchTicketChangeEvent ( retargeted_tickets , None , <EOL> req . authname , comment , new_values , <EOL> None ) <EOL> try : <EOL> NotificationSystem ( self . env ) . notify ( event ) <EOL> except Exception as e : <EOL> self . log . error ( "<STR_LIT>" <EOL> "<STR_LIT>" , exception_to_unicode ( e ) ) <EOL> add_warning ( req , tag_ ( "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> message = to_unicode ( e ) ) ) <EOL> req . redirect ( req . href . roadmap ( ) ) <EOL> def _do_save ( self , req , milestone ) : <EOL> if milestone . exists : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> else : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> if self . save_milestone ( req , milestone ) : <EOL> req . redirect ( req . href . milestone ( milestone . name ) ) <EOL> return self . _render_editor ( req , milestone ) <EOL> def _render_confirm ( self , req , milestone ) : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> milestones = [ m for m in Milestone . select ( self . env ) <EOL> if m . name != milestone . name <EOL> and '<STR_LIT>' in req . perm ( m . resource ) ] <EOL> attachments = Attachment . select ( self . env , self . realm , milestone . name ) <EOL> data = { <EOL> '<STR_LIT>' : milestone , <EOL> '<STR_LIT>' : group_milestones ( milestones , <EOL> '<STR_LIT>' in req . perm ) , <EOL> '<STR_LIT>' : milestone . get_num_tickets ( ) , <EOL> '<STR_LIT>' : self . default_retarget_to , <EOL> '<STR_LIT>' : list ( attachments ) <EOL> } <EOL> add_stylesheet ( req , '<STR_LIT>' ) <EOL> return '<STR_LIT>' , data , None <EOL> def _render_editor ( self , req , milestone ) : <EOL> data = { <EOL> '<STR_LIT>' : milestone , <EOL> '<STR_LIT>' : get_datetime_format_hint ( req . lc_time ) , <EOL> '<STR_LIT>' : self . get_default_due ( req ) , <EOL> '<STR_LIT>' : [ ] , <EOL> } <EOL> if milestone . exists : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> milestones = [ m for m in Milestone . select ( self . env ) <EOL> if m . name != milestone . name <EOL> and '<STR_LIT>' in req . perm ( m . resource ) ] <EOL> data [ '<STR_LIT>' ] = group_milestones ( milestones , <EOL> '<STR_LIT>' in req . perm ) <EOL> data [ '<STR_LIT>' ] = milestone . get_num_tickets ( exclude_closed = True ) <EOL> data [ '<STR_LIT>' ] = self . default_retarget_to <EOL> else : <EOL> req . perm ( milestone . resource ) . require ( '<STR_LIT>' ) <EOL> if milestone . name : <EOL> add_notice ( req , _ ( "<STR_LIT>" <EOL> "<STR_LIT>" , name = milestone . name ) ) <EOL> chrome = Chrome ( self . env ) <EOL> chrome . add_jquery_ui ( req ) <EOL> chrome . add_wiki_toolbars ( req ) <EOL> add_stylesheet ( req , '<STR_LIT>' ) <EOL> return '<STR_LIT>' , data , None <EOL> def _render_view ( self , req , milestone ) : <EOL> milestone_groups = [ ] <EOL> available_groups = [ ] <EOL> default_group_by_available = False <EOL> ticket_fields = TicketSystem ( self . env ) . get_ticket_fields ( ) <EOL> for field in ticket_fields : <EOL> if field [ '<STR_LIT:type>' ] == '<STR_LIT>' and field [ '<STR_LIT:name>' ] != '<STR_LIT>' or field [ '<STR_LIT:name>' ] in ( '<STR_LIT>' , '<STR_LIT>' ) : <EOL> available_groups . append ( { '<STR_LIT:name>' : field [ '<STR_LIT:name>' ] , <EOL> '<STR_LIT:label>' : field [ '<STR_LIT:label>' ] } ) <EOL> if field [ '<STR_LIT:name>' ] == self . default_group_by : <EOL> default_group_by_available = True <EOL> by = None <EOL> if default_group_by_available : <EOL> by = self . default_group_by <EOL> elif available_groups : <EOL> by = available_groups [ <NUM_LIT:0> ] [ '<STR_LIT:name>' ] <EOL> by = req . args . get ( '<STR_LIT>' , by ) <EOL> tickets = get_tickets_for_milestone ( self . env , milestone = milestone . name , <EOL> field = by ) <EOL> tickets = apply_ticket_permissions ( self . env , req , tickets ) <EOL> stat = get_ticket_stats ( self . stats_provider , tickets ) <EOL> context = web_context ( req , milestone . resource ) <EOL> data = { <EOL> '<STR_LIT>' : context , <EOL> '<STR_LIT>' : milestone , <EOL> '<STR_LIT>' : AttachmentModule ( self . env ) . attachment_data ( context ) , <EOL> '<STR_LIT>' : available_groups , <EOL> '<STR_LIT>' : by , <EOL> '<STR_LIT>' : milestone_groups <EOL> } <EOL> data . update ( milestone_stats_data ( self . env , req , stat , milestone . name ) ) <EOL> if by : <EOL> def per_group_stats_data ( gstat , group_name ) : <EOL> return milestone_stats_data ( self . env , req , gstat , <EOL> milestone . name , by , group_name ) <EOL> milestone_groups . extend ( <EOL> grouped_stats_data ( self . env , self . stats_provider , tickets , by , <EOL> per_group_stats_data ) ) <EOL> add_stylesheet ( req , '<STR_LIT>' ) <EOL> add_script ( req , '<STR_LIT>' ) <EOL> def add_milestone_link ( rel , milestone ) : <EOL> href = req . href . milestone ( milestone . name , by = req . args . get ( '<STR_LIT>' ) ) <EOL> add_link ( req , rel , href , _ ( '<STR_LIT>' , <EOL> name = milestone . name ) ) <EOL> milestones = [ m for m in Milestone . select ( self . env ) <EOL> if '<STR_LIT>' in req . perm ( m . resource ) ] <EOL> idx = [ i for i , m in enumerate ( milestones ) if m . name == milestone . name ] <EOL> if idx : <EOL> idx = idx [ <NUM_LIT:0> ] <EOL> if idx > <NUM_LIT:0> : <EOL> add_milestone_link ( '<STR_LIT>' , milestones [ <NUM_LIT:0> ] ) <EOL> add_milestone_link ( '<STR_LIT>' , milestones [ idx - <NUM_LIT:1> ] ) <EOL> if idx < len ( milestones ) - <NUM_LIT:1> : <EOL> add_milestone_link ( '<STR_LIT>' , milestones [ idx + <NUM_LIT:1> ] ) <EOL> add_milestone_link ( '<STR_LIT>' , milestones [ - <NUM_LIT:1> ] ) <EOL> prevnext_nav ( req , _ ( '<STR_LIT>' ) , _ ( '<STR_LIT>' ) , <EOL> _ ( '<STR_LIT>' ) ) <EOL> return '<STR_LIT>' , data , None <EOL> def get_wiki_syntax ( self ) : <EOL> return [ ] <EOL> def get_link_resolvers ( self ) : <EOL> yield ( '<STR_LIT>' , self . _format_link ) <EOL> def _format_link ( self , formatter , ns , name , label ) : <EOL> name , query , fragment = formatter . split_link ( name ) <EOL> return self . _render_link ( formatter . context , name , label , <EOL> query + fragment ) <EOL> def _render_link ( self , context , name , label , extra = '<STR_LIT>' ) : <EOL> if not ( name or extra ) : <EOL> return tag ( ) <EOL> try : <EOL> milestone = Milestone ( self . env , name ) <EOL> except ResourceNotFound : <EOL> milestone = None <EOL> href = context . href . milestone ( name ) <EOL> if milestone and milestone . exists : <EOL> if '<STR_LIT>' in context . perm ( milestone . resource ) : <EOL> title = None <EOL> if hasattr ( context , '<STR_LIT>' ) : <EOL> if milestone . is_completed : <EOL> title = _ ( <EOL> '<STR_LIT>' , <EOL> duration = pretty_timedelta ( milestone . completed ) , <EOL> date = user_time ( context . req , format_datetime , <EOL> milestone . completed ) ) <EOL> elif milestone . is_late : <EOL> title = _ ( '<STR_LIT>' , <EOL> duration = pretty_timedelta ( milestone . due ) , <EOL> date = user_time ( context . req , format_datetime , <EOL> milestone . due ) ) <EOL> elif milestone . due : <EOL> title = _ ( '<STR_LIT>' , <EOL> duration = pretty_timedelta ( milestone . due ) , <EOL> date = user_time ( context . req , format_datetime , <EOL> milestone . due ) ) <EOL> else : <EOL> title = _ ( '<STR_LIT>' ) <EOL> closed = '<STR_LIT>' if milestone . is_completed else '<STR_LIT>' <EOL> return tag . a ( label , class_ = '<STR_LIT>' % closed , <EOL> href = href + extra , title = title ) <EOL> elif '<STR_LIT>' in context . perm ( self . realm , name ) : <EOL> return tag . a ( label , class_ = '<STR_LIT>' , href = href + extra , <EOL> rel = '<STR_LIT>' ) <EOL> return tag . a ( label , class_ = '<STR_LIT>' ) <EOL> def get_resource_realms ( self ) : <EOL> yield self . realm <EOL> def get_resource_description ( self , resource , format = None , context = None , <EOL> ** kwargs ) : <EOL> desc = resource . id <EOL> if format != '<STR_LIT>' : <EOL> desc = _ ( '<STR_LIT>' , name = resource . id ) <EOL> if context : <EOL> return self . _render_link ( context , resource . id , desc ) <EOL> else : <EOL> return desc <EOL> def resource_exists ( self , resource ) : <EOL> """<STR_LIT>""" <EOL> return resource . id in MilestoneCache ( self . env ) . milestones <EOL> def get_search_filters ( self , req ) : <EOL> if '<STR_LIT>' in req . perm : <EOL> yield ( '<STR_LIT>' , _ ( '<STR_LIT>' ) ) <EOL> def get_search_results ( self , req , terms , filters ) : <EOL> if not '<STR_LIT>' in filters : <EOL> return <EOL> term_regexps = search_to_regexps ( terms ) <EOL> milestone_realm = Resource ( self . realm ) <EOL> for name , due , completed , description in MilestoneCache ( self . env ) . milestones . itervalues ( ) : <EOL> if all ( r . search ( description ) or r . search ( name ) <EOL> for r in term_regexps ) : <EOL> milestone = milestone_realm ( id = name ) <EOL> if '<STR_LIT>' in req . perm ( milestone ) : <EOL> dt = ( completed if completed else <EOL> due if due else datetime_now ( utc ) ) <EOL> yield ( get_resource_url ( self . env , milestone , req . href ) , <EOL> get_resource_name ( self . env , milestone ) , dt , <EOL> '<STR_LIT>' , shorten_result ( description , terms ) ) <EOL> for result in AttachmentModule ( self . env ) . get_search_results ( <EOL> req , milestone_realm , terms ) : <EOL> yield result </s>
