#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Modules
import sys
import os
import sqlite3
import csv
import re
import syslog
# Configurable stuff
# Other globals
progname = sys.argv[0].rpartition("/")[2]
del sys.argv[0]
syslog_opened_flag = False
DEVNULL = open(os.devnull, 'wb')
db_file = None
#db_file = '%s.sqlite' % (progname)
use_syslog_flag = False
waypoint_name_regex = '^camp(?:[0-9]{2,}|XXX)[*+]?$'
float_regex = '^[-+]?[0-9]+(?:\.[0-9]+)?$'
campsite_longitude_regex = float_regex
campsite_latitude_regex = float_regex
campsite_country_regex = '^(?:AT|CZ|CH|DE|SK|DK|HU)$'
letters = 'a-zA-ZáéřýőíüÉóéÖúžěöčľÜßČšŽŠäåňÅůŘřøæØťÍďÚ´ô'
campsite_name_regex = '^(?:[-%s 0-9/.()&+:"\']{3,}|[-?])$' % (letters)
campsite_city_regex = '^(?:[-%s 0-9/.()]{3,}|[-?])$' % (letters)
campsite_street_regex = '^(?:[-%s 0-9/.&]{5,}|[1-9][/0-9]*|[-?])$' % (letters)
tel_regex = '\+[1-9][0-9]{1,2} [1-9][0-9 ]*[0-9]'
campsite_tel_regex = '^(?:%s(?: or %s)*|\?)$' % (tel_regex, tel_regex)
campsite_tents_ok_regex = '^(?:yes|no|\?)$'
campsite_season_regex = '^((|mid)\d+|\?)-((|mid)\d+|\?)(|,((|mid)\d+|\?)-((|mid)\d+|\?))$'
campsite_season_regex = '^(?:(?:|mid)\d+|\?)-(?:(?:|mid)\d+|\?)$'
campsite_url_regex = '^(http://|https://|\?$)'
# Classes
# Functions
def main():
global verboselevel, DEVNULL, cursor, db_file
# Defaults for options
verboselevel = 2
output_fmt = None
# Process options, old school
while True:
if len(sys.argv) == 0:
break
# Application-specific options
elif sys.argv[0].startswith('--format='):
output_fmt = sys.argv[0][len('--format='):]
if output_fmt not in ['csv', 'gpx', 'gpx2', 'html']:
usage()
# '-' is an argument not an option
elif sys.argv[0] == '-':
break
# Standard options
elif sys.argv[0].startswith('--debug='):
verboselevel = sys.argv[0][len('--debug='):]
if not re.search('^\d+$', verboselevel):
usage()
verboselevel = int(verboselevel)
elif sys.argv[0] == '--verbose':
verboselevel = 3
elif sys.argv[0] == '--help':
usage(0)
elif sys.argv[0] == '-v':
verboselevel = 3
elif sys.argv[0] == '-d':
if len(sys.argv) < 3:
usage()
verboselevel = sys.argv[1]
del sys.argv[0]
if not re.search('^\d+$', verboselevel):
usage()
verboselevel = int(verboselevel)
elif sys.argv[0] == '-h':
usage(0)
elif sys.argv[0] == '--':
del sys.argv[0]
break
elif sys.argv[0].startswith('-'):
usage()
else:
break
del sys.argv[0]
# Process arguments
input_files = sys.argv if len(sys.argv) > 0 else ['-']
debug(10, 'main: input_files=%s' % (input_files))
# Sanity checks and derivations
if output_fmt is None:
usage()
if db_file is None:
database_exists_flag = False
elif os.path.isfile(db_file):
database_exists_flag = True
else:
database_exists_flag = False
# Guts
# Connect to database
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(db_file if db_file is not None else ':memory:')
# I want to control transactions myself
conn.isolation_level = None
# Create REGEXP function. (REGEXP operator maps to REGEXP function automatically)
def regexp(expr, item):
#debug(10, 'regexp: expr=%s, item=%s' % (expr, item))
reg = re.compile(expr)
return reg.search(item) is not None
conn.create_function("REGEXP", 2, regexp)
cursor = conn.cursor()
# Intialise database if necessary
if not database_exists_flag:
sqlite_exec_wrapper('CREATE TABLE campsites (input_file char, input_line int, waypoint_name char, campsite_latitude char, campsite_longitude char, campsite_name char, campsite_country char, campsite_city char, campsite_street char, campsite_tel char, campsite_tents_ok char, campsite_season char, campsite_url, PRIMARY KEY(input_file, input_line));')
# Normally I'd create a view to get the deduplicated list of campsites, but
# views don't have rowids and I want to use rowid to build the waypoint name.
# So we create a second copy of the table and we'll insert the deduplicated
# rows into into it with a single INSERT statement.
sqlite_exec_wrapper('CREATE TABLE deduplicated_campsites (campsite_latitude char, campsite_longitude char, campsite_name char, campsite_country char, campsite_city char, campsite_street char, campsite_tel char, campsite_tents_ok char, campsite_season char,campsite_url char);')
# Slurp input with filenames and line numbers (useful for error messages)
sqlite_exec_wrapper('BEGIN TRANSACTION;')
for input_file in input_files:
if input_file == '-':
fh = sys.stdin
else:
fh = open(input_file, 'r')
input_line = 0
for row in csv.reader(fh):
input_line += 1
(waypt_name, campsite_latitude, campsite_longitude, campsite_name, campsite_country, campsite_city, campsite_street, campsite_tel, campsite_tents_ok, campsite_season, campsite_url) = row[5:-1]
# Ignore all non-camp rows
if not 'camp' in waypt_name:
continue
# Ignore knowingly duplicated camps.
if 'see above' in campsite_name:
continue
debug(10, 'main: waypt_name=%s, campsite_latitude=%s, campsite_longitude=%s, campsite_name=%s, campsite_country=%s, campsite_city=%s, campsite_street=%s, campsite_tel=%s, campsite_tents_ok=%s, campsite_season=%s, campsite_url=%s' % (waypt_name, campsite_latitude, campsite_longitude, campsite_name, campsite_country, campsite_city, campsite_street, campsite_tel, campsite_tents_ok, campsite_season, campsite_url))
sqlite_exec_wrapper('INSERT INTO campsites VALUES (\'%s\', %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);' % (input_file, input_line, sql_stringify(waypt_name), sql_stringify(campsite_latitude), sql_stringify(campsite_longitude), sql_stringify(campsite_name), sql_stringify(campsite_country), sql_stringify(campsite_city), sql_stringify(campsite_street), sql_stringify(campsite_tel), sql_stringify(campsite_tents_ok), sql_stringify(campsite_season), sql_stringify(campsite_url)))
if input_file == '-':
fh.close()
sqlite_exec_wrapper('END TRANSACTION;')
# Report badly formatted values.
check_param_sets = [
{ 'col':'waypoint_name', 'regex':waypoint_name_regex },
{ 'col':'campsite_latitude', 'regex':campsite_latitude_regex },
{ 'col':'campsite_longitude', 'regex':campsite_longitude_regex },
{ 'col':'campsite_name', 'regex':campsite_name_regex },
{ 'col':'campsite_country', 'regex':campsite_country_regex },
{ 'col':'campsite_city', 'regex':campsite_city_regex },
{ 'col':'campsite_street', 'regex':campsite_street_regex },
{ 'col':'campsite_tel', 'regex':campsite_tel_regex },
{ 'col':'campsite_tents_ok', 'regex':campsite_tents_ok_regex },
{ 'col':'campsite_season', 'regex':campsite_season_regex },
{ 'col':'campsite_url', 'regex':campsite_url_regex },
]
for check_param_set in check_param_sets:
sql_statement = 'SELECT input_file, input_line as error FROM campsites WHERE %s IS NULL ORDER BY input_file, input_line;' % (check_param_set['col'])
# Check first for NULL (else REGEXP bauks)
for tuple in sqlite_exec_wrapper(sql_statement):
(input_file, input_line) = tuple
print("%-20s %s" % ('%s:%s:' % (input_file, input_line), 'empty %s' % (check_param_set['col'])))
sql_statement = 'SELECT input_file, input_line, %s as whatever FROM campsites WHERE %s IS NOT NULL AND NOT %s REGEXP %s ORDER BY input_file, input_line;' % (check_param_set['col'], check_param_set['col'],check_param_set['col'], sql_stringify(check_param_set['regex']))
# Check second value is formatted correctly.
for tuple in sqlite_exec_wrapper(sql_statement):
(input_file, input_line, whatever) = tuple
print('%-20s %s' % ('%s:%d:' % (input_file, input_line), '%s: bad %s' % (whatever, check_param_set['col'])))
# Report mismatching values in lines occurring multiple times.
columns = [
'campsite_name',
'campsite_country',
'campsite_city',
'campsite_street',
'campsite_tel',
'campsite_tents_ok',
'campsite_season',
'campsite_url',
]
for column in columns:
# Cross the table with itself and look for unequal fields. Use
# 'c1.whatever > c2.whatever' to ignore one of the two occurences
# of a mismatch this join would otherwise produce.
sql_statement = 'SELECT c1.input_file || \':\' || c1.input_line || \'/\' || c2.input_file || \':\' || c2.input_line AS location, \'mismatching %s\' AS error FROM campsites AS c1, campsites AS c2 WHERE c1.campsite_latitude == c2.campsite_latitude AND c1.campsite_longitude == c2.campsite_longitude AND c1.%s != c2.%s AND c1.%s > c2.%s;' % (column, column, column, column, column)
for tuple in sqlite_exec_wrapper(sql_statement):
(location, error) = tuple
print("%-40s %s" % (location, error))
# Deduplicate in a way that gives a rowid.
sqlite_exec_wrapper('INSERT INTO deduplicated_campsites SELECT DISTINCT campsite_latitude, campsite_longitude, campsite_name, campsite_country, campsite_city, campsite_street, campsite_tel, campsite_tents_ok, campsite_season, campsite_url FROM campsites ORDER BY campsite_latitude;')
# Whatever format we output in, we use use the same ordering and
# and selection in order to ensure that the rowid values correspond
# across the differing output files.
if output_fmt == 'gpx':
def prologue():
print('')
print('')
sql_statement = '''
SELECT 'camp' || printf('%04d',rowid) || 'Triangle, Green' as gpx_line
FROM deduplicated_campsites;
'''
def process_row(tuple):
(gpx_line,) = tuple
print(gpx_line)
def epilogue():
print('')
elif output_fmt == 'gpx2':
def prologue():
print('')
print('')
sql_statement = '''
SELECT campsite_latitude, campsite_longitude, waypoint_name
FROM campsites
ORDER BY waypoint_name;
'''
def process_row(tuple):
(campsite_latitude, campsite_longitude, waypoint_name) = tuple
print(' ' % (campsite_latitude, campsite_longitude))
print(' %s' % (waypoint_name))
print(' Triangle, Green')
print(' ')
def epilogue():
print('')
elif output_fmt == 'csv':
def prologue():
global spamwriter
spamwriter = csv.writer(sys.stdout, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
spamwriter.writerow(["marker","lat/lon","camp name & homepage","ctry","town","street","telephone","tents ok?","season"])
sql_statement = '''
SELECT rowid, campsite_latitude, campsite_longitude, campsite_name, campsite_url, campsite_country, campsite_city, campsite_street, campsite_tel, campsite_tents_ok, campsite_season
FROM deduplicated_campsites;
'''
def process_row(tuple):
global spamwriter
(rowid, campsite_latitude, campsite_longitude, campsite_name, campsite_url, campsite_country, campsite_city, campsite_street, campsite_tel, campsite_tents_ok, campsite_season) = tuple
spamwriter.writerow([
'camp%04d' % (rowid),
'%s,%s
(GM, OSM, BM, geo)' % (campsite_latitude, campsite_longitude, campsite_latitude, campsite_longitude, campsite_latitude, campsite_longitude, campsite_latitude, campsite_longitude, campsite_latitude, campsite_longitude),
'%s' % (campsite_url, campsite_name) if campsite_url.startswith('http') else campsite_name,
campsite_country,
campsite_city,
campsite_street,
' or '.join(['%s' % (tel, tel) for tel in campsite_tel.split(' or ') if tel not in ['?','-']]),
campsite_tents_ok,
campsite_season])
def epilogue():
pass
elif output_fmt == 'html':
def prologue():
print('')
print('
')
print('')
print('camps')
print('')
print('')
print('')
print(' ')
print(' waypt | name | addr | tel |
')
sql_statement = '''
SELECT waypoint_name, campsite_name, campsite_city, campsite_street, campsite_url, campsite_latitude, campsite_longitude, campsite_tel
FROM campsites
ORDER BY waypoint_name;
'''
def process_row(tuple):
(waypoint_name, campsite_name, campsite_city, campsite_street, campsite_url, campsite_latitude, campsite_longitude, campsite_tel) = tuple
print(' %s | %s | %s | %s |
' % (
waypoint_name,
'%s' % (campsite_url, campsite_name) if campsite_url.startswith('http') else campsite_name,
campsite_latitude, campsite_longitude, ', '.join([address_component for address_component in [campsite_street, campsite_city] if address_component not in ['?','-']]),
' or '.join(['%s' % (tel, tel) for tel in campsite_tel.split(' or ') if tel not in ['?','-']])
))
def epilogue():
print('
')
print('')
print('')
# Generate report
prologue()
for tuple in sqlite_exec_wrapper(sql_statement):
process_row(tuple)
epilogue()
# SQL functions
def sqlite_exec_wrapper(statement):
global cursor
return cursor.execute(statement)
def sql_stringify(s):
return 'NULL' if s == '' else '\'%s\''%(s.replace('\'', '\'\''))
# Messaging functions
def usage(rc=1):
global progname
fh = sys.stdout if rc == 0 else sys.stderr
fh.write('Usage: %s [ ] --format= [ ... ]\n' % progname)
fh.write('\n')
fh.write('Formats: gpx: suitable only for directory of european campsites\n')
fh.write(' gpx2: suitable only for single-holiday GPX files\n')
fh.write(' html: suitable only for single-holiday HTML files\n')
fh.write(' csv: intermediate filetype used by Makefiles\n')
sys.exit(rc)
def debug(level, text):
global verboselevel
if verboselevel < level:
return
msg_writer("DEBUG[%s]: %s" % (level, text), syslog.LOG_DEBUG)
def info(text):
global verboselevel
if verboselevel < 3:
return
msg_writer("INFO: %s" % (text), syslog.LOG_INFO)
def warning(text):
global verboselevel
if verboselevel < 2:
return
msg_writer("WARNING: %s" % (text), syslog.LOG_WARNING)
def error(text):
global errors_bubble_up_flag
msg_writer("ERROR: %s" % (text), syslog.LOG_ERR)
if not errors_bubble_up_flag:
sys.exit(1)
def internal(text):
msg_writer("INTERNAL ERROR: %s" % (text), syslog.LOG_CRIT)
sys.exit(2)
def msg_writer(text, syslog_level):
global use_syslog_flag
msg_writer_stderr(text)
if use_syslog_flag and not sys.stderr.isatty():
msg_writer_syslog(text, syslog_level)
def msg_writer_stderr(text):
global progname
sys.stderr.write("%s: %s\n" % (progname, text))
def msg_writer_syslog(text, syslog_level):
global syslog_opened_flag, progname
if not syslog_opened_flag:
if sys.version_info < (2, 7):
syslog.openlog(progname, syslog.LOG_PID)
else:
syslog.openlog(progname, logoption=syslog.LOG_PID)
syslog_opened_flag = True
syslog.syslog((syslog.LOG_LOCAL0|syslog_level), text)
# Entry point
if __name__=="__main__":
main()
## See http://stackoverflow.com/questions/10299682/how-to-unpack-tuple-of-length-n-to-mn-variables.
## This is needed because the spreadsheet for the current year typically has extra 'Alexis' and 'Suzie'
## columns where we write our draft plan for where to camp.
#def just(n, seq):
# it = iter(seq)
# for _ in range(n - 1):
# yield next(it, None)
# yield tuple(it)
#
## Initialise output dicts.
#o_csv_camps = {}
#o_gpx_camps = {}
#o_html_camps = {}
#
#for i_row in csv.reader(sys.stdin):
# #sys.stderr.write("i_row=%s\n" % (i_row))
#
# # Search for leading and trailing spaces (we want to improve the spreadsheets)
# for field in range(0,16):
# if i_row[field].startswith(' ') or i_row[field].endswith(' '):
# sys.stderr.write("%s: leading or trailing spaces\n" % i_row)
#
# # Extract first 17 fields. (python3 has nicer ways to do this, but not python2.)
# (i_stage_start, i_stage_end, i_stage_km, i_total_km, i_page, i_camp_waypt, i_camp_n, i_camp_e, i_camp_name, i_camp_country, i_camp_town, i_camp_street, i_camp_phones, i_camp_tent_flag, i_camp_season, i_camp_url, i_camp_notes) = just(17, i_row)
#
# # Ignore all non-camp rows
# if not i_camp_waypt.startswith("camp"):
# continue
#
# # Ignore knowingly duplicated camps.
# if i_camp_name.startswith("see above"):
# continue
#
# # Copy over some fields unmodified.
# (o_camp_n, o_camp_e, o_camp_waypt, o_camp_name, o_camp_country, o_camp_town, o_camp_street, o_camp_tent_flag, o_camp_season, o_camp_url, o_camp_notes) = (i_camp_n, i_camp_e, i_camp_waypt, i_camp_name, i_camp_country, i_camp_town, i_camp_street, i_camp_tent_flag, i_camp_season, i_camp_url, i_camp_notes)
#
# # Validate fields
# if not re.search('^\d+\.\d+$', o_camp_n):
# sys.stderr.write("%s: invalid N coordinate: %s\n" % (i_row, o_camp_n))
# if not re.search('^\d+\.\d+$', o_camp_e):
# sys.stderr.write("%s: invalid E coordinate: %s\n" % (i_row, o_camp_e))
# if not re.search('^(AT|CH|CZ|DE|DK|HU|SK)$', o_camp_country):
# sys.stderr.write("%s: invalid country: %s\n" % (i_row, o_camp_country))
# #sys.stderr.write("o_camp_tent_flag=%s\n" % (o_camp_tent_flag))
# if not re.search('^(yes|no|\?)$', o_camp_tent_flag):
# sys.stderr.write("%s: invalid camping poss flag: [%s]\n" % (i_row, o_camp_tent_flag))
# if not re.search('^(http://|https://|\?$)', o_camp_url):
# sys.stderr.write("%s: invalid URL: [%s]\n" % (i_row, o_camp_url))
# # season is X-X or X-X,X-X where X is , mid or ?
# if not re.search('^((|mid)\d+|\?)-((|mid)\d+|\?)(|,((|mid)\d+|\?)-((|mid)\d+|\?))$', o_camp_season):
# sys.stderr.write("%s: invalid season: [%s]\n" % (i_row, o_camp_season))
# if not re.search('^(yes|no|\?)$', o_camp_tent_flag):
# sys.stderr.write("%s: invalid tent poss flag: [%s]\n" % (i_row, o_camp_tent_flag))
#
# # Build the camp name.
#
# # Build the GPS coordinates.
# o_camp_n_and_e = i_camp_n + "," + i_camp_e + "
(GM, OSM, BM)"
#
# # Rewrite telephone number
# o_camp_phones = []
# for num in i_camp_phones.split(' or '):
# num = re.sub('[-/]',' ',num)
# num = re.sub(',',' or ',num)
# num = re.sub(' +',' ',num)
# num = re.sub('\(00420\)','+42 ',num)
# num = re.sub('\(0\)','',num)
# num = re.sub('\+ +','+',num)
# num = re.sub('^ +','',num)
# num = re.sub(' +$','',num)
# if num == '':
# pass
# elif num == '?':
# pass
# # dutch telnos.
# elif num == '+31 6 36 16 49 72' and i_camp_country == 'CZ':
# pass
# elif num == '+31 6 2638 0484' and i_camp_country == 'DE':
# pass
# elif num.startswith('+49 '):
# pass
# elif num.startswith('+420 '):
# pass
# elif num.startswith('+43 '):
# pass
# elif num.startswith('+41 '):
# pass
# elif num.startswith('+36 '):
# pass
# elif num.startswith('+421 '):
# pass
# elif num.startswith('+45 '):
# pass
# elif num.startswith('+'):
# sys.stderr.write("bad number: %s\n" % (num))
# elif i_camp_country == 'DE' and num.startswith('0'):
# num = '+49 ' + num[1:]
# elif i_camp_country == 'CZ' and num.startswith('0'):
# num = '+420 ' + num[1:]
# elif i_camp_country == 'AT' and num.startswith('0'):
# num = '+43 ' + num[1:]
# elif i_camp_country == 'CH' and num.startswith('0'):
# num = '+41 ' + num[1:]
# elif i_camp_country == 'HU' and num.startswith('0'):
# num = '+36 ' + num[1:]
# elif i_camp_country == 'SK' and num.startswith('0'):
# num = '+421 ' + num[1:]
# elif i_camp_country == 'DK' and int(num[0]) in (range(1,10)):
# num = '+45 ' + num[1:]
# else:
# sys.stderr.write("bad number/country: %s\n" % (num))
# o_camp_phones.append(str(num))
#
# if sys.argv[1] == 'csv':
# # Construct new CSV record
# o_csv_key = "KEY" + o_camp_n + "," + o_camp_e
# if i_camp_url.startswith("http"):
# o_camp_name_and_url_in_html = "%s" % (o_camp_url, o_camp_name)
# else:
# o_camp_name_and_url_in_html = o_camp_name
# o_csv_val = [ o_camp_n_and_e, o_camp_name_and_url_in_html, o_camp_country, o_camp_town, o_camp_street, ' or '.join(o_camp_phones), o_camp_tent_flag, o_camp_season ]
#
# # Do we have a record with this N/E already?
# try:
# o_csv_camps[o_csv_key]
# # If not then insert and skip duplicate/near-duplicate checks
# except KeyError:
# o_csv_camps[o_csv_key] = o_csv_val
# continue
# # If we have the record already (i.e. we got here) and the new record is a perfect duplicate of the existing record then continue
# if o_csv_camps[o_csv_key] == o_csv_val:
# pass
# # Else error
# else:
# sys.stderr.write("imperfect duplicates: \n %s\n %s\n" % (o_csv_camps[o_csv_key], o_csv_val))
# pass
#
# elif sys.argv[1] == 'gpx':
# # Construct new GPX waypoint record
# o_gpx_val = [ o_camp_n, o_camp_e ]
# o_gpx_key = "KEY" + o_camp_n + "," + o_camp_e
# #sys.stderr.write("o_gpx_key=%s, o_gpx_val=%s\n" % (o_gpx_key, o_gpx_val))
#
# # Do we have a record with this N/E already?
# try:
# o_gpx_camps[o_gpx_key]
# # If not then insert and skip duplicate/near-duplicate checks
# except KeyError:
# o_gpx_camps[o_gpx_key] = o_gpx_val
# continue
# # If we have the record already (i.e. we got here) and the new record is a perfect duplicate of the existing record then continue
# if o_gpx_camps[o_gpx_key] == o_gpx_val:
# pass
# # Else error
# else:
# sys.stderr.write("imperfect duplicate: %s and %s\n" % (o_gpx_camps[o_gpx_key], o_gpx_val))
# pass
#
# elif sys.argv[1] == 'gpx2':
# # In the Sieben Fluesse holiday, there are no 'see above' camp lines *that we are staying at*.
# # For this reason there is no need for 'see above' rows to even detail the waypoint name, lat
# # and long. Next holiday, I'll need to fix this here with some better logic.
# o_gpx_key = o_camp_waypt
# o_gpx_camps[o_gpx_key] = ' \n %s\n Triangle, Green\n ' % (o_camp_n, o_camp_e, o_camp_waypt)
#
# elif sys.argv[1] == 'html':
# o_html_key = o_camp_waypt
# if len(o_camp_phones) == 0 or o_camp_phones[0] == '?':
# o_camp_phones_html_str = ''
# else:
# o_camp_phones_html_str = ' or '.join(['%s' % (x, x) for x in o_camp_phones])
# if o_camp_street == '-':
# o_camp_addr = o_camp_town
# else:
# o_camp_addr = '%s, %s' % (o_camp_street, o_camp_town)
# o_camp_uri = 'geo:%s,%s' % (o_camp_n,o_camp_e)
# if i_camp_url.startswith("http"):
# o_camp_name_and_url_in_html = "%s" % (o_camp_url, o_camp_name)
# else:
# o_camp_name_and_url_in_html = o_camp_name
# o_html_camps[o_html_key] = '%s | %s | %s | %s |
' % (o_camp_waypt, o_camp_name_and_url_in_html, o_camp_uri, o_camp_addr, o_camp_phones_html_str)
#
#if sys.argv[1] == 'csv':
# # Dump records.
# spamwriter = csv.writer(sys.stdout, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
# spamwriter.writerow(["lat/lon","camp name & homepage","ctry","town","street","telephone","tents ok?","season"])
# for o_csv_key in o_csv_camps:
# spamwriter.writerow(o_csv_camps[o_csv_key])
#
#elif sys.argv[1] == 'gpx':
# print ""
# print ""
# i = 0
# # Note 'for' on a dictionary loops over *keys* not values.
# for o_gpx_key in o_gpx_camps:
# i += 1
# #sys.stderr.write("o_gpx_key=%s, o_gpx_camps[o_gpx_key]=%s\n" % (o_gpx_key, o_gpx_camps[o_gpx_key]))
# print " " % tuple(o_gpx_camps[o_gpx_key])
# print " camp%04d" % i
# print " Triangle, Green"
# print " "
# print ""
#
#elif sys.argv[1] == 'gpx2':
# print ""
# print ""
# for o_gpx_key in sorted(o_gpx_camps):
# print '%s' % (o_gpx_camps[o_gpx_key])
# print ""
#
#elif sys.argv[1] == 'html':
# print ''
# print ''
# print ''
# print 'camps'
# print ''
# print ''
# print ''
# print ' '
# print ' waypt | name | addr | tel |
'
# for o_html_key in sorted(o_html_camps):
# print ' %s' % (o_html_camps[o_html_key])
# print '
'
# print ''
# print ''