/* * Author: Essam Fahmy * Program: GHGReports * File: Program.cs * Date: August 7, 2023 * Course: INFO-3138 * Description: A console app that displays data about green house gases in Canada from an xml file. * User can use settings to display specific data about green house gases in Canada * such as start year and end years of data as well as specific regions and ghg sources. */ using System.Xml; using System.Xml.XPath; namespace GHGReports { internal class Program { // Global variables to hold the data and settings file paths const string DataFilePath = "..\\..\\..\\ghg-canada.xml"; const string SettingsFilePath = "..\\..\\..\\settings.xml"; // Global variables to hold the start year, end year, region, and ghg source settings private static int startYear; private static int endYear; private static string region; private static string ghgSource; /* * Name: Main * Purpose: This is the main entry point of the program. * We will intialize and call required functions through here. * Accepts: Nothing * Returns: Nothing */ static void Main() { // Try catch block to handle exceptions try { // While loop to continuously display menu until user quits while (true) { // Creating XmlDocument object and loading the data using DataFilePath XmlDocument doc = new XmlDocument(); doc.Load(DataFilePath); // Calling LoadSettings() to load the settings data into the global variables LoadSettings(); // Clearing console and display main menu with current settings Console.Clear(); Console.WriteLine("----------------------------------------------------------------------"); Console.WriteLine("| Greenhouse Gas Emissions in Canada ~ Main Menu |"); Console.WriteLine("----------------------------------------------------------------------"); Console.WriteLine($"| (1) To adjust the range of years (currently {startYear} to {endYear})"); Console.WriteLine($"| (2) To select a different region (currently {region})"); Console.WriteLine($"| (3) To select a different GHG source (currently {ghgSource})"); Console.WriteLine($"| (4) To learn about {region}'s emissions"); Console.WriteLine($"| (5) To learn about the {ghgSource}'s emissions"); Console.WriteLine("| (X) To quit"); Console.WriteLine("----------------------------------------------------------------------"); // Gaining user selection Console.Write("\nEnter your selection: "); string choice = Console.ReadLine() ?? ""; int numericChoice = 0; // Handling user selection and calling appropriate methods or outputting error if selection was invalid if (int.TryParse(choice, out numericChoice)) { switch (numericChoice) { case 1: numericChoice = 0; Console.Write("\nStarting year (1990 to 2019): "); choice = Console.ReadLine() ?? ""; while (int.TryParse(choice, out numericChoice) == false || numericChoice < 1990 || numericChoice > 2019) { Console.WriteLine("ERROR: Starting year must be an integer between 1990 and 2019."); Console.Write("\nStarting year (1990 to 2019): "); choice = Console.ReadLine() ?? ""; } startYear = numericChoice; Console.Write("\nEnding year (1990 to 2019): "); choice = Console.ReadLine() ?? ""; while (int.TryParse(choice, out numericChoice) == false || numericChoice < startYear || numericChoice > 2019 || numericChoice > startYear + 4) { Console.WriteLine($"ERROR: Ending year must be an integer between {startYear} and {startYear + 4}."); Console.Write("\nStarting year (1990 to 2019): "); choice = Console.ReadLine() ?? ""; } endYear = numericChoice; SaveSettings(); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); break; case 2: RegionMenu(doc); break; case 3: SourceMenu(doc); break; case 4: RegionReport(doc); break; case 5: SourceReport(doc); break; default: Console.WriteLine("Invalid input"); break; } } else { if (choice.ToLower() == "x") { return; } else { Console.WriteLine("Invalid input"); } } } } catch (XmlException err) { Console.WriteLine("\nXML ERROR: " + err.Message); } catch (XPathException err) { Console.WriteLine("\nXPATH ERROR: " + err.Message); } catch (Exception err) { Console.WriteLine("\nERROR: " + err.Message); } } /* * Name: RegionMenu * Purpose: Displays the regions and gains user selection for the region they want to display data for * Accepts: XmlDocument doc * Returns: Nothing */ private static void RegionMenu(XmlDocument doc) { // Clearing console Console.Clear(); // Selecting region nodes XmlNodeList? regions = doc.SelectNodes("/ghg-canada/region"); List<string> regionNames = new List<string>(); Console.WriteLine("Select a geographical region:\n"); // Outputting each region name and adding them to list foreach (XmlNode region in regions) { string regionName = region.Attributes["name"]?.InnerText; if (!regionNames.Contains(regionName)) { regionNames.Add(regionName); Console.WriteLine($"({regionNames.Count}) {regionName}"); } } // Gaining user input Console.Write("\nEnter a source #: "); string choice = Console.ReadLine() ?? ""; int numericChoice = 0; // Checking user input while (int.TryParse(choice, out numericChoice) == false || numericChoice < 1 || numericChoice > regionNames.Count) { Console.WriteLine($"Invalid input. Please enter a number from 1-{regionNames.Count}."); Console.Write("\nEnter a source #: "); choice = Console.ReadLine() ?? ""; } // Setting newly selected region region = regionNames[numericChoice - 1]; // Calling SaveSettings() to save new settings SaveSettings(); } /* * Name: SourceMenu * Purpose: Displays the sources and gains user selection for the source they want to display data for * Accepts: XmlDocument doc * Returns: Nothing */ private static void SourceMenu(XmlDocument doc) { // Clearing console Console.Clear(); // Selecting source nodes XmlNodeList? sources = doc.SelectNodes("/ghg-canada/region/source"); List<string> sourceDescriptions = new List<string>(); Console.WriteLine("Select a GHG source:\n"); // Outputting each source description and adding them to list foreach (XmlNode source in sources) { string sourceDescription = source.Attributes["description"]?.InnerText; if (!sourceDescriptions.Contains(sourceDescription)) { sourceDescriptions.Add(sourceDescription); Console.WriteLine($"({sourceDescriptions.Count}) {sourceDescription}"); } } // Gaining user input Console.Write("\nEnter a source #: "); string choice = Console.ReadLine() ?? ""; int numericChoice = 0; // Checking user input while (int.TryParse(choice, out numericChoice) == false || numericChoice < 1 || numericChoice > sourceDescriptions.Count) { Console.WriteLine($"Invalid input. Please enter a number from 1-{sourceDescriptions.Count}."); Console.Write("\nEnter a source #: "); choice = Console.ReadLine() ?? ""; } // Setting newly selected region ghgSource = sourceDescriptions[numericChoice - 1]; // Calling SaveSettings() to save new settings SaveSettings(); } /* * Name: LoadSettings * Purpose: Loads settings data from the settings xml file and initializes the settings global variables * Accepts: Nothing * Returns: Nothing */ private static void LoadSettings() { // Creating XmlDocument object and loading the settings xml file data using the SettingsFilePath XmlDocument doc = new XmlDocument(); doc.Load(SettingsFilePath); // Selecting each setting data XmlNode? startYearNode = doc.SelectSingleNode("/settings/startYear"); XmlNode? endYearNode = doc.SelectSingleNode("/settings/endYear"); XmlNode? regionNode = doc.SelectSingleNode("/settings/region"); XmlNode? ghgSourceNode = doc.SelectSingleNode("/settings/ghgSource"); // Setting each setting global variable with its respective data startYear = int.TryParse(startYearNode?.InnerText, out var parsedStartYear) ? parsedStartYear : 0; endYear = int.TryParse(endYearNode?.InnerText, out var parsedEndYear) ? parsedEndYear : 0; region = regionNode?.InnerText ?? ""; ghgSource = ghgSourceNode?.InnerText ?? ""; } /* * Name: SaveSettings * Purpose: Saves the current settings global variables to the settings xml file * Accepts: Nothing * Returns: Nothing */ private static void SaveSettings() { // Creating XmlDocument object and loading the settings xml file data using the SettingsFilePath XmlDocument doc = new XmlDocument(); doc.Load(SettingsFilePath); // Selecting each setting data XmlNode? startYearNode = doc.SelectSingleNode("/settings/startYear"); XmlNode? endYearNode = doc.SelectSingleNode("/settings/endYear"); XmlNode? regionNode = doc.SelectSingleNode("/settings/region"); XmlNode? ghgSourceNode = doc.SelectSingleNode("/settings/ghgSource"); // Setting each node with its respective data using our setting global variables if (startYearNode != null) startYearNode.InnerText = startYear.ToString(); if (endYearNode != null) endYearNode.InnerText = endYear.ToString(); if (regionNode != null) regionNode.InnerText = region; if (ghgSourceNode != null) ghgSourceNode.InnerText = ghgSource; // Saving new XML file data to the SettingsFilePath doc.Save(SettingsFilePath); } /* * Name: RegionReport * Purpose: To display the report/data for the selected region * Accepts: XmlDocument doc * Returns: Nothing */ private static void RegionReport(XmlDocument doc) { // Clearing console Console.Clear(); // Outputting title Console.WriteLine("--------------------------------------------------------------------------"); Console.WriteLine($"| Greenhouse Gas Emissions (Megatonnes) ~ {region,-25}|"); Console.WriteLine("--------------------------------------------------------------------------"); // Selecting the selected region node from our XmlDocument XmlNode? regionNode = doc.SelectSingleNode($"/ghg-canada/region[@name='{region}']"); // Selecting the sources in the region XmlNodeList? sources = regionNode.SelectNodes("source"); // Creating a list to hold each year to display List<string> years = new List<string>(); for (int i = startYear; i <= endYear; i++) { years.Add(i.ToString()); } // Printing table header and years Console.Write("| Source "); foreach (string year in years) { Console.Write($" {year,8}"); } Console.WriteLine(" |"); Console.WriteLine("--------------------------------------------------------------------------"); // Printing emissions data for each source with proper formatting foreach (XmlNode source in sources) { string sourceDescription = source.Attributes["description"]?.InnerText; // Checking length of source description and truncating it if needed if (sourceDescription.Length > 25) { sourceDescription = sourceDescription.Substring(0, 22) + "..."; } // Outputting source description Console.Write($"| {sourceDescription,-25}"); // Outputting source data for each year foreach (string year in years) { XmlNode? emission = source.SelectSingleNode($"emissions[@year='{year}']"); string value = emission?.InnerText ?? "-"; // Rounding to 2 decimal places and outputting if (double.TryParse(value, out double parsedValue)) { double roundedValue = Math.Round(parsedValue, 2); Console.Write($" {roundedValue,8}"); } else { Console.Write($" {value,8}"); } } Console.WriteLine(" |"); } Console.WriteLine("--------------------------------------------------------------------------"); // Lets user see data before continuing Console.WriteLine("\nPress any key to continue..."); Console.ReadKey(); } /* * Name: SourceReport * Purpose: To display the report/data for the selected source for each region * Accepts: XmlDocument doc * Returns: Nothing */ private static void SourceReport(XmlDocument doc) { // Clearing console Console.Clear(); // Outputting title Console.WriteLine("--------------------------------------------------------------------------"); Console.WriteLine($"| Greenhouse Gas Emissions (Megatonnes) ~ {(ghgSource.Length > 23 ? ghgSource.Substring(0, 23) + ".." : ghgSource),-25}|"); Console.WriteLine("--------------------------------------------------------------------------"); Console.Write("| Region "); // Selecting all regions XmlNodeList? regions = doc.SelectNodes("/ghg-canada/region"); // Creating a list to hold each year to display List<string> years = new List<string>(); for (int i = startYear; i <= endYear; i++) { years.Add(i.ToString()); } // Printing table header and years foreach (string year in years) { Console.Write($" {year,8}"); } Console.WriteLine(" |"); Console.WriteLine("--------------------------------------------------------------------------"); // Printing emissions data for each region with proper formatting foreach (XmlNode regionNode in regions) { string regionName = regionNode.Attributes["name"]?.InnerText; // Checking length of region name and truncating it if needed Console.Write($"| {(regionName.Length > 23 ? regionName.Substring(0, 23) + ".." : regionName),-25}"); // Outputting region source data for each year foreach (string year in years) { double emissionValue = 0; XmlNodeList? sources = regionNode.SelectNodes("source"); foreach (XmlNode source in sources) { if (source.Attributes["description"]?.InnerText == ghgSource) { XmlNode emission = source.SelectSingleNode($"emissions[@year='{year}']"); if (double.TryParse(emission?.InnerText, out double value)) { // Rounding to 2 decimal places emissionValue = Math.Round(value, 2); } } } // Outputting emission value Console.Write($" {emissionValue,8}"); } Console.WriteLine(" |"); } Console.WriteLine("--------------------------------------------------------------------------"); // Lets user see data before continuing Console.WriteLine("\nPress any key to continue..."); Console.ReadKey(); } } }