Add Distinta Base import tab: parse .xls/.xml export to CSV with preview
Reads the ERP-exported "Distinta Base" file (SpreadsheetML XML or real binary .xls/.xlsx via ExcelDataReader), extracts Componente/Descrizione, writes a CSV and previews it in a new tab alongside the existing CAD search. Also ignore the exported Excel/CSV files dropped in the repo root. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
82a69e0d20
commit
f88639bf4b
4 files changed
+209
-3
No files matched your search
@@ -0,0 +1,109 @@
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using ExcelDataReader;
|
||||
|
||||
namespace CadSearchGui;
|
||||
|
||||
/// <summary>
|
||||
/// Converte il file "Distinta Base" esportato dal gestionale (SpreadsheetML
|
||||
/// XML con estensione .xls/.xml, oppure .xls/.xlsx binario vero) nelle sole
|
||||
/// colonne Componente/Descrizione in CSV.
|
||||
/// </summary>
|
||||
static class DistintaBaseImporter
|
||||
{
|
||||
private static readonly XNamespace Ss = "urn:schemas-microsoft-com:office:spreadsheet";
|
||||
private const string NomeFoglio = "Distinta Base";
|
||||
|
||||
static DistintaBaseImporter()
|
||||
{
|
||||
Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
||||
}
|
||||
|
||||
public static (string PercorsoCsv, List<(string Componente, string Descrizione)> Righe) ConvertiInCsv(string percorsoInput)
|
||||
{
|
||||
var righeGrezze = EImmagineSpreadsheetMl(percorsoInput)
|
||||
? LeggiRigheSpreadsheetMl(percorsoInput)
|
||||
: LeggiRigheExcelBinario(percorsoInput);
|
||||
|
||||
var righe = righeGrezze.Skip(1) // salta l'intestazione
|
||||
.Where(r => r.Componente.Length > 0 || r.Descrizione.Length > 0)
|
||||
.ToList();
|
||||
|
||||
var percorsoCsv = Path.Combine(
|
||||
Path.GetDirectoryName(Path.GetFullPath(percorsoInput))!,
|
||||
Path.GetFileNameWithoutExtension(percorsoInput) + ".csv");
|
||||
|
||||
using var writer = new StreamWriter(percorsoCsv, false, Encoding.UTF8);
|
||||
writer.WriteLine("Componente;Descrizione");
|
||||
foreach (var (componente, descrizione) in righe)
|
||||
{
|
||||
writer.WriteLine($"{EscapeCsv(componente)};{EscapeCsv(descrizione)}");
|
||||
}
|
||||
|
||||
return (percorsoCsv, righe);
|
||||
}
|
||||
|
||||
/// <summary>I file XML "SpreadsheetML" iniziano con l'intestazione XML testuale;
|
||||
/// i file .xls/.xlsx binari veri iniziano invece con una firma binaria (OLE2 o ZIP).</summary>
|
||||
private static bool EImmagineSpreadsheetMl(string percorso)
|
||||
{
|
||||
Span<byte> primiByte = stackalloc byte[5];
|
||||
using var stream = File.OpenRead(percorso);
|
||||
var letti = stream.Read(primiByte);
|
||||
return letti >= 5 && Encoding.ASCII.GetString(primiByte) == "<?xml";
|
||||
}
|
||||
|
||||
private static List<(string Componente, string Descrizione)> LeggiRigheSpreadsheetMl(string percorsoInput)
|
||||
{
|
||||
var doc = XDocument.Load(percorsoInput);
|
||||
var foglio = doc.Descendants(Ss + "Worksheet")
|
||||
.FirstOrDefault(w => (string?)w.Attribute(Ss + "Name") == NomeFoglio)
|
||||
?? throw new InvalidDataException($"Foglio '{NomeFoglio}' non trovato nel file.");
|
||||
|
||||
var righeXml = foglio.Descendants(Ss + "Row").ToList();
|
||||
if (righeXml.Count == 0)
|
||||
throw new InvalidDataException($"Il foglio '{NomeFoglio}' non contiene righe.");
|
||||
|
||||
return righeXml.Select(LeggiColonneBeCSpreadsheetMl).ToList();
|
||||
}
|
||||
|
||||
private static (string Componente, string Descrizione) LeggiColonneBeCSpreadsheetMl(XElement riga)
|
||||
{
|
||||
string b = "", c = "";
|
||||
int colonna = 0;
|
||||
foreach (var cella in riga.Elements(Ss + "Cell"))
|
||||
{
|
||||
// ss:Index salta le celle vuote iniziali: senza attributo la colonna è quella precedente + 1
|
||||
var indice = cella.Attribute(Ss + "Index");
|
||||
colonna = indice != null ? int.Parse(indice.Value) : colonna + 1;
|
||||
if (colonna == 2) b = cella.Element(Ss + "Data")?.Value ?? "";
|
||||
else if (colonna == 3) c = cella.Element(Ss + "Data")?.Value ?? "";
|
||||
}
|
||||
return (b, c);
|
||||
}
|
||||
|
||||
private static List<(string Componente, string Descrizione)> LeggiRigheExcelBinario(string percorsoInput)
|
||||
{
|
||||
using var stream = File.Open(percorsoInput, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
using var reader = ExcelReaderFactory.CreateReader(stream);
|
||||
var dataSet = reader.AsDataSet();
|
||||
var tabella = dataSet.Tables[NomeFoglio]
|
||||
?? throw new InvalidDataException($"Foglio '{NomeFoglio}' non trovato nel file.");
|
||||
|
||||
var righe = new List<(string Componente, string Descrizione)>();
|
||||
foreach (System.Data.DataRow riga in tabella.Rows)
|
||||
{
|
||||
var b = riga.Table.Columns.Count > 1 ? riga[1]?.ToString() ?? "" : "";
|
||||
var c = riga.Table.Columns.Count > 2 ? riga[2]?.ToString() ?? "" : "";
|
||||
righe.Add((b, c));
|
||||
}
|
||||
return righe;
|
||||
}
|
||||
|
||||
private static string EscapeCsv(string valore)
|
||||
{
|
||||
if (valore.Contains(';') || valore.Contains('"') || valore.Contains('\n'))
|
||||
return $"\"{valore.Replace("\"", "\"\"")}\"";
|
||||
return valore;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user