git branch

Well, another side trip. After the last post, I realized I was going to be making changes to the code that might not end up in the final application. It is all a bit of a test. After a bit more thought, it occurred to me that this is exactly why Git has branches. Well, pretty much any version control system. So, though I should have done so sooner, I am going to create a branch for me to work on the menues and any required, related code changes.

I really should likely roll back all my recent changes to before I added the menues.py module. Within in the branch I could simply have started with a new population_by_age.py module and gone from there. Alas, I didn’t and I don’t really feel like going back and removing all the recent changes. Can deal with that if we ever merge the menu branch with the master branch. So now we go by the seat of our pants and learn from out mistakes.

We will use the command git branch menu-development to create a new branch. Then we will make that branch our working respository, git checkout menu-development. Along the way we will look at the current state of our git repository. So to start make sure you are in the application repository directory. /learn/py_play/ in my case. Make sure you don’t have any uncommitted changes in your master branch before attempting to create the new branch.

image of terminal window showing creation of new branch

Now, let’s commit our branch to the remote repository, and tell git to use the branch on the repository for all its current/future commits. Make sure you have changed your working directory to the new branch before issuing git push --set-upstream origin menu-development.

image of terminal window showing creation of branch on remote repository

Continue With the Menues

Since I will, if things work out, rename menues.py and make it the primary application module, I want to add a few things to menues.py. I added the following just below the module documentation and just above the definition of the variables for some of the terminal escape sequences I plan to use.

from pathlib import Path
from chart import chart
from database import population as pdb

# Test for the two most likely starting working directories.
# If found set DATA_DIR accordingly. Using pathlib to cover operating system issues.
# If not one of them print message and exit.
p = Path.cwd()
if p.name == 'py_play':
  DATA_DIR = Path('./data')
elif p.name == 'population':
  DATA_DIR = Path('../data')
else:
  print(f"\nCurrent working directory is an unexpected location, {p}!")
  print(f"Please run the application from the root directory. Quitting application")
  exit(1)

csv_nm = 'WPP2019_PopulationByAgeSex_Medium.csv'

And commit the change.

A: About

Let’s start with perhaps the easiest menu item. Afterall, it only needs to display some information. Which of course seriously understates the real effort required. I am going to start by writing way too much to the screen. Then over time look at making it more succinct. You should present whatever information you feel is needed. I am going to put this in a function, disp_about() in menues.py. Then call that function from within the code in the infinite loop whenever ‘A’ is ‘selected’ from the menu.

And, what I currently have looks like this:

image of terminal window showing initial version of about menu item output

My disp_about() function looks like:

def disp_about():
  """Display 'About' text in terminal window

     Parameters
     ---
     None

     Side Effects
     ---
     Prints message to terminal window
  """
  about = """
    This application allows you to plot the population of countries and various regions around the world
    by age group.

    Their are 3 plot styles:
      - Population for all age groups for one country/region for 1-5 years
      - Population for all age groups for one or more countries/regions for a given year
      - Population for one age group for one or more countries/regions for a number of years

    When you select the plot option, you will be given a choice of plot types. Once you select a
    plot type, you will be asked for the information required to generate that particular plot.

    Population is given for 5 year age groups. That is: 0-4, 5-9, 10-14, ..., 95-99, 100+.
    When specifying an age group, you only enter a single age within the group.

    In most sub-menus, entering 'Q' at the input prompt should immediately return you to
    the main or previous menu.
  """
  print(f"{about}")

And of course we needed to modify the code in the loop controlling the display of the menues and the collecting of information needed to plot the desired charts.

Do note, that my function to display the main menu and get the user’s input, had a default parameter: print_ttl=False. I wanted to display the application title the first time the main menu was displayed. So I added some code to look after that. You may decide you don’t need to do so at all, or would like to do so every time. Each to their own.

if __name__ == '__main__':
  do_ttl = True
  while True:
    u_choice = do_main_menu(print_ttl=do_ttl)
    do_ttl = False
    if u_choice.upper() == 'X':
      break
    elif u_choice.upper() == 'A':
      disp_about()
    else:
      print(f"user choice is {u_choice}")

Okay time to commit our changes. Probably should have done so in smaller pieces, but…

image of terminal window showing git commit for new branch

C: Plot chart

Before calling it quits for the day/post, let’s get the initial version of the Plot chart sub-menu coded. So, another function, do_chart_menu(). Go ahead give it a shot. See you in a bit.

Okay, my version of the menu looks and operates like this. The ‘you wish’ lines are my temporary substitutes for the actual sub-menues to get the user supplied information and plot the appropriate chart. That’ll be the next step and the next blog. This one is long enough for one day.

image showing chart menu following various user selections

For the code side of things, I added a new global/module variable MENU_C to hold the selections for the menu as I did for the main menu. The code for do_chart_menu() is very similar to that for do_main_menu(). Should likely write one function to display either menu. May do so down the road. But there are a few text differences, so may be more trouble than it’s worth. We shall see.

The various added pieces of my code are as follows.

MENU_C = {
  '1': 'One country/region, 1-5 years, all age groups',
  '2': '2-5 countries/regions, 1 year, all age groups',
  '3': '1-5 countries/regions, 1 - 10 years, one age group',
  'Q': 'Return to main menu'
}
def do_chart_menu():
  """Display the chart sub-menu allowing user to select the type of chart they wish to plot.

    Parameters
    ---
      None

    Side Effects
    ---
      Prints message to terminal window

    Returns
    ---
      user_choice : str
        The appropriate key from MENU_M
"""
  choice_ok = False
  add_valid = False
  while not choice_ok:
    if add_valid:
      print(f"\n\tPlease select a \033[1;34mvalid\033[0;0m chart type:")
    else:
      print(f"\n\tPlease select a chart type:")
    for key, value in MENU_C.items():
      print(f"\t\t\033[1;31m{key}\033[0;0m: {value}")
    user_choice = input("\tYour selection: ").upper()
    choice_ok = user_choice in MENU_C.keys()
    add_valid = not choice_ok
  return user_choice

And, of course we had to add an appropriate elif block to our infinite menu loop.

    elif u_choice.upper() == 'C':
      while True:
        c_choice = do_chart_menu()
        if c_choice.upper() == 'Q':
          break
        else:
          print(f"\n\tYou wish to print a plot showing: {MENU_C[c_choice]}")

Until Next Time

Enjoy messing with your code. Don’t forget to commit your changes to the branch repository. And see you next time. I will continue semi-weekly posts until the user menu refactoring is completed.