-
Notifications
You must be signed in to change notification settings - Fork 194
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
change report to parsing column name
- Loading branch information
Showing
6 changed files
with
548 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 160 additions & 0 deletions
160
Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/RowExtensionMethods.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace FikaAmazonAPI.ReportGeneration.ReportDataTable | ||
{ | ||
public static class RowExtensionMethods | ||
{ | ||
public static string GetString(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) | ||
? row[id] | ||
: null; | ||
} | ||
public static int GetInt32(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToInt32(row[id]) | ||
: int.MinValue; | ||
} | ||
public static long GetInt64(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToInt64(row[id]) | ||
: long.MinValue; | ||
} | ||
public static decimal GetDecimal(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToDecimal(row[id]) | ||
: decimal.MinValue; | ||
} | ||
public static DateTime GetDateTime(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToDateTime(row[id]) | ||
: DateTime.MinValue; | ||
} | ||
public static bool GetBoolean(this TableRow row, string id) | ||
{ | ||
if (TheBooleanValueIsEmpty(row, id)) | ||
return false; | ||
|
||
AssertThatTheRequestIsValid(row, id); | ||
|
||
return string.Equals(row[id], "true", StringComparison.OrdinalIgnoreCase); | ||
} | ||
private static void AssertThatTheRequestIsValid(TableRow row, string id) | ||
{ | ||
AssertThatAValueWithThisIdExistsInThisRow(row, id); | ||
AssertThatThisIsAnAcceptableBoolValue(row, id); | ||
} | ||
private static void AssertThatThisIsAnAcceptableBoolValue(TableRow row, string id) | ||
{ | ||
var acceptedValues = new[] { "true", "false" }; | ||
if (acceptedValues.Contains(row[id], StringComparer.OrdinalIgnoreCase) == false) | ||
throw new InvalidCastException($"You must use 'true' or 'false' when setting bools for {id}"); | ||
} | ||
|
||
private static void AssertThatAValueWithThisIdExistsInThisRow(TableRow row, string id) | ||
{ | ||
if (AValueWithThisIdExists(row, id) == false) | ||
throw new InvalidOperationException($"{id} could not be found in the row."); | ||
} | ||
private static bool TheBooleanValueIsEmpty(TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && string.IsNullOrEmpty(row[id]); | ||
} | ||
|
||
public static float GetSingle(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToSingle(row[id]) | ||
: float.MinValue; | ||
} | ||
public static char GetChar(this TableRow row, string id) | ||
{ | ||
return Convert.ToChar(row[id]); | ||
} | ||
public static T GetDiscreteEnum<T>(this TableRow row, string id) where T : struct, IConvertible | ||
{ | ||
var value = row[id].Replace(" ", string.Empty); | ||
T @enum; | ||
if (Enum.TryParse(value, true, out @enum)) | ||
return @enum; | ||
|
||
throw new InvalidOperationException($"No enum with value {value} found in enum {typeof(T).Name}"); | ||
} | ||
public static T GetDiscreteEnum<T>(this TableRow row, string id, T defaultValue) where T : struct, IConvertible | ||
{ | ||
var value = row[id].Replace(" ", string.Empty); | ||
T @enum; | ||
return Enum.TryParse(value, true, out @enum) ? @enum : defaultValue; | ||
} | ||
public static TEnum GetEnumValue<TEnum>(this TableRow row, string id) | ||
{ | ||
return (TEnum)Enum.Parse(typeof(TEnum), row[id]); | ||
} | ||
public static Enum GetEnum<T>(this TableRow row, string id) where T : class | ||
{ | ||
return GetTheEnumValue<T>(row[id], id); | ||
} | ||
private static Enum GetTheEnumValue<T>(string rowValue, string propertyName) where T : class | ||
{ | ||
var value = rowValue.Replace(" ", string.Empty); | ||
|
||
var enumType = GetTheEnumType<T>(propertyName, value); | ||
|
||
return Enum.Parse(enumType, value, true) as Enum; | ||
} | ||
|
||
private static Type GetTheEnumType<T>(string propertyName, string value) | ||
{ | ||
var propertyType = (from property in typeof(T).GetProperties() | ||
where property.Name == propertyName | ||
&& property.PropertyType.IsEnum | ||
&& EnumValueIsDefinedCaseInsensitve(property.PropertyType, value) | ||
select property.PropertyType).FirstOrDefault(); | ||
|
||
if (propertyType == null) | ||
throw new InvalidOperationException($"No enum with value {value} found in type {typeof(T).Name}"); | ||
|
||
return propertyType; | ||
} | ||
private static bool EnumValueIsDefinedCaseInsensitve(Type @enum, string value) | ||
{ | ||
Enum parsedEnum = null; | ||
try | ||
{ | ||
parsedEnum = Enum.Parse(@enum, value, true) as Enum; | ||
} | ||
catch | ||
{ | ||
// just catch it | ||
} | ||
|
||
return parsedEnum != null; | ||
} | ||
public static Guid GetGuid(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? new Guid(row[id]) | ||
: new Guid(); | ||
} | ||
public static double GetDouble(this TableRow row, string id) | ||
{ | ||
return AValueWithThisIdExists(row, id) && TheValueIsNotEmpty(row, id) | ||
? Convert.ToDouble(row[id]) | ||
: double.MinValue; | ||
} | ||
private static bool AValueWithThisIdExists(IEnumerable<KeyValuePair<string, string>> row, string id) | ||
{ | ||
return row.Any(x => x.Key == id); | ||
} | ||
private static bool TheValueIsNotEmpty(TableRow row, string id) | ||
{ | ||
return string.IsNullOrEmpty(row[id]) == false; | ||
} | ||
} | ||
} |
179 changes: 179 additions & 0 deletions
179
Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/Table.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace FikaAmazonAPI.ReportGeneration.ReportDataTable | ||
{ | ||
public class Table | ||
{ | ||
internal const string ERROR_NO_CELLS_TO_ADD = "No cells to add"; | ||
internal const string ERROR_NO_HEADER_TO_ADD = "No headers to add"; | ||
internal const string ERROR_COLUMN_NAME_NOT_FOUND = "Could not find a column named '{0}' in the table."; | ||
internal const string ERROR_CELLS_NOT_MATCHING_HEADERS = "The number of cells ({0}) you are trying to add doesn't match the number of columns ({1})"; | ||
|
||
private readonly string[] header; | ||
private readonly TableRows rows = new TableRows(); | ||
|
||
public ICollection<string> Header | ||
{ | ||
get { return header; } | ||
} | ||
|
||
public TableRows Rows | ||
{ | ||
get { return rows; } | ||
} | ||
|
||
public int RowCount | ||
{ | ||
get { return rows.Count; } | ||
} | ||
|
||
public Table(params string[] header) | ||
{ | ||
|
||
if (header == null || header.Length == 0) | ||
{ | ||
throw new ArgumentException(ERROR_NO_HEADER_TO_ADD, "header"); | ||
} | ||
for (int colIndex = 0; colIndex < header.Length; colIndex++) | ||
header[colIndex] = header[colIndex] ?? string.Empty; | ||
this.header = header; | ||
} | ||
|
||
public static Table ConvertFromCSV(string path, char separator = '\t') | ||
{ | ||
var lines = File.ReadAllLines(path); | ||
|
||
var table = new Table(lines.First().Split(separator)); | ||
|
||
|
||
lines.Skip(1).ToList().ForEach(a => ConvertFromCSVAddRow(table, a, separator)); | ||
return table; | ||
} | ||
private static void ConvertFromCSVAddRow(Table table, string line, char separator) | ||
{ | ||
table.AddRow(line.Split(separator)); | ||
} | ||
public bool ContainsColumn(string column) | ||
{ | ||
return GetHeaderIndex(column, false) >= 0; | ||
} | ||
|
||
internal int GetHeaderIndex(string column, bool throwIfNotFound = true) | ||
{ | ||
int index = Array.IndexOf(header, column); | ||
if (!throwIfNotFound) | ||
return index; | ||
if (index < 0) | ||
{ | ||
var mess = string.Format( | ||
ERROR_COLUMN_NAME_NOT_FOUND + "\nThe table looks like this:\n{1}", | ||
column, | ||
this); | ||
throw new IndexOutOfRangeException(mess); | ||
} | ||
return index; | ||
} | ||
|
||
public void AddRow(IDictionary<string, string> values) | ||
{ | ||
string[] cells = new string[header.Length]; | ||
foreach (var value in values) | ||
{ | ||
int headerIndex = GetHeaderIndex(value.Key); | ||
cells[headerIndex] = value.Value; | ||
} | ||
|
||
AddRow(cells); | ||
} | ||
|
||
public void AddRow(params string[] cells) | ||
{ | ||
if (cells == null) | ||
throw new Exception(ERROR_NO_CELLS_TO_ADD); | ||
|
||
if (cells.Length != header.Length) | ||
{ | ||
var mess = | ||
string.Format( | ||
ERROR_CELLS_NOT_MATCHING_HEADERS + ".\nThe table looks like this\n{2}", | ||
cells.Length, | ||
header.Length, | ||
this); | ||
throw new ArgumentException(mess); | ||
} | ||
var row = new TableRow(this, cells); | ||
rows.Add(row); | ||
} | ||
|
||
public void RenameColumn(string oldColumn, string newColumn) | ||
{ | ||
int colIndex = GetHeaderIndex(oldColumn); | ||
header[colIndex] = newColumn; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return ToString(false, true); | ||
} | ||
|
||
public string ToString(bool headersOnly = false, bool withNewline = true) | ||
{ | ||
int[] columnWidths = new int[header.Length]; | ||
for (int colIndex = 0; colIndex < header.Length; colIndex++) | ||
columnWidths[colIndex] = header[colIndex].Length; | ||
|
||
if (!headersOnly) | ||
{ | ||
foreach (TableRow row in rows) | ||
{ | ||
for (int colIndex = 0; colIndex < header.Length; colIndex++) | ||
columnWidths[colIndex] = Math.Max(columnWidths[colIndex], row[colIndex].Length); | ||
} | ||
} | ||
|
||
StringBuilder builder = new StringBuilder(); | ||
AddTableRow(builder, header, columnWidths); | ||
|
||
if (!headersOnly) | ||
{ | ||
foreach (TableRow row in rows) | ||
AddTableRow(builder, row.Select(pair => pair.Value), columnWidths); | ||
} | ||
|
||
if (!withNewline) | ||
{ | ||
var newlineLength = Environment.NewLine.Length; | ||
builder.Remove(builder.Length - newlineLength, newlineLength); | ||
} | ||
|
||
return builder.ToString(); | ||
} | ||
|
||
private void AddTableRow(StringBuilder builder, IEnumerable<string> cells, int[] widths) | ||
{ | ||
const string margin = " "; | ||
const string separator = "|"; | ||
int colIndex = 0; | ||
|
||
builder.Append(separator); | ||
foreach (string cell in cells) | ||
{ | ||
builder.Append(margin); | ||
|
||
builder.Append(cell); | ||
builder.Append(' ', widths[colIndex] - cell.Length); | ||
|
||
builder.Append(margin); | ||
builder.Append(separator); | ||
|
||
colIndex++; | ||
} | ||
|
||
builder.AppendLine(); | ||
} | ||
} | ||
} |
Oops, something went wrong.