| #!/usr/bin/env python |
| |
| import os |
| import sys |
| import glob |
| import yaml |
| import argparse |
| import datetime |
| import hashlib |
| from fcntl import lockf, LOCK_EX, LOCK_NB |
| |
| from lxml.etree import Element, SubElement, Comment, tostring, CDATA |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter |
| ) |
| parser.add_argument('-f', dest='filepattern', action='store', |
| default='users/*.yaml', |
| help='Pattern of YAML files to process') |
| parser.add_argument('-r', dest='privround', action='store_true', |
| default=False, |
| help='Round coordinates to two decimal points for privacy') |
| parser.add_argument('-o', dest='outfile', action='store', |
| default='www/developers.kml', |
| help='Where to output the resulting KML file') |
| parser.add_argument('-d', dest='dryrun', action='store_true', |
| help='Do not write anything, just do a dry run') |
| |
| args = parser.parse_args() |
| |
| el_kml = Element('kml') |
| el_kml.append(Comment('Generated on %s' % datetime.datetime.today().strftime('%F %T'))) |
| el_doc = SubElement(el_kml, 'Document') |
| |
| el_fol = SubElement(el_doc, 'Folder') |
| |
| # Lock before scanning the directory, unlock on exit |
| lockfile = os.path.join(os.path.dirname(args.outfile), |
| '.%s.lock' % os.path.basename(args.outfile)) |
| lockh = open(lockfile, 'w') |
| try: |
| lockf(lockh, LOCK_EX | LOCK_NB) |
| except IOError: |
| print('Could not create exclusive lock, assuming another process is running.') |
| sys.exit(0) |
| |
| for filename in glob.glob(args.filepattern): |
| with open(filename) as data_file: |
| try: |
| ydata = yaml.load(data_file, Loader=yaml.FullLoader) |
| except yaml.ParserError as ex: |
| print('Error parsing yaml in %s' % filename) |
| print(ex) |
| continue |
| |
| if 'name' not in ydata.keys() or 'email' not in ydata.keys(): |
| print('No name or email found in %s' % filename) |
| continue |
| |
| if 'locations' not in ydata.keys() or len(ydata['locations']) < 1: |
| print('No locations found in %s' % filename) |
| continue |
| |
| for place in ydata['locations'].keys(): |
| location = ydata['locations'][place] |
| # Find coords and make sure they are sane |
| if 'latlong' not in location.keys(): |
| print('No coordinates for location "%s" in file %s' % (place, filename)) |
| continue |
| |
| latlong = location['latlong'].split(',') |
| |
| try: |
| if len(latlong) < 2: |
| raise ValueError('Missing comma') |
| |
| # Bizarrely, KML wants longitude first |
| roundlevel = 6 |
| if args.privround: |
| roundlevel = 2 |
| |
| lat = round(float(latlong[0].strip()), roundlevel) |
| lon = round(float(latlong[1].strip()), roundlevel) |
| coords = '%.6f,%.6f' % (lon, lat) |
| |
| except ValueError: |
| print('Invalid coordinates for location "%s" in file %s' % (place, filename)) |
| continue |
| |
| el_placemark = SubElement(el_fol, 'Placemark') |
| el_name = SubElement(el_placemark, 'name') |
| el_name.text = ydata['name'] |
| |
| el_point = SubElement(el_placemark, 'Point') |
| el_coordinates = SubElement(el_point, 'coordinates') |
| el_coordinates.text = coords |
| |
| email = ydata['email'].strip().lower() |
| if hasattr(email, 'encode'): |
| email = email.encode('ascii') |
| libavhash = hashlib.md5(email).hexdigest() |
| |
| desc = ''' |
| <div class="devpop"> |
| <img class="devimg" src="https://seccdn.libravatar.org/avatar/%s?s=64&d=retro"/> |
| <p><b>Contact:</b> <a href="mailto:%s">%s</a> |
| ''' % (libavhash, ydata['email'], ydata['email']) |
| |
| if 'key' in ydata.keys(): |
| desc += '''<br/> |
| <b>Key stats:</b> <a href="http://pgp.cs.uu.nl/stats/%s.html">%s</a> |
| ''' % (ydata['key'], ydata['key']) |
| desc += '</p><p class="devclear">' |
| |
| if 'description' in location.keys(): |
| desc += location['description'].strip() |
| |
| desc += '</p></div>' |
| |
| el_description = SubElement(el_placemark, 'description') |
| el_description.text = CDATA(desc) |
| |
| kml_data = tostring(el_kml, pretty_print=True, encoding='utf-8') |
| |
| if args.dryrun: |
| print(kml_data) |
| sys.exit(0) |
| |
| with open(args.outfile, 'wb') as tfile: |
| tfile.write(kml_data) |