Feature: MS Excel (xlsx) Import/Export
Microsoft Excel files can be used to import/export devices. Several import bugs were also fixed in the process.
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
using Disco.Models.Services.Devices.Importing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.Importing
|
||||
{
|
||||
public class XlsxDeviceImportDataReader : IDeviceImportDataReader
|
||||
{
|
||||
private static string[] TrueValues = { "true", "1", "yes", "-1", "on" };
|
||||
private static string[] FalseValues = { "false", "0", "no", "off" };
|
||||
|
||||
private XlsxDeviceImportContext context;
|
||||
private List<object[]> rawData;
|
||||
private int currentRowIndex;
|
||||
private int rowOffset;
|
||||
private object[] currentRow;
|
||||
|
||||
public int Index { get { return currentRowIndex; } }
|
||||
|
||||
public XlsxDeviceImportDataReader(XlsxDeviceImportContext Context, List<object[]> RawData, bool HasHeaderRow)
|
||||
{
|
||||
context = Context;
|
||||
rawData = RawData;
|
||||
currentRowIndex = 0;
|
||||
rowOffset = currentRowIndex = HasHeaderRow ? 2 : 1;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
currentRowIndex = 0;
|
||||
currentRow = null;
|
||||
}
|
||||
|
||||
public bool Read()
|
||||
{
|
||||
if (++currentRowIndex >= rawData.Count)
|
||||
{
|
||||
currentRowIndex--;
|
||||
return false;
|
||||
}
|
||||
|
||||
currentRow = rawData[currentRowIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetRowNumber(int Index)
|
||||
{
|
||||
return Index + rowOffset;
|
||||
}
|
||||
|
||||
public string GetString(int ColumnIndex)
|
||||
{
|
||||
if (currentRow == null)
|
||||
throw new InvalidOperationException($"{nameof(XlsxDeviceImportDataReader.Read)} must be called before retrieving values");
|
||||
|
||||
var cell = currentRow[ColumnIndex];
|
||||
|
||||
if (cell == null)
|
||||
return null;
|
||||
else
|
||||
return cell.ToString();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetStrings(int ColumnIndex)
|
||||
{
|
||||
return rawData.Select(r => r[ColumnIndex]?.ToString());
|
||||
}
|
||||
|
||||
public bool TryGetNullableInt(int ColumnIndex, out int? value)
|
||||
{
|
||||
if (currentRow == null)
|
||||
throw new InvalidOperationException($"{nameof(XlsxDeviceImportDataReader.Read)} must be called before retrieving values");
|
||||
|
||||
return TryGetNullableInt(currentRow[ColumnIndex], out value);
|
||||
}
|
||||
|
||||
public bool TryGetNullableBool(int ColumnIndex, out bool? value)
|
||||
{
|
||||
if (currentRow == null)
|
||||
throw new InvalidOperationException($"{nameof(XlsxDeviceImportDataReader.Read)} must be called before retrieving values");
|
||||
|
||||
return TryGetNullableBool(currentRow[ColumnIndex], out value);
|
||||
}
|
||||
|
||||
public bool TryGetNullableDateTime(int ColumnIndex, out DateTime? value)
|
||||
{
|
||||
if (currentRow == null)
|
||||
throw new InvalidOperationException($"{nameof(XlsxDeviceImportDataReader.Read)} must be called before retrieving values");
|
||||
|
||||
return TryGetNullableDateTime(currentRow[ColumnIndex], out value);
|
||||
}
|
||||
|
||||
public bool TestAllNotEmpty(int ColumnIndex)
|
||||
{
|
||||
return GetStrings(ColumnIndex).All(s => !string.IsNullOrWhiteSpace(s));
|
||||
}
|
||||
|
||||
public bool TestAllNullableInt(int ColumnIndex)
|
||||
{
|
||||
return rawData.Select(r => r[ColumnIndex])
|
||||
.All(c =>
|
||||
{
|
||||
int? value;
|
||||
return TryGetNullableInt(c, out value);
|
||||
});
|
||||
}
|
||||
|
||||
public bool TestAllInt(int ColumnIndex)
|
||||
{
|
||||
return rawData.Select(r => r[ColumnIndex])
|
||||
.All(c =>
|
||||
{
|
||||
int? value;
|
||||
return TryGetNullableInt(c, out value) && value.HasValue;
|
||||
});
|
||||
}
|
||||
|
||||
public bool TestAllNullableBool(int ColumnIndex)
|
||||
{
|
||||
return rawData.Select(r => r[ColumnIndex])
|
||||
.All(c =>
|
||||
{
|
||||
bool? value;
|
||||
return TryGetNullableBool(c, out value);
|
||||
});
|
||||
}
|
||||
|
||||
public bool TestAllNullableDateTime(int ColumnIndex)
|
||||
{
|
||||
return rawData.Select(r => r[ColumnIndex])
|
||||
.All(c =>
|
||||
{
|
||||
DateTime? value;
|
||||
return TryGetNullableDateTime(c, out value);
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Nothing to dispose
|
||||
}
|
||||
|
||||
private bool TryGetNullableDateTime(object Content, out DateTime? value)
|
||||
{
|
||||
if (Content == null)
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Content is DateTime)
|
||||
{
|
||||
value = (DateTime)Content;
|
||||
return true;
|
||||
}
|
||||
|
||||
var stringValue = Content.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringValue = stringValue.Trim();
|
||||
|
||||
DateTime valueDateTime;
|
||||
if (DateTime.TryParse(stringValue, CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out valueDateTime))
|
||||
{
|
||||
value = valueDateTime;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetNullableBool(object Content, out bool? value)
|
||||
{
|
||||
if (Content == null)
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Content is bool)
|
||||
{
|
||||
value = (bool)Content;
|
||||
return true;
|
||||
}
|
||||
|
||||
var stringValue = Content.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringValue = stringValue.Trim();
|
||||
|
||||
if (TrueValues.Contains(stringValue, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
value = true;
|
||||
return true;
|
||||
}
|
||||
else if (FalseValues.Contains(stringValue, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
value = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetNullableInt(object Content, out int? value)
|
||||
{
|
||||
if (Content == null)
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Content is double)
|
||||
{
|
||||
value = (int)((double)Content);
|
||||
return true;
|
||||
}
|
||||
|
||||
var stringValue = Content.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int intValue;
|
||||
if (int.TryParse(stringValue, out intValue))
|
||||
{
|
||||
value = intValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user