4. Country Catalogue

4.1. Task

The goal is to create a collection of Country objects. The collection, called a CountryCatalogue, will provide functionality to store (add/remove) the Country objects in addition to making inquiries about the data in the collection.

You will

  • Create a Country class to store details about a country

  • Create a CountryCatalogue class

    • Provide a way to add and remove Country objects

    • Search through the catalogue

    • Ask questions about the data in the catalogue

    • Filter data in the catalogue

  • Use the written classes to build a CountryCatalogue

  • Read data from a file

  • Write data to a file

4.2. Provided Files

You are provided with

  • A notebook file called asn4.ipynp containing the starting point of the assignment

    • This file is to be uploaded to Google Colab

    • The notebook contains the start of the Country and CountryCatalogue classes

    • The notebook contains unit tests for the Country and CountryCatalogue classes

    • The notebook contains already written code that will make use of the classes you are to write

    • The notebook also includes a special if statement if __name__ == "__main__": at the end

      • This is included to help with marking and unit tests

      • More details on this line are provided below

    • Alternatively, if you prefer to complete the assignment with an IDE on your own computer, you may download and use the asn4.py file

  • A data file called country_data.csv containing information about countries that will be used to populate the CountryCatalogue

Warning

Do not alter the function details in the provided .ipynb/.py files

  • Do not change the name of the functions

  • Do not remove the function description

  • Do not remove or add to the parameter list

4.3. Part 0 — Read the Assignment

Read the assignment description in its entirety before starting.

4.4. Part 1 — Uploading Files to Colab

After downloading the notebook and data files above, you will need to upload them to Colab to get started. See the respective section from assignment 1 for an example on how to do this. I recommend saving a copy of this notebook file and csv data file to your Google drive and then work with that one. You don’t have to, but you will have to re-upload the project every time you want to work on it.

4.5. Country Class

The Country class is simply going to represent an individual country. For our purposes, a Country will know its name, continent, population, and land area. In addition to the attributes, a Country will also be able to determine it’s population density (population/area). A method for determining equality (__eq__) and generating a nice, human readable string representation of the Country object (__repr__) will be written.

4.5.1. Part 2 — Country Constructor

Write the constructor (__init__) for the Country class. The constructor will take a country name, which continent it’s on, its population, and the total land area as parameters. The constructor will assign the values passed as parameters to their respective attributes — name, continent, population, and area.

4.5.2. Part 3 — Population Density

Write a method population_density that returns the population density of the Country object. The population density should be a float.

4.5.3. Part 4 — Equals and Repr

Write the __eq__ magic method for the Country class. For our purposes, two Country objects will be considered equal if all their attributes are equal.

Write the __repr__ magic method for the Country class. For our needs, we will simply follow the pattern ClassName(attribute=value, attribute=value, ...). For example, representing the country Canada as a string would be Country(name=Canada, continent=North America, population=34207000, area=9976140.00).

4.5.4. Part 5 — Testing Country Class

To help ensure correctness, run the CountryTest class and ensure all unit tests pass. If any of the tests fail, read which test failed and under which condition. The output of the tests will help guide your debugging.

To run the tests, run the cell in the notebook containing the following

# Run this cell to run all unit tests
unittest.main(argv=[''], verbosity=2, exit=False)

4.6. Country Catalogue Class

The CountryCatalogue class holds reference to a collection of Country objects. Additionally, the CountryCatalogue provides some functionality to ask questions about the collection of data.

The CountryCatalogue is effectively a list keeping track of the Country objects with additional proprietary methods. Details on the functionality is provided below.

Although the description of this class is provided with an order and each part is numbered, one should feel free to complete the methods in any order they see fit. For example, the __len__ magic method, which is described last, may be helpful when writing the other methods. This may motivate one to write it earlier.

4.6.1. Part 6 — Country Catalogue Constructor

Write the constructor (__init__) for the CountryCatalogue class. The constructor for the class is simple — initialize the object with an attribute assigned to a reference to an empty list. It is recommended to call the attribute _catalogue. The underscore is included before since this attribute is not intended to be accessed directly from outside the clas.

4.6.2. Part 7 — Private Find Method

Write a method _find that takes a reference to a Country object and returns the index of the first occurrence of an equivalent Country within the catalogue. If no matching Country exists, the method will return -1.

This method is “private”, which means that it should not be accessed/used except from inside the object. In Python, this “private” property is not enforced, but for methods or attributes that are intended to be “private”, the convention is to start the method name with an underscore (_), thus the name _find.

4.6.3. Part 8 — Contains

Write a method called contains that takes a reference to a Country object as a parameter and returns True if an equivalent Country object exists within the collection, and False otherwise.

4.6.4. Part 9 — Add

Write a method add that takes a Country object as a parameter and adds the provided Country to the collection. In other words, append the provided Country to the CountryCatalogue object’s attribute referencing a list. This method returns nothing.

4.6.5. Part 10 — Remove

Write a method remove that takes a Country object as a parameter and removes the first occurrence of an equivalent Country object from the CountryCatalogue. This method returns a reference to the removed Country object. If no equivalent Country object is found within the CountryCatalogue, then the method will raise a ValueError exception.

4.6.6. Part 11 — Largest Density

Write a method country_with_largest_population_density that returns a reference to the Country object within the CountryCatalogue that has the largest population density. If the CountryCatalogue is empty, this method will raise an IndexError exception.

4.6.7. Part 12 — Smallest Density

Write a method country_with_smallest_population_density that returns a reference to the Country object within the CountryCatalogue that has the smallest population density. If the CountryCatalogue is empty, this method will raise an IndexError exception.

4.6.8. Part 13 — Filter by Density

Write a method filter_countries_by_population_density that takes a range of population density values as parameters and returns a new CountryCatalogue object with references to Country objects that fall within the specified population density range. The range specified will be \([low, high)\); the Country objects with a population density greater-than or equal to the low and strictly less-than the high will be included in the filtered CountryCatalogue. If no Country objects fall within the specified range, this method will return an empty CountryCatalogue.

For example, calling some_catalogue.filter_countries_by_population_density(200, 250) would return a new CountryCatalogue containing references to all the Country objects within some_catalogue that have a population density \(\ge 200\) and \(< 250\).

4.6.9. Part 14 — Most Populous Continent

Write a method most_populous_continent that returns the name of the continent (as a string) that has the largest population based on information within the CountryCatalogue. If the the CountryCatalogue is empty, this method raises an IndexError exception.

This method will only consider the Country objects contained within the CountryCatalogue. In other words, although Asia has a large population, if no Country objects from Asia were included in the CountryCatalogue, then those populations are not considered in the calculation.

Further, this method should work on any arbitrary planet within our universe that may have unusual continent names (do not hard code any details about continents based on Earth).

Hint: Consider using a dictionary within this method to help with determining each continent’s population.

4.6.10. Part 15 — Equals, Repr, get item, and Length

Write the __eq__ magic method for the CountryCatalogue class. For our purposes, two CountryCatalogue objects will be considered equal if their attributes (list of Country objects) are equal.

Write the __repr__ magic method for the CountryCatalogue class. For our needs, the string should be an aggregate of each individual Country object’s string representation, each on their own line. For example

Country(name=Canada, continent=North America, population=34207000, area=9976140.00)
Country(name=China, continent=Asia, population=1339190000, area=9596960.00)
Country(name=Egypt, continent=Africa, population=93383574, area=1000000.00)
Country(name=France, continent=Europe, population=64668129, area=541656.76)

Write the __getitem__ magic method for the CountryCatalogue class. This method takes an index as a parameter and returns a reference to the Country object at ths specified index. For more details on this method, see the relevant documentation.

Write the __len__ magic method for the CountryCatalogue class. This method returns the number of Country objects stored within the CountryCatalogue.

4.6.11. Part 16 — Testing Country Catalogue Class

To help ensure correctness, run the CountryCatalogueTest class and ensure all unit tests pass. If any of the tests fail, read which test failed and under which condition. The output of the tests will help guide your debugging.

To run the tests, run the cell in the notebook containing the following

# Run this cell to run all unit tests
unittest.main(argv=[''], verbosity=2, exit=False)

4.7. Part 17 — Putting it Together

The main portion of the code that was provided is what puts everything together. This code

  • Opens a file and loads the data into a CountryCatalogue

  • Makes changes to the contents of the CountryCatalogue

  • Asks questions of the data in the CountryCatalogue

  • Filters the CountryCatalogue based on population density

  • Saves the filtered data to a file

If everything was implemented correctly, this code should run with no issue. Although no unit tests are provided for this portion of code, you can likely determine if everything worked correctly by checking the output of the program.

4.8. Part 18 — Testing

Unlike previous assignments, no assertion tests are provided. Instead, to help ensure that your program is correct, run the provided unittests. There is no guarantee that if your code passes all the tests that you will be correct, but it certainly helps provide peace of mind that things are working as they should.

There are no unittests for the main portion of the assignment discussed in the previous part.

Realistically you should have been running tests after you complete each of the above parts, but this part is here to remind you. Remember, we are lucky that we get to test our solutions for correctness ourselves; you don’t need to wait for the marker to return your assignment before you have an idea of if it works correctly.

To run the tests, run the cell in the notebook containing the following

# Run this cell to run all unit tests
unittest.main(argv=[''], verbosity=2, exit=False)

4.9. Some Hints

  • Work on one function at a time

  • Get each function working perfectly before you go on to the next one

  • Test each function as you write it

    • This is a really nice thing about programming; you can call your functions and see what result gets returned

    • Mentally test before you even write — what does this function do? What problem is it solving?

  • If you need help, ask

    • Drop by office hours

4.10. Some Marking Details

Warning

Just because your program produces the correct output, that does not necessarily mean that you will get perfect, or even that your program is correct.

Below is a list of both quantitative and qualitative things we will look for:

  • Correctness?

  • Did you follow instructions?

  • Comments?

  • Variable Names?

  • Style?

  • Did you do just weird things that make no sense?

4.11. What to Submit to Moodle

  • Make sure your NAME and STUDENT NUMBER appear in a comment at the top of the program

  • Submit your version of asn4.py to Moodle

    • Do not submit the .ipynb file

    • To get the asn4.py file from Colab, see the example image in Assignment 1

Warning

Verify that your submission to Moodle worked. If you submit incorrectly, you will get a 0.

4.12. Assignment FAQ