static void Main(string[] args) { #region StartUp Section // Initialize all variables ConfigRallyUID = ""; ConfigRallyPWD = ""; ConfigLogPath = ""; ConfigStatusEOL = ""; ConfigReportPath = ""; ReportFile = ""; LogFile = ""; ConfigDBServer = ""; ConfigDBName = ""; ConfigDBUID = ""; ConfigDBPWD = ""; ReportDayNum = 0; ReportWeekNum = 0; ReportMonth = 0; ReportQuarter = 0; ReportYear = 0; // Get the configuration information from config file if (!GetConfigSettings()) { // If we can't get the configuration settings, we can't even log anything, so just terminate Environment.Exit(-1); } // Check for any commandline arguments. If there are not any, assume a "Daily" operating mode and set // the report date to yesterday (we don't want to report on today) if (args.Length != 0) { GetCommandArgs(); } else { OperatingMode = OperateMode.Daily; ReportingDay = DateTime.Today.AddDays(-1); ReportDayNum = ReportingDay.Day; ReportMonth = ReportingDay.Month; ReportYear = ReportingDay.Year; } #endregion StartUp Section // Log the start of processing LogOutput("Started processing at " + DateTime.Now.ToLongTimeString() + " on " + DateTime.Now.ToLongDateString(), "Main", false); DateTime dtStartTime = DateTime.Now; // Log the operating mode switch (OperatingMode) { case OperateMode.Daily: LogOutput("Operating in Daily Mode...Processing for Day " + ReportingDay.ToString("dd-MMM-yyyy"), "Main", false); break; case OperateMode.Monthly: LogOutput("Operating in Monthly Mode...Processing for " + ReportingDay.ToString("MMM"), "Main", false); break; case OperateMode.Quarterly: LogOutput("Operating in Quarterly mode...Processing for Quarter Q" + ReportQuarter.ToString() + "Y" + ReportYear.ToString(), "Main", false); break; case OperateMode.Annual: LogOutput("Operating in Annual mode...Processing for year " + ReportYear.ToString(), "Main", false); break; case OperateMode.Weekly: LogOutput("Operating in Weekly mode...Processing for Day " + ReportingDay.ToString("dd-MMM-yyyy"), "Main", false); break; default: LogOutput("Unknown Operating mode...assuming Daily...", "Main", false); break; } #region Gather from Rally // Create the Rally API object LogOutput("Creating reference to RallyAPI...", "Main", true); RallyAPI = new RallyRestApi(); // Login to Rally LogOutput("Starting connection to Rally...", "Main", true); try { RallyAPI.Authenticate(ConfigRallyUID, ConfigRallyPWD, ConfigRallyURL); LogOutput("Response from RallyAPI.Authenticate: " + RallyAPI.AuthenticationState.ToString(), "Main", true); if (RallyAPI.AuthenticationState.ToString() != "Authenticated") { // We did not actually connect LogOutput("Unable to connect to Rally and establish session. Application will terminate.", "Main", false); Environment.Exit(-1); } else { if (RallyAPI.ConnectionInfo.UserName == null) { LogOutput("RallyAPI.ConnectionInfo: " + RallyAPI.ConnectionInfo.ToString(), "Main", false); LogOutput("Unable to authenticate with Rally. Application will terminate.", "Main", false); Environment.Exit(-1); } else { LogOutput("Connected to Rally as user " + RallyAPI.ConnectionInfo.UserName, "Main", true); } } } catch (Exception ex) { LogOutput("Error Connecting to Rally: " + ex.Message, "Main", false); LogOutput("Rally Authentication State: " + RallyAPI.AuthenticationState.ToString() + "Rally Connection Info: " + RallyAPI.ConnectionInfo.ToString(), "Main", false); } // Grab the active Initiatives we want to report on LogOutput("Getting all Initiatives...", "Main", false); List<Initiative> InitiativeList = new List<Initiative>(); LogOutput("Calling 'GetInitiativeList'...", "Main", true); InitiativeList = GetInitiativeList(); LogOutput("Done with 'GetInitiativeList'", "Main", true); if (InitiativeList.Count == 0) { // Could not get the Initiatives...or nothing to report on, so stop LogOutput("Unable to open Initiative list or no Initiatives to report on. Application will terminate.", "Main", false); InitiativeList.Clear(); RallyAPI.Logout(); // Disconnect from Rally Environment.Exit(-1); // End Program } LogOutput("Retrieved " + InitiativeList.Count + " Initiatives to report on", "Main", false); // Now iterate through the initiatives and get all the Features or "Projects" LogOutput("Getting all Projects for the Initiatives...", "Main", false); BasicProjectList = new List<Project>(); LogOutput("Looping for each Initiative...", "Main", true); foreach (Initiative init in InitiativeList) { // Get the project list for the current initiative ONLY List<Project> ProjectList = new List<Project>(); LogOutput("Calling 'GetProjectsForInitiative' with " + init.Name.Trim() + "...", "Main", true); ProjectList = GetProjectsForInitiative(init.Name.Trim()); LogOutput("Done with 'GetProjectsForInitiative' for " + init.Name.Trim(), "Main", true); // Append this list to the FULL list LogOutput("Appending " + ProjectList.Count + " projects to object 'BasicProjectList'", "Main", true); BasicProjectList.AddRange(ProjectList); } LogOutput("Retrieved " + BasicProjectList.Count + " Projects total", "Main", false); // We need to loop through the project list now and for each project // we need to get all the epics. Then with each epic, we recursively // get all user stories LogOutput("Getting all User Stories for all projects...", "Main", false); // Initialize a new list of projects. This will become the full list including stories // and defects CompleteProjectList = new List<Project>(); LogOutput("Looping for each Project...", "Main", true); foreach (Project proj in BasicProjectList) { // Get all the epics for this project LogOutput("~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+", "Main", true); LogOutput("Calling 'GetEpicsForProject' with " + proj.Name.Trim() + "...", "Main", true); List<Epic> EpicList = new List<Epic>(); EpicList = GetEpicsForProject(proj.FormattedID.Trim()); LogOutput("Done with 'GetEpicsForProject' for " + proj.Name.Trim(), "Main", true); // Now go through each of the Epics for the current project // and recurse through to get all final-level user stories LogOutput("Getting all User Stories for " + proj.Name + "...", "Main", true); List<Epic> FullEpicList = new List<Epic>(); BasicStoryList = new List<UserStory>(); LogOutput("Looping for each Epic...", "Main", true); foreach (Epic epic in EpicList) { Epic newepic = new Epic(); List<UserStory> StoryList = new List<UserStory>(); LogOutput("Calling 'GetUserStoriesPerParent' with " + epic.FormattedID.Trim() + " as Root Parent...", "Main", true); StoryList = GetUserStoriesPerParent(epic.FormattedID.Trim(), epic.Name.Trim(), true); LogOutput("Done with 'GetUserStoriesPerParent' for " + epic.FormattedID.Trim(), "Main", true); // save the info as a new epic newepic.ActualEndDate = epic.ActualEndDate; newepic.DefectActual = epic.DefectActual; newepic.DefectEstimate = epic.DefectEstimate; newepic.DefectToDo = epic.DefectToDo; newepic.Description = epic.Description; newepic.FormattedID = epic.FormattedID; newepic.Name = epic.Name; newepic.Owner = epic.Owner; newepic.ParentProject = epic.ParentProject; newepic.PlannedEndDate = epic.PlannedEndDate; newepic.PlannedStartDate = epic.PlannedStartDate; newepic.Release = epic.Release; newepic.State = epic.State; newepic.StateChangedDate = epic.StateChangedDate; newepic.StoryActual = epic.StoryActual; newepic.StoryEstimate = epic.StoryEstimate; newepic.StoryToDo = epic.StoryToDo; newepic.UserStories = StoryList; // Add the stories to the full list FullEpicList.Add(newepic); BasicStoryList.AddRange(StoryList); } LogOutput("Retrieved " + BasicStoryList.Count + " User Stories for " + proj.Name, "Main", false); // Get any defects there may be for the User Stories LogOutput("Getting all Defects for " + proj.Name + "...", "Main", false); BasicDefectList = new List<Defect>(); LogOutput("Looping for each Story...", "Main", true); foreach (UserStory story in BasicStoryList) { List<Defect> DefectList = new List<Defect>(); // Defects will always be attached to a User Story LogOutput("Calling 'GetDefectsForStory' with " + story.Name + "...", "Main", true); DefectList = GetDefectsForStory(story); LogOutput("Done with 'GetDefectsForStory' for " + story.Name, "Main", true); // If there are any defects, add them to the list if (DefectList.Count > 0) { LogOutput("Appending " + DefectList.Count + " defects to object 'BasicDefectList'", "Main", true); BasicDefectList.AddRange(DefectList); } } // At this point we have the FULL list of User Stories/Defects for the current // project. We now create a "new" project with the same properties, but this time // we are able to store the Epics, User Stories, and Defects. LogOutput("Creating new project object and populating with full information...", "Main", true); Project newproject = new Project(); newproject.ActualEndDate = proj.ActualEndDate; newproject.Description = proj.Description; newproject.Expedite = proj.Expedite; newproject.FormattedID = proj.FormattedID; newproject.Initiative = proj.Initiative; newproject.LastUpdateDate = proj.LastUpdateDate; newproject.Name = proj.Name; newproject.OpportunityAmount = proj.OpportunityAmount; newproject.Owner = proj.Owner; newproject.UpdateOwner = proj.UpdateOwner; newproject.StakeHolder = proj.StakeHolder; newproject.PlannedEndDate = proj.PlannedEndDate; newproject.PlannedStartDate = proj.PlannedStartDate; newproject.RevokedReason = proj.RevokedReason; newproject.State = proj.State; newproject.StateChangedDate = proj.StateChangedDate; newproject.StatusUpdate = proj.StatusUpdate; newproject.UserStories = BasicStoryList; newproject.Defects = BasicDefectList; newproject.Epics = FullEpicList; LogOutput("Appending new project object to object 'CompleteProjectList'", "Main", true); CompleteProjectList.Add(newproject); } LogOutput("Done looping through all projects", "Main", false); LogOutput("Appending new project object to object 'CompleteProjectList'", "Main", true); #endregion Gather from Rally #region Calculation Section // We now have a full list of all projects with all complete information so // at this point we can calculate the Actuals for each project based on the reporting mode we are operating in switch (OperatingMode) { case OperateMode.Daily: // This mode runs every day and simply keeps running total of time with daily inserts for each project AllProjectInfo = new List<Project>(); LogOutput("Calling 'CalculateDailyTotals' with " + CompleteProjectList.Count + " complete projects...", "Main", true); AllProjectInfo = CalculateDailyTotals(CompleteProjectList, ReportingDay); LogOutput("Done with 'CalculateDailyTotals'", "Main", true); // Now create the final report LogOutput("Calling 'CreateDailyReport'...", "Main", true); CreateDailyReport(AllProjectInfo, ReportingDay); LogOutput("Done with 'CreateDailyReport'...", "Main", true); break; case OperateMode.Monthly: // This mode runs each month and creates very high-level summary info AllProjectInfo = new List<Project>(); LogOutput("Calling 'CalculateMonthlyReport' with " + CompleteProjectList.Count + " complete projects...", "Main", true); AllProjectInfo = CalculateMonthlyReport(CompleteProjectList, ReportMonth); LogOutput("Done with 'CalculateMonthlyReport'", "Main", true); // Now create the final report LogOutput("Calling 'CreateMonthlyReport'...", "Main", true); CreateMonthlyReport(AllProjectInfo, ReportMonth); LogOutput("Done with 'CreateMonthlyReport'...", "Main", true); break; case OperateMode.Quarterly: AllProjectInfo = new List<Project>(); LogOutput("Calling 'CalculateQuarterTotals' with " + CompleteProjectList.Count + " complete projects...", "Main", true); AllProjectInfo = CalculateQuarterTotals(CompleteProjectList, ReportQuarter, ReportYear); LogOutput("Done with 'CalculateQuarterTotals'", "Main", true); // Now create the final report LogOutput("Calling 'CreateQuarterReport'...", "Main", true); CreateQuarterReport(AllProjectInfo, ReportYear.ToString() + 'Q' + ReportQuarter.ToString()); LogOutput("Done with 'CreateQuarterReport'...", "Main", true); break; case OperateMode.Annual: AllProjectInfo = new List<Project>(); LogOutput("Calling 'CalculateAnnualTotals' with " + CompleteProjectList.Count + " complete projects...", "Main", true); AllProjectInfo = CalculateAnnualTotals(CompleteProjectList, ReportYear); LogOutput("Done with 'CalculateAnnualTotals'", "Main", true); // Now create the final report LogOutput("Calling 'CreateAnnualReport'...", "Main", true); CreateAnnualReport(AllProjectInfo, ReportYear); LogOutput("Done with 'CreateAnnualReport'...", "Main", true); break; case OperateMode.Weekly: // This mode is intended to run on Sunday in order to run stats for up-to-and-including current week for projects. The "Project Update" // is also included so the single table can be queried AllProjectInfo = new List<Project>(); LogOutput("Calling 'CalculateWeeklyTotals' with " + CompleteProjectList.Count + " complete projects...", "Main", true); AllProjectInfo = CalculateWeeklyTotals(CompleteProjectList, ReportingDay); LogOutput("Done with 'CalculateWeeklyTotals'", "Main", true); // Now create the weekly report LogOutput("Calling 'CreateWeeklyReport'...", "Main", true); CreateWeeklyReport(AllProjectInfo, ReportingDay); LogOutput("Done with 'CreateWeeklyReport'...", "Main", true); break; } #endregion DateTime dtEndTime = DateTime.Now; string strTotSeconds = dtEndTime.Subtract(dtStartTime).TotalSeconds.ToString(); LogOutput("Completed processing at " + DateTime.Now.ToLongTimeString() + " on " + DateTime.Now.ToLongDateString() + " - Total Processing Time: " + strTotSeconds + " seconds", "Main", false); Environment.Exit(0); }