using log4net.Repository.Hierarchy;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace DCU_Weather_App
public class DCU_Weather_App
public static void Main(string[] args)
RestRequestHandler apiHandler = new RestRequestHandler();
root = ((Hierarchy)LogManager.GetRepository()).Root;
root.AddAppender(GetConsoleAppender());
root.Level = log4net.Core.Level.All;
root.Repository.Configured = true;
ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
log.Debug("Logger configured.");
List<Location> places = GetPlacesOfInterest();
string temperatureUnitHeading = "F";
string temperatureUnit = "imperial";
string serviceUri = "http://api.openweathermap.org/";
WeatherService service = new WeatherService(temperatureUnit, serviceUri, apiHandler);
foreach (Location place in places)
log.DebugFormat("Requesting 5 day forecast for {0}", place.ToString());
List<DailyForecast> fiveDayForecast = service.GetFiveDayForecast(place);
PrintFiveDayForecast(temperatureUnitHeading, place, fiveDayForecast);
catch (ForecastException )
PrintFiveDayForecast(temperatureUnitHeading, place, null);
catch (JsonSerializationException )
PrintFiveDayForecast(temperatureUnitHeading, place, null);
private static void PrintFiveDayForecast(string temperatureUnitHeading, Location place, List<DailyForecast> fiveDayForecast)
Console.WriteLine("________________________________");
Console.WriteLine(place.ToString());
Console.WriteLine("Date Avg Temp ({0})", temperatureUnitHeading);
Console.WriteLine("--------------------------------");
if (fiveDayForecast != null && fiveDayForecast.Count > 0)
for (int i = 0; i < 5; i++)
Console.WriteLine(fiveDayForecast[i].ToString());
Console.WriteLine("Forecast not available.");
private static List<Location> GetPlacesOfInterest()
List<Location> places = new List<Location>();
places.Add(new Location("Marlborough", "MA", "US", "01752", 0, "5e29182a06c63b5f602678a726e792fd"));
places.Add(new Location("San Diego", "CA", "US", "92101", 0, "bf24ba3f36d441c256e1b273c09907b4"));
places.Add(new Location("Cheyenne", "WY", "US", "82001", 0, "9d301f9f938122e2cb2761fc6f1aeb44"));
places.Add(new Location("Anchorage", "AK", "US", "99501", 0, "9d301f9f938122e2cb2761fc6f1aeb44"));
places.Add(new Location("Austin", "TX", "US", "78701", 0, "100c9e8d324f793a3193472c7ce67cdc"));
places.Add(new Location("Orlando", "FL", "US", "32801", 0, "e6b773396aa847aa6e5781e7dd73f690"));
places.Add(new Location("Seattle", "WA", "US", "98101", 0, "a526352b22c8029e97710b86fb86214b"));
places.Add(new Location("Cleveland", "OH", "US", "44101", 0, "43ed1dc91611eceb140443bcd2d2ef15"));
places.Add(new Location("Portland", "ME", "US", "04101", 0, "c5d5f4ee0d98ca4abd2794d9916ac294"));
places.Add(new Location("Honolulu", "HI", "US", "96801", 0, "5395607abfce7204d1c0302e12242590"));
private static ConsoleAppender GetConsoleAppender()
ConsoleAppender lAppender = new ConsoleAppender();
lAppender.Name = "Console";
lAppender.Layout = new log4net.Layout.PatternLayout("%date{dd-MM-yyyy HH:mm:ss,fff} %5level [%2thread] %message (%logger{1}:%line)%n");
lAppender.Threshold = log4net.Core.Level.Error;
lAppender.ActivateOptions();
public class WeatherService
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string PathFiveDayMethod = "data/2.5/forecast";
private string TemperatureUnit;
private string TemperatureUnitLabel;
public string ServiceAddress { get; set; }
public RestRequestHandler ApiHandler { get; set; }
TemperatureUnit = string.Empty;
TemperatureUnitLabel = "K";
public WeatherService(string temperatureUnit, string serviceAddress, RestRequestHandler apiHandler)
string temperatureUnitCleansed = temperatureUnit.ToLower();
TemperatureUnit = temperatureUnitCleansed;
TemperatureUnit = temperatureUnitCleansed;
TemperatureUnitLabel = "F";
TemperatureUnit = temperatureUnitCleansed;
TemperatureUnitLabel = "C";
TemperatureUnitLabel = "K";
ServiceAddress = serviceAddress;
public List<DailyForecast> GetFiveDayForecast(Location location)
string url = string.Format("{0}{1}?zip={2}&APPID={3}&units={4}", ServiceAddress, PathFiveDayMethod, location.ZipCode, location.ApiKey, TemperatureUnit);
string responseData = ApiHandler.ExecuteMethodAsGet(url, "application/json");
FiveDayForecastRawData rawData = SerializeFiveDayForecast(responseData);
log.Debug(rawData.ToString());
List<DailyForecast> fiveDayForecast = GenerateDailyAverages(rawData);
catch (HttpRequestException ex)
log.ErrorFormat("Unable to acquire 5 day forecast for {0}. Url: {4} {1}:{2} {3}",
location.ToString(), ex.GetType().Name, ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", url);
string msg = string.Format("Unable to obtain forecast for {0}. Url:{1}", location.ToString(), url);
throw new ForecastException(msg, ex, url);
string msg = string.Format("Unable to deserialize forecast for {0}", location.ToString());
log.ErrorFormat("{0}. {1}:{2} ", msg, ex.GetType().Name, ex.Message);
throw new ForecastException(msg, ex, url);
private List<DailyForecast> GenerateDailyAverages(FiveDayForecastRawData rawForecast)
List<DailyForecast> fiveDayForecast = new List<DailyForecast>();
Dictionary<DateTime, List<ForecastUnit>> dailyForecasts = new Dictionary<DateTime, List<ForecastUnit>>();
foreach (ForecastUnit unit in rawForecast.list)
DateTime day = unit.dt_txt.Date;
if (!dailyForecasts.ContainsKey(day))
dailyForecasts.Add(day, new List<ForecastUnit>());
List<ForecastUnit> forecasts = dailyForecasts[day];
dailyForecasts[day] = forecasts;
foreach (DateTime day in dailyForecasts.Keys)
bool rainPredicted = false;
int countMeasurements = 0;
DailyForecast daysForecast = new DailyForecast();
foreach (ForecastUnit unit in dailyForecasts[day])
rainPredicted = rainPredicted || CheckForRain(unit.weather[0].id);
sumTemp += unit.main.temp;
daysForecast.RainPredicted = rainPredicted;
daysForecast.AverageTemperature = Math.Round(sumTemp / countMeasurements, 2);
daysForecast.TemperatureUnits = this.TemperatureUnitLabel;
daysForecast.ForecastDate = day;
fiveDayForecast.Add(daysForecast);
private FiveDayForecastRawData SerializeFiveDayForecast(string serializedForecast)
FiveDayForecastRawData rawForecast = null;
StringReader sr = new StringReader(serializedForecast);
JsonSerializer serializer = new JsonSerializer();
rawForecast = (FiveDayForecastRawData)serializer.Deserialize(sr, typeof(FiveDayForecastRawData));
log.ErrorFormat("Unable to deserialize file content. {0}", ex.Message);
private bool CheckForRain(int conditionCode)
return conditionCode >= 500 && conditionCode < 600;
public class RestRequestHandler : IDisposable
ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private HttpClient client;
public RestRequestHandler()
client = new HttpClient();
public string ExecuteMethodAsGet(string url, string responseMediaType)
string data = string.Empty;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(responseMediaType));
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
data = response.Content.ReadAsStringAsync().Result;
catch (HttpRequestException ex)
log.DebugFormat("Unsuccessful request: {0}. {1}:{2}", url, ex.GetType().Name, ex.Message);
GC.SuppressFinalize(this);
protected virtual void Dispose(bool disposing)
public string ZipCode { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public int LocationId { get; set; }
public string ApiKey { get; set; }
return string.Format("{0}, {1} ({2})", City, State, ZipCode);
public Location(string city, string state, string country, string zipCode, int locationId, string apiKey)
public class DailyForecast
public DateTime ForecastDate { get; set; }
public bool RainPredicted { get; set; }
public double AverageTemperature { get; set; }
public string TemperatureUnits { get; set; }
string annotatedDate = string.Format("{0}{1}", ForecastDate.ToString("d").PadLeft(10), RainPredicted ? "*" : " ").TrimStart();
return string.Format("{0} {1:0.00} {2}", annotatedDate, AverageTemperature, TemperatureUnits);
public class FiveDayForecastRawData
public int cod { get; set; }
public string message { get; set; }
public int cnt { get; set; }
public List<ForecastUnit> list { get; set; }
return JsonConvert.SerializeObject(this, Formatting.None);
public class ForecastUnit
public DateTime dt_txt { get; set; }
public ForecastUnitMain main { get; set; }
public List<ForecastUnitWeather> weather { get; set; }
return JsonConvert.SerializeObject(this, Formatting.None);
public class ForecastUnitMain
public double temp { get; set; }
return JsonConvert.SerializeObject(this, Formatting.None);
public class ForecastUnitWeather
public int id { get; set; }
public string main { get; set; }
return JsonConvert.SerializeObject(this, Formatting.None);
public class ForecastException : Exception
public string RequestUrl { get; set; }
public ForecastException()
public ForecastException(string message)
public ForecastException(string message, Exception inner)
public ForecastException(string message, Exception inner, string url)
public override String Message
return base.Message + String.Format(", Url={0}", RequestUrl);