# EasyScout Python Utils

# Copyright (c) 2004 by Ryan Kirkpatrick (scouts@rkirkpat.net). 
#   All rights reserved.
#
# 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; see the file COPYING.  If not, write to
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

# Documentation.
"""
These are a handful of utilities that are used by the other scripts
of this package. Functions:
  TS()                     -- Generates a date/time stamp, and a delta since
                              script start.
  Verbose(string,run)      -- Display an optional message with time stamp.
  GetURL(url, state, data) -- Attempts to retrieve a URL resources.
"""
__credits__ = 'Copyright 2004 (c) by Ryan Kirkpatrick. All Rights Reserved'
__author__ = 'Ryan Kirkpatrick <scouts@rkirkpat.net>'
__date__ = '2004/01/05'
__version__ = 'ESPyUtils 1.0'

# Needed modules.
import sys
import os
import re
import time
from urllib import urlopen, urlencode

# Generate date/time stamp.
startTS = time.time()
def TS():
    """
    Generates a time stamp and the amount of time that has elapsed since
    this module was imported. Returns this as a string.
    """
    
    # Return a date/time stamp.
    return time.strftime("[%H:%M:%S",time.localtime(time.time())) + "/%08.2f]" % \
           (time.time() - globals()["startTS"])

# Display an optional message with a time stamp.
delta = 0; verbose = 0; stdout = 0
def Verbose(string = "", run = 1):
    """
    If the global verbose variable is set, the given message is printed.
    Parameters:
      string -- The message to display if needed.
      run    -- If true, then the time between this call and the
                  next call will be printed on with the output of
                  the next call.

    To display a simple message, call with a string and a false value.
    To display a message, and then later the time since that message
      was displayed, call with a string now, and nothing later.
    The first time this function is called it will set STDOUT to
      unbuffered mode.
    Returns nothing.
    """

    # If not verbose mode, return now.
    if not globals()["verbose"]: return

    # Is stdout unbuffered?
    if not globals()["stdout"]:
        # No, make it such.
        number = sys.stdout.fileno()
        sys.stdout.close()
        sys.stdout = os.fdopen(number,"w",0)
        globals()["stdout"] = 1

    # If we got a string to display, display it now with a timestamp pre-pended,
    #   but no newline.
    if string: print TS(), string,

    # Deal with delta time requests, returning now if there is no such request.
    if not run: return
    if globals()["delta"] == 0:
        # Delta time not initialized, initialize now.
        globals()["delta"] = time.time()
    else:
        # Delta time initiailzed, calculated and display difference.
        print "%8.2f" % (time.time() - globals()["delta"])
        globals()["delta"] = 0

# Get a URL and handle error conditions.
def GetURL(url, state = {}, data = {}):
    """
    Attempts to retrieve a URL resources. Parameters:
      url   -- The URL to retrieve.
      state -- Optional paramters to add to the POST query based on the
               contents of the 'data' dictionary. Optional.
      data  -- POST data to encode and include with URL request. Optional.

    For the 'state' dictionary, the following parameters are added to the
    POST query if they exist, their values are not None, and:
      'session' parameter is added if 'data' does not have a 'z' key.
      'scoutid' parameter is added if 'data' does not have a 'scoutid' key.
      'leaderid' parameter is added if 'data' does not have a 'leaderid' key.

    Returns two values:
      okay   -- True if everything went fine, false if there was an error.
      result -- The result of the URL request, or if an error occured,
                an explanation of what happened.
    """

    # Add state variables if they are set.
    if state.has_key("session") and state["session"] and not data.has_key("z"):
        data["z"] = state["session"]
    if state.has_key("scoutid") and state["scoutid"] and not data.has_key("scoutid"):
        data["scoutid"] = state["scoutid"]
    if state.has_key("leaderid") and state["leaderid"] and not data.has_key("leaderid"):
        data["leaderid"] = state["leaderid"]

    # Send the URL request.
    try:
        # If this does not thrown an exceptions, things look good.
        result = urlopen(url,urlencode(data)).read()
        okay = 1
    except:
        # Oops, unknown host, or host refused our connection.
        result = "%s :: Host not found or host is not a web server!" % url
        okay = 0

    # Check for an error code from the server.
    error = re.search("<TITLE>[0-9][0-9][0-9] [^<]+</TITLE>",result)
    if not error: error = re.search("HTTP Status [0-9][0-9][0-9] - [^<]+</h1>",result)
    if error:
        # Found one, save it.
        result = "%s%s :: URL failed! %s" % (url,data, result[error.start():error.end()-4])
        okay = 0

    # Return the results.
    return okay, result
