Exercises for Programmers – Weather Checker in C#

Update: This post is part of the Second Annual C# Advent.

Along with koans and katas, several books have helped me knock the rust off after a recent two-year stint as a non-coding team leader. One of the exercises in Brian P. Hogan’sExercises for Programmers: 57 Challenges to Developer Your Coding Skills” jumped out at me recently, so I decided to tackle it.

At its simplest, exercise 48 – Grabbing the Weather is a relatively short exercise that asks you to use the OpenWeatherMap API and display weather information.

I decided to create a .NET Core console application. I started out at my beloved command line:

Weather_new.png
With the application created, I need to make sure it’s in git. It’s not that this is a hugely important application, it’s more about practicing good habits with source code control.

After initializing git, I copied an existing .gitignore file from another .NET project and then added my files and did an initial commit.

Weather_git.png

At this point, it’s a toss up whether to use Vim, VS Code or Visual Studio, but in this case, I used Visual Studio since it’s what many people are using AND because I have the VsVim extension installed. Best of both worlds!

The requirements for the exercise:

Using the OpenWeatherMap API at http://openweathermap.org/current, create a program that prompts for a city name and returns the current temperature for the city.

Easy enough! The first thing I need to do is visit https://openweathermap.org/current and either sign up or sign in. Sign up is free and takes about a minute. Once you have an account, you’ll need an API key. An account has a default key, so you can simply grab the key and hold on to it OR create a new one. Keep in mind, it might take some time for the key to become active.

I want my console application to call a service that will hide all the details of fetching the weather. Who knows, at some point, I may want to change weather providers and using a service should insulate the client (the console application) from internal code changes.

Instead of a city name, I want my weather service to take in a zip code and return back the a structure with all the data the OpenWeatherMap API returns.

public interface IWeatherFetcher
{
CurrentWeather GetCurrentWeather(string zipCode);
}

In Main, the call will look something like this:

static void Main(string[] args)
{
Console.WriteLine(“Mike’s Weather App”);

IWeatherFetcher wf = new WeatherFetcher();
var currentWeather = wf.GetCurrentWeather(“49036”);

Console.ReadLine();
}

The service:

public CurrentWeather GetCurrentWeather(string zipCode)
{
var json = RunAsync(“your API key”, zipCode).GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<CurrentWeather>(json);
}

private HttpClient client = new HttpClient();
private async Task<string> RunAsync(string key, string zipCode)
{
client.BaseAddress = new Uri(“http://api.openweathermap.org&#8221;);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));

var result = “”;
try
{
result = await GetWeatherAsync(key, zipCode);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}

return result;
}

private async Task<string> GetWeatherAsync(string key, string zipCode)
{
var result = “”;
string url = $”/data/2.5/weather?q={zipCode}&units=imperial&appid={key}”;

HttpResponseMessage response = await client.GetAsync(url);
if(response.IsSuccessStatusCode)
{
result = await response.Content.ReadAsStringAsync();
} else {
// dump any errors to the screen
Console.WriteLine(response.ToString());
}
return result;
}

To get the CurrentWeather class, I took the original json that was returned and used a special feature in Visual Studio called “Paste JSON as Classes” found under Edit | Paste Special in Visual Studio.

CurrentWeather (the result of “Paste JSON as Classes”):

public class CurrentWeather
{
public Coord coord { get; set; }
public Weather[] weather { get; set; }
public string _base { get; set; }
public Main main { get; set; }
public int visibility { get; set; }
public Wind wind { get; set; }
public Clouds clouds { get; set; }
public int dt { get; set; }
public Sys sys { get; set; }
public int id { get; set; }
public string name { get; set; }
public int cod { get; set; }
}

public class Coord
{
public int lon { get; set; }
public float lat { get; set; }
}

public class Main
{
public float temp { get; set; }
public int pressure { get; set; }
public int humidity { get; set; }
public float temp_min { get; set; }
public float temp_max { get; set; }
}

public class Wind
{
public float speed { get; set; }
public float deg { get; set; }
}

public class Clouds
{
public int all { get; set; }
}

public class Sys
{
public int type { get; set; }
public int id { get; set; }
public float message { get; set; }
public string country { get; set; }
public int sunrise { get; set; }
public int sunset { get; set; }
}

public class Weather
{
public int id { get; set; }
public string main { get; set; }
public string description { get; set; }
public string icon { get; set; }
}

With all that in place, I can now make one more modification to my Main to display some data:

static void Main(string[] args)
{
Console.WriteLine(“Mike’s Weather App”);

IWeatherFetcher wf = new WeatherFetcher();
var currentWeather = wf.GetCurrentWeather(“49036″);

Console.WriteLine($”The temp in {currentWeather.name} is {currentWeather.main.temp}.”);

Console.ReadLine();
}

I can now display any of the information that’s returned. I have had to make a few adjustments to the generated classes because data types coming back didn’t necessarily match what was generated – for example, Wind.deg was generated as an int, but it comes back as a float. Easy fix, but it’ll take some debugging to get it all correct.

This was a fun exercise that took me about 30 minutes to complete and I still have a couple things to do like actually prompt for the city (or Zip Code). One tweak I made but didn’t really talk about it is the inclusion of the API key within the code. I’ll explain that in a follow-up post.

I hope you enjoyed this post! Oh, and check out the code on GitHub!

 

4 thoughts on “Exercises for Programmers – Weather Checker in C#

  1. Great idea to “hide all the details of fetching the weather” — except this falls short in two areas.

    First, you are returning a structure based on OpenWeatherMap’s proprietary format. If you switch out the provider, they are going to send a different JSON packet. Best to design your own return object and map the data coming from the webservice to it.

    Second, the whole point of the service is to separate the data collection from display, which you do — on success. Upon failure, you dump things to the Console, which won’t work, if you wanted to move the weather checker to a Windows application, or a website. Better to have a “IsSuccessful” and a “FailureReason” properties to the return object (another reason to design your own)

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s