#!/usr/bin/python
#
# Fetch the result of a run by its URL
#

import datetime
import optparse
import os
import pipes
import pscheduler
import sys


pscheduler.set_graceful_exit()


#
# Gargle the arguments
#


class VerbatimParser(optparse.OptionParser):
    def format_epilog(self, formatter):
        return self.epilog

opt_parser = VerbatimParser(
    usage="Usage: %prog [options] run-url",
    epilog=
"""
Examples:

  result https://ps.foo.org/pscheduler/task/12345...
      Fetch a result of the specified run as plain text

  result --format text https://ps.foo.org/pscheduler/task/12345...
      Same as above, with explicit format

  result --format html https://ps.foo.org/pscheduler/task/12345...
      Fetch a result of the specified run and format as HTML

  result --format json html https://ps.foo.org/pscheduler/task/12345...
      Fetch a result of the specified run and format as JSON
"""
    )
opt_parser.disable_interspersed_args()

opt_parser.add_option("--archivings",
                      help="For text output, dump archiving statis",
                      action="store_true",
		      default=False,
                      dest="archivings")

opt_parser.add_option("--bind",
                      help="Make the request from the provided address",
                      default=None,
                      action="store", type="string",
                      dest="bind")

opt_parser.add_option("--diags",
                      help="For text output, dump participant diagnostics",
                      action="store_true",
		      default=False,
                      dest="diags")

opt_parser.add_option("--format",
                      help="Format for output: text (the default), html or json",
                      action="store", type="string",
		      default="text",
                      dest="format")

opt_parser.add_option("--quiet",
                      help="For text output, don't display anything but the result",
                      action="store_true",
		      default=False,
                      dest="quiet")



(options, remaining_args) = opt_parser.parse_args()

if len(remaining_args) < 1:
    opt_parser.print_usage()
    pscheduler.fail()

formats = {
    'html': 'text/html',
    'json': 'application/json',
    'text': 'text/plain',
    # Not "officially" supported, but here for completeness
    'text/html': 'text/html',
    'application/json': 'application/json',
    'text/plain': 'text/plain',
    }

try:
    out_format = formats[options.format]
except KeyError:
    pscheduler.fail("Invalid --format; must be text, html, or json")


run_url = remaining_args[0]


#
# Main Program
#

# Fetch the run so we can pluck out the result URL.

try:
    status, run_json = pscheduler.url_get(run_url, bind=options.bind, json=True)
except Exception as ex:
    pscheduler.fail(str(ex))

# Deal with the various reasons why the run might not have happened

if "state" not in run_json:
    pscheduler.fail("Data returned from the server doesn't look like a run result.")

if run_json["state"] == "nonstart":
    try:
        reason = run_json["errors"] 
    except KeyError:
        reason = "No reason provided."
    pscheduler.fail("Run never started: %s" % reason)

if run_json["state"] in ["pending", "on-deck", "running", "cleanup"]:
    pscheduler.succeed("Run has not completed.")


# TODO: Should dump diags for anything but preempted.

if run_json["state"] in ["overdue", "missed", "preempted"]:
    pscheduler.succeed("Run did not complete: %s" % run_json["state-display"])

if run_json["state"] == "failed":
    print "Run failed"
    succeeded = False



try:
    result_url = run_json["result-href"]
except KeyError:
    pscheduler.fail("No result URL returned by the server.")


try:
    task_url = run_json["task-href"]
except KeyError:
    pscheduler.fail("No task URL returned by the server.")



try:

    status, result = pscheduler.url_get(
        result_url,
        params={ "format": out_format },
        bind=options.bind,
        json=False)

    status, task_cli = pscheduler.url_get(
        "%s/cli" % task_url,
        bind=options.bind,
        json=True)

    status, task_json = pscheduler.url_get(task_url,
                                           params={"detail": True},
                                           bind=options.bind,
                                           json=True)

except Exception as ex:
    pscheduler.fail("Problem fetching results: %s" % str(ex))

if not options.quiet and out_format == "text/plain":

    firstline = [ run_json["start-time"], "on" ]

    parts = run_json["participants"]
    if len(parts) == 1:
        firstline.append(parts[0])
    else:
        last = parts.pop()
        if len(parts) == 1:
            firstline.append(parts[0])
        else:
            firstline.extend([ "%s," % part for part in parts ])
        firstline.append("and")
        firstline.append(last)

    firstline.append("with")
    firstline.append(task_json["tool"] + ":")
    print pscheduler.prefixed_wrap(
        "", " ".join(firstline), indent=2)

    print

    print pscheduler.prefixed_wrap(
        "", " ".join([ pipes.quote(arg) for arg in task_cli ]),
        indent=2)

    print


# The strip gets rid of any pesky newlines
print result.rstrip()


try:
    succeeded = run_json['result-merged']['succeeded']
    if succeeded is None:
        raise KeyError()

except KeyError as ex:
    succeeded = False


if out_format == "text/plain" \
   and (options.diags or not succeeded):

    print
    parts = task_json["detail"]["participants"]
    for participant in range(0, len(parts)):
        print
        print "Diagnostics from %s:" % (parts[participant])

        try:
            print "\n".join([
                "    %s" % (string) for string in
                run_json["result-full"][participant]["diags"].split("\n")
            ])
        except (KeyError, TypeError, AttributeError):
            print "    (No diagnostics)"

        try:
            err = run_json["result-full"][participant].get("error", None)
        except (AttributeError, TypeError):
            err = None

        if err is not None:
            print
            print "Error from %s:" % (parts[participant])
            try:
                print "\n".join([
                    "    %s" % (string) for string in
                    err.split("\n")
                ])
            except (KeyError, TypeError):
                print "    (No error)"


# Dump the archiving information

if out_format == "text/plain" and succeeded and options.archivings:
    print
    print "Archivings:"
    if run_json.get('archivings', None) is not None:
        for archiving in run_json['archivings']:
            print
            print "  To %s, %s" % (
                archiving['archiver']['name'],
                "Finished" if archiving['archived'] else "Unfinished"
                )
            for attempt in archiving['diags']:
                try:
                    if attempt['return-code'] <> 0:
                        raise TypeError  # Treat this as a failure.
                    succeeded = attempt['stdout']['succeeded']
                    diags = "" if succeeded else attempt['stdout']['error']
                except (KeyError, TypeError) as ex:
                    succeeded = False
                    diags = attempt['stderr']
                print "    %-25s %s" % (
                    attempt['time'],
                    "Succeeded" if succeeded else diags
                    )
    else:
        print
        print "    This task had no archivings."


pscheduler.succeed()
