@@ -37,6 +37,35 @@ namespace MigrationTools.Processors
3737 /// <processingtarget>Work Items</processingtarget>
3838 public class TfsWorkItemMigrationProcessor : TfsProcessor
3939 {
40+ private class ProgressTimer
41+ {
42+ private TimeSpan _totalProcessedTime = TimeSpan . Zero ;
43+
44+ public ProgressTimer ( int totalCount )
45+ {
46+ TotalCount = totalCount ;
47+ }
48+
49+ public int TotalCount { get ; private set ; }
50+ public int ProcessedCount { get ; private set ; }
51+ public TimeSpan TotalProcessedTime => _totalProcessedTime ;
52+
53+ public void AddProcessedItem ( TimeSpan processDuration , bool addToTotal )
54+ {
55+ if ( addToTotal )
56+ {
57+ TotalCount ++ ;
58+ }
59+ ProcessedCount ++ ;
60+ _totalProcessedTime += processDuration ;
61+ }
62+
63+ public TimeSpan AverageTime => ProcessedCount > 0
64+ ? TimeSpan . FromSeconds ( TotalProcessedTime . TotalSeconds / ProcessedCount )
65+ : TotalProcessedTime ;
66+
67+ public TimeSpan RemainingTime => TimeSpan . FromTicks ( AverageTime . Ticks * ( TotalCount - ProcessedCount ) ) ;
68+ }
4069
4170 private static int _count = 0 ;
4271 private static int _current = 0 ;
@@ -147,6 +176,8 @@ protected override void InternalExecute()
147176 _current = 1 ;
148177 _count = sourceWorkItems . Count ;
149178 _totalWorkItem = sourceWorkItems . Count ;
179+ ProgressTimer progressTimer = new ProgressTimer ( _totalWorkItem ) ;
180+
150181 ProcessorActivity . SetTag ( "source_workitems_to_process" , sourceWorkItems . Count ) ;
151182 foreach ( WorkItemData sourceWorkItemData in sourceWorkItems )
152183 {
@@ -163,7 +194,7 @@ protected override void InternalExecute()
163194 {
164195 try
165196 {
166- ProcessWorkItemAsync ( sourceWorkItemData , Options . WorkItemCreateRetryLimit ) . Wait ( ) ;
197+ ProcessWorkItemAsync ( sourceWorkItemData , progressTimer , Options . WorkItemCreateRetryLimit ) . Wait ( ) ;
167198
168199 stopwatch . Stop ( ) ;
169200 var processingTime = stopwatch . Elapsed . TotalMilliseconds ;
@@ -407,7 +438,6 @@ private WorkItemData CreateWorkItem_Shell(ProjectData destProject, WorkItemData
407438 activity ? . SetTagsFromOptions ( Options ) ;
408439 activity ? . SetTag ( "http.request.method" , "GET" ) ;
409440 activity ? . SetTag ( "migrationtools.client" , "TfsObjectModel" ) ;
410- activity ? . SetEndTime ( activity . StartTimeUtc . AddSeconds ( 10 ) ) ;
411441
412442 WorkItem newwit ;
413443 if ( destProject . ToProject ( ) . WorkItemTypes . Contains ( destType ) )
@@ -580,14 +610,13 @@ private void ProcessWorkItemEmbeddedLinks(WorkItemData sourceWorkItem, WorkItemD
580610 }
581611 }
582612
583- private async Task ProcessWorkItemAsync ( WorkItemData sourceWorkItem , int retryLimit = 5 , int retries = 0 )
613+ private async Task ProcessWorkItemAsync ( WorkItemData sourceWorkItem , ProgressTimer progressTimer , int retryLimit = 5 , int retries = 0 )
584614 {
585615 using ( var activity = ActivitySourceProvider . ActivitySource . StartActivity ( "ProcessWorkItemAsync" , ActivityKind . Client ) )
586616 {
587617 activity ? . SetTagsFromOptions ( Options ) ;
588618 activity ? . SetTag ( "http.request.method" , "GET" ) ;
589619 activity ? . SetTag ( "migrationtools.client" , "TfsObjectModel" ) ;
590- activity ? . SetEndTime ( activity . StartTimeUtc . AddSeconds ( 10 ) ) ;
591620 activity ? . SetTag ( "SourceURL" , Source . Options . Collection . ToString ( ) ) ;
592621 activity ? . SetTag ( "SourceWorkItem" , sourceWorkItem . Id ) ;
593622 activity ? . SetTag ( "TargetURL" , Target . Options . Collection . ToString ( ) ) ;
@@ -675,7 +704,7 @@ private async Task ProcessWorkItemAsync(WorkItemData sourceWorkItem, int retryLi
675704 { "Retrys" , retries } ,
676705 { "RetryLimit" , retryLimit }
677706 } ) ;
678- await ProcessWorkItemAsync ( sourceWorkItem , retryLimit , retries ) ;
707+ await ProcessWorkItemAsync ( sourceWorkItem , progressTimer , retryLimit , retries ) ;
679708 }
680709 else
681710 {
@@ -691,15 +720,15 @@ private async Task ProcessWorkItemAsync(WorkItemData sourceWorkItem, int retryLi
691720 Telemetry . TrackException ( ex , activity . Tags ) ;
692721 throw ex ;
693722 }
694- var average = new TimeSpan ( 0 , 0 , 0 , 0 , ( int ) ( activity . Duration . TotalMilliseconds / _current ) ) ;
695- var remaining = new TimeSpan ( 0 , 0 , 0 , 0 , ( int ) ( average . TotalMilliseconds * _count ) ) ;
723+ activity ? . Stop ( ) ;
724+ progressTimer . AddProcessedItem ( activity . Duration , retries > 0 ) ;
696725 TraceWriteLine ( LogEventLevel . Information ,
697- "Average time of {average:%s}.{average:%fff} per work item and {remaining:%h} hours {remaining:%m} minutes {remaining:%s}.{remaining:%fff} seconds estimated to completion" ,
726+ "Average time of {average:%s}.{average:%fff} per work item and {remaining:%h} hours {remaining:%m} minutes {remaining:%s} seconds estimated to completion. " ,
698727 new Dictionary < string , object > ( ) {
699- { "average" , average } ,
700- { "remaining" , remaining }
728+ { "average" , progressTimer . AverageTime } ,
729+ { "remaining" , progressTimer . RemainingTime }
701730 } ) ;
702- activity ? . Stop ( ) ;
731+
703732 activity ? . SetStatus ( ActivityStatusCode . Error ) ;
704733 activity ? . SetTag ( "http.response.status_code" , "200" ) ;
705734
0 commit comments