#!/usr/local/bin/python # # LICENSE: # # Program: poppy.py; generates population pyramid graphs and associated html # Copyright (C) 2003 by MetaMedia, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # MetaMedia, Inc.; 776 Circle Drive; Gainesville, Georgia 30501 # Heitzso, President: heitzso@growthmodels.com # # NOTE: the GNU General Public License can be viewed at: # http://www.gnu.org/copyleft/gpl.html # # import biggles import cgi import Numeric import math import os import stat import string import sys import time import types # colors title = "" source = '' configFilename = "" graphFilename = "population_pyramid.png" xSize = 425 ySize = 400 maxPercent = 10 maleCounts = [] malePercents = [] maleTotal = 0 femaleCounts = [] femalePercents = [] femaleTotal = 0 total = 0 aHrefs = [] aTitles = [] ageGroups = ('0-4','5-9','10-14','15-19','20-24','25-29','30-34','35-39',\ '40-44','45-49','50-54','55-59','60-64','65-69','70-74','75-79','80+') def loadConfiguration(): global title, source, maleCounts, femaleCounts, xSize, ySize, graphFilename, maxPercent # reset title = "" source = "" xSize = 400 ySize = 400 maxPercent = 10 maleCounts = [] femaleCounts = [] graphFilename = "population_pyramid.png" try: f = open(configFilename, "r") except IOError, e: print("Failed to open configuration file %s" % configFilename) sys.exit(1) ls = f.readlines() f.close() # loop through config file lines for l in ls: l = string.strip(l) if (len(l) == 0 or l[0] == '#'): continue p = string.split(l, '=') if (len(p) != 2): print("Missing single 'key = value' construct in " + configFilename) sys.exit(1) key = string.lower(p[0]) # keys forced to case insensitive lower case key = string.strip(key) if (len(key) == 0): print("Missing key to left of the '=' sign in " + configFilename) sys.exit(1) value = p[1] value = string.strip(value) # SWITCH on key to pick up values if (key == "title"): title = value elif (key == "source"): source = value elif (key == "filename"): graphFilename = value elif (key == "size"): p = string.split(value) if (len(p) != 2): print "size must have 2 values, x and y, in %s" % (configFilename) sys.exit(1) xSize = int(p[0]) ySize = int(p[1]) elif (key == "data"): p = string.split(value) if (len(p) != 2): print "data must have 2 values, male and female counts for an age bracket, in %s" % (configFilename) sys.exit(1) maleCounts.append(int(p[0])) femaleCounts.append(int(p[1])) elif (key == "maxpercent"): maxPercent = int(value) else: print "Configuration key %s in %s is not recognized" % (key, configFilename) sys.exit(1) # simple validate if (len(maleCounts) != 17 or len(femaleCounts) != 17): print("17 data lines required, first data line in file will be youngest age group)") sys.exit(1) def calculateData(): global total, maleTotal, malePercents, femaleTotal, femalePercents # reset total = 0 malePercents = [] femalePercents = [] maleTotal = 0 for i in maleCounts: maleTotal += i femaleTotal = 0 for i in femaleCounts: femaleTotal += i total = maleTotal + femaleTotal mp = 0 for i in maleCounts: p = i * 100.0 / total if (p > mp): mp = p malePercents.append(p) for i in femaleCounts: p = i * 100.0 / total if (p > mp): mp = p femalePercents.append(p) # if calculated maximum percent is greater than requested max percent for the graph, flag it if (mp > maxPercent): print "Calculated maximum percent is %.1f, request graph max percent is %i" \ % (mp, maxPercent) def writeGraph(): chart = biggles.FramedPlot() chart.title = title chart.xrange = (-maxPercent, maxPercent) chart.xlabel = "Percent of Total Population" chart.yrange = (0, 17) chart.y1.tickdir = +1 chart.y1.ticks = (.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, \ 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5) chart.y1.subticks = 0 chart.y2.ticks = (0,17) chart.y2.subticks = 0 chart.y1.ticklabels = ageGroups chart.x1.ticks = (-maxPercent, 0, maxPercent) chart.x1.subticks = maxPercent - 1 chart.x1.ticklabels = (str(maxPercent), "0", str(maxPercent)) chart.x1.tickdir = +1 chart.x2.ticks = 2 chart.x2.subticks = 0 chart.x2.tickdir = -1 # male/female labels label = biggles.Label(-maxPercent + maxPercent/2,16,'Male') chart.add(label) label = biggles.Label(maxPercent/2,16,'Female') chart.add(label) for i in range(0, 17): if (i < 3): c = '#3030FF' # blue = youth elif (i > 12): c = '#CCCCCC' # grey = retirement else: c = '#00FF00' # green = earning adult years fill = biggles.FillBetween([-malePercents[i],0], [i,i], \ [-malePercents[i],0], [i+1,i+1], \ color=c) chart.add(fill) if (i >= 3 and i <= 8): c = '#AAFFAA' # pale green = fertile female fill = biggles.FillBetween([0,femalePercents[i]], [i,i], \ [0,femalePercents[i]], [i+1,i+1], \ color=c) chart.add(fill) box = biggles.DataBox([-malePercents[i], i], [femalePercents[i], i+1], \ linecolor='black', linetype='solid', linewidth=3) chart.add(box) # x = [0, 0] y = [0, 17] curve = biggles.Curve(x, y, color="black", linetype="solid", linewidth=3) chart.add(curve) # chart.write_img(xSize, ySize, graphFilename) return def writeHtmlHead(): print "" print "" print "Population Pyramids" print "" print "" print "" print "" print "

Population Pyramids

" return def writeHtml(): global aHrefs, aTitles aHrefs.append("#" + configFilename) aTitles.append(title) print "
" print "

%s

" % (configFilename, title) print "" print "
" % (title, graphFilename, graphFilename) print "
" print "" # print "" % title print "" print "" print "
%s
Age GroupMaleFemaleTotal" print "
CountPercentCountPercentCountPercent" for i in range(16, -1, -1): print "
%s%i%.1f%i%.1f%i%.1f" % \ (ageGroups[i], \ maleCounts[i], malePercents[i], \ femaleCounts[i], femalePercents[i], \ maleCounts[i] + femaleCounts[i], malePercents[i] + femalePercents[i]) print "
Total%i%.1f%i%.1f%i%.1f" % \ (maleTotal, maleTotal * 100.0 / total, \ femaleTotal, femaleTotal * 100.0 / total, \ total, 100.0) print "" print "
" print "
" print "

%s" % (source) print "
" return def writeHtmlLinks(): print "



" print "List of the population pyramids provided on this page:" print "" return def writeHtmlFoot(): print "" print "" return if (len(sys.argv) < 2): print "Requires one or more configuration file names on the command line." sys.exit(1) writeHtmlHead() for iConfig in range(1, len(sys.argv)): configFilename = sys.argv[iConfig] loadConfiguration() calculateData() writeGraph() writeHtml() writeHtmlLinks() writeHtmlFoot() sys.exit(0)