Program.cs 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299
  1. using CUP_POD_Mail_Reader;
  2. using Ionic.Zip;
  3. using iTextSharp.text;
  4. using iTextSharp.text.pdf;
  5. using Limilabs.FTP;
  6. using MassTransit;
  7. using Message.MessageTypes;
  8. using Npgsql;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Configuration;
  12. using System.Data;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.IO;
  16. using System.IO.Compression;
  17. using System.Linq;
  18. using System.Net;
  19. using System.Reflection;
  20. using System.Text;
  21. using System.Text.RegularExpressions;
  22. using System.Threading;
  23. using WinSCP;
  24. using static CUP_POD_Mail_Reader.FileDownloadingDBOperations;
  25. using MailAddress = System.Net.Mail.MailAddress;
  26. using MailMessage = System.Net.Mail.MailMessage;
  27. using SmtpClient = System.Net.Mail.SmtpClient;
  28. namespace LNBPMJobcreator
  29. {
  30. public class OutLookreader
  31. {
  32. private readonly IRequestClient<Message.MessageTypes.ISimpleRequest, Message.MessageTypes.ISimpleResponse> _requestClient;
  33. public static string lastdate = string.Empty;
  34. public static DateTime? lastdate1;
  35. public static string reportsName = string.Empty;
  36. // private static readonly string[] DatePatterns = {
  37. // @"^\d{4}_\d{2}_\d{2}$", // 2023_06_23
  38. // @"^\d{4}_\d{2}_\d{2}-\d{2}$", // 2023_07_05-14
  39. // @"^\d{4}_\d{2}_\d{2}-\d{4}_\d{2}_\d{2}$", // 2023_04_06-2023_08_14
  40. // @"^3d-\d{4}_\d{2}_\d{2}-F$", // 3d-2021_07_12-F
  41. // @"^\d{4}_\d{2}_\d{2} A$", // 2023_06_23 A
  42. // @"^\d{4}_\d{2}_\d{2}_A$", // 2023_06_23_A
  43. // @"^\d{4}\.\d{2}\.\d{2}$" // 2021.12.15
  44. //};
  45. private static readonly string[] DatePatterns = {
  46. @"^\d{4}_\d{2}_\d{2}$", // ID 0 → 2023_06_23
  47. @"^\d{4}_\d{2}_\d{2}-\d{2}$", // ID 1 → 2023_07_05-14
  48. @"^\d{4}_\d{2}_\d{2}-\d{4}_\d{2}_\d{2}$", // ID 2 → 2023_04_06-2023_08_14
  49. @"^3d-\d{4}_\d{2}_\d{2}-F$", // ID 3 → 3d-2021_07_12-F
  50. @"^\d{4}_\d{2}_\d{2} [A-Z]{1}$", // ID 4 → 2023_06_23 A
  51. @"^\d{4}_\d{2}_\d{2}_(?:(?:[A-Z]{1})?)$", // ID 5 → 2023_06_23_A
  52. @"^\d{4}\.\d{2}\.\d{2}$" // ID 6 → 2021.12.15
  53. };
  54. // Regular expressions for folder names related to cases and opinions
  55. private static readonly string[] caseFolderPatterns = {
  56. @"\d{2}_cases", // 14_cases, 09_cases, etc.
  57. @"_Cases$", // *Cases
  58. @"Supreme Court Opinions$", // *Supreme Court Opinions
  59. @"Court of Appeal Opinions$", // *Court of Appeal Opinions
  60. @"Appellate Division of Superior Court Opinions$", // *Appellate Division of Superior Court Opinions
  61. @"Mchgn Supreme Court Cases$", // *Mchgn Supreme Court Cases
  62. @"Cases Accepted by Supreme Court$" // *Cases Accepted by Supreme Court
  63. };
  64. DataTable dt = new DataTable();
  65. FileDownloadingDBOperations dbOperation = new FileDownloadingDBOperations();
  66. MessageId messageid = new MessageId();
  67. public string LogFile = AppDomain.CurrentDomain.BaseDirectory + "\\CUP_POD_LogFile.txt";
  68. string filenamechange = string.Empty;
  69. FTPServerDbOperations ftpServerDb = new FTPServerDbOperations();
  70. FileDownloadingDBOperations dbOperatios = new FileDownloadingDBOperations();
  71. string ftpDorectory;
  72. public bool serverdownloaded;
  73. long fileSize;
  74. public static string _lastFileName;
  75. DateTime? startdownloadtime = null;
  76. public static int jobid = 0;
  77. public static int lotid = 0;
  78. public static int tranid = 0;
  79. string parentPath = string.Empty;
  80. string sourcePath = string.Empty;
  81. //string reportname = string.Empty;
  82. int bmpcount;
  83. public bool fileexixts;
  84. List<DownloadedFile> listdownloadFile = new List<DownloadedFile>();
  85. public static bool isdownloading = false;
  86. //public bool GenerateMail(string projectcode, List<DataTable> dataTables)
  87. //{
  88. // string mailBody = string.Empty;
  89. // mailBody = "<div align=\"left\">";
  90. // mailBody += "<font face=\"calibre\" style=\"color:black;font-size:15px;\">Dear Team,</font><br/><br/>";
  91. // mailBody += "<p align=\"left\" style=\"padding-left:2px;\"><font face=\"calibre\" style=\"color:black;font-size:15px;\">The list of files and attachments are downloaded successfully.</font></p>";
  92. // mailBody += "</div>";
  93. // // Process each DataTable
  94. // foreach (var DT in dataTables)
  95. // {
  96. // if (DT != null && DT.Rows.Count > 0)
  97. // {
  98. // string tableRows = string.Empty;
  99. // // Add table headers
  100. // tableRows += "<table border=\"1\" height=\"60\" width=\"400\" style=\"border:1px solid black;border-collapse: collapse\">";
  101. // tableRows += "<tr><td><b>File Name</b></td><td><b>Size</b></td><td><b>StartTime</b></td><td><b>Status</b></td></tr>\r\n";
  102. // // Iterate over each row in the DataTable and build the table rows
  103. // foreach (DataRow row in DT.Rows)
  104. // {
  105. // string status = Convert.ToBoolean(row["DownloadStatus"]) ? "Success" : "Fail";
  106. // tableRows += "<tr>";
  107. // tableRows += "<td>" + row["FileName"] + "</td>";
  108. // tableRows += "<td>" + row["totalFileSizeinLocal"] + "</td>";
  109. // tableRows += "<td>" + row["DownloadingStartTime"] + "</td>";
  110. // tableRows += "<td>" + status + "</td>";
  111. // tableRows += "</tr>";
  112. // }
  113. // tableRows += "</table>";
  114. // // Add the table to the email body
  115. // mailBody += tableRows;
  116. // }
  117. // }
  118. // // Add the footer to the email body
  119. // mailBody += "<div align=\"left\"><br/>";
  120. // mailBody += "<font face=\"calibre\" size=\"3\">Thanks & Regards,</font><br/>";
  121. // // Add different sign-offs based on project code
  122. // if (projectcode == "TF-01")
  123. // mailBody += "<font face=\"calibre\" size=\"3\">T&F_US-TypeSetting Team</font><br/>";
  124. // else if (projectcode == "CUP")
  125. // mailBody += "<font face=\"calibre\" size=\"3\">CUP-TypeSetting Team</font><br/>";
  126. // else if (projectcode == "CUP_POD")
  127. // mailBody += "<font face=\"calibre\" size=\"3\">CUP POD-TypeSetting Team</font><br/>";
  128. // else if (projectcode == "LN_BPM")
  129. // mailBody += "<font face=\"calibre\" size=\"3\">LN-BPM Team</font><br/>";
  130. // mailBody += "<font face=\"calibre\" size=\"3\">Lumina Datamatics Ltd - Puducherry</font><br/>";
  131. // mailBody += "<font face=\"calibre\" size=\"3\">Tel: 0413 - 6604503/6604530.</font><br/>";
  132. // mailBody += "<p align=\"left\"><font face=\"calibre\" style=\"font-size:12px;\">Note: This is a system generated email, do not reply to this email.</font></p>";
  133. // mailBody += "</div>";
  134. // // Send the email using the SendEmail method
  135. // bool autoGeneratedMailStatus = SendEmail(projectcode, mailBody, string.Empty);
  136. // if (autoGeneratedMailStatus)
  137. // {
  138. // Console.WriteLine("An auto-generated mail will be sent regarding the downloaded status.");
  139. // }
  140. // return autoGeneratedMailStatus;
  141. //}
  142. public static void SendEmail(string jobNo, int totalLots, DateTime createDateTime)
  143. {
  144. try
  145. {
  146. string fromAddress = "laps.projects@luminad.com";
  147. string toAddressesRaw = ConfigurationManager.AppSettings["EmailToAddresses"];
  148. if (string.IsNullOrWhiteSpace(toAddressesRaw))
  149. {
  150. throw new InvalidOperationException("No To addresses configured in app settings (EmailToAddresses).");
  151. }
  152. // Split into individual email strings, trim whitespace
  153. var toAddresses = toAddressesRaw
  154. .Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
  155. .Select(addr => addr.Trim())
  156. .Where(addr => !string.IsNullOrWhiteSpace(addr)); // remove any empty entries
  157. string smtpHost = "smtppro.zoho.in";
  158. int smtpPort = 587;
  159. string smtpUser = "laps.projects@luminad.com";
  160. string smtpPass = "lap@cdgLD687";
  161. using (MailMessage mail = new MailMessage())
  162. {
  163. mail.From = new MailAddress(fromAddress);
  164. foreach (var addr in toAddresses)
  165. {
  166. // Either use this:
  167. mail.To.Add(new MailAddress(addr));
  168. // or directly: mail.To.Add(addr); // there's an overload that takes string
  169. }
  170. mail.Subject = "WMS INVENTORY CREATION NOTIFICATION";
  171. mail.IsBodyHtml = true;
  172. mail.Body = $@"
  173. <html>
  174. <head>
  175. <style>
  176. table {{
  177. border-collapse: collapse;
  178. width: 70%;
  179. font-family: Arial, sans-serif;
  180. font-size: 14px;
  181. }}
  182. th {{
  183. background-color: #0078D7;
  184. color: white;
  185. padding: 8px;
  186. text-align: center;
  187. }}
  188. td {{
  189. border: 1px solid #ddd;
  190. padding: 8px;
  191. text-align: center;
  192. }}
  193. </style>
  194. </head>
  195. <body>
  196. <h3>VLEX CREATION NOTIFICATION</h3>
  197. <table>
  198. <tr>
  199. <th>Job No</th>
  200. <th>Total Lots</th>
  201. <th>Create Date-time</th>
  202. </tr>
  203. <tr>
  204. <td>{jobNo}</td>
  205. <td>{totalLots}</td>
  206. <td>{createDateTime}</td>
  207. </tr>
  208. </table>
  209. </body>
  210. </html>";
  211. using (SmtpClient smtp = new SmtpClient(smtpHost, smtpPort))
  212. {
  213. smtp.Credentials = new NetworkCredential(smtpUser, smtpPass);
  214. smtp.EnableSsl = true;
  215. smtp.Send(mail);
  216. }
  217. }
  218. Console.WriteLine("✅ Email sent successfully.");
  219. }
  220. catch (Exception ex)
  221. {
  222. Console.WriteLine($"❌ Failed to send email: {ex.Message}");
  223. }
  224. }
  225. public static void SendEmail(string subject, string bodyContent)
  226. {
  227. try
  228. {
  229. string fromAddress = "laps.projects@luminad.com";
  230. // Read "To" addresses from config
  231. string toAddressesRaw = ConfigurationManager.AppSettings["EmailToAddresses"];
  232. if (string.IsNullOrWhiteSpace(toAddressesRaw))
  233. {
  234. throw new InvalidOperationException("No To addresses configured in app settings (EmailToAddresses).");
  235. }
  236. // Split into individual email strings, trim whitespace
  237. var toAddresses = toAddressesRaw
  238. .Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
  239. .Select(addr => addr.Trim())
  240. .Where(addr => !string.IsNullOrWhiteSpace(addr)); // remove any empty entries
  241. string smtpHost = "smtppro.zoho.in";
  242. int smtpPort = 587;
  243. string smtpUser = "laps.projects@luminad.com";
  244. string smtpPass = "lap@cdgLD687"; // (you might want to move this to config/secure storage)
  245. // Build styled HTML body
  246. string body = $@"
  247. <html>
  248. <head>
  249. <style>
  250. body {{
  251. font-family: Arial, sans-serif;
  252. margin: 20px;
  253. font-size: 14px;
  254. color: #333;
  255. }}
  256. h2 {{
  257. color: #0073C0;
  258. text-align: center;
  259. }}
  260. table {{
  261. border-collapse: collapse;
  262. width: 80%;
  263. margin: 20px auto;
  264. }}
  265. th, td {{
  266. border: 1px solid #ddd;
  267. padding: 10px;
  268. text-align: center;
  269. }}
  270. th {{
  271. background-color: #0073C0;
  272. color: white;
  273. }}
  274. </style>
  275. </head>
  276. <body>
  277. <h2>📢 Notification</h2>
  278. <div>{bodyContent}</div>
  279. </body>
  280. </html>";
  281. using (MailMessage mail = new MailMessage())
  282. {
  283. mail.From = new MailAddress(fromAddress);
  284. // Add each "To" address
  285. foreach (var addr in toAddresses)
  286. {
  287. // Either use this:
  288. mail.To.Add(new MailAddress(addr));
  289. // or directly: mail.To.Add(addr); // there's an overload that takes string
  290. }
  291. mail.Subject = subject;
  292. mail.Body = body;
  293. mail.IsBodyHtml = true;
  294. using (SmtpClient smtp = new SmtpClient(smtpHost, smtpPort))
  295. {
  296. smtp.Credentials = new NetworkCredential(smtpUser, smtpPass);
  297. smtp.EnableSsl = true;
  298. smtp.Send(mail);
  299. }
  300. }
  301. Console.WriteLine("✅ Email sent successfully.");
  302. }
  303. catch (Exception ex)
  304. {
  305. Console.WriteLine($"❌ Failed to send email: {ex.Message}");
  306. }
  307. }
  308. public static void SendEmail(string subject, string folderPath, string fileCount)
  309. {
  310. try
  311. {
  312. string fromAddress = "laps.projects@luminad.com";
  313. string toAddressesRaw = ConfigurationManager.AppSettings["EmailToAddresses"];
  314. if (string.IsNullOrWhiteSpace(toAddressesRaw))
  315. {
  316. throw new InvalidOperationException("No To addresses configured in app settings (EmailToAddresses).");
  317. }
  318. // Split into individual email strings, trim whitespace
  319. var toAddresses = toAddressesRaw
  320. .Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
  321. .Select(addr => addr.Trim())
  322. .Where(addr => !string.IsNullOrWhiteSpace(addr)); // remove any empty entries
  323. string smtpHost = "smtppro.zoho.in";
  324. int smtpPort = 587;
  325. string smtpUser = "laps.projects@luminad.com";
  326. string smtpPass = "lap@cdgLD687"; // your app password
  327. // ✅ HTML body with styled table
  328. string body = $@"
  329. <html>
  330. <head>
  331. <style>
  332. table {{
  333. border-collapse: collapse;
  334. width: 80%;
  335. font-family: Arial, sans-serif;
  336. margin: 20px auto;
  337. font-size: 14px;
  338. }}
  339. th {{
  340. background-color: #0073C0;
  341. color: white;
  342. padding: 10px;
  343. text-align: center;
  344. }}
  345. td {{
  346. border: 1px solid #ddd;
  347. padding: 10px;
  348. text-align: center;
  349. }}
  350. h2 {{
  351. text-align: center;
  352. font-family: Arial, sans-serif;
  353. color: #0073C0;
  354. }}
  355. </style>
  356. </head>
  357. <body>
  358. <h2>📢 BMP FILE UPDATE NOTIFICATION</h2>
  359. <table>
  360. <tr>
  361. <th>Folder Path</th>
  362. <th>Total Files Updated</th>
  363. <th>Create Date-Time</th>
  364. </tr>
  365. <tr>
  366. <td>{folderPath}</td>
  367. <td>{fileCount}</td>
  368. <td>{DateTime.Now}</td>
  369. </tr>
  370. </table>
  371. </body>
  372. </html>";
  373. using (MailMessage mail = new MailMessage())
  374. {
  375. mail.From = new MailAddress(fromAddress);
  376. foreach (var addr in toAddresses)
  377. {
  378. // Either use this:
  379. mail.To.Add(new MailAddress(addr));
  380. // or directly: mail.To.Add(addr); // there's an overload that takes string
  381. }
  382. mail.Subject = subject;
  383. mail.Body = body;
  384. mail.IsBodyHtml = true;
  385. using (SmtpClient smtp = new SmtpClient(smtpHost, smtpPort))
  386. {
  387. smtp.Credentials = new NetworkCredential(smtpUser, smtpPass);
  388. smtp.EnableSsl = true;
  389. smtp.Send(mail);
  390. }
  391. }
  392. Console.WriteLine("✅ Email sent successfully.");
  393. }
  394. catch (Exception ex)
  395. {
  396. Console.WriteLine($"❌ Failed to send email: {ex.Message}");
  397. }
  398. }
  399. public List<DownloadedFile> DownloadFromFTP1(string projectCode, string dwnserverpath, bool batch_folder)
  400. {
  401. List<DataTable> tableList = new List<DataTable>();
  402. string localWorkRoot = ConfigurationManager.AppSettings["localExtractRoot"].ToString();
  403. // string reportsname = string.Empty;
  404. if (isdownloading)
  405. {
  406. Console.WriteLine("A folder is downloading kindly wait");
  407. return null;
  408. }
  409. else
  410. {
  411. try
  412. {
  413. // isdownloading = true;
  414. string projectid = FileDownloadingDBOperations.getscallervalue("SELECT projectdefinitionid::varchar FROM tblprojectdefinition WHERE project_subtitle='" + projectCode.Trim() + "' AND active=1", null);
  415. // Call_Msmq_watcher("130089", "0", "checkout_apps", "A");
  416. if (listdownloadFile.Count() > 0)
  417. {
  418. listdownloadFile.Clear();
  419. }
  420. Console.WriteLine("Downloading files from FTP server...");
  421. string privateKeyPath = ConfigurationManager.AppSettings["filepath"];
  422. string qrys = @"SELECT hostname,username,password FROM tblftpcredentials where project_code='" + projectCode + "' ";
  423. DataTable ftpcredentials = dbOperation.getdatatable(qrys);
  424. Console.WriteLine("Credentials got ...");
  425. var sessionOptions = new SessionOptions
  426. {
  427. Protocol = Protocol.Sftp,
  428. HostName = ftpcredentials.Rows[0]["hostname"].ToString(),
  429. UserName = ftpcredentials.Rows[0]["username"].ToString(),
  430. //HostName="sftp.fastcase.com",
  431. //UserName="sanaullah.noorullah@luminad.com",
  432. PrivateKeyPassphrase = "orGpyaxejx!2MMs",
  433. PortNumber = 22,
  434. // SshPrivateKeyPath = @"C:\Users\kiruthiga.p\Downloads\VLEX FTP Details\VLEX FTP Details\sanaullah_noorullah_old.ppk",
  435. SshPrivateKeyPath = privateKeyPath,
  436. GiveUpSecurityAndAcceptAnySshHostKey = true
  437. };
  438. using (WinSCP.Session session = new WinSCP.Session())
  439. {
  440. TransferOptions TO = new TransferOptions();
  441. Console.WriteLine("Connecting to FTP...");
  442. session.FileTransferProgress += SessionFileTransferProgress;
  443. session.Open(sessionOptions);
  444. Console.WriteLine("Session opened.");
  445. // ✅ Get allowed directories + their last processed date from DB
  446. Dictionary<string, string> GetAllowedDirectoriesFromDB()
  447. {
  448. var dbOps = new FileDownloadingDBOperations();
  449. string query = "SELECT reportname, datetocheck AS last_date FROM tblreportcheck WHERE active = 1";
  450. DataTable dt = dbOps.getdatatable(query);
  451. if (dt == null || dt.Rows.Count == 0)
  452. {
  453. Console.WriteLine("No data returned from database.");
  454. return new Dictionary<string, string>();
  455. }
  456. //var result = dt.AsEnumerable()
  457. // .Where(r => !r.IsNull("reportname") && !string.IsNullOrWhiteSpace(r["reportname"].ToString()))
  458. // .ToDictionary(
  459. // r => r["reportname"].ToString().Trim(),
  460. // r => !r.IsNull("last_date") && DateTime.TryParse(r["last_date"].ToString(), out DateTime d) ? d : DateTime.MinValue
  461. // );
  462. var result = dt.AsEnumerable()
  463. .Where(r => !r.IsNull("reportname") && !string.IsNullOrWhiteSpace(r["reportname"].ToString()))
  464. .ToDictionary(
  465. r => r["reportname"].ToString().Trim(),
  466. r => !r.IsNull("last_date")
  467. ? r["last_date"].ToString().Trim() // keep as string
  468. : string.Empty // default if null
  469. );
  470. return result;
  471. }
  472. // ✅ Load allowed directories from DB
  473. var allowedDirs = GetAllowedDirectoriesFromDB();
  474. if (allowedDirs.Count() > 0)
  475. {
  476. foreach (var data in allowedDirs)
  477. {
  478. //string reportsName = string.Empty;
  479. // reportsName = "Maryland";
  480. reportsName=data.Key;
  481. string reportName1 = data.Key;
  482. // Step 1: Locate "Hawai'i Reports" directory
  483. string root = "/" + reportName1 + "";
  484. lastdate = string.Empty;
  485. lastdate = data.Value;
  486. var remotePathss = session.EnumerateRemoteFiles("/", "*", EnumerationOptions.None)
  487. .Where(f => !f.IsDirectory);
  488. RemoteDirectoryInfo rootList = session.ListDirectory(root);
  489. //session.Open(sessionOptions);
  490. Console.WriteLine("Searching recursively for folders with today's date...");
  491. //List<RemoteFileInfo> allFiles = GetAllFiles(session, "/");
  492. // This searches recursively and filters remotely using WinSCP file mask
  493. //var remotePaths = session.EnumerateRemoteFiles(
  494. // "/",
  495. // $"*2025_09_09*/", // find folders with today’s date in name
  496. // EnumerationOptions.AllDirectories | EnumerationOptions.MatchDirectories);
  497. ProcessDirectory(session, root, rootList, localWorkRoot);
  498. }
  499. }
  500. }
  501. }
  502. catch (System.Exception ex)
  503. {
  504. isdownloading = false;
  505. Console.Write(ex);
  506. return null;
  507. }
  508. return listdownloadFile;
  509. }
  510. isdownloading = false;
  511. }
  512. public object Call_Msmq_watcher(string p_tranid, string p_userid, string checkout, string cfalg)
  513. {
  514. RabbitMq sendmsg = new RabbitMq(_requestClient);
  515. sendmsg.SendMq_StageComplete(p_tranid.ToString(), p_userid, checkout, "server", "", cfalg, "");
  516. return true;
  517. }
  518. public void SessionFileTransferProgress(object sender, FileTransferProgressEventArgs e)
  519. {
  520. Console.WriteLine("try this");
  521. try
  522. {
  523. if ((_lastFileName != null) && (_lastFileName != e.FileName))
  524. {
  525. Console.WriteLine();
  526. }
  527. string str = string.Format("\r{0} \r\n({1:P0})", e.FileName, e.FileProgress);
  528. File.AppendAllText(LogFile, str);
  529. Console.WriteLine(str);
  530. _lastFileName = e.FileName;
  531. }
  532. catch (System.Exception ex)
  533. {
  534. isdownloading = false;
  535. Console.WriteLine(ex);
  536. }
  537. }
  538. public static List<RemoteFileInfo> GetAllFiles(Session session, string remotePath)
  539. {
  540. var allFiles = new List<RemoteFileInfo>();
  541. try
  542. {
  543. RemoteDirectoryInfo dir = session.ListDirectory(remotePath);
  544. foreach (RemoteFileInfo file in dir.Files)
  545. {
  546. // Skip "." and ".." directories
  547. if (file.Name == "." || file.Name == "..")
  548. continue;
  549. if (file.IsDirectory)
  550. {
  551. // 🔁 Recurse into subdirectories
  552. string subPath = remotePath.TrimEnd('/') + "/" + file.Name;
  553. allFiles.AddRange(GetAllFiles(session, subPath));
  554. }
  555. else
  556. {
  557. allFiles.Add(file);
  558. }
  559. }
  560. }
  561. catch (Exception ex)
  562. {
  563. Console.WriteLine($"❌ Error while listing {remotePath}: {ex.Message}");
  564. }
  565. return allFiles;
  566. }
  567. static string FindCasesFolder(Session session, string parentPath, string localWorkRoot)
  568. {
  569. try
  570. {
  571. // ✅ Step 1: First search locally (parentPath could already have extracted ZIPs)
  572. var foundLocal = Directory.GetDirectories(parentPath, "*", SearchOption.AllDirectories)
  573. .OrderBy(d =>
  574. {
  575. string lname = Path.GetFileName(d).ToLowerInvariant();
  576. if (lname == "09_cases") return 0;
  577. if (lname == "14_cases") return 1;
  578. return 2;
  579. })
  580. .FirstOrDefault(d =>
  581. d.EndsWith("cases", StringComparison.OrdinalIgnoreCase) ||
  582. d.ToLowerInvariant().Contains("supreme court opinions") ||
  583. d.ToLowerInvariant().Contains("court of appeal opinions") ||
  584. d.ToLowerInvariant().Contains("appellate division of superior court opinions") ||
  585. d.ToLowerInvariant().Contains("mchgn supreme court cases") ||
  586. d.ToLowerInvariant().Contains("cases accepted by supreme court"));
  587. if (foundLocal != null)
  588. {
  589. Console.WriteLine($"📂 Found cases locally: {foundLocal}");
  590. return foundLocal;
  591. }
  592. // ✅ Step 2: If not found, check remote directory for zip files
  593. RemoteDirectoryInfo list = session.ListDirectory(parentPath);
  594. foreach (RemoteFileInfo file in list.Files)
  595. {
  596. if (!file.IsDirectory && file.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
  597. {
  598. string remoteZipPath = BuildRemotePath(parentPath, file.Name);
  599. string extractPath = Path.Combine(localWorkRoot, Path.GetFileNameWithoutExtension(file.Name));
  600. Console.WriteLine($"📦 Extracting zip: {file.Name} → {extractPath}");
  601. OutLookreader.ExtractZip(session, remoteZipPath, extractPath);
  602. // ✅ After extraction, search inside extracted folder
  603. var foundInZip = Directory.GetDirectories(extractPath, "*", SearchOption.AllDirectories)
  604. .OrderBy(d =>
  605. {
  606. string lname = Path.GetFileName(d).ToLowerInvariant();
  607. if (lname == "09_cases") return 0;
  608. if (lname == "14_cases") return 1;
  609. return 2;
  610. })
  611. .FirstOrDefault(d =>
  612. d.EndsWith("cases", StringComparison.OrdinalIgnoreCase) ||
  613. d.ToLowerInvariant().Contains("supreme court opinions") ||
  614. d.ToLowerInvariant().Contains("court of appeal opinions") ||
  615. d.ToLowerInvariant().Contains("appellate division of superior court opinions") ||
  616. d.ToLowerInvariant().Contains("mchgn supreme court cases") ||
  617. d.ToLowerInvariant().Contains("cases accepted by supreme court"));
  618. if (foundInZip != null)
  619. {
  620. Console.WriteLine($"📂 Found cases inside extracted zip: {foundInZip}");
  621. return foundInZip;
  622. }
  623. }
  624. }
  625. }
  626. catch (Exception ex)
  627. {
  628. Console.WriteLine($"❌ FindCasesFolder failed for '{parentPath}': {ex.Message}");
  629. }
  630. return null; // nothing found
  631. }
  632. static string FindLocalCasesFolder(string parentPath)
  633. {
  634. try
  635. {
  636. // ✅ Search in local Parent path for priority cases folders
  637. var found = Directory.GetDirectories(parentPath, "*", SearchOption.AllDirectories)
  638. .OrderBy(d =>
  639. {
  640. string lname = Path.GetFileName(d).ToLowerInvariant();
  641. if (lname == "09_cases") return 0;
  642. if (lname == "14_cases") return 1;
  643. return 2;
  644. })
  645. .FirstOrDefault(d =>
  646. d.EndsWith("cases", StringComparison.OrdinalIgnoreCase) ||
  647. d.ToLowerInvariant().Contains("supreme court opinions") ||
  648. d.ToLowerInvariant().Contains("court of appeal opinions") ||
  649. d.ToLowerInvariant().Contains("appellate division of superior court opinions") ||
  650. d.ToLowerInvariant().Contains("mchgn supreme court cases") ||
  651. d.ToLowerInvariant().Contains("cases accepted by supreme court"));
  652. if (found != null)
  653. {
  654. Console.WriteLine($"📂 Found local cases folder: {found}");
  655. return found;
  656. }
  657. }
  658. catch (Exception ex)
  659. {
  660. Console.WriteLine($"❌ FindLocalCasesFolder failed for '{parentPath}': {ex.Message}");
  661. }
  662. return null;
  663. }
  664. static void ProcessDirectory(Session session, string remotePath, RemoteDirectoryInfo list, string localWorkRoot, bool isTopLevel = true)
  665. {
  666. //RemoteDirectoryInfo list;,
  667. string reportsName = string.Empty;
  668. string reportid = string.Empty;
  669. if (isTopLevel == false)
  670. {
  671. try
  672. {
  673. list = session.ListDirectory(remotePath);
  674. }
  675. catch (Exception ex)
  676. {
  677. Console.WriteLine($"Failed to list '{remotePath}': {ex.Message}");
  678. return;
  679. }
  680. }
  681. // Separate date folders, non-date folders, and zip files
  682. List<RemoteFileInfo> dateFolders = new List<RemoteFileInfo>();
  683. List<RemoteFileInfo> nonDateFolders = new List<RemoteFileInfo>();
  684. List<RemoteFileInfo> zipFiles = new List<RemoteFileInfo>();
  685. // Directories to ignore at top level
  686. //string[] ignoreTopLevelDirs = { "DCL", "Massachusetts", "Paginated Reporters" };
  687. //var files = from p in list.Files
  688. // where p.IsDirectory
  689. // && p.Name != "."
  690. // && p.Name != ".."
  691. // select p;
  692. //if (files.Count() > 0)
  693. //{
  694. foreach (var obj in list.Files)
  695. {
  696. var file = obj as RemoteFileInfo;
  697. if (file == null || file.Name == "." || file.Name == "..") continue;
  698. // ✅ Skip ignored directories at top-level only
  699. //if (isTopLevel && file.IsDirectory &&
  700. // ignoreTopLevelDirs.Any(d => file.Name.Equals(d, StringComparison.OrdinalIgnoreCase)))
  701. //{
  702. // Console.WriteLine($"Skipping top-level directory: {file.Name}");
  703. // continue;
  704. //}
  705. //string[] parts = file.FullName.TrimStart('/', '\\')
  706. // .Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
  707. //reportsName = parts.Length > 0 ? parts[0] : string.Empty;
  708. if (file.IsDirectory && IsDateFolder(file.Name))
  709. {
  710. int regexid = GetMatchedPatternId(file.Name);
  711. string folderDate = NormalizeToStandardDate(file.Name);
  712. //if (TryGetFolderDate(file.Name, out DateTime folderDate))
  713. //{
  714. lastdate1 = DateTime.ParseExact(lastdate, "yyyy_MM_dd", CultureInfo.InvariantCulture);
  715. DateTime folderDate_out = DateTime.ParseExact(folderDate, "yyyy_MM_dd", CultureInfo.InvariantCulture);
  716. Console.WriteLine("date folder: {" + lastdate1 + "}");
  717. if (folderDate_out >= lastdate1)
  718. {
  719. dateFolders.Add(file);
  720. }
  721. else
  722. {
  723. Console.WriteLine($"🕒 Skipping old date folder {file.Name} (< {lastdate:yyyy-MM-dd})");
  724. }
  725. //}
  726. //else
  727. //{
  728. // Console.WriteLine($"⚠️ Could not parse date folder: {file.Name}");
  729. //}
  730. }
  731. else if (file.IsDirectory)
  732. {
  733. nonDateFolders.Add(file);
  734. }
  735. else if (file.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
  736. {
  737. zipFiles.Add(file);
  738. }
  739. }
  740. //}
  741. // Process date folders
  742. foreach (var dateFolder in dateFolders)
  743. {
  744. string dateRemotePath = BuildRemotePath(remotePath, dateFolder.Name);
  745. //Console.WriteLine($"\n=== Processing DATE folder: {dateRemotePath} ===");
  746. //string dateRemotePath = BuildRemotePath(session,"/Maryland/2025_09_26", "Maryland");
  747. // Process each date folder: find cases and BMPs
  748. ProcessDateFolder(session, dateRemotePath, localWorkRoot);
  749. // ProcessDateFolder(session, "/Maryland/2025_10_17", localWorkRoot);
  750. //ProcessDateFolder(session, "/North Eastern Reporter/N.E.3d/2025_08_13", localWorkRoot, "North Eastern Reporter");
  751. }
  752. // Recurse into non-date folders
  753. foreach (var nonDate in nonDateFolders)
  754. {
  755. string nonDateRemote = BuildRemotePath(remotePath.ToString(), nonDate.Name);
  756. Console.WriteLine($"\n--- Recursing into non-date folder: {nonDateRemote} ---");
  757. ProcessDirectory(session, nonDateRemote, list, localWorkRoot, false);
  758. ProcessDateFolder(session,"","");
  759. }
  760. // Handle zip files at this level (optional, skip top-level)
  761. foreach (var zip in zipFiles)
  762. {
  763. string zipRemote = BuildRemotePath(remotePath, zip.Name);
  764. if (isTopLevel)
  765. {
  766. Console.WriteLine($"Skipping top-level zip: {zipRemote}");
  767. }
  768. else
  769. {
  770. try
  771. {
  772. string extractPath = Path.Combine(localWorkRoot, Path.GetFileNameWithoutExtension(zip.Name));
  773. OutLookreader.ExtractZip(session, zipRemote, extractPath);
  774. }
  775. catch (Exception ex)
  776. {
  777. Console.WriteLine($"Failed to extract zip '{zipRemote}': {ex.Message}");
  778. }
  779. }
  780. }
  781. }
  782. public static bool TryGetFolderDate(string fileName, out DateTime folderDate)
  783. {
  784. folderDate = DateTime.MinValue;
  785. if (string.IsNullOrWhiteSpace(fileName))
  786. return false;
  787. string cleaned = fileName.Trim();
  788. // Replace separators
  789. cleaned = cleaned.Replace("_", "-").Replace(".", "-");
  790. // Split by special characters (for cases like "3d-2021_07_12-F")
  791. string[] parts = cleaned.Split(new char[] { '-', ' ', 'A', 'B', 'C', 'D', 'F' },
  792. StringSplitOptions.RemoveEmptyEntries);
  793. foreach (var part in parts)
  794. {
  795. if (DateTime.TryParseExact(part,
  796. new[] { "yyyy-MM-dd", "yyyy.MM.dd", "yyyy_MM_dd", "yyyyMMdd" },
  797. CultureInfo.InvariantCulture,
  798. DateTimeStyles.None,
  799. out folderDate))
  800. {
  801. return true;
  802. }
  803. }
  804. // Try fallback full parse
  805. if (DateTime.TryParse(cleaned, out folderDate))
  806. {
  807. return true;
  808. }
  809. // If all fails, return false
  810. return false;
  811. }
  812. public static string NormalizeToStandardDate(string fileName)
  813. {
  814. if (string.IsNullOrWhiteSpace(fileName))
  815. return null;
  816. // Extract possible date part
  817. string cleaned = fileName.Trim();
  818. // Replace common separators with '-'
  819. cleaned = cleaned.Replace("_", "-").Replace(".", "-");
  820. // Handle ranges or suffixes like “2023-07-05-14” or “3d-2021-07-12-F”
  821. // Keep only the first valid date portion
  822. string[] parts = cleaned.Split(new char[] { '-', ' ', 'A', 'B', 'C', 'D', 'F' },
  823. StringSplitOptions.RemoveEmptyEntries);
  824. DateTime parsedDate;
  825. foreach (var part in parts)
  826. {
  827. if (DateTime.TryParseExact(part,
  828. new[] { "yyyy-MM-dd", "yyyy-MM", "yyyyMMdd", "yyyy.MM.dd", "yyyy_MM_dd" },
  829. CultureInfo.InvariantCulture,
  830. DateTimeStyles.None,
  831. out parsedDate))
  832. {
  833. return parsedDate.ToString("yyyy_MM_dd");
  834. }
  835. }
  836. // Fallback: try general parse if specific formats fail
  837. if (DateTime.TryParse(cleaned, out parsedDate))
  838. {
  839. return parsedDate.ToString("yyyy_MM_dd");
  840. }
  841. // If all fails, return today's date (or you could return null)
  842. return DateTime.Now.ToString("yyyy_MM_dd");
  843. }
  844. static string FindCasesFolderOnFtp(Session session, string parentRemotePath)
  845. {
  846. try
  847. {
  848. RemoteDirectoryInfo list = session.ListDirectory(parentRemotePath);
  849. // collect candidate case folders with priority
  850. var candidateDirs = list.Files
  851. .Where(f => f.IsDirectory)
  852. .Select(f => new
  853. {
  854. Info = f,
  855. Priority = GetPriority(f.Name)
  856. })
  857. .Where(x => x.Priority >= 0) // only case-like folders
  858. .OrderBy(x => x.Priority)
  859. .ToList();
  860. if (candidateDirs.Any())
  861. {
  862. var best = candidateDirs.First();
  863. string remoteCasesPath = BuildRemotePath(parentRemotePath, best.Info.Name);
  864. Console.WriteLine($"📂 Found cases folder on FTP: {remoteCasesPath} (priority {best.Priority})");
  865. return remoteCasesPath;
  866. }
  867. // Step 2: No direct cases folder → look for ZIP file
  868. foreach (RemoteFileInfo file in list.Files)
  869. {
  870. if (!file.IsDirectory && file.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
  871. {
  872. string remoteZipPath = BuildRemotePath(parentRemotePath, file.Name);
  873. Console.WriteLine($"📦 Found zip file on FTP: {remoteZipPath}");
  874. return remoteZipPath; // caller should handle extraction
  875. }
  876. }
  877. }
  878. catch (Exception ex)
  879. {
  880. Console.WriteLine($"❌ FindCasesFolderOnFtp failed for '{parentRemotePath}': {ex.Message}");
  881. }
  882. return null; // Nothing found
  883. }
  884. /// <summary>
  885. /// Assign priority for cases-like folder names.
  886. /// </summary>
  887. static int GetPriority(string folderName)
  888. {
  889. string lname = folderName.ToLowerInvariant();
  890. if (lname == "09_cases") return 0;
  891. if (lname == "14_cases") return 1;
  892. if (lname.EndsWith("cases") ||
  893. lname.Contains("supreme court opinions") ||
  894. lname.Contains("court of appeal opinions") ||
  895. lname.Contains("appellate division of superior court opinions") ||
  896. lname.Contains("mchgn supreme court cases") ||
  897. lname.Contains("cases accepted by supreme court"))
  898. return 2;
  899. return -1; // not a cases folder
  900. }
  901. // -------------------------
  902. // Process date folder logic
  903. // -------------------------
  904. // static void ProcessDateFolder(Session session, string dateRemotePath, string localWorkRoot)
  905. // {
  906. // try
  907. // {
  908. // int bmpcount = 0;
  909. // string dateFolderName = Path.GetFileName(dateRemotePath);
  910. // var dbOps = new FileDownloadingDBOperations();
  911. // // 🔹 Get project and reporter info
  912. // string projectCode = ConfigurationManager.AppSettings["VLEX"].ToString();
  913. // string projectid = FileDownloadingDBOperations.getscallervalue(
  914. // $"SELECT projectdefinitionid::varchar FROM tblprojectdefinition WHERE project_subtitle='{projectCode.Trim()}'"
  915. // );
  916. // string reportid = FileDownloadingDBOperations.getscallervalue(
  917. // $"SELECT reporter_key::varchar FROM tblvlexreporterkey WHERE reporter_name='{reportsName}'"
  918. // );
  919. // string reportidname = FileDownloadingDBOperations.getscallervalue(
  920. // $"SELECT reporterid::varchar FROM tblvlexreporterkey WHERE reporter_name='{reportsName}'");
  921. // string jobTitle = $"{reportid}_2025_09_26";
  922. // string lotTitle = $"{reportid}_2025_09_26";
  923. // lotTitle = string.Concat(lotTitle, "01");
  924. // // ✅ Check if job already exists
  925. // string existingJobId = FileDownloadingDBOperations.getscallervalue(
  926. // $"SELECT jobid::varchar FROM tbljob WHERE jobname = '{jobTitle}'"
  927. // );
  928. // if (!string.IsNullOrEmpty(existingJobId))
  929. // {
  930. // Console.WriteLine($"Job '{jobTitle}' already exists. Skipping folder.");
  931. // return;
  932. // }
  933. // // 1️⃣ List all files/folders in date folder
  934. // RemoteDirectoryInfo dateFolderList = session.ListDirectory(dateRemotePath);
  935. // // 2️⃣ Check for ZIP files
  936. // var zipFiles = dateFolderList.Files
  937. // .Where(f => !f.IsDirectory && f.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
  938. // .ToList();
  939. // // If ZIPs exist but none updated today → skip folder
  940. // // var zipFilesToday = zipFiles.Where(z => z.LastWriteTime.Date == DateTime.Today).ToList();
  941. // // var zipFilesToday = zipFiles
  942. // //.Where(z => z.LastWriteTime.Date == DateTime.Today.AddDays(-1))
  943. // //.ToList();
  944. // var zipFilesToday = zipFiles
  945. //.Where(z => z.LastWriteTime.Date == DateTime.Today)
  946. //.ToList();
  947. // // var zipFilesToday = new List<string> { "NW2d - 2022_11_01.zip" };
  948. // if (zipFiles.Count > 0 && zipFilesToday.Count == 0)
  949. // {
  950. // Console.WriteLine($"ZIP files exist but none updated today in {dateRemotePath}. Skipping folder.");
  951. // return; // skip this date folder entirely
  952. // }
  953. // // 3️⃣ Check for BMP if no ZIP today
  954. // string casesFolderRemotePath = null;
  955. // bool bmpUpdatedToday = false;
  956. // List<RemoteFileInfo> bmpFilesToday = null;
  957. // if (zipFilesToday.Count == 0)
  958. // {
  959. // casesFolderRemotePath = FindCasesFolderOnFtp(session, dateRemotePath);
  960. // if (casesFolderRemotePath != null)
  961. // {
  962. // // 🔎 Get BMPs updated yesterday (including subfolders)
  963. // //bmpFilesToday = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  964. // //bmpUpdatedToday = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  965. // //if (bmpUpdatedToday == false)
  966. // //{
  967. // // return;
  968. // //}
  969. // (bool found, int count) = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  970. // bmpUpdatedToday = found;
  971. // if (!found)
  972. // {
  973. // Console.WriteLine("❌ No BMPs updated yesterday.");
  974. // //return;
  975. // }
  976. // bmpcount = count;
  977. // Console.WriteLine($"✅ Total BMPs found: {count}");
  978. // //Console.WriteLine($"📂 Found {bmpFilesToday.Count} BMP files updated yesterday in {casesFolderRemotePath}.");
  979. // // proceed with job creation, email sending, etc.
  980. // //foreach (var bmp in bmpFilesToday)
  981. // //{
  982. // // Console.WriteLine($"➡ {bmp.FullName} ({bmp.LastWriteTime})");
  983. // //}
  984. // }
  985. // else
  986. // {
  987. // Console.WriteLine($"No ZIP or cases folder found in {dateRemotePath}. Skipping folder.");
  988. // return;
  989. // }
  990. // }
  991. // // if (zipFilesToday.Count == 0)
  992. // // {
  993. // // casesFolderRemotePath = FindCasesFolderOnFtp(session, dateRemotePath);
  994. // // if (casesFolderRemotePath != null)
  995. // // {
  996. // // RemoteDirectoryInfo casesList = session.ListDirectory(casesFolderRemotePath);
  997. // // // bmpUpdatedToday = casesList.Files
  998. // // // .Any(f => !f.IsDirectory &&
  999. // // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1000. // // // f.LastWriteTime.Date == DateTime.Today);
  1001. // // // bmpFilesToday = casesList.Files
  1002. // // //.Where(f => !f.IsDirectory &&
  1003. // // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1004. // // // f.LastWriteTime.Date == DateTime.Today)
  1005. // // //.ToList();
  1006. // // bmpUpdatedToday = casesList.Files
  1007. // //.Any(f => !f.IsDirectory &&
  1008. // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1009. // // f.LastWriteTime.Date == DateTime.Today.AddDays(-1));
  1010. // // bmpFilesToday = casesList.Files
  1011. // // .Where(f => !f.IsDirectory &&
  1012. // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1013. // // f.LastWriteTime.Date == DateTime.Today.AddDays(-1)).ToList();
  1014. // // if (!bmpUpdatedToday)
  1015. // // {
  1016. // // Console.WriteLine($"No BMP files updated today in {casesFolderRemotePath}. Skipping folder.");
  1017. // // return;
  1018. // // }
  1019. // // }
  1020. // // else
  1021. // // {
  1022. // // Console.WriteLine($"No ZIP or cases folder found in {dateRemotePath}. Skipping folder.");
  1023. // // return;
  1024. // // }
  1025. // // }
  1026. // // 🔹 Send email before creating job
  1027. // if (zipFilesToday.Count > 0)
  1028. // {
  1029. // //SendEmail(
  1030. // // subject: $"ZIP File Found - {reportsName}",
  1031. // // bodyContent: $"ZIP file(s) updated today found in {dateRemotePath}. Job '{jobTitle}' will be created and ZIPs will be extracted."
  1032. // //);
  1033. // }
  1034. // else if (bmpUpdatedToday)
  1035. // {
  1036. // // SendEmail(
  1037. // //subject: $"BMP Files Found - {reportsName}",
  1038. // //folderPath: $"BMP files were updated today in {casesFolderRemotePath}",
  1039. // //fileCount: $"{bmpcount}.");
  1040. // }
  1041. // // 🔹 Create job
  1042. // int jobId = FileDownloadingDBOperations.fncreatejob(projectid, jobTitle, jobTitle, dateFolderName);
  1043. // //if (jobId <= 0)
  1044. // //{
  1045. // // Console.WriteLine($"Failed to create job '{jobTitle}'. Skipping folder.");
  1046. // // return;
  1047. // //}
  1048. // FileDownloadingDBOperations.fncreatejobconfiguration(jobId.ToString(), projectid);
  1049. // // 🔹 Get job path
  1050. // string jobPath = dbOps.GetServerPath(jobId.ToString(), false, "serverinfo_job");
  1051. // if (string.IsNullOrEmpty(jobPath))
  1052. // {
  1053. // //Console.WriteLine($"Warning: GetServerPath returned empty for job {jobId}. Skipping folder.");
  1054. // return;
  1055. // }
  1056. // string parentPath = Path.Combine(jobPath, "Parent");
  1057. // Directory.CreateDirectory(parentPath);
  1058. // // 🔹 Process ZIP files if any updated today
  1059. // if (zipFilesToday.Count > 0)
  1060. // {
  1061. // foreach (var zip in zipFilesToday)
  1062. // {
  1063. // // OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zip.Name), parentPath);
  1064. // //string zipFileName = "NW2d - 2022_11_01.zip";
  1065. // // Use it directly instead of zip.Name
  1066. // // OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zipFileName), parentPath);
  1067. // OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zip.Name), parentPath);
  1068. // localWorkRoot = Path.Combine(parentPath, zip.Name);
  1069. // }
  1070. // // After extraction, find the cases folder locally inside Parent
  1071. // //string extractedCasesFolder = FindCasesFolder(session, parentPath, localWorkRoot); // local path
  1072. // //if (string.IsNullOrEmpty(extractedCasesFolder))
  1073. // //{
  1074. // // Console.WriteLine($"No cases folder found inside extracted ZIP. Skipping job processing.");
  1075. // // return;
  1076. // //}
  1077. // // 🔹 Process the cases folder
  1078. // //ProcessCasesFolder(session, extractedCasesFolder, jobId, jobPath, reportsName, lotTitle, reportidname);
  1079. // }
  1080. // // 🔹 Process BMP files if no ZIP today
  1081. // else if (bmpUpdatedToday)
  1082. // {
  1083. // // ✅ Download the full date folder (including subfolders)
  1084. // //session.SynchronizeDirectories(
  1085. // // SynchronizationMode.Local,
  1086. // // parentPath, // Local target path
  1087. // // dateRemotePath, // Remote source path
  1088. // // false // Do not remove extra local files
  1089. // //).Check();
  1090. // //if (extractedCasesFolder == null)
  1091. // //{
  1092. // // Console.WriteLine("No cases folder found under Parent. Skipping processing.");
  1093. // // return;
  1094. // //}
  1095. // // ✅ Process the cases folder
  1096. // }
  1097. // string extractedCasesFolder = FindLocalCasesFolder(parentPath);
  1098. // //ProcessCasesFolder(session, extractedCasesFolder, jobId, jobPath, reportsName, lotTitle, reportidname);
  1099. // Console.WriteLine($"Job '{jobTitle}' created and processed successfully.");
  1100. // }
  1101. // catch (Exception ex)
  1102. // {
  1103. // Console.WriteLine($"Error processing date folder '{dateRemotePath}': {ex.Message}");
  1104. // }
  1105. // }
  1106. static void ProcessDateFolder(Session session, string dateRemotePath, string localWorkRoot)
  1107. {
  1108. try
  1109. {
  1110. int bmpcount = 0;
  1111. string dateFolderName = Path.GetFileName(dateRemotePath);
  1112. var dbOps = new FileDownloadingDBOperations();
  1113. // 🔹 Get project and reporter info
  1114. string projectCode = ConfigurationManager.AppSettings["VLEX"].ToString();
  1115. string projectid = FileDownloadingDBOperations.getscallervalue(
  1116. $"SELECT projectdefinitionid::varchar FROM tblprojectdefinition WHERE project_subtitle='{projectCode.Trim()}'"
  1117. );
  1118. string reportid = FileDownloadingDBOperations.getscallervalue(
  1119. $"SELECT reporter_key::varchar FROM tblvlexreporterkey WHERE reporter_name='{reportsName}'"
  1120. );
  1121. string reportidname = FileDownloadingDBOperations.getscallervalue(
  1122. $"SELECT reporterid::varchar FROM tblvlexreporterkey WHERE reporter_name='{reportsName}'");
  1123. string jobTitle = $"{reportid}_{dateFolderName}";
  1124. string lotTitle = $"{reportid}_{dateFolderName}";
  1125. lotTitle = string.Concat(lotTitle, "01");
  1126. // ✅ Check if job already exists
  1127. string existingJobId = FileDownloadingDBOperations.getscallervalue(
  1128. $"SELECT jobid::varchar FROM tbljob WHERE jobname = '{jobTitle}'"
  1129. );
  1130. if (!string.IsNullOrEmpty(existingJobId))
  1131. {
  1132. Console.WriteLine($"Job '{jobTitle}' already exists. Skipping folder.");
  1133. return;
  1134. }
  1135. // 1️⃣ List all files/folders in date folder
  1136. RemoteDirectoryInfo dateFolderList = session.ListDirectory(dateRemotePath);
  1137. // 2️⃣ Check for ZIP files
  1138. var zipFiles = dateFolderList.Files
  1139. .Where(f => !f.IsDirectory && f.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
  1140. .ToList();
  1141. // If ZIPs exist but none updated today → skip folder
  1142. // var zipFilesToday = zipFiles.Where(z => z.LastWriteTime.Date == DateTime.Today).ToList();
  1143. var zipFilesToday = zipFiles
  1144. .Where(z => z.LastWriteTime.Date == DateTime.Today.AddDays(-1))
  1145. .ToList();
  1146. // var zipFilesToday = new List<string> { "NW2d - 2022_11_01.zip" };
  1147. if (zipFiles.Count > 0 && zipFilesToday.Count == 0)
  1148. {
  1149. Console.WriteLine($"ZIP files exist but none updated today in {dateRemotePath}. Skipping folder.");
  1150. return; // skip this date folder entirely
  1151. }
  1152. // 3️⃣ Check for BMP if no ZIP today
  1153. string casesFolderRemotePath = null;
  1154. bool bmpUpdatedToday = false;
  1155. List<RemoteFileInfo> bmpFilesToday = null;
  1156. if (zipFilesToday.Count == 0)
  1157. {
  1158. casesFolderRemotePath = FindCasesFolderOnFtp(session, dateRemotePath);
  1159. if (casesFolderRemotePath != null)
  1160. {
  1161. // 🔎 Get BMPs updated yesterday (including subfolders)
  1162. //bmpFilesToday = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  1163. //bmpUpdatedToday = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  1164. //if (bmpUpdatedToday == false)
  1165. //{
  1166. // return;
  1167. //}
  1168. (bool found, int count) = GetTodayUpdatedBmps(session, casesFolderRemotePath);
  1169. bmpUpdatedToday = found;
  1170. if (!found)
  1171. {
  1172. //Console.WriteLine("❌ No BMPs updated yesterday.");
  1173. // return;
  1174. RemoteDirectoryInfo casesList = session.ListDirectory(casesFolderRemotePath);
  1175. int totalFiles = casesList.Files.Count(f => !f.IsDirectory && f.Name != "." && f.Name != "..");
  1176. if (totalFiles == 0)
  1177. {
  1178. // Get folder date from name (e.g. 2025_11_10)
  1179. string dateFolderName_ = Path.GetFileName(dateRemotePath);
  1180. if (DateTime.TryParseExact(dateFolderName_, "yyyy_MM_dd", CultureInfo.InvariantCulture,
  1181. DateTimeStyles.None, out DateTime folderDate))
  1182. {
  1183. // Compare with Cases folder LastWriteTime
  1184. RemoteFileInfo folderMeta = session
  1185. .EnumerateRemoteFiles(Path.GetDirectoryName(casesFolderRemotePath), "*", EnumerationOptions.AllDirectories)
  1186. .FirstOrDefault(f => f.Name.Equals(Path.GetFileName(casesFolderRemotePath), StringComparison.OrdinalIgnoreCase));
  1187. if (folderMeta != null && folderMeta.LastWriteTime.Date == folderDate.Date)
  1188. {
  1189. Console.WriteLine($" 'Cases' folder has 0 files but date matches {folderDate:yyyy-MM-dd}. Creating empty job...");
  1190. int jobId_ = FileDownloadingDBOperations.fncreatejob(projectid, jobTitle, jobTitle, dateFolderName);
  1191. if (jobId_ > 0)
  1192. {
  1193. FileDownloadingDBOperations.fncreatejobconfiguration(jobId_.ToString(), projectid);
  1194. string jobPath_ = dbOps.GetServerPath(jobId_.ToString(), false, "serverinfo_job");
  1195. if (!string.IsNullOrEmpty(jobPath_))
  1196. {
  1197. string parentPath_ = Path.Combine(jobPath_, "Parent");
  1198. Directory.CreateDirectory(parentPath_);
  1199. Console.WriteLine($" Empty job '{jobTitle}' created successfully (Cases folder empty).");
  1200. }
  1201. else
  1202. {
  1203. Console.WriteLine(" Job path not found, skipping further processing.");
  1204. }
  1205. }
  1206. else
  1207. {
  1208. Console.WriteLine("Job creation failed.");
  1209. }
  1210. return;
  1211. }
  1212. else
  1213. {
  1214. Console.WriteLine($" 'Cases' folder empty but date does not match {folderDate:yyyy-MM-dd}. Skipping.");
  1215. return;
  1216. }
  1217. }
  1218. else
  1219. {
  1220. Console.WriteLine($" Could not parse date folder name '{dateFolderName_}'. Skipping.");
  1221. return;
  1222. }
  1223. }
  1224. else
  1225. {
  1226. Console.WriteLine($"No BMPs but {totalFiles} other files exist. Skipping.");
  1227. return;
  1228. }
  1229. }
  1230. bmpcount = count;
  1231. Console.WriteLine($"✅ Total BMPs found: {count}");
  1232. //Console.WriteLine($"📂 Found {bmpFilesToday.Count} BMP files updated yesterday in {casesFolderRemotePath}.");
  1233. // proceed with job creation, email sending, etc.
  1234. //foreach (var bmp in bmpFilesToday)
  1235. //{
  1236. // Console.WriteLine($"➡ {bmp.FullName} ({bmp.LastWriteTime})");
  1237. //}
  1238. }
  1239. else
  1240. {
  1241. Console.WriteLine($"No ZIP or cases folder found in {dateRemotePath}. Skipping folder.");
  1242. return;
  1243. }
  1244. }
  1245. // if (zipFilesToday.Count == 0)
  1246. // {
  1247. // casesFolderRemotePath = FindCasesFolderOnFtp(session, dateRemotePath);
  1248. // if (casesFolderRemotePath != null)
  1249. // {
  1250. // RemoteDirectoryInfo casesList = session.ListDirectory(casesFolderRemotePath);
  1251. // // bmpUpdatedToday = casesList.Files
  1252. // // .Any(f => !f.IsDirectory &&
  1253. // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1254. // // f.LastWriteTime.Date == DateTime.Today);
  1255. // // bmpFilesToday = casesList.Files
  1256. // //.Where(f => !f.IsDirectory &&
  1257. // // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1258. // // f.LastWriteTime.Date == DateTime.Today)
  1259. // //.ToList();
  1260. // bmpUpdatedToday = casesList.Files
  1261. //.Any(f => !f.IsDirectory &&
  1262. // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1263. // f.LastWriteTime.Date == DateTime.Today.AddDays(-1));
  1264. // bmpFilesToday = casesList.Files
  1265. // .Where(f => !f.IsDirectory &&
  1266. // f.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
  1267. // f.LastWriteTime.Date == DateTime.Today.AddDays(-1)).ToList();
  1268. // if (!bmpUpdatedToday)
  1269. // {
  1270. // Console.WriteLine($"No BMP files updated today in {casesFolderRemotePath}. Skipping folder.");
  1271. // return;
  1272. // }
  1273. // }
  1274. // else
  1275. // {
  1276. // Console.WriteLine($"No ZIP or cases folder found in {dateRemotePath}. Skipping folder.");
  1277. // return;
  1278. // }
  1279. // }
  1280. // 🔹 Send email before creating job
  1281. // 🔹 Create job
  1282. int jobId = FileDownloadingDBOperations.fncreatejob(projectid, jobTitle, jobTitle, dateFolderName);
  1283. if (jobId <= 0)
  1284. {
  1285. Console.WriteLine($"Failed to create job '{jobTitle}'. Skipping folder.");
  1286. return;
  1287. }
  1288. FileDownloadingDBOperations.fncreatejobconfiguration(jobId.ToString(), projectid);
  1289. // 🔹 Get job path
  1290. string jobPath = dbOps.GetServerPath(jobId.ToString(), false, "serverinfo_job");
  1291. if (string.IsNullOrEmpty(jobPath))
  1292. {
  1293. Console.WriteLine($"Warning: GetServerPath returned empty for job {jobId}. Skipping folder.");
  1294. return;
  1295. }
  1296. string parentPath = Path.Combine(jobPath, "Parent");
  1297. Directory.CreateDirectory(parentPath);
  1298. // 🔹 Process ZIP files if any updated today
  1299. if (zipFilesToday.Count > 0)
  1300. {
  1301. foreach (var zip in zipFilesToday)
  1302. {
  1303. // OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zip.Name), parentPath);
  1304. //string zipFileName = "NW2d - 2022_11_01.zip";
  1305. // Use it directly instead of zip.Name
  1306. // OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zipFileName), parentPath);
  1307. OutLookreader.ExtractZip(session, BuildRemotePath(dateRemotePath, zip.Name), parentPath);
  1308. localWorkRoot = Path.Combine(parentPath, zip.Name);
  1309. }
  1310. // After extraction, find the cases folder locally inside Parent
  1311. string extractedCasesFolder = FindCasesFolder(session, parentPath, localWorkRoot); // local path
  1312. if (string.IsNullOrEmpty(extractedCasesFolder))
  1313. {
  1314. Console.WriteLine($"No cases folder found inside extracted ZIP. Skipping job processing.");
  1315. return;
  1316. }
  1317. // 🔹 Process the cases folder
  1318. ProcessCasesFolder(session, extractedCasesFolder, jobId, jobPath, reportsName, lotTitle, reportidname, bmpcount);
  1319. }
  1320. // 🔹 Process BMP files if no ZIP today
  1321. else if (bmpUpdatedToday)
  1322. {
  1323. // ✅ Download the full date folder (including subfolders)
  1324. session.SynchronizeDirectories(
  1325. SynchronizationMode.Local,
  1326. parentPath, // Local target path
  1327. dateRemotePath, // Remote source path
  1328. false // Do not remove extra local files
  1329. ).Check();
  1330. string extractedCasesFolder = FindLocalCasesFolder(parentPath);
  1331. if (extractedCasesFolder == null)
  1332. {
  1333. Console.WriteLine("No cases folder found under Parent. Skipping processing.");
  1334. return;
  1335. }
  1336. // ✅ Process the cases folder
  1337. ProcessCasesFolder(session, extractedCasesFolder, jobId, jobPath, reportsName, lotTitle, reportidname, bmpcount);
  1338. }
  1339. if (zipFilesToday.Count > 0)
  1340. {
  1341. SendEmail(
  1342. subject: $"ZIP File Found - {reportsName}",
  1343. bodyContent: $"ZIP file(s) updated today found in {dateRemotePath}. Job '{jobTitle}' will be created and ZIPs will be extracted."
  1344. );
  1345. }
  1346. else if (bmpUpdatedToday)
  1347. {
  1348. SendEmail(
  1349. subject: $"BMP Files Found - {reportsName}",
  1350. folderPath: $"BMP files were updated today in {casesFolderRemotePath}",
  1351. fileCount: $"{bmpcount}.");
  1352. }
  1353. Console.WriteLine($"Job '{jobTitle}' created and processed successfully.");
  1354. }
  1355. catch (Exception ex)
  1356. {
  1357. Console.WriteLine($"Error processing date folder '{dateRemotePath}': {ex.Message}");
  1358. }
  1359. }
  1360. // Helper method to copy directories recursively
  1361. private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
  1362. {
  1363. DirectoryInfo dir = new DirectoryInfo(sourceDirName);
  1364. if (!dir.Exists) throw new DirectoryNotFoundException($"Source directory does not exist: {sourceDirName}");
  1365. DirectoryInfo[] dirs = dir.GetDirectories();
  1366. Directory.CreateDirectory(destDirName);
  1367. foreach (FileInfo file in dir.GetFiles())
  1368. {
  1369. string tempPath = Path.Combine(destDirName, file.Name);
  1370. file.CopyTo(tempPath, true);
  1371. }
  1372. if (copySubDirs)
  1373. {
  1374. foreach (DirectoryInfo subdir in dirs)
  1375. {
  1376. string tempPath = Path.Combine(destDirName, subdir.Name);
  1377. DirectoryCopy(subdir.FullName, tempPath, copySubDirs);
  1378. }
  1379. }
  1380. }
  1381. //public static (bool found, int count) GetTodayUpdatedBmps(Session session, string remotePath)
  1382. //{
  1383. // int count = 0;
  1384. // bool found = false;
  1385. // try
  1386. // {
  1387. // RemoteDirectoryInfo dir = session.ListDirectory(remotePath);
  1388. // foreach (RemoteFileInfo file in dir.Files)
  1389. // {
  1390. // if (file.IsDirectory && file.Name != "." && file.Name != "..")
  1391. // {
  1392. // // 🔁 Recurse into subfolder
  1393. // string subPath = remotePath.TrimEnd('/') + "/" + file.Name;
  1394. // var (subFound, subCount) = GetTodayUpdatedBmps(session, subPath);
  1395. // if (subFound)
  1396. // found = true;
  1397. // count += subCount;
  1398. // }
  1399. // else if (!file.IsDirectory && file.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
  1400. // {
  1401. // DateTime fileDate = file.LastWriteTime.ToLocalTime().Date;
  1402. // DateTime targetDate = DateTime.Today.AddDays(-1);
  1403. // if (fileDate == targetDate)
  1404. // {
  1405. // Console.WriteLine($"✅ Found BMP updated on {fileDate}: {file.Name}");
  1406. // count++;
  1407. // found = true;
  1408. // }
  1409. // else
  1410. // {
  1411. // Console.WriteLine($"Skipping {file.Name}, fileDate={fileDate}, expected={targetDate}");
  1412. // }
  1413. // }
  1414. // }
  1415. // }
  1416. // catch (Exception ex)
  1417. // {
  1418. // Console.WriteLine($"❌ Error while checking {remotePath}: {ex.Message}");
  1419. // }
  1420. // return (found, count);
  1421. //}
  1422. public static (bool found, int count) GetTodayUpdatedBmps(Session session, string remotePath)
  1423. {
  1424. int count = 0;
  1425. bool found = false;
  1426. try
  1427. {
  1428. RemoteDirectoryInfo dir = session.ListDirectory(remotePath);
  1429. foreach (RemoteFileInfo file in dir.Files)
  1430. {
  1431. if (file.IsDirectory && file.Name != "." && file.Name != "..")
  1432. {
  1433. // 🔁 Recurse into subfolder
  1434. string subPath = remotePath.TrimEnd('/') + "/" + file.Name;
  1435. var (subFound, subCount) = GetTodayUpdatedBmps(session, subPath);
  1436. if (subFound)
  1437. found = true;
  1438. count += subCount;
  1439. }
  1440. else if (!file.IsDirectory && file.Name.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
  1441. {
  1442. //DateTime fileDate = file.LastWriteTime.ToLocalTime().Date;
  1443. DateTime fileDate = file.LastWriteTime.ToLocalTime();
  1444. DateTime targetDate = DateTime.Today.AddDays(-1);
  1445. //DateTime targetDate = DateTime.Now.AddDays(-4);
  1446. // if (fileDate == targetDate)
  1447. if (fileDate >= targetDate && fileDate <= DateTime.Now)
  1448. {
  1449. Console.WriteLine($"✅ Found BMP updated on {fileDate}: {file.Name}");
  1450. count++;
  1451. found = true;
  1452. }
  1453. else
  1454. {
  1455. Console.WriteLine($"Skipping {file.Name}, fileDate={fileDate}, expected={targetDate}");
  1456. }
  1457. }
  1458. }
  1459. }
  1460. catch (Exception ex)
  1461. {
  1462. Console.WriteLine($"❌ Error while checking {remotePath}: {ex.Message}");
  1463. }
  1464. return (found, count);
  1465. }
  1466. private static string FindCasesFolderInternal(Session session, string parentPath, int remainingDepth)
  1467. {
  1468. RemoteDirectoryInfo list;
  1469. try
  1470. {
  1471. list = session.ListDirectory(parentPath);
  1472. }
  1473. catch (Exception ex)
  1474. {
  1475. Console.WriteLine($"FindCasesFolder -> ListDirectory failed for '{parentPath}': {ex.Message}");
  1476. return null;
  1477. }
  1478. if (list?.Files == null || list.Files.Count == 0)
  1479. {
  1480. Console.WriteLine($"FindCasesFolder -> no entries under '{parentPath}'");
  1481. return null;
  1482. }
  1483. // helper: get base name (strip any returned path parts)
  1484. string GetBaseName(string raw)
  1485. {
  1486. if (string.IsNullOrEmpty(raw)) return string.Empty;
  1487. raw = raw.Trim();
  1488. int ix = Math.Max(raw.LastIndexOf('/'), raw.LastIndexOf('\\'));
  1489. return ix >= 0 ? raw.Substring(ix + 1).Trim() : raw;
  1490. }
  1491. // collect sub-directories (skip '.' and '..')
  1492. var dirs = new List<RemoteFileInfo>();
  1493. foreach (var obj in list.Files)
  1494. {
  1495. var f = obj as RemoteFileInfo;
  1496. if (f == null) continue;
  1497. if (!f.IsDirectory) continue;
  1498. if (f.Name == "." || f.Name == "..") continue;
  1499. string raw = GetBaseName(f.Name);
  1500. if (string.IsNullOrEmpty(raw) || raw == "." || raw == "..") continue;
  1501. dirs.Add(f);
  1502. }
  1503. // 1) Highest priority: specifically 09_cases and 14_cases
  1504. foreach (var f in dirs)
  1505. {
  1506. string baseName = GetBaseName(f.Name);
  1507. string lower = baseName.ToLowerInvariant();
  1508. if (lower == "09_cases" || lower == "14_cases")
  1509. {
  1510. Console.WriteLine($"FindCasesFolder -> matched priority folder: {baseName} under {parentPath}");
  1511. return $"{parentPath}/{baseName}";
  1512. }
  1513. }
  1514. // 2) Exact "cases"
  1515. foreach (var f in dirs)
  1516. {
  1517. string baseName = GetBaseName(f.Name);
  1518. string lower = baseName.ToLowerInvariant();
  1519. if (lower == "cases")
  1520. {
  1521. Console.WriteLine($"FindCasesFolder -> matched exact 'cases': {baseName} under {parentPath}");
  1522. return $"{parentPath}/{baseName}";
  1523. }
  1524. }
  1525. // 3) Any NN_cases pattern (generic like 01_cases, 22_cases, etc.)
  1526. foreach (var f in dirs)
  1527. {
  1528. string baseName = GetBaseName(f.Name);
  1529. string lower = baseName.ToLowerInvariant();
  1530. if (Regex.IsMatch(lower, @"^\d{2}_cases$"))
  1531. {
  1532. Console.WriteLine($"FindCasesFolder -> matched generic NN_cases: {baseName} under {parentPath}");
  1533. return $"{parentPath}/{baseName}";
  1534. }
  1535. }
  1536. // 4) Other ends-with patterns (" cases" or generic "_cases")
  1537. foreach (var f in dirs)
  1538. {
  1539. string baseName = GetBaseName(f.Name);
  1540. string lower = baseName.ToLowerInvariant();
  1541. if (lower.EndsWith("_cases") || lower.EndsWith(" cases"))
  1542. {
  1543. Console.WriteLine($"FindCasesFolder -> matched ends-with 'cases' pattern: {baseName} under {parentPath}");
  1544. return $"{parentPath}/{baseName}";
  1545. }
  1546. }
  1547. // 5) Phrase matches
  1548. var phrases = new[]
  1549. {
  1550. "supreme court opinions",
  1551. "court of appeal opinions",
  1552. "appellate division of superior court opinions",
  1553. "mchgn supreme court cases",
  1554. "cases accepted by supreme court"
  1555. };
  1556. foreach (var f in dirs)
  1557. {
  1558. string baseName = GetBaseName(f.Name);
  1559. string lower = baseName.ToLowerInvariant();
  1560. foreach (var p in phrases)
  1561. {
  1562. if (lower.Contains(p))
  1563. {
  1564. Console.WriteLine($"FindCasesFolder -> matched phrase '{p}': {baseName} under {parentPath}");
  1565. return $"{parentPath}/{baseName}";
  1566. }
  1567. }
  1568. }
  1569. // 6) Recurse deeper if allowed
  1570. if (remainingDepth > 0)
  1571. {
  1572. foreach (var f in dirs)
  1573. {
  1574. string baseName = GetBaseName(f.Name);
  1575. string childPath = $"{parentPath}/{baseName}";
  1576. var found = FindCasesFolderInternal(session, childPath, remainingDepth - 1);
  1577. if (!string.IsNullOrEmpty(found)) return found;
  1578. }
  1579. }
  1580. Console.WriteLine($"FindCasesFolder -> no matching 'cases' or alternatives under '{parentPath}'");
  1581. return null;
  1582. }
  1583. // helper to build remote paths safely
  1584. static string BuildRemotePath(string parent, string child)
  1585. {
  1586. if (string.IsNullOrEmpty(parent) || parent == "/") return "/" + child;
  1587. return parent.TrimEnd('/') + "/" + child;
  1588. }
  1589. static void ProcessCasesFolder(Session session, string casesFolderRemotePath, int jobid, string jobPath, string reportsName, string lottitle, string reportidname,int bmpcount)
  1590. {
  1591. string FileName = ConfigurationSettings.AppSettings["mergingtool"].ToString();
  1592. var dbOps = new FileDownloadingDBOperations();
  1593. // Use the already downloaded Parent folder
  1594. string parentPath = Path.Combine(jobPath, "Parent");
  1595. // 🔍 Find the local Cases folder inside Parent
  1596. string casesFolderLocalPath = FindLocalCasesFolder(parentPath);
  1597. if (casesFolderLocalPath == null)
  1598. {
  1599. Console.WriteLine("⚠️ No local Cases folder found under Parent. Skipping.");
  1600. //return;
  1601. }
  1602. // 🎯 Take only BMP files
  1603. string[] bmpFiles = Directory.GetFiles(casesFolderLocalPath, "*.bmp", SearchOption.AllDirectories);
  1604. if (bmpFiles.Length == 0)
  1605. {
  1606. Console.WriteLine($"⚠️ No BMP files found in {casesFolderLocalPath}");
  1607. return;
  1608. }
  1609. // Sort files for correct sequence (important for merging PDFs)
  1610. Array.Sort(bmpFiles, StringComparer.InvariantCulture);
  1611. // 🔽 Database & batch processing logic
  1612. Console.WriteLine("Fetching Batch details...");
  1613. string batchqry = $"SELECT batchid FROM tbllotbatch WHERE jobid = {jobid} AND active = 1";
  1614. DataTable batch_dt = dbOps.getdatatable(batchqry);
  1615. string batchid;
  1616. if (batch_dt.Rows.Count > 0)
  1617. batchid = batch_dt.Rows[0]["batchid"].ToString();
  1618. else
  1619. {
  1620. Console.WriteLine("Creating new batch...");
  1621. string insertBatchQry =
  1622. $"INSERT INTO tbllotbatch(jobid, batchname, batchstatus, active, unmerged) " +
  1623. $"VALUES ({jobid}, 'Batch_{jobid}_1', 'R', 1, 0) RETURNING batchid;";
  1624. batchid = FileDownloadingDBOperations.insertdataScalar(insertBatchQry).ToString();
  1625. }
  1626. int lotid = FileDownloadingDBOperations.fncreatlot(jobid.ToString(), lottitle, "0", bmpcount);
  1627. string insLotBatchDetQry =
  1628. $"INSERT INTO tbllotbatchdetails(lotid, batchid, active, merged, ignored) " +
  1629. $"VALUES({lotid}, {batchid}, 1, 1, 0) RETURNING lotbatchdetailsid;";
  1630. string lot_batch_detail_id = FileDownloadingDBOperations.insertdataScalar(insLotBatchDetQry).ToString();
  1631. //// Prepare transaction folders
  1632. string lotfilepath = Path.Combine(jobPath, $"B{batchid}");
  1633. if (!Directory.Exists(lotfilepath)) Directory.CreateDirectory(lotfilepath);
  1634. int tranid = FileDownloadingDBOperations.InsertLotTrasaction(Convert.ToInt64(batchid), "");
  1635. string transactionPath = dbOps.GetServerPath(tranid.ToString(), false, "serverinfo");
  1636. string transactionInPath = Path.Combine(transactionPath, "IN");
  1637. string transactionOutPath = Path.Combine(transactionPath, "OUT");
  1638. if (!Directory.Exists(transactionInPath)) Directory.CreateDirectory(transactionInPath);
  1639. if (!Directory.Exists(transactionOutPath)) Directory.CreateDirectory(transactionOutPath);
  1640. // Copy only BMP files into transaction IN folder
  1641. int count = 0;
  1642. foreach (string file in bmpFiles)
  1643. {
  1644. string fileNameWithExtension = Path.GetFileNameWithoutExtension(file);
  1645. string pattern = @"(?<=\.)(\d{8}[A-Z0-9]{6})";
  1646. Match match = Regex.Match(fileNameWithExtension, pattern);
  1647. string filenamechange = match.Success ? match.Groups[1].Value : fileNameWithExtension;
  1648. string ext = Path.GetExtension(file);
  1649. string filenameChangedWithExt = $"{filenamechange}{ext}";
  1650. File.Copy(file, Path.Combine(transactionInPath, filenameChangedWithExt), true);
  1651. count++;
  1652. }
  1653. Console.WriteLine($"✅ {count} BMP files copied to {transactionInPath}");
  1654. // Update lot status
  1655. modcommon.nz(FileDownloadingDBOperations.insertdataScalar(
  1656. $"update tbllottransaction set lotstatus = 'C', endtime=now() where transactionid={tranid}"
  1657. ));
  1658. // ⚡ Prepare for PDF merge
  1659. string firstFileNumber = GetImageNumbers(Path.GetFileNameWithoutExtension(bmpFiles.First()));
  1660. string lastFileNumber = GetImageNumbers(Path.GetFileNameWithoutExtension(bmpFiles.Last()));
  1661. int nexttranid = FileDownloadingDBOperations.InsertLotTrasaction(Convert.ToInt64(batchid), "nexttraind");
  1662. modcommon.nz(FileDownloadingDBOperations.insertdataScalar(
  1663. $"update tbllottransaction set lotstatus = 'W', endtime=now() where transactionid={nexttranid}"
  1664. ));
  1665. string transactionnextPath = dbOps.GetServerPath(nexttranid.ToString(), false, "serverinfo");
  1666. string transactionInnextPath = Path.Combine(transactionnextPath, "IN");
  1667. string transactionOutnextPath = Path.Combine(transactionnextPath, "OUT");
  1668. string outputPdfPath = Path.Combine(transactionInnextPath, $"{firstFileNumber}_{reportsName}_{lastFileNumber}.pdf");
  1669. if (!Directory.Exists(transactionInnextPath)) Directory.CreateDirectory(transactionInnextPath);
  1670. if (!Directory.Exists(transactionOutnextPath)) Directory.CreateDirectory(transactionOutnextPath);
  1671. Console.WriteLine($"📄 Generating PDF: {outputPdfPath}");
  1672. // ✅ Merge BMP → PDF using your external tool
  1673. Program.Conv(FileName, outputPdfPath, transactionInPath);
  1674. // Update lot status back to Ready
  1675. modcommon.nz(FileDownloadingDBOperations.insertdataScalar(
  1676. $"update tbllottransaction set lotstatus = 'R', endtime=now() where transactionid={nexttranid}"
  1677. ));
  1678. Console.WriteLine($"✅ Lot {lotid} processed for Job {jobid}");
  1679. long reportId = InsertVlexReport(jobid,
  1680. lotid, reportidname, "", "", "", "", 0, 1);
  1681. }
  1682. static int GetMatchedPatternId(string input)
  1683. {
  1684. for (int i = 0; i < DatePatterns.Length; i++)
  1685. {
  1686. if (Regex.IsMatch(input, DatePatterns[i]))
  1687. return i; // Return ID/index of matched pattern
  1688. }
  1689. return -1; // No match
  1690. }
  1691. static bool IsDateFolder(string folderName)
  1692. {
  1693. foreach (var pattern in DatePatterns)
  1694. {
  1695. if (Regex.IsMatch(folderName, pattern))
  1696. return true;
  1697. }
  1698. return false;
  1699. }
  1700. static string GetImageNumbers(string filePath)
  1701. {
  1702. string[] parts = Path.GetFileNameWithoutExtension(filePath).Split('_');
  1703. string lastPart = parts[parts.Length - 1];
  1704. // Keep only digits
  1705. return new string(lastPart.Where(char.IsDigit).ToArray());
  1706. }
  1707. static void ExtractZip(Session session, string zipPath, string extractTo)
  1708. {
  1709. try
  1710. {
  1711. // 🔹 Ensure extraction folder exists
  1712. Directory.CreateDirectory(extractTo);
  1713. // 🔹 Local path to download the zip
  1714. string localZip = Path.Combine(extractTo, Path.GetFileName(zipPath));
  1715. // 🔹 Ensure parent folder exists for local zip
  1716. Directory.CreateDirectory(Path.GetDirectoryName(localZip));
  1717. // 🔹 Download zip from remote
  1718. session.GetFiles(zipPath, localZip, false).Check();
  1719. // 🔹 Extract zip entries
  1720. using (FileStream zipStream = new FileStream(localZip, FileMode.Open, FileAccess.Read))
  1721. using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Read))
  1722. {
  1723. foreach (ZipArchiveEntry entry in archive.Entries)
  1724. {
  1725. string destinationPath = Path.Combine(extractTo, entry.FullName);
  1726. if (string.IsNullOrEmpty(entry.Name))
  1727. {
  1728. // folder
  1729. Directory.CreateDirectory(destinationPath);
  1730. }
  1731. else
  1732. {
  1733. Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
  1734. using (var entryStream = entry.Open())
  1735. using (var destStream = File.Create(destinationPath))
  1736. {
  1737. entryStream.CopyTo(destStream);
  1738. }
  1739. // 🔹 Preserve timestamp from zip
  1740. File.SetLastWriteTime(destinationPath, entry.LastWriteTime.DateTime);
  1741. }
  1742. }
  1743. }
  1744. // 🔹 Optional: delete downloaded zip
  1745. try { File.Delete(localZip); } catch { }
  1746. Console.WriteLine($"✅ Extracted {Path.GetFileName(zipPath)} into {extractTo}");
  1747. }
  1748. catch (Exception ex)
  1749. {
  1750. Console.WriteLine($"❌ ExtractZip failed for '{zipPath}': {ex.Message}");
  1751. throw;
  1752. }
  1753. }
  1754. static long InsertVlexReport(
  1755. long jobId, long lotId, string report, string volume,
  1756. string edition, string issue, string printpage, long createdBy, int active)
  1757. {
  1758. string connStr = ConfigurationManager.AppSettings["npgconn"].ToString();
  1759. using (var conn = new NpgsqlConnection(connStr))
  1760. {
  1761. using (var cmd = new NpgsqlCommand("spvlexreport_insert", conn))
  1762. {
  1763. cmd.CommandType = CommandType.StoredProcedure;
  1764. // Add parameters
  1765. cmd.Parameters.AddWithValue("p_jobid", jobId);
  1766. cmd.Parameters.AddWithValue("p_lotid", lotId);
  1767. cmd.Parameters.AddWithValue("p_report", report ?? (object)DBNull.Value);
  1768. cmd.Parameters.AddWithValue("p_volume", volume ?? (object)DBNull.Value);
  1769. cmd.Parameters.AddWithValue("p_edition", edition ?? (object)DBNull.Value);
  1770. cmd.Parameters.AddWithValue("p_issue", issue ?? (object)DBNull.Value);
  1771. cmd.Parameters.AddWithValue("p_printpage", printpage ?? (object)DBNull.Value);
  1772. cmd.Parameters.AddWithValue("p_createdby", createdBy);
  1773. cmd.Parameters.AddWithValue("p_active", active);
  1774. // ✅ Open the connection before executing
  1775. conn.Open();
  1776. // Execute and return the new report ID
  1777. return Convert.ToInt64(cmd.ExecuteScalar());
  1778. }
  1779. }
  1780. }
  1781. static void CopyFolder(Session session, string sourcePath, string destinationPath)
  1782. {
  1783. foreach (RemoteFileInfo file in session.ListDirectory(sourcePath).Files)
  1784. {
  1785. if (file.Name == "." || file.Name == "..")
  1786. continue;
  1787. string sourceFile = sourcePath.TrimEnd('/') + "/" + file.Name;
  1788. string destFile = destinationPath.TrimEnd('/') + "/" + file.Name;
  1789. if (file.IsDirectory)
  1790. {
  1791. session.CreateDirectory(destFile);
  1792. CopyFolder(session, sourceFile, destFile);
  1793. }
  1794. else
  1795. {
  1796. session.GetFiles(sourceFile, Path.GetTempPath() + file.Name).Check();
  1797. session.PutFiles(Path.GetTempPath() + file.Name, destFile, false).Check();
  1798. }
  1799. }
  1800. }
  1801. public int GetPageCount(string pdfPath)
  1802. {
  1803. using (PdfReader reader = new PdfReader(pdfPath))
  1804. {
  1805. // Get the number of pages in the PDF
  1806. return reader.NumberOfPages;
  1807. }
  1808. }
  1809. }
  1810. class Program
  1811. {
  1812. private static string PrcRv = "";
  1813. private static string PrcEr = "";
  1814. private static string PrcOut = "";
  1815. static void Main(string[] args)
  1816. {
  1817. try
  1818. {
  1819. string projectCode = ConfigurationManager.AppSettings["VLEX"].ToString();
  1820. Console.WriteLine("Project Code = " + projectCode);
  1821. var now = DateTime.Now;
  1822. // Set time to 4:00 AM today
  1823. var nextRunTime = DateTime.Today.AddHours(4);
  1824. // Run once immediately
  1825. //CallMethod(projectCode);
  1826. // If 4 AM already passed today → schedule for tomorrow
  1827. if (now > nextRunTime)
  1828. {
  1829. nextRunTime = nextRunTime.AddDays(1);
  1830. }
  1831. var initialDelay = nextRunTime - now; // Time until next 4 AM
  1832. var dailyInterval = TimeSpan.FromDays(1); // Run every 24 hours
  1833. var timer = new System.Threading.Timer((e) =>
  1834. {
  1835. CallMethod(projectCode);
  1836. }, null, initialDelay, dailyInterval);
  1837. Console.WriteLine($"Next run scheduled at: {nextRunTime}");
  1838. Console.WriteLine("Press any key to exit...");
  1839. Console.ReadKey();
  1840. }
  1841. catch (System.Exception ex)
  1842. {
  1843. Console.WriteLine(ex.Message);
  1844. }
  1845. }
  1846. public static void CallMethod(string projectCode)
  1847. {
  1848. Console.WriteLine("\nMailWatcher started at: " + DateTime.Now + "\n");
  1849. OutLookreader outLookReader = new OutLookreader();
  1850. //string dwnserverpath = @"\\172.20.254.139\internal_laps\DEV\LN-BPM\Download";
  1851. string dwnserverpath = @"D:\internal_laps\UAT\JOB";
  1852. outLookReader.DownloadFromFTP1(projectCode, dwnserverpath, true);
  1853. }
  1854. private static void Prc_Extd(object sender, EventArgs e)
  1855. {
  1856. }
  1857. private static void Prc_ErDtRcvd(object sender, DataReceivedEventArgs e)
  1858. {
  1859. if (e.Data == null || !(e.Data != ""))
  1860. return;
  1861. Program.PrcEr = Program.PrcEr + "\r\n" + e.Data;
  1862. }
  1863. private static void Prc_OpDtRcvd(object sender, DataReceivedEventArgs e)
  1864. {
  1865. if (e.Data == null || !(e.Data != ""))
  1866. return;
  1867. Program.PrcOut = Program.PrcOut + "\r\n" + e.Data;
  1868. }
  1869. public static string Conv(string fileName, string parameter1, string parameter2)
  1870. {
  1871. string str;
  1872. Program.PrcOut = str = "";
  1873. Program.PrcEr = str;
  1874. Program.PrcRv = str;
  1875. Process process = null;
  1876. try
  1877. {
  1878. process = new Process();
  1879. process.StartInfo.FileName = fileName;
  1880. Console.WriteLine(fileName);
  1881. // Combine both parameters into the arguments string
  1882. // process.StartInfo.Arguments = $"\"{parameter1}\" \"{parameter2}\"";
  1883. process.StartInfo.Arguments = $"\"{parameter1},{parameter2}\"";
  1884. process.StartInfo.WorkingDirectory = Path.GetDirectoryName(fileName);
  1885. Console.WriteLine(Path.GetDirectoryName(fileName).ToString());
  1886. process.StartInfo.CreateNoWindow = true;
  1887. process.StartInfo.UseShellExecute = false;
  1888. process.Exited += new EventHandler(Program.Prc_Extd);
  1889. process.StartInfo.RedirectStandardError = true;
  1890. process.StartInfo.RedirectStandardOutput = true;
  1891. process.ErrorDataReceived += new DataReceivedEventHandler(Program.Prc_ErDtRcvd);
  1892. process.OutputDataReceived += new DataReceivedEventHandler(Program.Prc_OpDtRcvd);
  1893. process.Start();
  1894. process.BeginErrorReadLine();
  1895. process.BeginOutputReadLine();
  1896. process.WaitForExit();
  1897. if (Program.PrcEr != "")
  1898. Program.PrcRv = Program.PrcRv + "\r\n" + Program.PrcEr;
  1899. }
  1900. catch (Exception ex)
  1901. {
  1902. Program.PrcRv = Program.PrcRv + "Error while conversion, exception is: "
  1903. + ex.Message.Replace(fileName, "") + "::"
  1904. + ex.StackTrace.Replace(fileName, "");
  1905. }
  1906. finally
  1907. {
  1908. process?.Close();
  1909. }
  1910. return Program.PrcRv;
  1911. }
  1912. }
  1913. public class PublishMessage
  1914. {
  1915. public PublishMessage(string customerId, string Tranid, string UserId, string AppType, string StageType, string Enviroment, object DynamicList, string Flag, string Batch = "", string PublicationType = "", string comment = "", string PublicationID = "")
  1916. {
  1917. _customerId = customerId;
  1918. _TransId = Tranid;
  1919. _UserId = UserId;
  1920. _AppType = AppType;
  1921. _StageType = StageType;
  1922. _Enviroment = Enviroment;
  1923. _timestamp = DateTime.UtcNow;
  1924. _DynamicList = DynamicList;
  1925. _Flag = Flag;
  1926. _Batch = Batch;
  1927. _PublicationType = PublicationType;
  1928. _comment = comment;
  1929. _PublicationID = PublicationID;
  1930. }
  1931. public string _customerId { get; set; }
  1932. public string _TransId { get; set; }
  1933. public string _UserId { get; set; }
  1934. public string _AppType { get; set; }
  1935. public string _StageType { get; set; }
  1936. public string _Enviroment { get; set; }
  1937. public DateTime _timestamp { get; set; }
  1938. public object _DynamicList { get; set; }
  1939. public string _Flag { get; set; }
  1940. public string _Batch { get; set; }
  1941. public string _PublicationType { get; set; }
  1942. public string _comment { get; set; }
  1943. public string _PublicationID { get; set; }
  1944. }
  1945. }