And Again, Continue With the Menues

Let’s move on to the code for the other two ‘get chart parameters from the user’ functions.

Chart Type 2: 2-5 countries/regions, 1 year, all age groups

The type 2 chart allows for 2-5 countries for a single year (and all age ranges). We’ve the got the single year covered. So we just need to sort how we are going to deal with getting multiple country/region names from the user. And, how to determine when the user has entered as many names as they are interested in if less than the allowed maximum.

Get 2-5 Country/Region Names

I temporarily thought about modifying get_cr_name(). But, it does one thing and does it cleanly and clearly. So I am going to write another function get_mult_cr_names(max_nm=2) that calls on get_cr_name() to do the real work. I also thought about letting the user enter multiple comma-separated names on one line. But, that could get messy if any region names have commas in them. So, I will use a loop controlled by the max_nm variable. Users will be able to stop the loop by entering a name of “D”.

This way, we also deal with the user entering partial names, names that are not in the database or text that finds too many country and/or region names in the database. That was already being handled by the get_cr_name() function.

Once get_mult_cr_names(max_nm=2) is sorted I will go to work to sort out do_chart_m1a(), and update the menu loop(s) accordingly. Committing and testing as I go along.

Give it a shot and see you back here in a while.

Function: get_mult_cr_names(max_nm=2)

So basically we are going to loop in the range(max_nm), having the user input country names until they enter a name of “D” or have entered max-nm names. Given a name of “X” returned by get_cr_name() (which it does when a user enters a country name of “D”) we will terminate the loop and return the list of the valid names entered so far, or an empty list if no valid names returned so far.

def get_mult_cr_names(max_nm=2):
  cr_nms = []
  print(f"\n\tPlease enter 2-{max_nm} country/region names when prompted.")
  print(f"\tEnter 'D' as the name to indicate you are done if entering less than {max_nm} names.")
  for _ in range(max_nm):
    u_resp = get_cr_name()
    if u_resp.upper() == 'X':
      break
    else:
      cr_nms.append(u_resp)
  return cr_nms

The above function and get_years() make coding do_chart_m1a() fairly simple.

def do_chart_m1a():
  cr_nms = []
  p_yr = ''
  
  print(f"\n\t{CCTTL}{MENU_C['2']}{TRESET}")
  cr_nms = get_mult_cr_names(max_nm=5)
  if len(cr_nms) >= 1:
    p_yr = get_years()

  return [cr_nms, p_yr, ['all']]

Get Age Group

Now on to the final function in this group of functions, get_age_range(). I did think about just adding the code to obtain the desired age range right inside do_chart_mm1(). But I don’t want the user to have to sort out the age ranges when making their selection. So, I am just going to ask them for a single age. Then let the function determine the appropriate age range and return the text identifying that age range in the database CSV. So, if the user enters 62 the function would return the string '60-64'. Then, within do_chart_mm1(), I will use the three ui functions to get all the data we need: list of country names, initial year & range and the age group.

Function: get_age_range()

Getting an age, 0 - 100, from the user should be straightforward enough. Determining the range is arithmetically fairly simple if you understand the modulus operator, %. Don’t forget that age 100 is different from the other ages in the allowed range. Have a think and a try. Then if necessary have a look at my code. I think I got it right.

So, testing my code for just get_age_range() looks like the following. Of course, had to edit do_chart_mm1() to use the age range ui function.

image of terminal showing test code output

And, the code:

def get_age_range():
  age_ok = False
  bad_entry = False
  u_age = ''
  a_rng = ''
  t_age = -1
  while not age_ok:
    if not bad_entry:
      u_age = input(f"\n\tPlease enter the age for which you wish population data plotted (0 - 100): ")
    else:
      u_age = input(f"\n\tPlease enter a {CHLITE}valid{TRESET} age for which you wish data plotted ({CHLITE}0 - 100{TRESET}): ")
    if u_age.upper() == 'X':
      exit(1)
    else:
      try:
        t_age = int(u_age)
        if t_age >= 0 and t_age <= 100:
          bad_entry = False
          age_ok = True
      except ValueError:
        bad_entry = True
  
  if u_age == '100':
    a_rng = '100+'
  else:  
    r_strt = t_age - (t_age % 5)
    r_end = r_strt + 4
    a_rng = str(r_strt) + "-" + str(r_end)

  return [a_rng]

Ok, looks to work as intended. Let’s commit our changes before going on to finish do_chart_mm1() to get the other data for the plot.

Now add the finishing touches to do_chart_mm1(). Run a simple test. If done commit our changes and call it a day.

image of terminal window showing result of simple test of do_chart_mm1 function
def do_chart_mm1():
  cr_nms = []
  p_yr = ['', 1]
  a_rng = ['one']

  print(f"\n\t{CCTTL}{MENU_C['3']}{TRESET}")
  
  cr_nms = get_mult_cr_names(max_nm=5)
  if len(cr_nms) >= 1:
    p_yr = get_years(max_rng=10)
    if p_yr[0].upper() != 'X':
      a_rng = get_age_range()

  return [cr_nms, p_yr, a_rng]

Until next time…