[Stats] Misc. cleanup and Stats overhaul

This commit is contained in:
Matt Nadareski
2016-09-26 14:38:05 -07:00
parent fa5e87a9e1
commit e0c3623cbb
7 changed files with 190 additions and 200 deletions

View File

@@ -143,9 +143,11 @@ namespace SabreTools.Helper
helptext.Add(" -out= Output directory"); helptext.Add(" -out= Output directory");
helptext.Add(" -hs, --hash-split Split a DAT or folder by best-available hashes"); helptext.Add(" -hs, --hash-split Split a DAT or folder by best-available hashes");
helptext.Add(" -out= Output directory"); helptext.Add(" -out= Output directory");
helptext.Add(" -html, --html Write statistics on all input DATs to HTML");
helptext.Add(" -st, --stats Get statistics on all input DATs"); helptext.Add(" -st, --stats Get statistics on all input DATs");
helptext.Add(" -csv, --csv Output in Comma-Separated Value format");
helptext.Add(" -html, --html Write stats to HTML");
helptext.Add(" -si, --single Show individual statistics"); helptext.Add(" -si, --single Show individual statistics");
helptext.Add(" -tsv, --tsv Output in Tab-Separated Value format");
helptext.Add(" -ts, --type-split Split a DAT or folder by file types (rom/disk)"); helptext.Add(" -ts, --type-split Split a DAT or folder by file types (rom/disk)");
helptext.Add(" -out= Output directory"); helptext.Add(" -out= Output directory");
helptext.Add(" -ud, --update Update a DAT file"); helptext.Add(" -ud, --update Update a DAT file");

View File

@@ -1,26 +1,5 @@
namespace SabreTools.Helper namespace SabreTools.Helper
{ {
#region DATabase
/// <summary>
/// Possible DAT import classes
/// </summary>
public enum DatType
{
none = 0,
Custom,
MAME,
NoIntro,
Redump,
TOSEC,
TruRip,
NonGood,
MaybeIntro,
Good,
}
#endregion
#region DatFile related #region DatFile related
/// <summary> /// <summary>
@@ -97,6 +76,17 @@
NotNodump = 5, // This is a fake flag that is used for filter only NotNodump = 5, // This is a fake flag that is used for filter only
} }
/// <summary>
/// Determine which format to output Stats to
/// </summary>
public enum StatOutputFormat
{
None = 0,
HTML = 1,
CSV = 2,
TSV = 3,
}
#endregion #endregion
#region Skippers and Mappers #region Skippers and Mappers

View File

@@ -3003,7 +3003,9 @@ namespace SabreTools.Helper
// Output initial statistics, for kicks // Output initial statistics, for kicks
if (stats) if (stats)
{ {
OutputStats(logger, logger, (RomCount + DiskCount == 0)); StreamWriter sw = new StreamWriter(new MemoryStream());
OutputStats(sw, StatOutputFormat.None, logger, (RomCount + DiskCount == 0));
sw.Dispose();
} }
// Bucket roms by game name and optionally dedupe // Bucket roms by game name and optionally dedupe
@@ -4335,11 +4337,12 @@ namespace SabreTools.Helper
/// <summary> /// <summary>
/// Output the stats for the Dat in a human-readable format /// Output the stats for the Dat in a human-readable format
/// </summary> /// </summary>
/// <param name="sw">StreamWriter representing the output file or stream for the statistics</param>
/// <param name="statOutputFormat">Set the statistics output format to use</param>
/// <param name="logger">Logger object for file and console writing</param> /// <param name="logger">Logger object for file and console writing</param>
/// <param name="statLogger">Logger object for file and console output (statistics)</param>
/// <param name="recalculate">True if numbers should be recalculated for the DAT, false otherwise (default)</param> /// <param name="recalculate">True if numbers should be recalculated for the DAT, false otherwise (default)</param>
/// <param name="game">Number of games to use, -1 means recalculate games (default)</param> /// <param name="game">Number of games to use, -1 means recalculate games (default)</param>
public void OutputStats(Logger logger, Logger statLogger, bool recalculate = false, long game = -1) public void OutputStats(StreamWriter sw, StatOutputFormat statOutputFormat, Logger logger, bool recalculate = false, long game = -1)
{ {
// If we're supposed to recalculate the statistics, do so // If we're supposed to recalculate the statistics, do so
if (recalculate) if (recalculate)
@@ -4352,7 +4355,11 @@ namespace SabreTools.Helper
{ {
TotalSize = Int64.MaxValue + TotalSize; TotalSize = Int64.MaxValue + TotalSize;
} }
statLogger.User(" Uncompressed size: " + Style.GetBytesReadable(TotalSize) + @"
// Log the results to screen
logger.User(@"For file '" + FileName + @"':
--------------------------------------------------
Uncompressed size: " + Style.GetBytesReadable(TotalSize) + @"
Games found: " + (game == -1 ? Files.Count : game) + @" Games found: " + (game == -1 ? Files.Count : game) + @"
Roms found: " + RomCount + @" Roms found: " + RomCount + @"
Disks found: " + DiskCount + @" Disks found: " + DiskCount + @"
@@ -4360,39 +4367,66 @@ namespace SabreTools.Helper
Roms with MD5: " + MD5Count + @" Roms with MD5: " + MD5Count + @"
Roms with SHA-1: " + SHA1Count + @" Roms with SHA-1: " + SHA1Count + @"
Roms with Nodump status: " + NodumpCount + @" Roms with Nodump status: " + NodumpCount + @"
", false);
}
/// <summary> ");
/// Output the stats for the Dat in a human-readable HTML format
/// </summary> // Now write it out to file as well
/// <param name="sw">StreamWriter representing the output file or stream for the statistics</param> string line = "";
/// <param name="logger">Logger object for file and console writing</param> switch (statOutputFormat)
/// <param name="recalculate">True if numbers should be recalculated for the DAT, false otherwise (default)</param>
/// <param name="game">Number of games to use, -1 means recalculate games (default)</param>
public void OutputHTMLStats(StreamWriter sw, Logger logger, bool recalculate = false, long game = -1)
{
// If we're supposed to recalculate the statistics, do so
if (recalculate)
{ {
RecalculateStats(); case StatOutputFormat.CSV:
line = "\"" + FileName + "\","
+ "\"" + Style.GetBytesReadable(TotalSize) + "\","
+ "\"" + (game == -1 ? Files.Count : game) + "\","
+ "\"" + RomCount + "\","
+ "\"" + DiskCount + "\","
+ "\"" + CRCCount + "\","
+ "\"" + MD5Count + "\","
+ "\"" + SHA1Count + "\","
+ "\"" + NodumpCount + "\"\n";
break;
case StatOutputFormat.HTML:
line = "\t\t\t<tr><td>" + HttpUtility.HtmlEncode(FileName) + "</td>"
+ "<td>" + Style.GetBytesReadable(TotalSize) + "</td>"
+ "<td>" + (game == -1 ? Files.Count : game) + "</td>"
+ "<td>" + RomCount + "</td>"
+ "<td>" + DiskCount + "</td>"
+ "<td>" + CRCCount + "</td>"
+ "<td>" + MD5Count + "</td>"
+ "<td>" + SHA1Count + "</td>"
+ "<td>" + NodumpCount + "</td>"
+ "</tr>\n";
break;
case StatOutputFormat.None:
default:
line = @"For file '" + FileName + @"':
--------------------------------------------------
Uncompressed size: " + Style.GetBytesReadable(TotalSize) + @"
Games found: " + (game == -1 ? Files.Count : game) + @"
Roms found: " + RomCount + @"
Disks found: " + DiskCount + @"
Roms with CRC: " + CRCCount + @"
Roms with MD5: " + MD5Count + @"
Roms with SHA-1: " + SHA1Count + @"
Roms with Nodump status: " + NodumpCount + @"
";
break;
case StatOutputFormat.TSV:
line = "\"" + FileName + "\"\t"
+ "\"" + Style.GetBytesReadable(TotalSize) + "\"\t"
+ "\"" + (game == -1 ? Files.Count : game) + "\"\t"
+ "\"" + RomCount + "\"\t"
+ "\"" + DiskCount + "\"\t"
+ "\"" + CRCCount + "\"\t"
+ "\"" + MD5Count + "\"\t"
+ "\"" + SHA1Count + "\"\t"
+ "\"" + NodumpCount + "\"\n";
break;
} }
BucketByGame(false, true, logger, false); // Output the line to the streamwriter
if (TotalSize < 0) sw.Write(line);
{
TotalSize = Int64.MaxValue + TotalSize;
}
sw.Write("\t\t\t<tr><td>" + HttpUtility.HtmlEncode(FileName) + "</td>"
+ "<td>" + Style.GetBytesReadable(TotalSize) + "</td>"
+ "<td>" + (game == -1 ? Files.Count : game) + "</td>"
+ "<td>" + RomCount + "</td>"
+ "<td>" + DiskCount + "</td>"
+ "<td>" + CRCCount + "</td>"
+ "<td>" + MD5Count + "</td>"
+ "<td>" + SHA1Count + "</td>"
+ "<td>" + NodumpCount + "</td>"
+ "</tr>\n");
} }
#endregion #endregion
@@ -5057,99 +5091,31 @@ namespace SabreTools.Helper
/// Output the stats for a list of input dats as files in a human-readable format /// Output the stats for a list of input dats as files in a human-readable format
/// </summary> /// </summary>
/// <param name="inputs">List of input files and folders</param> /// <param name="inputs">List of input files and folders</param>
/// <param name="reportName">Name of the output file</param>
/// <param name="single">True if single DAT stats are output, false otherwise</param> /// <param name="single">True if single DAT stats are output, false otherwise</param>
/// <param name="statOutputFormat" > Set the statistics output format to use</param>
/// <param name="logger">Logger object for file and console output</param> /// <param name="logger">Logger object for file and console output</param>
/// <param name="statLogger">Logger object for file and console output (statistics)</param> public static void OutputStats(List<string> inputs, string reportName, bool single, StatOutputFormat statOutputFormat, Logger logger)
public static void OutputStats(List<string> inputs, bool single, Logger logger, Logger statLogger)
{ {
// Make sure we have all files string reportExtension = "";
List<string> newinputs = new List<string>(); switch (statOutputFormat)
foreach (string input in inputs)
{ {
if (File.Exists(input)) case StatOutputFormat.CSV:
{ reportExtension = ".csv";
newinputs.Add(input); break;
} case StatOutputFormat.HTML:
if (Directory.Exists(input)) reportExtension = ".html";
{ break;
foreach (string file in Directory.GetFiles(input, "*", SearchOption.AllDirectories)) case StatOutputFormat.None:
{ default:
newinputs.Add(file); reportExtension = ".txt";
} break;
} case StatOutputFormat.TSV:
reportExtension = ".csv";
break;
} }
reportName += reportExtension;
// Init all total variables StreamWriter sw = new StreamWriter(File.OpenWrite(reportName));
long totalSize = 0;
long totalGame = 0;
long totalRom = 0;
long totalDisk = 0;
long totalCRC = 0;
long totalMD5 = 0;
long totalSHA1 = 0;
long totalNodump = 0;
/// Now process each of the input files
foreach (string filename in newinputs)
{
logger.Verbose("Beginning stat collection for '" + filename + "'", false);
List<string> games = new List<string>();
DatFile datdata = new DatFile();
datdata.Parse(filename, 0, 0, logger);
datdata.BucketByGame(false, true, logger, false);
// Output single DAT stats (if asked)
if (single)
{
statLogger.User(@"\nFor file '" + filename + @"':
--------------------------------------------------", false);
datdata.OutputStats(logger, statLogger);
}
else
{
logger.User("Adding stats for file '" + filename + "'\n", false);
}
// Add single DAT stats to totals
totalSize += datdata.TotalSize;
totalGame += datdata.Files.Count;
totalRom += datdata.RomCount;
totalDisk += datdata.DiskCount;
totalCRC += datdata.CRCCount;
totalMD5 += datdata.MD5Count;
totalSHA1 += datdata.SHA1Count;
totalNodump += datdata.NodumpCount;
}
// Output total DAT stats
if (!single) { logger.User("", false); }
DatFile totaldata = new DatFile
{
TotalSize = totalSize,
RomCount = totalRom,
DiskCount = totalDisk,
CRCCount = totalCRC,
MD5Count = totalMD5,
SHA1Count = totalSHA1,
NodumpCount = totalNodump,
};
statLogger.User(@"For ALL DATs found
--------------------------------------------------", false);
totaldata.OutputStats(logger, statLogger, game: totalGame);
logger.User(@"
Please check the log folder if the stats scrolled offscreen", false);
}
/// <summary>
/// Output the stats for a list of input dats as files in a human-readable HTML format
/// </summary>
/// <param name="inputs">List of input files and folders</param>
/// <param name="single">True if single DAT stats are output, false otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <param name="statLogger">Logger object for file and console output (statistics)</param>
public static void OutputHTMLStats(List<string> inputs, Logger logger)
{
StreamWriter sw = new StreamWriter(File.OpenWrite("report.html"));
// Make sure we have all files // Make sure we have all files
List<string> newinputs = new List<string>(); List<string> newinputs = new List<string>();
@@ -5168,17 +5134,6 @@ Please check the log folder if the stats scrolled offscreen", false);
} }
} }
// Write the HTML header
sw.Write(@"<!DOCTYPE html>
<html>
<header>
<title>DAT Statistics Report</title>
</header>
<body>
<table border=""1"" cellpadding=""0"" cellspacing=""0"">
<tr><th>File Name</th><th>Total Size</th><th>Games</th><th>Roms</th><th>Disks</th><th>&#35; with CRC</th>"
+ "<th>&#35; with MD5</th><th>&#35; with SHA-1</th><th>Nodumps</th></tr>\n");
// Init all total variables // Init all total variables
long totalSize = 0; long totalSize = 0;
long totalGame = 0; long totalGame = 0;
@@ -5200,7 +5155,36 @@ Please check the log folder if the stats scrolled offscreen", false);
// Output single DAT stats (if asked) // Output single DAT stats (if asked)
logger.User("Adding stats for file '" + filename + "'\n", false); logger.User("Adding stats for file '" + filename + "'\n", false);
datdata.OutputHTMLStats(sw, logger); if (single)
{
string line = "";
switch (statOutputFormat)
{
case StatOutputFormat.CSV:
line = "\"File Name\",\"Total Size\",\"Games\",\"Roms\",\"Disks\",\"# with CRC\",\"# with MD5\",\"# with SHA-1\",\"Nodumps\"\n";
break;
case StatOutputFormat.HTML:
line = @"<!DOCTYPE html>
<html>
<header>
<title>DAT Statistics Report</title>
</header>
<body>
<table border=""1"" cellpadding=""5"" cellspacing=""0"">
<tr><th>File Name</th><th>Total Size</th><th>Games</th><th>Roms</th><th>Disks</th><th>&#35; with CRC</th>"
+ "<th>&#35; with MD5</th><th>&#35; with SHA-1</th><th>Nodumps</th></tr>\n";
break;
case StatOutputFormat.None:
default:
break;
case StatOutputFormat.TSV:
line = "\"File Name\"\t\"Total Size\"\t\"Games\"\t\"Roms\"\t\"Disks\"\t\"# with CRC\"\t\"# with MD5\"\t\"# with SHA-1\"\t\"Nodumps\"\n";
break;
}
sw.Write(line);
datdata.OutputStats(sw, statOutputFormat, logger);
}
// Add single DAT stats to totals // Add single DAT stats to totals
totalSize += datdata.TotalSize; totalSize += datdata.TotalSize;
@@ -5213,12 +5197,25 @@ Please check the log folder if the stats scrolled offscreen", false);
totalNodump += datdata.NodumpCount; totalNodump += datdata.NodumpCount;
} }
sw.Write("<tr><td colspan=\"9\"></td></tr>"); // Output midpoint separator if needed
string mid = "";
switch (statOutputFormat)
{
case StatOutputFormat.CSV:
break;
case StatOutputFormat.HTML:
mid = "<tr><td colspan=\"9\"></td></tr>\n";
break;
case StatOutputFormat.None:
default:
break;
}
sw.Write(mid);
// Output total DAT stats // Output total DAT stats
DatFile totaldata = new DatFile DatFile totaldata = new DatFile
{ {
FileName = "Totals", FileName = "ALL",
TotalSize = totalSize, TotalSize = totalSize,
RomCount = totalRom, RomCount = totalRom,
DiskCount = totalDisk, DiskCount = totalDisk,
@@ -5227,15 +5224,31 @@ Please check the log folder if the stats scrolled offscreen", false);
SHA1Count = totalSHA1, SHA1Count = totalSHA1,
NodumpCount = totalNodump, NodumpCount = totalNodump,
}; };
totaldata.OutputHTMLStats(sw, logger, game: totalGame); totaldata.OutputStats(sw, statOutputFormat, logger, game: totalGame);
// Write HTML footer // Output footer if needed
sw.Write(@" </table> string end = "";
switch (statOutputFormat)
{
case StatOutputFormat.CSV:
break;
case StatOutputFormat.HTML:
end = @" </table>
</body> </body>
</html> </html>
"); ";
break;
case StatOutputFormat.None:
default:
break;
}
sw.Write(end);
sw.Flush(); sw.Flush();
sw.Dispose(); sw.Dispose();
logger.User(@"
Please check the log folder if the stats scrolled offscreen", false);
} }
#endregion #endregion

View File

@@ -262,7 +262,9 @@ namespace SabreTools.Helper
_logger.ClearBeneath(Constants.HeaderHeight); _logger.ClearBeneath(Constants.HeaderHeight);
Console.SetCursorPosition(0, Constants.HeaderHeight + 1); Console.SetCursorPosition(0, Constants.HeaderHeight + 1);
_logger.User("Stats of the matched ROMs:"); _logger.User("Stats of the matched ROMs:");
_matched.OutputStats(_logger, _logger, true); StreamWriter sw = new StreamWriter(new MemoryStream());
_matched.OutputStats(sw, StatOutputFormat.None, _logger, true);
sw.Dispose();
// Now output the fixdat based on the original input if asked // Now output the fixdat based on the original input if asked
if (_updateDat) if (_updateDat)

View File

@@ -310,18 +310,6 @@ Options:
This should only be used if one of the inputs starts with a flag or another already This should only be used if one of the inputs starts with a flag or another already
defined input. defined input.
-html, --html Get statistics on all input DATs written to HTML
This will output by default the combined statistics for all input DAT files. The stats
that are outputted are as follows:
- Total uncompressed size
- Number of games found
- Number of roms found
- Number of disks found
- Roms that include a CRC
- Roms that include a MD5
- Roms that include a SHA-1
- Roms with Nodump status
-st, --stats Get statistics on all input DATs -st, --stats Get statistics on all input DATs
This will output by default the combined statistics for all input DAT files. The stats This will output by default the combined statistics for all input DAT files. The stats
that are outputted are as follows: that are outputted are as follows:
@@ -334,11 +322,20 @@ Options:
- Roms that include a SHA-1 - Roms that include a SHA-1
- Roms with Nodump status - Roms with Nodump status
-csv, --csv Write all statistics to CSV
Output all rom information in standardized CSV format
-html, --html Write all statistics to HTML
This will output by default the combined statistics for all input DAT files.
-si, --single Show individual statistics -si, --single Show individual statistics
Optionally, the statistics for each of the individual input DATs can be output Optionally, the statistics for each of the individual input DATs can be output
as well. This can be useful to show where the size or amount of files found as well. This can be useful to show where the size or amount of files found
in the combined totals can be broken down from. in the combined totals can be broken down from.
-tsv, --tsv Output in Tab-Separated Value format
Output all rom information in standardized TSV format
-ts, --type-split Split a DAT or folder by file types (rom/disk) -ts, --type-split Split a DAT or folder by file types (rom/disk)
For a DAT, or set of DATs, allow for splitting based on the types of the files, For a DAT, or set of DATs, allow for splitting based on the types of the files,
specifically if the type is a rom or a disk. specifically if the type is a rom or a disk.

View File

@@ -245,15 +245,6 @@ namespace SabreTools
} }
} }
/// <summary>
/// Wrap getting statistics on a DAT or folder of DATs to HTML
/// </summary>
/// <param name="inputs">List of inputs to be used</param>
private static void InitHTMLStats(List<string> inputs)
{
DatFile.OutputHTMLStats(inputs, _logger);
}
/// <summary> /// <summary>
/// Wrap sorting files using an input DAT /// Wrap sorting files using an input DAT
/// </summary> /// </summary>
@@ -293,11 +284,10 @@ namespace SabreTools
/// </summary> /// </summary>
/// <param name="inputs">List of inputs to be used</param> /// <param name="inputs">List of inputs to be used</param>
/// <param name="single">True to show individual DAT statistics, false otherwise</param> /// <param name="single">True to show individual DAT statistics, false otherwise</param>
private static void InitStats(List<string> inputs, bool single) /// <param name="statOutputFormat">Set the statistics output format to use</param>
private static void InitStats(List<string> inputs, bool single, StatOutputFormat statOutputFormat)
{ {
Logger statlog = new Logger(true, "stats.txt"); DatFile.OutputStats(inputs, "report", single, statOutputFormat, _logger);
DatFile.OutputStats(inputs, single, _logger, statlog);
statlog.Close(true);
} }
/// <summary> /// <summary>

View File

@@ -62,7 +62,6 @@ namespace SabreTools
forceunpack = false, forceunpack = false,
hashsplit = false, hashsplit = false,
headerer = false, headerer = false,
html = false,
inplace = false, inplace = false,
merge = false, merge = false,
noMD5 = false, noMD5 = false,
@@ -89,6 +88,7 @@ namespace SabreTools
slt = -1, slt = -1,
seq = -1; seq = -1;
OutputFormat outputFormat = 0x0; OutputFormat outputFormat = 0x0;
StatOutputFormat statOutputFormat = StatOutputFormat.None;
string addext = "", string addext = "",
author = "", author = "",
category = "", category = "",
@@ -159,6 +159,7 @@ namespace SabreTools
case "-csv": case "-csv":
case "--csv": case "--csv":
tsv = false; tsv = false;
statOutputFormat = StatOutputFormat.CSV;
break; break;
case "-clean": case "-clean":
case "--clean": case "--clean":
@@ -215,7 +216,7 @@ namespace SabreTools
break; break;
case "-html": case "-html":
case "--html": case "--html":
html = true; statOutputFormat = StatOutputFormat.HTML;
break; break;
case "-ip": case "-ip":
case "--inplace": case "--inplace":
@@ -324,6 +325,7 @@ namespace SabreTools
case "-tsv": case "-tsv":
case "--tsv": case "--tsv":
tsv = true; tsv = true;
statOutputFormat = StatOutputFormat.TSV;
break; break;
case "-u": case "-u":
case "--unzip": case "--unzip":
@@ -523,8 +525,8 @@ namespace SabreTools
} }
// If more than one switch is enabled, show the help screen // If more than one switch is enabled, show the help screen
if (!(extsplit ^ hashsplit ^ headerer ^ html ^ (datfromdir || merge || diffMode != 0 || update if (!(extsplit ^ hashsplit ^ headerer ^ (datfromdir || merge || diffMode != 0 || update
|| outputFormat != 0 || tsv != null|| trim) ^ rem ^ stats ^ typesplit)) || outputFormat != 0 || trim) ^ rem ^ stats ^ typesplit))
{ {
_logger.Error("Only one feature switch is allowed at a time"); _logger.Error("Only one feature switch is allowed at a time");
Build.Help(); Build.Help();
@@ -533,8 +535,8 @@ namespace SabreTools
} }
// If a switch that requires a filename is set and no file is, show the help screen // If a switch that requires a filename is set and no file is, show the help screen
if (inputs.Count == 0 && (datfromdir || extsplit || hashsplit || headerer || html if (inputs.Count == 0 && (datfromdir || extsplit || hashsplit || headerer
|| (merge || diffMode != 0 || update || outputFormat != 0 || tsv != null) || stats || trim || typesplit)) || (merge || diffMode != 0 || update || outputFormat != 0) || stats || trim || typesplit))
{ {
_logger.Error("This feature requires at least one input"); _logger.Error("This feature requires at least one input");
Build.Help(); Build.Help();
@@ -569,16 +571,10 @@ namespace SabreTools
InitHeaderer(inputs, restore, outDir); InitHeaderer(inputs, restore, outDir);
} }
// Get statistics on input files to HTML
else if (html)
{
InitHTMLStats(inputs);
}
// Get statistics on input files // Get statistics on input files
else if (stats) else if (stats)
{ {
InitStats(inputs, single); InitStats(inputs, single, statOutputFormat);
} }
// Split a DAT by item type // Split a DAT by item type