FormMain.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using qdr.app.studiou.orders2printpack.Properties;
  2. using Quadarax.Foundation.Core.Data;
  3. using Quadarax.Foundation.Core.IO;
  4. using Quadarax.Foundation.Core.Value;
  5. using System.Data;
  6. using System.IO.Abstractions;
  7. namespace qdr.app.studiou.orders2printpack
  8. {
  9. public partial class FormMain : Form
  10. {
  11. #region *** Constants ***
  12. private const string CS_COL_SOURCE_PATH = "LocalPath";
  13. private const string CS_COL_ID = "LocalID";
  14. private const string CS_TAG_LB = "{";
  15. private const string CS_TAG_RB = "}";
  16. private const string CS_TAG_EXT = "ext";
  17. private const string CS_TAG_FILE = "file";
  18. private const string CS_TAG_ORDINAL = "ordinal";
  19. #endregion
  20. #region *** Private Fields ***
  21. private DataTable _source = new();
  22. private readonly Dictionary<string, int> _sourceColOrdinals = [];
  23. private readonly List<string> _sourcePathCache = [];
  24. private readonly IFileSystem _fs = new FileSystem();
  25. private string _outputPath;
  26. #endregion
  27. #region *** Constructor ***
  28. public FormMain()
  29. {
  30. InitializeComponent();
  31. _outputPath = string.Empty;
  32. }
  33. #endregion
  34. #region *** Form Handlers ***
  35. private void FormMain_Load(object sender, EventArgs e)
  36. {
  37. Text = $"{Application.ProductName} v{Application.ProductVersion}";
  38. RefreshToolButtons();
  39. }
  40. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  41. private void butOpenOrdersFile_Click(object sender, EventArgs e)
  42. {
  43. if (dlgOpenFile.ShowDialog() == DialogResult.OK)
  44. OpenOrderBarchFile(dlgOpenFile.FileName);
  45. RefreshToolButtons();
  46. }
  47. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  48. private void butOpenSource_Click(object sender, EventArgs e)
  49. {
  50. dlgOpenDir.Description = "Otevřít složku se zdrojovými (RAW) soubory...";
  51. dlgOpenDir.UseDescriptionForTitle = true;
  52. dlgOpenDir.InitialDirectory = AppSettings.Default.LastSourceDir;
  53. if (dlgOpenDir.ShowDialog() == DialogResult.OK)
  54. {
  55. AppSettings.Default.LastSourceDir = dlgOpenDir.SelectedPath;
  56. AppSettings.Default.Save();
  57. OpenSourceDir(dlgOpenDir.SelectedPath);
  58. }
  59. RefreshToolButtons();
  60. }
  61. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  62. private void butOutputDir_Click(object sender, EventArgs e)
  63. {
  64. dlgOpenDir.Description = "Otevřít složku pro výstup...";
  65. dlgOpenDir.UseDescriptionForTitle = true;
  66. dlgOpenDir.InitialDirectory = AppSettings.Default.LastOutputDir;
  67. if (dlgOpenDir.ShowDialog() == DialogResult.OK)
  68. {
  69. _outputPath = dlgOpenDir.SelectedPath;
  70. Log($"Otevřena složka výstupu '{_outputPath}'");
  71. AppSettings.Default.LastOutputDir = dlgOpenDir.SelectedPath;
  72. AppSettings.Default.Save();
  73. tbOutputDir.Text = _outputPath;
  74. }
  75. RefreshToolButtons();
  76. }
  77. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  78. private void tsbCheck_Click(object sender, EventArgs e)
  79. {
  80. CheckSourceOrderFile();
  81. }
  82. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  83. private void tsbDo_Click(object sender, EventArgs e)
  84. {
  85. ProcessSourceFile();
  86. }
  87. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  88. private void tsbAddSourceDir_Click(object sender, EventArgs e)
  89. {
  90. dlgOpenDir.Description = "Otevřít další složku se zdrojovými (RAW) soubory...";
  91. dlgOpenDir.UseDescriptionForTitle = true;
  92. dlgOpenDir.InitialDirectory = AppSettings.Default.LastSourceDir;
  93. if (dlgOpenDir.ShowDialog() == DialogResult.OK)
  94. {
  95. Log("Přidávám další zdrojovou složku...");
  96. OpenSourceDir(dlgOpenDir.SelectedPath, true);
  97. }
  98. RefreshToolButtons();
  99. }
  100. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  101. private void lbNotMapped_MouseDoubleClick(object sender, MouseEventArgs e)
  102. {
  103. if (lbNotMapped.SelectedIndices.Count == 0)
  104. return;
  105. var id = (int?)lbNotMapped.SelectedValue;
  106. if (id == null)
  107. return;
  108. var row = _source.Rows[id.GetValueOrDefault()];
  109. var fileName = _fs.Path.GetFileName(row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColUri]));
  110. dlgOpenFile.Title = "Vyberte zdrojový soubor soubor pro záznam...";
  111. dlgOpenFile.InitialDirectory = AppSettings.Default.LastSourceDir;
  112. dlgOpenFile.FileName = fileName;
  113. dlgOpenFile.Filter = "Všechny soubory|*.*";
  114. if (dlgOpenFile.ShowDialog() == DialogResult.OK)
  115. {
  116. var sourcePath = dlgOpenFile.FileName;
  117. row.SetField(_sourceColOrdinals[CS_COL_SOURCE_PATH], sourcePath);
  118. RefreshToolButtons();
  119. }
  120. }
  121. [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
  122. private void tsbSettings_Click(object sender, EventArgs e)
  123. {
  124. var dlg = new dlgSettings();
  125. dlg.ShowDialog();
  126. }
  127. #endregion
  128. #region *** Private Methods ***
  129. #region **** Common ****
  130. private void Log(string message)
  131. {
  132. lbLog.Items.Insert(0, message);
  133. }
  134. private int GetOrderCount()
  135. {
  136. return _source.AsEnumerable().Select(x => x.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColOrderNo])).Distinct().Count();
  137. }
  138. private int GetLinesCount()
  139. {
  140. return _source.AsEnumerable().Count();
  141. }
  142. private string? GetSourceFullFileName(string? webUrl)
  143. {
  144. if (string.IsNullOrEmpty(webUrl))
  145. return null;
  146. var fileName = Path.GetFileName(webUrl);
  147. var sourcePath = _sourcePathCache.FirstOrDefault(x => string.Equals(Path.GetFileName(x), fileName));
  148. if (string.IsNullOrEmpty(sourcePath))
  149. Log($"Soubor '{fileName}' nebyl nalezen ve zdrojové složce.");
  150. return sourcePath;
  151. }
  152. private static string NormalizeColumnName(string name)
  153. {
  154. return name.ToLower().Replace(" ", "_");
  155. }
  156. private void ApplyRowToParametrizedString(DataRow row, ParameterizedString ps)
  157. {
  158. foreach (DataColumn col in row.Table.Columns)
  159. {
  160. var ord = _sourceColOrdinals[col.ColumnName];
  161. ps.AddOrSetParameter(NormalizeColumnName(col.ColumnName), row[ord]?.ToString()!);
  162. }
  163. }
  164. private void BlockErrorHandled(Action block)
  165. {
  166. try
  167. {
  168. block();
  169. }
  170. catch (Exception ex)
  171. {
  172. Log("Error: " + ex.Message);
  173. MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  174. RefreshToolButtons();
  175. }
  176. }
  177. #endregion
  178. #region **** Bussiness ****
  179. private void OpenOrderBarchFile(string fileName)
  180. {
  181. BlockErrorHandled(() =>
  182. {
  183. _source = CsvHelper.CsvToDataTable(_fs, fileName, AppSettings.Default.CSVDelimiter);
  184. _source.Columns.Add(new DataColumn(CS_COL_SOURCE_PATH, typeof(string)));
  185. _source.Columns.Add(new DataColumn(CS_COL_ID, typeof(int)));
  186. _sourceColOrdinals.Clear();
  187. for (int i = 0; i < _source.Columns.Count; i++)
  188. _sourceColOrdinals.Add(_source.Columns[i].ColumnName.Replace("\"", ""), i);
  189. var cntId = 0;
  190. foreach (DataRow row in _source.Rows)
  191. row.SetField(_sourceColOrdinals[CS_COL_ID], cntId++);
  192. var cntOrders = GetOrderCount();
  193. var cntLines = GetLinesCount();
  194. Log($"Soubor {fileName} načten. Počet objednávek: {cntOrders}, počet záznamů: {cntLines}");
  195. tbOrdersSourceFile.Text = fileName;
  196. tspOrdersCount.Text = $"Objednávek: {cntOrders}";
  197. });
  198. }
  199. private void OpenSourceDir(string pathToSourceFolder, bool appendCache = false)
  200. {
  201. BlockErrorHandled(() =>
  202. {
  203. var fsrch = new FileSearch(pathToSourceFolder, true);
  204. if (!appendCache)
  205. _sourcePathCache.Clear();
  206. tbSourceDir.Text = pathToSourceFolder;
  207. Log($"Procházím všechny soubory {AppSettings.Default.SourceSearchPattern} ve složce '{pathToSourceFolder}'...");
  208. Log("Tato operace může chvíli trvat!");
  209. var files = fsrch.Search([AppSettings.Default.SourceSearchPattern]);
  210. if (files.Length == 0)
  211. throw new Exception($"Nenalezeny žádné soubory pro zpracování ve složce '{pathToSourceFolder}'.");
  212. foreach (var file in files)
  213. if (!_sourcePathCache.Any(x => x == file))
  214. _sourcePathCache.Add(file);
  215. tsbFiles.Text = "Souborů: " + _sourcePathCache.Count.ToString();
  216. Log($"Nalezeno {files.Length} souborů.");
  217. });
  218. }
  219. private void CheckSourceOrderFile()
  220. {
  221. ssProgress.Value = 0;
  222. int cntFailed = 0;
  223. BlockErrorHandled(() =>
  224. {
  225. var rows = _source.AsEnumerable().Where(x => string.IsNullOrEmpty(x.Field<string>(_sourceColOrdinals[CS_COL_SOURCE_PATH])));
  226. ssProgress.Maximum = rows.Count();
  227. foreach (var row in rows)
  228. {
  229. var orderNo = row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColOrderNo]);
  230. var sourceUrl = row[_sourceColOrdinals[AppSettings.Default.MapColUri]]?.ToString();
  231. var sourcePath = GetSourceFullFileName(sourceUrl);
  232. if (sourcePath == null) cntFailed++;
  233. row.SetField(_sourceColOrdinals[CS_COL_SOURCE_PATH], sourcePath);
  234. ssProgress.Increment(1);
  235. }
  236. });
  237. tsbNotFound.Text = $"Nenalezeno: {cntFailed}";
  238. ssProgress.Value = 0;
  239. }
  240. private void ProcessSourceFile()
  241. {
  242. ssProgress.Value = 0;
  243. BlockErrorHandled(() =>
  244. {
  245. var rows = _source.AsEnumerable().Where(x => string.IsNullOrEmpty(x.Field<string>(_sourceColOrdinals[CS_COL_SOURCE_PATH])));
  246. if (rows.Any())
  247. throw new Exception("Některé řádky nebyly zkontrolovány. Proveďte kontrolu zdrojových souborů (1. Kontrola).");
  248. rows = _source.AsEnumerable();
  249. //var colNames = _source.Columns.Cast<DataColumn>().Select(x => NormalizeColumnName(x.ColumnName)).ToArray();
  250. ssProgress.Maximum = rows.Count();
  251. var outputFileNameBuilder = new ParameterizedString(AppSettings.Default.OutputFileMask, CS_TAG_LB, CS_TAG_RB);
  252. foreach (var row in rows)
  253. {
  254. var orderNo = row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColOrderNo]);
  255. var sourceUrl = row[_sourceColOrdinals[AppSettings.Default.MapColUri]]?.ToString();
  256. var qty = int.Parse(row[_sourceColOrdinals[AppSettings.Default.MapColQuantity]]?.ToString()!);
  257. var sourcePath = row[_sourceColOrdinals[CS_COL_SOURCE_PATH]]?.ToString();
  258. var sourceExt = _fs.Path.GetExtension(sourcePath);
  259. var sourceFileName = _fs.Path.GetFileNameWithoutExtension(sourcePath);
  260. ApplyRowToParametrizedString(row, outputFileNameBuilder);
  261. outputFileNameBuilder.AddOrSetParameter(CS_TAG_EXT, sourceExt!);
  262. outputFileNameBuilder.AddOrSetParameter(CS_TAG_FILE, sourceFileName!);
  263. for (int i = 0; i < qty; i++)
  264. {
  265. outputFileNameBuilder.AddOrSetParameter(CS_TAG_ORDINAL, i.ToString().PadLeft(3, '0'));
  266. var outputFileName = FileUtils.SanitizedFileName(_fs, outputFileNameBuilder.ToString());
  267. var outputPath = _fs.Path.Combine(_outputPath, outputFileName);
  268. _fs.File.Copy(sourcePath!, outputPath, true);
  269. Log($"Soubor '{outputPath}' vytvořen.");
  270. }
  271. ssProgress.Increment(1);
  272. }
  273. });
  274. ssProgress.Value = 0;
  275. }
  276. #endregion
  277. #region **** Refreshes ****
  278. private void RefreshToolButtons()
  279. {
  280. var canBeChecked = GetOrderCount() > 0 && _sourcePathCache.Count > 0;
  281. var canBeProcess = canBeChecked && !_source.AsEnumerable().Any(x => string.IsNullOrEmpty(x.Field<string>(_sourceColOrdinals[CS_COL_SOURCE_PATH]))) && !string.IsNullOrEmpty(_outputPath);
  282. tsbCheck.Enabled = canBeChecked;
  283. tsbDo.Enabled = canBeProcess;
  284. RefreshNotMappedList();
  285. }
  286. private void RefreshNotMappedList()
  287. {
  288. var rows = _source.AsEnumerable().Where(x => string.IsNullOrEmpty(x.Field<string>(_sourceColOrdinals[CS_COL_SOURCE_PATH])));
  289. var list = new List<Tuple<int, string>>();
  290. foreach (var row in rows)
  291. {
  292. var id = row.Field<int>(_sourceColOrdinals[CS_COL_ID]);
  293. var fileName = _fs.Path.GetFileName(row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColUri]));
  294. var productName = row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColProductFormat]);
  295. var orderNo = row.Field<string>(_sourceColOrdinals[AppSettings.Default.MapColOrderNo]);
  296. list.Add(new Tuple<int, string>(id, $"{fileName} [Jméno: {productName} / Obj.:{orderNo}]"));
  297. }
  298. lbNotMapped.DisplayMember = "Item2";
  299. lbNotMapped.ValueMember = "Item1";
  300. lbNotMapped.DataSource = list;
  301. lbNotMapped.Update();
  302. }
  303. #endregion
  304. #endregion
  305. }
  306. }