scripts/export_kml.py
author Mario Ferraro <fadinlight@gmail.com>
Sun, 15 Nov 2009 22:12:20 +0100
changeset 3093 d1be59b6b627
parent 2967 676c0ca67e21
permissions -rwxr-xr-x
GMaps related JS changed to use new google namespace. Google is going to change permanently in the future the way to load its services, so better stay safe. Also this commit shows uses of the new melange.js module. Fixes Issue 634.

#!/usr/bin/python2.5
#
# Copyright 2009 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""The script which generates KML file for Google Summer of Code 2009 program.
"""

__authors__ = [
  '"Daniel Hans" <daniel.m.hans@gmail.com>',
]


import sys
import codecs
import interactive


from xml.dom.minidom import Document


def _getMentoredProjects(mentor):
  """Returns a list of projects which are mentored by a given mentor.
  """

  from soc.logic.models.student_project import logic as project_logic

  filter = {
      'mentor': mentor
      }

  return project_logic.getForFields(filter=filter)


def _getAcceptedOrgs():
  """Returns a list of organizations which got accepted.
  """

  from soc.logic.models.organization import logic as org_logic

  filter = {
      'status': 'active'
      }

  entities = org_logic.getForFields(filter=filter)

  filter = {
      'status': 'new'
      }

  entities += org_logic.getForFields(filter=filter)

  return entities


def _getStudentProject(entity):
  """Returns a project for a given student.
  """

  from soc.logic.models.student_project import logic as project_logic

  filter = {
      'student': entity,
      'status': 'accepted',
      }

  return project_logic.getForFields(filter=filter, unique=True)


def _getAllUsers():
  """Returns a list of all valid users.
  """

  from soc.models.user import User

  gen = lambda: User.all().filter('status =', 'valid')
  return interactive.deepFetch(gen)


def _getAllOrgAdmins():
  """Returns a generator of all active mentors.
  """

  from soc.models.org_admin import OrgAdmin

  gen = lambda: OrgAdmin.all().filter('status = ', 'active')
  return interactive.deepFetch(gen)


def _getAllMentors():
  """Returns a generator of all active mentors.
  """

  from soc.models.mentor import Mentor

  gen = lambda: Mentor.all().filter('status = ', 'active')
  return interactive.deepFetch(gen)


def _getAllStudents():
  """Returns a generator of all active students.
  """

  from soc.models.student import Student

  gen = lambda: Student.all().filter('status = ', 'active')
  return interactive.deepFetch(gen)


def _getPersonStyle(doc, type):
  """Returns <Style> element for a particular person.
  """

  if type == 'org_admin':
    x_text, y_text = '0', '0'
  elif type == 'mentor':
    x_text, y_text = '128', '96'
  elif type == 'student':
    x_text, y_text = '64', '160'

  style = doc.createElement('Style')

  icon_style = doc.createElement('IconStyle')
  style.appendChild(icon_style)

  icon = doc.createElement('Icon')
  icon_style.appendChild(icon)

  href = doc.createElement('href')
  icon.appendChild(href)

  text = doc.createTextNode('root://icons/palette-5.png')
  href.appendChild(text)

  x = doc.createElement('x')
  icon.appendChild(x)

  text = doc.createTextNode(x_text)
  x.appendChild(text)

  y = doc.createElement('y')
  icon.appendChild(y)

  text = doc.createTextNode(y_text)
  y.appendChild(text)

  w = doc.createElement('w')
  icon.appendChild(w)

  text = doc.createTextNode('32')
  w.appendChild(text)

  h = doc.createElement('h')
  icon.appendChild(h)

  text = doc.createTextNode('32')
  h.appendChild(text)

  return style


def _getLineStringStyle(doc):
   """Returns <Style> element for a line string placemark.
   """

   style = doc.createElement('Style')

   line_style = doc.createElement('LineStyle')
   style.appendChild(line_style)

   color = doc.createElement('color')
   line_style.appendChild(color)

   text = doc.createTextNode('ff00ff00')
   color.appendChild(text)

   width = doc.createElement('width')
   line_style.appendChild(width)

   text = doc.createTextNode('1')
   width.appendChild(text)

   return style


def _getDescriptionForStudent(doc, student, project):
  """Returns <description> element for a given student.
  """

  description = doc.createElement('description')

  text = doc.createTextNode('Working on...')
  description.appendChild(text)

  description.appendChild(doc.createElement('br'))

  i = doc.createElement('i')
  description.appendChild(i)

  title = doc.createTextNode(project.title)
  i.appendChild(title)
  description.appendChild(doc.createElement('br'))

  mentor = doc.createTextNode(
      'mentored by ' + _getName(project.mentor))
  description.appendChild(mentor)
  description.appendChild(doc.createElement('br'))

  org = doc.createTextNode(project.scope.name)
  description.appendChild(org)
  description.appendChild(doc.createElement('br'))
  description.appendChild(doc.createElement('br'))

  description = _appendHomePageAndBlogContent(doc, description, student)

  description = _appendStateAndCountryContnent(doc, description, student)

  return description


def _appendStateAndCountryContnent(doc, description, state, country):
  """Appends state and country info to the description of a placemark.
  """

  if state:
    description.appendChild(doc.createTextNode(state + ', '))

  description.appendChild(doc.createTextNode(country))

  return description


def _appendHomePageAndBlogContent(doc, description, home_pages, blogs):
  """Appends home page and blog info to the description of a placemark.
  """

  if home_pages:
    text = doc.createTextNode('Home page:')
    description.appendChild(text)
    description.appendChild(doc.createElement('br'))

  for home_page in home_pages:
    description.appendChild(doc.createTextNode(home_page))
    description.appendChild(doc.createElement('br'))

  if home_pages:
    description.appendChild(doc.createElement('br'))

  if blogs:
    text = doc.createTextNode('Blog:')
    description.appendChild(text)
    description.appendChild(doc.createElement('br'))

  for blog in blogs:
    description.appendChild(doc.createTextNode(blog))
    description.appendChild(doc.createElement('br'))

  if blogs:
    description.appendChild(doc.createElement('br'))

  return description


def _getName(entity):
  """For a given entity returns a name to be displayed.
  """

  return entity.name()


def _getMentorDescription(doc, content):
  """Returns <description> element for a mentor / org admin based on content.
  """

  description = doc.createElement('description')

  admin = content['admin']
  if admin:
    text = doc.createTextNode('Organization admin for ' + admin)
    description.appendChild(text)
    description.appendChild(doc.createElement('br'))

  projects = content['projects']
  if projects:
    text = doc.createTextNode('Mentoring...')
    description.appendChild(text)
    description.appendChild(doc.createElement('br'))

  for project in projects:
    i = doc.createElement('i')
    description.appendChild(i)

    title = doc.createTextNode(project['title'])
    i.appendChild(title)
    description.appendChild(doc.createElement('br'))

    student = doc.createTextNode('by ' + project['student'])
    description.appendChild(student)
    description.appendChild(doc.createElement('br'))

    organization = doc.createTextNode(project['org'])
    description.appendChild(organization)
    description.appendChild(doc.createElement('br'))

  consults = content['consults']
  for consult in consults:
    text = doc.createTextNode(consult)
    description.appendChild(text)
    description.appendChild(doc.createElement('br'))

  description.appendChild(doc.createElement('br'))

  home_pages = content['home_pages']
  blogs = content['blogs']
  description = _appendHomePageAndBlogContent(doc, description,
      home_pages, blogs)

  state = content['state']
  country = content['country']
  description = _appendStateAndCountryContnent(doc, description, state,
      country)

  return description


def _getStudentDescription(doc, content):
  """Returns <description> element for a student based on content.
  """

  description = doc.createElement('description')

  text = doc.createTextNode('Working on...')
  description.appendChild(text)

  description.appendChild(doc.createElement('br'))

  project = content['project']
  i = doc.createElement('i')
  description.appendChild(i)

  title = doc.createTextNode(project.title)
  i.appendChild(title)
  description.appendChild(doc.createElement('br'))

  mentor = doc.createTextNode('mentored by ' + project.mentor.name())
  description.appendChild(mentor)
  description.appendChild(doc.createElement('br'))

  org = doc.createTextNode(project.scope.name)
  description.appendChild(org)
  description.appendChild(doc.createElement('br'))
  description.appendChild(doc.createElement('br'))

  home_pages = content['home_pages']
  blogs = content['blogs']
  description = _appendHomePageAndBlogContent(doc, description,
      home_pages, blogs)

  state = content['state']
  country = content['country']
  description = _appendStateAndCountryContnent(doc, description, state,
      country)

  return description


def _createFolderForMentorsAndOrgAdmins(doc):
  """
  """

  folder = doc.createElement("Folder")

  name = doc.createElement("name")
  folder.appendChild(name)

  nametext = doc.createTextNode("Mentors")
  name.appendChild(nametext)

  return folder


def _createLineString(doc, student, mentor):
  """Generates line string between a given student and mentor.
  """

  line_string = doc.createElement('LineString')

  coordinates = doc.createElement('coordinates')
  line_string.appendChild(coordinates)

  text = doc.createTextNode(str(student[0]) + ',' + str(student[1]) + ' ' +
      str(mentor[0]) + ',' + str(mentor[1]))
  coordinates.appendChild(text)

  altitude_mode = doc.createElement('altitudeMode')
  line_string.appendChild(altitude_mode)

  text = doc.createTextNode('clampToGround')
  altitude_mode.appendChild(text)

  extrude = doc.createElement('extrude')
  line_string.appendChild(extrude)

  text = doc.createTextNode('1')
  extrude.appendChild(text)

  tessellate = doc.createElement('tessellate')
  line_string.appendChild(tessellate)

  text = doc.createTextNode('1')
  tessellate.appendChild(text)

  return line_string


def _createPoint(doc, longitude, latitude):
  """Generates <Point> subtree with coordinates for a given entity.
  """

  point = doc.createElement('Point')

  coordinates = doc.createElement('coordinates')
  point.appendChild(coordinates)

  text = doc.createTextNode(str(longitude) + ',' + str(latitude))
  coordinates.appendChild(text)

  return point


def _createStudentPlacemark(doc, coordinates, content):
  """Creates <Placemark> element for a student based on a given content.
  """

  placemark = doc.createElement('Placemark')

  style = _getPersonStyle(doc, 'student')
  placemark.appendChild(style)

  name = doc.createElement('name')
  placemark.appendChild(name)

  text = doc.createTextNode(content['name'])
  name.appendChild(text)

  description = _getStudentDescription(doc, content)
  placemark.appendChild(description)

  point = _createPoint(doc, coordinates[0], coordinates[1])
  placemark.appendChild(point)

  return placemark


def _createMentorPlacemark(doc, coordinates, content):
  """Creates <Placemark> element for a mentor based on a given content.
  """

  placemark = doc.createElement('Placemark')

  type = 'org_admin' if content['admin'] is not None else 'mentor'
  style = _getPersonStyle(doc, type)
  placemark.appendChild(style)

  name = doc.createElement('name')
  placemark.appendChild(name)

  text = doc.createTextNode(content['name'])
  name.appendChild(text)

  description = _getMentorDescription(doc, content)
  placemark.appendChild(description)

  point = _createPoint(doc, coordinates[0], coordinates[1])
  placemark.appendChild(point)

  return placemark


def _createLineStringPlacemark(doc, student_coordinates, mentor_coordinates):
  """Creates <Placemark> element for a line string between given student
     coordinates and mentor coordinates.
  """

  placemark = doc.createElement('Placemark')

  line_string = _createLineString(doc, student_coordinates, mentor_coordinates)
  placemark.appendChild(line_string)

  style = _getLineStringStyle(doc)
  placemark.appendChild(style)

  return placemark


def _processMentor(doc, org_admin, mentors, folder):
  """Processes a student and adds information to a folder.
  """

  projects = []
  placemarks = {}
  longitude = None
  latitude = None

  for entity in mentors:
    if not entity.publish_location:
      continue

    longitude, latitude = (entity.longitude, entity.latitude)

    if not placemarks.get((longitude, latitude)):
      placemarks[(longitude, latitude)] = {
          'admin': None,
          'projects': [],
          'consults': [],
          'name': entity.name(),
          'country': entity.res_country,
          'state': entity.res_state,
          'home_pages': set(),
          'blogs': set()
          }

    projects = _getMentoredProjects(entity)
    if not projects:
      placemarks[(longitude, latitude)]['consults'].append(
          'Mentor for ' + entity.scope.name)
    else:
      for project in projects:
        placemarks[(longitude, latitude)]['projects'].append({
            'title': project.title,
            'org': project.scope.name,
            'student': project.student.name()
            })

    if entity.home_page:
      placemarks[(longitude, latitude)]['home_pages'].add(entity.home_page)
    if entity.blog:
      placemarks[(longitude, latitude)]['blogs'].add(entity.blog)

  if org_admin and org_admin.publish_location:

    if not (longitude, latitude):
      longitude, latitude = (org_admin.longitude, org_admin.latitude)

    if not placemarks.get((longitude, latitude)):
      placemarks[(longitude, latitude)] = {
          'admin': None,
          'projects': [],
          'consults': [],
          'name': org_admin.name(),
          'country': org_admin.res_country,
          'state': org_admin.res_state,
          'home_pages': set(),
          'blogs': set()
          }

    placemarks[(longitude, latitude)]['admin'] = org_admin.scope.name

    if org_admin.home_page:
      placemarks[(longitude, latitude)]['home_pages'].add(org_admin.home_page)
    if org_admin.blog:
      placemarks[(longitude, latitude)]['blogs'].add(org_admin.blog)

  for coordinates, content in placemarks.iteritems():
    placemark = _createMentorPlacemark(doc, coordinates, content)
    folder.appendChild(placemark)


def _processStudent(doc, student, orgs_folder):
  """Processes a student and adds information to a folder.
  """

  if not student.publish_location:
    return

  project = _getStudentProject(student)

  if not project:
    return

  folder = doc.createElement('Folder')

  name = doc.createElement('name')
  folder.appendChild(name)

  name.appendChild(doc.createTextNode(student.name()))

  content = {
      'name': student.name(),
      'project': project,
      'country': student.res_country,
      'state': student.res_state,
      'home_pages': [student.home_page] if student.home_page else [],
      'blogs': [student.blog] if student.blog else []
      }

  coordinates = student.longitude, student.latitude

  placemark = _createStudentPlacemark(doc, coordinates, content)
  folder.appendChild(placemark)

  mentor = project.mentor

  if mentor.publish_location:
    mentor_coordinates = mentor.longitude, mentor.latitude
    line_string = _createLineStringPlacemark(doc, coordinates,
        mentor_coordinates)
    folder.appendChild(line_string)

  org = project.scope
  org_folder = orgs_folder[org.name]
  org_folder.appendChild(folder)


def _processAllUsers(doc, mentors_folder, orgs_folder):
  """Processes all users and fills folders with information based on roles.
  """

  from soc.logic.models.mentor import logic as mentor_logic
  from soc.logic.models.org_admin import logic as org_admin_logic
  from soc.logic.models.student import logic as student_logic

  it = _getAllOrgAdmins()
  org_admins = dict()
  for org_admin in it:
    org_admins[org_admin.link_id] = org_admin

  it = _getAllMentors()
  mentors = dict()
  for mentor in it:
    link_id = mentor.link_id
    if link_id not in mentors:
      mentors[link_id] = []
    mentors[link_id].append(mentor)

  it = _getAllStudents()
  students = dict()
  for student in it:
    students[student.link_id] = student

  link_ids = set(mentors.keys() + org_admins.keys())

  for link_id in link_ids:
    entity = org_admins.get(link_id)
    entities = mentors.get(link_id, [])

    if entity or entities:
      _processMentor(doc, entity, entities, mentors_folder)
      continue

  link_ids = students.keys()
  for link_id in link_ids:
    entity = students.get(link_id)
    if entity:
      _processStudent(doc, entity, orgs_folder)


def _createFolderForStudentsAndOrgs(doc):
  """Creates <Folder> elements for all students and for all accepted
     organizations.

  Returns:
    A tuple whose first element is a folder for students and the second
    one is a dictionary mapping organization names with their folders.
  """

  folder = doc.createElement('Folder')

  name = doc.createElement('name')
  folder.appendChild(name)

  text = doc.createTextNode('Students')
  name.appendChild(text)

  orgs = _getAcceptedOrgs()
  org_folders = {}

  for org in orgs:
    org_folder = doc.createElement('Folder')
    folder.appendChild(org_folder)

    name = doc.createElement('name')
    org_folder.appendChild(name)

    text = doc.createTextNode(org.name)
    name.appendChild(text)

    org_folders[org.name] = org_folder

  return folder, org_folders


def generateCompleteKML():
  """Generates complete KML file for Google Summer of Code 2009.
  """

  doc = Document()

  kml = doc.createElement('kml')
  doc.appendChild(kml)

  document = doc.createElement('Document')
  kml.appendChild(document)

  mentor_folder = _createFolderForMentorsAndOrgAdmins(doc)
  document.appendChild(mentor_folder)

  student_folder, org_folders = _createFolderForStudentsAndOrgs(doc)
  document.appendChild(student_folder)

  _processAllUsers(doc, mentor_folder, org_folders)

  out = codecs.open('soc_map2009.kml', 'w', 'utf-8')
  out.write(doc.toprettyxml(indent='  '))
  out.close()


def main(args):

  context = {
      'export': generateCompleteKML,
      }
  interactive.setup()
  interactive.remote(args, context)


if __name__ == '__main__':
  if len(sys.argv) < 2:
    print "Usage: %s app_id [host]" % (sys.argv[0],)
    sys.exit(1)

  main(sys.argv[1:])