public override void PublishWebReports()
        {
            var reportName = string.Format(@"{0}\Index.html", DestinationFolder);


            var lastStatusDate = VirusTrackerItems.Select(item => item.StatusDate.ToShortDateString())
                                 .Distinct()
                                 .OrderBy(item => DateTime.Parse(item))
                                 .Last();

            var allCountries = VirusTrackerItems.Select(item => item.Country).Distinct().ToList();

            var consolidatedTrackerItems = allCountries.Select(country => new {
                Country    = country,
                StatusDate = lastStatusDate,
                Infections = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == lastStatusDate && item.Country == country).Sum(item => item.Infections),
                Deaths     = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == lastStatusDate && item.Country == country).Sum(item => item.Deaths),
                Recovery   = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == lastStatusDate && item.Country == country).Sum(item => item.Recovery)
            });

            var template = File.ReadAllText(@"Templates\LandingPage.txt");

            var countryData = consolidatedTrackerItems
                              .Where(item => item.Infections > 0)
                              .OrderByDescending(item => item.Infections)
                              .Aggregate(string.Empty, (curr, next) => curr + string.Format("<tr><td><a href='{0}-Page.html'>{0}</a></td><td>{1}</td><td>{2}</td><td>{3}</td></tr>", next.Country, next.Infections, next.Deaths, next.Recovery));

            template = template.Replace("ALLCOUNTRIESDATAGOESHERE", countryData);

            template = template.Replace("LASTUPDATEDDATE", lastStatusDate);

            File.WriteAllText(reportName, template);
        }
        public override void PublishWebReports()
        {
            ValidateInputs();

            var distinctStatesOrProvinces = VirusTrackerItems.Where(item => item.Country == Country)
                                            .Select(item => item.ProvinceOrState)
                                            .Distinct()
                                            .Where(provinceOrState => provinceOrState.Trim() != string.Empty)
                                            .ToList();

            if (!distinctStatesOrProvinces.Any())
            {
                return;
            }

            var hasStatesOrProvinceDate = VirusTrackerItems.Select(item => item.ProvinceOrState)
                                          .Any(state => !string.IsNullOrEmpty(state.Trim()));

            Parallel.ForEach(distinctStatesOrProvinces, provinceOrState =>
            {
                var reportName = string.Format(@"{0}\{1}-{2}-Page.html", DestinationFolder, Country, provinceOrState);

                var distinctDates = VirusTrackerItems.Where(item => item.ProvinceOrState == provinceOrState)
                                    .Select(item => item.StatusDate.ToShortDateString())
                                    .Distinct()
                                    .OrderBy(item => DateTime.Parse(item))
                                    .ToList();


                var consolidatedTrackerItems = distinctDates.Select(statusDate => new {
                    Country         = Country,
                    ProvinceOrState = provinceOrState,
                    StatusDate      = statusDate,
                    Infections      = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == statusDate && item.ProvinceOrState == provinceOrState).Sum(item => item.Infections),
                    Deaths          = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == statusDate && item.ProvinceOrState == provinceOrState).Sum(item => item.Deaths),
                    Recovery        = VirusTrackerItems.Where(item => item.StatusDate.ToShortDateString() == statusDate && item.ProvinceOrState == provinceOrState).Sum(item => item.Recovery)
                }).ToList();

                //If there are no Infections in a province or state don't produce the report
                if (consolidatedTrackerItems.Last().Infections == 0)
                {
                    return;
                }

                var template = File.ReadAllText(@"Templates\ProvinceOrStatePage.txt");

                var chartTitle = string.Format("COVID-19 Infections, Deaths & Recovery - {0}-{1}", Country, provinceOrState);

                var chartData = consolidatedTrackerItems.Aggregate("['Date', 'Infections','Deaths','Recovery']", (curr, next) => curr + "," + "['" + DateTime.Parse(next.StatusDate).ToString("MM/dd") + "'," + next.Infections + "," + next.Deaths + "," + next.Recovery + "]");

                var deathProgressBarWidth = Math.Round(((double)consolidatedTrackerItems.Last().Deaths / (double)consolidatedTrackerItems.Last().Infections) * 100);

                var recoveryProgressBarWidth = Math.Round(((double)consolidatedTrackerItems.Last().Recovery / (double)consolidatedTrackerItems.Last().Infections) * 100);

                template = template.Replace("STATENAMEGOESHERE", provinceOrState);

                template = template.Replace("COUNTRYNAMEGOESHERE", Country);

                template = template.Replace("CHARTTITLEGOESHERE", chartTitle);

                template = template.Replace("CHARTDATAGOESHERE", chartData);

                template = template.Replace("TOTALINFECTIONS", consolidatedTrackerItems.Last().Infections.ToString());

                template = template.Replace("TOTALDEATHS", consolidatedTrackerItems.Last().Deaths.ToString());

                template = template.Replace("TOTALRECOVERIES", consolidatedTrackerItems.Last().Recovery.ToString());

                template = template.Replace("DEATHPROGRESSBARWIDTH", deathProgressBarWidth.ToString());

                template = template.Replace("RECOVERYPROGRESSBARWIDTH", recoveryProgressBarWidth.ToString());

                template = template.Replace("UNDERTREATMENTPROGRESSBARWIDTH", (100 - deathProgressBarWidth).ToString());

                template = template.Replace("NONRECOVEREDPROGRESSBARWIDTH", (100 - recoveryProgressBarWidth).ToString());


                //Update or overwrite a file only if has changed
                if (!File.Exists(reportName))
                {
                    File.WriteAllText(reportName, template);
                }
                else
                {
                    var existingFileContent = File.ReadAllText(reportName);
                    if (!existingFileContent.Equals(template))
                    {
                        File.WriteAllText(reportName, template);
                    }
                }
            });
        }