#!/usr/bin/env python
#
# Data Quality Checker also known as DQC
#
# Read the README.md for more detailed information
#
#       Barcelona Supercomputing Center - Centro Nacional de Supercomputacion
#       Earth Sciences Department - Computational Earth Sciences
#     
#     Authors: 
#       Joan Sala Calero (joan.sala@bsc.es)
#       Daniel Trujillo Viedma (daniel.trujillo@bsc.es)
#

import json
import sys
import dateutil.parser
from _functools import reduce

if len(sys.argv) != 4:
    print('usage: python json2txt_integration.py <input JSON> <section1.txt> <section2.txt>')
    sys.exit()

with open(sys.argv[1], 'r') as f:
    input_read = f.read()

json_parsed = json.loads(input_read)

##########
## Extract preliminary data
##########
tests_data = json_parsed["results"]
tool_name = json_parsed["info"]["tool_name"]
test_timestamp = dateutil.parser.isoparse(json_parsed["info"]["check_run_time"]).strftime('%Y-%m-%d')

##########
## First, build the summary information
##########
passed_test = lambda result: False if("error" in result["output"]) else result["output"]["passed"]
number_of_tests_passed = len(list(filter(passed_test, tests_data)))
percentage_passed = round(100 * number_of_tests_passed / len(tests_data), 1)

results = "res = {}% passed, {}% not passed".format(percentage_passed, round(100-percentage_passed, 2))
message = "msg = The tool " + tool_name + (" has " if (percentage_passed == 100) else " hasn't ") + "passed all integration tests"
number_of_tests = "number of tests = {}".format(len(tests_data))
generation_time = "time = generated on " + test_timestamp

get_dataset_name = lambda result: result["input"][0]
dataset_names = map(get_dataset_name, tests_data)
get_dataset_type = lambda dataset_name: dataset_name[0:dataset_name.find("-")]
dataset_types = list(set(map(get_dataset_type, dataset_names)))

extended_dataset_type_names = {"projections": "climate projections", "seasonal": "seasonal forecasts", "satellite": "satellite observations"}
def extend_dataset_type_name(type_name: str) -> str:
    return extended_dataset_type_names[type_name] if (type_name in extended_dataset_type_names) else type_name

dataset_types_message = "dataset types considered = " + reduce(lambda msg,new_type: "{}, {}".format(msg, extend_dataset_type_name(new_type)), dataset_types[1:], extend_dataset_type_name(dataset_types[0]))
introduction_paragraph = "This test verifies whether the tool can be successfully applied to a representative selection of different CDS datasets and variables. Indeed, the integration test looks at the tool when interacting in its environment, using the CDS data (as opposed to unit testing which uses synthetic inputs). The coverage of the test is complete when it checks the behaviour of the tool using one example of input from each dataset type served by the CDS Catalogue (i.e. satellite and in-situ observations, reanalysis, climate projections and seasonal forecasts).\n \nThe checks were performed using a Python-based software package, which runs on the ECMWF virtual machines dedicated to the evaluation and quality control. The code is available upon request at copernicus-support@ecmwf.int.\n \nThe summary of the result is given below, the log details are available in the following pages.\n________________________________________________________________________________"
                 

##########
## Now extract relevant data for the report  
##########  
def dictToString(obj):
    return reduce(lambda a,b : "{}\n  {}: {}".format(a, b[0],b[1]), obj.items(), "")

def summarizeOutput(string: str, maxOutputLength: int = 2048):
    return string if(len(string) < maxOutputLength) else "{}...\n(Excessive output was omitted)".format(string[0:maxOutputLength])

extract_data = lambda result: "**  Integration test number {}  **\nPassed: {}\nInput: {}\nParameters applied:{}\nResults:{}\n \n \n \n".format(
                                                                                                                                                result[1], 
                                                                                                                                                result[0]["output"]["passed"],
                                                                                                                                                result[0]["input"][0],
                                                                                                                                                dictToString(result[0]["input"][1]),
                                                                                                                                                summarizeOutput(dictToString(result[0]["output"]["result"][0])) if("result" in result[0]["output"]) else result[0]["output"]["error"]
                                                                                                                                            )

##########
## So, this has to be inside a list() because in the next section we use 2 times
## relevant_data and, although iterators are lazy, they are also mutable, so the first
## following map would consume the iterator, leaving none for the second map.
##########
def zipWithIndex(array):
    return list(zip(array, range(0, len(array))))

relevant_data = list(map(extract_data, zipWithIndex(tests_data)))

##########
## The final step is to format the relevant data
##########
format_log  = lambda data: "{}\n\n".format(data)
     
relevant_data_formatted = "{}\n \n[integration]\n \n[integration_result]\n{}\n{}\n \n[metadata]\n{}\n{}\ntest = integration\n{}".format(introduction_paragraph, results, message, number_of_tests, dataset_types_message, generation_time)
relevant_log_formatted  = relevant_data

##########
## And write the files
##########
with open(sys.argv[2], 'w') as f:
    list(map(lambda s: f.write(s), relevant_data_formatted))

with open(sys.argv[3], 'w') as f:
    list(map(lambda s: f.write(s), relevant_log_formatted))




