For workspaces in select depots, show the depot's latest transaction the last time the workspace was successfully updated. Flags workspaces that are in an inconsistent state (update cancellation/failure) by displaying their {update - target} levels. Results are sent to the console.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="Depots" type="AcUtils.DepotsSection, AcUtils, Version=1.6.4.0, Culture=neutral, PublicKeyToken=26470c2daf5c2e2f, processorArchitecture=MSIL"/>
  </configSections>
  <Depots>
    <depots>
      <!-- depots whose workspaces should be included in the report -->
    </depots>
  </Depots>
  <system.diagnostics>
    <assert assertuienabled="false"/>
    <sources>
      <source name="WSpaceTransLevel">
        <listeners>
          <remove name="Default"/>
          <add name="AcLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
  </startup>
</configuration>
using System.Collections.Generic;
 
namespace WSpaceTransLevel
{
    class Program
    {
        #region class variables
        private static DepotsCollection _selDepots;
        private static AcWorkspaces _wspaces;
        #endregion
        static int Main()
        {
            if (!init()) return 1;
            Task<bool> rpt = reportAsync();
            bool ret = rpt.Result;
            return (ret) ? 0 : 1;
        }
        
        
        
        private static async Task<bool> reportAsync()
        {
            int num = (
from ws in _wspaces
 
                       where ws.UpdateLevel > 0 && ws.TargetLevel > 0
                       select ws).Count();
            List<Task<XElement>> tasks = new List<Task<XElement>>(num);
            foreach (AcWorkspace ws in _wspaces.OrderBy(n => n))
            {
                if (ws.UpdateLevel > 0 && ws.TargetLevel > 0)
                    tasks.Add(latestTransAsync(ws));
                else
                    Console.WriteLine($"{ws} off {ws.getBasis()} in depot {ws.Depot} needs an update.");
            }
            XElement[] arr = await Task.WhenAll(tasks); 
            if (arr == null || arr.Any(n => n == null)) return false;
            foreach (XElement t in arr.OrderBy(n => n.Annotation<AcWorkspace>().Depot)
                .ThenByDescending(n => n.acxTime("time"))
                .ThenBy(n => n.Annotation<AcWorkspace>().Name))
            {
                AcWorkspace ws = t.Annotation<AcWorkspace>();
                string levels = (ws.UpdateLevel == ws.TargetLevel) ? String.Empty : $", {{{ws.UpdateLevel} - {ws.TargetLevel}}}";
                Console.WriteLine($"The last time {ws} off {ws.getBasis()} was successfully updated,{Environment.NewLine}" +
                    $"the latest transaction {(int)t.Attribute("id")} in depot {ws.Depot} occurred on {t.acxTime("time")}{levels}");
            }
            return true;
        }
        
        
        
        
        private static async Task<XElement> latestTransAsync(AcWorkspace wspace)
        {
            XElement trans = null; 
            try
            {
                AcResult r = await AcCommand.runAsync($@"hist -fx -p ""{wspace.Depot}"" -t {wspace.UpdateLevel}");
                if (r != null && r.RetVal == 0)
                {
                    XElement xml = XElement.Parse(r.CmdResult);
                    trans = xml.Element("transaction");
                    if (trans != null)
                        trans.AddAnnotation(wspace);
                }
            }
            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in Program.latestTransAsync{Environment.NewLine}{ecx.Message}");
            }
            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in Program.latestTransAsync{Environment.NewLine}{ecx.Message}");
            }
            return trans;
        }
        
        private static bool init()
        {
            
            if (!AcDebug.initAcLogging())
            {
                Console.WriteLine("Logging support initialization failed.");
                return false;
            }
            
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(AcDebug.unhandledException);
            
            Task<string> prncpl = AcQuery.getPrincipalAsync();
            if (String.IsNullOrEmpty(prncpl.Result))
            {
                AcDebug.Log($"Not logged into AccuRev.{Environment.NewLine}Please login and try again.");
                return false;
            }
            
            if (!initAppConfigData()) return false;
            
            Task<bool> wslist = initWSListAsync();
            if (!wslist.Result)
            {
                AcDebug.Log($"Workspaces list initialization failed. See log file:{Environment.NewLine}{AcDebug.getLogFile()}");
                return false;
            }
            return true;
        }
        
        
        
        private static bool initAppConfigData()
        {
            bool ret = false; 
            try
            {
                DepotsSection depotsConfigSection = ConfigurationManager.GetSection("Depots") as DepotsSection;
                if (depotsConfigSection == null)
                    AcDebug.Log("Error in Program.initAppConfigData creating DepotsSection");
                else
                {
                    _selDepots = depotsConfigSection.Depots;
                    ret = true;
                }
            }
            catch (ConfigurationErrorsException exc)
            {
                Process currentProcess = Process.GetCurrentProcess();
                ProcessModule pm = currentProcess.MainModule;
                AcDebug.Log($"Invalid data in {pm.ModuleName}.config{Environment.NewLine}{exc.Message}");
            }
            return ret;
        }
        
        
        private static async Task<bool> initWSListAsync()
        {
            
            AcDepots depots = new AcDepots();
            if (!(await depots.initAsync(_selDepots))) return false;
            
            _wspaces = new AcWorkspaces(depots, allWSpaces: true, includeHidden: false);
            if (!(await _wspaces.initAsync())) return false;
            return true;
        }
    }
}