/// <summary> /// Create a message table ready for writing. /// </summary> /// <param name="dataStore">The data store to read the message table from</param> /// <param name="simulationName">The simulation name to get messages for</param> /// <returns>The filled message table</returns> private static DataTable GetMessageTable(DataStore dataStore, string simulationName) { DataTable messageTable = new DataTable(); DataTable messages = dataStore.GetData(simulationName, "Messages"); if (messages != null && messages.Rows.Count > 0) { messageTable.Columns.Add("Date", typeof(string)); messageTable.Columns.Add("Message", typeof(string)); string previousCol1Text = null; string previousMessage = null; foreach (DataRow row in messages.Rows) { // Work out the column 1 text. string modelName = (string)row[1]; DateTime date = (DateTime)row[2]; string col1Text = date.ToString("yyyy-MM-dd") + " " + modelName; // If the date and model name have changed then write a row. if (col1Text != previousCol1Text) { if (previousCol1Text != null) { messageTable.Rows.Add(new object[] { previousCol1Text, previousMessage }); } previousMessage = string.Empty; previousCol1Text = col1Text; } else { col1Text = null; } string message = (string)row[3]; Models.DataStore.ErrorLevel errorLevel = (Models.DataStore.ErrorLevel)Enum.Parse(typeof(Models.DataStore.ErrorLevel), row[4].ToString()); if (errorLevel == DataStore.ErrorLevel.Error) { previousMessage += "FATAL ERROR: " + message; } else if (errorLevel == DataStore.ErrorLevel.Warning) { previousMessage += "WARNING: " + message; } else { previousMessage += message; } previousMessage += "\r\n"; } } return(messageTable); }
/// <summary> /// Create a message table ready for writing. /// </summary> /// <param name="dataStore">The data store to read the message table from</param> /// <param name="simulationName">The simulation name to get messages for</param> /// <returns>The filled message table</returns> private static DataTable GetMessageTable(DataStore dataStore, string simulationName) { DataTable messageTable = new DataTable(); DataTable messages = dataStore.GetData(simulationName, "Messages"); if (messages != null && messages.Rows.Count > 0) { messageTable.Columns.Add("Date", typeof(string)); messageTable.Columns.Add("Message", typeof(string)); string previousCol1Text = null; string previousMessage = null; foreach (DataRow row in messages.Rows) { // Work out the column 1 text. string modelName = (string)row[1]; string col1Text; if (row[2].GetType() == typeof(DateTime)) { DateTime date = (DateTime)row[2]; col1Text = date.ToString("yyyy-MM-dd") + " " + modelName; } else col1Text = row[2].ToString(); // If the date and model name have changed then write a row. if (col1Text != previousCol1Text) { if (previousCol1Text != null) { messageTable.Rows.Add(new object[] { previousCol1Text, previousMessage }); } previousMessage = string.Empty; previousCol1Text = col1Text; } else { col1Text = null; } string message = (string)row[3]; Models.DataStore.ErrorLevel errorLevel = (Models.DataStore.ErrorLevel)Enum.Parse(typeof(Models.DataStore.ErrorLevel), row[4].ToString()); if (errorLevel == DataStore.ErrorLevel.Error) { previousMessage += "FATAL ERROR: " + message; } else if (errorLevel == DataStore.ErrorLevel.Warning) { previousMessage += "WARNING: " + message; } else { previousMessage += message; } previousMessage += "\r\n"; } if (previousMessage != null) { messageTable.Rows.Add(new object[] { previousCol1Text, previousMessage }); } } return messageTable; }
/// <summary> /// Write the summary report to the specified writer. /// </summary> /// <param name="dataStore">The data store to write a summary report from</param> /// <param name="simulationName">The simulation name to produce a summary report for</param> /// <param name="writer">Text writer to write to</param> /// <param name="apsimSummaryImageFileName">The file name for the logo. Can be null</param> /// <param name="outtype">Indicates the format to be produced</param> public static void WriteReport( DataStore dataStore, string simulationName, TextWriter writer, string apsimSummaryImageFileName, OutputType outtype) { Document document = null; RtfDocumentRenderer renderer = null; if (outtype == OutputType.html) { writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<html>"); writer.WriteLine("<head>"); writer.WriteLine("<meta content='text/html; charset=UTF-8; http-equiv='content-type'>"); writer.WriteLine("<style>"); writer.WriteLine("h2 { color:darkblue; } "); writer.WriteLine("h3 { color:darkblue; } "); writer.WriteLine("table { border:1px solid black; border-collapse:collapse; width:100%; table-layout:fixed; text-align:left; }"); writer.WriteLine("table.headered {text-align:right; }"); writer.WriteLine("tr.total { color:darkorange; font-weight:bold; }"); writer.WriteLine("table.headered td.col1 { text-align:left; font-weight:bold; }"); writer.WriteLine("td { border:1px solid; }"); writer.WriteLine("th { border:1px solid; text-align:right; background-color: palegoldenrod}"); writer.WriteLine("th.col1 { text-align:left; }"); writer.WriteLine("</style>"); writer.WriteLine("</head>"); writer.WriteLine("<body>"); } else if (outtype == OutputType.rtf) { document = new Document(); renderer = new RtfDocumentRenderer(); // Get the predefined style Normal. Style style = document.Styles["Normal"]; // Because all styles are derived from Normal, the next line changes the // font of the whole document. Or, more exactly, it changes the font of // all styles and paragraphs that do not redefine the font. style.Font.Name = "Arial"; // Heading1 to Heading9 are predefined styles with an outline level. An outline level // other than OutlineLevel.BodyText automatically creates the outline (or bookmarks) // in PDF. style = document.Styles["Heading2"]; style.Font.Size = 14; style.Font.Bold = true; style.Font.Color = Colors.DarkBlue; style.ParagraphFormat.PageBreakBefore = false; style.ParagraphFormat.SpaceAfter = 3; style.ParagraphFormat.SpaceBefore = 16; style = document.Styles["Heading3"]; style.Font.Size = 12; style.Font.Bold = true; style.Font.Color = Colors.DarkBlue; style.ParagraphFormat.SpaceBefore = 10; style.ParagraphFormat.SpaceAfter = 2; // Create a new style called Monospace based on style Normal style = document.Styles.AddStyle("Monospace", "Normal"); System.Drawing.FontFamily monoFamily = new System.Drawing.FontFamily(System.Drawing.Text.GenericFontFamilies.Monospace); style.Font.Name = monoFamily.Name; Section section = document.AddSection(); } // Get the initial conditions table. DataTable initialConditionsTable = dataStore.GetData(simulationName, "InitialConditions"); if (initialConditionsTable != null) { // Convert the 'InitialConditions' table in the DataStore to a series of // DataTables for each model. List<DataTable> tables = new List<DataTable>(); ConvertInitialConditionsToTables(initialConditionsTable, tables); // Now write all tables to our report. for (int i = 0; i < tables.Count; i += 2) { // Only write something to the summary file if we have something to write. if (tables[i].Rows.Count > 0 || tables[i + 1].Rows.Count > 0) { string heading = tables[i].TableName; WriteHeading(writer, heading, outtype, document); // Write the manager script. if (tables[i].Rows.Count == 1 && tables[i].Rows[0][0].ToString() == "Script code: ") { WriteScript(writer, tables[i].Rows[0], outtype, document); } else { // Write the properties table if we have any properties. if (tables[i].Rows.Count > 0) { WriteTable(writer, tables[i], outtype, "PropertyTable", document); } // Write the general data table if we have any data. if (tables[i + 1].Rows.Count > 0) { WriteTable(writer, tables[i + 1], outtype, "ApsimTable", document); } } if (outtype == OutputType.html) writer.WriteLine("<br/>"); } } } // Write out all messages. WriteHeading(writer, "Simulation log:", outtype, document); DataTable messageTable = GetMessageTable(dataStore, simulationName); WriteMessageTable(writer, messageTable, outtype, false, "MessageTable", document); if (outtype == OutputType.html) { writer.WriteLine("</body>"); writer.WriteLine("</html>"); } else if (outtype == OutputType.rtf) { string rtf = renderer.RenderToString(document, Path.GetTempPath()); writer.Write(rtf); } }
/// <summary> /// Run tests /// </summary> /// <param name="accept">If true, the stats from this run will be written to file as the accepted stats.</param> /// <param name="GUIrun">If true, do not raise an exception on test failure.</param> public void Test(bool accept = false, bool GUIrun = false) { PredictedObserved PO = Parent as PredictedObserved; if (PO == null) { return; } DataStore DS = PO.Parent as DataStore; MathUtilities.RegrStats[] stats; List <string> statNames = (new MathUtilities.RegrStats()).GetType().GetFields().Select(f => f.Name).ToList(); // use reflection, get names of stats available DataTable POtable = DS.GetData("*", PO.Name); List <string> columnNames; string sigIdent = "X"; if (POtable == null) { object sim = PO.Parent; while (sim as Simulations == null) { sim = ((Model)sim).Parent; } throw new ApsimXException(this, "Could not find PO table in " + (sim != null ? ((Simulations)sim).FileName : "<unknown>") + ". Has the simulation been run?"); } columnNames = POtable.Columns.Cast <DataColumn>().Select(c => c.ColumnName).ToList(); //get list of column names columnNames = columnNames.Where(c => c.Contains("Observed")).ToList(); //filter names that are not pred/obs pairs for (int i = 0; i < columnNames.Count; i++) { columnNames[i] = columnNames[i].Replace("Observed.", ""); } columnNames.Sort(); //ensure column names are always in the same order stats = new MathUtilities.RegrStats[columnNames.Count]; List <double> x = new List <double>(); List <double> y = new List <double>(); string xstr, ystr; double xres; double yres; for (int c = 0; c < columnNames.Count; c++) //on each P/O column pair { x.Clear(); y.Clear(); foreach (DataRow row in POtable.Rows) { xstr = row["Observed." + columnNames[c]].ToString(); ystr = row["Predicted." + columnNames[c]].ToString(); if (Double.TryParse(xstr, out xres) && Double.TryParse(ystr, out yres)) { x.Add(xres); y.Add(yres); } } if (x.Count == 0 || y.Count == 0) { continue; } stats[c] = MathUtilities.CalcRegressionStats(columnNames[c], y, x); } //remove any null stats which can occur from non-numeric columns such as dates List <MathUtilities.RegrStats> list = new List <MathUtilities.RegrStats>(stats); list.RemoveAll(l => l == null); stats = list.ToArray(); //remove entries from column names for (int i = columnNames.Count() - 1; i >= 0; i--) { bool found = false; for (int j = 0; j < stats.Count(); j++) { if (columnNames[i] == stats[j].Name) { found = true; break; } } if (!found) { columnNames.RemoveAt(i); } } //turn stats array into a DataTable //first, check if there is already an AcceptedStats array, create if not. //If the names don't match, then use current stats as user has dragged //an already existing Test to a new node. if (AcceptedStats == null || POName != PO.Name) { POName = PO.Name; AcceptedStats = stats; AcceptedStatsName = StringUtilities.Build(statNames, " "); } //then make sure the names and order of the accepted stats are the same as the new ones. if (StringUtilities.Build(statNames, " ") != AcceptedStatsName) { throw new ApsimXException(this, "Names, number or order of accepted stats do not match class MathUtilities.RegrStats. The class has probably changed."); } Table = new DataTable("StatTests"); Table.Columns.Add("Name", typeof(string)); Table.Columns.Add("Variable", typeof(string)); Table.Columns.Add("Test", typeof(string)); Table.Columns.Add("Accepted", typeof(double)); Table.Columns.Add("Current", typeof(double)); Table.Columns.Add("Difference", typeof(double)); Table.Columns.Add("Fail?", typeof(string)); double accepted; double current; DataTable AcceptedTable = Table.Copy(); DataTable CurrentTable = Table.Copy(); //accepted table for (int i = 0; i < AcceptedStats.Count(); i++) { for (int j = 1; j < statNames.Count; j++) //start at 1; we don't want Name field. { accepted = Convert.ToDouble(AcceptedStats[i].GetType().GetField(statNames[j]).GetValue(AcceptedStats[i])); AcceptedTable.Rows.Add(PO.Name, AcceptedStats[i].Name, statNames[j], accepted, null, null, null); } } //current table Table = AcceptedTable.Copy(); int rowIndex = 0; for (int i = 0; i < stats.Count(); i++) { for (int j = 1; j < statNames.Count; j++) //start at 1; we don't want Name field. { current = Convert.ToDouble(stats[i].GetType().GetField(statNames[j]).GetValue(stats[i])); CurrentTable.Rows.Add(PO.Name, stats[i].Name, statNames[j], null, current, null, null); Table.Rows[rowIndex]["Current"] = current; rowIndex++; } } //Merge overwrites rows, so add the correct data back in foreach (DataRow row in Table.Rows) { DataRow[] rowAccepted = AcceptedTable.Select("Name = '" + row["Name"] + "' AND Variable = '" + row["Variable"] + "' AND Test = '" + row["Test"] + "'"); DataRow[] rowCurrent = CurrentTable.Select("Name = '" + row["Name"] + "' AND Variable = '" + row["Variable"] + "' AND Test = '" + row["Test"] + "'"); if (rowAccepted.Count() == 0) { row["Accepted"] = DBNull.Value; } else { row["Accepted"] = rowAccepted[0]["Accepted"]; } if (rowCurrent.Count() == 0) { row["Current"] = DBNull.Value; } else { row["Current"] = rowCurrent[0]["Current"]; } if (row["Accepted"] != DBNull.Value && row["Current"] != DBNull.Value) { row["Difference"] = Convert.ToDouble(row["Current"]) - Convert.ToDouble(row["Accepted"]); row["Fail?"] = Math.Abs(Convert.ToDouble(row["Difference"])) > Math.Abs(Convert.ToDouble(row["Accepted"])) * 0.01 ? sigIdent : " "; } else { row["Difference"] = DBNull.Value; row["Fail?"] = sigIdent; } } //Tables could be large so free the memory. AcceptedTable = null; CurrentTable = null; if (accept) { AcceptedStats = stats; } else { foreach (DataRow row in Table.Rows) { if (row["Fail?"].ToString().Equals(sigIdent)) { if (!GUIrun) { object sim = PO.Parent; while (sim as Simulations == null) { sim = ((Model)sim).Parent; } throw new ApsimXException(this, "Significant differences found during regression testing of " + PO.Name + " in " + (sim != null ? ((Simulations)sim).FileName : "<unknown>")); } } } } }
/// <summary> /// Write the summary report to the specified writer. /// </summary> /// <param name="dataStore">The data store to write a summary report from</param> /// <param name="simulationName">The simulation name to produce a summary report for</param> /// <param name="writer">Text writer to write to</param> /// <param name="apsimSummaryImageFileName">The file name for the logo. Can be null</param> /// <param name="outtype">Indicates the format to be produced</param> public static void WriteReport( DataStore dataStore, string simulationName, TextWriter writer, string apsimSummaryImageFileName, OutputType outtype) { Document document = null; RtfDocumentRenderer renderer = null; if (outtype == OutputType.html) { writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<html>"); writer.WriteLine("<head>"); writer.WriteLine("<meta content='text/html; charset=UTF-8; http-equiv='content-type'>"); writer.WriteLine("<style>"); writer.WriteLine("h2 { color:darkblue; } "); writer.WriteLine("h3 { color:darkblue; } "); writer.WriteLine("table { border:1px solid black; border-collapse:collapse; width:100%; table-layout:fixed; text-align:left; }"); writer.WriteLine("table.headered {text-align:right; }"); writer.WriteLine("tr.total { color:darkorange; font-weight:bold; }"); writer.WriteLine("table.headered td.col1 { text-align:left; font-weight:bold; }"); writer.WriteLine("td { border:1px solid; }"); writer.WriteLine("th { border:1px solid; text-align:right; background-color: palegoldenrod}"); writer.WriteLine("th.col1 { text-align:left; }"); writer.WriteLine("</style>"); writer.WriteLine("</head>"); writer.WriteLine("<body>"); } else if (outtype == OutputType.rtf) { document = new Document(); renderer = new RtfDocumentRenderer(); // Get the predefined style Normal. Style style = document.Styles["Normal"]; // Because all styles are derived from Normal, the next line changes the // font of the whole document. Or, more exactly, it changes the font of // all styles and paragraphs that do not redefine the font. style.Font.Name = "Arial"; // Heading1 to Heading9 are predefined styles with an outline level. An outline level // other than OutlineLevel.BodyText automatically creates the outline (or bookmarks) // in PDF. style = document.Styles["Heading2"]; style.Font.Size = 14; style.Font.Bold = true; style.Font.Color = Colors.DarkBlue; style.ParagraphFormat.PageBreakBefore = false; style.ParagraphFormat.SpaceAfter = 3; style.ParagraphFormat.SpaceBefore = 16; style = document.Styles["Heading3"]; style.Font.Size = 12; style.Font.Bold = true; style.Font.Color = Colors.DarkBlue; style.ParagraphFormat.SpaceBefore = 10; style.ParagraphFormat.SpaceAfter = 2; // Create a new style called Monospace based on style Normal style = document.Styles.AddStyle("Monospace", "Normal"); System.Drawing.FontFamily monoFamily = new System.Drawing.FontFamily(System.Drawing.Text.GenericFontFamilies.Monospace); style.Font.Name = monoFamily.Name; Section section = document.AddSection(); } // Get the initial conditions table. DataTable initialConditionsTable = dataStore.GetData(simulationName, "InitialConditions"); if (initialConditionsTable != null) { // Convert the 'InitialConditions' table in the DataStore to a series of // DataTables for each model. List <DataTable> tables = new List <DataTable>(); ConvertInitialConditionsToTables(initialConditionsTable, tables); // Now write all tables to our report. for (int i = 0; i < tables.Count; i += 2) { // Only write something to the summary file if we have something to write. if (tables[i].Rows.Count > 0 || tables[i + 1].Rows.Count > 0) { string heading = tables[i].TableName; WriteHeading(writer, heading, outtype, document); // Write the manager script. if (tables[i].Rows.Count == 1 && tables[i].Rows[0][0].ToString() == "Script code: ") { WriteScript(writer, tables[i].Rows[0], outtype, document); } else { // Write the properties table if we have any properties. if (tables[i].Rows.Count > 0) { WriteTable(writer, tables[i], outtype, "PropertyTable", document); } // Write the general data table if we have any data. if (tables[i + 1].Rows.Count > 0) { WriteTable(writer, tables[i + 1], outtype, "ApsimTable", document); } } if (outtype == OutputType.html) { writer.WriteLine("<br/>"); } } } } // Write out all messages. WriteHeading(writer, "Simulation log:", outtype, document); DataTable messageTable = GetMessageTable(dataStore, simulationName); WriteMessageTable(writer, messageTable, outtype, false, "MessageTable", document); if (outtype == OutputType.html) { writer.WriteLine("</body>"); writer.WriteLine("</html>"); } else if (outtype == OutputType.rtf) { string rtf = renderer.RenderToString(document, Path.GetTempPath()); writer.Write(rtf); } }
/// <summary> /// Write the summary report to the specified writer. /// </summary> /// <param name="dataStore">The data store to write a summary report from</param> /// <param name="simulationName">The simulation name to produce a summary report for</param> /// <param name="writer">Text writer to write to</param> /// <param name="apsimSummaryImageFileName">The file name for the logo. Can be null</param> /// <param name="html">Indicates whether to produce html format</param> public static void WriteReport( DataStore dataStore, string simulationName, TextWriter writer, string apsimSummaryImageFileName, bool html) { if (html) { writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<!DOCTYPE html PUBLIC \" -//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"); writer.WriteLine("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">"); writer.WriteLine("<head><title>Summary file</title>"); writer.WriteLine("<style type=\"text/css\">"); writer.WriteLine("table.ApsimTable {font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333333;border-width: 1px;border-color: #729ea5;border-collapse: collapse;text-align:right;table-layout:auto;width: 50%;whitespace:nowrap}"); writer.WriteLine("table.ApsimTable th {font-family:Arial,Helvetica,sans-serif;font-size:14px;background-color:#acc8cc;border-width: 1px;padding: 8px;border-style: solid;border-color: #729ea5;text-align:right}"); writer.WriteLine("table.ApsimTable tr {font-family:Arial,Helvetica,sans-serif;vertical-align:top;background-color:#d4e3e5;}"); writer.WriteLine("table.ApsimTable td {font-family:Arial,Helvetica,sans-serif;font-size:14px;border-width: 1px;padding: 8px;border-style: solid;border-color: #729ea5;}"); writer.WriteLine("table.PropertyTable {font-family:Arial,Helvetica,sans-serif;font-size:14px;border-width: 0px;table-layout:fixed;width: 50%}"); writer.WriteLine("table.PropertyTable th {font-family:Arial,Helvetica,sans-serif;font-size:14px;border-width: 0px;}"); writer.WriteLine("table.PropertyTable tr {\r\n" + " font-family:Arial,Helvetica,sans-serif;\r\n" + " vertical-align:middle;\r\n" + " padding: 0px 0px 0px 0px;\r\n" + "}"); writer.WriteLine("table.PropertyTable td {font-family:Arial,Helvetica,sans-serif;font-size:14px;border-width: 0px;}"); writer.WriteLine("table.MessageTable {\r\n" + " font-family:Arial,Helvetica,sans-serif;\r\n" + " font-size:14px;\r\n" + " width: 100%;\r\n" + " table-layout: fixed;\r\n" + " border-width: 1px;}"); writer.WriteLine("table.MessageTable th {font-family:Arial,Helvetica,sans-serif;font-size:14px;border-width: 1px;}"); writer.WriteLine("table.MessageTable tr {\r\n" + " font-family:Arial,Helvetica,sans-serif;\r\n" + " vertical-align:middle;\r\n" + " padding: 0px 0px 0px 0px;\r\n" + "}"); writer.WriteLine("table.MessageTable td {\r\n" + " font-family:Arial,Helvetica,sans-serif;font-size:14px;\r\n" + " border-width: 1px;\r\n" + "}"); writer.WriteLine("td.col1 {\r\n" + " width: 30%;\r\n" + "}"); writer.WriteLine("td.col2 {\r\n" + " width: 70%;\r\n" + "}"); writer.WriteLine("p.Warning {font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#FF6600;}"); writer.WriteLine("p.Error {font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#FF0000;}"); writer.WriteLine("tr.TitleRow {font-family:Arial,Helvetica,sans-serif;font-size:14px;color:red;font-weight:bold;}"); writer.WriteLine("</style>"); writer.WriteLine("</head>"); writer.WriteLine("<body>"); if (apsimSummaryImageFileName != null) { writer.WriteLine("<p><img src=\"" + apsimSummaryImageFileName + "\" alt=\"logo\"></img></p>"); } } // Get the initial conditions table. DataTable initialConditionsTable = dataStore.GetData(simulationName, "InitialConditions"); if (initialConditionsTable != null) { // Convert the 'InitialConditions' table in the DataStore to a series of // DataTables for each model. List <DataTable> tables = new List <DataTable>(); ConvertInitialConditionsToTables(initialConditionsTable, tables); // Now write all tables to our report. for (int i = 0; i < tables.Count; i += 2) { // Only write something to the summary file if we have something to write. if (tables[i].Rows.Count > 0 || tables[i + 1].Rows.Count > 0) { string heading = tables[i].TableName; WriteHeading(writer, heading, html); // Write the manager script. if (tables[i].Rows.Count == 1 && tables[i].Rows[0][0].ToString() == "Script code: ") { WriteScript(writer, tables[i].Rows[0], html); } else { // Write the properties table if we have any properties. if (tables[i].Rows.Count > 0) { WriteTable(writer, tables[i], html, includeHeadings: false, className: "PropertyTable"); } // Write the general data table if we have any data. if (tables[i + 1].Rows.Count > 0) { WriteTable(writer, tables[i + 1], html, includeHeadings: true, className: "ApsimTable"); } } } } } // Write out all messages. WriteHeading(writer, "Simulation log:", html); DataTable messageTable = GetMessageTable(dataStore, simulationName); WriteMessageTable(writer, messageTable, html, false, "MessageTable"); if (html) { writer.WriteLine("</body>"); writer.WriteLine("</html>"); } }
/// <summary> /// Write the summary report to the specified writer. /// </summary> /// <param name="dataStore">The data store to write a summary report from</param> /// <param name="simulationName">The simulation name to produce a summary report for</param> /// <param name="writer">Text writer to write to</param> /// <param name="apsimSummaryImageFileName">The file name for the logo. Can be null</param> /// <param name="html">Indicates whether to produce html format</param> public static void WriteReport( DataStore dataStore, string simulationName, TextWriter writer, string apsimSummaryImageFileName, bool html) { if (html) { writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<html>"); writer.WriteLine("<body>"); } // Get the initial conditions table. DataTable initialConditionsTable = dataStore.GetData(simulationName, "InitialConditions"); if (initialConditionsTable != null) { // Convert the 'InitialConditions' table in the DataStore to a series of // DataTables for each model. List <DataTable> tables = new List <DataTable>(); ConvertInitialConditionsToTables(initialConditionsTable, tables); // Now write all tables to our report. for (int i = 0; i < tables.Count; i += 2) { // Only write something to the summary file if we have something to write. if (tables[i].Rows.Count > 0 || tables[i + 1].Rows.Count > 0) { string heading = tables[i].TableName; WriteHeading(writer, heading, html); // Write the manager script. if (tables[i].Rows.Count == 1 && tables[i].Rows[0][0].ToString() == "Script code: ") { WriteScript(writer, tables[i].Rows[0], html); } else { // Write the properties table if we have any properties. if (tables[i].Rows.Count > 0) { WriteTable(writer, tables[i], html, includeHeadings: false, className: "PropertyTable"); } // Write the general data table if we have any data. if (tables[i + 1].Rows.Count > 0) { WriteTable(writer, tables[i + 1], html, includeHeadings: true, className: "ApsimTable"); } } writer.WriteLine("<br/>"); } } } // Write out all messages. WriteHeading(writer, "Simulation log:", html); DataTable messageTable = GetMessageTable(dataStore, simulationName); WriteMessageTable(writer, messageTable, html, false, "MessageTable"); if (html) { writer.WriteLine("</body>"); writer.WriteLine("</html>"); } }