Compare commits

...

58 Commits

Author SHA1 Message Date
d57be048e7 SECS/GEM log-to-trace (Day-Helper-2025-07-10)
java-script-object-notation-to-reactor (Day-Helper-2025-07-09)

Dynamic extension for (Day-Helper-2025-07-01)
2025-07-10 13:07:42 -07:00
89bb87bd14 export-faces (Day-Helper-2025-07-05) 2025-07-05 13:13:00 -07:00
e7c1fd2221 process-data-standard-format-to-java-script-object-notation (Day-Helper-2025-07-01) 2025-07-03 19:10:54 -07:00
fc4449f515 log-iso-information (Day-Helper-2025-06-28) 2025-06-29 14:25:54 -07:00
6ce6c28db1 Including .json and .html after creating .iso (Day-Helper-2024-12-17) 2025-06-28 11:02:19 -07:00
2e4aa313fa Update Markdown Helper to write json files for tables and yaml 2025-06-24 08:35:55 -07:00
116d5e9734 move-all-but-x-of-each (Day-Helper-2025-06-18) 2025-06-18 17:33:29 -07:00
5ca22b3792 mklink for release 2025-06-14 20:17:40 -07:00
b98d1e480b Second pass with standard get files (Day-Helper-2024-12-17)
mklink for vscode extension
2025-06-14 08:01:01 -07:00
404ffe18f5 Create equipment-automation-framework status and cell-instance-state-image-verb-if (Day-Helper-2025-06-01 and Day-Helper-2025-06-02) 2025-06-08 13:12:38 -07:00
5becc546b2 Create MatchDirectory with Parse (Day-Helper-2025-05-21) 2025-06-08 13:10:24 -07:00
0f9d004122 Updated Live Sync (Day-Helper-2025-05-19) 2025-06-08 13:09:05 -07:00
dd5baba9bc Updated Backup (Day-Helper-2024-12-17) 2025-06-08 13:06:53 -07:00
43527b3356 Selenium (Not Fully Tested)
hyper-text-markup-language-to-portable-document-format (Not Fully Tested)
2025-05-19 10:07:27 -07:00
8ca489d818 ~ over | split 2025-05-17 07:51:53 -07:00
8f22f188a2 WriteNginxFileSystem 2025-04-29 15:12:17 -07:00
d23f802cdb free-file-sync-change-created-date 2025-04-21 10:27:06 -07:00
74e9fc33af process-data-standard-format-to-json update 2025-04-19 08:15:08 -07:00
aa6461c62d Scripts 2025-04-11 20:31:29 -07:00
fad2db46b5 https sync 2025-04-10 09:16:01 -07:00
cc9c5013a9 KumaToGatus 2025-04-04 18:59:31 -07:00
23c0ff9683 Update to 2025-02-18 and 2025-02-19 2025-03-31 19:52:26 -07:00
919279a917 javascript methods for sequence to readable date
c# like java for PI5

Helper 2025-02-19 more updates for Compare
2025-03-26 17:02:35 -07:00
0621d0f07e Updates to Backup method 2025-03-23 15:57:07 -07:00
b74a6a5267 SortCodeMethods
DeleteEmptyDirectories

Remove deprecated tasks

Current Results Move to 1 or 9 ...
2025-03-22 15:37:50 -07:00
3e2fb15db0 Split Process Data Standard Format method into multiple methods 2025-03-18 08:57:43 -07:00
6783621dab #pragma warning disable CA1845, IDE0057
GetInferredCheckDirectory

Process.Start for insiders vscode
2025-03-11 11:00:47 -07:00
38ab4424bc WriteNginxFileSystemDelta
ProcessDataStandardFormatToJson
2025-03-06 14:01:00 -07:00
e89e11dcf6 PocketBaseImportWithDeno 2025-03-01 11:11:50 -07:00
384c83304b PostgresDumpToJson 2025-03-01 09:20:35 -07:00
30931eda9c Dynamic Done Column
Changes for AOT
2025-02-27 14:45:50 -07:00
55adcb69aa Removed Renci.SshNet 2025-02-24 21:25:49 -07:00
d9e394f446 CSV Compare 2025-02-24 17:46:47 -07:00
dd4a16117c Move to Archive 2025-02-19 08:43:45 -07:00
a156ff8dcb Scripts 2025-02-15 10:03:30 -07:00
b771761936 Add option directory
Allow lower case drive letters
2025-02-11 14:23:11 -07:00
264b6319cb Linux AOT and Container 2025-02-09 15:21:13 -07:00
7aada4303e Added RemainingWork and StoryPoints 2025-02-08 09:57:54 -07:00
618fa0d55f ExtractKanban 2025-02-05 16:19:13 -07:00
930963965d Changed trigger for code-insiders 2025-01-29 20:13:41 -07:00
9d612d3d3d Move Bank PDF files to Year Month 2025-01-26 16:02:14 -07:00
3070fee04c Removed GetTaskArgumentsForDayHelper20240623 2025-01-15 08:56:36 -07:00
9f4286e3e9 Directory Renaming 2025-01-14 13:00:52 -07:00
5c08ac222a Format from docker 2025-01-08 16:05:21 +00:00
4b85f8807d Year-Season-Attachments
Updated mklink for new version and directory creation
Epoch Testing
2025-01-06 13:46:00 -07:00
7f1f149eac MoveToDelete 2025-01-01 19:44:01 -07:00
fe524b7536 DeleteFirst testing 2024-12-30 13:49:53 -07:00
fb9289a572 Update Subtasks In Markdown Files
Better ISO support

Only reviewing Files when comparing

Extracted sections from UpdateSubTasksInMarkdownFiles
2024-12-26 14:14:31 -07:00
2361796bbf Compile Warnings 2024-12-14 09:41:18 -07:00
9afc7360b9 Not tested 2024-12-13 22:18:34 -07:00
02c03f2853 Rename by replace 2024-12-12 12:20:56 -07:00
c37771da61 Removed AppSettings layer 2024-12-11 10:14:45 -07:00
09c37aed14 DebugProxyPass dynamic server_name and Trim ; 2024-12-04 16:49:39 -07:00
2f65dd3120 DebugProxyPass dynamic server_name 2024-12-04 16:35:35 -07:00
cf531cff36 ConvertToUTF8 2024-12-04 13:18:46 -07:00
ec98d1e275 Update to Nginx parse for default 2024-12-04 10:13:32 -07:00
ecc1cf3a62 Log Only 2024-11-22 17:51:40 -07:00
446b5587be Not Tested 2024-11-22 17:09:25 -07:00
77 changed files with 16316 additions and 1047 deletions

1
.gitignore vendored
View File

@ -336,3 +336,4 @@ ASALocalRun/
.extensions-vscode-oss .extensions-vscode-oss
.extensions-vscode-insiders .extensions-vscode-insiders
.vscode/.UserSecrets/secrets.json .vscode/.UserSecrets/secrets.json
.vscode/.helper

27
.vscode/download-work-items.http vendored Normal file
View File

@ -0,0 +1,27 @@
@host = https://tfs.intra.infineon.com
@pat = asdf
@ids = 126018, 224543
GET {{host}}/tfs/FactoryIntegration/_apis/wit/workitems?ids={{ids}}&$expand=Relations
Accept: application/json
Authorization: Basic {{pat}}
###
GET {{host}}/tfs/FactoryIntegration/_apis/wit/workitems/{{ids}}/updates
Accept: application/json
Authorization: Basic {{pat}}
### Iterations
GET {{host}}/tfs/FactoryIntegration/ART%20SPS/cea9f426-6fb1-4d65-93d5-dbf471056212/_apis/work/teamsettings/iterations?
Accept: application/json
Authorization: Basic {{pat}}
###
DELETE http://localhost:5004/api/SyncV1/?size=4&ticks=638796666663591762&path=d:\Tmp\phares\VisualStudioCode\z-include-patterns - Copy.nsv
###
GET http://localhost:5004/api/SyncV1/?size=4&ticks=638796666663591762&path=d:\Tmp\phares\VisualStudioCode\z-include-patterns - Copy.nsv

View File

@ -0,0 +1,4 @@
@host = eaf-prod.mes.infineon.com:9003
POST {{host}}/StatusQuery
Accept: application/json

289
.vscode/launch.json vendored
View File

@ -1,82 +1,211 @@
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": ".NET Core Launch (console)", "name": ".NET Core Launch (console)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll",
"args": [ "args": [
"s", "s",
"X", "X",
"D:/5-Other-Small/Kanban-mestsa003/ART-SPS/2024/PI4/Sprint-4.1/126996-User-Story", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Log",
"Day-Helper-2024-11-08", "Day-Helper-2025-07-10",
"Bug|User Story", "R*",
"D:/5-Other-Small/Kanban-mestsa003/ART-SPS", "EAF_INFO*",
"444", "s",
"555", "X",
"666", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/JavaScriptObjectNotation",
"777", "Day-Helper-2025-07-09",
"888", "*.json",
"999", "987654321",
"s", ".agg",
"X", "id93,vp93,ProcessState,id154,vp154,SystemState,id78,vp78,LL1State,id83,vp83,LL2State,id176,vp176,TotalWaferCount,id80,vp80,LL1Lotid,id85,vp85,LL2Lotid,id153,vp153,PPSTEPNAME,id221,vp221,LeftDefaultRecipe,id222,vp222,RightDefaultRecipe,id223,vp223,RecipeCompleteMsg",
"D:/5-Other-Small/Kanban-mestsa003/ART-SPS/2024/PI4/Sprint-4.1/126448-User-Story/.files/Production__v2_57_0__HGCV1__pcl__Normal", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Reactor",
"Day-Helper-2024-10-30", "s",
"*.txt", "X",
"s", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/PollPath",
"X", "Day-Helper-2025-07-01",
"D:/5-Other-Small/Kanban-mestsa003/ART-SPS/2024/PI4/Sprint-4.1/126448-User-Story/.files/Production__v2_57_0__CDE6__RsM__Normal", "*.pdsf",
"Day-Helper-2024-10-31", "987654321",
"*.RsM", "Time",
"s", ".json",
"X", "id12~CenterTemp,id13~CenterSetpt,id15~FrontTemp,id153~PPSTEPNAME,id154~SystemState,id16~FrontSetpt,id172~LVC1Ratio,id173~LVC1Carrier,id176~TotalWaferCount,i-d-1-7-8~TIME,id18~SideTemp,id183~SCRDrive4,id19~SideSetpt,id193~SCRLOAD4,id21~RearTemp,id22~RearSetpt,id221~LeftDefaultRecipe,id222~RightDefaultRecipe,id223~RecipeCompleteMsg,id25~N2H2Setpt,id26~N2H2Flow,id27~HCLSetpt,id28~HCLFlow,id29~HCLHISetpt,id30~HCLHIFlow,id37~NSRCSetpt,id38~NSRCFlow,id39~NDILSetpt,id40~NDILFlow,id41~NINJSetpt,id42~NINJFlow,id57~LVC1Setpt,id58~LVC1Flow,id61~ROTSetpt,id62~ROTSpeed,id78~LL1State,id79~LL1Init,id80~LL1Lotid,id81~LL1WafersIn,id82~LL1WfrCnt,id83~LL2State,id84~LL2Init,id85~LL2Lotid,id86~LL2WafersIn,id87~LL2WfrCnt,id93~ProcessState,vp93~ProcessState,vp154~SystemState,vp78~LL1State,vp83~LL2State,vp176~TotalWaferCount,vp80~LL1Lotid,vp85~LL2Lotid,vp153~PPSTEPNAME,vp221~LeftDefaultRecipe,vp222~RightDefaultRecipe,vp223~RecipeCompleteMsg",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/JavaScriptObjectNotation",
"Day-Helper-2024-09-11", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Markdown",
"https://eaf-dev.mes.infineon.com/json/work-items.json?v=2024-10-04-08-34&_=1728333679350", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/KeyValuePairs",
"https://oi-metrology-viewer-prod.mes.infineon.com/json/work-items.json?v=2024-10-07-10-09&_=1728336047608", "654321",
"Epic|Feature|User Story", "s",
"https://tfs.intra.infineon.com/tfs/FactoryIntegration/ART%20SPS/_workitems/edit/", "s",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs", "X",
"D:/5-Other-Small/Proxmox/exiftool/286628400329.jpg.jpg", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/R55",
"s", "Day-Helper-2023-11-30",
"X", "yyMMddhhmmssfff",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs", "\"vp154\"",
"Day-Helper-2024-09-11", "s",
"https://eaf-dev.mes.infineon.com/json/work-items.json?v=2024-10-04-08-34&_=1728333679350", "X",
"https://oi-metrology-viewer-prod.mes.infineon.com/json/work-items.json?v=2024-10-07-10-09&_=1728336047608", "V:/Tmp/Phares/Pictures/2023 TI2023.6 Fall Samsung",
"Epic|Feature|User Story", "Day-Helper-2025-07-05",
"https://tfs.intra.infineon.com/tfs/FactoryIntegration/ART%20SPS/_workitems/edit/", "x-653889110721.jpg~401223300869.jpg",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs", "3648,2736,1~3024,4032,6",
"s", "0.341694,0.599963,0.1642,0.279605~0.552357,0.65095,0.195175,0.32383~0.31002,0.42328,0.0379464,0.0459656",
"X", "0.259594,0.460161,0.1642,0.279605~0.45477,0.489035,0.195175,0.32383~0.328993,0.446263,0.0379464,0.0459656",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG", "9",
"Day-Helper-2024-01-08", "x-825511723~x-444522128~831410304",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/Markdown", "X",
"s", "F:/0-ISO-A",
"X", "Day-Helper-2025-06-28",
"L:/DevOps/Mesa_FI/File-Folder-Helper", "*.iso",
"Day-Helper-2024-01-08", "F",
"L:/DevOps/Mesa_FI/File-Folder-Helper/ADO2024/PI3" "s",
], "X",
"cwd": "${workspaceFolder}", "D:/5-Other-Small",
"console": "integratedTerminal", "Day-Helper-2024-12-17",
"stopAtEntry": false ".job.json",
}, "thumbs.db~sync.ffs_db~verify.json~.html",
{ "D:/0-ISO-A",
"name": ".NET Core Attach", "D:/5-Other-Small/Proxmox/Snap2HTML/Snap2HTML.exe",
"type": "coreclr", "s",
"request": "attach" "M",
}, "D:/5-Other-Small/Notes/EC-Documentation",
{ "-d",
"type": "node", "D:/5-Other-Small/Notes/EC-Documentation/.vscode/helper",
"request": "launch", "s",
"name": "node Launch Current Opened File", "X",
"program": "${file}" "D:/5-Other-Small/Proxmox/DiskInfo",
} "Day-Helper-2025-06-18",
] "*.json",
"D:/5-Other-Small/Proxmox/Disk-Info-Old",
"-2025-",
"1",
"s",
"X",
"D:/Tmp",
"Day-Helper-2025-06-02",
"infineon\\MESPhares",
"BACKLOG~BIORAD2~BIORAD3~BIORAD4~BIORAD5~CDE4~CDE5~CDE6~DEP08CEPIEPSILON~DEP08SIASM~DEP08SIHTRPLC~EC~HGCV1~HGCV2~HGCV3~MESAFIBACKLOG~MET06AWCT~MET08ANLYSDIFAAST230~MET08AWCT~MET08DDUPSFS6420~MET08DDUPSP1TBI~MET08RESIHGCV~MET08RESIMAPCDE~MET08RESISRP2100~MET08THFTIRQS408M~MET08THFTIRSTRATUS~METCLIMATEC~R29~R32~R36~R47~R55~R57~R61~R62~R65~R70~R72~R73~R74~R75~R77~SP101~SPV01~SRP~TENCOR1~TENCOR2~TENCOR3~TRENDLOG~WC6INCH1~WC6INCH2~WC6INCH3~WC6INCH4~WC8INCH1~WC8INCH2~WC8INCH3",
"s",
"X",
"D:/5-Other-Small/Proxmox/ffnm",
"Day-Helper-2025-05-21",
"*.pdf",
"*.md",
"2",
"MM-dd-yy",
"Trans Date~Effective Date~Description~Withdrawal Deposit~Balance",
"s",
"X",
"D:/Tmp/phares/VisualStudioCode",
"Day-Helper-2025-05-19",
"D:/Tmp/phares/VisualStudioCode/.vscode/input.json",
"s",
"X",
"D:/Tmp/phares/VisualStudioCode",
"Day-Helper-2025-05-19",
"D:/Tmp/phares/VisualStudioCodeLeft",
"z-include-patterns.nsv",
"z-exclude-patterns.nsv",
"http://localhost:5004",
"/api/SyncV1/?",
",L",
".G",
"+~G~~L~+~Custom-Default",
"",
"+~G~~G~-~Mirror",
"+~G~~~~Update",
"+~G~~L~+~Custom-Default",
"-~G~~G~+~Custom-A",
"-~L~~L~+~Custom-B",
"+~L~~L~-~Custom-C",
"s",
"X",
"\\\\mesfs.infineon.com\\EC_Characterization_Si\\Archive\\BIORAD4\\2025_Week_16\\2025-04-17",
"Day-Helper-2025-02-19",
"csv-*.pdsf",
"*.pdsf",
"Time,HeaderUniqueId,UniqueId,Date,Wafer,Position,BIORAD4",
",BIORAD4",
",BIORAD4",
"Test|EventId,Date|DateTime,Position|Slot,DeltaThicknessSlotsOneAndTwentyFive|Actual Delta Thick Pts 1 and 25,PercentDeltaThicknessSlotsOneAndTwentyFive|% Delta Thick Pts 1 and 25,MID|Cassette,Lot|Batch,Title|Batch,Wafer|Text,Thickness|Site,MeanThickness|GradeMean,|BIORAD4",
"Time,A_LOGISTICS,B_LOGISTICS,Test,Count,Index,MesEntity,MID,Date,Employee,Lot,PSN,Reactor,Recipe,Cassette,GradeStdDev,HeaderUniqueId,Layer,MeanThickness,PassFail,RDS,Slot,Title,UniqueId,Wafer,Zone,Mean,Position,StdDev,Thickness,ThicknessSlotOne,ThicknessSlotTwentyFive,DeltaThicknessSlotsOneAndTwentyFive,PercentDeltaThicknessSlotsOneAndTwentyFive",
"Time,A_LOGISTICS,B_LOGISTICS,Count,Sequence,MesEntity,Index,Batch,Cassette,DateTime,Destination,Mean,PassFail,Recipe,Reference,Site,Slot,Source,StdDev,Text,GradeMean,GradeStdDev,RDS,PSN,Reactor,Layer,Zone,Employee,InferredLot,Thickness First Slot,Thickness Last Slot,Actual Delta Thick Pts 1 and 25,% Delta Thick Pts 1 and 25,EventId",
"0,1,2,31,3,6,5,8,9,27,7,23,24,13,8,21,-1,25,20,12,22,16,7,-1,19,26,11,16,18,15,-1,-1,29,30",
"s",
"X",
"C:/Users/phares/AppData/Roaming/FreeFileSync",
"Day-Helper-2025-04-21",
"GlobalSettings.xml",
"LastSync|Config",
"s",
"X",
"L:/Tmp/MET08ANLYSDIFAAST230",
"Day-Helper-2025-03-06",
"*.pdsf",
"s",
"X",
"D:/ProgramData/VisualStudioCode|D:/6-Other-Large-Z/Linux-Ubuntu-Phares/home/lphares/dorico",
"Day-Helper-2025-04-07",
"z-include-patterns.nsv",
"z-exclude-patterns.nsv",
"https://isccvm57294f1ed/VisualStudioCode|hxttps://dorico.phares.duckdns.org|hxttps://mestsa006.infineon.com/VisualStudioCode",
"+|G|G|G|-",
"||||",
"666",
"777",
"888",
"999",
"s",
"X",
"C:/Users/PHARES/AppData/Local/IFXApps/gatus",
"Day-Helper-2025-04-04",
"*.json",
".metrics",
"https://messa010ec.infineon.com/metrics",
"gatus_results_endpoint_success",
"666",
"777",
"888",
"999",
""
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"type": "node",
"request": "launch",
"name": "node Launch Current Opened File",
"program": "${file}"
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "launch",
"name": "Debug File",
"program": "${file}",
"cwd": "${workspaceFolder}",
"stopOnEntry": false,
"watchMode": false
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "launch",
"name": "Run File",
"program": "${file}",
"cwd": "${workspaceFolder}",
"noDebug": true,
"watchMode": false
}
]
} }

31
.vscode/mklink.md vendored
View File

@ -14,15 +14,28 @@ mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.kanbn" "D:\5-Other-Small\Kanban
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.kanbn" "D:\5-Other-Small\Kanban\File-Folder-Helper" mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.kanbn" "D:\5-Other-Small\Kanban\File-Folder-Helper"
``` ```
```bash
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode"
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss"
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders"
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode" "C:\Users\phares\.vscode\extensions\ifx.type-script-helper-1.8.0"
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss" "C:\Users\phares\.vscode-oss\extensions\ifx.type-script-helper-1.8.0"
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders" "C:\Users\phares\.vscode-insiders\extensions\ifx.type-script-helper-1.8.0"
```
```bash Thu Jul 18 2024 13:47:40 GMT-0700 (Mountain Standard Time) ```bash Thu Jul 18 2024 13:47:40 GMT-0700 (Mountain Standard Time)
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\8da397d4-13ec-4576-9722-3c79cad25563" mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\8da397d4-13ec-4576-9722-3c79cad25563"
``` ```
```bash 1749414316830 = 638850111168300000 = 2025-2.Spring = Sun Jun 08 2025 13:25:16 GMT-0700 (Mountain Standard Time)
C:\Users\PHARES\.vscode\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode"
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss"
del "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders"
mkdir "C:\Users\phares\.vscode\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64"
mkdir "C:\Users\phares\.vscode-oss\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64"
mkdir "C:\Users\phares\.vscode-insiders\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64"
mklink /J "C:\Users\phares\.vscode\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64\publish" "L:\DevOps\Mesa_FI\File-Folder-Helper\bin\Release\net8.0\win-x64\publish"
mklink /J "C:\Users\phares\.vscode-oss\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64\publish" "L:\DevOps\Mesa_FI\File-Folder-Helper\bin\Release\net8.0\win-x64\publish"
mklink /J "C:\Users\phares\.vscode-insiders\extensions\infineon-technologies-ag-mesa-fi.infineon-technologies-ag-mesa-fi-cost-of-delay-helper-1.124.0\net8.0\win-x64\publish" "L:\DevOps\Mesa_FI\File-Folder-Helper\bin\Release\net8.0\win-x64\publish"
```
```bash 1749957317559 = 638855541175590000 = 2025-2.Spring = Sat Jun 14 2025 20:15:17 GMT-0700 (Mountain Standard Time)
mkdir "L:\DevOps\MESA_FI\file-folder-helper\bin\Release\net8.0\win-x64"
mklink /J "L:\DevOps\MESA_FI\file-folder-helper\bin\Release\net8.0\win-x64\publish" "D:\5-Other-Small\Proxmox\publish"
```
```bash 1750459968132 = 638860567681320000 = 2025-3.Summer = Fri Jun 20 2025 15:52:47 GMT-0700 (Mountain Standard Time)
mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.vscode\.helper" "D:\5-Other-Small\Notes\Infineon\.vscode\helper"
```

14
.vscode/settings.json vendored
View File

@ -10,7 +10,10 @@
"**/node_modules": true "**/node_modules": true
}, },
"cSpell.words": [ "cSpell.words": [
"abcdefghiklmnopqrstuvwxyz",
"Acks",
"ASPNETCORE", "ASPNETCORE",
"BIORAD",
"BIRT", "BIRT",
"CHIL", "CHIL",
"DEAT", "DEAT",
@ -18,7 +21,9 @@
"Exif", "Exif",
"FAMC", "FAMC",
"FAMS", "FAMS",
"Gatus",
"GIVN", "GIVN",
"HGCV",
"HUSB", "HUSB",
"Immich", "Immich",
"INDI", "INDI",
@ -26,19 +31,28 @@
"Kanban", "Kanban",
"kanbn", "kanbn",
"Kofax", "Kofax",
"Linc",
"mesfs",
"mestsa",
"netrm",
"NpgSql", "NpgSql",
"NSFX", "NSFX",
"OBJE", "OBJE",
"onenote", "onenote",
"PDFC",
"pdsf",
"Permyriad", "Permyriad",
"pged", "pged",
"Phares", "Phares",
"Renci",
"Reparse", "Reparse",
"Rijndael", "Rijndael",
"Serilog", "Serilog",
"startable",
"SUBM", "SUBM",
"SURN", "SURN",
"SYSLIB", "SYSLIB",
"TENCOR",
"VSTS", "VSTS",
"WIQL", "WIQL",
"WSJF" "WSJF"

173
.vscode/tasks.json vendored
View File

@ -58,12 +58,95 @@
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"-r",
"win-x64",
"${workspaceFolder}/File-Folder-Helper.csproj", "${workspaceFolder}/File-Folder-Helper.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{
"label": "build Linux",
"command": "dotnet",
"type": "process",
"args": [
"build",
"-r",
"linux-x64",
"${workspaceFolder}/File-Folder-Helper.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanLogin",
"command": "podman",
"type": "process",
"args": [
"login",
"gitea.phares.duckdns.org:443"
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanBuild",
"command": "podman",
"type": "process",
"args": [
"build",
"-t",
"file-folder-helper",
"."
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanImageList",
"command": "podman",
"type": "process",
"args": [
"image",
"ls"
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanRun",
"command": "podman",
"type": "process",
"args": [
"run",
"-p",
"5001:5001",
"--name",
"file-folder-helper-001",
"a3de856b5731"
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanTag",
"command": "podman",
"type": "process",
"args": [
"tag",
"a3de856b5731",
"gitea.phares.duckdns.org:443/phares3757/file-folder-helper:latest"
],
"problemMatcher": "$msCompile"
},
{
"label": "podmanPush",
"command": "podman",
"type": "process",
"args": [
"push",
"gitea.phares.duckdns.org:443/phares3757/file-folder-helper:latest"
],
"problemMatcher": "$msCompile"
},
{ {
"label": "publish", "label": "publish",
"command": "dotnet", "command": "dotnet",
@ -106,65 +189,21 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "File-Folder-Helper AOT s J Verdaccio", "label": "Publish AOT Linux",
"type": "shell", "command": "dotnet",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", "type": "process",
"args": [ "args": [
"s", "publish",
"J", "-r",
"L:/Verdaccio/storage", "linux-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/File-Folder-Helper.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": [] "problemMatcher": "$msCompile"
},
{
"label": "File-Folder-Helper AOT s S BaGet",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"S",
"L:/BaGet/packages",
],
"problemMatcher": []
},
{
"label": "File-Folder-Helper AOT s X SortCodeMethods",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"X",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG",
"Day-Helper-2024-01-08",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/ADO",
"s",
"X",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG",
"Day-Helper-2024-01-08",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/Kanban",
"s",
"X",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG",
"Day-Helper-2024-01-08",
"T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/Markdown",
"s",
"X",
"L:/DevOps/Mesa_FI/File-Folder-Helper",
"Day-Helper-2024-01-08",
"L:/DevOps/Mesa_FI/File-Folder-Helper/ADO2024/PI3"
],
"problemMatcher": []
},
{
"label": "File-Folder-Helper AOT s F Staging _Logs",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"F",
"'\\\\messv02ecc1.ec.local\\EC_EAFLog\\Staging\\_ Logs'",
],
"problemMatcher": []
}, },
{ {
"label": "Kanbn Console", "label": "Kanbn Console",
@ -183,6 +222,26 @@
"type": "npm", "type": "npm",
"script": "kanbn.board.json", "script": "kanbn.board.json",
"problemMatcher": [] "problemMatcher": []
},
{
"label": "Jest",
"type": "shell",
"command": "npx jest",
"problemMatcher": []
},
{
"label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"X",
"L:/DevOps/Mesa_FI/File-Folder-Helper",
"Day-Helper-2025-03-20",
"false",
"4"
],
"problemMatcher": []
} }
] ]
} }

View File

@ -1089,7 +1089,7 @@ internal static partial class Helper20240105
port = new System.Uri(uri).Port; port = new System.Uri(uri).Port;
} }
List<Field> itemFields = (from l in keePassFileGroupEntryStrings select new Field(l.Key, l.Value.Value, 0)).ToList(); List<Field> itemFields = (from l in keePassFileGroupEntryStrings select new Field(l.Key, l.Value.Value, 0)).ToList();
Login login = new(new Uri[] { new(uri, host, port) }, username, password); Login login = new([new(uri, host, port)], username, password);
result = new(revisionDate, result = new(revisionDate,
creationTime, creationTime,
folderId, folderId,
@ -1282,7 +1282,7 @@ internal static partial class Helper20240105
revisionDate = item.RevisionDate; revisionDate = item.RevisionDate;
notes.Add($"{item.Login.Password} on {item.RevisionDate}"); notes.Add($"{item.Login.Password} on {item.RevisionDate}");
} }
login = new(new Uri[] { new(uri, host, port) }, username, password); login = new([new(uri, host, port)], username, password);
result = new(revisionDate, result = new(revisionDate,
creationTime, creationTime,
folderId, folderId,

View File

@ -1,7 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace File_Folder_Helper.ADO2024.PI1; namespace File_Folder_Helper.ADO2024.PI1;
@ -16,12 +14,6 @@ internal static partial class Helper20240108
int ParameterCount, int ParameterCount,
int StartLine); int StartLine);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Method[]))]
private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext
{
}
[GeneratedRegex(@"(?<method>[A-Z]{1}[A-Za-z_0-9]*)\(")] [GeneratedRegex(@"(?<method>[A-Z]{1}[A-Za-z_0-9]*)\(")]
private static partial Regex CSharpMethodName(); private static partial Regex CSharpMethodName();
@ -248,8 +240,6 @@ internal static partial class Helper20240108
{ {
bool result; bool result;
List<string> results = []; List<string> results = [];
if (methods.Count == 0)
File.WriteAllText(".vscode/.json", JsonSerializer.Serialize(methods.ToArray(), MethodCollectionCommonSourceGenerationContext.Default.MethodArray));
ReadOnlyCollection<int> methodLines = GetMethodLines(methods); ReadOnlyCollection<int> methodLines = GetMethodLines(methods);
int minMethodLines = methodLines.Min(); int minMethodLines = methodLines.Min();
for (int i = 0; i < minMethodLines; i++) for (int i = 0; i < minMethodLines; i++)
@ -277,12 +267,18 @@ internal static partial class Helper20240108
return result; return result;
} }
private static bool SortFile(ILogger<Worker> logger, string cSharpFile, string[] lines) private static bool SortFile(ILogger<Worker> logger, bool logOnly, string cSharpFile, string[] lines)
{ {
bool result; bool result;
ReadOnlyCollection<Method> methods = GetMethods(cSharpFile, logger, lines); ReadOnlyCollection<Method> methods = GetMethods(cSharpFile, logger, lines);
if (methods.Count == 0) if (methods.Count == 0)
result = false; result = false;
else if (logOnly)
{
foreach (Method method in methods.OrderBy(l => l.Name))
logger.LogInformation("{cSharpFile} - {Name} has {lines} line(s)", cSharpFile, method.Name, (method.EndLine - method.StartLine).ToString("000000"));
result = false;
}
else else
result = WriteAllLines(cSharpFile, lines, methods); result = WriteAllLines(cSharpFile, lines, methods);
return result; return result;
@ -295,11 +291,12 @@ internal static partial class Helper20240108
string[] lines; string[] lines;
bool usePathCombine = true; bool usePathCombine = true;
long ticks = DateTime.Now.Ticks; long ticks = DateTime.Now.Ticks;
bool logOnly = bool.Parse(args[3]);
logger.LogInformation("{ticks}", ticks); logger.LogInformation("{ticks}", ticks);
string directory = Path.GetFullPath(args[2]); string directory = Path.GetFullPath(args[2]);
string repositoryDirectory = Path.GetFullPath(args[0]); string repositoryDirectory = Path.GetFullPath(args[0]);
string[] cSharpFiles = Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories); string[] cSharpFiles = Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories);
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = Helpers.HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(repositoryDirectory, usePathCombine, cancellationToken); ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = logOnly ? new(cSharpFiles) : Helpers.HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(repositoryDirectory, usePathCombine, cancellationToken);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
foreach (string cSharpFile in cSharpFiles) foreach (string cSharpFile in cSharpFiles)
@ -307,11 +304,11 @@ internal static partial class Helper20240108
if (!gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(cSharpFile)) if (!gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(cSharpFile))
continue; continue;
lines = File.ReadAllLines(cSharpFile); lines = File.ReadAllLines(cSharpFile);
check = SortFile(logger, cSharpFile, lines); check = SortFile(logger, logOnly, cSharpFile, lines);
if (check && !result) if (check && !result)
result = true; result = true;
} }
if (!result) if (logOnly || !result)
break; break;
} }
} }

View File

@ -42,7 +42,7 @@ internal static partial class Helper20240403
string keyIndex = args[5]; string keyIndex = args[5];
string directory = args[0]; string directory = args[0];
logger.LogInformation(directory); logger.LogInformation(directory);
string[] columns = args[4].Split('|'); string[] columns = args[4].Split('~');
DynamicHostConfigurationProtocolConfiguration dynamicHostConfigurationProtocolConfiguration = new(columns, directory, ignore, int.Parse(keyIndex), pattern, primary); DynamicHostConfigurationProtocolConfiguration dynamicHostConfigurationProtocolConfiguration = new(columns, directory, ignore, int.Parse(keyIndex), pattern, primary);
AlertIfNewDeviceIsConnected(dynamicHostConfigurationProtocolConfiguration, logger); AlertIfNewDeviceIsConnected(dynamicHostConfigurationProtocolConfiguration, logger);
} }

View File

@ -100,8 +100,8 @@ internal static partial class Helper20240427
private static void MoveAssets(ILogger<Worker> logger, string var, string home, string pictures, List<Asset> assets) private static void MoveAssets(ILogger<Worker> logger, string var, string home, string pictures, List<Asset> assets)
{ {
string? checkFile; string? checkFile;
ReadOnlyCollection<string> varDirectoryNames = HelperDirectory.GetDirectoryNames(home); ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(home);
string lastVarDirectoryName = varDirectoryNames[^1]; string lastDirectoryName = directoryNames[^1];
foreach (Asset asset in assets) foreach (Asset asset in assets)
{ {
if (asset.OriginalFileName is null) if (asset.OriginalFileName is null)
@ -114,10 +114,10 @@ internal static partial class Helper20240427
continue; continue;
if (asset.OriginalPath is null || !asset.OriginalPath.StartsWith(pictures)) if (asset.OriginalPath is null || !asset.OriginalPath.StartsWith(pictures))
continue; continue;
checkFile = MoveAsset(home, asset.OriginalFileName, lastVarDirectoryName, asset.PreviewPath); checkFile = MoveAsset(home, asset.OriginalFileName, lastDirectoryName, asset.PreviewPath);
if (checkFile is null) if (checkFile is null)
continue; continue;
checkFile = MoveAsset(home, asset.OriginalFileName, lastVarDirectoryName, asset.ThumbnailPath); checkFile = MoveAsset(home, asset.OriginalFileName, lastDirectoryName, asset.ThumbnailPath);
if (checkFile is null) if (checkFile is null)
continue; continue;
logger.LogDebug("<{OriginalFileName}> moved.", asset.OriginalFileName); logger.LogDebug("<{OriginalFileName}> moved.", asset.OriginalFileName);

View File

@ -1,205 +1,733 @@
using File_Folder_Helper.Helpers;
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.ADO2024.PI2; namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240623 internal static partial class Helper20240623
{ {
private record SubTaskLine(string Text, bool Done, long? Ticks, int? Line); [GeneratedRegex("([A-Z]+(.))")]
private record Record(int? CodeInsidersLine, string File, string[] Lines, int? StopLine, int? SubTasksLine); private static partial Regex UpperCase();
private static List<Record> GetRecords(string sourceDirectory, string searchPattern, string codeInsiders, string subTasks) [GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
private static partial Regex InvalidCharacter();
private record H1AndParamCase(string H1, string ParamCase);
private record SubTaskLine(string Text, bool Started, bool Completed, long? Ticks, int? Line);
private record Record(int? CodeInsidersLine, FileInfo FileInfo, LineNumber LineNumber, int? StopLine, int? SubTasksLine);
private record Input(long? AfterEpochTotalMilliseconds,
string CodeInsiders,
ReadOnlyCollection<string> DestinationDirectories,
string DirectoryFilter,
string Done,
string IndexFile,
string SearchPattern,
string SubTasks,
string SourceDirectory,
ReadOnlyCollection<string> Tasks);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Input))]
private partial class InputSourceGenerationContext : JsonSerializerContext
{ {
List<Record> results = []; }
int? stopLine;
string[] lines; private static Record GetRecord(Input input, FileInfo fileInfo)
int? subTasksLine; {
int? codeInsidersLine; Record result;
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); string line;
foreach (string file in files) int? stopLine = null;
int? subTasksLine = null;
int? codeInsidersLine = null;
LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
for (int i = 0; i < lineNumber.Lines.Count; i++)
{ {
stopLine = null; line = lineNumber.Lines[i];
subTasksLine = null; if (line.StartsWith(input.CodeInsiders) && line[^1] == ')')
codeInsidersLine = null; codeInsidersLine = i;
lines = File.ReadAllLines(file); if (line != input.SubTasks)
for (int i = 0; i < lines.Length; i++) continue;
subTasksLine = i;
if (codeInsidersLine is null)
break;
if (lineNumber.Lines.Count > i)
{ {
if (lines[i].StartsWith(codeInsiders) && lines[i][^1] == '"') for (int j = i + 1; j < lineNumber.Lines.Count; j++)
{ {
if (lines.Length > i + 1 && lines[i + 1] == "```") if (lineNumber.Lines[j].Length > 0 && lineNumber.Lines[j][0] == '#')
codeInsidersLine = i;
}
if (lines[i] != subTasks)
continue;
subTasksLine = i;
if (codeInsidersLine is null)
break;
if (lines.Length > i)
{
for (int j = i + 1; j < lines.Length; j++)
{ {
if (lines[j].Length > 0 && lines[j][0] == '#') stopLine = j;
{ break;
stopLine = j;
break;
}
} }
} }
stopLine ??= lines.Length;
break;
} }
results.Add(new(codeInsidersLine, file, lines, stopLine, subTasksLine)); stopLine ??= lineNumber.Lines.Count;
break;
}
result = new(codeInsidersLine, fileInfo, lineNumber, stopLine, subTasksLine);
return result;
}
private static List<Record> GetRecords(Input input)
{
List<Record> results = [];
Record record;
FileInfo fileInfo;
string sourceDirectory = input.SourceDirectory;
ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(input.SourceDirectory);
if (!directoryNames.Any(l => l.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase)))
{
string directoryName;
string[] checkDirectories = Directory.GetDirectories(input.SourceDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string checkDirectory in checkDirectories)
{
directoryName = Path.GetFileName(checkDirectory);
if (directoryName.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
{
sourceDirectory = checkDirectory;
break;
}
}
}
string[] subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
List<string> files = Directory.GetFiles(sourceDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly).ToList();
foreach (string subDirectory in subDirectories)
files.AddRange(Directory.GetFiles(subDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly));
foreach (string file in files)
{
fileInfo = new(file);
record = GetRecord(input, fileInfo);
results.Add(record);
} }
return results; return results;
} }
private static ReadOnlyCollection<SubTaskLine> GetSubTasks(string subTasks, string[] tasks, bool? foundDone, string fallbackLine, FileInfo fileInfo) private static string GetParamCase(string value)
{
string result;
StringBuilder stringBuilder = new(value);
Match[] matches = UpperCase().Matches(value).ToArray();
for (int i = matches.Length - 1; i > -1; i--)
_ = stringBuilder.Insert(matches[i].Index, '-');
string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower());
result = string.Join('-', segments).Trim('-');
return result;
}
private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, bool? foundStarted, bool? foundCompleted, string fallbackLine, Record record)
{ {
List<SubTaskLine> results = []; List<SubTaskLine> results = [];
char done;
string line; string line;
bool doneValue; string text;
string? h1 = null; bool startedValue;
bool completedValue;
SubTaskLine subTaskLine;
bool foundSubTasks = false; bool foundSubTasks = false;
int tasksZeroLength = tasks[0].Length; int tasksZeroLength = input.Tasks[0].Length;
string[] lines = File.ReadAllLines(fileInfo.FullName); long ticks = record.FileInfo.LastWriteTime.Ticks;
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < record.LineNumber.Lines.Count; i++)
{ {
line = lines[i]; line = record.LineNumber.Lines[i];
if (line.StartsWith("# ")) if (!foundSubTasks && line == input.SubTasks)
h1 = line[2..];
if (!foundSubTasks && line == subTasks)
foundSubTasks = true; foundSubTasks = true;
if (!foundSubTasks) if (!foundSubTasks)
continue; continue;
if (line.Length <= tasksZeroLength || !line.StartsWith(tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']') if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
continue; continue;
doneValue = foundDone is not null && foundDone.Value; startedValue = foundStarted is not null && foundStarted.Value;
results.Add(new($" {line}", doneValue, fileInfo.LastWriteTime.Ticks, i)); completedValue = foundCompleted is not null && foundCompleted.Value;
subTaskLine = new(Text: $" {line}", Started: startedValue, Completed: completedValue, Ticks: ticks, Line: i);
results.Add(subTaskLine);
} }
doneValue = foundDone is not null && foundDone.Value; startedValue = foundStarted is not null && foundStarted.Value;
if (h1 is null) completedValue = foundCompleted is not null && foundCompleted.Value;
results.Add(new(fallbackLine, doneValue, fileInfo.LastWriteTime.Ticks, Line: null)); if (record.LineNumber.H1 is null)
subTaskLine = new(Text: fallbackLine, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: null);
else else
results.Add(new(foundDone is null || !foundDone.Value ? $"- [ ] {fileInfo.LastWriteTime.Ticks} ~~~ {h1}" : $"- [x] {fileInfo.LastWriteTime.Ticks} ~~~ {h1}", doneValue, fileInfo.LastWriteTime.Ticks, Line: 0)); {
done = foundCompleted is null || !foundCompleted.Value ? ' ' : 'x';
string codeInsidersLine = record.CodeInsidersLine is null ? string.Empty : $" ~~{record.LineNumber.Lines[record.CodeInsidersLine.Value]}~~";
text = $"- [{done}] {ticks} {record.LineNumber.Lines[record.LineNumber.H1.Value]}{codeInsidersLine}";
subTaskLine = new(Text: text, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: 0);
}
results.Add(subTaskLine);
return new(results); return new(results);
} }
private static string GetSeasonName(int dayOfYear)
{
string result = dayOfYear switch
{
< 78 => "0.Winter",
< 124 => "1.Spring",
< 171 => "2.Spring",
< 217 => "3.Summer",
< 264 => "4.Summer",
< 309 => "5.Fall",
< 354 => "6.Fall",
_ => "7.Winter"
};
return result;
}
private static string[] GetIndexLines(string h1, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection) =>
[
"---",
"startedColumns:",
" - 'In Progress'",
"completedColumns:",
" - Done",
"---",
string.Empty,
h1[0] == '#' ? h1 : $"# {h1}",
string.Empty,
"## Backlog",
string.Empty,
string.Join(Environment.NewLine, h1ParamCaseCollection.Select(l => $"- [{l.ParamCase}](tasks/{l.ParamCase}.md)")),
string.Empty,
"## Todo",
string.Empty,
"## In Progress",
string.Empty,
"## Done",
string.Empty
];
private static string[] GetCascadingStyleSheetsLines() =>
[
".kanbn-column-done .kanbn-column-task-list {",
" border-color: #198038;",
"}",
string.Empty,
".kanbn-task-data-created {",
" display: none;",
"}",
string.Empty,
".kanbn-task-data-workload {",
" display: none;",
"}"
];
private static string GetSettingsLines() =>
/*lang=json,strict*/ """
{
"[markdown]": {
"editor.wordWrap": "off"
},
"cSpell.words": [
"kanbn"
]
}
""";
private static string GetTasksLines(string directory) =>
/*lang=json,strict*/ """
{
"version": "2.0.0",
"tasks": [
{
"label": "File-Folder-Helper AOT s X Day-Helper-2024-06-23",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"X",
"{}",
"Day-Helper-2024-06-23",
"*.md",
"##_Sub-tasks",
"-_[code-insiders](",
"index.md",
"-_[,](",
"##_Done",
".kan",
"D:/5-Other-Small/Kanban/Year-Season",
"316940400000"
],
"problemMatcher": []
}
]
}
""".Replace("{}", directory.Replace('\\', '/'));
private static void FileWriteAllText(string path, string contents)
{
// string checkJson = Regex.Replace(File.ReadAllText(path), @"\s+", " ", RegexOptions.Multiline);
// if (Regex.Replace(singletonJson, @"\s+", " ", RegexOptions.Multiline) != checkJson)
// File.WriteAllText(path, singletonJson);
string old = !File.Exists(path) ? string.Empty : File.ReadAllText(path);
if (old != contents)
File.WriteAllText(path, contents);
}
private static void FileWriteAllText(string path, string[] contents) =>
FileWriteAllText(path, string.Join(Environment.NewLine, contents));
private static ReadOnlyCollection<H1AndParamCase> GetH1ParamCaseCollection(Input input, ReadOnlyCollection<string> lines)
{
List<H1AndParamCase> results = [];
string h1;
string line;
string paramCase;
bool foundSubTasks = false;
H1AndParamCase h1AndParamCase;
int tasksZeroLength = input.Tasks[0].Length;
for (int i = 0; i < lines.Count; i++)
{
line = lines[i];
if (!foundSubTasks && line == input.SubTasks)
foundSubTasks = true;
if (!foundSubTasks)
continue;
if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
continue;
h1 = line[(tasksZeroLength + 3)..];
if (string.IsNullOrEmpty(h1))
continue;
paramCase = GetParamCase(h1);
h1AndParamCase = new(h1, paramCase);
results.Add(h1AndParamCase);
}
return results.AsReadOnly();
}
private static void CreateFiles(string directory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
{
foreach (H1AndParamCase h1ParamCase in h1ParamCaseCollection)
FileWriteAllText(Path.Combine(directory, $"{h1ParamCase.ParamCase}.md"), $"# {h1ParamCase.H1}");
}
private static string WriteAndGetIndexFile(string h1, string verifiedDirectory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
{
string result;
string[] indexLines = GetIndexLines(h1, h1ParamCaseCollection);
string kanbanDirectory = Path.Combine(verifiedDirectory, ".kanbn");
string tasksKanbanDirectory = Path.Combine(kanbanDirectory, "tasks");
if (!Directory.Exists(tasksKanbanDirectory))
_ = Directory.CreateDirectory(tasksKanbanDirectory);
string verifiedVisualStudioCodeDirectory = Path.Combine(verifiedDirectory, ".vscode");
if (!Directory.Exists(verifiedVisualStudioCodeDirectory))
_ = Directory.CreateDirectory(verifiedVisualStudioCodeDirectory);
result = Path.Combine(kanbanDirectory, "index.md");
CreateFiles(tasksKanbanDirectory, h1ParamCaseCollection);
FileWriteAllText(result, indexLines);
FileWriteAllText(Path.Combine(kanbanDirectory, "board.css"), GetCascadingStyleSheetsLines());
FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "settings.json"), GetSettingsLines());
FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "tasks.json"), GetTasksLines(verifiedDirectory));
return result;
}
private static ReadOnlyCollection<string> GetXColumns(Input input, int frontMatterYamlEnd, int value, ReadOnlyCollection<string> lines)
{
List<string> results = [];
string[] segments;
for (int i = value + 1; i < frontMatterYamlEnd; i++)
{
segments = lines[i].Replace("\t", " ").Split(" - ");
if (segments.Length != 2)
break;
results.Add($"## {segments[1].Replace("'", string.Empty)}");
}
if (results.Count == 0)
results.Add(input.Done);
return results.AsReadOnly();
}
private static ReadOnlyCollection<string> GetCompletedColumns(Input input, LineNumber lineNumber)
{
List<string> results;
if (lineNumber.FrontMatterYamlEnd is null || lineNumber.CompletedColumns is null)
results = [];
else
results = GetXColumns(input, lineNumber.FrontMatterYamlEnd.Value, lineNumber.CompletedColumns.Value, lineNumber.Lines).ToList();
if (results.Count == 0)
results.Add(input.Done);
return results.AsReadOnly();
}
private static ReadOnlyCollection<string> GetStartedColumns(Input input, LineNumber lineNumber)
{
List<string> results;
if (lineNumber.FrontMatterYamlEnd is null || lineNumber.StartedColumns is null)
results = [];
else
results = GetXColumns(input, lineNumber.FrontMatterYamlEnd.Value, lineNumber.StartedColumns.Value, lineNumber.Lines).ToList();
if (results.Count == 0)
results.Add(input.Done);
return results.AsReadOnly();
}
private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, FileInfo fileInfo, LineNumber lineNumber)
{
List<SubTaskLine> results = [];
FileInfo f;
Record record;
char completed;
string[] segments;
bool startedValue;
bool completedValue;
string fallbackLine;
bool? foundStarted = null;
bool? foundCompleted = null;
ReadOnlyCollection<SubTaskLine> subTaskLines;
ReadOnlyCollection<string> startedColumns = GetStartedColumns(input, lineNumber);
ReadOnlyCollection<string> completedColumns = GetCompletedColumns(input, lineNumber);
int start = lineNumber.FrontMatterYamlEnd is null ? 0 : lineNumber.FrontMatterYamlEnd.Value + 1;
for (int i = start; i < lineNumber.Lines.Count; i++)
{
if ((foundStarted is null || !foundStarted.Value) && startedColumns.Any(lineNumber.Lines[i].StartsWith))
foundStarted = true;
if ((foundCompleted is null || !foundCompleted.Value) && completedColumns.Any(lineNumber.Lines[i].StartsWith))
foundCompleted = true;
segments = lineNumber.Lines[i].Split(input.Tasks[1]);
startedValue = foundStarted is not null && foundStarted.Value;
completedValue = foundCompleted is not null && foundCompleted.Value;
if (segments.Length > 2 || !segments[0].StartsWith(input.Tasks[0]))
continue;
completed = foundCompleted is null || !foundCompleted.Value ? ' ' : 'x';
fallbackLine = $"- [{completed}] {segments[0][input.Tasks[0].Length..]} ~~FallbackLine~~";
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
continue;
f = new(Path.GetFullPath(Path.Combine(fileInfo.DirectoryName, segments[1][..^1])));
if (!f.Exists)
{
results.Add(new(Text: fallbackLine, Started: startedValue, Completed: completedValue, Ticks: null, Line: null));
continue;
}
record = GetRecord(input, f);
subTaskLines = GetSubTaskLines(input, startedValue, completedValue, fallbackLine, record);
for (int j = subTaskLines.Count - 1; j >= 0; j--)
results.Add(subTaskLines[j]);
}
return results.AsReadOnly();
}
private static Input GetInput(List<string> args)
{
string indexFile = args[5];
string searchPattern = args[2];
string directoryFilter = args[8];
string done = args[7].Replace('_', ' ');
string subTasks = args[3].Replace('_', ' ');
string codeInsiders = args[4].Replace('_', ' ');
string sourceDirectory = Path.GetFullPath(args[0]);
string[] tasks = args[6].Split(',').Select(l => l.Replace('_', ' ')).ToArray();
long? afterEpochTotalMilliseconds = args.Count < 11 ? null : long.Parse(args[10]);
string[] destinationDirectories = args.Count < 10 ? [] : args.Count < 12 ? [Path.GetFullPath(args[9])] : [Path.GetFullPath(args[9]), Path.GetFullPath(args[11])];
Input input = new(AfterEpochTotalMilliseconds: afterEpochTotalMilliseconds,
CodeInsiders: codeInsiders,
DestinationDirectories: destinationDirectories.AsReadOnly(),
DirectoryFilter: directoryFilter,
Done: done,
IndexFile: indexFile,
SearchPattern: searchPattern,
SubTasks: subTasks,
SourceDirectory: sourceDirectory,
Tasks: tasks.AsReadOnly());
if (input.Tasks[0] != "- [" || input.Tasks[1] != "](")
throw new Exception(JsonSerializer.Serialize(input, InputSourceGenerationContext.Default.Input));
return input;
}
private static string? MaybeWriteAndGetIndexFile(Input input, Record record, string? checkDirectory)
{
string? result;
if (string.IsNullOrEmpty(checkDirectory) || input.AfterEpochTotalMilliseconds is null || input.DestinationDirectories.Count == 0)
result = null;
else
{
if (!input.DestinationDirectories.Any(checkDirectory.Contains))
result = null;
else
{
if (record.LineNumber.H1 is null)
result = null;
else
{
string segment = Path.GetFileName(checkDirectory);
string h1 = record.LineNumber.Lines[record.LineNumber.H1.Value];
DateTime utcEpochDateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long utcEpochTotalMilliseconds = (long)Math.Floor(DateTime.UtcNow.Subtract(utcEpochDateTime).TotalMilliseconds);
if (!long.TryParse(segment, out long check) || check < input.AfterEpochTotalMilliseconds || check > utcEpochTotalMilliseconds)
result = null;
else
{
ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection = GetH1ParamCaseCollection(input, record.LineNumber.Lines);
if (h1ParamCaseCollection.Count == 0)
result = null;
else
{
DateTime dateTime = utcEpochDateTime.AddMilliseconds(check).ToLocalTime();
string seasonName = GetSeasonName(dateTime.DayOfYear);
ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(checkDirectory);
if (!directoryNames.Contains(dateTime.Year.ToString()) || !directoryNames.Contains($"{dateTime.Year}-{seasonName}") || !directoryNames.Contains(check.ToString()))
result = null;
else
result = WriteAndGetIndexFile(h1, checkDirectory, h1ParamCaseCollection);
}
}
}
}
}
return result;
}
private static bool FileWrite(long ticks, Record record, List<string> newLines, double percent)
{
bool result = false;
if (record.StopLine is not null && record.SubTasksLine is not null)
{
string contents;
string progressLine;
List<string> resultLines;
if (record.FileInfo.LastWriteTime.Ticks <= ticks)
resultLines = record.LineNumber.Lines.ToList();
else
resultLines = File.ReadAllLines(record.FileInfo.FullName).ToList();
if (record.LineNumber.FrontMatterYamlEnd is not null)
{
progressLine = $"progress: {percent}";
if (record.LineNumber.Progress is not null)
resultLines[record.LineNumber.Progress.Value] = progressLine;
else
{
resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, progressLine);
contents = string.Join(Environment.NewLine, resultLines);
FileWriteAllText(record.FileInfo.FullName, contents);
result = true;
}
if (!result && record.LineNumber.Completed is null && percent > 99.9)
{
resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, $"completed: {DateTime.Now:yyyy-MM-dd}");
contents = string.Join(Environment.NewLine, resultLines);
FileWriteAllText(record.FileInfo.FullName, contents);
result = true;
}
if (!result && record.LineNumber.Completed is not null && percent < 99.9)
{
resultLines.RemoveAt(record.LineNumber.Completed.Value);
contents = string.Join(Environment.NewLine, resultLines);
FileWriteAllText(record.FileInfo.FullName, contents);
result = true;
}
}
if (!result)
{
for (int i = record.StopLine.Value - 1; i > record.SubTasksLine.Value + 1; i--)
resultLines.RemoveAt(i);
if (record.StopLine.Value == record.LineNumber.Lines.Count && resultLines[^1].Length == 0)
resultLines.RemoveAt(resultLines.Count - 1);
for (int i = 0; i < newLines.Count; i++)
resultLines.Insert(record.SubTasksLine.Value + 1 + i, newLines[i]);
resultLines.Insert(record.SubTasksLine.Value + 1, string.Empty);
contents = string.Join(Environment.NewLine, resultLines);
FileWriteAllText(record.FileInfo.FullName, contents);
}
}
return result;
}
private static string? GetInferredCheckDirectory(string directory)
{
string? result = null;
List<string> directoryNames = [];
DirectoryInfo directoryInfo;
string? checkDirectory = directory;
directoryNames.Add(Path.GetFileName(checkDirectory));
string pathRoot = Path.GetPathRoot(directory) ?? throw new Exception();
for (int i = 0; i < directory.Length; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
directoryInfo = new(checkDirectory);
if (!directoryInfo.Exists)
directoryNames.Add(directoryInfo.Name);
else
{
directoryNames.Reverse();
result = string.IsNullOrEmpty(directoryInfo.LinkTarget) ? checkDirectory : directoryInfo.LinkTarget;
for (int j = 0; j < directoryNames.Count; j++)
result = Path.GetDirectoryName(result) ?? throw new Exception();
foreach (string directoryName in directoryNames)
result = Path.Combine(result, directoryName);
break;
}
}
return result;
}
private static void UpdateFileAndStartNewProcess(ILogger<Worker> logger, Input input, Record record, string inferredCheckDirectory)
{
if (record.CodeInsidersLine is null)
throw new Exception();
List<string> lines = record.LineNumber.Lines.ToList();
lines[record.CodeInsidersLine.Value] = $"{input.CodeInsiders}{inferredCheckDirectory})";
string text = string.Join(Environment.NewLine, lines);
File.WriteAllText(record.FileInfo.FullName, text);
record.FileInfo.Refresh();
string file = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "Microsoft VS Code Insiders", "Code - Insiders.exe");
try
{ _ = Process.Start(file, $"\"{inferredCheckDirectory}\""); }
catch (Exception) { logger.LogWarning("Failed to start code-insiders!"); }
}
private static FileInfo GetIndexFileInfo(ILogger<Worker> logger, Input input, Record record)
{
FileInfo result;
string? indexFile;
List<string> results;
if (record.CodeInsidersLine is null)
throw new Exception();
string codeInsidersLine = record.LineNumber.Lines[record.CodeInsidersLine.Value];
string raw = codeInsidersLine[input.CodeInsiders.Length..^1];
string checkDirectory = $"{raw[..2].ToUpper()}{raw[2..]}";
if (!Directory.Exists(checkDirectory))
{
if (input.DestinationDirectories.Count > 0 && input.DestinationDirectories.Any(checkDirectory.Contains))
{
string? inferredCheckDirectory = GetInferredCheckDirectory(checkDirectory);
if (!string.IsNullOrEmpty(inferredCheckDirectory))
{
checkDirectory = inferredCheckDirectory;
_ = Directory.CreateDirectory(inferredCheckDirectory);
UpdateFileAndStartNewProcess(logger, input, record, inferredCheckDirectory);
}
}
}
if (!Directory.Exists(checkDirectory))
results = [];
else
{
results = Directory.GetFiles(checkDirectory, input.IndexFile, SearchOption.AllDirectories).ToList();
if (results.Count != 1)
{
for (int i = results.Count - 1; i > -1; i--)
{
if (!results[i].Contains(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
results.RemoveAt(i);
}
}
if (results.Count == 0)
{
indexFile = MaybeWriteAndGetIndexFile(input, record, checkDirectory);
if (!string.IsNullOrEmpty(indexFile))
results.Add(indexFile);
else
logger.LogInformation("<{checkDirectory}>", checkDirectory);
}
}
result = results.Count == 0 ? new(Path.Combine(checkDirectory, input.IndexFile)) : new(results[0]);
return result;
}
internal static void UpdateSubTasksInMarkdownFiles(ILogger<Worker> logger, List<string> args) internal static void UpdateSubTasksInMarkdownFiles(ILogger<Worker> logger, List<string> args)
{ {
bool reload;
int allCount;
int lineCheck; int lineCheck;
bool doneValue; double percent;
bool? foundDone; string replace;
FileInfo fileInfo; FileInfo fileInfo;
string[] newLines; double startedCount;
string[] segments; List<Record> records;
List<string> lines; double completedCount;
string fallbackLine; LineNumber lineNumber;
string[] indexLines; List<string> newLines;
string checkDirectory; bool reloadAny = false;
string done = args[7]; string? checkDirectory;
List<string> indexFiles;
string subTasks = args[3];
List<string> oldLines = []; List<string> oldLines = [];
string indexFile = args[5]; Input input = GetInput(args);
string searchPattern = args[2]; string fileNameWithoutExtension;
string directoryFilter = args[8]; long ticks = DateTime.Now.Ticks;
string[] tasks = args[6].Split(',');
string codeInsiders = $"{args[4]} \"";
List<SubTaskLine> allSubTaskLines = [];
ReadOnlyCollection<SubTaskLine> subTaskLines; ReadOnlyCollection<SubTaskLine> subTaskLines;
string sourceDirectory = Path.GetFullPath(args[0]); for (int z = 0; z < 9; z++)
List<Record> records = GetRecords(sourceDirectory, searchPattern, codeInsiders, subTasks);
foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l)
{ {
if (record.SubTasksLine is null) records = GetRecords(input);
continue; foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l)
if (record.CodeInsidersLine is not null)
logger.LogInformation("<{file}> has [{subTasks}]", Path.GetFileNameWithoutExtension(record.File), subTasks);
else
{ {
logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", Path.GetFileNameWithoutExtension(record.File), subTasks, codeInsiders); if (record.SubTasksLine is null)
continue; continue;
} fileNameWithoutExtension = Path.GetFileNameWithoutExtension(record.FileInfo.FullName);
if (record.StopLine is null) if (record.CodeInsidersLine is not null)
continue; logger.LogInformation("<{file}> has [{subTasks}]", fileNameWithoutExtension, input.SubTasks);
checkDirectory = record.Lines[record.CodeInsidersLine.Value][codeInsiders.Length..^1]; else
if (!Directory.Exists(checkDirectory))
{
logger.LogError("<{checkDirectory}> doesn't exist", Path.GetFileName(checkDirectory));
continue;
}
indexFiles = Directory.GetFiles(checkDirectory, indexFile, SearchOption.AllDirectories).ToList();
if (indexFiles.Count != 1)
{
for (int i = indexFiles.Count - 1; i > -1; i--)
{ {
if (!indexFiles[i].Contains(directoryFilter, StringComparison.CurrentCultureIgnoreCase)) logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", fileNameWithoutExtension, input.SubTasks, input.CodeInsiders);
indexFiles.RemoveAt(i);
}
if (indexFiles.Count != 1)
{
logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", Path.GetFileName(checkDirectory), indexFile);
continue; continue;
} }
} if (record.StopLine is null)
foundDone = null;
oldLines.Clear();
allSubTaskLines.Clear();
indexLines = File.ReadAllLines(indexFiles[0]);
checkDirectory = Path.GetDirectoryName(indexFiles[0]) ?? throw new Exception();
for (int i = 0; i < indexLines.Length; i++)
{
if (indexLines[i] == done)
foundDone = true;
segments = indexLines[i].Split(tasks[1]);
doneValue = foundDone is not null && foundDone.Value;
if (segments.Length > 2 || !segments[0].StartsWith(tasks[0]))
continue; continue;
fallbackLine = foundDone is null || !foundDone.Value ? $"- [ ] {segments[0][tasks[0].Length..]}" : $"- [x] {segments[0][tasks[0].Length..]}"; fileInfo = GetIndexFileInfo(logger, input, record);
fileInfo = new(Path.GetFullPath(Path.Combine(checkDirectory, segments[1][..^1])));
if (!fileInfo.Exists) if (!fileInfo.Exists)
{ {
allSubTaskLines.Add(new(fallbackLine, doneValue, Ticks: null, Line: null)); logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", fileInfo.DirectoryName, input.IndexFile);
continue; continue;
} }
subTaskLines = GetSubTasks(subTasks, tasks, doneValue, fallbackLine, fileInfo); oldLines.Clear();
for (int j = subTaskLines.Count - 1; j >= 0; j--) checkDirectory = fileInfo.DirectoryName;
allSubTaskLines.Add(subTaskLines[j]); lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
} subTaskLines = GetSubTaskLines(input, fileInfo, lineNumber);
if (allSubTaskLines.Count == 0) if (subTaskLines.Count == 0)
continue; continue;
lineCheck = 0; lineCheck = 0;
for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++) for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++)
oldLines.Add(record.Lines[i]); oldLines.Add(record.LineNumber.Lines[i]);
if (allSubTaskLines.Any(l => l.Ticks is null)) if (subTaskLines.Count == 0)
newLines = (from l in allSubTaskLines select l.Text).ToArray();
else
newLines = (from l in allSubTaskLines orderby l.Done descending, l.Ticks, l.Line select l.Text).ToArray();
if (newLines.Length == oldLines.Count)
{
for (int i = 0; i < newLines.Length; i++)
{ {
if (newLines[i] != record.Lines[record.SubTasksLine.Value + 1 + i]) percent = 0;
continue; replace = "0";
lineCheck++;
} }
if (lineCheck == newLines.Length) else
{
allCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 select 1).Count();
completedCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Completed select 1).Count();
startedCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Started && !l.Completed select 1).Count();
percent = allCount == 0 ? 0 : Math.Round(completedCount / allCount, 3);
// newLines.Insert(0, $"- [{done}] Sub-tasks {doneCount} of {allCount} [{percent * 100}%]");
replace = $"{allCount} » {startedCount} ✓ {completedCount} {Math.Floor(percent * 100)}%".Replace(" ✓ 0 0%", string.Empty).Replace(" 100%", string.Empty).Replace(" » 0", string.Empty);
}
if (subTaskLines.Any(l => l.Ticks is null))
newLines = (from l in subTaskLines
select l.Text).ToList();
else
{
newLines = (from l in subTaskLines
orderby l.Completed descending, l.Started descending, l.Ticks, l.Line
select l.Text.Replace($"{l.Ticks}", replace)).ToList();
}
if (newLines.Count == oldLines.Count)
{
for (int i = 0; i < newLines.Count; i++)
{
if (newLines[i] != record.LineNumber.Lines[record.SubTasksLine.Value + 1 + i])
continue;
lineCheck++;
}
if (lineCheck == newLines.Count)
continue;
}
if (string.IsNullOrEmpty(checkDirectory))
continue; continue;
checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString());
_ = Directory.CreateDirectory(checkDirectory);
Thread.Sleep(500);
Directory.Delete(checkDirectory);
reload = FileWrite(ticks, record, newLines, percent);
if (!reloadAny && reload)
reloadAny = true;
} }
checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString()); if (!reloadAny)
_ = Directory.CreateDirectory(checkDirectory); break;
Thread.Sleep(500);
Directory.Delete(checkDirectory);
lines = record.Lines.ToList();
for (int i = record.StopLine.Value - 1; i > record.SubTasksLine.Value + 1; i--)
lines.RemoveAt(i);
if (record.StopLine.Value == record.Lines.Length && lines[^1].Length == 0)
lines.RemoveAt(lines.Count - 1);
for (int i = 0; i < newLines.Length; i++)
lines.Insert(record.SubTasksLine.Value + 1 + i, newLines[i]);
lines.Insert(record.SubTasksLine.Value + 1, string.Empty);
File.WriteAllLines(record.File, lines);
} }
} }

View File

@ -1,206 +0,0 @@
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240724
{
private record FileConnectorConfigurationSystem(string AlternateTargetFolder,
string FileAgeThreshold,
string[] SourceFileFilters,
string TargetFileLocation);
#pragma warning disable IDE0028, IDE0056, IDE0300, IDE0240, IDE0241
private static readonly HttpClient _HttpClient = new();
private static readonly string _StaticFileServer = "localhost:5054";
private static readonly FileConnectorConfigurationSystem _FileConnectorConfiguration = new(
"D:/Tmp/Phares/AlternateTargetFolder",
"000:20:00:01",
[".txt"],
"D:/Tmp/Phares/TargetFileLocation");
private static string[] GetValidDays(DateTime fileAgeThresholdDateTime)
{
DateTime dateTime = DateTime.Now;
return new string[] { dateTime.ToString("yyyy-MM-dd"), fileAgeThresholdDateTime.ToString("yyyy-MM-dd") }.Distinct().ToArray();
}
private static string[] GetValidWeeks(DateTime fileAgeThresholdDateTime)
{
DateTime dateTime = DateTime.Now;
Calendar calendar = new CultureInfo("en-US").Calendar;
string weekOfYear = $"{dateTime:yyyy}_Week_{calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
string lastWeekOfYear = $"{fileAgeThresholdDateTime:yyyy}_Week_{calendar.GetWeekOfYear(fileAgeThresholdDateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
return new string[] { weekOfYear, lastWeekOfYear }.Distinct().ToArray();
}
private static ReadOnlyCollection<NginxFileSystem> GetDayNginxFileSystemCollection(DateTime fileAgeThresholdDateTime, string week, string day, string dayUrl, NginxFileSystem[] dayNginxFileSystemCollection)
{
List<NginxFileSystem> results = new();
DateTime dateTime;
string nginxFormat = "ddd, dd MMM yyyy HH:mm:ss zzz";
foreach (NginxFileSystem dayNginxFileSystem in dayNginxFileSystemCollection)
{
if (!DateTime.TryParseExact(dayNginxFileSystem.MTime.Replace("GMT", "+00:00"), nginxFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
if (dateTime < fileAgeThresholdDateTime)
continue;
results.Add(new(
Path.GetFullPath(Path.Combine(_FileConnectorConfiguration.TargetFileLocation, week, day, dayNginxFileSystem.Name)),
string.Concat(dayUrl, '/', dayNginxFileSystem.Name),
dateTime.ToString(),
dayNginxFileSystem.Size));
}
return results.AsReadOnly();
}
private static DateTime GetFileAgeThresholdDateTime(string fileAgeThreshold)
{
DateTime result = DateTime.Now;
string[] segments = fileAgeThreshold.Split(':');
for (int i = 0; i < segments.Length; i++)
{
result = i switch
{
0 => result.AddDays(double.Parse(segments[i]) * -1),
1 => result.AddHours(double.Parse(segments[i]) * -1),
2 => result.AddMinutes(double.Parse(segments[i]) * -1),
3 => result.AddSeconds(double.Parse(segments[i]) * -1),
_ => throw new Exception(),
};
}
return result;
}
private static ReadOnlyCollection<NginxFileSystem> GetDayNginxFileSystemCollection(DateTime fileAgeThresholdDateTime)
{
#nullable enable
List<NginxFileSystem> results = new();
string dayUrl;
string dayJson;
string weekJson;
string checkWeek;
Task<HttpResponseMessage> task;
NginxFileSystem[]? dayNginxFileSystemCollection;
NginxFileSystem[]? weekNginxFileSystemCollection;
string[] days = GetValidDays(fileAgeThresholdDateTime);
string[] weeks = GetValidWeeks(fileAgeThresholdDateTime);
foreach (string week in weeks)
{
checkWeek = string.Concat("http://", _StaticFileServer, '/', week);
task = _HttpClient.GetAsync(checkWeek);
task.Wait();
if (!task.Result.IsSuccessStatusCode)
continue;
weekJson = _HttpClient.GetStringAsync(checkWeek).Result;
weekNginxFileSystemCollection = JsonSerializer.Deserialize(weekJson, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (weekNginxFileSystemCollection is null)
continue;
foreach (NginxFileSystem weekNginxFileSystem in weekNginxFileSystemCollection)
{
if (!(from l in days where weekNginxFileSystem.Name == l select false).Any())
continue;
dayUrl = string.Concat(checkWeek, '/', weekNginxFileSystem.Name);
dayJson = _HttpClient.GetStringAsync(dayUrl).Result;
dayNginxFileSystemCollection = JsonSerializer.Deserialize(dayJson, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (dayNginxFileSystemCollection is null)
continue;
results.AddRange(GetDayNginxFileSystemCollection(fileAgeThresholdDateTime, week, weekNginxFileSystem.Name, dayUrl, dayNginxFileSystemCollection));
}
}
return results.AsReadOnly();
#nullable disable
}
private static ReadOnlyCollection<Tuple<DateTime, FileInfo, FileInfo, string>> GetPossible()
{
List<Tuple<DateTime, FileInfo, FileInfo, string>> results = new();
DateTime dateTime;
FileInfo targetFileInfo;
FileInfo alternateFileInfo;
DateTime fileAgeThresholdDateTime = GetFileAgeThresholdDateTime(_FileConnectorConfiguration.FileAgeThreshold);
ReadOnlyCollection<NginxFileSystem> dayNginxFileSystemCollection = GetDayNginxFileSystemCollection(fileAgeThresholdDateTime);
foreach (NginxFileSystem nginxFileSystem in dayNginxFileSystemCollection)
{
targetFileInfo = new FileInfo(nginxFileSystem.Name);
if (targetFileInfo.Directory is null)
continue;
if (!Directory.Exists(targetFileInfo.Directory.FullName))
_ = Directory.CreateDirectory(targetFileInfo.Directory.FullName);
if (!DateTime.TryParse(nginxFileSystem.MTime, out dateTime))
continue;
if (targetFileInfo.Exists && targetFileInfo.LastWriteTime == dateTime)
continue;
alternateFileInfo = new(Path.Combine(_FileConnectorConfiguration.AlternateTargetFolder, nginxFileSystem.Name));
results.Add(new(dateTime, targetFileInfo, alternateFileInfo, nginxFileSystem.Type));
}
return (from l in results orderby l.Item1 select l).ToList().AsReadOnly();
}
private static void Test()
{
#nullable enable
if (_HttpClient is null)
throw new Exception();
if (string.IsNullOrEmpty(_StaticFileServer))
throw new Exception();
if (string.IsNullOrEmpty(_StaticFileServer))
{
ReadOnlyCollection<Tuple<DateTime, FileInfo, FileInfo, string>> possibleDownload = GetPossible();
if (possibleDownload.Count > 0)
{
string targetFileName = possibleDownload[0].Item4;
FileInfo targetFileInfo = possibleDownload[0].Item2;
FileInfo alternateFileInfo = possibleDownload[0].Item3;
DateTime matchNginxFileSystemDateTime = possibleDownload[0].Item1;
// if (alternateFileInfo.Exists)
// File.Delete(alternateFileInfo.FullName);
if (targetFileInfo.Exists)
File.Delete(targetFileInfo.FullName);
string targetJson = _HttpClient.GetStringAsync(targetFileName).Result;
File.WriteAllText(targetFileInfo.FullName, targetJson);
targetFileInfo.LastWriteTime = matchNginxFileSystemDateTime;
// File.Copy(targetFileInfo.FullName, alternateFileInfo.FullName);
File.AppendAllText(alternateFileInfo.FullName, targetJson);
}
}
#nullable disable
}
internal static void CopyDirectories(ILogger<Worker> logger, List<string> args)
{
Test();
string[] files;
Process process;
string checkDirectory;
string filter = args[3];
string replaceWith = args[4];
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] foundDirectories = Directory.GetDirectories(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation($"Found {foundDirectories.Length} directories");
foreach (string foundDirectory in foundDirectories)
{
if (!foundDirectory.Contains(filter))
continue;
logger.LogDebug(foundDirectory);
checkDirectory = foundDirectory.Replace(filter, replaceWith);
if (Directory.Exists(checkDirectory))
{
files = Directory.GetFiles(checkDirectory, "*", SearchOption.AllDirectories);
if (files.Length > 0)
continue;
Directory.Delete(checkDirectory);
}
process = Process.Start("cmd.exe", $"/c xCopy \"{foundDirectory}\" \"{checkDirectory}\" /S /E /I /H /Y");
process.WaitForExit();
}
}
}

View File

@ -1,12 +1,17 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
#if CommonMark
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
#endif
namespace File_Folder_Helper.ADO2024.PI3; namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240911 internal static partial class Helper20240911
{ {
#if CommonMark
private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked, private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked,
[property: JsonPropertyName("name")] string Name); [property: JsonPropertyName("name")] string Name);
@ -696,7 +701,7 @@ internal static partial class Helper20240911
List<char> spaces = []; List<char> spaces = [];
List<string> lines = []; List<string> lines = [];
ReadOnlyCollection<WorkItem> results; ReadOnlyCollection<WorkItem> results;
string[] workItemTypes = args[4].Split('|'); string[] workItemTypes = args[4].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]); string sourceDirectory = Path.GetFullPath(args[0]);
string destinationDirectory = Path.GetFullPath(args[6]); string destinationDirectory = Path.GetFullPath(args[6]);
if (!Directory.Exists(destinationDirectory)) if (!Directory.Exists(destinationDirectory))
@ -758,4 +763,14 @@ internal static partial class Helper20240911
} }
} }
#else
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{
logger.LogError("WriteMarkdown is not available in CommonMark {args[0]}", args[0]);
logger.LogError("WriteMarkdown is not available in CommonMark {args[1]}", args[1]);
}
#endif
} }

View File

@ -1,57 +1,46 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.ADO2024.PI3; namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240916 internal static partial class Helper20240916
{ {
private static ReadOnlyCollection<string> Get(string searchPattern, string[] configFileLines)
{
List<string> results = [];
string[] segments;
foreach (string line in configFileLines)
{
if (line.Length < 2 || line[0] == '#')
continue;
segments = line.Split(searchPattern);
if (segments.Length < 2)
continue;
results.Add(segments[1].Split(';')[0]);
}
return results.AsReadOnly();
}
internal static void DebugProxyPass(ILogger<Worker> logger, List<string> args) internal static void DebugProxyPass(ILogger<Worker> logger, List<string> args)
{ {
string debug; string debug;
string enable; string domain;
string[] lines; string[] lines;
string fileName; string fileName;
string[] segments; string[] segments;
string configFile = args[3]; string includePath = args[3];
string searchString = args[4];
string searchPattern = args[2]; string searchPattern = args[2];
string searchPatternB = args[4]; string[] searchStrings = args[5].Split('~');
string searchPatternC = args[5];
string sourceDirectory = Path.GetFullPath(args[0]); string sourceDirectory = Path.GetFullPath(args[0]);
string[] configFileLines = File.ReadAllLines(configFile);
ReadOnlyCollection<string> enabledSites = Get(searchPatternB, configFileLines);
logger.LogInformation("Found {files} file(s)", enabledSites.Count);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length);
foreach (string file in files) foreach (string file in files)
{ {
debug = string.Empty; debug = string.Empty;
domain = string.Empty;
lines = File.ReadAllLines(file); lines = File.ReadAllLines(file);
fileName = Path.GetFileName(file); fileName = Path.GetFileName(file);
foreach (string line in lines) foreach (string line in lines)
{ {
segments = line.Split(searchPatternC); segments = line.Split(searchString, StringSplitOptions.None);
if (segments.Length > 1 && segments[1][0] is ' ' or '\t')
domain = segments[1].Trim().Trim(';');
segments = line.Split(searchStrings, StringSplitOptions.None);
if (segments.Length < 2) if (segments.Length < 2)
continue; continue;
debug = segments[1].Trim(); if (segments[1][0] is not ' ' and not '\t')
continue;
debug = segments[1].Trim().Trim(';');
} }
enable = enabledSites.Contains(fileName) ? string.Empty : "# "; logger.LogInformation("include {includePath}{fileName}; # https://{domain} # {debug}",
logger.LogInformation("{enable}{searchPatternB}{fileName}; # https://{fileName}.phares.duckddns.org # {debug}", enable, searchPatternB, fileName, fileName, debug); includePath,
fileName,
domain,
debug);
} }
} }

View File

@ -1,19 +1,26 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
#if WorkItems
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
#endif
namespace File_Folder_Helper.ADO2024.PI4; namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241108 internal static partial class Helper20241108
{ {
private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked, #if WorkItems
[property: JsonPropertyName("name")] string Name);
private record Relation([property: JsonPropertyName("rel")] string Type, private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked,
[property: JsonPropertyName("url")] string URL, [property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("attributes")] Attribute Attributes); [property: JsonPropertyName("parameterTitle")] string? ParameterTitle,
[property: JsonPropertyName("state")] string? State,
[property: JsonPropertyName("workItemType")] string? WorkItemType);
private record Relation([property: JsonPropertyName("attributes")] Attribute Attributes,
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("rel")] string Rel);
private record WorkItem(DateTime? ActivatedDate, private record WorkItem(DateTime? ActivatedDate,
string AreaPath, string AreaPath,
@ -47,6 +54,39 @@ internal static partial class Helper20241108
public override string ToString() => $"{Id} - {WorkItemType} - {Title}"; public override string ToString() => $"{Id} - {WorkItemType} - {Title}";
public static WorkItem Get(WorkItem workItem, Relation[] relations)
{
WorkItem result = new(workItem.ActivatedDate,
workItem.AreaPath,
workItem.AssignedTo,
workItem.BusinessValue,
workItem.ChangedDate,
workItem.ClosedDate,
workItem.CommentCount,
workItem.CreatedDate,
workItem.Description,
workItem.Effort,
workItem.Id,
workItem.IterationPath,
workItem.Parent,
workItem.Priority,
relations,
workItem.Requester,
workItem.ResolvedDate,
workItem.Revision,
workItem.RiskReductionMinusOpportunityEnablement,
workItem.StartDate,
workItem.State,
workItem.Tags,
workItem.TargetDate,
workItem.TimeCriticality,
workItem.Title,
workItem.Violation,
workItem.WeightedShortestJobFirst,
workItem.WorkItemType);
return result;
}
public static WorkItem? GetWithOutRelations(WorkItem? workItem) public static WorkItem? GetWithOutRelations(WorkItem? workItem)
{ {
WorkItem? result = workItem is null ? null : new(workItem.ActivatedDate, WorkItem? result = workItem is null ? null : new(workItem.ActivatedDate,
@ -88,7 +128,13 @@ internal static partial class Helper20241108
{ {
} }
private record Record(WorkItem WorkItem, WorkItem? Parent, Record[] Children, Record[] Related, Record[] Successors) [JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WorkItem[]))]
private partial class WorkItemCollectionSourceGenerationContext : JsonSerializerContext
{
}
private record Record(WorkItem WorkItem, WorkItem? Parent, Record[]? Children, Record[]? Related, Record[]? Successors)
{ {
internal static Record GetWithoutNesting(Record record, string? violation) internal static Record GetWithoutNesting(Record record, string? violation)
@ -122,95 +168,95 @@ internal static partial class Helper20241108
record.WorkItem.Violation is null ? violation : record.WorkItem.Violation, record.WorkItem.Violation is null ? violation : record.WorkItem.Violation,
record.WorkItem.WeightedShortestJobFirst, record.WorkItem.WeightedShortestJobFirst,
record.WorkItem.WorkItemType); record.WorkItem.WorkItemType);
result = new(workItem, record.Parent, [], [], []); result = new(workItem, record.Parent, null, null, null);
return result; return result;
} }
private static Record Get(Record record, bool keepRelations) private static Record Get(Record record, bool keepRelations)
{ {
Record result; Record result;
Record[]? childRecords;
Record[]? relatedRecords;
Record[]? successorRecords;
List<Record> relationRecords;
WorkItem? parentWorkItem = keepRelations ? record.Parent : WorkItem.GetWithOutRelations(record.Parent); WorkItem? parentWorkItem = keepRelations ? record.Parent : WorkItem.GetWithOutRelations(record.Parent);
WorkItem? workItem = keepRelations ? record.WorkItem : WorkItem.GetWithOutRelations(record.WorkItem) ?? throw new Exception(); WorkItem? workItem = keepRelations ? record.WorkItem : WorkItem.GetWithOutRelations(record.WorkItem) ?? throw new Exception();
List<Record> childRecords = []; if (record.Children is null)
List<Record> relatedRecords = []; childRecords = null;
List<Record> successorRecords = [];
foreach (Record r in record.Children)
childRecords.Add(Get(r, keepRelations));
foreach (Record r in record.Related)
relatedRecords.Add(Get(r, keepRelations));
foreach (Record r in record.Successors)
successorRecords.Add(Get(r, keepRelations));
result = new(workItem, parentWorkItem, childRecords.ToArray(), relatedRecords.ToArray(), successorRecords.ToArray());
return result;
}
internal static Record Get(WorkItem workItem, WorkItem? parent, ReadOnlyCollection<Record> children, ReadOnlyCollection<Record> related, ReadOnlyCollection<Record> successors, bool keepRelations)
{
Record result;
Record record = new(workItem, parent, children.ToArray(), related.ToArray(), successors.ToArray());
result = Get(record, keepRelations);
return result;
}
private static int? GetIdFromUrl(string relationName, Relation relation)
{
int? result;
string[] segments = relation?.Attributes is null || relation.Attributes.Name != relationName ? [] : relation.URL.Split('/');
if (segments.Length < 2)
result = null;
else else
{ {
if (!int.TryParse(segments[^1], out int id)) relationRecords = [];
result = null; foreach (Record r in record.Children)
else relationRecords.Add(Get(r, keepRelations));
result = id; childRecords = relationRecords.ToArray();
} }
if (record.Related is null)
relatedRecords = null;
else
{
relationRecords = [];
foreach (Record r in record.Related)
relationRecords.Add(Get(r, keepRelations));
relatedRecords = relationRecords.ToArray();
}
if (record.Successors is null)
successorRecords = null;
else
{
relationRecords = [];
foreach (Record r in record.Successors)
relationRecords.Add(Get(r, keepRelations));
successorRecords = relationRecords.ToArray();
}
result = new(workItem, parentWorkItem, childRecords, relatedRecords, successorRecords);
return result;
}
internal static Record Get(WorkItem workItem, WorkItem? parent, ReadOnlyCollection<Record>? children, ReadOnlyCollection<Record>? related, ReadOnlyCollection<Record>? successors, bool keepRelations)
{
Record result;
Record record = new(workItem, parent, children?.ToArray(), related?.ToArray(), successors?.ToArray());
result = Get(record, keepRelations);
return result; return result;
} }
internal static ReadOnlyCollection<Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, WorkItem workItem, string relationName, List<bool> nests, bool keepRelations) internal static ReadOnlyCollection<Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, WorkItem workItem, string relationName, List<bool> nests, bool keepRelations)
{ {
List<Record> results = []; List<Record> results = [];
int? childId;
Record record; Record record;
nests.Add(true); nests.Add(true);
WorkItem? childWorkItem;
WorkItem? parentWorkItem; WorkItem? parentWorkItem;
WorkItem? relationWorkItem;
List<WorkItem> collection = []; List<WorkItem> collection = [];
ReadOnlyCollection<Record> childRecords; ReadOnlyCollection<Record>? childRecords;
ReadOnlyCollection<Record> relatedRecords; ReadOnlyCollection<Record>? relatedRecords;
ReadOnlyCollection<Record> successorRecords; ReadOnlyCollection<Record>? successorRecords;
if (workItem.Relations is not null && workItem.Relations.Length > 0) if (workItem.Relations is not null && workItem.Relations.Length > 0)
{ {
collection.Clear(); collection.Clear();
foreach (Relation relation in workItem.Relations) foreach (Relation relation in workItem.Relations)
{ {
childId = GetIdFromUrl(relationName, relation); if (relation.Attributes.Name != relationName)
if (childId is not null && workItem.Parent is not null && relation?.URL is not null && relation.URL.Contains(workItem.Parent.Value.ToString()))
continue; continue;
if (childId is null || !keyValuePairs.TryGetValue(childId.Value, out childWorkItem)) if (workItem.Parent is not null && relation.Id == workItem.Parent.Value)
continue; continue;
collection.Add(childWorkItem); if (!keyValuePairs.TryGetValue(relation.Id, out relationWorkItem))
continue;
collection.Add(relationWorkItem);
} }
collection = (from l in collection orderby l.State != "Closed", l.Id select l).ToList(); collection = (from l in collection orderby l.State != "Closed", l.Id select l).ToList();
foreach (WorkItem w in collection) foreach (WorkItem w in collection)
{ {
if (nests.Count > 99) if (nests.Count > 500)
break; break;
if (w.Parent is null) if (w.Parent is null)
parentWorkItem = null; parentWorkItem = null;
else else
_ = keyValuePairs.TryGetValue(w.Parent.Value, out parentWorkItem); _ = keyValuePairs.TryGetValue(w.Parent.Value, out parentWorkItem);
childRecords = GetKeyValuePairs(keyValuePairs, w, "Child", nests, keepRelations); // Forward childRecords = GetKeyValuePairs(keyValuePairs, w, "Child", nests, keepRelations); // Forward
// records = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests); // Reverse relatedRecords = null; // GetKeyValuePairs(keyValuePairs, w, "Related", nests, keepRelations); // Related
// successorRecords = GetKeyValuePairs(keyValuePairs, w, "Successor", nests); // Forward successorRecords = null; // GetKeyValuePairs(keyValuePairs, w, "Successor", nests, keepRelations); // Forward
// if (successorRecords.Count > 0) // predecessorRecords = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests, keepRelations); // Reverse
// {
// if (successorRecords.Count > 0)
// { }
// }
successorRecords = new([]);
relatedRecords = GetKeyValuePairs(keyValuePairs, w, "Related", nests, keepRelations); // Related
record = Get(w, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); record = Get(w, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations);
results.Add(record); results.Add(record);
} }
@ -269,125 +315,70 @@ internal static partial class Helper20241108
return results; return results;
} }
private static ReadOnlyDictionary<int, string> GetCurrentDirectories(string destinationDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes) private static ReadOnlyCollection<WorkItem>? GetWorkItems(string fileName, string sourceDirectory)
{ {
Dictionary<int, string> results = []; WorkItem[]? results;
int id; string? checkFile = null;
string idCheck; string? checkDirectory = sourceDirectory;
string? fileName; string? pathRoot = Path.GetPathRoot(sourceDirectory);
string[] directories; for (int i = 0; i < int.MaxValue; i++)
string[] split = ["-"];
foreach (string w in bugUserStoryWorkItemTypes)
{ {
directories = Directory.GetDirectories(destinationDirectory, $"*-{w.Replace(" ", "-")}", SearchOption.AllDirectories); checkDirectory = Path.GetDirectoryName(checkDirectory);
foreach (string directory in directories) if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
{ break;
fileName = Path.GetFileName(directory); checkFile = Path.Combine(checkDirectory, fileName);
if (string.IsNullOrEmpty(fileName))
continue;
idCheck = fileName.Split(split, StringSplitOptions.None)[0];
if (!int.TryParse(idCheck, out id))
continue;
if (!results.ContainsKey(id))
results.Add(id, directory);
else
MoveToDuplicate(destinationDirectory, directory);
}
}
return new(results);
}
private static void MoveToDuplicate(string destinationDirectory, string directory)
{
if (string.IsNullOrEmpty(destinationDirectory))
throw new ArgumentException($"'{nameof(destinationDirectory)}' cannot be null or empty.", nameof(destinationDirectory));
if (string.IsNullOrEmpty(directory))
throw new ArgumentException($"'{nameof(directory)}' cannot be null or empty.", nameof(directory));
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(string sourceDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes)
{
List<WorkItem> results = [];
string json;
string checkFile;
WorkItem? workItem;
string directoryName;
string? checkDirectory = null;
foreach (string w in bugUserStoryWorkItemTypes)
{
directoryName = Path.GetFileName(sourceDirectory);
if (!directoryName.EndsWith(w.Replace(" ", "-")))
continue;
checkDirectory = Path.Combine(sourceDirectory, directoryName.Split('-')[0]);
if (!Directory.Exists(checkDirectory))
{
checkDirectory = null;
continue;
}
break;
}
if (!string.IsNullOrEmpty(checkDirectory))
{
checkFile = Path.Combine(checkDirectory, ".json");
if (File.Exists(checkFile)) if (File.Exists(checkFile))
{ break;
json = File.ReadAllText(checkFile); checkFile = null;
workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem);
if (workItem is not null)
results.Add(workItem);
}
} }
return new(results); if (checkFile is null)
} results = null;
private static ReadOnlyCollection<WorkItem> GetWorkItems(ReadOnlyDictionary<int, string> collection)
{
List<WorkItem> results = [];
string json;
string checkFile;
string? directory;
WorkItem? workItem;
string? checkDirectory;
foreach (KeyValuePair<int, string> keyValuePair in collection)
{
if (!collection.TryGetValue(keyValuePair.Key, out directory) || string.IsNullOrEmpty(directory))
continue;
checkDirectory = Path.Combine(directory, $"{keyValuePair.Key}");
if (!Directory.Exists(checkDirectory))
continue;
checkFile = Path.Combine(checkDirectory, ".json");
if (!File.Exists(checkFile))
continue;
json = File.ReadAllText(checkFile);
workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem);
if (workItem is null)
continue;
results.Add(workItem);
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(string sourceDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes, ReadOnlyDictionary<int, string> collection)
{
ReadOnlyCollection<WorkItem> results;
if (collection.Count > 0)
results = GetWorkItems(collection);
else else
results = GetWorkItems(sourceDirectory, bugUserStoryWorkItemTypes); {
return results; string json = File.ReadAllText(checkFile);
results = JsonSerializer.Deserialize(json, WorkItemCollectionSourceGenerationContext.Default.WorkItemArray);
}
return results is null ? null : new(results);
} }
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args) internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{ {
// string url = args[5]; // string url = args[5];
bool keepRelations = true; bool keepRelations = true;
string fileName = args[2];
string sourceDirectory = Path.GetFullPath(args[0]); string sourceDirectory = Path.GetFullPath(args[0]);
ReadOnlyCollection<string> bugUserStoryWorkItemTypes = args[2].Split('|').ToArray().AsReadOnly(); string sourceDirectoryName = Path.GetFileName(sourceDirectory);
// ReadOnlyCollection<string> bugUserStoryTaskWorkItemTypes = new(new string[] { "Bug", "User Story", "Task" }); string idCheck = sourceDirectoryName.Split('-', StringSplitOptions.None)[0];
ReadOnlyDictionary<int, string> collection = GetCurrentDirectories(sourceDirectory, bugUserStoryWorkItemTypes); if (!int.TryParse(idCheck, out int id))
ReadOnlyCollection<WorkItem> workItems = GetWorkItems(sourceDirectory, bugUserStoryWorkItemTypes, collection); logger.LogInformation("Not valid directory!");
ReadOnlyDictionary<int, Record> keyValuePairs = GetWorkItems(workItems, keepRelations); else
logger.LogInformation("Found {keyValuePairs}", keyValuePairs.Count); {
ReadOnlyCollection<WorkItem>? workItems = GetWorkItems(fileName, sourceDirectory);
if (workItems is null)
logger.LogInformation("No file found!");
else
{
Record? record;
ReadOnlyDictionary<int, Record> keyValuePairs = GetWorkItems(workItems, keepRelations);
logger.LogInformation("Found {keyValuePairs}", keyValuePairs.Count);
if (!keyValuePairs.TryGetValue(id, out record))
logger.LogInformation($"Id {id} not found!");
else
{
logger.LogInformation($"Id {id} found with title {record.WorkItem.Title}!");
}
}
}
} }
#else
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{
logger.LogError("WriteMarkdown is not available in WorkItems {args[0]}", args[0]);
logger.LogError("WriteMarkdown is not available in WorkItems {args[1]}", args[1]);
}
#endif
} }

View File

@ -0,0 +1,62 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241115
{
#if BIORAD
internal static void ScanPast(string text, int[] i, string search)
{
int num = text.IndexOf(search, i[0]);
if (num > -1)
i[0] = num + search.Length;
else
i[0] = text.Length;
}
internal static void GetComplete(ILogger<Worker> logger, List<string> args)
{
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly);
if (files.Length != 1)
logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern);
else
{
List<string> group = [];
string text = File.ReadAllText(files[0]);
int[] i = [0];
ScanPast(text, i, "Recipe ID:");
ScanPast(text, i, "*");
#pragma warning disable IDE0057
string[] segments = text.Substring(i[0]).Split('*');
string[] segmentsB;
string[] segmentsC;
foreach (string segment in segments)
{
segmentsB = segment.Split(Environment.NewLine);
segmentsC = segmentsB[0].Split(' ');
if (segment.Contains("Group"))
{
}
}
}
}
#else
internal static void GetComplete(ILogger<Worker> logger, List<string> args)
{
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly);
if (files.Length != 1)
logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern);
logger.LogError("GetComplete is not available in BioRad {args[1]}", args[1]);
}
#endif
}

View File

@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
using System.Text;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241204
{
internal static void ConvertToUTF8(ILogger<Worker> logger, List<string> args)
{
string text;
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly);
if (files.Length == 0)
logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern);
foreach (string file in files)
{
text = File.ReadAllText(file);
File.WriteAllText(file, text, Encoding.UTF8);
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241212
{
internal static void Rename(ILogger<Worker> logger, List<string> args)
{
string newFile;
string fileName;
string newFileName;
string directoryName;
string searchPattern = args[2];
string[] searchPatterns = args[3].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern);
foreach (string file in files)
{
fileName = Path.GetFileName(file);
directoryName = Path.GetDirectoryName(file) ?? throw new Exception();
newFileName = fileName;
foreach (string pattern in searchPatterns)
newFileName = newFileName.Replace(pattern, "");
newFile = Path.Combine(directoryName, newFileName);
if (File.Exists(newFile))
logger.LogError("File {newFile} already exists", newFile);
else
File.Move(file, newFile);
}
}
}

View File

@ -0,0 +1,362 @@
using DiscUtils.Iso9660;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241217
{
private record SecureShell(
);
private record ServerMessageBlock(string Path,
bool Required);
private record Target(SecureShell? SecureShell,
ServerMessageBlock? ServerMessageBlock);
private record File(long LastWriteTicks,
long Length,
string RelativePath);
private record Record(string DestinationDirectory,
string DirectoryName,
Job Job,
string Path,
string Snap2HyperTextMarkupLanguage,
string SourceDirectory);
private record Job(string? AlternatePath,
string Directory,
string Extension,
File[] Files,
int FilesCount,
double FilesTotalLength,
int Keep,
Target[] Targets);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Job))]
private partial class JobSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(File[]))]
private partial class FilesSourceGenerationContext : JsonSerializerContext
{
}
private static ReadOnlyCollection<File> GetFiles(string searchPattern, string[] ignoreFileNames, Record record) =>
GetFiles(record.SourceDirectory, searchPattern, ignoreFileNames);
internal static void Backup(ILogger<Worker> logger, List<string> args)
{
Job jobNew;
string path;
string? json;
string fileName;
bool areTheyTheSame;
IEnumerable<Record> records;
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
ReadOnlyCollection<File> files;
string searchPattern = args[2];
IEnumerable<string> searchPatternFiles;
string[] ignoreFileNames = args[3].Split('~');
string destination = Path.GetFullPath(args[4]);
string sourceDirectory = Path.GetFullPath(args[0]);
string? snap2HyperTextMarkupLanguage = args.Count < 6 ? null : Path.GetFullPath(args[5]);
logger.LogInformation("Searching <{sourceDirectory}> with search pattern {searchPattern}", args[0], searchPattern);
if (Debugger.IsAttached)
Verify(searchPattern, ignoreFileNames);
for (int i = 1; i < 3; i++)
{
if (i == 1)
{
searchPatternFiles = Directory.EnumerateFiles(sourceDirectory, searchPattern, new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
}
else if (i == 2)
{
searchPatternFiles = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
}
else
throw new NotImplementedException();
records = GetRecords(sourceDirectory, destination, searchPatternFiles);
foreach (Record record in records)
{
if (record.Job is null || string.IsNullOrEmpty(record.Job.Extension))
continue;
logger.LogInformation("Searching <{directory}>", record.SourceDirectory);
if (snap2HyperTextMarkupLanguage is not null && System.IO.File.Exists(snap2HyperTextMarkupLanguage))
WriteSnap2HyperTextMarkupLanguage(logger, snap2HyperTextMarkupLanguage, record);
files = GetFiles(searchPattern, ignoreFileNames, record);
jobNew = GetJob(searchPattern, ignoreFileNames, record, files);
json = JsonSerializer.Serialize(jobNew, JobSourceGenerationContext.Default.Job);
areTheyTheSame = GetAreTheyTheSame(logger, searchPattern, ignoreFileNames, record, jobNew);
if (areTheyTheSame)
{
WriteAllText(record.Path, json);
continue;
}
fileName = $"{record.DirectoryName}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}{record.Job.Extension}";
path = Path.Combine(record.DestinationDirectory, fileName);
logger.LogInformation("Writing <{path}> extension", path);
WritePassedExtension(record, files, record.DirectoryName, path);
logger.LogInformation("Wrote <{path}> extension", path);
logger.LogInformation("Moved <{path}> extension", path);
WriteAllText(record, json, path);
}
}
}
private static void Verify(string searchPattern, string[] ignoreFileNames)
{
List<Target> targets = [
new(new SecureShell(), null),
new(null, new ServerMessageBlock("\\\\mesfs.infineon.com\\EC_APC\\DEV", true))
];
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
string path = Path.Combine(directory, "verify.json");
ReadOnlyCollection<File> files = GetFiles(directory, searchPattern, ignoreFileNames);
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, files);
double filesTotalLength = collection.Select(l => l.Length).Sum();
Job job = new(AlternatePath: "C:/Users/phares",
Directory: directory,
Extension: ".iso",
Files: collection.ToArray(),
FilesCount: files.Count,
FilesTotalLength: filesTotalLength,
Keep: 3,
Targets: targets.ToArray());
string json = JsonSerializer.Serialize(job, JobSourceGenerationContext.Default.Job);
WriteAllText(path, json);
}
private static ReadOnlyCollection<File> GetFilteredFiles(string searchPattern, string[] ignoreFileNames, ReadOnlyCollection<File> files)
{
List<File> results = [];
string fileName;
foreach (File file in files)
{
if (file.RelativePath == searchPattern)
continue;
fileName = Path.GetFileName(file.RelativePath);
if (fileName == searchPattern)
throw new Exception("Found nested file!");
if (ignoreFileNames.Any(l => l == fileName))
continue;
if (file.Length == 0)
continue;
results.Add(file);
}
return results.AsReadOnly();
}
private static IEnumerable<Record> GetRecords(string directory, string destination, IEnumerable<string> files)
{
Job? job;
string json;
Record record;
string fileName;
string directoryName;
string sourceDirectory;
string destinationDirectory;
string snap2HyperTextMarkupLanguage;
foreach (string file in files)
{
fileName = Path.GetFileName(file);
sourceDirectory = Path.GetDirectoryName(file) ?? throw new Exception();
directoryName = Path.GetFileName(sourceDirectory);
if (!fileName.StartsWith('.'))
{
System.IO.File.Move(file, Path.Combine(sourceDirectory, $".{fileName}"));
continue;
}
json = System.IO.File.ReadAllText(file);
snap2HyperTextMarkupLanguage = Path.Combine(sourceDirectory, ".html");
if (string.IsNullOrEmpty(json) || json is "{}" or "[]")
job = null;
else
job = JsonSerializer.Deserialize(json, JobSourceGenerationContext.Default.Job);
job ??= new(AlternatePath: null,
Directory: directory,
Extension: ".iso",
Files: [],
FilesCount: 0,
FilesTotalLength: 0,
Keep: 3,
Targets: []);
destinationDirectory = $"{destination}{sourceDirectory[2..]}";
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
record = new(DestinationDirectory: destinationDirectory,
DirectoryName: directoryName,
Job: job,
Path: file,
Snap2HyperTextMarkupLanguage: snap2HyperTextMarkupLanguage,
SourceDirectory: sourceDirectory);
yield return record;
}
}
private static void WriteSnap2HyperTextMarkupLanguage(ILogger<Worker> logger, string snap2HyperTextMarkupLanguage, Record record)
{
string title = Path.GetFileName(record.SourceDirectory);
ProcessStartInfo processStartInfo = new()
{
Arguments = $"-path:\"{record.SourceDirectory}\" -outfile:\"{record.Snap2HyperTextMarkupLanguage}\" -title:\"{title}\" -hidden -silent",
FileName = snap2HyperTextMarkupLanguage,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WorkingDirectory = record.SourceDirectory
};
Process? process = Process.Start(processStartInfo) ?? throw new Exception("Process should not be null.");
process.WaitForExit();
logger.LogInformation($"Snap2HyperTextMarkupLanguage: {process.StandardOutput.ReadToEnd()}{Environment.NewLine}{process.StandardError.ReadToEnd()}{Environment.NewLine}{process.ExitCode}");
}
private static ReadOnlyCollection<File> GetFiles(string directory, string searchPattern, string[] ignoreFileNames)
{
List<File> results = [];
File file;
string relativePath;
string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
FileInfo[] fileInfoCollection = files.Select(l => new FileInfo(l)).ToArray();
foreach (FileInfo fileInfo in fileInfoCollection)
{
if (fileInfo.Name == searchPattern)
continue;
if (ignoreFileNames.Any(l => l == fileInfo.Name))
continue;
if (!string.IsNullOrEmpty(fileInfo.LinkTarget))
continue;
relativePath = Path.GetRelativePath(directory, fileInfo.FullName).Replace(';', '_');
if (relativePath.StartsWith(".."))
relativePath = relativePath[3..];
file = new(LastWriteTicks: fileInfo.LastWriteTime.Ticks, Length: fileInfo.Length, RelativePath: relativePath);
results.Add(file);
}
return results.AsReadOnly();
}
private static Job GetJob(string searchPattern, string[] ignoreFileNames, Record record, ReadOnlyCollection<File> files)
{
Job result;
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, files);
double filesTotalLengthNew = collection.Select(l => l.Length).Sum();
result = new(AlternatePath: record.Job.AlternatePath,
Directory: record.SourceDirectory,
Extension: record.Job.Extension,
Files: collection.ToArray(),
FilesCount: collection.Count,
FilesTotalLength: filesTotalLengthNew,
Keep: record.Job.Keep,
Targets: record.Job.Targets);
return result;
}
private static bool GetAreTheyTheSame(ILogger<Worker> logger, string searchPattern, string[] ignoreFileNames, Record record, Job jobNew)
{
bool result;
ReadOnlyCollection<File> collection = GetFilteredFiles(searchPattern, ignoreFileNames, record.Job.Files.AsReadOnly());
int filesCountOld = collection.Count;
int filesCountNew = jobNew.Files.Length;
if (filesCountNew != filesCountOld)
{
result = false;
logger.LogInformation("<{directory}> file count has changed {filesCountNew} != {filesCountOld}", record.SourceDirectory, filesCountNew, filesCountOld);
}
else
{
double filesTotalLengthOld = collection.Select(l => l.Length).Sum();
double filesTotalLengthNew = jobNew.Files.Select(l => l.Length).Sum();
if (filesTotalLengthNew != filesTotalLengthOld)
{
result = false;
logger.LogInformation("<{directory}> file length has changed {filesTotalLengthNew} != {filesTotalLengthOld}", record.SourceDirectory, filesTotalLengthNew, filesTotalLengthOld);
}
else
{
string jsonNew = JsonSerializer.Serialize(jobNew.Files, FilesSourceGenerationContext.Default.FileArray);
string jsonOld = JsonSerializer.Serialize(collection.ToArray(), FilesSourceGenerationContext.Default.FileArray);
if (jsonNew == jsonOld)
result = true;
else
{
result = false;
if (Debugger.IsAttached)
{
WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "old.json"), jsonOld);
WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "new.json"), jsonNew);
}
logger.LogInformation("<{directory}> file serialized are different {filesTotalLengthNew} != {filesTotalLengthOld}", record.SourceDirectory, filesTotalLengthNew, filesTotalLengthOld);
}
}
}
return result;
}
private static void WriteAllText(string path, string text)
{
string check = !System.IO.File.Exists(path) ? string.Empty : System.IO.File.ReadAllText(path);
if (check != text)
System.IO.File.WriteAllText(path, text);
}
private static void WritePassedExtension(Record record, ReadOnlyCollection<File> files, string directoryName, string path)
{
if (record.Job.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase))
WriteISO(record, files, path, directoryName);
else if (record.Job.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase))
WriteZIP(record, files, path);
else
throw new NotImplementedException();
}
private static void WriteISO(Record record, ReadOnlyCollection<File> files, string path, string directoryName)
{
CDBuilder builder = new() { UseJoliet = true, VolumeIdentifier = directoryName.Length < 25 ? directoryName : directoryName[..25] };
foreach (File file in files)
_ = builder.AddFile(file.RelativePath, Path.Combine(record.SourceDirectory, file.RelativePath));
builder.Build(path);
}
private static void WriteZIP(Record record, ReadOnlyCollection<File> files, string path)
{
using ZipArchive zip = ZipFile.Open(path, ZipArchiveMode.Create);
string directoryEntry;
List<string> directoryEntries = [];
foreach (File file in files)
{
directoryEntry = Path.GetDirectoryName(file.RelativePath) ?? throw new Exception();
if (!directoryEntries.Contains(directoryEntry))
continue;
directoryEntries.Add(directoryEntry);
_ = zip.CreateEntry(file.RelativePath);
}
foreach (File file in files)
_ = zip.CreateEntryFromFile(Path.Combine(record.SourceDirectory, file.RelativePath), file.RelativePath);
}
private static void WriteAllText(Record record, string text, string path)
{
WriteAllText(record.Path, text);
System.IO.File.Copy(record.Path, $"{path}.json");
string checkFile = Path.Combine(record.SourceDirectory, ".html");
if (System.IO.File.Exists(checkFile))
System.IO.File.Copy(checkFile, $"{path}.html");
}
}

View File

@ -0,0 +1,200 @@
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
#if ShellProgressBar
using ShellProgressBar;
#endif
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.Json;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241224
{
private static readonly HttpClient _HttpClient = new();
private record Record(Uri URI, string Path, DateTime LastModified);
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(string host, string page)
{
List<NginxFileSystem>? results;
Uri uri = new($"https://{host}/{page}");
string format = NginxFileSystem.GetFormat();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
taskHttpResponseMessage.Wait();
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
results = null;
else
{
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (nginxFileSystems is null)
results = null;
else
{
results = [];
NginxFileSystem nginxFileSystem;
ReadOnlyCollection<NginxFileSystem>? directory;
for (int i = 0; i < nginxFileSystems.Length; i++)
{
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
if (nginxFileSystem.Type == "file")
results.Add(nginxFileSystem);
else
{
directory = GetRecursiveCollection(host, $"{page}/{nginxFileSystem.Name}");
if (directory is null)
continue;
results.AddRange(directory);
}
}
}
}
return results?.AsReadOnly();
}
private static ReadOnlyCollection<NginxFileSystem>? GetCollection(string format, TimeZoneInfo timeZoneInfo, Uri uri)
{
List<NginxFileSystem>? results;
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
taskHttpResponseMessage.Wait();
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
results = null;
else
{
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
if (taskString.Result.StartsWith('<'))
results = null;
else
{
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (nginxFileSystems is null)
results = null;
else
{
results = [];
NginxFileSystem nginxFileSystem;
for (int i = 0; i < nginxFileSystems.Length; i++)
{
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
results.Add(nginxFileSystem);
}
}
}
}
return results?.AsReadOnly();
}
private static Record? CompareFile(string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
{
Record? result;
if (nginxFileSystem.LastModified is null || nginxFileSystem.Length is null)
result = null;
else
{
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}/{nginxFileSystem.Name}");
FileInfo fileInfo = new($"{compareDirectory}\\{string.Join('\\', directoryNames)}\\{nginxFileSystem.Name}");
if (!fileInfo.Exists || fileInfo.Length != nginxFileSystem.Length.Value)
result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
else
{
double totalSeconds = new TimeSpan(fileInfo.LastWriteTime.Ticks - nginxFileSystem.LastModified.Value.Ticks).TotalSeconds;
if (totalSeconds is < 2 and > -2)
result = null;
else
result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
}
}
return result;
}
private static ReadOnlyCollection<Record> CompareDirectory(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
{
ReadOnlyCollection<Record> results;
List<string> collection = directoryNames.ToList();
collection.Add(nginxFileSystem.Name);
results = GetRecord(format, timeZoneInfo, host, collection.AsReadOnly(), compareDirectory);
return results;
}
private static ReadOnlyCollection<Record> GetRecord(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory)
{
List<Record> results = [];
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}");
ReadOnlyCollection<NginxFileSystem>? nginxFileSystems = GetCollection(format, timeZoneInfo, uri);
if (nginxFileSystems is not null)
{
NginxFileSystem nginxFileSystem;
ReadOnlyCollection<Record> records;
string checkDirectory = $"{compareDirectory}\\{string.Join('\\', directoryNames)}";
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
for (int i = 0; i < nginxFileSystems.Count; i++)
{
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
if (nginxFileSystem.Type == "file")
{
Record? record = CompareFile(host, directoryNames, compareDirectory, nginxFileSystem);
if (record is not null)
results.Add(record);
}
else
{
records = CompareDirectory(format, timeZoneInfo, host, directoryNames, compareDirectory, nginxFileSystem);
foreach (Record record in records)
results.Add(record);
}
}
}
return results.AsReadOnly();
}
private static void Download(Record record)
{
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(record.URI);
taskHttpResponseMessage.Wait();
if (taskHttpResponseMessage.Result.IsSuccessStatusCode)
{
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
File.WriteAllText(record.Path, taskString.Result);
File.SetLastWriteTime(record.Path, record.LastModified);
}
}
internal static void Compare(ILogger<Worker> logger, List<string> args)
{
string host = args[2];
string rootDirectoryName = args[3];
string format = NginxFileSystem.GetFormat();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
string compareDirectory = Path.GetFullPath(args[0]);
logger.LogInformation("Comparing files on {host}", host);
ReadOnlyCollection<Record> records = GetRecord(format, timeZoneInfo, host, new([rootDirectoryName]), compareDirectory);
#if ShellProgressBar
ProgressBar progressBar = new(records.Count, "Downloading", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
foreach (Record record in records)
{
#if ShellProgressBar
progressBar.Tick();
#endif
Download(record);
}
#if ShellProgressBar
progressBar.Dispose();
#endif
if (Debugger.IsAttached)
{
ReadOnlyCollection<NginxFileSystem>? recursiveCollection = GetRecursiveCollection(host, rootDirectoryName);
string? json = recursiveCollection is null ? null : JsonSerializer.Serialize(recursiveCollection.ToArray(), NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (!string.IsNullOrEmpty(json))
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json);
}
}
}

View File

@ -0,0 +1,61 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.ADO2025.PI4;
internal static partial class Helper20250101
{
private static ReadOnlyDictionary<string, List<FileInfo>> GetKeyValuePairs(string directory, string searchPattern, string split)
{
string key;
List<FileInfo>? collection;
Dictionary<string, List<FileInfo>> results = [];
string[] files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
FileInfo[] fileInfoCollection = files.Select(l => new FileInfo(l)).ToArray();
foreach (FileInfo fileInfo in fileInfoCollection.OrderBy(l => l.LastWriteTime))
{
key = fileInfo.Name.Split(split)[0];
if (!results.TryGetValue(key, out collection))
{
results.Add(key, []);
if (!results.TryGetValue(key, out collection))
throw new Exception();
}
collection.Add(fileInfo);
}
return results.AsReadOnly();
}
private static void MoveToDelete(ILogger<Worker> logger, string appendage, ReadOnlyDictionary<string, List<FileInfo>> keyValuePairs)
{
string checkFile;
FileInfo fileInfo;
foreach (KeyValuePair<string, List<FileInfo>> keyValuePair in keyValuePairs)
{
if (keyValuePair.Value.Count < 3)
continue;
for (int i = 1; i < keyValuePair.Value.Count - 1; i++)
{
fileInfo = keyValuePair.Value[i];
checkFile = Path.Combine($"{fileInfo.Directory}{appendage}", fileInfo.Name);
if (File.Exists(checkFile))
continue;
logger.LogInformation("Moving <{fileInfo.FullName}> to <{checkFile}>", fileInfo.FullName, checkFile);
File.Move(fileInfo.FullName, checkFile);
}
}
}
internal static void MoveToDelete(ILogger<Worker> logger, List<string> args)
{
string split = args[3];
string appendage = args[4];
string searchPattern = args[2];
string compareDirectory = Path.GetFullPath(args[0]);
ReadOnlyDictionary<string, List<FileInfo>> keyValuePairs = GetKeyValuePairs(compareDirectory, searchPattern, split);
logger.LogInformation("KeyValuePairs: {keyValuePairs}", keyValuePairs.Count);
MoveToDelete(logger, appendage, keyValuePairs);
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI4;
internal static partial class Helper20250114
{
private static void Rename(string[] directories, string dateFormat)
{
string[] files;
DateTime dateTime;
FileInfo fileInfo;
string checkDirectory;
foreach (string directory in directories)
{
dateTime = DateTime.MinValue;
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
fileInfo = new(file);
if (dateTime > fileInfo.LastWriteTime)
continue;
dateTime = fileInfo.LastWriteTime;
}
if (dateTime == DateTime.MinValue)
continue;
checkDirectory = Path.Combine(Path.GetDirectoryName(directory) ?? throw new Exception(), dateTime.ToString(dateFormat));
if (checkDirectory != directory)
{
if (Directory.Exists(checkDirectory))
continue;
Directory.Move(directory, checkDirectory);
}
Directory.SetLastWriteTime(checkDirectory, dateTime);
}
}
private static void Rename(ILogger<Worker> logger, string sourceDirectory, string searchPattern, string dateFormat)
{
string[] directories = Directory.GetDirectories(sourceDirectory, searchPattern);
logger.LogInformation("directories: {directories}", directories.Length);
Rename(directories, dateFormat);
}
internal static void Rename(ILogger<Worker> logger, List<string> args)
{
string dateFormat = args[3];
string[] searchPatterns = args[2].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
foreach (string searchPattern in searchPatterns)
Rename(logger, sourceDirectory, searchPattern, dateFormat);
}
}

View File

@ -0,0 +1,93 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
namespace File_Folder_Helper.ADO2025.PI4;
internal static partial class Helper20250126
{
private static void Move(string file, string fileName, string checkFile, List<string> foundLines, ReadOnlyCollection<DateTime> dateTimes)
{
string checkDirectory = Path.Combine(Path.GetDirectoryName(file) ?? throw new Exception(), dateTimes[0].ToString("yyyy-MM"));
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
string fileNameB = Path.GetFileName(checkFile);
string checkFileB = Path.Combine(checkDirectory, fileName);
string checkFileC = Path.Combine(checkDirectory, fileNameB);
string contents = string.Join(Environment.NewLine, foundLines);
string checkFileD = Path.Combine(checkDirectory, $"{fileName}.txt");
if (!File.Exists(checkFileB))
File.Move(file, checkFileB);
if (!File.Exists(checkFileC))
File.Move(checkFile, checkFileC);
File.WriteAllText(checkFileD, contents);
}
private static void Move(ILogger<Worker> logger, string dateFormat, string file, string checkFile, string fileName, ReadOnlyCollection<string> statementPeriodSegments, List<string> foundLines)
{
DateTime dateTime;
List<DateTime> dateTimes = [];
foreach (string check in statementPeriodSegments)
{
if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
dateTimes.Add(dateTime);
}
if (dateTimes.Count != 2)
logger.LogInformation($"Only {dateTimes.Count} date(s) were found in <{fileName}>!");
else
Move(file, fileName, checkFile, foundLines, dateTimes.AsReadOnly());
}
private static void Move(ILogger<Worker> logger, string file, string checkFile, string dateFormat, string statementPeriod, string search)
{
List<string> foundLines = [];
bool statementPeriodFound = false;
string[]? statementPeriodSegments = null;
string fileName = Path.GetFileName(file);
string[] lines = File.ReadAllLines(file);
foreach (string line in lines)
{
if (statementPeriodSegments is not null)
{
if (line.Contains(search))
foundLines.Add(line);
}
else
{
if (statementPeriodFound)
{
statementPeriodSegments = line.Split(' ');
continue;
}
if (!line.Contains(statementPeriod))
continue;
statementPeriodFound = true;
}
}
if (statementPeriodSegments is null || statementPeriodSegments.Length < 4)
logger.LogInformation($"{nameof(statementPeriod)}: {statementPeriod}; wasn't found in <{fileName}>!");
else
Move(logger, dateFormat, file, checkFile, fileName, statementPeriodSegments.AsReadOnly(), foundLines);
}
internal static void Move(ILogger<Worker> logger, List<string> args)
{
string checkFile;
string search = args[5];
string dateFormat = args[3];
string searchPatterns = args[2];
string statementPeriod = args[4];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPatterns, SearchOption.AllDirectories);
foreach (string file in files)
{
checkFile = Path.ChangeExtension(file, ".pdf");
if (!File.Exists(checkFile))
continue;
Move(logger, file, checkFile, dateFormat, statementPeriod, search);
}
}
}

View File

@ -0,0 +1,311 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.ADO2025.PI4;
internal static partial class Helper20250204
{
[GeneratedRegex("([A-Z]+(.))")]
private static partial Regex UpperCase();
[GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
private static partial Regex InvalidCharacter();
private record H1ParamCaseAndState(string H1, string ParamCase, string State)
{
private static string GetParamCase(string value)
{
string result;
StringBuilder stringBuilder = new(value);
Match[] matches = UpperCase().Matches(value).ToArray();
for (int i = matches.Length - 1; i > -1; i--)
_ = stringBuilder.Insert(matches[i].Index, '-');
string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower());
result = string.Join('-', segments).Trim('-');
return result;
}
private static string GetState(string value) =>
value switch
{
"New" => "ToDo",
"Active" => "In Progress",
"Closed" => "Done",
_ => "Backlog",
};
internal static H1ParamCaseAndState Get(WorkItem workItem)
{
H1ParamCaseAndState result;
string paramCase = GetParamCase(workItem.Title);
string state = GetState(workItem.State);
result = new(workItem.Title, paramCase, state);
return result;
}
}
private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("parameterTitle")] string? ParameterTitle,
[property: JsonPropertyName("state")] string? State,
[property: JsonPropertyName("workItemType")] string? WorkItemType);
private record Relation([property: JsonPropertyName("attributes")] Attribute Attributes,
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("rel")] string Rel);
private record WorkItem(DateTime? ActivatedDate,
string AreaPath,
string? AssignedTo,
long? BusinessValue,
DateTime ChangedDate,
DateTime? ClosedDate,
int CommentCount,
DateTime CreatedDate,
string Description,
long? Effort,
int Id,
string IterationPath,
int? Parent,
int? Priority,
Relation[]? Relations,
long? RemainingWork,
string? Requester,
DateTime? ResolvedDate,
int Revision,
long? RiskReductionMinusOpportunityEnablement,
DateTime? StartDate,
string State,
long? StoryPoints,
string Tags,
DateTime? TargetDate,
long? TimeCriticality,
string Title,
string? Violation,
long? WeightedShortestJobFirst,
string WorkItemType)
{
public override string ToString() => $"{Id} - {WorkItemType} - {Title}";
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WorkItem))]
private partial class WorkItemSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WorkItem[]))]
private partial class WorkItemCollectionSourceGenerationContext : JsonSerializerContext
{
}
private static string[] GetTaskLines(string directory, string rootDirectory) =>
[
"{",
"\"version\": \"2.0.0\",",
"\"tasks\": [",
"{",
"\"label\": \"File-Folder-Helper AOT s X Day-Helper-2025-02-04\",",
"\"type\": \"shell\",",
"\"command\": \"L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe\",",
"\"args\": [",
"\"s\",",
"\"X\",",
$"\"{directory}\",",
"\"Day-Helper-2025-02-04\",",
$"\"{rootDirectory}\",",
"],",
"\"problemMatcher\": []",
"},",
"{",
"\"label\": \"File-Folder-Helper AOT s X Day-Helper-2024-06-23\",",
"\"type\": \"shell\",",
"\"command\": \"L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe\",",
"\"args\": [",
"\"s\",",
"\"X\",",
$"\"{directory}\",",
"\"Day-Helper-2024-06-23\",",
"\"*.md\",",
"\"##_Sub-tasks\",",
"\"-_[code-insiders](\",",
"\"index.md\",",
"\"-_[,](\",",
"\"##_Done\",",
"\".kan\",",
$"\"{rootDirectory}\",",
"\"316940400000\",",
"],",
"\"problemMatcher\": []",
"}",
"]",
"}",
];
private static string GetTaskText(string directory, string rootDirectory) =>
string.Join(Environment.NewLine, GetTaskLines(directory, rootDirectory));
private static void WriteTaskFile(string sourceDirectory, string rootDirectory)
{
string tasksFile = Path.Combine(sourceDirectory, ".vscode", "tasks.json");
string oldText = File.ReadAllText(tasksFile);
string jsonSafeDirectory = sourceDirectory.Replace('\\', '/');
if (!oldText.Contains(jsonSafeDirectory))
{
string text = GetTaskText(jsonSafeDirectory, rootDirectory);
File.WriteAllText(tasksFile, text);
}
}
private static string GetFilter(ReadOnlyCollection<H1ParamCaseAndState> collection, string filter) =>
string.Join(Environment.NewLine, from l in collection where l.State == filter select $"- [{l.ParamCase}](tasks/{l.ParamCase}.md)");
private static string[] GetIndexLines(WorkItem workItem, H1ParamCaseAndState h1ParamCaseAndState, ReadOnlyCollection<H1ParamCaseAndState> collection) =>
[
"---",
"startedColumns:",
" - 'In Progress'",
"completedColumns:",
" - Done",
"---",
string.Empty,
$"# {workItem.Id} - {h1ParamCaseAndState.H1}",
string.Empty,
"## Backlog",
string.Empty,
GetFilter(collection, "Backlog"),
string.Empty,
"## Todo",
string.Empty,
GetFilter(collection, "ToDo"),
string.Empty,
"## In Progress",
string.Empty,
GetFilter(collection, "In Progress"),
string.Empty,
"## Done",
string.Empty,
GetFilter(collection, "Done"),
string.Empty
];
private static string GetIndexText(WorkItem workItem, H1ParamCaseAndState h1ParamCaseAndState, ReadOnlyCollection<H1ParamCaseAndState> collection) =>
string.Join(Environment.NewLine, GetIndexLines(workItem, h1ParamCaseAndState, collection));
private static string GetIndexMarkdown(FileInfo fileInfo, ReadOnlyCollection<WorkItem> workItems)
{
string result;
H1ParamCaseAndState h1ParamCaseAndState;
List<H1ParamCaseAndState> collection = [];
foreach (WorkItem w in workItems)
{
h1ParamCaseAndState = H1ParamCaseAndState.Get(w);
collection.Add(h1ParamCaseAndState);
}
string line = Environment.NewLine;
string json = File.ReadAllText(fileInfo.FullName);
WorkItem? workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem) ??
throw new NullReferenceException(nameof(WorkItem));
h1ParamCaseAndState = H1ParamCaseAndState.Get(workItem);
string text = GetIndexText(workItem, h1ParamCaseAndState, collection.AsReadOnly());
result = text.Replace($"{line}{line}{line}{line}", $"{line}{line}").Replace("408m](tasks", "408M](tasks");
return result;
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(string[] files)
{
List<WorkItem> results = [];
string json;
WorkItem? workItem;
foreach (string file in files)
{
json = File.ReadAllText(file);
workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem);
if (workItem is null)
continue;
results.Add(workItem);
}
return results.AsReadOnly();
}
private static void ExtractKanban(string searchPattern, string rootDirectory, DirectoryInfo kanbanDirectory, FileInfo fileInfo)
{
string checkFile;
string weekOfYear;
string workItemDirectory;
string line = Environment.NewLine;
H1ParamCaseAndState h1ParamCaseAndState;
Calendar calendar = new CultureInfo("en-US").Calendar;
string tasksDirectory = Path.Combine(kanbanDirectory.FullName, "tasks");
if (!Directory.Exists(tasksDirectory))
_ = Directory.CreateDirectory(tasksDirectory);
string[] files = Directory.GetFiles(tasksDirectory, searchPattern, SearchOption.TopDirectoryOnly);
ReadOnlyCollection<WorkItem> workItems = GetWorkItems(files);
string markdown = GetIndexMarkdown(fileInfo, workItems);
string indexFile = Path.Combine(kanbanDirectory.FullName, "index.md");
string markdownOld = File.Exists(indexFile) ? File.ReadAllText(indexFile) : string.Empty;
if (markdown != markdownOld)
File.WriteAllText(indexFile, markdown);
foreach (WorkItem workItem in workItems)
{
h1ParamCaseAndState = H1ParamCaseAndState.Get(workItem);
checkFile = Path.Combine(tasksDirectory, $"{h1ParamCaseAndState.ParamCase}.md");
markdownOld = File.Exists(checkFile) ? File.ReadAllText(checkFile) : string.Empty;
if (markdownOld.Contains("]("))
continue;
weekOfYear = calendar.GetWeekOfYear(workItem.CreatedDate, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
workItemDirectory = Path.GetFullPath(Path.Combine(rootDirectory, $"{workItem.CreatedDate:yyyy}", $"{workItem.CreatedDate:yyyy}_Week_{weekOfYear}", $"{workItem.Id}"));
markdown = $"# {h1ParamCaseAndState.H1}{line}{line}## Id {workItem.Id}{line}{line}## Code Insiders{line}{line}- [code-insiders]({workItemDirectory}){line}";
if (markdown != markdownOld)
File.WriteAllText(checkFile, markdown);
}
}
private static string GetSourceDirectory(string directory)
{
string? result = null;
DirectoryInfo directoryInfo;
string? checkDirectory = directory;
string? pathRoot = Path.GetPathRoot(directory);
for (int i = 0; i < int.MaxValue; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
directoryInfo = new(checkDirectory);
if (string.IsNullOrEmpty(directoryInfo.LinkTarget))
continue;
result = directory.Replace(checkDirectory, directoryInfo.LinkTarget);
break;
}
result ??= directory;
return result;
}
internal static void ExtractKanban(ILogger<Worker> logger, List<string> args)
{
string searchPattern = "*.json";
string fullPath = Path.GetFullPath(args[0]);
string sourceDirectory = GetSourceDirectory(fullPath);
string rootDirectory = args.Count < 3 || args[2].Length < 16 ? "D:/5-Other-Small/Kanban-mestsa003/{}" : args[2];
WriteTaskFile(sourceDirectory, rootDirectory);
string sourceDirectoryName = Path.GetFileName(sourceDirectory);
DirectoryInfo directoryInfo = new(Path.Combine(sourceDirectory, ".kanbn"));
FileInfo? fileInfo = !directoryInfo.Exists ? null : new(Path.Combine(directoryInfo.FullName, $"{sourceDirectoryName}.json"));
if (directoryInfo.Exists && fileInfo is not null && fileInfo.Exists)
ExtractKanban(searchPattern, rootDirectory, directoryInfo, fileInfo);
else
logger.LogWarning("<{directoryInfo}> doesn't exist", directoryInfo.FullName);
}
}

381
ADO2025/PI5/.editorconfig Normal file
View File

@ -0,0 +1,381 @@
[*.md]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.csproj]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.cs]
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
csharp_prefer_braces = false
csharp_prefer_qualified_reference = true:error
csharp_prefer_simple_default_expression = true:warning
csharp_prefer_simple_using_statement = true:warning
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true
csharp_style_conditional_delegate_call = true
csharp_style_deconstructed_variable_declaration = false
csharp_style_expression_bodied_accessors = when_on_single_line:warning
csharp_style_expression_bodied_constructors = when_on_single_line:warning
csharp_style_expression_bodied_indexers = when_on_single_line:warning
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
csharp_style_expression_bodied_local_functions = when_on_single_line:warning
csharp_style_expression_bodied_methods = when_on_single_line:warning
csharp_style_expression_bodied_operators = when_on_single_line:warning
csharp_style_expression_bodied_properties = when_on_single_line:warning
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = false
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = true:warning
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_using_directive_placement = outside_namespace
dotnet_analyzer_diagnostic.category-Design.severity = error
dotnet_analyzer_diagnostic.category-Documentation.severity = error
dotnet_analyzer_diagnostic.category-Globalization.severity = none
dotnet_analyzer_diagnostic.category-Interoperability.severity = error
dotnet_analyzer_diagnostic.category-Maintainability.severity = error
dotnet_analyzer_diagnostic.category-Naming.severity = none
dotnet_analyzer_diagnostic.category-Performance.severity = none
dotnet_analyzer_diagnostic.category-Reliability.severity = error
dotnet_analyzer_diagnostic.category-Security.severity = error
dotnet_analyzer_diagnostic.category-SingleFile.severity = error
dotnet_analyzer_diagnostic.category-Style.severity = error
dotnet_analyzer_diagnostic.category-Usage.severity = error
dotnet_code_quality_unused_parameters = all
dotnet_code_quality_unused_parameters = non_public
dotnet_code_quality.CAXXXX.api_surface = private, internal
dotnet_diagnostic.CA1001.severity = error # CA1001: Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = error # CA1051: Do not declare visible instance fields
dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1513.severity = warning # Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1825.severity = warning # CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1829.severity = error # CA1829: Use Length/Count property instead of Count() when available
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance
dotnet_diagnostic.CA1862.severity = warning # CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'
dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime
dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
dotnet_diagnostic.IDE0005.severity = error # Using directive is unnecessary
dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010)
dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
dotnet_diagnostic.IDE0048.severity = none # Parentheses preferences (IDE0047 and IDE0048)
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049)
dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ]
dotnet_diagnostic.IDE0058.severity = error # IDE0058: Expression value is never used
dotnet_diagnostic.IDE0060.severity = error # IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130)
dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290)
dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified
dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified
dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified
dotnet_diagnostic.IDE2000.severity = error # IDE2000: Allow multiple blank lines
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method
dotnet_naming_rule.class_should_be_pascal_case.severity = warning
dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.class_should_be_pascal_case.symbols = class
dotnet_naming_rule.delegate_should_be_pascal_case.severity = warning
dotnet_naming_rule.delegate_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.delegate_should_be_pascal_case.symbols = delegate
dotnet_naming_rule.enum_should_be_pascal_case.severity = warning
dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
dotnet_naming_rule.event_should_be_pascal_case.severity = warning
dotnet_naming_rule.event_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.event_should_be_pascal_case.symbols = event
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.method_should_be_pascal_case.severity = warning
dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.method_should_be_pascal_case.symbols = method
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.private_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.private_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_method_should_be_pascal_case.symbols = private_method
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.symbols = private_or_internal_static_field
dotnet_naming_rule.property_should_be_pascal_case.severity = warning
dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.property_should_be_pascal_case.symbols = property
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.symbols = public_or_protected_field
dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field
dotnet_naming_rule.static_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_method_should_be_pascal_case.symbols = static_method
dotnet_naming_rule.struct_should_be_pascal_case.severity = warning
dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.private_of_internal_field.capitalization = pascal_case
dotnet_naming_style.private_of_internal_field.required_prefix = _
dotnet_naming_style.private_of_internal_field.required_suffix =
dotnet_naming_style.private_of_internal_field.word_separator =
dotnet_naming_symbols.abstract_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.abstract_method.applicable_kinds = method
dotnet_naming_symbols.abstract_method.required_modifiers = abstract
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.class.applicable_kinds = class
dotnet_naming_symbols.class.required_modifiers =
dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.delegate.applicable_kinds = delegate
dotnet_naming_symbols.delegate.required_modifiers =
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enum.applicable_kinds = enum
dotnet_naming_symbols.enum.required_modifiers =
dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.event.applicable_kinds = event
dotnet_naming_symbols.event.required_modifiers =
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.method.applicable_accessibilities = public
dotnet_naming_symbols.method.applicable_kinds = method
dotnet_naming_symbols.method.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_method.applicable_accessibilities = private
dotnet_naming_symbols.private_method.applicable_kinds = method
dotnet_naming_symbols.private_method.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.property.applicable_kinds = property
dotnet_naming_symbols.property.required_modifiers =
dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected
dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field
dotnet_naming_symbols.public_or_protected_field.required_modifiers =
dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_field.applicable_kinds = field
dotnet_naming_symbols.static_field.required_modifiers = static
dotnet_naming_symbols.static_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_method.applicable_kinds = method
dotnet_naming_symbols.static_method.required_modifiers = static
dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.struct.applicable_kinds = struct
dotnet_naming_symbols.struct.required_modifiers =
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.required_modifiers =
dotnet_remove_unnecessary_suppression_exclusions = 0
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = true
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = false
dotnet_style_prefer_conditional_expression_over_return = false
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_qualification_for_event = false:error
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false:error
dotnet_style_qualification_for_property = false:error
dotnet_style_readonly_field = true:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members
end_of_line = crlf
file_header_template = unset
indent_size = 4
indent_style = space
insert_final_newline = false
root = true
tab_width = 4
# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
# https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
# https://github.com/dotnet/project-system/blob/main/.editorconfig
# Question
csharp_prefer_simple_using_statement = false # Question
csharp_style_expression_bodied_constructors = when_on_single_line:none # Question
csharp_style_expression_bodied_properties = true # Question
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning # Question
csharp_style_pattern_matching_over_as_with_null_check = false # Question
csharp_style_prefer_pattern_matching = false # Question
csharp_style_prefer_range_operator = false # Question
csharp_style_prefer_switch_expression = false # Question
csharp_style_unused_value_assignment_preference = unused_local_variable # Question
csharp_style_unused_value_expression_statement_preference = false # Question
csharp_style_var_elsewhere = false:none # Question
csharp_style_var_for_built_in_types = false:none # Question
csharp_style_var_when_type_is_apparent = false:warning # Question
dotnet_diagnostic.CA1001.severity = none # Question - Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = none # Question - Do not declare visible instance fields
dotnet_diagnostic.CA1416.severity = none # Question - This call site is reachable on all platforms.
dotnet_diagnostic.CA1510.severity = none # Question - Use
dotnet_diagnostic.CA1834.severity = none # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1860.severity = none # Question - Avoid using
dotnet_diagnostic.CA1862.severity = none # Question - Prefer using
dotnet_diagnostic.CA2208.severity = none # Question - Instantiate argument exceptions correctly
dotnet_diagnostic.CA2211.severity = none # Question - Non-constant fields should not be visible
dotnet_diagnostic.CA2249.severity = none # Question - Use
dotnet_diagnostic.CA2253.severity = none # Question - Named placeholders should not be numeric values
dotnet_diagnostic.CS0103.severity = none # Question - The name
dotnet_diagnostic.CS0168.severity = none # Question - The variable
dotnet_diagnostic.CS0219.severity = none # Question - The variable
dotnet_diagnostic.CS0612.severity = none # Question - is obsolete
dotnet_diagnostic.CS0618.severity = none # Question - Compiler Warning (level 2)
dotnet_diagnostic.CS0659.severity = none # Question - Compiler Warning (level 3)
dotnet_diagnostic.CS8019.severity = warning # Question - Unnecessary using directive.
dotnet_diagnostic.CS8600.severity = none # Question - Converting null literal or possible null value to non-nullable type
dotnet_diagnostic.CS8602.severity = none # Question - Dereference of a possibly null reference.
dotnet_diagnostic.CS8603.severity = none # Question - Possible null reference return
dotnet_diagnostic.CS8604.severity = none # Question - Possible null reference argument for parameter.
dotnet_diagnostic.CS8618.severity = none # Question - Non-nullable variable must contain a non-null value when exiting constructor
dotnet_diagnostic.CS8625.severity = none # Question - Cannot convert null literal to non-nullable reference type.
dotnet_diagnostic.CS8629.severity = none # Question - Nullable value type may be null
dotnet_diagnostic.CS8765.severity = none # Question - Nullability of type of parameter
dotnet_diagnostic.IDE0005.severity = none # Question - Remove unnecessary using directives
dotnet_diagnostic.IDE0008.severity = warning # Question - Use explicit type instead of
dotnet_diagnostic.IDE0017.severity = none # Question - Object initialization can be simplified
dotnet_diagnostic.IDE0019.severity = none # Question - Use pattern matching
dotnet_diagnostic.IDE0021.severity = none # Question - Use expression body for constructor
dotnet_diagnostic.IDE0022.severity = none # Question - Use expression body for method
dotnet_diagnostic.IDE0025.severity = none # Question - Use expression body for property
dotnet_diagnostic.IDE0027.severity = none # Question - Use expression body for accessor
dotnet_diagnostic.IDE0028.severity = none # Question - Use collection initializers or expressions
dotnet_diagnostic.IDE0031.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0032.severity = none # Question - Use auto property
dotnet_diagnostic.IDE0037.severity = none # Question - Member name can be simplified
dotnet_diagnostic.IDE0041.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0047.severity = none # Question - Parentheses preferences
dotnet_diagnostic.IDE0049.severity = warning # Question - Name can be simplified
dotnet_diagnostic.IDE0051.severity = none # Question - Remove unused private member
dotnet_diagnostic.IDE0053.severity = none # Question - Use expression body for lambdas
dotnet_diagnostic.IDE0054.severity = none # Question - Use compound assignment
dotnet_diagnostic.IDE0055.severity = none # Question - Formatting rule
dotnet_diagnostic.IDE0057.severity = none # Question - Substring can be simplified
dotnet_diagnostic.IDE0058.severity = none # Question - Remove unnecessary expression value
dotnet_diagnostic.IDE0059.severity = none # Question - Unnecessary assignment of a value to
dotnet_diagnostic.IDE0060.severity = none # Question - Remove unused parameter
dotnet_diagnostic.IDE0063.severity = none # Question - Use simple
dotnet_diagnostic.IDE0065.severity = none # Question -
dotnet_diagnostic.IDE0066.severity = none # Question - Use
dotnet_diagnostic.IDE0078.severity = none # Question - Use pattern matching (may change code meaning)
dotnet_diagnostic.IDE0090.severity = warning # Question - Simplify new expression
dotnet_diagnostic.IDE0100.severity = error # Question - Remove redundant equality
dotnet_diagnostic.IDE0160.severity = warning # Question - Use block-scoped namespace
dotnet_diagnostic.IDE0161.severity = warning # Question - Namespace declaration preferences
dotnet_diagnostic.IDE0270.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0300.severity = none # Question - Collection initialization can be simplified
dotnet_diagnostic.IDE1006.severity = none # Question - Use collection expression for builder dotnet_style_prefer_collection_expression
dotnet_style_null_propagation = false # Question
dotnet_style_object_initializer = false # Question
dotnet_style_prefer_auto_properties = false # Question
dotnet_style_allow_statement_immediately_after_block_experimental = true # Question
dotnet_style_prefer_inferred_anonymous_type_member_names = false:warning # Question
dotnet_style_prefer_is_null_check_over_reference_equality_method = false # Question

View File

@ -0,0 +1,83 @@
using System.Globalization;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250218 {
internal static void MoveToArchive(ILogger<Worker> logger, List<string> args) {
string checkDirectory;
string searchMES = args[4];
string searchPattern = args[3];
string searchSequence = args[5];
string destinationRoot = args[6];
string checkDirectoryName = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories) {
checkDirectory = Path.Combine(directory, checkDirectoryName);
if (!Directory.Exists(checkDirectory))
continue;
MoveToArchive(logger, searchPattern, searchMES, searchSequence, destinationRoot, checkDirectory);
}
}
private static void MoveToArchive(ILogger<Worker> logger, string searchPattern, string searchMES, string searchSequence, string destinationRoot, string checkDirectory) {
string[] files = Directory.GetFiles(checkDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogInformation("<{files}>(s)", files.Length);
else
MoveToArchive(logger, searchMES, searchSequence, destinationRoot, files);
}
private static void MoveToArchive(ILogger<Worker> logger, string searchMES, string searchSequence, string destinationRoot, string[] files) {
string mes;
string text;
string sequence;
string checkFile;
string[] matches;
FileInfo fileInfo;
string weekOfYear;
string[] segments;
string[] segmentsB;
string[] segmentsC;
string checkDirectory;
Calendar calendar = new CultureInfo("en-US").Calendar;
foreach (string file in files) {
fileInfo = new(file);
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
continue;
text = File.ReadAllText(file);
segments = text.Split(searchMES);
if (segments.Length < 2)
continue;
segmentsB = text.Split(searchSequence);
if (segmentsB.Length < 2)
continue;
mes = segments[1].Split(';')[0];
sequence = segmentsB[1].Split(';')[0];
segmentsC = Path.GetFileName(fileInfo.DirectoryName).Split('-');
weekOfYear = $"{fileInfo.LastWriteTime.Year}_Week_{calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
checkDirectory = Path.GetFullPath(Path.Combine(destinationRoot, mes, weekOfYear, fileInfo.LastWriteTime.ToString("yyyy-MM-dd")));
if (!Directory.Exists(checkDirectory)) {
logger.LogInformation("<{checkDirectory}>", checkDirectory);
continue;
}
matches = Directory.GetDirectories(checkDirectory, sequence, SearchOption.AllDirectories);
if (matches.Length != 1) {
logger.LogInformation("!= 1 <{checkDirectory}>", checkDirectory);
continue;
}
checkFile = segmentsC.Length == 2 ? Path.Combine(matches[0], $"csv-{segmentsC[1]}-{fileInfo.Name}") : Path.Combine(matches[0], $"csv-{fileInfo.Name}");
if (File.Exists(checkFile)) {
logger.LogInformation("csv- {segmentsC} <{checkDirectory}>", segmentsC.Length, checkDirectory);
continue;
}
File.Move(fileInfo.FullName, checkFile);
}
}
}
// L:\DevOps\MESA_FI\file-folder-helper\bin\Debug\net8.0\win-x64>dotnet File-Folder-Helper.dll X \\mesfs.infineon.com\EC_EDA\Production\Traces Day-Helper-2025-02-18 Source *.pdsf A_MES_ENTITY= B_SEQUENCE= \\mesfs.infineon.com\EC_Characterization_Si\Archive

View File

@ -0,0 +1,372 @@
using System.Collections.ObjectModel;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250219 {
private record ProcessDataStandardFormat(ReadOnlyCollection<string> Body,
ReadOnlyCollection<string> Columns,
ReadOnlyCollection<string> Logistics,
long? Sequence);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(JsonElement[]))]
private partial class JsonElementCollectionSourceGenerationContext : JsonSerializerContext {
}
private record ProcessDataStandardFormatMapping(ReadOnlyCollection<string> BackfillColumns,
ReadOnlyCollection<int> ColumnIndices,
ReadOnlyCollection<string> IgnoreColumns,
ReadOnlyCollection<string> IndexOnlyColumns,
ReadOnlyDictionary<string, string> KeyValuePairs,
ReadOnlyCollection<string> NewColumnNames,
ReadOnlyCollection<string> OldColumnNames);
internal static void Compare(ILogger<Worker> logger, List<string> args) {
string[] segmentsB;
List<string> distinct = [];
string searchPattern = args[2];
string searchPatternB = args[3];
string[] segments = args[7].Split(',');
Dictionary<string, string> keyValuePairs = [];
ReadOnlyCollection<string> ignore = args[4].Split(',').AsReadOnly();
ReadOnlyCollection<string> backfill = args[5].Split(',').AsReadOnly();
ReadOnlyCollection<string> indexOnly = args[6].Split(',').AsReadOnly();
ReadOnlyCollection<string> oldColumnNames = args[8].Split(',').AsReadOnly();
ReadOnlyCollection<string> newColumnNames = args[9].Split(',').AsReadOnly();
ReadOnlyCollection<int> columnIndices = args[10].Split(',').Select(int.Parse).ToArray().AsReadOnly();
foreach (string segment in segments) {
segmentsB = segment.Split('|');
if (segmentsB.Length != 2)
continue;
if (distinct.Contains(segmentsB[0]))
continue;
distinct.Add(segmentsB[0]);
keyValuePairs.Add(segmentsB[0], segmentsB[1]);
}
ProcessDataStandardFormatMapping processDataStandardFormatMapping = new(BackfillColumns: backfill,
ColumnIndices: columnIndices,
NewColumnNames: newColumnNames,
IgnoreColumns: ignore,
IndexOnlyColumns: indexOnly,
KeyValuePairs: keyValuePairs.AsReadOnly(),
OldColumnNames: oldColumnNames);
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation("<{files}>(s)", files.Length);
Compare(logger, sourceDirectory.Length, searchPatternB, processDataStandardFormatMapping, files);
}
private static void Compare(ILogger<Worker> logger, int sourceDirectoryLength, string searchPattern, ProcessDataStandardFormatMapping pdsfMapping, string[] files) {
bool compare;
string directory;
string[] matches;
string directorySegment;
string[] directoryFiles;
const int columnsLine = 6;
JsonElement[]? jsonElementsNew;
JsonElement[]? jsonElementsOld;
ProcessDataStandardFormat processDataStandardFormat;
FileInfo[] collection = files.Select(l => new FileInfo(l)).ToArray();
foreach (FileInfo fileInfo in collection) {
directory = fileInfo.DirectoryName ?? throw new Exception();
directoryFiles = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
matches = (from l in directoryFiles where l != fileInfo.FullName select l).ToArray();
if (matches.Length < 1)
continue;
directorySegment = directory[sourceDirectoryLength..];
processDataStandardFormat = GetProcessDataStandardFormat(logger, fileInfo.LastWriteTime, pdsfMapping.NewColumnNames.Count, columnsLine, fileInfo.FullName, lines: null);
jsonElementsNew = GetArray(logger, pdsfMapping.NewColumnNames.Count, processDataStandardFormat, lookForNumbers: false);
if (jsonElementsNew is null)
continue;
if (pdsfMapping.OldColumnNames.Count == pdsfMapping.ColumnIndices.Count) {
processDataStandardFormat = GetProcessDataStandardFormat(logger, pdsfMapping, jsonElementsNew, processDataStandardFormat);
Write(logger, fileInfo, processDataStandardFormat);
}
foreach (string match in matches) {
processDataStandardFormat = GetProcessDataStandardFormat(logger, fileInfo.LastWriteTime, pdsfMapping.OldColumnNames.Count, columnsLine, match, lines: null);
jsonElementsOld = GetArray(logger, pdsfMapping.OldColumnNames.Count, processDataStandardFormat, lookForNumbers: false);
if (jsonElementsOld is null || jsonElementsOld.Length != jsonElementsNew.Length) {
logger.LogWarning("! <{match}> (jsonElementsOld.Length:{jsonElementsOld} != jsonElementsNew.Length:{jsonElementsNew})", match, jsonElementsOld?.Length, jsonElementsNew.Length);
continue;
}
compare = Compare(logger, pdsfMapping, directorySegment, jsonElementsNew, jsonElementsOld);
if (!compare) {
logger.LogWarning("! <{match}>", match);
continue;
}
logger.LogInformation("<{match}>", match);
}
}
}
private static bool Compare(ILogger<Worker> logger, ProcessDataStandardFormatMapping processDataStandardFormatMapping, string directory, JsonElement[] jsonElementsNew, JsonElement[] jsonElementsOld) {
bool result;
int? q;
string valueNew;
string valueOld;
List<string> columns = [];
JsonProperty jsonPropertyOld;
JsonProperty jsonPropertyNew;
List<string> columnPairs = [];
JsonProperty[] jsonPropertiesOld;
JsonProperty[] jsonPropertiesNew;
List<string> unknownColumns = [];
List<string> differentColumns = [];
int last = jsonElementsOld.Length - 1;
List<string> sameAfterSpaceSplitColumns = [];
for (int i = last; i > -1; i--) {
if (jsonElementsOld[i].ValueKind != JsonValueKind.Object) {
unknownColumns.Add(string.Empty);
break;
}
jsonPropertiesOld = jsonElementsOld[i].EnumerateObject().ToArray();
jsonPropertiesNew = jsonElementsNew[i].EnumerateObject().ToArray();
for (int p = 0; p < jsonPropertiesOld.Length; p++) {
jsonPropertyOld = jsonPropertiesOld[p];
valueOld = jsonPropertyOld.Value.ToString();
if (processDataStandardFormatMapping.KeyValuePairs.TryGetValue(jsonPropertyOld.Name, out string? name) && !string.IsNullOrEmpty(name)) {
q = TryGetPropertyIndex(jsonPropertiesNew, name);
if (q is null && i == 0)
unknownColumns.Add($"{jsonPropertyOld.Name}|{name}");
} else {
q = TryGetPropertyIndex(jsonPropertiesNew, jsonPropertyOld.Name);
if (q is null) {
if (i == 0)
unknownColumns.Add(jsonPropertyOld.Name);
}
}
if (q is null) {
if (processDataStandardFormatMapping.IgnoreColumns.Contains(jsonPropertyOld.Name)) {
if (i == last) {
columns.Add("-1");
columnPairs.Add($"{jsonPropertyOld.Name}:");
logger.LogDebug("{p} )) {jsonPropertyOld.Name} **", p, jsonPropertyOld.Name);
}
continue;
}
if (i == last) {
columns.Add("-1");
columnPairs.Add($"{jsonPropertyOld.Name}:");
if (!string.IsNullOrEmpty(valueOld))
logger.LogDebug("{p} )) {jsonPropertyOld.Name} ??", p, jsonPropertyOld.Name);
}
} else {
jsonPropertyNew = jsonPropertiesNew[q.Value];
if (i == last) {
columns.Add(q.Value.ToString());
columnPairs.Add($"{jsonPropertyOld.Name}:{jsonPropertyNew.Name}");
}
valueNew = jsonPropertyNew.Value.ToString();
if (i == last)
logger.LogDebug("{p} )) {jsonPropertyOld.Name} ~~ {q.Value} => {jsonPropertyNew.Name}", p, jsonPropertyOld.Name, q.Value, jsonPropertyNew.Name);
if (valueNew != valueOld && !differentColumns.Contains(jsonPropertyOld.Name)) {
if (valueNew.Length >= 2 && valueNew.Split(' ')[0] == valueOld)
sameAfterSpaceSplitColumns.Add(jsonPropertyOld.Name);
else {
if (processDataStandardFormatMapping.BackfillColumns.Contains(jsonPropertyOld.Name) && i != last)
continue;
if (processDataStandardFormatMapping.IndexOnlyColumns.Contains(jsonPropertyOld.Name) && int.TryParse(jsonPropertyOld.Name[^2..], out int index) && i != index - 1)
continue;
logger.LogWarning("For [{jsonProperty.Name}] <{directory}> doesn't match (valueNew:{valueNew} != valueOld:{valueOld})!", jsonPropertyOld.Name, directory, valueNew, valueOld);
differentColumns.Add(jsonPropertyOld.Name);
}
}
}
}
if (i == last) {
logger.LogInformation(string.Join(',', columns));
logger.LogInformation($"{string.Join(';', columnPairs)};");
}
}
result = unknownColumns.Count == 0 && differentColumns.Count == 0 && sameAfterSpaceSplitColumns.Count == 0;
return result;
}
private static int? TryGetPropertyIndex(JsonProperty[] jsonProperties, string propertyName) {
int? result = null;
for (int i = 0; i < jsonProperties.Length; i++) {
if (jsonProperties[i].Name != propertyName)
continue;
result = i;
break;
}
if (result is null) {
for (int i = 0; i < jsonProperties.Length; i++) {
if (jsonProperties[i].Name[0] != propertyName[0])
continue;
if (jsonProperties[i].Name.Length != propertyName.Length)
continue;
if (jsonProperties[i].Name != propertyName)
continue;
result = i;
break;
}
}
return result;
}
private static ProcessDataStandardFormat GetProcessDataStandardFormat(ILogger<Worker> logger, DateTime lastWriteTime, int expectedColumns, int columnsLine, string path, string[]? lines) {
ProcessDataStandardFormat result;
long sequence;
string[] segments;
List<string> body = [];
List<string> logistics = [];
bool lookForLogistics = false;
lines ??= File.ReadAllLines(path);
if (lines.Length <= columnsLine)
segments = [];
else {
segments = lines[columnsLine].Split('\t');
if (segments.Length != expectedColumns) {
logger.LogWarning("{segments} != {expectedColumns}", segments.Length, expectedColumns);
segments = [];
}
}
string[] columns = segments.Select(l => l.Trim('"')).ToArray();
for (int r = columnsLine + 1; r < lines.Length; r++) {
if (lines[r].StartsWith("NUM_DATA_ROWS"))
lookForLogistics = true;
if (!lookForLogistics) {
body.Add(lines[r]);
continue;
}
if (lines[r].StartsWith("LOGISTICS_1")) {
for (int i = r; i < lines.Length; i++) {
if (lines[r].StartsWith("END_HEADER"))
break;
logistics.Add(lines[i]);
}
break;
}
}
if (logistics.Count == 0)
sequence = lastWriteTime.Ticks;
else {
segments = logistics[0].Split("SEQUENCE=");
sequence = segments.Length < 2 || !long.TryParse(segments[1].Split(';')[0], out long s) ? lastWriteTime.Ticks : s;
}
result = new(Body: body.AsReadOnly(),
Columns: columns.AsReadOnly(),
Logistics: logistics.AsReadOnly(),
Sequence: sequence);
return result;
}
private static JsonElement[]? GetArray(ILogger<Worker> logger, int expectedColumns, ProcessDataStandardFormat processDataStandardFormat, bool lookForNumbers) {
JsonElement[]? results;
if (processDataStandardFormat.Body.Count == 0 || !processDataStandardFormat.Body[0].Contains('\t'))
results = JsonSerializer.Deserialize("[]", JsonElementCollectionSourceGenerationContext.Default.JsonElementArray) ?? throw new Exception();
else {
string value;
string[] segments;
List<string> lines = [];
StringBuilder stringBuilder = new();
foreach (string bodyLine in processDataStandardFormat.Body) {
_ = stringBuilder.Clear();
_ = stringBuilder.Append('{');
segments = bodyLine.Split('\t');
if (segments.Length != expectedColumns) {
logger.LogWarning("{segments} != {expectedColumns}", segments.Length, expectedColumns);
continue;
}
if (!lookForNumbers) {
for (int c = 0; c < segments.Length; c++) {
value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\"");
_ = stringBuilder.Append('"').Append(processDataStandardFormat.Columns[c]).Append("\":\"").Append(value).Append("\",");
}
} else {
for (int c = 0; c < segments.Length; c++) {
value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\"");
if (string.IsNullOrEmpty(value))
_ = stringBuilder.Append('"').Append(processDataStandardFormat.Columns[c]).Append("\":").Append(value).Append("null,");
else if (value.All(char.IsDigit))
_ = stringBuilder.Append('"').Append(processDataStandardFormat.Columns[c]).Append("\":").Append(value).Append(',');
else
_ = stringBuilder.Append('"').Append(processDataStandardFormat.Columns[c]).Append("\":\"").Append(value).Append("\",");
}
}
_ = stringBuilder.Remove(stringBuilder.Length - 1, 1);
_ = stringBuilder.AppendLine("}");
lines.Add(stringBuilder.ToString());
}
string json = $"[{string.Join(',', lines)}]";
results = JsonSerializer.Deserialize(json, JsonElementCollectionSourceGenerationContext.Default.JsonElementArray);
}
return results;
}
private static ProcessDataStandardFormat GetProcessDataStandardFormat(ILogger<Worker> logger, ProcessDataStandardFormatMapping processDataStandardFormatMapping, JsonElement[] jsonElements, ProcessDataStandardFormat processDataStandardFormat) {
ProcessDataStandardFormat result;
int column;
string value;
List<string> values = [];
List<string> results = [];
JsonProperty jsonProperty;
JsonProperty[] jsonProperties;
List<string> unknownColumns = [];
for (int i = 0; i < jsonElements.Length; i++) {
values.Clear();
if (jsonElements[i].ValueKind != JsonValueKind.Object) {
unknownColumns.Add(string.Empty);
break;
}
jsonProperties = jsonElements[i].EnumerateObject().ToArray();
if (jsonProperties.Length != processDataStandardFormatMapping.NewColumnNames.Count) {
logger.LogWarning("{jsonProperties} != {NewColumnNames}", jsonProperties.Length, processDataStandardFormatMapping.NewColumnNames.Count);
continue;
}
for (int c = 0; c < processDataStandardFormatMapping.ColumnIndices.Count; c++) {
column = processDataStandardFormatMapping.ColumnIndices[c];
if (column == -1)
value = processDataStandardFormatMapping.OldColumnNames[c];
else {
jsonProperty = jsonProperties[column];
value = jsonProperty.Value.ToString();
}
values.Add(value);
}
results.Add(string.Join('\t', values));
}
result = new(Body: new(results),
Columns: processDataStandardFormatMapping.OldColumnNames,
Logistics: processDataStandardFormat.Logistics,
Sequence: processDataStandardFormat.Sequence);
return result;
}
private static void Write(ILogger<Worker> logger, FileInfo fileInfo, ProcessDataStandardFormat processDataStandardFormat) {
List<string> results = [];
if (processDataStandardFormat.Sequence is null)
throw new NullReferenceException(nameof(processDataStandardFormat.Sequence));
string endOffset = "E#######T";
string dataOffset = "D#######T";
string headerOffset = "H#######T";
string format = "MM/dd/yyyy HH:mm:ss";
string startTime = new DateTime(processDataStandardFormat.Sequence.Value).ToString(format);
results.Add("HEADER_TAG\tHEADER_VALUE");
results.Add("FORMAT\t2.00");
results.Add("NUMBER_PASSES\t0001");
results.Add($"HEADER_OFFSET\t{headerOffset}");
results.Add($"DATA_OFFSET\t{dataOffset}");
results.Add($"END_OFFSET\t{endOffset}");
results.Add($"\"{string.Join("\"\t\"", processDataStandardFormat.Columns)}\"");
results.AddRange(processDataStandardFormat.Body);
results.Add($"NUM_DATA_ROWS\t{processDataStandardFormat.Body.Count.ToString().PadLeft(9, '0')}");
results.Add($"NUM_DATA_COLUMNS\t{processDataStandardFormat.Columns.Count.ToString().PadLeft(9, '0')}");
results.Add("DELIMITER\t;");
results.Add($"START_TIME_FORMAT\t{format}");
results.Add($"START_TIME\t{startTime}");
results.Add("LOGISTICS_COLUMN\tA_LOGISTICS");
results.Add("LOGISTICS_COLUMN\tB_LOGISTICS");
results.AddRange(processDataStandardFormat.Logistics);
File.WriteAllText($"{fileInfo.FullName}.tsv", string.Join(Environment.NewLine, results));
logger.LogDebug("<{fileInfo}>", fileInfo);
}
}

View File

@ -0,0 +1,111 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250228 {
private record Record(string TableName, ReadOnlyCollection<string> Columns, ReadOnlyCollection<string[]> Rows);
internal static void PostgresDumpToJson(ILogger<Worker> logger, List<string> args) {
string searchPattern = args[2];
string headerA = args[3].Replace('_', ' ');
string headerB = args[4].Replace('_', ' ');
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length != 1)
logger.LogWarning("<{files}>(s)", files.Length);
else
PostgresDumpToJson(logger, headerA, headerB, files[0]);
}
private static void PostgresDumpToJson(ILogger<Worker> logger, string headerA, string headerB, string file) {
ReadOnlyCollection<Record> records = GetRecords(headerA, headerB, file);
if (records.Count > 0)
WriteFile(file, records);
else
logger.LogWarning("<{records}>(s)", records.Count);
}
private static ReadOnlyCollection<Record> GetRecords(string headerA, string headerB, string file) {
List<Record> results = [];
string line;
string[] segmentsA;
string[] segmentsB;
string[] segmentsC;
string[] segmentsD;
string[] segmentsE;
string[] segmentsF;
List<string[]> rows;
string? tableName = null;
string[] lines = File.ReadAllLines(file);
ReadOnlyCollection<string>? columns = null;
for (int i = 0; i < lines.Length; i++) {
line = lines[i];
if (tableName is null) {
segmentsA = line.Split(headerA);
if (segmentsA.Length != 2)
continue;
segmentsB = segmentsA[1].Split(headerB);
if (segmentsB.Length != 2)
continue;
segmentsC = segmentsB[0].Split('(');
if (segmentsC.Length != 2)
continue;
segmentsD = segmentsC[1].Split(')');
if (segmentsD.Length != 2)
continue;
columns = segmentsD[0].Split(',').Select(l => l.Trim(' ').Trim('"')).ToArray().AsReadOnly();
if (columns.Count == 0)
continue;
segmentsE = segmentsB[0].Split(' ');
tableName = segmentsE[0];
} else if (columns is null)
break;
else {
rows = [];
for (int j = i + 1; j < lines.Length; j++) {
i = j;
segmentsF = lines[j].Split('\t');
if (segmentsF.Length != columns.Count) {
if (rows.Count > 0)
results.Add(new(TableName: tableName, Columns: columns, Rows: rows.AsReadOnly()));
break;
}
rows.Add(segmentsF);
}
columns = null;
tableName = null;
}
}
return results.AsReadOnly();
}
private static void WriteFile(string file, ReadOnlyCollection<Record> records) {
List<string> results = [];
string json;
string text;
Dictionary<string, string?> keyValuePairs = [];
foreach (Record record in records) {
results.Clear();
foreach (string[] row in record.Rows) {
keyValuePairs.Clear();
for (int i = 0; i < row.Length; i++) {
if (row[i] == "\\N")
keyValuePairs.Add(record.Columns[i], null);
else
keyValuePairs.Add(record.Columns[i], row[i]);
}
#pragma warning disable IL3050, IL2026
json = JsonSerializer.Serialize(keyValuePairs);
#pragma warning restore IL3050, IL2026
results.Add(json);
}
text = string.Join($",{Environment.NewLine}", results);
File.WriteAllText($"{file[..^4]}-{record.TableName}.json", $"[{Environment.NewLine}{text}{Environment.NewLine}]");
}
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.ObjectModel;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250301 {
internal static void PocketBaseImportWithDeno(ILogger<Worker> logger, List<string> args) {
char split = args[3][0];
string directory = args[6];
string scriptName = args[5];
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string workingDirectory = Path.GetFullPath(args[4]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogWarning("<{files}>(s)", files.Length);
else
PocketBaseImportWithDeno(logger, split, workingDirectory, scriptName, directory, files);
}
private static void PocketBaseImportWithDeno(ILogger<Worker> logger, char split, string workingDirectory, string scriptName, string directory, string[] files) {
string checkFile = Path.Combine(workingDirectory, scriptName);
if (!File.Exists(checkFile))
logger.LogWarning("<{checkFile}> doesn't exist!", checkFile);
else {
ReadOnlyCollection<string> fileNames = CopyFiles(split, workingDirectory, directory, files);
if (fileNames.Count == 0)
logger.LogWarning("<{fileNames}>(s)", fileNames.Count);
else {
foreach (string fileName in fileNames)
logger.LogInformation("deno run --unstable --allow-read --allow-env --allow-net {scriptName} --id=true --input={fileName}", scriptName, fileName);
}
}
}
private static ReadOnlyCollection<string> CopyFiles(char split, string workingDirectory, string directory, string[] files) {
List<string> results = [];
string fileName;
string checkFile;
string checkDirectory = Path.Combine(workingDirectory, directory);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files) {
fileName = Path.GetFileName(file).Split(split)[^1];
checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
File.Delete(checkFile);
File.Copy(file, checkFile);
results.Add(fileName);
}
return results.AsReadOnly();
}
}

View File

@ -0,0 +1,140 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250305 {
private static readonly HttpClient _HttpClient = new();
private record Record(Uri URI, string Path, DateTime LastModified, int? TotalSeconds);
internal static void WriteNginxFileSystemDelta(ILogger<Worker> logger, List<string> args) {
string host = args[2];
string rootDirectoryName = args[3];
string format = NginxFileSystem.GetFormat();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
string compareDirectory = Path.GetFullPath(args[0]);
logger.LogInformation("Comparing files on {host}", host);
ReadOnlyCollection<Record> records = GetRecords(format, timeZoneInfo, host, new([rootDirectoryName]), compareDirectory);
#if ShellProgressBar
ProgressBar progressBar = new(records.Count, "Downloading", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
foreach (Record record in records) {
#if ShellProgressBar
progressBar.Tick();
#endif
if (record.TotalSeconds is null)
Download(record);
else if (record.TotalSeconds.Value == 0)
logger.LogInformation("Different lengths");
else if (record.TotalSeconds.Value > 0)
logger.LogInformation("Overwrite remote (https)");
else
logger.LogInformation("Overwrite local");
}
#if ShellProgressBar
progressBar.Dispose();
#endif
}
private static ReadOnlyCollection<Record> GetRecords(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory) {
List<Record> results = [];
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}");
ReadOnlyCollection<NginxFileSystem>? nginxFileSystems = GetCollection(format, timeZoneInfo, uri);
if (nginxFileSystems is not null) {
NginxFileSystem nginxFileSystem;
ReadOnlyCollection<Record> records;
string checkDirectory = $"{compareDirectory}\\{string.Join('\\', directoryNames)}";
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
for (int i = 0; i < nginxFileSystems.Count; i++) {
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
if (nginxFileSystem.Type == "file") {
Record? record = CompareFile(host, directoryNames, compareDirectory, nginxFileSystem);
if (record is not null)
results.Add(record);
} else {
records = CompareDirectory(format, timeZoneInfo, host, directoryNames, compareDirectory, nginxFileSystem);
foreach (Record record in records)
results.Add(record);
}
}
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<NginxFileSystem>? GetCollection(string format, TimeZoneInfo timeZoneInfo, Uri uri) {
List<NginxFileSystem>? results;
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
taskHttpResponseMessage.Wait();
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
results = null;
else {
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
if (taskString.Result.StartsWith('<'))
results = null;
else {
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (nginxFileSystems is null)
results = null;
else {
results = [];
NginxFileSystem nginxFileSystem;
for (int i = 0; i < nginxFileSystems.Length; i++) {
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
results.Add(nginxFileSystem);
}
}
}
}
return results?.AsReadOnly();
}
private static Record? CompareFile(string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem) {
Record? result;
if (nginxFileSystem.LastModified is null || nginxFileSystem.Length is null)
result = null;
else {
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}/{nginxFileSystem.Name}");
FileInfo fileInfo = new($"{compareDirectory}\\{string.Join('\\', directoryNames)}\\{nginxFileSystem.Name}");
if (!fileInfo.Exists)
result = new(URI: uri, Path: fileInfo.FullName, LastModified: nginxFileSystem.LastModified.Value, TotalSeconds: null);
else {
int totalSeconds = (int)new TimeSpan(fileInfo.LastWriteTime.Ticks - nginxFileSystem.LastModified.Value.Ticks).TotalSeconds;
if (totalSeconds is not < 2 or not > -2)
result = new(URI: uri, Path: fileInfo.FullName, LastModified: nginxFileSystem.LastModified.Value, TotalSeconds: totalSeconds);
else if (fileInfo.Length != nginxFileSystem.Length.Value)
result = new(URI: uri, Path: fileInfo.FullName, LastModified: nginxFileSystem.LastModified.Value, TotalSeconds: 0);
else
result = null;
}
}
return result;
}
private static ReadOnlyCollection<Record> CompareDirectory(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem) {
ReadOnlyCollection<Record> results;
List<string> collection = directoryNames.ToList();
collection.Add(nginxFileSystem.Name);
results = GetRecords(format, timeZoneInfo, host, collection.AsReadOnly(), compareDirectory);
return results;
}
private static void Download(Record record) {
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(record.URI);
taskHttpResponseMessage.Wait();
if (taskHttpResponseMessage.Result.IsSuccessStatusCode) {
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
File.WriteAllText(record.Path, taskString.Result);
File.SetLastWriteTime(record.Path, record.LastModified);
}
}
}

View File

@ -0,0 +1,85 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250306 {
internal static void ProcessDataStandardFormatToJson(ILogger<Worker> logger, List<string> args) {
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly);
if (files.Length != 1)
logger.LogWarning("<{files}>(s)", files.Length);
else
ProcessDataStandardFormatToJson(logger, files[0]);
}
private static void ProcessDataStandardFormatToJson(ILogger<Worker> logger, string file) {
string[] lines = File.ReadAllLines(file);
int? columnTitlesLine = GetProcessDataStandardFormatColumnTitlesLine(lines);
if (columnTitlesLine is null)
logger.LogWarning("<{columnTitlesLine}> is null", nameof(columnTitlesLine));
else {
string? text = ProcessDataStandardFormatToLastDataLine(lines, columnTitlesLine.Value);
File.WriteAllText(Path.Combine(".vscode", "helper", ".lbl"), text);
if (lines.Length < columnTitlesLine.Value + 1)
logger.LogWarning("<{lines}>(s)", lines.Length);
else {
string json = ProcessDataStandardFormatToJson(columnTitlesLine.Value, [], lines);
File.WriteAllText(Path.Combine(".vscode", "helper", ".json"), json);
}
}
}
private static int? GetProcessDataStandardFormatColumnTitlesLine(string[] lines) {
int? result = null;
bool foundEndOfFile = false;
for (int i = 0; i < lines.Length; i++) {
if (lines[i] == "EOF")
foundEndOfFile = true;
if (foundEndOfFile && lines[i].StartsWith("END_OFFSET") && i + 3 < lines.Length) {
result = i + 2;
break;
}
}
return result;
}
private static string? ProcessDataStandardFormatToLastDataLine(string[] lines, int columnTitlesLine) {
string? result = null;
for (int i = columnTitlesLine + 1; i < lines.Length; i++) {
if (lines[i].StartsWith("NUM_DATA_ROWS")) {
result = lines[i - 2];
break;
}
}
return result;
}
private static string ProcessDataStandardFormatToJson(int columnTitlesLine, string[] columns, string[] lines) {
#pragma warning disable CA1845, IDE0057
string result = "[\n";
string line;
string value;
string[] segments;
if (columns.Length == 0)
columns = lines[columnTitlesLine].Trim().Split('|');
int columnsLength = columns.Length - 2;
for (int i = columnTitlesLine + 1; i < lines.Length; i++) {
line = "{";
segments = lines[i].Trim().Split('|');
if (segments.Length != columnsLength)
continue;
for (int c = 1; c < segments.Length; c++) {
value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\"");
line += '"' + columns[c].Trim('"') + '"' + ':' + '"' + value + '"' + ',';
}
line = line.Substring(0, line.Length - 1) + '}' + ',' + '\n';
result += line;
}
result = result.Substring(0, result.Length - 2) + ']';
return result;
#pragma warning restore CA1845, IDE0057
}
}

View File

@ -0,0 +1,46 @@
using File_Folder_Helper.Helpers;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250315 {
internal static void Empty(ILogger<Worker> logger, List<string> args) {
string[] searchPatterns = args[2].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
if (searchPatterns.Length == 1) {
string[] files = Directory.GetFiles(sourceDirectory, searchPatterns[0], SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogWarning("<{files}>(s)", files.Length);
else {
string directoryName;
string[] directories;
foreach (string file in files) {
directoryName = Path.GetDirectoryName(file) ?? throw new Exception();
directories = Directory.GetDirectories(directoryName, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory);
}
}
} else {
string[] files;
string checkFile;
HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
foreach (string searchPattern in searchPatterns) {
files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogWarning("<{files}>(s)", files.Length);
else {
foreach (string file in files) {
checkFile = $"{file}.json";
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
}
}
}
}
}

View File

@ -0,0 +1,521 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250320 {
private record Match(string Name,
string Parameters,
string Result,
string Scope,
string Static,
string Value,
string Async,
string Partial);
private record Search(string Constructor,
string Delegate,
string Name,
string Not,
string Wrap);
private record Method(int? EndLine,
ReadOnlyDictionary<string, string> Parameters,
string FirstLine,
int I,
string Line,
Match Match,
ReadOnlyCollection<int> ReferenceToLineNumbers,
int? ScopeEnum,
Search Search,
int StartLine) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, MethodCollectionCommonSourceGenerationContext.Default.Method);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Method[]))]
private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext {
}
private record MethodWith(int? EndLine,
ReadOnlyDictionary<string, string> Parameters,
string FirstLine,
string Line,
Match Match,
ReadOnlyCollection<MethodWith> References,
ReadOnlyCollection<int> ReferenceToLineNumbers,
int? ScopeEnum,
Search Search,
int StartLine) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, MethodCollectionCommonSourceGenerationContext.Default.Method);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MethodWith[]))]
private partial class MethodWithCollectionCommonSourceGenerationContext : JsonSerializerContext {
}
private const string _Name = "name";
private const string _Async = "async";
private const string _Scope = "scope";
private const string _Result = "result";
private const string _Static = "static";
private const string _Partial = "partial";
private const string _Parameters = "parameters";
[GeneratedRegex(@"[[\]<,>?a-zA-Z0-9_()\s]*?\s[a-z_]{1}[a-zA-Z0-9_]*?,")]
private static partial Regex CSharpParameter();
// VSCode Search ^\s*\b(?<scope>public|private|internal|protected|\sI[a-zA-Z0-9_]*\.)\s?\b(?<static>static)?\s?\b(?<partial>partial)?\s?\b(?<async>async)?\s?\b(?<result>[\[\]\.\?<,>a-zA-Z0-9_()\s]*?)\s?\b(?<name>[A-Z_]{1}[a-zA-Z0-9_])+\((?<parameters>.*)\)\s?\{?$
[GeneratedRegex(@"^\s*\b(?<scope>public|private|internal|protected|\sI[a-zA-Z0-9_]*\.)\s?\b(?<static>static)?\s?\b(?<partial>partial)?\s?\b(?<async>async)?\s?\b(?<result>[\[\]\.\?<,>a-zA-Z0-9_()\s]*?)\s?\b(?<name>[A-Z_]{1}[a-zA-Z0-9_]*)+\((?<parameters>.*)\)\s?\{?$")]
private static partial Regex CSharpMethodLine();
private static ReadOnlyCollection<Method> GetSortedMethods(ReadOnlyCollection<Method> methods) =>
(from l in methods orderby l.ScopeEnum descending, l.ReferenceToLineNumbers.Count descending, l.Line.Length, l.Match.Name.Length, l.Match.Name select l).ToArray().AsReadOnly();
internal static void SortCodeMethods(ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken) {
bool check;
string[] lines;
List<string> changed = [];
bool usePathCombine = true;
long ticks = DateTime.Now.Ticks;
bool logOnly = bool.Parse(args[2]);
int scopeSpaces = int.Parse(args[3]);
logger.LogInformation("{ticks}", ticks);
string repositoryDirectory = Path.GetFullPath(args[0]);
string[] cSharpFiles = Directory.GetFiles(repositoryDirectory, "*.cs", SearchOption.AllDirectories);
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = logOnly ? new(cSharpFiles) : Helpers.HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(repositoryDirectory, usePathCombine, cancellationToken);
foreach (string cSharpFile in cSharpFiles) {
if (!gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(cSharpFile))
continue;
for (int i = 0; i < 10; i++) {
lines = File.ReadAllLines(cSharpFile);
check = SortFile(logger, logOnly, scopeSpaces, cSharpFile, lines);
if (check) {
Thread.Sleep(500);
changed.Add($"{i + 1:00}) {cSharpFile}");
}
if (logOnly || !check)
break;
}
}
if (changed.Count == 0)
logger.LogInformation("No changes :)");
else {
changed.Reverse();
foreach (string c in changed)
logger.LogInformation(c);
}
}
private static bool SortFile(ILogger<Worker> logger, bool logOnly, int scopeSpaces, string cSharpFile, string[] lines) {
bool result;
ReadOnlyCollection<Method> methods = GetMethods(logger, scopeSpaces, cSharpFile, lines);
if (methods.Count == 0)
result = false;
else if (methods.Any(l => l.EndLine is null))
result = false;
else if (logOnly) {
foreach (Method method in methods)
logger.LogInformation("{cSharpFile} - {Name} has {lines} line(s)", cSharpFile, method.Match.Name, (method.EndLine is null ? 999999 : method.EndLine.Value - method.StartLine).ToString("000000"));
result = false;
} else {
ReadOnlyCollection<Method> sortedMethods = GetSortedMethods(methods);
if (Debugger.IsAttached)
File.WriteAllText(Path.Combine(".vscode", "helper", ".json"), JsonSerializer.Serialize(sortedMethods.ToArray(), MethodCollectionCommonSourceGenerationContext.Default.MethodArray));
ReadOnlyCollection<MethodWith> collection = GetCollection(logger, lines, sortedMethods);
result = WriteAllLines(cSharpFile, lines, collection);
}
return result;
}
private static ReadOnlyCollection<Method> GetMethods(ILogger<Worker> logger, int scopeSpaces, string cSharpFile, string[] lines) {
List<Method> results = [];
int check;
int blocks;
bool isLinq;
Match match;
string line;
int? endLine;
int startLine;
Method method;
Search search;
int? scopeEnum;
string firstLine;
string innerLine;
string lineSegmentFirst;
List<int> referenceToLineNumbers;
Regex parameterRegex = CSharpParameter();
ReadOnlyDictionary<string, string> parameters;
System.Text.RegularExpressions.Match regularExpressionsMatch;
for (int i = 0; i < lines.Length; i++) {
check = GetNumberOfStartSpaces(lines, i);
if (check != scopeSpaces)
continue;
line = lines[i].Trim();
if (string.IsNullOrEmpty(line))
continue;
if (line.Length < 5)
continue;
if (line.EndsWith(','))
continue;
regularExpressionsMatch = CSharpMethodLine().Match(line);
if (!regularExpressionsMatch.Success)
continue;
match = new(Async: regularExpressionsMatch.Groups[_Async].Value,
Name: regularExpressionsMatch.Groups[_Name].Value,
Parameters: regularExpressionsMatch.Groups[_Parameters].Value,
Partial: regularExpressionsMatch.Groups[_Partial].Value,
Result: regularExpressionsMatch.Groups[_Result].Value,
Scope: regularExpressionsMatch.Groups[_Scope].Value,
Static: regularExpressionsMatch.Groups[_Static].Value,
Value: regularExpressionsMatch.Value);
scopeEnum = GetScopeEnum(match);
parameters = GetParameters(parameterRegex, match);
search = new(Constructor: $"{match.Name.ToLower()} = new(",
Delegate: $" += {match.Name};",
Name: $" {match.Name}(",
Not: $"!{match.Name}(",
Wrap: $"({match.Name}(");
logger.LogInformation("{line} {a} // {results}", line.Split(" =>")[0], "{ }", results.Count);
if (string.IsNullOrEmpty(match.Name))
continue;
blocks = 0;
startLine = GetStartLine(lines, i);
if (!lines[startLine].StartsWith("#pragma") && !lines[startLine].StartsWith("#nullable"))
firstLine = lines[startLine].Trim();
else
firstLine = lines[startLine + 1].Trim();
isLinq = !lines[i + 1].StartsWith("#pragma") && !lines[i + 1].StartsWith("#nullable") && lines[i].Trim()[^1] != '{' && lines[i + 1].Trim() != "{";
if (isLinq)
blocks++;
endLine = null;
if (lines[i].Trim()[^1] == '{')
blocks++;
for (int j = i + 1; j < lines.Length; j++) {
innerLine = lines[j].Trim();
if (innerLine.StartsWith("#pragma") || innerLine.StartsWith("#nullable"))
continue;
if (isLinq && string.IsNullOrEmpty(innerLine)) {
if (line.EndsWith(';'))
blocks--;
}
blocks += GetLineBlockCount(innerLine, isLinq);
if (blocks != 0)
continue;
endLine = j;
if (lines.Length > j + 1 && string.IsNullOrEmpty(lines[j + 1].Trim()))
endLine++;
if (j > lines.Length - 2)
throw new Exception();
break;
}
referenceToLineNumbers = GetReferenceToLineNumbers(lines: lines, start: 0, end: lines.Length, i: i, search: search, parameters: parameters);
if (referenceToLineNumbers.Count == 0) {
lineSegmentFirst = line.Split(match.Name)[0];
if (!lines[i - 1].Trim().StartsWith("[Obsolete")) {
if (lineSegmentFirst.StartsWith("private"))
logger.LogWarning("// <{cSharpFileName}> {name} with {parameters} parameter(s) <{line}>", Path.GetFileName(cSharpFile), match.Name, parameters, lineSegmentFirst);
else
logger.LogInformation("// <{cSharpFileName}> {name} with {parameters} parameter(s) <{line}>", Path.GetFileName(cSharpFile), match.Name, parameters, lineSegmentFirst);
}
}
if (referenceToLineNumbers.Count == 0)
referenceToLineNumbers.Add(-1);
logger.LogInformation("{line} {a} // {results} ~~~ {startLine} => {firstUsedLine}", line.Split(" =>")[0], "{ }", results.Count, startLine, referenceToLineNumbers.First());
method = new(EndLine: endLine,
FirstLine: firstLine,
I: i,
Line: line,
Match: match,
Parameters: parameters,
ReferenceToLineNumbers: referenceToLineNumbers.AsReadOnly(),
Search: search,
ScopeEnum: scopeEnum,
StartLine: startLine);
results.Add(method);
}
return results.AsReadOnly();
}
private static int GetNumberOfStartSpaces(string[] lines, int i) {
int result = 0;
foreach (char @char in lines[i]) {
if (@char != ' ')
break;
result += 1;
}
return result;
}
private static int GetScopeEnum(Match match) {
int result;
int value = match.Scope switch {
"public" => 8000,
"internal" => 7000,
"protected" => 6000,
"private" => 5000,
_ => match.Scope.Length > 2
&& match.Scope[..2] == " I"
&& match.Scope[^1] == '.' ? 9000 : throw new NotImplementedException()
};
result = value
+ (string.IsNullOrEmpty(match.Result) ? 100 : 0)
+ (string.IsNullOrEmpty(match.Static) ? 0 : 10)
+ (string.IsNullOrEmpty(match.Async) ? 0 : 1);
return result;
}
private static ReadOnlyDictionary<string, string> GetParameters(Regex parameterRegex, Match match) {
Dictionary<string, string> results = [];
string value;
string[] segments;
System.Text.RegularExpressions.Match[] matches = parameterRegex.Matches($"{match.Parameters},").ToArray();
try {
foreach (System.Text.RegularExpressions.Match m in matches) {
if (!m.Success)
continue;
value = m.Value.Trim()[..^1];
segments = value.Split(' ');
results.Add(segments[^1], value);
}
} catch (Exception) {
results.Clear();
System.Text.RegularExpressions.Match m;
for (int i = 0; i < matches.Length; i++) {
m = matches[i];
if (!m.Success)
continue;
results.Add(i.ToString(), i.ToString());
}
}
return new(results);
}
private static int GetStartLine(string[] lines, int i) {
int result = i;
string line;
for (int j = i - 1; j > -1; j--) {
line = lines[j].Trim();
if (!line.StartsWith('[') && !line.StartsWith('#') && !line.StartsWith("/// "))
break;
result--;
}
return result;
}
private static int GetLineBlockCount(string line, bool isLinq) {
int result = 0;
bool ignore = false;
for (int i = 0; i < line.Length; i++) {
if (line[i] == '\'')
i++;
else if (!isLinq && !ignore && line[i] == '{')
result++;
else if (!isLinq && !ignore && line[i] == '}')
result--;
else if (isLinq && !ignore && line[i] == ';')
result--;
else if (i > 0 && line[i] == '"' && line[i - 1] != '\\')
ignore = !ignore;
}
return result;
}
private static List<int> GetReferenceToLineNumbers(string[] lines, int start, int end, int i, Search search, ReadOnlyDictionary<string, string> parameters) {
List<int> results = [];
string[] segments;
string[] afterSegments;
string lastSegmentBeforeDot;
for (int j = start; j < end; j++) {
if (j == i)
continue;
segments = lines[j].Split(search.Name);
if (segments.Length == 1) {
segments = lines[j].Split(search.Not);
if (segments.Length == 1) {
segments = lines[j].Split(search.Wrap);
if (segments.Length == 1) {
if (!lines[j].EndsWith(search.Delegate)) {
segments = lines[j].Split(search.Constructor);
if (segments.Length == 1)
continue;
}
}
}
}
if (lines[j].EndsWith(search.Delegate))
results.Add(j);
else {
lastSegmentBeforeDot = segments[^1].Split(").")[0];
if (parameters.Count == 0) {
if (lastSegmentBeforeDot.Contains(','))
continue;
} else {
afterSegments = lastSegmentBeforeDot.Split(',');
if (afterSegments.Length != parameters.Count)
continue;
}
results.Add(j);
}
}
return results;
}
private static ReadOnlyCollection<MethodWith> GetCollection(ILogger<Worker> logger, string[] lines, ReadOnlyCollection<Method> sortedMethods) {
List<MethodWith> results = [];
List<Method> check = sortedMethods.ToList();
foreach (Method method in sortedMethods) {
logger.LogInformation($"{method.Match.Name} => {method.Parameters.Count}");
if (method.EndLine is null)
continue;
if (!check.Remove(method))
continue;
MethodWith methodWith = GetMethodWith(lines, sortedMethods, check, method, method.EndLine.Value);
results.Add(methodWith);
}
return results.AsReadOnly();
}
private static MethodWith GetMethodWith(string[] lines, ReadOnlyCollection<Method> methods, List<Method> check, Method method, int methodEndLineValue) {
MethodWith methodWith;
List<int> referenceToLineNumbers;
MethodWith[] sortedReferences;
Dictionary<int, MethodWith> references = [];
foreach (Method m in methods) {
if (m.EndLine is null)
continue;
if (m == method)
continue;
referenceToLineNumbers = GetReferenceToLineNumbers(lines: lines, start: method.StartLine, end: methodEndLineValue, i: -1, search: m.Search, parameters: m.Parameters);
if (referenceToLineNumbers.Count > 0) {
if (!check.Remove(m))
continue;
foreach (int i in referenceToLineNumbers) {
if (references.ContainsKey(i))
continue;
methodWith = GetMethodWith(lines, methods, check, m, m.EndLine.Value);
references.Add(i, methodWith);
break;
}
}
}
if (references.Count < 2)
sortedReferences = (from l in references select l.Value).ToArray();
else
sortedReferences = (from l in references orderby l.Key select l.Value).ToArray();
methodWith = new(EndLine: method.EndLine,
FirstLine: method.FirstLine,
Line: method.Line,
Match: method.Match,
Parameters: method.Parameters,
References: new(sortedReferences),
ReferenceToLineNumbers: method.ReferenceToLineNumbers,
ScopeEnum: method.ScopeEnum,
Search: method.Search,
StartLine: method.StartLine);
return methodWith;
}
private static bool WriteAllLines(string cSharpFile, string[] lines, ReadOnlyCollection<MethodWith> collection) {
bool result;
if (Debugger.IsAttached)
WriteDebug(collection);
List<string> results = [];
ReadOnlyCollection<int> methodLines = GetMethodLines(collection);
int maxMethodLines = methodLines.Max();
for (int i = 0; i < maxMethodLines; i++) {
if (methodLines.Contains(i))
continue;
results.Add(lines[i]);
}
List<bool> nests = [true];
foreach (MethodWith methodWith in collection) {
if (methodWith.EndLine is null)
continue;
AppendLines(results, nests, lines, methodWith, methodWith.EndLine.Value);
}
for (int i = maxMethodLines + 1; i < lines.Length; i++)
results.Add(lines[i]);
string text = File.ReadAllText(cSharpFile);
string join = string.Join(Environment.NewLine, results);
if (join == text)
result = false;
else {
result = true;
File.WriteAllText(cSharpFile, join);
}
return result;
}
private static void WriteDebug(ReadOnlyCollection<MethodWith> collection) {
List<string> results = [];
List<bool> nests = [true];
foreach (MethodWith methodWith in collection)
AppendLines(results, nests, methodWith);
File.WriteAllText(Path.Combine(".vscode", "helper", ".md"), string.Join(Environment.NewLine, results));
}
private static void AppendLines(List<string> results, List<bool> nests, MethodWith methodWith) {
nests.Add(true);
results.Add($" - {new string('#', nests.Count)} {methodWith.Match.Name} => {methodWith.Parameters.Count}");
foreach (MethodWith m in methodWith.References)
AppendLines(results, nests, m);
nests.RemoveAt(nests.Count - 1);
}
private static ReadOnlyCollection<int> GetMethodLines(ReadOnlyCollection<MethodWith> collection) {
List<int> results = [];
List<bool> nests = [true];
foreach (MethodWith methodWith in collection) {
if (methodWith.EndLine is null)
continue;
AppendLineNumbers(results, nests, methodWith, methodWith.EndLine.Value);
}
int[] distinct = results.Distinct().ToArray();
if (distinct.Length != results.Count)
throw new Exception();
return new(results);
}
private static void AppendLineNumbers(List<int> results, List<bool> nests, MethodWith methodWith, int methodWithEndLineValue) {
nests.Add(true);
for (int i = methodWith.StartLine; i < methodWithEndLineValue + 1; i++)
results.Add(i);
foreach (MethodWith m in methodWith.References) {
if (m.EndLine is null)
continue;
AppendLineNumbers(results, nests, m, m.EndLine.Value);
}
nests.RemoveAt(nests.Count - 1);
}
private static void AppendLines(List<string> results, List<bool> nests, string[] lines, MethodWith methodWith, int methodWithEndLineValue) {
nests.Add(true);
for (int i = methodWith.StartLine; i < methodWithEndLineValue + 1; i++)
results.Add(lines[i]);
foreach (MethodWith m in methodWith.References) {
if (m.EndLine is null)
continue;
AppendLines(results, nests, lines, m, m.EndLine.Value);
}
nests.RemoveAt(nests.Count - 1);
}
}

View File

@ -0,0 +1,138 @@
using System.Collections.ObjectModel;
using File_Folder_Helper.Helpers;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250321 {
private record Record(string Directory,
string File,
ThreeDeep ThreeDeep) {
public static ReadOnlyCollection<Record> GetCollection(string sourceDirectory, string searchPattern, string[] files) {
List<Record> results = [];
Record record;
string directory;
string fileNameWithoutExtension;
bool json = searchPattern.Contains(".json");
bool check = searchPattern.Split('.').Length == 3;
ReadOnlyCollection<ThreeDeep> collection = ThreeDeep.GetCollection(files);
foreach (ThreeDeep threeDeep in collection) {
if (!json && check)
fileNameWithoutExtension = threeDeep.DirectoryName;
else if (!json && !check)
fileNameWithoutExtension = threeDeep.FileNameWithoutExtension;
else if (json)
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(threeDeep.FileNameWithoutExtension);
else
throw new NotImplementedException();
directory = $"{fileNameWithoutExtension[^1]}{fileNameWithoutExtension[^3..][..2]}";
if (json || (!json && !check)) {
record = new(Directory: Path.Combine(sourceDirectory, "new-a", directory),
File: $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}",
ThreeDeep: threeDeep);
} else if (!json && check) {
record = new(Directory: Path.Combine(sourceDirectory, "new-b", directory, threeDeep.DirectoryName),
File: $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}",
ThreeDeep: threeDeep);
} else
throw new NotImplementedException();
results.Add(record);
}
return results.AsReadOnly();
}
}
private record ThreeDeep(string Extension,
string FileNameWithoutExtension,
long LastModified,
long Length,
string DirectoryName,
string ParentDirectoryName,
string Root) {
public static ReadOnlyCollection<ThreeDeep> GetCollection(string[] files) {
List<ThreeDeep> results = [];
ThreeDeep record;
FileInfo fileInfo;
string parentDirectory;
foreach (string file in files) {
fileInfo = new(file);
parentDirectory = Path.GetDirectoryName(fileInfo.DirectoryName) ?? throw new Exception();
record = new(Extension: Path.GetExtension(file),
FileNameWithoutExtension: Path.GetFileNameWithoutExtension(file),
LastModified: fileInfo.LastWriteTime.Ticks,
Length: fileInfo.Length,
DirectoryName: Path.GetFileName(fileInfo.DirectoryName) ?? throw new Exception(),
ParentDirectoryName: Path.GetFileName(parentDirectory),
Root: Path.GetDirectoryName(parentDirectory) ?? throw new Exception());
results.Add(record);
}
return results.AsReadOnly();
}
public static string GetFullPath(ThreeDeep threeDeep) =>
Path.Combine(threeDeep.Root, threeDeep.ParentDirectoryName, threeDeep.DirectoryName, $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}");
}
internal static void MoveToLast(ILogger<Worker> logger, List<string> args) {
string[] searchPatterns = args[2].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
if (searchPatterns.Length == 1)
logger.LogInformation("No code for just one!");
else {
HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
ReadOnlyCollection<Record> collection = GetCollection(logger, searchPatterns, sourceDirectory);
if (collection.Count != 0)
UseCollection(collection);
else
logger.LogInformation("No files!");
if (collection.Count != 0)
HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
}
}
private static ReadOnlyCollection<Record> GetCollection(ILogger<Worker> logger, string[] searchPatterns, string sourceDirectory) {
string[] files;
List<Record> results = [];
foreach (string searchPattern in searchPatterns) {
files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogWarning("<{files}>(s)", files.Length);
else {
ReadOnlyCollection<Record> collection = Record.GetCollection(sourceDirectory, searchPattern, files);
results.AddRange(collection);
}
}
return results.AsReadOnly();
}
private static void UseCollection(ReadOnlyCollection<Record> collection) {
string fullPath;
string checkFile;
List<string> distinct = [];
foreach (Record record in collection) {
if (distinct.Contains(record.Directory))
continue;
distinct.Add(record.Directory);
}
foreach (string directory in distinct) {
if (Directory.Exists(directory))
continue;
_ = Directory.CreateDirectory(directory);
}
foreach (Record record in collection) {
checkFile = Path.Combine(record.Directory, record.File);
if (File.Exists(checkFile))
continue;
fullPath = ThreeDeep.GetFullPath(record.ThreeDeep);
File.Move(fullPath, checkFile);
}
}
}

View File

@ -0,0 +1,236 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250404 {
internal record KafkaProducerSaslOptions(
[property: JsonPropertyName("mechanism")] string Mechanism
);
internal record MonitorList(
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("pathName")] string PathName,
[property: JsonPropertyName("parent")] int? Parent,
[property: JsonPropertyName("childrenIDs")] IReadOnlyList<int> ChildrenIDs,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("method")] string Method,
[property: JsonPropertyName("hostname")] object Hostname,
[property: JsonPropertyName("port")] object Port,
[property: JsonPropertyName("maxretries")] int MaxRetries,
[property: JsonPropertyName("weight")] int Weight,
[property: JsonPropertyName("active")] bool Active,
[property: JsonPropertyName("forceInactive")] bool ForceInactive,
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("timeout")] int Timeout,
[property: JsonPropertyName("interval")] int Interval,
[property: JsonPropertyName("retryInterval")] int RetryInterval,
[property: JsonPropertyName("resendInterval")] int ResendInterval,
[property: JsonPropertyName("keyword")] object Keyword,
[property: JsonPropertyName("invertKeyword")] bool InvertKeyword,
[property: JsonPropertyName("expiryNotification")] bool ExpiryNotification,
[property: JsonPropertyName("ignoreTls")] bool IgnoreTls,
[property: JsonPropertyName("upsideDown")] bool UpsideDown,
[property: JsonPropertyName("packetSize")] int PacketSize,
[property: JsonPropertyName("maxredirects")] int MaxRedirects,
[property: JsonPropertyName("accepted_statuscodes")] IReadOnlyList<string> AcceptedStatusCodes,
[property: JsonPropertyName("dns_resolve_type")] string DnsResolveType,
[property: JsonPropertyName("dns_resolve_server")] string DnsResolveServer,
[property: JsonPropertyName("dns_last_result")] object DnsLastResult,
[property: JsonPropertyName("docker_container")] string DockerContainer,
[property: JsonPropertyName("docker_host")] object DockerHost,
[property: JsonPropertyName("proxyId")] object ProxyId,
[property: JsonPropertyName("notificationIDList")] NotificationIDList NotificationIDList,
[property: JsonPropertyName("tags")] IReadOnlyList<object> Tags,
[property: JsonPropertyName("maintenance")] bool Maintenance,
[property: JsonPropertyName("mqttTopic")] string MqttTopic,
[property: JsonPropertyName("mqttSuccessMessage")] string MqttSuccessMessage,
[property: JsonPropertyName("databaseQuery")] object DatabaseQuery,
[property: JsonPropertyName("authMethod")] string AuthMethod,
[property: JsonPropertyName("grpcUrl")] object GrpcUrl,
[property: JsonPropertyName("grpcProtobuf")] object GrpcProtobuf,
[property: JsonPropertyName("grpcMethod")] object GrpcMethod,
[property: JsonPropertyName("grpcServiceName")] object GrpcServiceName,
[property: JsonPropertyName("grpcEnableTls")] bool GrpcEnableTls,
[property: JsonPropertyName("radiusCalledStationId")] object RadiusCalledStationId,
[property: JsonPropertyName("radiusCallingStationId")] object RadiusCallingStationId,
[property: JsonPropertyName("game")] object Game,
[property: JsonPropertyName("gamedigGivenPortOnly")] bool GameDigGivenPortOnly,
[property: JsonPropertyName("httpBodyEncoding")] string HttpBodyEncoding,
[property: JsonPropertyName("jsonPath")] object JsonPath,
[property: JsonPropertyName("expectedValue")] object ExpectedValue,
[property: JsonPropertyName("kafkaProducerTopic")] object KafkaProducerTopic,
[property: JsonPropertyName("kafkaProducerBrokers")] IReadOnlyList<object> KafkaProducerBrokers,
[property: JsonPropertyName("kafkaProducerSsl")] bool KafkaProducerSsl,
[property: JsonPropertyName("kafkaProducerAllowAutoTopicCreation")] bool KafkaProducerAllowAutoTopicCreation,
[property: JsonPropertyName("kafkaProducerMessage")] object KafkaProducerMessage,
[property: JsonPropertyName("screenshot")] object Screenshot,
[property: JsonPropertyName("headers")] object Headers,
[property: JsonPropertyName("body")] object Body,
[property: JsonPropertyName("grpcBody")] object GrpcBody,
[property: JsonPropertyName("grpcMetadata")] object GrpcMetadata,
[property: JsonPropertyName("basic_auth_user")] string BasicAuthUser,
[property: JsonPropertyName("basic_auth_pass")] string BasicAuthPass,
[property: JsonPropertyName("oauth_client_id")] object OauthClientId,
[property: JsonPropertyName("oauth_client_secret")] object OauthClientSecret,
[property: JsonPropertyName("oauth_token_url")] object OauthTokenUrl,
[property: JsonPropertyName("oauth_scopes")] object OauthScopes,
[property: JsonPropertyName("oauth_auth_method")] string OauthAuthMethod,
[property: JsonPropertyName("pushToken")] string PushToken,
[property: JsonPropertyName("databaseConnectionString")] string DatabaseConnectionString,
[property: JsonPropertyName("radiusUsername")] object RadiusUsername,
[property: JsonPropertyName("radiusPassword")] object RadiusPassword,
[property: JsonPropertyName("radiusSecret")] object RadiusSecret,
[property: JsonPropertyName("mqttUsername")] string MqttUsername,
[property: JsonPropertyName("mqttPassword")] string MqttPassword,
[property: JsonPropertyName("authWorkstation")] object AuthWorkstation,
[property: JsonPropertyName("authDomain")] object AuthDomain,
[property: JsonPropertyName("tlsCa")] object TlsCa,
[property: JsonPropertyName("tlsCert")] object TlsCert,
[property: JsonPropertyName("tlsKey")] object TlsKey,
[property: JsonPropertyName("kafkaProducerSaslOptions")] KafkaProducerSaslOptions KafkaProducerSaslOptions,
[property: JsonPropertyName("includeSensitiveData")] bool IncludeSensitiveData
);
internal record NotificationIDList(
[property: JsonPropertyName("4")] bool _4
);
internal record NotificationList(
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("active")] bool Active,
[property: JsonPropertyName("userId")] int UserId,
[property: JsonPropertyName("isDefault")] bool IsDefault,
[property: JsonPropertyName("config")] string Config
);
internal record Kuma(
[property: JsonPropertyName("version")] string Version,
[property: JsonPropertyName("notificationList")] IReadOnlyList<NotificationList> NotificationList,
[property: JsonPropertyName("monitorList")] IReadOnlyList<MonitorList> MonitorList
);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Kuma))]
private partial class KumaCommonSourceGenerationContext : JsonSerializerContext {
}
internal static void KumaToGatus(ILogger<Worker> logger, List<string> args) {
string url = args[4];
string fileName = args[3];
string searchPattern = args[2];
ParseMetrics(logger, fileName, url);
string sourceDirectory = Path.GetFullPath(args[0]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length == 0)
logger.LogWarning("<{files}>(s)", files.Length);
else
KumaToGatus(files);
}
private static void ParseMetrics(ILogger<Worker> logger, string fileName, string url) {
FileStream fileStream = new(fileName, FileMode.Truncate);
HttpClient httpClient = new();
Task<Stream> streamTask = httpClient.GetStreamAsync(url);
streamTask.Wait();
Task task = streamTask.Result.CopyToAsync(fileStream);
task.Wait();
ParseMetrics(logger, fileStream);
fileStream.Dispose();
streamTask.Dispose();
httpClient.Dispose();
}
private static void ParseMetrics(ILogger<Worker> _, FileStream __) {
// Task<List<IMetric>> metrics = PrometheusMetricsParser.ParseAsync(fileStream);
// metrics.Wait();
// foreach (IMetric metric in metrics.Result) {
// if (metric is not Gauge gauge)
// continue;
// foreach (GaugeMeasurement gaugeMeasurement in gauge.Measurements) {
// if (string.IsNullOrEmpty(metric.Name))
// continue;
// foreach (KeyValuePair<string, string> keyValuePair in gaugeMeasurement.Labels) {
// logger.LogInformation("name:{name}; timestamp:{timestamp}; value:{value}; key-name:{key-name}; key-value:{key-value}",
// metric.Name,
// gaugeMeasurement.Timestamp,
// gaugeMeasurement.Value,
// keyValuePair.Key,
// keyValuePair.Value);
// }
// }
// }
}
private static void KumaToGatus(string[] files) {
Kuma? kuma;
string json;
string checkFile;
foreach (string file in files) {
checkFile = file.ToLower().Replace('_', '-');
if (checkFile != file)
File.Move(file, checkFile);
json = File.ReadAllText(checkFile);
kuma = JsonSerializer.Deserialize(json, KumaCommonSourceGenerationContext.Default.Kuma);
if (kuma is null)
continue;
WriteGatus(checkFile, kuma);
}
}
private static void WriteGatus(string file, Kuma kuma) {
List<string> results = [
string.Empty,
$"# set GATUS_CONFIG_PATH=./{Path.GetFileName(file)}.yaml",
string.Empty,
"endpoints:"
];
string[] segments;
foreach (MonitorList monitorList in kuma.MonitorList) {
if (monitorList.Type is not "http" and not "postgres")
continue;
results.Add($" - name: {monitorList.Name}");
results.Add($" group: {monitorList.PathName.Split(' ')[0]}");
results.Add($" enabled: {monitorList.Active.ToString().ToLower()}");
results.Add($" interval: {monitorList.Interval}s");
if (monitorList.Type == "http") {
results.Add($" method: {monitorList.Method}");
results.Add($" url: \"{monitorList.Url}\"");
if (monitorList.AuthMethod == "basic") {
results.Add($" # user: \"{monitorList.BasicAuthUser}\"");
results.Add($" # password: \"{monitorList.BasicAuthPass}\"");
}
results.Add(" conditions:");
results.Add(" - \"[STATUS] < 300\"");
if (monitorList.Url.Contains("https"))
results.Add(" - \"[CERTIFICATE_EXPIRATION] > 48h\"");
results.Add($" - \"[RESPONSE_TIME] < {monitorList.Timeout}\"");
} else if (monitorList.Type == "postgres") {
segments = monitorList.DatabaseConnectionString.Split('@');
if (segments.Length != 2)
continue;
results.Add($" # connectionString: \"{monitorList.DatabaseConnectionString}\"");
results.Add($" url: \"tcp://{segments[1].Split('/')[0]}\"");
results.Add(" conditions:");
results.Add(" - \"[CONNECTED] == true\"");
} else
throw new NotImplementedException();
results.Add(" alerts:");
results.Add(" - type: email");
results.Add(" description: \"healthcheck failed\"");
results.Add(" send-on-resolved: true");
results.Add(" - type: gotify");
results.Add(" description: \"healthcheck failed\"");
results.Add(" send-on-resolved: true");
results.Add(string.Empty);
}
File.WriteAllText($"{file}.yaml", string.Join(Environment.NewLine, results));
}
}

View File

@ -0,0 +1,693 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using File_Folder_Helper.Models;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Logging;
#if ShellProgressBar
using ShellProgressBar;
#endif
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250407 {
private record Record(string RelativePath,
long Size,
long Ticks);
private record Download(string Directory,
string Display,
string File,
long Size,
long Ticks,
string UniformResourceLocator);
private record Segment(Record? Left,
string? LeftDirectory,
Record? Right,
string RightDirectory,
string RootUniformResourceLocator);
private record Logic(char GreaterThan,
bool? LeftSideIsNewer,
int LeftSideIsNewerIndex,
bool? LeftSideOnly,
int LeftSideOnlyIndex,
char LessThan,
char Minus,
bool? NotEqualBut,
int NotEqualButIndex,
char Plus,
string[] Raw,
bool? RightSideIsNewer,
int RightSideIsNewerIndex,
bool? RightSideOnly,
int RightSideOnlyIndex) {
internal static Logic? Get(string[] segments) {
Logic? result;
bool check = true;
bool? notEqualBut;
bool? leftSideOnly;
bool? rightSideOnly;
bool? leftSideIsNewer;
const char plus = '+';
bool? rightSideIsNewer;
const char minus = '-';
const char lessThan = 'L';
const char greaterThan = 'G';
const int notEqualButIndex = 2;
const int leftSideOnlyIndex = 0;
const int rightSideOnlyIndex = 4;
const int leftSideIsNewerIndex = 1;
const int rightSideIsNewerIndex = 3;
if (string.IsNullOrEmpty(segments[leftSideOnlyIndex]))
leftSideOnly = null;
else if (segments[leftSideOnlyIndex][0] == plus)
leftSideOnly = true;
else if (segments[leftSideOnlyIndex][0] == minus)
leftSideOnly = false;
else {
check = false;
leftSideOnly = null;
}
if (string.IsNullOrEmpty(segments[leftSideIsNewerIndex]))
leftSideIsNewer = null;
else if (segments[leftSideIsNewerIndex][0] == greaterThan)
leftSideIsNewer = true;
else if (segments[leftSideIsNewerIndex][0] == lessThan)
leftSideIsNewer = false;
else {
check = false;
leftSideIsNewer = null;
}
if (string.IsNullOrEmpty(segments[notEqualButIndex]))
notEqualBut = null;
else if (segments[notEqualButIndex][0] == greaterThan)
notEqualBut = true;
else if (segments[notEqualButIndex][0] == lessThan)
notEqualBut = false;
else {
check = false;
notEqualBut = null;
}
if (string.IsNullOrEmpty(segments[rightSideIsNewerIndex]))
rightSideIsNewer = null;
else if (segments[rightSideIsNewerIndex][0] == greaterThan)
rightSideIsNewer = true;
else if (segments[rightSideIsNewerIndex][0] == lessThan)
rightSideIsNewer = false;
else {
check = false;
rightSideIsNewer = null;
}
if (string.IsNullOrEmpty(segments[rightSideOnlyIndex]))
rightSideOnly = null;
else if (segments[rightSideOnlyIndex][0] == plus)
rightSideOnly = true;
else if (segments[rightSideOnlyIndex][0] == minus)
rightSideOnly = false;
else {
check = false;
rightSideOnly = null;
}
result = !check ? null : new(GreaterThan: greaterThan,
LeftSideIsNewerIndex: leftSideIsNewerIndex,
LeftSideIsNewer: leftSideIsNewer,
LeftSideOnly: leftSideOnly,
LeftSideOnlyIndex: leftSideOnlyIndex,
LessThan: lessThan,
Minus: minus,
NotEqualBut: notEqualBut,
NotEqualButIndex: notEqualButIndex,
Plus: plus,
RightSideIsNewer: rightSideIsNewer,
RightSideIsNewerIndex: rightSideIsNewerIndex,
RightSideOnly: rightSideOnly,
Raw: segments,
RightSideOnlyIndex: rightSideOnlyIndex);
return result;
}
}
private record Review(Segment[]? AreEqual,
Segment[]? LeftSideIsNewer,
Segment[]? LeftSideOnly,
Segment[]? NotEqualBut,
Record[]? Records,
Segment[]? RightSideIsNewer,
Segment[]? RightSideOnly);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Review))]
private partial class ReviewCommonSourceGenerationContext : JsonSerializerContext {
}
internal static void Sync(ILogger<Worker> logger, List<string> args) {
Matcher matcher = new();
string fileName = $"{args[1]}.json";
string[] segments = args[5].Split('~');
string rightDirectory = Path.GetFullPath(args[0].Split('~')[0]);
Logic? logic = segments.Length != 5 ? null : Logic.Get(segments);
string includePatternsFile = Path.Combine(rightDirectory, args[2]);
string excludePatternsFile = Path.Combine(rightDirectory, args[3]);
string[] rootUniformResourceLocators = args.Count < 5 ? [] : args[4].Split('~');
matcher.AddIncludePatterns(!File.Exists(includePatternsFile) ? ["*"] : File.ReadAllLines(includePatternsFile));
matcher.AddExcludePatterns(!File.Exists(excludePatternsFile) ? ["System Volume Information"] : File.ReadAllLines(excludePatternsFile));
ReadOnlyCollection<Record> rightRecords = GetRecords(rightDirectory, matcher);
if (rightRecords.Count == 0)
logger.LogInformation("No source records");
else {
string checkFile = Path.Combine(rightDirectory, fileName);
Review review = new(AreEqual: null,
LeftSideIsNewer: null,
LeftSideOnly: null,
NotEqualBut: null,
Records: rightRecords.ToArray(),
RightSideIsNewer: null,
RightSideOnly: null);
string json = JsonSerializer.Serialize(review, ReviewCommonSourceGenerationContext.Default.Review);
WriteAllText(checkFile, json);
if (rootUniformResourceLocators.Length == 0)
logger.LogInformation("No urls");
else {
string format = NginxFileSystem.GetFormat();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
Sync(logger, rightDirectory, fileName, logic, rootUniformResourceLocators, rightRecords, format, timeZoneInfo);
}
}
}
private static ReadOnlyCollection<Record> GetRecords(string rightDirectory, Matcher matcher) {
List<Record> results = [
new(RelativePath: rightDirectory,
Size: 0,
Ticks: 0)];
Record record;
FileInfo fileInfo;
string relativePath;
ReadOnlyCollection<ReadOnlyCollection<string>> collection = Helpers.HelperDirectory.GetFilesCollection(rightDirectory, "*", "*");
foreach (ReadOnlyCollection<string> c in collection) {
foreach (string f in c) {
if (!matcher.Match(rightDirectory, f).HasMatches)
continue;
fileInfo = new(f);
if (fileInfo.Length == 0)
continue;
relativePath = Path.GetRelativePath(rightDirectory, fileInfo.FullName);
record = new(RelativePath: relativePath,
Size: fileInfo.Length,
Ticks: fileInfo.LastWriteTime.ToUniversalTime().Ticks);
results.Add(record);
}
}
return results.AsReadOnly();
}
private static void WriteAllText(string path, string text) {
string check = !File.Exists(path) ? string.Empty : File.ReadAllText(path);
if (check != text)
File.WriteAllText(path, text);
}
private static void Sync(ILogger<Worker> logger, string rightDirectory, string fileName, Logic? logic, string[] rootUniformResourceLocators, ReadOnlyCollection<Record> rightRecords, string format, TimeZoneInfo timeZoneInfo) {
Review? review;
foreach (string rootUniformResourceLocator in rootUniformResourceLocators) {
if (!rootUniformResourceLocator.StartsWith("https:"))
logger.LogInformation("Not supported URL <{url}>", rootUniformResourceLocator);
else {
review = GetJsonResponse(logger, fileName, rootUniformResourceLocator, format, timeZoneInfo);
if (review?.Records is null || review.Records.Length == 0)
logger.LogInformation("No response records");
else {
ReadOnlyCollection<Record> leftRecords = review.Records.AsReadOnly();
Sync(logger, rightDirectory, fileName, logic, rightRecords, rootUniformResourceLocator, leftRecords);
}
}
}
}
private static Review? GetJsonResponse(ILogger<Worker> logger, string fileName, string rootUniformResourceLocator, string format, TimeZoneInfo timeZoneInfo) {
Review? result;
Task<string> response;
HttpClient httpClient = new();
Task<HttpResponseMessage> httpResponseMessage;
string url = new(rootUniformResourceLocator.EndsWith('/') ?
$"{rootUniformResourceLocator[..^1]}/{fileName}" :
$"{rootUniformResourceLocator}/{fileName}");
httpResponseMessage = httpClient.GetAsync(rootUniformResourceLocator);
httpResponseMessage.Wait();
if (!httpResponseMessage.Result.IsSuccessStatusCode) {
logger.LogInformation("Failed to download: <{rootUniformResourceLocator}>;", rootUniformResourceLocator);
result = null;
} else {
response = httpResponseMessage.Result.Content.ReadAsStringAsync();
response.Wait();
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(response.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
bool isNewest = nginxFileSystems is not null && IsNewest(fileName, format, timeZoneInfo, new(rootUniformResourceLocator), nginxFileSystems);
if (nginxFileSystems is null) {
logger.LogInformation("Failed to parse: <{rootUniformResourceLocator}>;", rootUniformResourceLocator);
result = null;
} else if (!isNewest) {
logger.LogInformation("Outdated remote file: <{rootUniformResourceLocator}>;", rootUniformResourceLocator);
result = null;
} else {
httpResponseMessage = httpClient.GetAsync(url);
httpResponseMessage.Wait();
if (!httpResponseMessage.Result.IsSuccessStatusCode) {
logger.LogInformation("Failed to download: <{url}>;", url);
result = null;
} else {
response = httpResponseMessage.Result.Content.ReadAsStringAsync();
response.Wait();
result = string.IsNullOrEmpty(response.Result) ?
null :
JsonSerializer.Deserialize(response.Result, ReviewCommonSourceGenerationContext.Default.Review);
}
}
}
return result;
}
private static bool IsNewest(string fileName, string format, TimeZoneInfo timeZoneInfo, Uri uri, NginxFileSystem[] nginxFileSystems) {
bool result;
DateTime? match = null;
NginxFileSystem nginxFileSystem;
DateTime dateTime = DateTime.MinValue;
for (int i = 0; i < nginxFileSystems.Length; i++) {
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
if (nginxFileSystem.LastModified is not null && nginxFileSystem.Name == fileName) {
match = nginxFileSystem.LastModified.Value;
continue;
}
if (nginxFileSystem.LastModified is null || nginxFileSystem.LastModified <= dateTime)
continue;
dateTime = nginxFileSystem.LastModified.Value;
}
result = match is not null && match.Value > dateTime;
return result;
}
private static void Sync(ILogger<Worker> logger, string rightDirectory, string fileName, Logic? l, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
string json;
string checkFile;
HttpClient httpClient = new();
checkFile = Path.Combine(rightDirectory, fileName);
if (File.Exists(checkFile))
File.Delete(checkFile);
ReadOnlyCollection<Segment> areEqual = GetAreEqual(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
ReadOnlyCollection<Segment> notEqualBut = GetNotEqualBut(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
ReadOnlyCollection<Segment> leftSideOnly = GetLeftSideOnly(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
ReadOnlyCollection<Segment> rightSideOnly = GetRightSideOnly(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
ReadOnlyCollection<Segment> leftSideIsNewer = GetLeftSideIsNewer(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
ReadOnlyCollection<Segment> rightSideIsNewer = GetRightSideIsNewer(rightDirectory, fileName, rightRecords, rootUniformResourceLocators, leftRecords);
Review review = new(AreEqual: areEqual.ToArray(),
LeftSideIsNewer: leftSideIsNewer.ToArray(),
LeftSideOnly: leftSideOnly.ToArray(),
NotEqualBut: notEqualBut.ToArray(),
Records: null,
RightSideIsNewer: rightSideIsNewer.ToArray(),
RightSideOnly: rightSideOnly.ToArray());
json = JsonSerializer.Serialize(review, ReviewCommonSourceGenerationContext.Default.Review);
checkFile = Path.Combine(rightDirectory, fileName);
WriteAllText(checkFile, json);
if (notEqualBut.Count > 0 && l is not null && l.NotEqualBut is not null && l.Raw[l.NotEqualButIndex][0] == l.Minus && !l.NotEqualBut.Value)
logger.LogDebug("Doing nothing with {name}", nameof(Logic.NotEqualBut));
if (leftSideOnly.Count > 0 && l is not null && l.LeftSideOnly is not null && l.Raw[l.LeftSideOnlyIndex][0] == l.Minus && !l.LeftSideOnly.Value)
throw new NotImplementedException("Not possible with https!");
if (leftSideIsNewer.Count > 0 && l is not null && l.LeftSideIsNewer is not null && l.Raw[l.LeftSideIsNewerIndex][0] == l.LessThan && !l.LeftSideIsNewer.Value)
throw new NotImplementedException("Not possible with https!");
if (rightSideIsNewer.Count > 0 && l is not null && l.RightSideIsNewer is not null && l.Raw[l.RightSideIsNewerIndex][0] == l.LessThan && !l.RightSideIsNewer.Value)
throw new NotImplementedException("Not possible with https!");
if (rightSideOnly.Count > 0 && l is not null && l.RightSideOnly is not null && l.Raw[l.RightSideOnlyIndex][0] == l.Plus && l.RightSideOnly.Value)
throw new NotImplementedException("Not possible with https!");
if (rightSideOnly.Count > 0 && l is not null && l.RightSideOnly is not null && l.Raw[l.RightSideOnlyIndex][0] == l.Minus && !l.RightSideOnly.Value)
DoWork(logger, rightDirectory, httpClient, rightSideOnly, delete: true, download: false);
if (leftSideOnly.Count > 0 && l is not null && l.LeftSideOnly is not null && l.Raw[l.LeftSideOnlyIndex][0] == l.Plus && l.LeftSideOnly.Value)
DoWork(logger, rightDirectory, httpClient, leftSideOnly, delete: false, download: true);
if (leftSideIsNewer.Count > 0 && l is not null && l.LeftSideIsNewer is not null && l.Raw[l.LeftSideIsNewerIndex][0] == l.GreaterThan && l.LeftSideIsNewer.Value)
DoWork(logger, rightDirectory, httpClient, leftSideIsNewer, delete: true, download: true);
if (notEqualBut.Count > 0 && l is not null && l.NotEqualBut is not null && l.Raw[l.NotEqualButIndex][0] == l.Plus && l.NotEqualBut.Value)
DoWork(logger, rightDirectory, httpClient, notEqualBut, delete: true, download: true);
if (rightSideIsNewer.Count > 0 && l is not null && l.RightSideIsNewer is not null && l.Raw[l.RightSideIsNewerIndex][0] == l.GreaterThan && l.RightSideIsNewer.Value)
DoWork(logger, rightDirectory, httpClient, rightSideIsNewer, delete: true, download: true);
}
private static ReadOnlyCollection<Segment> GetAreEqual(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
double totalSeconds;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(rightRecords);
foreach (Record r in leftRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (!keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
totalSeconds = new TimeSpan(record.Ticks - r.Ticks).TotalSeconds;
if (record.Size != r.Size || totalSeconds is > 2 or < -2)
continue;
segment = new(Left: r,
LeftDirectory: checkDirectory,
Right: record,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(ReadOnlyCollection<Record> records) {
Dictionary<string, Record> results = [];
foreach (Record record in records)
results.Add(record.RelativePath, record);
return new(results);
}
private static ReadOnlyCollection<Segment> GetNotEqualBut(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
double totalSeconds;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(rightRecords);
foreach (Record r in leftRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (!keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
if (record.Size == r.Size)
continue;
totalSeconds = new TimeSpan(record.Ticks - r.Ticks).TotalSeconds;
if (totalSeconds is >= 2 or <= -2)
continue;
segment = new(Left: r,
LeftDirectory: checkDirectory,
Right: record,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Segment> GetLeftSideOnly(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(rightRecords);
foreach (Record r in leftRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
segment = new(Left: r,
LeftDirectory: checkDirectory,
Right: record,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Segment> GetRightSideOnly(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(leftRecords);
foreach (Record r in rightRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
segment = new(Left: record,
LeftDirectory: null,
Right: r,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Segment> GetLeftSideIsNewer(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
double totalSeconds;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(rightRecords);
foreach (Record r in leftRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (!keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
totalSeconds = new TimeSpan(record.Ticks - r.Ticks).TotalSeconds;
if (totalSeconds is > -2)
continue;
segment = new(Left: r,
LeftDirectory: checkDirectory,
Right: record,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Segment> GetRightSideIsNewer(string rightDirectory, string fileName, ReadOnlyCollection<Record> rightRecords, string rootUniformResourceLocators, ReadOnlyCollection<Record> leftRecords) {
List<Segment> results = [];
Record? record;
Segment segment;
double totalSeconds;
string? checkDirectory = null;
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(leftRecords);
foreach (Record r in rightRecords) {
if (checkDirectory is null && r.Size == 0 && r.Ticks == 0) {
checkDirectory = r.RelativePath;
continue;
}
if (r.RelativePath == rightDirectory || r.RelativePath == fileName)
continue;
if (!keyValuePairs.TryGetValue(r.RelativePath, out record))
continue;
totalSeconds = new TimeSpan(record.Ticks - r.Ticks).TotalSeconds;
if (totalSeconds is > -2)
continue;
segment = new(Left: record,
LeftDirectory: null,
Right: r,
RightDirectory: rightDirectory,
RootUniformResourceLocator: rootUniformResourceLocators);
results.Add(segment);
}
return results.AsReadOnly();
}
private static void DoWork(ILogger<Worker> logger, string rightDirectory, HttpClient httpClient, ReadOnlyCollection<Segment> segments, bool delete, bool download) {
long sum;
Record[] records = (from l in segments where l.Left is not null select l.Left).ToArray();
try { sum = records.Sum(l => l.Size); } catch (Exception) { sum = 0; }
string size = GetSizeWithSuffix(sum);
if (delete) {
logger.LogInformation("Starting to delete {count} file(s) [{sum}]", segments.Count, size);
DoDeletes(logger, rightDirectory, segments);
logger.LogInformation("Deleted {count} file(s) [{sum}]", segments.Count, size);
}
if (download) {
logger.LogInformation("Starting to download {count} file(s) [{sum}]", segments.Count, size);
DoDownloads(logger, rightDirectory, segments, httpClient);
logger.LogInformation("Downloaded {count} file(s) [{sum}]", segments.Count, size);
}
}
private static string GetSizeWithSuffix(long value) {
string result;
int i = 0;
string[] SizeSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
if (value < 0) {
result = "-" + GetSizeWithSuffix(-value);
} else {
while (Math.Round(value / 1024f) >= 1) {
value /= 1024;
i++;
}
result = string.Format("{0:n1} {1}", value, SizeSuffixes[i]);
}
return result;
}
private static string GetDurationWithSuffix(long ticks) {
string result;
TimeSpan timeSpan = new(DateTime.Now.Ticks - ticks);
if (timeSpan.TotalMilliseconds < 1000)
result = $"{timeSpan.Milliseconds} ms";
else if (timeSpan.TotalMilliseconds < 60000)
result = $"{Math.Floor(timeSpan.TotalSeconds)} s";
else if (timeSpan.TotalMilliseconds < 3600000)
result = $"{Math.Floor(timeSpan.TotalMinutes)} m";
else
result = $"{Math.Floor(timeSpan.TotalHours)} h";
return result;
}
private static void DoDeletes(ILogger<Worker> logger, string rightDirectory, ReadOnlyCollection<Segment> segments) {
Record? record;
string size;
string count = segments.Count.ToString("000000");
#if ShellProgressBar
ProgressBar progressBar = new(segments.Count, $"Deleting: {count};", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
for (int i = 0; i < segments.Count; i++) {
#if ShellProgressBar
progressBar.Tick();
#endif
record = segments[i].Right;
if (record is null)
continue;
size = GetSizeWithSuffix(record.Size);
try {
File.Delete(Path.Combine(rightDirectory, record.RelativePath));
logger.LogInformation("{i} of {count} - Deleted: <{RelativePath}> - {size};", i.ToString("000000"), count, record.RelativePath, size);
} catch (Exception) {
logger.LogInformation("Failed to delete: <{RelativePath}> - {size};", record.RelativePath, size);
}
}
#if ShellProgressBar
progressBar.Dispose();
#endif
}
private static void DoDownloads(ILogger<Worker> logger, string rightDirectory, ReadOnlyCollection<Segment> segments, HttpClient httpClient) {
int i = 0;
long ticks;
string size;
string duration;
DateTime dateTime;
Task<string> response;
string count = segments.Count.ToString("000000");
ReadOnlyCollection<Download> downloads = GetDownloads(rightDirectory, segments);
Task<HttpResponseMessage> httpResponseMessage;
#if ShellProgressBar
ProgressBar progressBar = new(downloads.Count, $"Downloading: {count};", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
foreach (Download download in downloads) {
#if ShellProgressBar
progressBar.Tick();
#endif
i += 1;
ticks = DateTime.Now.Ticks;
size = GetSizeWithSuffix(download.Size);
httpResponseMessage = httpClient.GetAsync(download.UniformResourceLocator);
httpResponseMessage.Wait(-1);
if (!httpResponseMessage.Result.IsSuccessStatusCode)
logger.LogInformation("Failed to download: <{checkURL}> - {size};", download.UniformResourceLocator, size);
else {
response = httpResponseMessage.Result.Content.ReadAsStringAsync();
response.Wait();
try {
File.WriteAllText(download.File, response.Result);
duration = GetDurationWithSuffix(ticks);
dateTime = new DateTime(download.Ticks).ToLocalTime();
File.SetLastWriteTime(download.File, dateTime);
logger.LogInformation("{i} of {count} - Downloaded: <{checkURL}> - {size} - {timeSpan};",
i.ToString("000000"),
count,
download.Display,
size,
duration);
} catch (Exception) {
logger.LogInformation("Failed to download: <{checkURL}> - {size};", download.UniformResourceLocator, size);
}
}
}
#if ShellProgressBar
progressBar.Dispose();
#endif
}
private static ReadOnlyCollection<Download> GetDownloads(string rightDirectory, ReadOnlyCollection<Segment> segments) {
List<Download> results = [];
string checkFile;
Download download;
string? checkDirectory;
List<Download> collection = [];
string? checkUniformResourceLocator;
foreach (Segment segment in segments) {
if (segment.Left is null)
continue;
checkFile = Path.Combine(rightDirectory, segment.Left.RelativePath);
checkDirectory = Path.GetDirectoryName(checkFile);
if (string.IsNullOrEmpty(checkDirectory))
continue;
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
if (File.Exists(checkFile) && new FileInfo(checkFile).Length == 0)
File.Delete(checkFile);
checkUniformResourceLocator = ConvertTo(segment.RootUniformResourceLocator, segment.Left.RelativePath);
if (string.IsNullOrEmpty(checkUniformResourceLocator))
continue;
download = new(Directory: checkDirectory,
Display: checkUniformResourceLocator[segment.RootUniformResourceLocator.Length..],
File: checkFile,
Size: segment.Left.Size,
Ticks: segment.Left.Ticks,
UniformResourceLocator: checkUniformResourceLocator);
collection.Add(download);
}
Download[] sorted = (from l in collection orderby l.Size select l).ToArray();
int stop = sorted.Length < 100 ? sorted.Length : 100;
for (int i = 0; i < stop; i++)
results.Add(sorted[i]);
for (int i = sorted.Length - 1; i > stop - 1; i--)
results.Add(sorted[i]);
if (collection.Count != results.Count)
throw new Exception();
return results.AsReadOnly();
}
private static string? ConvertTo(string rootURL, string relativePath) {
string? result = rootURL.EndsWith('/') ? rootURL[..^1] : rootURL;
string windowsRoot = "c:\\";
string windowsMock = $"{windowsRoot}{relativePath}";
string fileName = Path.GetFileName(windowsMock);
ReadOnlyCollection<string> directoryNames = Helpers.HelperDirectory.GetDirectoryNames(windowsMock);
foreach (string directoryName in directoryNames) {
if (directoryName == windowsRoot || directoryName == fileName)
continue;
result = $"{result}/{directoryName}";
}
result = result == rootURL ? null : $"{result}/{fileName}";
return result;
}
}

View File

@ -0,0 +1,47 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250421 {
internal static void FreeFileSyncChangeCreatedDate(ILogger<Worker> logger, List<string> args) {
string searchPattern = args[2];
string[] searchPatterns = args[3].Split('~');
string sourceDirectory = Path.GetFullPath(args[0]);
if (searchPatterns.Length != 2)
throw new NotImplementedException($"Not the correct number of {searchPatterns} were passed!");
string lastSyncSearch = $"{searchPatterns[0]}=\"";
string configurationFileSearch = $"{searchPatterns[1]}=\"";
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
if (files.Length != 1)
logger.LogWarning("<{files}>(s)", files.Length);
else
ChangeCreatedDate(lastSyncSearch, configurationFileSearch, files[0]);
}
private static void ChangeCreatedDate(string lastSyncSearch, string configurationFileSearch, string sourceFile) {
long epoch;
string lastSync;
string[] segments;
string[] segmentsB;
DateTime creationTime;
string configurationFile;
string[] lines = File.ReadAllLines(sourceFile);
foreach (string line in lines) {
segments = line.Split(lastSyncSearch);
if (segments.Length != 2)
continue;
segmentsB = line.Split(configurationFileSearch);
if (segmentsB.Length != 2)
continue;
lastSync = segments[1].Split('"')[0];
if (!long.TryParse(lastSync, out epoch) || epoch == 0)
continue;
configurationFile = segmentsB[1].Split('"')[0];
if (!File.Exists(configurationFile))
continue;
creationTime = new(DateTimeOffset.UnixEpoch.AddSeconds(epoch).ToLocalTime().Ticks);
File.SetCreationTime(configurationFile, creationTime);
}
}
}

View File

@ -0,0 +1,72 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250429 {
private record Record(string Directory, string File, bool FileExists);
internal static void WriteNginxFileSystem(ILogger<Worker> logger, List<string> args) {
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0]);
ReadOnlyCollection<Record> subDirectories = GetSubDirectories(searchPattern, sourceDirectory);
if (subDirectories.Count == 0)
logger.LogWarning("<{results}>(s)", subDirectories.Count);
else
WriteNginxFileSystem(searchPattern, subDirectories);
}
private static ReadOnlyCollection<Record> GetSubDirectories(string searchPattern, string sourceDirectory) {
List<Record> results = [];
bool exists;
Record record;
string checkFile;
string[] subDirectories;
string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories) {
subDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly);
foreach (string subDirectory in subDirectories) {
checkFile = Path.Combine(subDirectory, $"{searchPattern.Split('*')[^1]}.json");
exists = File.Exists(checkFile);
record = new(Directory: subDirectory, File: checkFile, FileExists: exists);
results.Add(record);
}
}
return results.OrderByDescending(l => l.FileExists).ToArray().AsReadOnly();
}
private static void WriteNginxFileSystem(string searchPattern, ReadOnlyCollection<Record> subDirectories) {
string lines;
string result;
string[] files;
FileInfo fileInfo;
List<string> results = [];
NginxFileSystem nginxFileSystem;
foreach (Record record in subDirectories) {
results.Clear();
files = Directory.GetFiles(record.Directory, searchPattern, SearchOption.AllDirectories);
foreach (string file in files) {
fileInfo = new(file);
nginxFileSystem = new(Name: fileInfo.FullName,
LastModified: null,
MTime: fileInfo.LastWriteTime.ToUniversalTime().ToString(),
URI: null,
Type: "file",
Length: fileInfo.Length);
results.Add(JsonSerializer.Serialize(nginxFileSystem, NginxFileSystemSingleLineSourceGenerationContext.Default.NginxFileSystem));
}
if (results.Count == 0)
continue;
result = $"[{Environment.NewLine}{string.Join($",{Environment.NewLine}", results)}{Environment.NewLine}]";
lines = !record.FileExists ? string.Empty : File.ReadAllText(record.File);
if (result == lines)
continue;
File.WriteAllText(record.File, result);
}
}
}

View File

@ -0,0 +1,84 @@
#if Html2pdf
using iText.Html2pdf;
#endif
using Microsoft.Extensions.Logging;
#if Selenium
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
#endif
namespace File_Folder_Helper.ADO2025.PI5;
internal static partial class Helper20250505 {
// <PackageReference Include="Selenium.WebDriver" Version="4.31.0" />
// <PackageReference Include="iText" Version="9.1.0" />
// <PackageReference Include="iText.bouncy-castle-adapter" Version="9.1.0" />
// <PackageReference Include="iText.commons" Version="9.1.0" />
// <PackageReference Include="iText.hyph" Version="9.1.0" />
// <PackageReference Include="iText.pdfhtml" Version="6.1.0" />
internal static void HyperTextMarkupLanguageToPortableDocumentFormat(ILogger<Worker> logger, List<string> args) {
if (args.Count == 999)
TestA();
if (args.Count == 999)
TestB();
if (args.Count != 999)
TestC(logger);
}
private static void TestA() {
#if Html2pdf
string inputFile = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".html");
string outputFile = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "a.pdf");
using (FileStream htmlSource = File.Open(inputFile, FileMode.Open))
using (FileStream pdfDest = File.Open(outputFile, FileMode.Create)) {
ConverterProperties converterProperties = new();
HtmlConverter.ConvertToPdf(htmlSource, pdfDest, converterProperties);
}
#endif
}
private static void TestB() {
#if Html2pdf
HttpClient httpClient = new();
Task<Stream> stream = httpClient.GetStreamAsync("https://ourrescue.org/");
stream.Wait();
string outputFile = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "b.pdf");
using (FileStream pdfDest = File.Open(outputFile, FileMode.Create)) {
ConverterProperties converterProperties = new();
HtmlConverter.ConvertToPdf(stream.Result, pdfDest, converterProperties);
}
#endif
}
private static void TestC(ILogger<Worker> logger) {
#if Selenium
EdgeOptions edgeOptions = new();
edgeOptions.AddArgument("--no-sandbox");
edgeOptions.AddArgument("--disable-gpu");
edgeOptions.AddArgument("--headless=new");
edgeOptions.AddArgument("--start-maximized");
edgeOptions.AddArgument("--profile-directory=Default");
edgeOptions.AddArgument("--browser-version 133.0.3065.82");
EdgeDriver edgeDriver = new(edgeOptions);
string outputFile = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".png");
try {
// edgeDriver.Navigate().GoToUrl("https://ourrescue.org/");
// edgeDriver.Navigate().GoToUrl("https://intranet.infineon.com/");
edgeDriver.Navigate().GoToUrl("https://messa020ec.infineon.com:50205/ProductionReport/DailyReport");
int fullWidth = int.Parse(edgeDriver.ExecuteScript("return document.body.parentNode.scrollWidth").ToString());
int fullHeight = int.Parse(edgeDriver.ExecuteScript("return document.body.parentNode.scrollHeight").ToString());
edgeDriver.Manage().Window.Size = new(fullWidth, fullHeight);
Screenshot screenshot = edgeDriver.GetScreenshot();
screenshot.SaveAsFile(outputFile);
} catch (Exception ex) {
logger.LogError(ex, ex.Message);
}
edgeDriver.Close();
#endif
}
}

381
ADO2025/PI6/.editorconfig Normal file
View File

@ -0,0 +1,381 @@
[*.md]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.csproj]
end_of_line = crlf
file_header_template = unset
indent_size = 2
indent_style = space
insert_final_newline = false
root = true
tab_width = 2
[*.cs]
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
csharp_prefer_braces = false
csharp_prefer_qualified_reference = true:error
csharp_prefer_simple_default_expression = true:warning
csharp_prefer_simple_using_statement = true:warning
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true
csharp_style_conditional_delegate_call = true
csharp_style_deconstructed_variable_declaration = false
csharp_style_expression_bodied_accessors = when_on_single_line:warning
csharp_style_expression_bodied_constructors = when_on_single_line:warning
csharp_style_expression_bodied_indexers = when_on_single_line:warning
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
csharp_style_expression_bodied_local_functions = when_on_single_line:warning
csharp_style_expression_bodied_methods = when_on_single_line:warning
csharp_style_expression_bodied_operators = when_on_single_line:warning
csharp_style_expression_bodied_properties = when_on_single_line:warning
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = false
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = true:warning
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_using_directive_placement = outside_namespace
dotnet_analyzer_diagnostic.category-Design.severity = error
dotnet_analyzer_diagnostic.category-Documentation.severity = error
dotnet_analyzer_diagnostic.category-Globalization.severity = none
dotnet_analyzer_diagnostic.category-Interoperability.severity = error
dotnet_analyzer_diagnostic.category-Maintainability.severity = error
dotnet_analyzer_diagnostic.category-Naming.severity = none
dotnet_analyzer_diagnostic.category-Performance.severity = none
dotnet_analyzer_diagnostic.category-Reliability.severity = error
dotnet_analyzer_diagnostic.category-Security.severity = error
dotnet_analyzer_diagnostic.category-SingleFile.severity = error
dotnet_analyzer_diagnostic.category-Style.severity = error
dotnet_analyzer_diagnostic.category-Usage.severity = error
dotnet_code_quality_unused_parameters = all
dotnet_code_quality_unused_parameters = non_public
dotnet_code_quality.CAXXXX.api_surface = private, internal
dotnet_diagnostic.CA1001.severity = error # CA1001: Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = error # CA1051: Do not declare visible instance fields
dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1513.severity = warning # Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1825.severity = warning # CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1829.severity = error # CA1829: Use Length/Count property instead of Count() when available
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance
dotnet_diagnostic.CA1862.severity = warning # CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'
dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime
dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
dotnet_diagnostic.IDE0005.severity = error # Using directive is unnecessary
dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010)
dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
dotnet_diagnostic.IDE0048.severity = none # Parentheses preferences (IDE0047 and IDE0048)
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049)
dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ]
dotnet_diagnostic.IDE0058.severity = error # IDE0058: Expression value is never used
dotnet_diagnostic.IDE0060.severity = error # IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130)
dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290)
dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified
dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified
dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified
dotnet_diagnostic.IDE2000.severity = error # IDE2000: Allow multiple blank lines
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method
dotnet_naming_rule.class_should_be_pascal_case.severity = warning
dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.class_should_be_pascal_case.symbols = class
dotnet_naming_rule.delegate_should_be_pascal_case.severity = warning
dotnet_naming_rule.delegate_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.delegate_should_be_pascal_case.symbols = delegate
dotnet_naming_rule.enum_should_be_pascal_case.severity = warning
dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
dotnet_naming_rule.event_should_be_pascal_case.severity = warning
dotnet_naming_rule.event_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.event_should_be_pascal_case.symbols = event
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.method_should_be_pascal_case.severity = warning
dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.method_should_be_pascal_case.symbols = method
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.private_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.private_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_method_should_be_pascal_case.symbols = private_method
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.symbols = private_or_internal_static_field
dotnet_naming_rule.property_should_be_pascal_case.severity = warning
dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.property_should_be_pascal_case.symbols = property
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.severity = warning
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.style = private_of_internal_field
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.symbols = public_or_protected_field
dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field
dotnet_naming_rule.static_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_method_should_be_pascal_case.symbols = static_method
dotnet_naming_rule.struct_should_be_pascal_case.severity = warning
dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.private_of_internal_field.capitalization = pascal_case
dotnet_naming_style.private_of_internal_field.required_prefix = _
dotnet_naming_style.private_of_internal_field.required_suffix =
dotnet_naming_style.private_of_internal_field.word_separator =
dotnet_naming_symbols.abstract_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.abstract_method.applicable_kinds = method
dotnet_naming_symbols.abstract_method.required_modifiers = abstract
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.class.applicable_kinds = class
dotnet_naming_symbols.class.required_modifiers =
dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.delegate.applicable_kinds = delegate
dotnet_naming_symbols.delegate.required_modifiers =
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enum.applicable_kinds = enum
dotnet_naming_symbols.enum.required_modifiers =
dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.event.applicable_kinds = event
dotnet_naming_symbols.event.required_modifiers =
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.method.applicable_accessibilities = public
dotnet_naming_symbols.method.applicable_kinds = method
dotnet_naming_symbols.method.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_method.applicable_accessibilities = private
dotnet_naming_symbols.private_method.applicable_kinds = method
dotnet_naming_symbols.private_method.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.property.applicable_kinds = property
dotnet_naming_symbols.property.required_modifiers =
dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected
dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field
dotnet_naming_symbols.public_or_protected_field.required_modifiers =
dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_field.applicable_kinds = field
dotnet_naming_symbols.static_field.required_modifiers = static
dotnet_naming_symbols.static_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.static_method.applicable_kinds = method
dotnet_naming_symbols.static_method.required_modifiers = static
dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.struct.applicable_kinds = struct
dotnet_naming_symbols.struct.required_modifiers =
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.required_modifiers =
dotnet_remove_unnecessary_suppression_exclusions = 0
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = true
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = false
dotnet_style_prefer_conditional_expression_over_return = false
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_qualification_for_event = false:error
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false:error
dotnet_style_qualification_for_property = false:error
dotnet_style_readonly_field = true:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members
end_of_line = crlf
file_header_template = unset
indent_size = 4
indent_style = space
insert_final_newline = false
root = true
tab_width = 4
# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
# https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
# https://github.com/dotnet/project-system/blob/main/.editorconfig
# Question
csharp_prefer_simple_using_statement = false # Question
csharp_style_expression_bodied_constructors = when_on_single_line:none # Question
csharp_style_expression_bodied_properties = true # Question
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning # Question
csharp_style_pattern_matching_over_as_with_null_check = false # Question
csharp_style_prefer_pattern_matching = false # Question
csharp_style_prefer_range_operator = false # Question
csharp_style_prefer_switch_expression = false # Question
csharp_style_unused_value_assignment_preference = unused_local_variable # Question
csharp_style_unused_value_expression_statement_preference = false # Question
csharp_style_var_elsewhere = false:none # Question
csharp_style_var_for_built_in_types = false:none # Question
csharp_style_var_when_type_is_apparent = false:warning # Question
dotnet_diagnostic.CA1001.severity = none # Question - Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = none # Question - Do not declare visible instance fields
dotnet_diagnostic.CA1416.severity = none # Question - This call site is reachable on all platforms.
dotnet_diagnostic.CA1510.severity = none # Question - Use
dotnet_diagnostic.CA1834.severity = none # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1860.severity = none # Question - Avoid using
dotnet_diagnostic.CA1862.severity = none # Question - Prefer using
dotnet_diagnostic.CA2208.severity = none # Question - Instantiate argument exceptions correctly
dotnet_diagnostic.CA2211.severity = none # Question - Non-constant fields should not be visible
dotnet_diagnostic.CA2249.severity = none # Question - Use
dotnet_diagnostic.CA2253.severity = none # Question - Named placeholders should not be numeric values
dotnet_diagnostic.CS0103.severity = none # Question - The name
dotnet_diagnostic.CS0168.severity = none # Question - The variable
dotnet_diagnostic.CS0219.severity = none # Question - The variable
dotnet_diagnostic.CS0612.severity = none # Question - is obsolete
dotnet_diagnostic.CS0618.severity = none # Question - Compiler Warning (level 2)
dotnet_diagnostic.CS0659.severity = none # Question - Compiler Warning (level 3)
dotnet_diagnostic.CS8019.severity = warning # Question - Unnecessary using directive.
dotnet_diagnostic.CS8600.severity = none # Question - Converting null literal or possible null value to non-nullable type
dotnet_diagnostic.CS8602.severity = none # Question - Dereference of a possibly null reference.
dotnet_diagnostic.CS8603.severity = none # Question - Possible null reference return
dotnet_diagnostic.CS8604.severity = none # Question - Possible null reference argument for parameter.
dotnet_diagnostic.CS8618.severity = none # Question - Non-nullable variable must contain a non-null value when exiting constructor
dotnet_diagnostic.CS8625.severity = none # Question - Cannot convert null literal to non-nullable reference type.
dotnet_diagnostic.CS8629.severity = none # Question - Nullable value type may be null
dotnet_diagnostic.CS8765.severity = none # Question - Nullability of type of parameter
dotnet_diagnostic.IDE0005.severity = none # Question - Remove unnecessary using directives
dotnet_diagnostic.IDE0008.severity = warning # Question - Use explicit type instead of
dotnet_diagnostic.IDE0017.severity = none # Question - Object initialization can be simplified
dotnet_diagnostic.IDE0019.severity = none # Question - Use pattern matching
dotnet_diagnostic.IDE0021.severity = none # Question - Use expression body for constructor
dotnet_diagnostic.IDE0022.severity = none # Question - Use expression body for method
dotnet_diagnostic.IDE0025.severity = none # Question - Use expression body for property
dotnet_diagnostic.IDE0027.severity = none # Question - Use expression body for accessor
dotnet_diagnostic.IDE0028.severity = none # Question - Use collection initializers or expressions
dotnet_diagnostic.IDE0031.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0032.severity = none # Question - Use auto property
dotnet_diagnostic.IDE0037.severity = none # Question - Member name can be simplified
dotnet_diagnostic.IDE0041.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0047.severity = none # Question - Parentheses preferences
dotnet_diagnostic.IDE0049.severity = warning # Question - Name can be simplified
dotnet_diagnostic.IDE0051.severity = none # Question - Remove unused private member
dotnet_diagnostic.IDE0053.severity = none # Question - Use expression body for lambdas
dotnet_diagnostic.IDE0054.severity = none # Question - Use compound assignment
dotnet_diagnostic.IDE0055.severity = none # Question - Formatting rule
dotnet_diagnostic.IDE0057.severity = none # Question - Substring can be simplified
dotnet_diagnostic.IDE0058.severity = none # Question - Remove unnecessary expression value
dotnet_diagnostic.IDE0059.severity = none # Question - Unnecessary assignment of a value to
dotnet_diagnostic.IDE0060.severity = none # Question - Remove unused parameter
dotnet_diagnostic.IDE0063.severity = none # Question - Use simple
dotnet_diagnostic.IDE0065.severity = none # Question -
dotnet_diagnostic.IDE0066.severity = none # Question - Use
dotnet_diagnostic.IDE0078.severity = none # Question - Use pattern matching (may change code meaning)
dotnet_diagnostic.IDE0090.severity = warning # Question - Simplify new expression
dotnet_diagnostic.IDE0100.severity = error # Question - Remove redundant equality
dotnet_diagnostic.IDE0160.severity = warning # Question - Use block-scoped namespace
dotnet_diagnostic.IDE0161.severity = warning # Question - Namespace declaration preferences
dotnet_diagnostic.IDE0270.severity = none # Question - Null check can be simplified
dotnet_diagnostic.IDE0300.severity = none # Question - Collection initialization can be simplified
dotnet_diagnostic.IDE1006.severity = none # Question - Use collection expression for builder dotnet_style_prefer_collection_expression
dotnet_style_null_propagation = false # Question
dotnet_style_object_initializer = false # Question
dotnet_style_prefer_auto_properties = false # Question
dotnet_style_allow_statement_immediately_after_block_experimental = true # Question
dotnet_style_prefer_inferred_anonymous_type_member_names = false:warning # Question
dotnet_style_prefer_is_null_check_over_reference_equality_method = false # Question

277
ADO2025/PI6/Envelope.cs Normal file
View File

@ -0,0 +1,277 @@
#nullable disable
#pragma warning disable CS8603
#pragma warning disable CS8632
#pragma warning disable IDE1006
namespace IFX.Shared.PasteSpecialXml.EAF.XML.API.Envelope;
// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.w3.org/2003/05/soap-envelope")]
[System.Xml.Serialization.XmlRoot(Namespace = "http://www.w3.org/2003/05/soap-envelope", IsNullable = false)]
public partial class Envelope
{
private EnvelopeHeader? headerField;
private EnvelopeBody? bodyField;
/// <remarks/>
public EnvelopeHeader Header
{
get => this.headerField;
set => this.headerField = value;
}
/// <remarks/>
public EnvelopeBody Body
{
get => this.bodyField;
set => this.bodyField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public partial class EnvelopeHeader
{
private Sequence? sequenceField;
private SequenceAcknowledgement? sequenceAcknowledgementField;
private Action? actionField;
private string? relatesToField;
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public Sequence Sequence
{
get => this.sequenceField;
set => this.sequenceField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public SequenceAcknowledgement SequenceAcknowledgement
{
get => this.sequenceAcknowledgementField;
set => this.sequenceAcknowledgementField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://www.w3.org/2005/08/addressing")]
public Action Action
{
get => this.actionField;
set => this.actionField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://www.w3.org/2005/08/addressing")]
public string RelatesTo
{
get => this.relatesToField;
set => this.relatesToField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
[System.Xml.Serialization.XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm", IsNullable = false)]
public partial class Sequence
{
private string? identifierField;
private byte messageNumberField;
private object? lastMessageField;
private byte mustUnderstandField;
/// <remarks/>
public string Identifier
{
get => this.identifierField;
set => this.identifierField = value;
}
/// <remarks/>
public byte MessageNumber
{
get => this.messageNumberField;
set => this.messageNumberField = value;
}
/// <remarks/>
public object LastMessage
{
get => this.lastMessageField;
set => this.lastMessageField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public byte mustUnderstand
{
get => this.mustUnderstandField;
set => this.mustUnderstandField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
[System.Xml.Serialization.XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm", IsNullable = false)]
public partial class SequenceAcknowledgement
{
private string? identifierField;
private SequenceAcknowledgementAcknowledgementRange? acknowledgementRangeField;
private byte bufferRemainingField;
/// <remarks/>
public string Identifier
{
get => this.identifierField;
set => this.identifierField = value;
}
/// <remarks/>
public SequenceAcknowledgementAcknowledgementRange AcknowledgementRange
{
get => this.acknowledgementRangeField;
set => this.acknowledgementRangeField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://schemas.microsoft.com/ws/2006/05/rm")]
public byte BufferRemaining
{
get => this.bufferRemainingField;
set => this.bufferRemainingField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public partial class SequenceAcknowledgementAcknowledgementRange
{
private byte lowerField;
private byte upperField;
/// <remarks/>
[System.Xml.Serialization.XmlAttribute()]
public byte Lower
{
get => this.lowerField;
set => this.lowerField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlAttribute()]
public byte Upper
{
get => this.upperField;
set => this.upperField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.w3.org/2005/08/addressing")]
[System.Xml.Serialization.XmlRoot(Namespace = "http://www.w3.org/2005/08/addressing", IsNullable = false)]
public partial class Action
{
private byte mustUnderstandField;
private string? valueField;
/// <remarks/>
[System.Xml.Serialization.XmlAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public byte mustUnderstand
{
get => this.mustUnderstandField;
set => this.mustUnderstandField = value;
}
/// <remarks/>
[System.Xml.Serialization.XmlText()]
public string Value
{
get => this.valueField;
set => this.valueField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://www.w3.org/2003/05/soap-envelope")]
public partial class EnvelopeBody
{
private CreateSequenceResponse? createSequenceResponseField;
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public CreateSequenceResponse CreateSequenceResponse
{
get => this.createSequenceResponseField;
set => this.createSequenceResponseField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
[System.Xml.Serialization.XmlRoot(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm", IsNullable = false)]
public partial class CreateSequenceResponse
{
private string? identifierField;
private CreateSequenceResponseAccept? acceptField;
/// <remarks/>
public string Identifier
{
get => this.identifierField;
set => this.identifierField = value;
}
/// <remarks/>
public CreateSequenceResponseAccept Accept
{
get => this.acceptField;
set => this.acceptField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public partial class CreateSequenceResponseAccept
{
private CreateSequenceResponseAcceptAcksTo? acksToField;
/// <remarks/>
public CreateSequenceResponseAcceptAcksTo AcksTo
{
get => this.acksToField;
set => this.acksToField = value;
}
}
/// <remarks/>
[Serializable()]
[System.ComponentModel.DesignerCategory("code")]
[System.Xml.Serialization.XmlType(AnonymousType = true, Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")]
public partial class CreateSequenceResponseAcceptAcksTo
{
private string? addressField;
/// <remarks/>
[System.Xml.Serialization.XmlElement(Namespace = "http://www.w3.org/2005/08/addressing")]
public string Address
{
get => this.addressField;
set => this.addressField = value;
}
}

View File

@ -0,0 +1,531 @@
using System.Collections.ObjectModel;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Web;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250519 {
private record RelativePath(string LeftDirectory,
string? RightDirectory,
Record[] Records) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, Helper20250519RelativePath.Default.RelativePath);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(RelativePath))]
private partial class Helper20250519RelativePath : JsonSerializerContext {
}
private record Review(Segment[]? AreEqual,
Segment[]? LeftSideIsNewer,
Segment[]? LeftSideOnly,
Segment[]? NotEqualBut,
Record[]? Records,
Segment[]? RightSideIsNewer,
Segment[]? RightSideOnly) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, Helper20250519Review.Default.Review);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Review))]
private partial class Helper20250519Review : JsonSerializerContext {
}
private record Record(string RelativePath,
long Size,
long Ticks);
private record Segment(Record? Left,
Record? Right);
private record Verb(string Directory,
string Display,
string File,
string Multipart,
string RelativePath,
long Size,
long Ticks,
string UrlEncodedFile);
private record Input(string RightDirectory,
string LeftDirectory,
string IncludePatternsFile,
string ExcludePatternsFile,
string[] BaseAddresses,
string Page,
string[] Segments) {
private static string GetDirectory(List<string> args) =>
Path.GetFullPath(args[0].Split('~')[0]);
internal static Input Get(List<string> args) =>
new(RightDirectory: GetDirectory(args),
LeftDirectory: Path.GetFullPath(args[2].Split('~')[0]),
IncludePatternsFile: Path.Combine(GetDirectory(args), ".vscode", args[3]),
ExcludePatternsFile: Path.Combine(GetDirectory(args), ".vscode", args[4]),
BaseAddresses: args.Count < 5 ? [] : args[5].Split('~'),
Page: args[6],
Segments: args[9].Split('~'));
public override string ToString() {
string result = JsonSerializer.Serialize(this, Helper20250519Input.Default.Input);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Input))]
private partial class Helper20250519Input : JsonSerializerContext {
}
private record Logic(string Comment,
char GreaterThan,
bool? LeftSideIsNewer,
int LeftSideIsNewerIndex,
bool? LeftSideOnly,
int LeftSideOnlyIndex,
char LessThan,
char Minus,
bool? NotEqualBut,
int NotEqualButIndex,
char Plus,
string[] Raw,
bool? RightSideIsNewer,
int RightSideIsNewerIndex,
bool? RightSideOnly,
int RightSideOnlyIndex) {
internal static Logic? Get(string[] segments) {
Logic? result;
bool check = true;
bool? notEqualBut;
bool? leftSideOnly;
bool? rightSideOnly;
bool? leftSideIsNewer;
const char plus = '+';
bool? rightSideIsNewer;
const char minus = '-';
const char lessThan = 'L';
const int commentIndex = 5;
const char greaterThan = 'G';
const int notEqualButIndex = 2;
const int leftSideOnlyIndex = 0;
const int rightSideOnlyIndex = 4;
const int leftSideIsNewerIndex = 1;
const int rightSideIsNewerIndex = 3;
string comment = segments[commentIndex];
if (string.IsNullOrEmpty(segments[leftSideOnlyIndex])) {
leftSideOnly = null;
} else if (segments[leftSideOnlyIndex][0] == plus) {
leftSideOnly = true;
} else if (segments[leftSideOnlyIndex][0] == minus) {
leftSideOnly = false;
} else {
check = false;
leftSideOnly = null;
}
if (string.IsNullOrEmpty(segments[leftSideIsNewerIndex])) {
leftSideIsNewer = null;
} else if (segments[leftSideIsNewerIndex][0] == greaterThan) {
leftSideIsNewer = true;
} else if (segments[leftSideIsNewerIndex][0] == lessThan) {
leftSideIsNewer = false;
} else {
check = false;
leftSideIsNewer = null;
}
if (string.IsNullOrEmpty(segments[notEqualButIndex])) {
notEqualBut = null;
} else if (segments[notEqualButIndex][0] == greaterThan) {
notEqualBut = true;
} else if (segments[notEqualButIndex][0] == lessThan) {
notEqualBut = false;
} else {
check = false;
notEqualBut = null;
}
if (string.IsNullOrEmpty(segments[rightSideIsNewerIndex])) {
rightSideIsNewer = null;
} else if (segments[rightSideIsNewerIndex][0] == greaterThan) {
rightSideIsNewer = true;
} else if (segments[rightSideIsNewerIndex][0] == lessThan) {
rightSideIsNewer = false;
} else {
check = false;
rightSideIsNewer = null;
}
if (string.IsNullOrEmpty(segments[rightSideOnlyIndex])) {
rightSideOnly = null;
} else if (segments[rightSideOnlyIndex][0] == plus) {
rightSideOnly = true;
} else if (segments[rightSideOnlyIndex][0] == minus) {
rightSideOnly = false;
} else {
check = false;
rightSideOnly = null;
}
result = !check ? null : new(Comment: comment,
GreaterThan: greaterThan,
LeftSideIsNewerIndex: leftSideIsNewerIndex,
LeftSideIsNewer: leftSideIsNewer,
LeftSideOnly: leftSideOnly,
LeftSideOnlyIndex: leftSideOnlyIndex,
LessThan: lessThan,
Minus: minus,
NotEqualBut: notEqualBut,
NotEqualButIndex: notEqualButIndex,
Plus: plus,
RightSideIsNewer: rightSideIsNewer,
RightSideIsNewerIndex: rightSideIsNewerIndex,
RightSideOnly: rightSideOnly,
Raw: segments,
RightSideOnlyIndex: rightSideOnlyIndex);
return result;
}
public override string ToString() {
string result = JsonSerializer.Serialize(this, Helper20250519Logic.Default.Logic);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Logic))]
private partial class Helper20250519Logic : JsonSerializerContext {
}
internal static void LiveSync(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
if (args[2].EndsWith("input.json") && !File.Exists(args[2])) {
File.WriteAllText(args[2], "{}");
}
string? json = !args[2].EndsWith("input.json") ? null : File.ReadAllText(args[2]);
Input input = string.IsNullOrEmpty(json)
? Input.Get(args)
: JsonSerializer.Deserialize(json, Helper20250519Input.Default.Input)
?? throw new Exception();
Logic? logic = input.Segments.Length != 6 ? null : Logic.Get(input.Segments);
if (logic is null || input.BaseAddresses.Length == 0) {
logger.LogInformation($"Invalid input!{Environment.NewLine}{input}");
} else {
Matcher matcher = GetMatcher(input.ExcludePatternsFile, input.IncludePatternsFile);
ReadOnlyCollection<Record> records = GetRecords(input.RightDirectory, matcher);
if (records.Count == 0) {
logger.LogInformation("No source records");
} else {
RelativePath relativePath = new(LeftDirectory: input.LeftDirectory, RightDirectory: input.RightDirectory, Records: records.ToArray());
json = JsonSerializer.Serialize(relativePath, Helper20250519RelativePath.Default.RelativePath);
if (string.IsNullOrEmpty(json)) {
LiveSync180(logger, logic, input.BaseAddresses, input.Page, relativePath);
} else {
File.WriteAllText(Path.Combine(input.RightDirectory, ".vscode", $"{nameof(RelativePath)}.json"), json);
}
}
}
}
private static Matcher GetMatcher(string excludePatternsFile, string includePatternsFile) {
Matcher result = new();
result.AddIncludePatterns(!File.Exists(includePatternsFile) ? ["*"] : File.ReadAllLines(includePatternsFile));
result.AddExcludePatterns(!File.Exists(excludePatternsFile) ? ["System Volume Information"] : File.ReadAllLines(excludePatternsFile));
return result;
}
private static ReadOnlyCollection<Record> GetRecords(string directory, Matcher matcher) {
List<Record> results = [
new(RelativePath: directory,
Size: 0,
Ticks: 0)];
Record record;
FileInfo fileInfo;
string relativePath;
ReadOnlyCollection<ReadOnlyCollection<string>> collection = Helpers.HelperDirectory.GetFilesCollection(directory, "*", "*");
foreach (ReadOnlyCollection<string> c in collection) {
foreach (string f in c) {
if (!matcher.Match(directory, f).HasMatches) {
continue;
}
fileInfo = new(f);
if (fileInfo.Length == 0) {
continue;
}
relativePath = Path.GetRelativePath(directory, fileInfo.FullName);
record = new(RelativePath: relativePath,
Size: fileInfo.Length,
Ticks: fileInfo.LastWriteTime.ToUniversalTime().Ticks);
results.Add(record);
}
}
return results.AsReadOnly();
}
private static void LiveSync180(ILogger<Worker> logger, Logic logic, string[] baseAddresses, string page, RelativePath relativePath) {
Review? review;
Task<string> response;
Task<HttpResponseMessage> httpResponseMessage;
string json = JsonSerializer.Serialize(relativePath, Helper20250519RelativePath.Default.RelativePath);
foreach (string baseAddress in baseAddresses) {
if (!baseAddress.StartsWith("http:")) {
logger.LogInformation("Not supported URL <{url}>", baseAddress);
} else {
HttpClient httpClient = new();
httpClient.BaseAddress = new(baseAddress);
StringContent stringContent = new(json, Encoding.UTF8, "application/json");
httpResponseMessage = httpClient.PostAsync(page, stringContent);
httpResponseMessage.Wait();
if (!httpResponseMessage.Result.IsSuccessStatusCode) {
logger.LogInformation("Failed to download: <{uniformResourceLocator}>;", httpClient.BaseAddress);
} else {
response = httpResponseMessage.Result.Content.ReadAsStringAsync();
response.Wait();
review = JsonSerializer.Deserialize(response.Result, Helper20250519Review.Default.Review);
if (review is null) {
logger.LogInformation("Failed to download: <{uniformResourceLocator}>;", httpClient.BaseAddress);
continue;
}
LiveSync(logger, logic, page, relativePath, httpClient, review);
}
}
}
}
private static void LiveSync(ILogger<Worker> logger, Logic l, string page, RelativePath relativePath, HttpClient httpClient, Review review) {
if (review.NotEqualBut?.Length > 0 && l is not null && l.NotEqualBut is not null && l.Raw[l.NotEqualButIndex][0] == l.Minus && !l.NotEqualBut.Value) {
logger.LogDebug("Doing nothing with {name}", nameof(Logic.NotEqualBut));
}
if (review.LeftSideOnly?.Length > 0 && l is not null && l.LeftSideOnly is not null && l.Raw[l.LeftSideOnlyIndex][0] == l.Minus && !l.LeftSideOnly.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.LeftDirectory, (from x in review.LeftSideOnly select x.Left).ToArray().AsReadOnly(), HttpMethod.Delete, delete: false);
}
if (review.LeftSideIsNewer?.Length > 0 && l is not null && l.LeftSideIsNewer is not null && l.Raw[l.LeftSideIsNewerIndex][0] == l.LessThan && !l.LeftSideIsNewer.Value) {
throw new Exception(); // LiveSync(logger, page, relativePath, httpClient, relativePath.LeftDirectory, (from x in review.LeftSideIsNewer select x.Left).ToArray().AsReadOnly(), HttpMethod.Patch, delete: true);
}
if (review.RightSideIsNewer?.Length > 0 && l is not null && l.RightSideIsNewer is not null && l.Raw[l.RightSideIsNewerIndex][0] == l.LessThan && !l.RightSideIsNewer.Value) {
throw new Exception(); // LiveSync(logger, page, relativePath, httpClient, relativePath.RightDirectory, (from x in review.RightSideIsNewer select x.Right).ToArray().AsReadOnly(), HttpMethod.Patch, delete: true);
}
if (review.RightSideOnly?.Length > 0 && l is not null && l.RightSideOnly is not null && l.Raw[l.RightSideOnlyIndex][0] == l.Plus && l.RightSideOnly.Value) {
throw new Exception(); // LiveSync(logger, page, relativePath, httpClient, relativePath.RightDirectory, (from x in review.RightSideOnly select x.Right).ToArray().AsReadOnly(), HttpMethod.Put, delete: false);
}
if (review.RightSideOnly?.Length > 0 && l is not null && l.RightSideOnly is not null && l.Raw[l.RightSideOnlyIndex][0] == l.Minus && !l.RightSideOnly.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.RightDirectory, (from x in review.RightSideOnly select x.Right).ToArray().AsReadOnly(), httpMethod: null, delete: true);
}
if (review.LeftSideOnly?.Length > 0 && l is not null && l.LeftSideOnly is not null && l.Raw[l.LeftSideOnlyIndex][0] == l.Plus && l.LeftSideOnly.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.LeftDirectory, (from x in review.LeftSideOnly select x.Left).ToArray().AsReadOnly(), HttpMethod.Get, delete: false);
}
if (review.LeftSideIsNewer?.Length > 0 && l is not null && l.LeftSideIsNewer is not null && l.Raw[l.LeftSideIsNewerIndex][0] == l.GreaterThan && l.LeftSideIsNewer.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.LeftDirectory, (from x in review.LeftSideIsNewer select x.Left).ToArray().AsReadOnly(), HttpMethod.Get, delete: true);
}
if (review.NotEqualBut?.Length > 0 && l is not null && l.NotEqualBut is not null && l.Raw[l.NotEqualButIndex][0] == l.Plus && l.NotEqualBut.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.LeftDirectory, (from x in review.NotEqualBut select x.Left).ToArray().AsReadOnly(), HttpMethod.Get, delete: true);
}
if (review.RightSideIsNewer?.Length > 0 && l is not null && l.RightSideIsNewer is not null && l.Raw[l.RightSideIsNewerIndex][0] == l.GreaterThan && l.RightSideIsNewer.Value) {
LiveSync(logger, page, relativePath, httpClient, relativePath.RightDirectory, (from x in review.RightSideIsNewer select x.Right).ToArray().AsReadOnly(), HttpMethod.Get, delete: true);
}
}
private static void LiveSync(ILogger<Worker> logger, string page, RelativePath relativePath, HttpClient httpClient, string directory, ReadOnlyCollection<Record> records, HttpMethod? httpMethod, bool delete) {
long sum;
try { sum = records.Sum(l => l.Size); } catch (Exception) { sum = 0; }
string size = GetSizeWithSuffix(sum);
if (delete) {
logger.LogInformation("Starting to delete {count} file(s) [{sum}]", records.Count, size);
PreformDeletes(logger, relativePath.RightDirectory, records);
logger.LogInformation("Deleted {count} file(s) [{sum}]", records.Count, size);
}
if (httpMethod is not null) {
logger.LogInformation("Starting to {httpMethod} {count} file(s) [{sum}]", httpMethod.ToString().ToLower(), records.Count, size);
Preform(logger, page, directory, records, httpClient, httpMethod);
logger.LogInformation("{httpMethod}'ed {count} file(s) [{sum}]", httpMethod.ToString(), records.Count, size);
}
}
private static string GetSizeWithSuffix(long value) {
string result;
int i = 0;
string[] SizeSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
if (value < 0) {
result = "-" + GetSizeWithSuffix(-value);
} else {
while (Math.Round(value / 1024f) >= 1) {
value /= 1024;
i++;
}
result = string.Format("{0:n1} {1}", value, SizeSuffixes[i]);
}
return result;
}
private static void PreformDeletes(ILogger<Worker> logger, string directory, ReadOnlyCollection<Record> records) {
string size;
Record? record;
string count = records.Count.ToString("000000");
#if ShellProgressBar
ProgressBar progressBar = new(records.Count, $"Deleting: {count};", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
for (int i = 0; i < records.Count; i++) {
#if ShellProgressBar
progressBar.Tick();
#endif
record = records[i];
if (record is null) {
continue;
}
size = GetSizeWithSuffix(record.Size);
try {
File.Delete(Path.Combine(directory, record.RelativePath));
logger.LogInformation("{i} of {count} - Deleted: <{RelativePath}> - {size};", i.ToString("000000"), count, record.RelativePath, size);
} catch (Exception) {
logger.LogInformation("Failed to delete: <{RelativePath}> - {size};", record.RelativePath, size);
}
}
#if ShellProgressBar
progressBar.Dispose();
#endif
}
private static void Preform(ILogger<Worker> logger, string page, string directory, ReadOnlyCollection<Record> records, HttpClient httpClient, HttpMethod httpMethod) {
Verb verb;
long ticks;
string size;
string iValue;
string duration;
DateTime dateTime;
Task<string> response;
HttpRequestMessage httpRequestMessage;
Task<HttpResponseMessage> httpResponseMessage;
string count = records.Count.ToString("000000");
MultipartFormDataContent multipartFormDataContent;
ReadOnlyCollection<Verb> collection = GetVerbCollection(directory, records);
#if ShellProgressBar
ProgressBar progressBar = new(downloads.Count, $"{httpMethod}ing: {count};", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
for (int i = 0; i < collection.Count; i++) {
verb = collection[i];
#if ShellProgressBar
progressBar.Tick();
#endif
ticks = DateTime.Now.Ticks;
iValue = (i + 1).ToString("000000");
size = GetSizeWithSuffix(verb.Size);
if (httpMethod == HttpMethod.Get || httpMethod == HttpMethod.Delete) {
httpRequestMessage = new(httpMethod, $"{page}size={verb.Size}&ticks={verb.Ticks}&path={verb.UrlEncodedFile}");
} else if (httpMethod == HttpMethod.Patch || httpMethod == HttpMethod.Put) {
httpRequestMessage = new(httpMethod, $"{page}path={verb.Directory}");
multipartFormDataContent = new();
multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes(verb.File)), "formFiles", verb.Multipart);
multipartFormDataContent.Add(new StringContent(verb.Directory), "path", iValue);
httpRequestMessage.Content = multipartFormDataContent;
} else
throw new NotImplementedException();
httpResponseMessage = httpClient.SendAsync(httpRequestMessage);
httpResponseMessage.Wait(-1);
if (!httpResponseMessage.Result.IsSuccessStatusCode) {
logger.LogInformation("Failed to {httpMethod}: <{display}> - {size};", httpMethod, verb.Display, size);
} else {
try {
if (httpMethod != HttpMethod.Get) {
duration = GetDurationWithSuffix(ticks);
} else {
response = httpResponseMessage.Result.Content.ReadAsStringAsync();
response.Wait();
File.WriteAllText(verb.File, response.Result);
duration = GetDurationWithSuffix(ticks);
dateTime = new DateTime(verb.Ticks).ToLocalTime();
File.SetLastWriteTime(verb.File, dateTime);
}
logger.LogInformation("{i} of {count} - {httpMethod}'ed: <{display}> - {size} - {timeSpan};",
iValue,
count,
httpMethod,
verb.Display,
size,
duration);
} catch (Exception) {
logger.LogInformation("Failed to {httpMethod}: <{display}> - {size};", httpMethod, verb.Display, size);
}
}
}
#if ShellProgressBar
progressBar.Dispose();
#endif
}
private static ReadOnlyCollection<Verb> GetVerbCollection(string directory, ReadOnlyCollection<Record> records) {
List<Verb> results = [];
Verb verb;
string checkFile;
string checkFileName;
string? checkDirectory;
List<Verb> collection = [];
foreach (Record record in records) {
checkFile = Path.Combine(directory, record.RelativePath);
checkFileName = Path.GetFileName(checkFile);
checkDirectory = Path.GetDirectoryName(checkFile);
if (string.IsNullOrEmpty(checkDirectory)) {
continue;
}
if (!Directory.Exists(checkDirectory)) {
_ = Directory.CreateDirectory(checkDirectory);
}
if (File.Exists(checkFile) && new FileInfo(checkFile).Length == 0) {
File.Delete(checkFile);
}
verb = new(Directory: checkDirectory,
Display: $"{checkFileName}{Environment.NewLine}{checkDirectory}",
File: checkFile,
Multipart: $"RelativePath:{record.RelativePath}|Size:{record.Size}|Ticks:{record.Ticks};",
RelativePath: record.RelativePath,
Size: record.Size,
Ticks: record.Ticks,
UrlEncodedFile: HttpUtility.UrlEncode(checkFile));
collection.Add(verb);
}
Verb[] sorted = (from l in collection orderby l.Size select l).ToArray();
int stop = sorted.Length < 100 ? sorted.Length : 100;
for (int i = 0; i < stop; i++) {
results.Add(sorted[i]);
}
for (int i = sorted.Length - 1; i > stop - 1; i--) {
results.Add(sorted[i]);
}
if (collection.Count != results.Count) {
throw new Exception();
}
return results.AsReadOnly();
}
private static string GetDurationWithSuffix(long ticks) {
string result;
TimeSpan timeSpan = new(DateTime.Now.Ticks - ticks);
if (timeSpan.TotalMilliseconds < 1000) {
result = $"{timeSpan.Milliseconds} ms";
} else if (timeSpan.TotalMilliseconds < 60000) {
result = $"{Math.Floor(timeSpan.TotalSeconds)} s";
} else if (timeSpan.TotalMilliseconds < 3600000) {
result = $"{Math.Floor(timeSpan.TotalMinutes)} m";
} else {
result = $"{Math.Floor(timeSpan.TotalHours)} h";
}
return result;
}
}

View File

@ -0,0 +1,184 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250521 {
[GeneratedRegex(@"[~\-,.0-9]")]
private static partial Regex Number();
[GeneratedRegex(@"[^\u0020-\u007E]")]
private static partial Regex ASCII();
private record Record(string Directory, string FileNameWithoutExtension);
private record LineCheck(string[] Segments, DateTime TransactionDate, DateTime EffectiveDate) {
internal static LineCheck Get(int dateLineSegmentCount, string datePattern, string line) {
LineCheck result;
string[] segments = line.Split(' ');
if (segments.Length >= dateLineSegmentCount
&& segments[0].Length == datePattern.Length
&& segments[1].Length == datePattern.Length
&& DateTime.TryParseExact(segments[0], datePattern, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime transactionDate)
&& DateTime.TryParseExact(segments[1], datePattern, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime effectiveDate)) {
result = new(Segments: segments, TransactionDate: transactionDate, EffectiveDate: effectiveDate);
} else {
result = new(Segments: segments, TransactionDate: DateTime.MinValue, EffectiveDate: DateTime.MinValue);
}
return result;
}
}
private record RecordB(int I,
DateTime TransactionDate,
DateTime EffectiveDate,
string Description,
decimal WithdrawalOrDeposit,
decimal Balance);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(RecordB[]))]
private partial class Helper20250521RecordB : JsonSerializerContext {
}
internal static void MatchDirectory(ILogger<Worker> logger, List<string> args) {
Record record;
string datePattern = args[5];
string searchPattern = args[2];
string searchPatternB = args[3];
string columns = args[6].Replace('~', ',');
int dateLineSegmentCount = int.Parse(args[4]);
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
ReadOnlyDictionary<string, string> keyValuePairs = GetKeyValuePairs(searchPattern, sourceDirectory);
MoveMatchDirectory(searchPatternB, keyValuePairs, sourceDirectory);
ReadOnlyCollection<RecordB> records = GetRecords(searchPatternB, sourceDirectory, dateLineSegmentCount, datePattern, columns);
WriteRecords(sourceDirectory, records);
}
private static ReadOnlyDictionary<string, string> GetKeyValuePairs(string searchPattern, string sourceDirectory) {
Dictionary<string, string> results = [];
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
foreach (string file in files) {
results.Add(Path.GetFileNameWithoutExtension(file), Path.GetDirectoryName(file));
}
return results.AsReadOnly();
}
private static void MoveMatchDirectory(string searchPatternB, ReadOnlyDictionary<string, string> keyValuePairs, string sourceDirectory) {
string checkFile;
string fileNameWithoutExtension;
string[] files = Directory.GetFiles(sourceDirectory, searchPatternB, SearchOption.AllDirectories);
foreach (string file in files) {
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
if (!keyValuePairs.TryGetValue(fileNameWithoutExtension, out string? match))
continue;
checkFile = Path.Combine(match, Path.GetFileName(file));
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
}
private static ReadOnlyCollection<RecordB> GetRecords(string searchPatternB, string sourceDirectory, int dateLineSegmentCount, string datePattern, string columns) {
List<RecordB> results = [];
string line;
string[] lines;
RecordB? record;
LineCheck lineCheck;
string[] files = Directory.GetFiles(sourceDirectory, searchPatternB, SearchOption.AllDirectories);
foreach (string file in files) {
lines = File.ReadAllLines(file);
for (int i = 0; i < lines.Length; i++) {
line = lines[i];
if (string.IsNullOrEmpty(line)) {
continue;
}
lineCheck = LineCheck.Get(dateLineSegmentCount, datePattern, line);
if (lineCheck.EffectiveDate == DateTime.MinValue || lineCheck.TransactionDate == DateTime.MinValue) {
continue;
} else {
record = GetRecord(dateLineSegmentCount, datePattern, lines, i, lineCheck.Segments, lineCheck.TransactionDate, lineCheck.EffectiveDate);
if (record is not null) {
i = record.I;
results.Add(record);
}
}
}
}
return results.AsReadOnly();
}
private static RecordB? GetRecord(int dateLineSegmentCount, string datePattern, string[] lines, int i, string[] segments, DateTime transactionDate, DateTime effectiveDate) {
RecordB? result = null;
string line;
RecordB record;
LineCheck lineCheck;
List<string> collection = [];
for (int j = i + 1; j < lines.Length; j++) {
line = lines[j];
if (string.IsNullOrEmpty(line)) {
continue;
}
lineCheck = LineCheck.Get(dateLineSegmentCount, datePattern, line);
if (lineCheck.EffectiveDate == DateTime.MinValue || lineCheck.TransactionDate == DateTime.MinValue) {
collection.Add(line);
} else {
if (lineCheck.Segments.Length > dateLineSegmentCount) {
collection.Insert(0, string.Join(' ', lineCheck.Segments.Skip(2)));
}
result = GetRecord(transactionDate, effectiveDate, collection.AsReadOnly(), j - 1);
break;
}
}
if (result is null && collection.Count > 0) {
result = GetRecord(transactionDate, effectiveDate, collection.AsReadOnly(), lines.Length - 1);
}
return result;
}
private static RecordB GetRecord(DateTime transactionDate, DateTime effectiveDate, ReadOnlyCollection<string> collection, int i) {
RecordB? result;
List<string> verified = [];
foreach (string check in collection) {
if (Number().Replace(check, string.Empty).Length != 0) {
verified.Clear();
} else {
verified.Add(check);
}
if (verified.Count == 2) {
break;
}
}
if (verified.Count != 2) {
result = null;
} else {
decimal balance = decimal.Parse(verified[^1]);
decimal withdrawalOrDeposit = decimal.Parse(verified[^2]);
string description = ASCII().Replace(string.Join(' ', collection.SkipLast(2)), string.Empty);
result = new(I: i,
TransactionDate: transactionDate,
EffectiveDate: effectiveDate,
Description: description,
WithdrawalOrDeposit: withdrawalOrDeposit,
Balance: balance);
}
return result;
}
private static void WriteRecords(string sourceDirectory, ReadOnlyCollection<RecordB> records) {
string json = JsonSerializer.Serialize(records.ToArray(), Helper20250521RecordB.Default.RecordBArray);
string sourceDirectoryVsCode = Path.Combine(sourceDirectory, ".vscode");
if (!Directory.Exists(sourceDirectoryVsCode))
_ = Directory.CreateDirectory(sourceDirectoryVsCode);
File.WriteAllText(Path.Combine(sourceDirectoryVsCode, $"{DateTime.Now.Ticks}.json"), json);
}
}

View File

@ -0,0 +1,394 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using IFX.Shared.PasteSpecialXml.EAF.XML.API.Envelope;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250601 {
private static readonly bool _IsEnvironment_Development = false;
private record Record(string Text, string Host, int Port, string[] Segments, bool StateContainsDisabled);
private record Status(string CellInstanceName,
string CommunicationState,
string CurrentActiveVersion,
string CurrentHost,
string ErrorDescription,
string Host,
string IsReadyForRestart,
string NPort,
int Port,
string StartTime,
string Startable,
string State,
string StopTime,
string Text);
internal static void EquipmentAutomationFrameworkStatus(ILogger<Worker> logger, List<string> args) {
Status status;
Record? record;
List<string[]> messages;
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
string[] cellInstanceNames = args[2].Split('~');
Dictionary<string, Record> records;
if (_IsEnvironment_Development) {
records = GetEquipmentAutomationFrameworkCellInstanceStatus(development: true, staging: false, production: false);
} else {
records = GetEquipmentAutomationFrameworkCellInstanceStatus(development: false, staging: true, production: true);
}
foreach (string cellInstanceName in cellInstanceNames) {
if (!records.TryGetValue(cellInstanceName, out record)) {
logger.LogWarning("{cellInstance} not found!", cellInstanceName);
continue;
}
status = EquipmentAutomationFrameworkCellInstanceStatus(cellInstanceName, record);
logger.LogInformation("{host}) {cellInstanceName} => {status}", record.Host, cellInstanceName, status.ToString());
}
}
private static Dictionary<string, Record> GetEquipmentAutomationFrameworkCellInstanceStatus(bool development, bool staging, bool production) {
Dictionary<string, Record> results = [];
string key;
string host;
string text;
string state;
string response;
bool stop = false;
string[] segments;
string[] cellNames;
byte[] responseBytes;
string responseAfter;
#pragma warning disable SYSLIB0014
WebClient webClient = new();
#pragma warning restore SYSLIB0014
string disabled = "Disabled";
UnicodeCategory unicodeCategory;
StringBuilder stringBuilder = new();
EquipmentAutomationFrameworkCellInstanceParseCheck();
Dictionary<char, char> unicodeReplaces = GetUnicodeReplaces();
List<UnicodeCategory> unicodeCategories = GetUnicodeCategory();
ReadOnlyCollection<string> urls = GetUrls(development, staging, production);
// Dictionary<UnicodeCategory, List<char>> unicodeCategoriesList = new Dictionary<UnicodeCategory, List<char>>();
byte[] bodyBytes = [86, 2, 11, 1, 115, 4, 11, 1, 97, 6, 86, 8, 68, 10, 30, 0, 130, 153, 48, 104, 116, 116, 112, 58, 47, 47, 116, 101, 109, 112, 117, 114, 105, 46, 111, 114, 103, 47, 73, 83, 116, 97, 116, 117, 115, 81, 117, 101, 114, 121, 47, 71, 101, 116, 70, 97, 99, 116, 111, 114, 121, 83, 116, 97, 116, 117, 115, 68, 26, 173, 181, 241, 2, 149, 65, 209, 208, 66, 143, 234, 233, 157, 246, 118, 78, 238, 68, 44, 68, 42, 171, 20, 1, 68, 12, 30, 0, 130, 153, 49, 104, 116, 116, 112, 58, 47, 47, 101, 97, 102, 45, 112, 114, 111, 100, 46, 109, 101, 115, 46, 105, 110, 102, 105, 110, 101, 111, 110, 46, 99, 111, 109, 58, 57, 48, 48, 51, 47, 83, 116, 97, 116, 117, 115, 81, 117, 101, 114, 121, 1, 86, 14, 64, 16, 71, 101, 116, 70, 97, 99, 116, 111, 114, 121, 83, 116, 97, 116, 117, 115, 8, 19, 104, 116, 116, 112, 58, 47, 47, 116, 101, 109, 112, 117, 114, 105, 46, 111, 114, 103, 47, 64, 16, 105, 110, 99, 108, 117, 100, 101, 65, 103, 101, 110, 116, 76, 105, 115, 116, 135, 64, 17, 105, 110, 99, 108, 117, 100, 101, 83, 116, 97, 116, 117, 115, 76, 105, 115, 116, 135, 64, 23, 101, 120, 116, 101, 110, 100, 101, 100, 83, 116, 97, 116, 117, 115, 67, 101, 108, 108, 78, 97, 109, 101, 115, 9, 1, 98, 57, 104, 116, 116, 112, 58, 47, 47, 115, 99, 104, 101, 109, 97, 115, 46, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 99, 111, 109, 47, 50, 48, 48, 51, 47, 49, 48, 47, 83, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 47, 65, 114, 114, 97, 121, 115, 9, 1, 105, 41, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 49, 47, 88, 77, 76, 83, 99, 104, 101, 109, 97, 45, 105, 110, 115, 116, 97, 110, 99, 101, 95, 6, 115, 116, 114, 105, 110, 103, 153, 20, 66, 73, 79, 82, 65, 68, 53, 95, 70, 105, 108, 101, 65, 114, 99, 104, 105, 118, 101, 114, 1, 1, 1, 1];
foreach (string url in urls) {
if (stop) {
break;
}
segments = url.Split(':');
host = segments[0];
if (segments.Length == 0 || !int.TryParse(segments[1], out int port)) {
port = 80;
}
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+msbin1");
responseBytes = webClient.UploadData($"http://{host}:{port}/StatusQuery", bodyBytes);
// File.WriteAllText(@"L:\Tmp\a.txt", BitConverter.ToString(responseBytes));
response = Encoding.UTF8.GetString(responseBytes);
foreach (char c in response) {
unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory == UnicodeCategory.Control && unicodeReplaces.ContainsKey(c)) {
_ = stringBuilder.Append(unicodeReplaces[c]);
} else if (unicodeCategories.Contains(unicodeCategory)) {
_ = stringBuilder.Append(c);
}
}
responseAfter = stringBuilder.ToString();
cellNames = responseAfter.Split(new string[] { "CellName" }, StringSplitOptions.None);
foreach (string segment in cellNames) {
if (stop) {
break;
}
key = string.Empty;
state = string.Empty;
segments = segment.Split(new string[] { "WindowsName" }, StringSplitOptions.None);
if (segments.Length != 2) {
continue;
}
text = segments[0];
segments = text.Replace('\r', ' ').Replace('\n', ' ').Split(' ');
for (int i = 0; i < segments.Length - 3; i++) {
if (stop) {
break;
}
if (!string.IsNullOrEmpty(segments[i]) && string.IsNullOrEmpty(key)) {
key = segments[i].Trim();
} else if (segments[i].StartsWith("State")) {
state = segments[i + 1];
break;
}
}
if (key.EndsWith("a")) {
key = key[..^1];
}
if (!results.ContainsKey(key)) {
results.Add(key, new Record(Text: text, Host: host, Port: port, Segments: segments, StateContainsDisabled: state.Contains(disabled)));
} else if (results[key].StateContainsDisabled) {
results[key] = new Record(Text: text, Host: host, Port: port, Segments: segments, StateContainsDisabled: state.Contains(disabled));
}
}
}
return results;
}
private static ReadOnlyCollection<string> GetUrls(bool development, bool staging, bool production) {
List<string> results = [];
if (development) {
results.Add("eaf-dev.mes.infineon.com:9003");
}
if (staging) {
results.Add("eaf-staging.mes.infineon.com:9003");
}
if (production) {
results.Add("eaf-prod.mes.infineon.com:9003");
}
return results.AsReadOnly();
}
private static List<UnicodeCategory> GetUnicodeCategory() {
List<UnicodeCategory> unicodeCategories = [
// UnicodeCategory.Control, // 33 - <20>
UnicodeCategory.UppercaseLetter, // 25 - ABCDEFGHIJKLMNOPQRSTUVWXY
UnicodeCategory.LowercaseLetter, // 25 - abcdefghiklmnopqrstuvwxyz
UnicodeCategory.DecimalDigitNumber, // 10 - 0123456789
UnicodeCategory.OtherPunctuation, // 10 - !"#%&,./:@
UnicodeCategory.ClosePunctuation, // 2 - )]
UnicodeCategory.MathSymbol, // 2 - |؈
UnicodeCategory.OpenPunctuation, // 2 - ([
// UnicodeCategory.OtherSymbol, // 1 - <20>
UnicodeCategory.DashPunctuation, // 1 - -
UnicodeCategory.ConnectorPunctuation, // 1 - _
UnicodeCategory.ModifierSymbol, // 1 - `
UnicodeCategory.NonSpacingMark, // 1 - ̵
UnicodeCategory.SpaceSeparator, // 1 -
UnicodeCategory.CurrencySymbol, // 1 - $
];
return unicodeCategories;
}
private static void EquipmentAutomationFrameworkCellInstanceParseCheck() {
Envelope? envelope;
string xmlStart621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:6eb7a538-0b2b-4d04-8f2a-ab50e1e5338a</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlStart891 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/StartAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:38977fa4-262a-42fb-8df7-d8d3074820b2</a:RelatesTo></s:Header><s:Body><StartAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlStart748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlStart707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string xmlStop621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:97f7aeb4-015f-440b-b0ff-a2a5aa4f4ab9</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlStop889 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/StopAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:04b8b0ea-8576-4756-b456-8a817cd10826</a:RelatesTo></s:Header><s:Body><StopAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlStop748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlStop707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string xmlRestart621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:e228a621-e7ab-4ebf-97ba-5571cb5f4ad7</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlRestart895 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/RestartAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:efaeaf12-4aa0-4cd1-8296-05019e47261a</a:RelatesTo></s:Header><s:Body><RestartAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlRestart748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlRestart707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string[] xmlSets = [xmlStart621, xmlStart891, xmlStart748, xmlStart707, xmlStop621, xmlStop889, xmlStop748, xmlStop707, xmlRestart621, xmlRestart895, xmlRestart748, xmlRestart707];
foreach (string xmlSet in xmlSets) {
envelope = ParseXML<Envelope>(xmlSet, throwExceptions: true);
}
}
private static T? ParseXML<T>(string value, bool throwExceptions) where T : class {
object? result = null;
try {
Stream stream = ToStream(value.Trim());
XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
#pragma warning disable IL2026, IL2090
XmlSerializer xmlSerializer = new(typeof(T), typeof(T).GetNestedTypes());
result = xmlSerializer.Deserialize(xmlReader);
#pragma warning restore IL2026, IL2090
stream.Dispose();
} catch (Exception) {
if (throwExceptions) {
throw;
}
}
return result as T;
}
private static Stream ToStream(string value) {
MemoryStream memoryStream = new();
StreamWriter streamWriter = new(memoryStream);
streamWriter.Write(value);
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
private static Dictionary<char, char> GetUnicodeReplaces() {
Dictionary<char, char> results = new() {
{ '\u0000', ' ' },
{ '\u0001', ' ' },
{ '\u0002', ' ' },
{ '\u0003', ' ' },
{ '\u0004', ' ' },
{ '\u0005', ' ' },
{ '\u0006', ' ' },
{ '\u0007', ' ' },
{ '\u0008', ' ' },
{ '\u0009', '\t' },
{ '\u000A', '\r' },
{ '\u000B', '\r' },
{ '\u000C', '\t' },
{ '\u000D', '\r' },
{ '\u000E', ' ' },
{ '\u000F', ' ' },
{ '\u0010', ' ' },
{ '\u0011', ' ' },
{ '\u0012', ' ' },
{ '\u0013', ' ' },
{ '\u0014', ' ' },
{ '\u0015', ' ' },
{ '\u0016', ' ' },
{ '\u0017', ' ' },
{ '\u0018', ' ' },
{ '\u0019', ' ' },
{ '\u001A', ' ' },
{ '\u001B', ' ' },
{ '\u001C', '\r' },
{ '\u001D', '\t' },
{ '\u001E', '\t' },
{ '\u001F', '\t' },
{ '\u007F', ' ' },
// C1
{ '\u0080', '\t' },
{ '\u0081', ' ' },
{ '\u0082', ' ' },
{ '\u0083', ' ' },
{ '\u0084', ' ' },
{ '\u0085', '\r' },
{ '\u0086', ' ' },
{ '\u0087', ' ' },
{ '\u0088', '\t' },
{ '\u0089', '\t' },
{ '\u008A', '\t' },
{ '\u008B', '\r' },
{ '\u008C', ' ' },
{ '\u008D', ' ' },
{ '\u008E', ' ' },
{ '\u008F', ' ' },
{ '\u0090', ' ' },
{ '\u0091', ' ' },
{ '\u0092', ' ' },
{ '\u0093', ' ' },
{ '\u0094', ' ' },
{ '\u0095', ' ' },
{ '\u0096', ' ' },
{ '\u0097', ' ' },
{ '\u0098', ' ' },
{ '\u0099', ' ' },
{ '\u009A', ' ' },
{ '\u009B', ' ' },
{ '\u009C', ' ' },
{ '\u009D', ' ' },
{ '\u009E', ' ' },
{ '\u009F', ' ' }
};
return results;
}
private static Status EquipmentAutomationFrameworkCellInstanceStatus(string cellInstanceName, Record record) {
Status result;
bool stop = false;
string state = string.Empty;
string stopTime = string.Empty;
string startTime = string.Empty;
string startable = string.Empty;
string currentHost = string.Empty;
string errorDescription = string.Empty;
string isReadyForRestart = string.Empty;
string communicationState = string.Empty;
string currentActiveVersion = string.Empty;
for (int i = 0; i < record.Segments.Length - 3; i++) {
if (stop) {
break;
}
if (string.IsNullOrEmpty(state) && record.Segments[i].StartsWith("State")) {
state = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(startable) && record.Segments[i].Contains("Startable")) {
startable = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(stopTime) && record.Segments[i].StartsWith("StopTime")) {
stopTime = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(currentHost) && record.Segments[i].Contains("CurrentHost")) {
currentHost = $"{record.Segments[i]} {record.Segments[i + 1]} {record.Segments[i + 2]}";
} else if (string.IsNullOrEmpty(errorDescription) && record.Segments[i].StartsWith("ErrorDescription")) {
errorDescription = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(communicationState) && record.Segments[i].StartsWith("CommunicationState")) {
communicationState = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(isReadyForRestart) && record.Segments[i].StartsWith("IsReadyForRestart")) {
isReadyForRestart = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(currentActiveVersion) && record.Segments[i].Contains("CurrentActiveVersion")) {
currentActiveVersion = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(startTime) && record.Segments[i].Contains("StartTime")) {
startTime = $"{record.Segments[i + 1]} {record.Segments[i + 2]} {record.Segments[i + 3]}".Split('\t')[0];
}
}
if (errorDescription != "a") {
string[] segments = record.Text.Split(new string[] { "ErrorDescription" }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1) {
segments = segments[1].Split(new string[] { "Info" }, StringSplitOptions.RemoveEmptyEntries);
errorDescription = segments[0].Trim();
}
}
string nPort;
Dictionary<string, string> nPorts = GetnPorts();
if (!nPorts.ContainsKey(cellInstanceName)) {
nPort = string.Empty;
} else {
nPort = nPorts[cellInstanceName];
}
if (state.EndsWith("a")) {
state = state[0..^1];
}
if (state == "Running" && communicationState == "Not") {
state = "Warning";
}
result = new(Host: record.Host,
Port: record.Port,
Text: record.Text,
NPort: nPort,
State: state,
CellInstanceName: cellInstanceName,
StopTime: stopTime,
StartTime: startTime,
Startable: startable,
CurrentHost: currentHost,
ErrorDescription: errorDescription,
IsReadyForRestart: isReadyForRestart,
CommunicationState: communicationState,
CurrentActiveVersion: currentActiveVersion);
return result;
}
private static Dictionary<string, string> GetnPorts() {
Dictionary<string, string> results = new() {
{ "TENCOR1", "10.95.192.31" },
{ "TENCOR2", "10.95.192.32" },
{ "TENCOR3", "10.95.192.33" },
{ "HGCV1", "10.95.192.34" },
{ "HGCV2", "10.95.154.17" },
{ "HGCV3", "10.95.192.36" },
{ "BIORAD2", "10.95.192.37" },
{ "BIORAD3", "10.95.192.38" },
{ "CDE2", "10.95.192.39" },
{ "CDE3", "10.95.154.19" },
{ "CDE5", "10.95.192.40" },
{ "SPARE-1", "10.95.192.47" },
{ "SPARE-2", "10.95.192.48" },
{ "SPARE-3", "10.95.192.49" },
{ "SPARE-4", "10.95.192.50" },
{ "SPARE-5", "10.95.192.51" },
};
return results;
}
}

View File

@ -0,0 +1,545 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using IFX.Shared.PasteSpecialXml.EAF.XML.API.Envelope;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250602 {
private record Record(string Text, string Host, int Port, string[] Segments, bool StateContainsDisabled);
private record Status(string CellInstanceName,
string CommunicationState,
string CurrentActiveVersion,
string CurrentHost,
string ErrorDescription,
string Host,
string IsReadyForRestart,
string NPort,
int Port,
string StartTime,
string Startable,
string State,
string StopTime,
string Text);
internal static void EquipmentAutomationFrameworkCellInstanceStateImageVerbIf(ILogger<Worker> logger, List<string> args) {
string path;
Status status;
Record? record;
string verbBy = args[2];
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
string[] cellInstanceNames = args[3].Split('~');
Dictionary<string, Record> records = GetEquipmentAutomationFrameworkCellInstanceStatus(development: false, staging: false, production: true);
foreach (string cellInstanceName in cellInstanceNames) {
if (!records.TryGetValue(cellInstanceName, out record)) {
logger.LogWarning("{cellInstance} not found!", cellInstanceName);
continue;
}
if (records[cellInstanceName].StateContainsDisabled) {
logger.LogWarning("{cellInstance} not found!", cellInstanceName);
}
status = GetEquipmentAutomationFrameworkCellInstanceStateImageVerbIf(logger, verbBy, cellInstanceName, record);
path = $"/images/{cellInstanceName}_{status.State}.jpg";
logger.LogInformation("{host}) {cellInstanceName} => {state} <{path}>", record.Host, cellInstanceName, status.State, path);
}
}
private static Status GetEquipmentAutomationFrameworkCellInstanceStateImageVerbIf(ILogger<Worker> logger, string verbBy, string cellInstanceName, Record record) {
Status result;
Dictionary<string, DateTime> equipmentAutomationFrameworkTriggers = [];
if (!equipmentAutomationFrameworkTriggers.ContainsKey(cellInstanceName)) {
equipmentAutomationFrameworkTriggers.Add(cellInstanceName, DateTime.MinValue);
}
result = EquipmentAutomationFrameworkCellInstanceStatus(cellInstanceName, record);
if (equipmentAutomationFrameworkTriggers[cellInstanceName] < DateTime.Now.AddSeconds(-60)) {
if (result.State == "Offline") {
EquipmentAutomationFrameworkCellInstanceStart(record.Host, record.Port, cellInstanceName, verbBy);
logger.LogInformation("Start invoked for {cellName}", cellInstanceName);
} else if (result.State == "Warning") {
EquipmentAutomationFrameworkCellInstanceRestart(record.Host, record.Port, cellInstanceName, verbBy);
logger.LogInformation("Restart invoked for {cellName}", cellInstanceName);
}
}
return result;
}
private static Dictionary<string, Record> GetEquipmentAutomationFrameworkCellInstanceStatus(bool development, bool staging, bool production) {
Dictionary<string, Record> results = [];
string key;
string host;
string text;
string state;
string response;
bool stop = false;
string[] segments;
string[] cellNames;
byte[] responseBytes;
string responseAfter;
#pragma warning disable SYSLIB0014
WebClient webClient = new();
#pragma warning restore SYSLIB0014
string disabled = "Disabled";
UnicodeCategory unicodeCategory;
StringBuilder stringBuilder = new();
EquipmentAutomationFrameworkCellInstanceParseCheck();
Dictionary<char, char> unicodeReplaces = GetUnicodeReplaces();
List<UnicodeCategory> unicodeCategories = GetUnicodeCategory();
ReadOnlyCollection<string> urls = GetUrls(development, staging, production);
// Dictionary<UnicodeCategory, List<char>> unicodeCategoriesList = new Dictionary<UnicodeCategory, List<char>>();
byte[] bodyBytes = [86, 2, 11, 1, 115, 4, 11, 1, 97, 6, 86, 8, 68, 10, 30, 0, 130, 153, 48, 104, 116, 116, 112, 58, 47, 47, 116, 101, 109, 112, 117, 114, 105, 46, 111, 114, 103, 47, 73, 83, 116, 97, 116, 117, 115, 81, 117, 101, 114, 121, 47, 71, 101, 116, 70, 97, 99, 116, 111, 114, 121, 83, 116, 97, 116, 117, 115, 68, 26, 173, 181, 241, 2, 149, 65, 209, 208, 66, 143, 234, 233, 157, 246, 118, 78, 238, 68, 44, 68, 42, 171, 20, 1, 68, 12, 30, 0, 130, 153, 49, 104, 116, 116, 112, 58, 47, 47, 101, 97, 102, 45, 112, 114, 111, 100, 46, 109, 101, 115, 46, 105, 110, 102, 105, 110, 101, 111, 110, 46, 99, 111, 109, 58, 57, 48, 48, 51, 47, 83, 116, 97, 116, 117, 115, 81, 117, 101, 114, 121, 1, 86, 14, 64, 16, 71, 101, 116, 70, 97, 99, 116, 111, 114, 121, 83, 116, 97, 116, 117, 115, 8, 19, 104, 116, 116, 112, 58, 47, 47, 116, 101, 109, 112, 117, 114, 105, 46, 111, 114, 103, 47, 64, 16, 105, 110, 99, 108, 117, 100, 101, 65, 103, 101, 110, 116, 76, 105, 115, 116, 135, 64, 17, 105, 110, 99, 108, 117, 100, 101, 83, 116, 97, 116, 117, 115, 76, 105, 115, 116, 135, 64, 23, 101, 120, 116, 101, 110, 100, 101, 100, 83, 116, 97, 116, 117, 115, 67, 101, 108, 108, 78, 97, 109, 101, 115, 9, 1, 98, 57, 104, 116, 116, 112, 58, 47, 47, 115, 99, 104, 101, 109, 97, 115, 46, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 99, 111, 109, 47, 50, 48, 48, 51, 47, 49, 48, 47, 83, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 47, 65, 114, 114, 97, 121, 115, 9, 1, 105, 41, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 49, 47, 88, 77, 76, 83, 99, 104, 101, 109, 97, 45, 105, 110, 115, 116, 97, 110, 99, 101, 95, 6, 115, 116, 114, 105, 110, 103, 153, 20, 66, 73, 79, 82, 65, 68, 53, 95, 70, 105, 108, 101, 65, 114, 99, 104, 105, 118, 101, 114, 1, 1, 1, 1];
foreach (string url in urls) {
if (stop) {
break;
}
segments = url.Split(':');
host = segments[0];
if (segments.Length == 0 || !int.TryParse(segments[1], out int port)) {
port = 80;
}
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+msbin1");
responseBytes = webClient.UploadData($"http://{host}:{port}/StatusQuery", bodyBytes);
// File.WriteAllText(@"L:\Tmp\a.txt", BitConverter.ToString(responseBytes));
response = Encoding.UTF8.GetString(responseBytes);
foreach (char c in response) {
unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory == UnicodeCategory.Control && unicodeReplaces.ContainsKey(c)) {
_ = stringBuilder.Append(unicodeReplaces[c]);
} else if (unicodeCategories.Contains(unicodeCategory)) {
_ = stringBuilder.Append(c);
}
}
responseAfter = stringBuilder.ToString();
cellNames = responseAfter.Split(new string[] { "CellName" }, StringSplitOptions.None);
foreach (string segment in cellNames) {
if (stop) {
break;
}
key = string.Empty;
state = string.Empty;
segments = segment.Split(new string[] { "WindowsName" }, StringSplitOptions.None);
if (segments.Length != 2) {
continue;
}
text = segments[0];
segments = text.Replace('\r', ' ').Replace('\n', ' ').Split(' ');
for (int i = 0; i < segments.Length - 3; i++) {
if (stop) {
break;
}
if (!string.IsNullOrEmpty(segments[i]) && string.IsNullOrEmpty(key)) {
key = segments[i].Trim();
} else if (segments[i].StartsWith("State")) {
state = segments[i + 1];
break;
}
}
if (key.EndsWith("a")) {
key = key[..^1];
}
if (!results.ContainsKey(key)) {
results.Add(key, new Record(Text: text, Host: host, Port: port, Segments: segments, StateContainsDisabled: state.Contains(disabled)));
} else if (results[key].StateContainsDisabled) {
results[key] = new Record(Text: text, Host: host, Port: port, Segments: segments, StateContainsDisabled: state.Contains(disabled));
}
}
}
return results;
}
private static ReadOnlyCollection<string> GetUrls(bool development, bool staging, bool production) {
List<string> results = [];
if (development) {
results.Add("eaf-dev.mes.infineon.com:9003");
}
if (staging) {
results.Add("eaf-staging.mes.infineon.com:9003");
}
if (production) {
results.Add("eaf-prod.mes.infineon.com:9003");
}
return results.AsReadOnly();
}
private static List<UnicodeCategory> GetUnicodeCategory() {
List<UnicodeCategory> unicodeCategories = [
// UnicodeCategory.Control, // 33 - <20>
UnicodeCategory.UppercaseLetter, // 25 - ABCDEFGHIJKLMNOPQRSTUVWXY
UnicodeCategory.LowercaseLetter, // 25 - abcdefghiklmnopqrstuvwxyz
UnicodeCategory.DecimalDigitNumber, // 10 - 0123456789
UnicodeCategory.OtherPunctuation, // 10 - !"#%&,./:@
UnicodeCategory.ClosePunctuation, // 2 - )]
UnicodeCategory.MathSymbol, // 2 - |؈
UnicodeCategory.OpenPunctuation, // 2 - ([
// UnicodeCategory.OtherSymbol, // 1 - <20>
UnicodeCategory.DashPunctuation, // 1 - -
UnicodeCategory.ConnectorPunctuation, // 1 - _
UnicodeCategory.ModifierSymbol, // 1 - `
UnicodeCategory.NonSpacingMark, // 1 - ̵
UnicodeCategory.SpaceSeparator, // 1 -
UnicodeCategory.CurrencySymbol, // 1 - $
];
return unicodeCategories;
}
private static void EquipmentAutomationFrameworkCellInstanceParseCheck() {
Envelope? envelope;
string xmlStart621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:6eb7a538-0b2b-4d04-8f2a-ab50e1e5338a</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlStart891 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/StartAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:38977fa4-262a-42fb-8df7-d8d3074820b2</a:RelatesTo></s:Header><s:Body><StartAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlStart748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlStart707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:31c290af-2312-4b00-a57c-d5e1ab51e02a</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:f169e50f-5ca8-43cd-a1e9-724840ff5e00</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string xmlStop621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:97f7aeb4-015f-440b-b0ff-a2a5aa4f4ab9</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlStop889 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/StopAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:04b8b0ea-8576-4756-b456-8a817cd10826</a:RelatesTo></s:Header><s:Body><StopAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlStop748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlStop707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:e34d16ad-21d5-4a11-a6dc-5b5b58a74f96</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:c9a4d5b6-435b-49a4-a2f9-d93cd8aecc36</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string xmlRestart621 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action><a:RelatesTo>urn:uuid:e228a621-e7ab-4ebf-97ba-5571cb5f4ad7</a:RelatesTo></s:Header><s:Body><CreateSequenceResponse xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</Identifier><Accept><AcksTo><a:Address>http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:Address></AcksTo></Accept></CreateSequenceResponse></s:Body></s:Envelope>";
string xmlRestart895 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/RestartAllCellInstancesResponse</a:Action><a:RelatesTo>urn:uuid:efaeaf12-4aa0-4cd1-8296-05019e47261a</a:RelatesTo></s:Header><s:Body><RestartAllCellInstancesResponse xmlns=\"http://tempuri.org/\"/></s:Body></s:Envelope>";
string xmlRestart748 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:Sequence s:mustUnderstand=\"1\"><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action></s:Header><s:Body/></s:Envelope>";
string xmlRestart707 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:a1650ed7-34dc-4fac-993f-ed2559c453a2</r:Identifier><r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/><netrm:BufferRemaining xmlns:netrm=\"http://schemas.microsoft.com/ws/2006/05/rm\">8</netrm:BufferRemaining></r:SequenceAcknowledgement><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:50c82506-bd4d-4117-b632-640cf84d556e</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>";
string[] xmlSets = [xmlStart621, xmlStart891, xmlStart748, xmlStart707, xmlStop621, xmlStop889, xmlStop748, xmlStop707, xmlRestart621, xmlRestart895, xmlRestart748, xmlRestart707];
foreach (string xmlSet in xmlSets) {
envelope = ParseXML<Envelope>(xmlSet, throwExceptions: true);
}
}
private static T? ParseXML<T>(string value, bool throwExceptions) where T : class {
object? result = null;
try {
Stream stream = ToStream(value.Trim());
XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
#pragma warning disable IL2026, IL2090
XmlSerializer xmlSerializer = new(typeof(T), typeof(T).GetNestedTypes());
result = xmlSerializer.Deserialize(xmlReader);
#pragma warning restore IL2026, IL2090
stream.Dispose();
} catch (Exception) {
if (throwExceptions) {
throw;
}
}
return result as T;
}
private static Stream ToStream(string value) {
MemoryStream memoryStream = new();
StreamWriter streamWriter = new(memoryStream);
streamWriter.Write(value);
streamWriter.Flush();
memoryStream.Position = 0;
return memoryStream;
}
private static Dictionary<char, char> GetUnicodeReplaces() {
Dictionary<char, char> results = new() {
{ '\u0000', ' ' },
{ '\u0001', ' ' },
{ '\u0002', ' ' },
{ '\u0003', ' ' },
{ '\u0004', ' ' },
{ '\u0005', ' ' },
{ '\u0006', ' ' },
{ '\u0007', ' ' },
{ '\u0008', ' ' },
{ '\u0009', '\t' },
{ '\u000A', '\r' },
{ '\u000B', '\r' },
{ '\u000C', '\t' },
{ '\u000D', '\r' },
{ '\u000E', ' ' },
{ '\u000F', ' ' },
{ '\u0010', ' ' },
{ '\u0011', ' ' },
{ '\u0012', ' ' },
{ '\u0013', ' ' },
{ '\u0014', ' ' },
{ '\u0015', ' ' },
{ '\u0016', ' ' },
{ '\u0017', ' ' },
{ '\u0018', ' ' },
{ '\u0019', ' ' },
{ '\u001A', ' ' },
{ '\u001B', ' ' },
{ '\u001C', '\r' },
{ '\u001D', '\t' },
{ '\u001E', '\t' },
{ '\u001F', '\t' },
{ '\u007F', ' ' },
// C1
{ '\u0080', '\t' },
{ '\u0081', ' ' },
{ '\u0082', ' ' },
{ '\u0083', ' ' },
{ '\u0084', ' ' },
{ '\u0085', '\r' },
{ '\u0086', ' ' },
{ '\u0087', ' ' },
{ '\u0088', '\t' },
{ '\u0089', '\t' },
{ '\u008A', '\t' },
{ '\u008B', '\r' },
{ '\u008C', ' ' },
{ '\u008D', ' ' },
{ '\u008E', ' ' },
{ '\u008F', ' ' },
{ '\u0090', ' ' },
{ '\u0091', ' ' },
{ '\u0092', ' ' },
{ '\u0093', ' ' },
{ '\u0094', ' ' },
{ '\u0095', ' ' },
{ '\u0096', ' ' },
{ '\u0097', ' ' },
{ '\u0098', ' ' },
{ '\u0099', ' ' },
{ '\u009A', ' ' },
{ '\u009B', ' ' },
{ '\u009C', ' ' },
{ '\u009D', ' ' },
{ '\u009E', ' ' },
{ '\u009F', ' ' }
};
return results;
}
private static Status EquipmentAutomationFrameworkCellInstanceStatus(string cellInstanceName, Record record) {
Status result;
bool stop = false;
string state = string.Empty;
string stopTime = string.Empty;
string startTime = string.Empty;
string startable = string.Empty;
string currentHost = string.Empty;
string errorDescription = string.Empty;
string isReadyForRestart = string.Empty;
string communicationState = string.Empty;
string currentActiveVersion = string.Empty;
for (int i = 0; i < record.Segments.Length - 3; i++) {
if (stop) {
break;
}
if (string.IsNullOrEmpty(state) && record.Segments[i].StartsWith("State")) {
state = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(startable) && record.Segments[i].Contains("Startable")) {
startable = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(stopTime) && record.Segments[i].StartsWith("StopTime")) {
stopTime = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(currentHost) && record.Segments[i].Contains("CurrentHost")) {
currentHost = $"{record.Segments[i]} {record.Segments[i + 1]} {record.Segments[i + 2]}";
} else if (string.IsNullOrEmpty(errorDescription) && record.Segments[i].StartsWith("ErrorDescription")) {
errorDescription = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(communicationState) && record.Segments[i].StartsWith("CommunicationState")) {
communicationState = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(isReadyForRestart) && record.Segments[i].StartsWith("IsReadyForRestart")) {
isReadyForRestart = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(currentActiveVersion) && record.Segments[i].Contains("CurrentActiveVersion")) {
currentActiveVersion = record.Segments[i + 1];
} else if (string.IsNullOrEmpty(startTime) && record.Segments[i].Contains("StartTime")) {
startTime = $"{record.Segments[i + 1]} {record.Segments[i + 2]} {record.Segments[i + 3]}".Split('\t')[0];
}
}
if (errorDescription != "a") {
string[] segments = record.Text.Split(new string[] { "ErrorDescription" }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1) {
segments = segments[1].Split(new string[] { "Info" }, StringSplitOptions.RemoveEmptyEntries);
errorDescription = segments[0].Trim();
}
}
string nPort;
Dictionary<string, string> nPorts = GetnPorts();
if (!nPorts.ContainsKey(cellInstanceName)) {
nPort = string.Empty;
} else {
nPort = nPorts[cellInstanceName];
}
if (state.EndsWith("a")) {
state = state[0..^1];
}
if (state == "Running" && communicationState == "Not") {
state = "Warning";
}
result = new(Host: record.Host,
Port: record.Port,
Text: record.Text,
NPort: nPort,
State: state,
CellInstanceName: cellInstanceName,
StopTime: stopTime,
StartTime: startTime,
Startable: startable,
CurrentHost: currentHost,
ErrorDescription: errorDescription,
IsReadyForRestart: isReadyForRestart,
CommunicationState: communicationState,
CurrentActiveVersion: currentActiveVersion);
return result;
}
private static Dictionary<string, string> GetnPorts() {
Dictionary<string, string> results = new() {
{ "TENCOR1", "10.95.192.31" },
{ "TENCOR2", "10.95.192.32" },
{ "TENCOR3", "10.95.192.33" },
{ "HGCV1", "10.95.192.34" },
{ "HGCV2", "10.95.154.17" },
{ "HGCV3", "10.95.192.36" },
{ "BIORAD2", "10.95.192.37" },
{ "BIORAD3", "10.95.192.38" },
{ "CDE2", "10.95.192.39" },
{ "CDE3", "10.95.154.19" },
{ "CDE5", "10.95.192.40" },
{ "SPARE-1", "10.95.192.47" },
{ "SPARE-2", "10.95.192.48" },
{ "SPARE-3", "10.95.192.49" },
{ "SPARE-4", "10.95.192.50" },
{ "SPARE-5", "10.95.192.51" },
};
return results;
}
private static void EquipmentAutomationFrameworkCellInstanceRestart(string host, int port, string cellName = "R71-HSMS", string verbBy = @"EC\EcMesEaf") {
EquipmentAutomationFrameworkCellInstanceParseCheck();
// Restart
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action><a:MessageID>urn:uuid:09fd9303-dcfe-4563-803b-678441b12949</a:MessageID><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><CreateSequence xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm"><AcksTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></AcksTo><Offer><Identifier>urn:uuid:4f2050da-4287-411b-992f-3126a5b3b35b</Identifier></Offer></CreateSequence></s:Body></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:Sequence s:mustUnderstand="1"><r:Identifier>urn:uuid:fbf34c20-f381-4e82-b81f-b4c1809f14fa</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><a:Action s:mustUnderstand="1">http://tempuri.org/ICellControllerManager/RestartAllCellInstances</a:Action><a:MessageID>urn:uuid:c9f80db4-a2c2-4e53-9bed-8ba3a60b653c</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><RestartAllCellInstances xmlns="http://tempuri.org/"><cellInstances xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><b:string>SP101_FileArchiver</b:string></cellInstances><updateInfo>Restarted by EC\ecphares</updateInfo></RestartAllCellInstances></s:Body></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:4f2050da-4287-411b-992f-3126a5b3b35b</r:Identifier><r:AcknowledgementRange Lower="1" Upper="1"/></r:SequenceAcknowledgement><r:Sequence s:mustUnderstand="1"><r:Identifier>urn:uuid:fbf34c20-f381-4e82-b81f-b4c1809f14fa</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body/></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:4f2050da-4287-411b-992f-3126a5b3b35b</r:Identifier><r:AcknowledgementRange Lower="1" Upper="2"/></r:SequenceAcknowledgement><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action><a:MessageID>urn:uuid:3b063df5-e6df-47a5-b530-aa380a4c6a38</a:MessageID><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:fbf34c20-f381-4e82-b81f-b4c1809f14fa</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>
EquipmentAutomationFrameworkCellInstanceVerb(host, port, cellName, verbBy, restart: true, stop: false, start: false);
}
private static void EquipmentAutomationFrameworkCellInstanceStart(string host, int port, string cellName = "R71-HSMS", string verbBy = @"EC\EcMesEaf") {
EquipmentAutomationFrameworkCellInstanceParseCheck();
// Start
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action><a:MessageID>urn:uuid:a1188d61-df04-4955-b1e4-b90faff95d4d</a:MessageID><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><CreateSequence xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm"><AcksTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></AcksTo><Offer><Identifier>urn:uuid:35310d6d-3d17-419c-9b4f-cf20b705e5c9</Identifier></Offer></CreateSequence></s:Body></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:Sequence s:mustUnderstand="1"><r:Identifier>urn:uuid:739e01d3-5573-48a4-8bbb-53e2dfc344af</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><a:Action s:mustUnderstand="1">http://tempuri.org/ICellControllerManager/StartAllCellInstances</a:Action><a:MessageID>urn:uuid:8758eec2-b4dc-4338-ba3d-235aa15e634c</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><StartAllCellInstances xmlns="http://tempuri.org/"><cellInstances xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><b:string>SP101_FileArchiver</b:string></cellInstances><updateInfo>Started by EC\ecphares</updateInfo></StartAllCellInstances></s:Body></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:Sequence s:mustUnderstand="1"><r:Identifier>urn:uuid:739e01d3-5573-48a4-8bbb-53e2dfc344af</r:Identifier><r:MessageNumber>1</r:MessageNumber></r:Sequence><a:Action s:mustUnderstand="1">http://tempuri.org/ICellControllerManager/StartAllCellInstances</a:Action><a:MessageID>urn:uuid:8758eec2-b4dc-4338-ba3d-235aa15e634c</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><StartAllCellInstances xmlns="http://tempuri.org/"><cellInstances xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><b:string>SP101_FileArchiver</b:string></cellInstances><updateInfo>Started by EC\ecphares</updateInfo></StartAllCellInstances></s:Body></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:35310d6d-3d17-419c-9b4f-cf20b705e5c9</r:Identifier><r:AcknowledgementRange Lower="1" Upper="1"/></r:SequenceAcknowledgement><r:Sequence s:mustUnderstand="1"><r:Identifier>urn:uuid:739e01d3-5573-48a4-8bbb-53e2dfc344af</r:Identifier><r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body/></s:Envelope>
// <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><r:SequenceAcknowledgement><r:Identifier>urn:uuid:35310d6d-3d17-419c-9b4f-cf20b705e5c9</r:Identifier><r:AcknowledgementRange Lower="1" Upper="2"/></r:SequenceAcknowledgement><a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action><a:MessageID>urn:uuid:b2bb5329-c846-4cd1-98a8-343136923702</a:MessageID><a:To s:mustUnderstand="1">http://eaf-prod.mes.infineon.com:9003/CellControllerManager</a:To></s:Header><s:Body><r:TerminateSequence><r:Identifier>urn:uuid:739e01d3-5573-48a4-8bbb-53e2dfc344af</r:Identifier></r:TerminateSequence></s:Body></s:Envelope>
EquipmentAutomationFrameworkCellInstanceVerb(host, port, cellName, verbBy, restart: false, stop: false, start: true);
}
private static void EquipmentAutomationFrameworkCellInstanceVerb(string host, int port, string cellName, string verbBy, bool restart, bool stop, bool start) {
#pragma warning disable SYSLIB0014
WebClient webClient = new();
#pragma warning restore SYSLIB0014
string xml;
string verb;
Envelope? envelope;
string updateInfoVerb;
StringBuilder stringBuilder = new();
string cellControllerManager = $"http://{host}:{port}/CellControllerManager";
if (!restart && !stop && !start) {
throw new Exception();
} else if (restart && stop && start) {
throw new Exception();
} else if (restart) {
updateInfoVerb = "Restarted";
verb = "RestartAllCellInstances";
} else if (stop) {
updateInfoVerb = "Stopped";
verb = "StopAllCellInstancesResponse";
} else if (start) {
updateInfoVerb = "Started";
verb = "StartAllCellInstances";
} else
throw new Exception();
string headerMessageID621 = Guid.NewGuid().ToString();
string bodyIdentifier621 = Guid.NewGuid().ToString();
_ = stringBuilder.Append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\">").
Append("<s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action>").
Append("<a:MessageID>urn:uuid:").Append(headerMessageID621).Append("</a:MessageID>").
Append("<a:To s:mustUnderstand=\"1\">").Append(cellControllerManager).Append("</a:To>").
Append("</s:Header><s:Body><CreateSequence xmlns=\"http://schemas.xmlsoap.org/ws/2005/02/rm\"><AcksTo>").
Append("<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></AcksTo><Offer>").
Append("<Identifier>urn:uuid:").Append(bodyIdentifier621).Append("</Identifier>").
Append("</Offer></CreateSequence></s:Body></s:Envelope>");
xml = stringBuilder.ToString();
_ = stringBuilder.Clear();
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+xml; charset=utf-8");
xml = webClient.UploadString(cellControllerManager, xml);
envelope = ParseXML<Envelope>(xml, throwExceptions: true);
if (envelope is null || envelope.Body is null || envelope.Body.CreateSequenceResponse is null || string.IsNullOrEmpty(envelope.Body.CreateSequenceResponse.Identifier)) {
throw new Exception("Invalid reply! Example [urn:uuid:f1aa1fa8-9099-48b6-b27f-50e6c098605b]");
}
string headerSequenceIdentifier895 = envelope.Body.CreateSequenceResponse.Identifier["urn:uuid:".Length..];
string headerMessageID895 = Guid.NewGuid().ToString();
_ = stringBuilder.Append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\">").
Append("<s:Header><r:Sequence s:mustUnderstand=\"1\">").
Append("<r:Identifier>urn:uuid:").Append(headerSequenceIdentifier895).Append("</r:Identifier>").
Append("<r:MessageNumber>1</r:MessageNumber></r:Sequence>").
Append("<a:Action s:mustUnderstand=\"1\">http://tempuri.org/ICellControllerManager/").Append(verb).Append("</a:Action>").
Append("<a:MessageID>urn:uuid:").Append(headerMessageID895).Append("</a:MessageID>").
Append("<a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>").
Append("<a:To s:mustUnderstand=\"1\">").Append(cellControllerManager).Append("</a:To>").
Append("</s:Header><s:Body>").
Append('<').Append(verb).Append(" xmlns=\"http://tempuri.org/\">").
Append("<cellInstances xmlns:b=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">").
Append("<b:string>").Append(cellName).Append("</b:string>").
Append("</cellInstances>").
Append("<updateInfo>").Append(updateInfoVerb).Append(" by ").Append(verbBy).Append("</updateInfo>").
Append("</").Append(verb).Append('>').
Append("</s:Body></s:Envelope>");
xml = stringBuilder.ToString();
_ = stringBuilder.Clear();
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+xml; charset=utf-8");
xml = webClient.UploadString(cellControllerManager, xml);
_ = ParseXML<Envelope>(xml, throwExceptions: true);
string headerSequenceAcknowledgementIdentifier748 = bodyIdentifier621;
string headerSequenceIdentifier748 = headerSequenceIdentifier895;
_ = stringBuilder.Append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\">").
Append("<s:Header><r:SequenceAcknowledgement>").
Append("<r:Identifier>urn:uuid:").Append(headerSequenceAcknowledgementIdentifier748).Append("</r:Identifier>").
Append("<r:AcknowledgementRange Lower=\"1\" Upper=\"1\"/></r:SequenceAcknowledgement><r:Sequence s:mustUnderstand=\"1\">").
Append("<r:Identifier>urn:uuid:").Append(headerSequenceIdentifier748).Append("</r:Identifier>").
Append("<r:MessageNumber>2</r:MessageNumber><r:LastMessage/></r:Sequence>").
Append("<a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage</a:Action>").
Append("<a:To s:mustUnderstand=\"1\">").Append(cellControllerManager).Append("</a:To>").
Append("</s:Header><s:Body/></s:Envelope>");
xml = stringBuilder.ToString();
_ = stringBuilder.Clear();
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+xml; charset=utf-8");
xml = webClient.UploadString(cellControllerManager, xml);
_ = ParseXML<Envelope>(xml, throwExceptions: true);
string headerSequenceAcknowledgementIdentifier707 = bodyIdentifier621;
string headerMessageID707 = Guid.NewGuid().ToString();
string bodyTerminateSequenceIdentifier707 = headerSequenceIdentifier895;
_ = stringBuilder.Append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:r=\"http://schemas.xmlsoap.org/ws/2005/02/rm\" xmlns:a=\"http://www.w3.org/2005/08/addressing\">").
Append("<s:Header><r:SequenceAcknowledgement>").
Append("<r:Identifier>urn:uuid:").Append(headerSequenceAcknowledgementIdentifier707).Append("</r:Identifier>").
Append("<r:AcknowledgementRange Lower=\"1\" Upper=\"2\"/></r:SequenceAcknowledgement>").
Append("<a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence</a:Action>").
Append("<a:MessageID>urn:uuid:").Append(headerMessageID707).Append("</a:MessageID>").
Append("<a:To s:mustUnderstand=\"1\">").Append(cellControllerManager).Append("</a:To>").
Append("</s:Header><s:Body>").
Append("<r:TerminateSequence>").
Append("<r:Identifier>urn:uuid:").Append(bodyTerminateSequenceIdentifier707).Append("</r:Identifier>").
Append("</r:TerminateSequence>").
Append("</s:Body></s:Envelope>");
xml = stringBuilder.ToString();
_ = stringBuilder.Clear();
webClient.Headers.Clear();
webClient.Headers.Add("Accept-Encoding: gzip, deflate");
webClient.Headers.Add("Content-Type: application/soap+xml; charset=utf-8");
xml = webClient.UploadString(cellControllerManager, xml);
_ = ParseXML<Envelope>(xml, throwExceptions: true);
}
}

View File

@ -0,0 +1,89 @@
using System.Collections.ObjectModel;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250618 {
private record Record(string Directory, List<string> Files);
internal static void MoveAllButXOfEach(ILogger<Worker> logger, List<string> args) {
int keep = int.Parse(args[5]);
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
string searchPattern = args[2];
string split = args[4].Split('~')[0];
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string destinationDirectory = Path.GetFullPath(args[3].Split('~')[0]);
if (destinationDirectory.Contains(sourceDirectory)) {
throw new Exception("Not allowed!");
}
ReadOnlyCollection<string> directories = GetDirectories(sourceDirectory);
MoveAllButXOfEachB(logger, searchPattern, sourceDirectory, destinationDirectory, split, keep, directories);
}
private static ReadOnlyCollection<string> GetDirectories(string sourceDirectory) {
List<string> results = [sourceDirectory];
results.AddRange(Directory.GetDirectories(sourceDirectory, "*", SearchOption.AllDirectories));
return results.AsReadOnly();
}
private static void MoveAllButXOfEachB(ILogger<Worker> logger, string searchPattern, string sourceDirectory, string destinationDirectory, string split, int keep, ReadOnlyCollection<string> directories) {
string[] files;
int sourceDirectoryLength = sourceDirectory.Length;
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs;
foreach (string directory in directories) {
files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
keyValuePairs = GetFiles(split, files);
foreach (KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair in keyValuePairs) {
if (keyValuePair.Value.Count <= keep) {
continue;
} else {
MoveAllButXOfEachC(logger, sourceDirectoryLength, destinationDirectory, keyValuePair);
}
}
}
}
private static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetFiles(string split, string[] files) {
Dictionary<string, ReadOnlyCollection<string>> results = [];
string key;
List<string>? collection;
Dictionary<string, List<string>> keyValuePairs = [];
foreach (string file in files) {
key = Path.GetFileName(file).Split(split)[0];
if (!keyValuePairs.TryGetValue(key, out collection)) {
keyValuePairs.Add(key, []);
if (!keyValuePairs.TryGetValue(key, out collection)) {
throw new Exception();
}
}
collection.Add(file);
}
foreach (KeyValuePair<string, List<string>> keyValuePair in keyValuePairs) {
results.Add(keyValuePair.Key, keyValuePair.Value.OrderByDescending(l => l).ToArray().AsReadOnly());
}
return results.AsReadOnly();
}
private static void MoveAllButXOfEachC(ILogger<Worker> logger, int sourceDirectoryLength, string destinationDirectory, KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair) {
string file;
string checkFile;
string checkDirectory;
for (int i = 1; i < keyValuePair.Value.Count; i++) {
file = keyValuePair.Value[i];
checkFile = $"{destinationDirectory}{file[sourceDirectoryLength..]}";
checkDirectory = Path.GetDirectoryName(checkFile) ?? throw new Exception();
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
logger.LogInformation("<{file}> moved", file);
}
}
}

View File

@ -0,0 +1,89 @@
using System.Collections.ObjectModel;
using DiscUtils.Iso9660;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250628 {
private record Record(string Path,
long Size,
long Ticks);
internal static void LogIsoInformation(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
string searchPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string[] searchPatternFiles = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
ReadOnlyCollection<Record> records = GetRecords(searchPatternFiles);
LogIsoInformation(logger, records, args[3]);
}
private static ReadOnlyCollection<Record> GetRecords(string[] searchPatternFiles) {
List<Record> results = [];
Record record;
string[] files;
FileInfo fileInfo;
foreach (string searchPatternFile in searchPatternFiles) {
fileInfo = new(searchPatternFile);
record = new(searchPatternFile, fileInfo.Length, fileInfo.LastWriteTime.Ticks);
results.Add(record);
}
return results.AsReadOnly();
}
private static string GetSizeWithSuffix(long value) {
string result;
int i = 0;
string[] SizeSuffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
if (value < 0) {
result = "-" + GetSizeWithSuffix(-value);
} else {
while (Math.Round(value / 1024f) >= 1) {
value /= 1024;
i++;
}
result = string.Format("{0:n1} {1}", value, SizeSuffixes[i]);
}
return result;
}
private static void LogIsoInformation(ILogger<Worker> logger, ReadOnlyCollection<Record> records, string letter) {
string size;
string[] files;
List<string> messages = [];
Record[] sorted = records.OrderBy(l => l.Ticks).ToArray();
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper");
foreach (Record record in sorted) {
if (string.IsNullOrEmpty(record.Path)) {
using FileStream fileStream = File.OpenRead(record.Path);
CDReader reader = new(fileStream, true);
files = reader.GetFiles("", "*");
foreach (string _ in files) {
}
}
size = GetSizeWithSuffix(record.Size);
messages.Add($"#; {size}");
messages.Add($"#; {new DateTime(record.Ticks):yyyy-MM-dd HH:mm:ss.fff}");
messages.Add($"$driveLetter = \"{letter}:\"");
messages.Add($"$isoImg = \"{record.Path}\"");
messages.Add("$diskImg = Mount-DiskImage -ImagePath $isoImg -NoDriveLetter");
messages.Add("$volInfo = $diskImg | Get-Volume");
messages.Add("mountvol $driveLetter $volInfo.UniqueId");
messages.Add(string.Empty);
}
if (Directory.Exists(directory)) {
File.WriteAllText(Path.Combine(directory, ".lsv"), string.Join(Environment.NewLine, messages));
} else {
messages.Reverse();
foreach (string message in messages) {
logger.LogInformation(message);
}
}
}
}

View File

@ -0,0 +1,427 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250701 {
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(JsonElement))]
internal partial class JsonElementSourceGenerationContext : JsonSerializerContext {
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(ReadOnlyDictionary<string, ReadOnlyDictionary<string, string>>))]
internal partial class ReadOnlyDictionaryStringReadOnlyDictionaryStringStringSourceGenerationContext : JsonSerializerContext {
}
internal static void ProcessDataStandardFormatTo(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
logger.LogInformation(args[6]);
logger.LogInformation(args[7]);
string[] segments;
string extension = args[5];
string timeColumn = args[4];
string searchPattern = args[2];
int sizeFilter = int.Parse(args[3]);
string[] columns = args[6].Split(',');
Dictionary<string, string> columnMapping = [];
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string destinationDirectory = Path.GetFullPath(args[7].Split('~')[0]);
foreach (string column in columns) {
segments = column.Split('~');
columnMapping.Add(segments[0], segments[1]);
}
string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
ProcessDataStandardFormatTo(logger, sourceDirectory, searchPattern, sizeFilter, timeColumn, extension, columnMapping.AsReadOnly(), destinationDirectory, directories.AsReadOnly());
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
}
private static void ProcessDataStandardFormatTo(ILogger<Worker> logger, string sourceDirectory, string searchPattern, int sizeFilter, string timeColumn, string extension, ReadOnlyDictionary<string, string> columnMapping, string destinationDirectory, ReadOnlyCollection<string> directories) {
string text;
string? json;
string[] files;
string markdown;
string checkFile;
string[] matches;
FileInfo fileInfo;
string? pipeTable;
string? collections;
string directoryName;
string checkDirectory;
foreach (string directory in directories) {
if (sizeFilter < 987654321 && Path.GetFileName(directory).Contains('-')) {
continue;
}
files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
foreach (string file in files) {
fileInfo = new(file);
if (fileInfo.LastWriteTime > DateTime.Now.AddSeconds(-5)) {
continue;
}
directoryName = Path.GetFileName(fileInfo.DirectoryName);
if (fileInfo.Length > sizeFilter && !directoryName.StartsWith('Z')) {
checkDirectory = Path.Combine(sourceDirectory, $"Z{directoryName}");
if (!Directory.Exists(checkDirectory)) {
_ = Directory.CreateDirectory(checkDirectory);
}
checkFile = Path.Combine(checkDirectory, fileInfo.Name);
if (File.Exists(checkFile)) {
continue;
}
File.Move(file, checkFile);
continue;
}
checkDirectory = Path.Combine(destinationDirectory, directoryName);
if (!Directory.Exists(checkDirectory)) {
_ = Directory.CreateDirectory(checkDirectory);
}
checkFile = Path.Combine(checkDirectory, $"{fileInfo.Name}{extension}");
if (File.Exists(checkFile)) {
continue;
}
if (extension == ".md") {
collections = GetMarkdown(logger, timeColumn, columnMapping, file, fileInfo.Name);
if (string.IsNullOrEmpty(collections)) {
logger.LogWarning("collections is null");
continue;
}
text = collections;
} else if (extension == ".pipe.md") {
json = GetJavaScriptObjectNotation(logger, file);
if (string.IsNullOrEmpty(json)) {
logger.LogWarning("json is null");
continue;
}
pipeTable = GetPipeTable(logger, json);
if (string.IsNullOrEmpty(pipeTable)) {
logger.LogWarning("pipeTable is null");
continue;
}
markdown = $"# {fileInfo.Name}{Environment.NewLine}{Environment.NewLine}{pipeTable}{Environment.NewLine}";
text = markdown;
} else if (extension == ".json") {
json = GetJavaScriptObjectNotation(logger, file);
if (string.IsNullOrEmpty(json)) {
logger.LogWarning("json is null");
continue;
}
text = json;
} else {
logger.LogWarning("{extension} is not mapped!", extension);
continue;
}
File.WriteAllText(checkFile, text);
File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime);
logger.LogInformation("<{checkFile}> was written", checkFile);
}
}
}
private static string? GetMarkdown(ILogger<Worker> logger, string timeColumn, ReadOnlyDictionary<string, string> columnMapping, string file, string name) {
string? result = null;
string[] lines = File.ReadAllLines(file);
int? columnTitlesLine = GetProcessDataStandardFormatColumnTitlesLine(lines);
if (columnTitlesLine is null) {
logger.LogWarning("<{columnTitlesLine}> is null", nameof(columnTitlesLine));
} else {
if (lines.Length < columnTitlesLine.Value + 1) {
logger.LogWarning("<{lines}>(s)", lines.Length);
} else {
result = GetMarkdown(timeColumn, columnMapping, name, lines, columnTitlesLine);
}
}
return result;
}
private static int? GetProcessDataStandardFormatColumnTitlesLine(string[] lines) {
int? result = null;
for (int i = 0; i < lines.Length; i++) {
if (lines[i].StartsWith("END_OFFSET") && i + 3 < lines.Length) {
result = i + 1;
break;
}
}
return result;
}
private static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetKeyValuePairs(int columnTitlesLine, string[] lines) {
Dictionary<string, ReadOnlyCollection<string>> results = [];
string value;
string[] segments;
List<List<string>> collections = [];
string[] columns = lines[columnTitlesLine].Split('\t');
foreach (string column in columns) {
collections.Add([]);
}
for (int i = columnTitlesLine + 1; i < lines.Length; i++) {
if (lines[i].StartsWith("NUM_DATA_ROWS")) {
break;
}
segments = lines[i].Split('\t');
if (segments.Length > columns.Length) {
continue;
}
for (int c = 0; c < segments.Length; c++) {
collections[c].Add(segments[c]);
}
}
for (int i = 0; i < collections.Count; i++) {
if (collections[i].Count > 1) {
if (string.IsNullOrEmpty(collections[i][0])) {
collections[i][0] = collections[i][1];
}
if (string.IsNullOrEmpty(collections[i][^1])) {
collections[i][^1] = collections[i][^2];
}
}
results.Add(columns[i].Trim('"'), collections[i].AsReadOnly());
}
return results.AsReadOnly();
}
private static string? GetMarkdown(string timeColumn, ReadOnlyDictionary<string, string> columnMapping, string name, string[] lines, int? columnTitlesLine) {
string? result;
List<string> charts = [];
List<string> results = [];
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = GetKeyValuePairs(columnTitlesLine.Value, lines);
string[] columns = keyValuePairs.Keys.OrderBy(l => l).ToArray();
if (!columns.Contains(timeColumn)) {
result = null;
} else {
string? alias;
string[] mappedColumns = columnMapping.Keys.ToArray();
string labels = string.Join(',', keyValuePairs[timeColumn].Select(l => l.Replace(',', '_')));
foreach (string column in columns) {
if (column == timeColumn) {
continue;
}
if (!columnMapping.TryGetValue(column, out alias) || keyValuePairs[column].Count == 0 || string.IsNullOrEmpty(keyValuePairs[column][0])) {
results.Add(string.Concat("## ", column, Environment.NewLine, Environment.NewLine,
"- ", string.Join(',', keyValuePairs[column].Select(l => l.Replace(',', '_'))), Environment.NewLine
));
} else {
charts.Add(string.Concat(
"## ", column, " - ", alias, Environment.NewLine, Environment.NewLine,
"```chart", Environment.NewLine,
"{\"type\": \"line\", \"data\": {\"datasets\": [{", Environment.NewLine,
"\"label\": \"", column, " - ", alias, "\",", Environment.NewLine,
"\"data\": [", string.Join(',', keyValuePairs[column].Select(l => l.Replace(',', '_'))), "],", Environment.NewLine,
"\"borderColor\": \"rgb(75, 192, 192)\"", Environment.NewLine,
"}],", Environment.NewLine,
"\"labels\": [", labels, "]", Environment.NewLine,
"}}", Environment.NewLine,
"```", Environment.NewLine
));
}
}
if (results.Count == 0 && charts.Count == 0) {
result = null;
} else {
string[] segments;
results.Add($"## Footer{Environment.NewLine}");
results.Insert(0, $"# {name}{Environment.NewLine}");
for (int i = columnTitlesLine.Value + 1; i < lines.Length; i++) {
if (lines[i].StartsWith("NUM_DATA_ROWS")) {
for (int j = i; j < lines.Length; j++) {
results.Add($"- {lines[j]}");
}
}
}
results.Add(string.Empty);
results.Add(string.Empty);
result = $"{string.Join(Environment.NewLine, results)}{string.Join(Environment.NewLine, charts)}";
}
}
return result;
}
private static string? GetJavaScriptObjectNotation(ILogger<Worker> logger, string file) {
string? result = null;
string[] lines = File.ReadAllLines(file);
int? columnTitlesLine = GetProcessDataStandardFormatColumnTitlesLine(lines);
if (columnTitlesLine is null) {
logger.LogWarning("<{columnTitlesLine}> is null", nameof(columnTitlesLine));
} else {
if (lines.Length < columnTitlesLine.Value + 1) {
logger.LogWarning("<{lines}>(s)", lines.Length);
} else {
result = GetJavaScriptObjectNotation(columnTitlesLine.Value, lines);
}
}
return result;
}
private static string GetJavaScriptObjectNotation(int columnTitlesLine, string[] lines) {
#pragma warning disable CA1845, IDE0057
string result;
string record;
string value;
string[] segments;
string? json = null;
List<string> records = [];
ReadOnlyCollection<string>? footerLines = null;
string[] columns = lines[columnTitlesLine].Split('\t');
for (int i = columnTitlesLine + 1; i < lines.Length; i++) {
if (lines[i].StartsWith("NUM_DATA_ROWS")) {
footerLines = GetFooterLines(lines, i);
break;
}
record = "{";
segments = lines[i].Split('\t');
if (segments.Length > columns.Length) {
continue;
}
for (int c = 0; c < segments.Length; c++) {
value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\"");
record += string.Concat('"', columns[c].Trim('"'), '"', ':', '"', value, '"', ',');
}
record = string.Concat(record.Substring(0, record.Length - 1), '}');
records.Add(record);
}
if (footerLines is not null && footerLines.Count > 0) {
ReadOnlyDictionary<string, string> footerKeyValuePairs = GetFooterKeyValuePairs(footerLines);
ReadOnlyDictionary<string, ReadOnlyDictionary<string, string>> logisticKeyValuePairs = GetLogisticKeyValuePairs(footerLines, footerKeyValuePairs);
json = JsonSerializer.Serialize(logisticKeyValuePairs, ReadOnlyDictionaryStringReadOnlyDictionaryStringStringSourceGenerationContext.Default.ReadOnlyDictionaryStringReadOnlyDictionaryStringString);
}
string footerText = string.IsNullOrEmpty(json) || json == "{}" ? string.Empty : $",{Environment.NewLine}\"ProcessDataStandardFormat\":{Environment.NewLine}{json}";
result = string.Concat(
'{',
Environment.NewLine,
'"',
"Count",
'"',
": ",
records.Count,
',',
Environment.NewLine,
'"',
"Records",
'"',
": ",
Environment.NewLine,
'[',
Environment.NewLine,
string.Join($",{Environment.NewLine}", records),
Environment.NewLine,
']',
footerText,
'}');
return result;
#pragma warning restore CA1845, IDE0057
}
private static ReadOnlyCollection<string> GetFooterLines(string[] lines, int i) {
List<string> results = [];
for (int j = i; j < lines.Length; j++) {
results.Add(lines[j]);
if (lines[j].StartsWith("END_HEADER"))
break;
}
return results.AsReadOnly();
}
private static ReadOnlyDictionary<string, string> GetFooterKeyValuePairs(ReadOnlyCollection<string> footerLines) {
Dictionary<string, string> results = [];
string[] segments;
foreach (string footerLine in footerLines) {
segments = footerLine.Split('\t');
if (segments.Length != 2 || string.IsNullOrEmpty(segments[1].Trim())) {
continue;
}
if (segments[1].Contains(';')) {
continue;
} else {
results.Add(segments[0], segments[1]);
}
}
return results.AsReadOnly();
}
private static ReadOnlyDictionary<string, ReadOnlyDictionary<string, string>> GetLogisticKeyValuePairs(ReadOnlyCollection<string> footerLines, ReadOnlyDictionary<string, string> footerKeyValuePairs) {
Dictionary<string, ReadOnlyDictionary<string, string>> results = [];
string[] segments;
string[] subSegments;
string[] subSubSegments;
Dictionary<string, string>? keyValue;
results.Add("Footer", footerKeyValuePairs);
Dictionary<string, Dictionary<string, string>> keyValuePairs = [];
foreach (string footerLine in footerLines) {
segments = footerLine.Split('\t');
if (segments.Length != 2 || string.IsNullOrEmpty(segments[1].Trim())) {
continue;
}
if (!segments[1].Contains(';') || !segments[1].Contains('=')) {
continue;
} else {
subSegments = segments[1].Split(';');
if (subSegments.Length < 1) {
continue;
}
if (!keyValuePairs.TryGetValue(segments[0], out keyValue)) {
keyValuePairs.Add(segments[0], []);
if (!keyValuePairs.TryGetValue(segments[0], out keyValue)) {
throw new Exception();
}
}
foreach (string segment in subSegments) {
subSubSegments = segment.Split('=');
if (subSubSegments.Length != 2) {
continue;
}
keyValue.Add(subSubSegments[0], subSubSegments[1]);
}
}
}
foreach (KeyValuePair<string, Dictionary<string, string>> keyValuePair in keyValuePairs) {
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
}
return results.AsReadOnly();
}
private static string? GetPipeTable(ILogger<Worker> logger, string json) {
string? result = null;
string? value;
string[]? columns = null;
List<string> values = [];
List<string> results = [];
Dictionary<string, string> keyValuePairs = [];
JsonElement jsonElement = JsonSerializer.Deserialize(json, JsonElementSourceGenerationContext.Default.JsonElement);
JsonElement[] jsonElements = jsonElement.EnumerateArray().ToArray();
foreach (JsonElement j in jsonElements) {
values.Clear();
keyValuePairs.Clear();
foreach (JsonProperty jsonProperty in j.EnumerateObject().ToArray()) {
if (columns is null) {
JsonProperty[] jsonProperties = j.EnumerateObject().OrderBy(l => l.Name).ToArray();
columns = jsonProperties.Select(l => l.Name).ToArray();
results.Add($"|{string.Join('|', columns)}|");
results.Add($"|{string.Join('|', columns.Select(l => '-'))}|");
}
keyValuePairs.Add(jsonProperty.Name, jsonProperty.Value.ToString());
}
foreach (string column in columns) {
if (!keyValuePairs.TryGetValue(column, out value)) {
values.Add(new string(' ', column.Length));
} else {
values.Add(value.PadLeft(column.Length, ' '));
}
}
results.Add($"|{string.Join('|', values)}|");
}
if (results.Count > 0) {
result = string.Join(Environment.NewLine, results);
}
return result;
}
}

View File

@ -0,0 +1,112 @@
using System.Drawing;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250705 {
internal static void ExportFaces(ILogger<Worker> logger, List<string> args) {
string checkFile;
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
logger.LogInformation(args[6]);
logger.LogInformation(args[7]);
string locationDigits = args[6];
string[] mp = args[4].Split('~');
string[] mwg = args[5].Split('~');
string[] xyz = args[3].Split(',');
string[] fileNames = args[2].Split('~');
string[] wholePercentages = args[7].Split('~');
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
if (mp.Length != mwg.Length || mp.Length != wholePercentages.Length) {
logger.LogWarning("Lengths don't match!");
} else {
foreach (string fileName in fileNames) {
checkFile = Path.Combine(sourceDirectory, fileName);
if (!File.Exists(checkFile)) {
logger.LogWarning("<{file}> doesn't exist!", args[2]);
continue;
}
for (int i = 0; i < mp.Length; i++) {
if (wholePercentages[i].StartsWith("x-")) {
logger.LogWarning("Skipping {wholePercentages}", wholePercentages[i]);
continue;
}
Ext(checkFile, xyz, i, mp[i], mwg[i], locationDigits, wholePercentages[i]);
}
}
}
}
private static void Ext(string checkFile, string[] xyz, int i, string mp, string mwg, string locationDigits, string wholePercentages) {
int width = int.Parse(xyz[0]);
int height = int.Parse(xyz[1]);
string[] mpValues = mp.Split(',');
string[] mwgValues = mwg.Split(',');
float mpWidth = float.Parse(mpValues[2]) * width;
float mpHeight = float.Parse(mpValues[3]) * height;
double mpLeft = (float.Parse(mpValues[0]) * width) - (mpWidth * .5);
double mpTop = (float.Parse(mpValues[1]) * height) - (mpHeight * .5);
Extract(file: checkFile,
width: mpWidth,
height: mpHeight,
left: mpLeft,
top: mpTop,
suffix: $"-{i}mp.jpg");
float mwgWidth = float.Parse(mwgValues[2]) * width;
float mwgHeight = float.Parse(mwgValues[3]) * height;
double mwgLeft = double.Parse(mwgValues[0]) * width;
double mwgTop = double.Parse(mwgValues[1]) * height;
Extract(file: checkFile,
width: mwgWidth,
height: mwgHeight,
left: mwgLeft,
top: mwgTop,
suffix: $"-{i}mwg.jpg");
int length = (int.Parse(locationDigits) - 1) / 4;
string[] segments =
[
wholePercentages[..1],
wholePercentages.Substring(1, length),
wholePercentages.Substring(3, length),
wholePercentages.Substring(5, length),
wholePercentages.Substring(7, length)
];
if (string.Join(string.Empty, segments) == wholePercentages) {
if (int.TryParse(segments[1], out int xWholePercent)
&& int.TryParse(segments[2], out int yWholePercent)
&& int.TryParse(segments[3], out int wWholePercent)
&& int.TryParse(segments[4], out int hWholePercent)) {
float factor = 100;
RectangleF rectangleF = new(xWholePercent / factor, yWholePercent / factor, wWholePercent / factor, hWholePercent / factor);
float rectangleFWidth = rectangleF.Width * width;
float rectangleFHeight = rectangleF.Height * height;
double rectangleFLeft = rectangleF.Left * width;
double rectangleFTop = rectangleF.Top * height;
Extract(file: checkFile,
width: rectangleFWidth,
height: rectangleFHeight,
left: rectangleFLeft,
top: rectangleFTop,
suffix: $"-{i}whole-percentages.jpg");
}
}
}
private static void Extract(string file, float width, float height, double left, double top, string suffix) {
RectangleF rectangle = new((float)left, (float)top, width, height);
using (Bitmap source = new(file)) {
using (Bitmap bitmap = new((int)width, (int)height)) {
using (Graphics graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new RectangleF(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
bitmap.Save($"{file}{suffix}");
}
}
}
}

View File

@ -0,0 +1,234 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250709 {
// [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
// [JsonSerializable(typeof(JsonElement[]))]
// internal partial class Helper20250709JsonElementSourceGenerationContext : JsonSerializerContext {
// }
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Raw))]
private partial class RawSourceGenerationContext : JsonSerializerContext {
}
private record Raw(int Count, JsonElement[] Records);
// cspell::disable
private record Reactor([property: JsonPropertyName("id12")] double? Id12,
[property: JsonPropertyName("id13")] double? Id13,
[property: JsonPropertyName("id15")] double? Id15,
[property: JsonPropertyName("id153")] string? Id153,
[property: JsonPropertyName("id154")] long? Id154,
[property: JsonPropertyName("id16")] double? Id16,
[property: JsonPropertyName("id172")] double? Id172,
[property: JsonPropertyName("id173")] double? Id173,
[property: JsonPropertyName("id176")] double? Id176,
[property: JsonPropertyName("id178")] string? Id178,
[property: JsonPropertyName("id18")] double? Id18,
[property: JsonPropertyName("id183")] double? Id183,
[property: JsonPropertyName("id19")] double? Id19,
[property: JsonPropertyName("id193")] double? Id193,
[property: JsonPropertyName("id21")] double? Id21,
[property: JsonPropertyName("id22")] double? Id22,
[property: JsonPropertyName("id221")] string? Id221,
[property: JsonPropertyName("id222")] string? Id222,
[property: JsonPropertyName("id223")] string? Id223,
[property: JsonPropertyName("id25")] double? Id25,
[property: JsonPropertyName("id26")] double? Id26,
[property: JsonPropertyName("id27")] double? Id27,
[property: JsonPropertyName("id28")] double? Id28,
[property: JsonPropertyName("id29")] double? Id29,
[property: JsonPropertyName("id30")] double? Id30,
[property: JsonPropertyName("id37")] double? Id37,
[property: JsonPropertyName("id38")] double? Id38,
[property: JsonPropertyName("id39")] double? Id39,
[property: JsonPropertyName("id40")] double? Id40,
[property: JsonPropertyName("id41")] double? Id41,
[property: JsonPropertyName("id42")] double? Id42,
[property: JsonPropertyName("id57")] double? Id57,
[property: JsonPropertyName("id58")] double? Id58,
[property: JsonPropertyName("id61")] double? Id61,
[property: JsonPropertyName("id62")] double? Id62,
[property: JsonPropertyName("id78")] long? Id78,
[property: JsonPropertyName("id79")] long? Id79,
[property: JsonPropertyName("id80")] string? Id80,
[property: JsonPropertyName("id81")] long? Id81,
[property: JsonPropertyName("id82")] long? Id82,
[property: JsonPropertyName("id83")] long? Id83,
[property: JsonPropertyName("id84")] long? Id84,
[property: JsonPropertyName("id85")] string? Id85,
[property: JsonPropertyName("id86")] long? Id86,
[property: JsonPropertyName("id87")] long? Id87,
[property: JsonPropertyName("id93")] long? Id93,
[property: JsonPropertyName("vp93")] long? Vp93,
[property: JsonPropertyName("vp154")] long? Vp154,
[property: JsonPropertyName("vp78")] long? Vp78,
[property: JsonPropertyName("vp83")] long? Vp83,
[property: JsonPropertyName("vp176")] double? Vp176,
[property: JsonPropertyName("vp80")] string? Vp80,
[property: JsonPropertyName("vp85")] string? Vp85,
[property: JsonPropertyName("vp153")] string? Vp153,
[property: JsonPropertyName("vp221")] string? Vp221,
[property: JsonPropertyName("vp222")] string? Vp222,
[property: JsonPropertyName("vp223")] string? Vp223,
[property: JsonPropertyName("YYYYMMDDhhmmsscc")] string? YYYYMMDDhhmmsscc,
[property: JsonPropertyName("CenterTemp")] double? CenterTemp,
[property: JsonPropertyName("CenterSetpt")] double? CenterSetpt,
[property: JsonPropertyName("FrontTemp")] double? FrontTemp,
[property: JsonPropertyName("PPSTEPNAME")] string? PPSTEPNAME,
[property: JsonPropertyName("SystemState")] long? SystemState,
[property: JsonPropertyName("FrontSetpt")] double? FrontSetpt,
[property: JsonPropertyName("LVC1Ratio")] double? LVC1Ratio,
[property: JsonPropertyName("LVC1Carrier")] double? LVC1Carrier,
[property: JsonPropertyName("TotalWaferCount")] double? TotalWaferCount,
[property: JsonPropertyName("TIME")] string? TIME,
[property: JsonPropertyName("SideTemp")] double? SideTemp,
[property: JsonPropertyName("SCRDrive4")] double? SCRDrive4,
[property: JsonPropertyName("SideSetpt")] double? SideSetpt,
[property: JsonPropertyName("SCRLOAD4")] double? SCRLOAD4,
[property: JsonPropertyName("RearTemp")] double? RearTemp,
[property: JsonPropertyName("RearSetpt")] double? RearSetpt,
[property: JsonPropertyName("LeftDefaultRecipe")] string? LeftDefaultRecipe,
[property: JsonPropertyName("RightDefaultRecipe")] string? RightDefaultRecipe,
[property: JsonPropertyName("RecipeCompleteMsg")] string? RecipeCompleteMsg,
[property: JsonPropertyName("N2H2Setpt")] double? N2H2Setpt,
[property: JsonPropertyName("N2H2Flow")] double? N2H2Flow,
[property: JsonPropertyName("HCLSetpt")] double? HCLSetpt,
[property: JsonPropertyName("HCLFlow")] double? HCLFlow,
[property: JsonPropertyName("HCLHISetpt")] double? HCLHISetpt,
[property: JsonPropertyName("HCLHIFlow")] double? HCLHIFlow,
[property: JsonPropertyName("NSRCSetpt")] double? NSRCSetpt,
[property: JsonPropertyName("NSRCFlow")] double? NSRCFlow,
[property: JsonPropertyName("NDILSetpt")] double? NDILSetpt,
[property: JsonPropertyName("NDILFlow")] double? NDILFlow,
[property: JsonPropertyName("NINJSetpt")] double? NINJSetpt,
[property: JsonPropertyName("NINJFlow")] double? NINJFlow,
[property: JsonPropertyName("LVC1Setpt")] double? LVC1Setpt,
[property: JsonPropertyName("LVC1Flow")] double? LVC1Flow,
[property: JsonPropertyName("ROTSetpt")] double? ROTSetpt,
[property: JsonPropertyName("ROTSpeed")] double? ROTSpeed,
[property: JsonPropertyName("LL1State")] long? LL1State,
[property: JsonPropertyName("LL1Init")] long? LL1Init,
[property: JsonPropertyName("LL1Lotid")] string? LL1Lotid,
[property: JsonPropertyName("LL1WafersIn")] long? LL1WafersIn,
[property: JsonPropertyName("LL1WfrCnt")] long? LL1WfrCnt,
[property: JsonPropertyName("LL2State")] long? LL2State,
[property: JsonPropertyName("LL2Init")] long? LL2Init,
[property: JsonPropertyName("LL2Lotid")] string? LL2Lotid,
[property: JsonPropertyName("LL2WafersIn")] long? LL2WafersIn,
[property: JsonPropertyName("LL2WfrCnt")] long? LL2WfrCnt,
[property: JsonPropertyName("ProcessState")] long? ProcessState);
// [property: JsonPropertyName("A_BASIC_TYPE")] string? ABasicType,
// [property: JsonPropertyName("A_INFO")] string? AInfo,
// [property: JsonPropertyName("A_INFO2")] string? AInfo2,
// [property: JsonPropertyName("A_JOBID")] string? AJobid,
// [property: JsonPropertyName("A_LAYER")] string? ALayer,
// [property: JsonPropertyName("A_LAYER2")] string? ALayer2,
// [property: JsonPropertyName("A_MES_ENTITY")] string? AMesEntity,
// [property: JsonPropertyName("A_MID")] string? AMID,
// [property: JsonPropertyName("A_NULL_DATA")] string? ANullData,
// [property: JsonPropertyName("A_PPID")] string? APPID,
// [property: JsonPropertyName("A_PROCESS_JOBID")] string? AProcessJobid,
// [property: JsonPropertyName("A_PRODUCT")] string? AProduct,
// [property: JsonPropertyName("A_SEQUENCE")] string? ASequence,
// [property: JsonPropertyName("A_WAFER_ID")] string? AWaferId,
// [property: JsonPropertyName("A_WAFER_POS")] string? AWaferPos
// cspell::enable,
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, NumberHandling = JsonNumberHandling.AllowReadingFromString)]
[JsonSerializable(typeof(Reactor))]
private partial class ReactorSourceGenerationContext : JsonSerializerContext {
}
internal static void JavaScriptObjectNotationToReactor(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
string[] segments;
string extension = args[4];
string searchPattern = args[2];
int sizeFilter = int.Parse(args[3]);
string[] columns = args[5].Split(',');
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string destinationDirectory = Path.GetFullPath(args[6].Split('~')[0]);
string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
JavaScriptObjectNotationTo(logger, sourceDirectory, searchPattern, sizeFilter, extension, columns.AsReadOnly(), destinationDirectory, directories.AsReadOnly());
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
}
private static void JavaScriptObjectNotationTo(ILogger<Worker> logger, string sourceDirectory, string searchPattern, int sizeFilter, string extension, ReadOnlyCollection<string> columnMapping, string destinationDirectory, ReadOnlyCollection<string> directories) {
Raw? raw;
string text;
string json;
string[] files;
string checkFile;
Reactor? reactor;
FileInfo fileInfo;
string directoryName;
string checkDirectory;
foreach (string directory in directories) {
if (sizeFilter < 987654321 && Path.GetFileName(directory).Contains('-')) {
continue;
}
files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
foreach (string file in files) {
fileInfo = new(file);
if (fileInfo.LastWriteTime > DateTime.Now.AddSeconds(-5)) {
continue;
}
directoryName = Path.GetFileName(fileInfo.DirectoryName);
if (fileInfo.Length > sizeFilter && !directoryName.StartsWith('Z')) {
checkDirectory = Path.Combine(sourceDirectory, $"Z{directoryName}");
if (!Directory.Exists(checkDirectory)) {
_ = Directory.CreateDirectory(checkDirectory);
}
checkFile = Path.Combine(checkDirectory, fileInfo.Name);
if (File.Exists(checkFile)) {
continue;
}
File.Move(file, checkFile);
continue;
}
checkDirectory = Path.Combine(destinationDirectory, directoryName);
if (!Directory.Exists(checkDirectory)) {
_ = Directory.CreateDirectory(checkDirectory);
}
checkFile = Path.Combine(checkDirectory, $"{fileInfo.Name}{extension}");
if (File.Exists(checkFile)) {
continue;
}
if (extension == ".agg") {
json = File.ReadAllText(fileInfo.FullName);
raw = JsonSerializer.Deserialize(json, RawSourceGenerationContext.Default.Raw);
if (raw is null) {
continue;
}
foreach (JsonElement jsonElement in raw.Records) {
try {
reactor = JsonSerializer.Deserialize(jsonElement.ToString().Replace("\"\"", "null"), ReactorSourceGenerationContext.Default.Reactor);
} catch (Exception) {
reactor = null;
}
if (reactor is null) {
continue;
}
if (reactor is null) {
continue;
}
}
}
}
}
}
}

View File

@ -0,0 +1,98 @@
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250710 {
[GeneratedRegex(@"^(?<stream>S[0-9]{1,2})(?<function>F[0-9]{1,2}) W-Bit=(?<wait>[01])")]
private static partial Regex StreamFunction();
internal static void StripLog(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
string[] segments;
string searchPattern = args[3];
string directoryPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string[] directories = Directory.GetDirectories(sourceDirectory, directoryPattern, SearchOption.TopDirectoryOnly);
LogToTrace(logger, sourceDirectory, searchPattern, directories.AsReadOnly());
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
}
private static void LogToTrace(ILogger<Worker> logger, string sourceDirectory, string searchPattern, ReadOnlyCollection<string> directories) {
string text;
string[] lines;
string[] files;
string checkFile;
FileInfo fileInfo;
string directoryName;
const string extension = ".trc";
foreach (string directory in directories) {
files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
foreach (string file in files) {
fileInfo = new(file);
if (fileInfo.LastWriteTime > DateTime.Now.AddSeconds(-5)) {
continue;
}
directoryName = Path.GetFileName(fileInfo.DirectoryName);
checkFile = Path.Combine(directoryName, $"{fileInfo.Name}{extension}");
if (File.Exists(checkFile)) {
continue;
}
lines = File.ReadAllLines(fileInfo.FullName);
LogToTrace(logger, fileInfo, lines, checkFile);
}
}
}
private static void LogToTrace(ILogger<Worker> logger, FileInfo fileInfo, string[] lines, string checkFile) {
string line;
Match match;
string body;
string[] segments;
string previousLine;
List<string> log = [];
for (int i = 1; i < lines.Length - 1; i++) {
line = lines[i];
if (!line.StartsWith('S')) {
continue;
}
match = StreamFunction().Match(line);
if (!match.Success) {
continue;
}
segments = lines[i - 1].Split(" ");
if (segments.Length < 2) {
continue;
}
if (!segments[0].Contains(' ')) {
continue;
}
body = GetBody(lines, i, segments[0].Split(' '));
log.Add($"{segments[0]} {line} ~ {body}");
}
if (log.Count > 0) {
File.WriteAllText(checkFile, string.Join(Environment.NewLine, log));
File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime);
logger.LogInformation("<{checkFile}> was written", checkFile);
}
}
private static string GetBody(string[] lines, int i, string[] segments) {
string result;
List<string> results = [];
for (int j = i + 1; j < lines.Length; j++) {
if (lines[j].StartsWith(segments[0]))
break;
results.Add(lines[j].Trim());
}
result = string.Join('_', results);
return result;
}
}

View File

@ -81,8 +81,6 @@ internal static class HelperDay
ADO2024.PI2.Helper20240711.GitRemoteRemove(logger, args); ADO2024.PI2.Helper20240711.GitRemoteRemove(logger, args);
else if (args[1] == "Day-Helper-2024-07-18") else if (args[1] == "Day-Helper-2024-07-18")
ADO2024.PI2.Helper20240718.JsonToMarkdown(logger, args); ADO2024.PI2.Helper20240718.JsonToMarkdown(logger, args);
else if (args[1] == "Day-Helper-2024-07-24")
ADO2024.PI2.Helper20240724.CopyDirectories(logger, args);
else if (args[1] == "Day-Helper-2024-07-28") else if (args[1] == "Day-Helper-2024-07-28")
ADO2024.PI2.Helper20240728.DownloadSslCertificates(logger, args); ADO2024.PI2.Helper20240728.DownloadSslCertificates(logger, args);
else if (args[1] == "Day-Helper-2024-08-05") else if (args[1] == "Day-Helper-2024-08-05")
@ -117,6 +115,72 @@ internal static class HelperDay
ADO2024.PI3.Helper20241031.GetComplete(logger, args); ADO2024.PI3.Helper20241031.GetComplete(logger, args);
else if (args[1] == "Day-Helper-2024-11-08") else if (args[1] == "Day-Helper-2024-11-08")
ADO2024.PI4.Helper20241108.WriteMarkdown(logger, args); ADO2024.PI4.Helper20241108.WriteMarkdown(logger, args);
else if (args[1] == "Day-Helper-2024-11-15")
ADO2024.PI4.Helper20241115.GetComplete(logger, args);
else if (args[1] == "Day-Helper-2024-12-04")
ADO2024.PI4.Helper20241204.ConvertToUTF8(logger, args);
else if (args[1] == "Day-Helper-2024-12-12")
ADO2024.PI4.Helper20241212.Rename(logger, args);
else if (args[1] == "Day-Helper-2024-12-17")
ADO2024.PI4.Helper20241217.Backup(logger, args);
else if (args[1] == "Day-Helper-2024-12-24")
ADO2024.PI4.Helper20241224.Compare(logger, args);
else if (args[1] == "Day-Helper-2025-01-01")
ADO2025.PI4.Helper20250101.MoveToDelete(logger, args);
else if (args[1] == "Day-Helper-2025-01-14")
ADO2025.PI4.Helper20250114.Rename(logger, args);
else if (args[1] == "Day-Helper-2025-01-26")
ADO2025.PI4.Helper20250126.Move(logger, args);
else if (args[1] == "Day-Helper-2025-02-04")
ADO2025.PI4.Helper20250204.ExtractKanban(logger, args);
else if (args[1] == "Day-Helper-2025-02-18")
ADO2025.PI5.Helper20250218.MoveToArchive(logger, args);
else if (args[1] == "Day-Helper-2025-02-19")
ADO2025.PI5.Helper20250219.Compare(logger, args);
else if (args[1] == "Day-Helper-2025-02-28")
ADO2025.PI5.Helper20250228.PostgresDumpToJson(logger, args);
else if (args[1] == "Day-Helper-2025-03-01")
ADO2025.PI5.Helper20250301.PocketBaseImportWithDeno(logger, args);
else if (args[1] == "Day-Helper-2025-03-05")
ADO2025.PI5.Helper20250305.WriteNginxFileSystemDelta(logger, args);
else if (args[1] == "Day-Helper-2025-03-06")
ADO2025.PI5.Helper20250306.ProcessDataStandardFormatToJson(logger, args);
else if (args[1] == "Day-Helper-2025-03-15")
ADO2025.PI5.Helper20250315.Empty(logger, args);
else if (args[1] == "Day-Helper-2025-03-20")
ADO2025.PI5.Helper20250320.SortCodeMethods(logger, args, cancellationToken);
else if (args[1] == "Day-Helper-2025-03-21")
ADO2025.PI5.Helper20250321.MoveToLast(logger, args);
else if (args[1] == "Day-Helper-2025-04-04")
ADO2025.PI5.Helper20250404.KumaToGatus(logger, args);
else if (args[1] == "Day-Helper-2025-04-07")
ADO2025.PI5.Helper20250407.Sync(logger, args);
else if (args[1] == "Day-Helper-2025-04-21")
ADO2025.PI5.Helper20250421.FreeFileSyncChangeCreatedDate(logger, args);
else if (args[1] == "Day-Helper-2025-04-29")
ADO2025.PI5.Helper20250429.WriteNginxFileSystem(logger, args);
else if (args[1] == "Day-Helper-2025-05-05")
ADO2025.PI5.Helper20250505.HyperTextMarkupLanguageToPortableDocumentFormat(logger, args);
else if (args[1] == "Day-Helper-2025-05-19")
ADO2025.PI6.Helper20250519.LiveSync(logger, args);
else if (args[1] == "Day-Helper-2025-05-21")
ADO2025.PI6.Helper20250521.MatchDirectory(logger, args);
else if (args[1] == "Day-Helper-2025-06-01")
ADO2025.PI6.Helper20250601.EquipmentAutomationFrameworkStatus(logger, args);
else if (args[1] == "Day-Helper-2025-06-02")
ADO2025.PI6.Helper20250602.EquipmentAutomationFrameworkCellInstanceStateImageVerbIf(logger, args);
else if (args[1] == "Day-Helper-2025-06-18")
ADO2025.PI6.Helper20250618.MoveAllButXOfEach(logger, args);
else if (args[1] == "Day-Helper-2025-06-28")
ADO2025.PI6.Helper20250628.LogIsoInformation(logger, args);
else if (args[1] == "Day-Helper-2025-07-01")
ADO2025.PI6.Helper20250701.ProcessDataStandardFormatTo(logger, args);
else if (args[1] == "Day-Helper-2025-07-05")
ADO2025.PI6.Helper20250705.ExportFaces(logger, args);
else if (args[1] == "Day-Helper-2025-07-09")
ADO2025.PI6.Helper20250709.JavaScriptObjectNotationToReactor(logger, args);
else if (args[1] == "Day-Helper-2025-07-10")
ADO2025.PI6.Helper20250710.StripLog(logger, args);
else else
throw new Exception(appSettings.Company); throw new Exception(appSettings.Company);
} }

View File

@ -65,7 +65,7 @@ internal static class Helper20231130
return result; return result;
} }
private static ReadOnlyCollection<string> GetSystemStateValues(List<string> lines, string[] columns, int keyColumnIndex, ReadOnlyDictionary<string, string> systemStates) private static ReadOnlyCollection<string> GetSystemStateValues(List<string> lines, string[] columns, int keyColumnIndex, ReadOnlyDictionary<string, string> systemStateToNames)
{ {
List<string> results = []; List<string> results = [];
string[] values; string[] values;
@ -79,7 +79,7 @@ internal static class Helper20231130
keyColumnValue = values[keyColumnIndex]; keyColumnValue = values[keyColumnIndex];
if (string.IsNullOrEmpty(keyColumnValue)) if (string.IsNullOrEmpty(keyColumnValue))
continue; continue;
if (!systemStates.TryGetValue(keyColumnValue, out systemState)) if (!systemStateToNames.TryGetValue(keyColumnValue, out systemState))
continue; continue;
if (results.Contains(systemState)) if (results.Contains(systemState))
continue; continue;
@ -106,7 +106,7 @@ internal static class Helper20231130
string missingKeyDirectory = Path.Combine(sourceDirectory, "Missing-Key"); string missingKeyDirectory = Path.Combine(sourceDirectory, "Missing-Key");
if (!Directory.Exists(missingKeyDirectory)) if (!Directory.Exists(missingKeyDirectory))
_ = Directory.CreateDirectory(missingKeyDirectory); _ = Directory.CreateDirectory(missingKeyDirectory);
ReadOnlyDictionary<string, string> systemStates = GetSystemStates(); ReadOnlyDictionary<string, string> systemStateToNames = GetSystemStates();
ReadOnlyCollection<Record> records = GetRecords(sourceDirectory, timestampFormat); ReadOnlyCollection<Record> records = GetRecords(sourceDirectory, timestampFormat);
foreach (Record record in records) foreach (Record record in records)
{ {
@ -132,7 +132,7 @@ internal static class Helper20231130
continue; continue;
} }
logger.LogInformation("{timestamp} triggered", record.TimeStamp); logger.LogInformation("{timestamp} triggered", record.TimeStamp);
systemStateValues = GetSystemStateValues(lines, columns, keyColumnIndex.Value, systemStates); systemStateValues = GetSystemStateValues(lines, columns, keyColumnIndex.Value, systemStateToNames);
if (systemStateValues.Count == 0) if (systemStateValues.Count == 0)
{ {
File.Move(record.File, Path.Combine(sourceDirectory, missingKeyDirectory, record.FileName)); File.Move(record.File, Path.Combine(sourceDirectory, missingKeyDirectory, record.FileName));
@ -142,7 +142,6 @@ internal static class Helper20231130
systemState = string.Join('-', systemStateValues); systemState = string.Join('-', systemStateValues);
checkFileName = Path.Combine(Path.GetDirectoryName(record.File) ?? throw new Exception(), $"{record.Equipment}-{record.TimeStamp}-{systemState}.pdsf"); checkFileName = Path.Combine(Path.GetDirectoryName(record.File) ?? throw new Exception(), $"{record.Equipment}-{record.TimeStamp}-{systemState}.pdsf");
File.WriteAllLines(checkFileName, lines); File.WriteAllLines(checkFileName, lines);
File.Delete(record.File);
if (DateTime.TryParseExact(record.TimeStamp, timestampFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) if (DateTime.TryParseExact(record.TimeStamp, timestampFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
File.SetLastWriteTime(checkFileName, dateTime); File.SetLastWriteTime(checkFileName, dateTime);
} }

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
# Stage 1: Build Stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# restore
COPY ["src/File-Folder-Helper/File-Folder-Helper.csproj", "File-Folder-Helper/"]
RUN dotnet restore 'File-Folder-Helper/File-Folder-Helper.csproj'
# build
COPY ["src/File-Folder-Helper", "File-Folder-Helper/"]
WORKDIR /src/File-Folder-Helper
RUN dotnet build 'File-Folder-Helper.csproj' -c Release -o /app/build
# Stage 2: Publish Stage
FROM build AS publish
RUN dotnet publish 'File-Folder-Helper.csproj' -c Release -o /app/publish
# Stage 3: Run Stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0
ENV ASPNETCORE_HTTP_PORTS=5001
EXPOSE 5001
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT [ "dotnet", "File-Folder-Helper.dll" ]

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
@ -13,16 +13,15 @@
<RuntimeHostConfigurationOption Include="AssemblyName" Value="File-Folder-Helper" /> <RuntimeHostConfigurationOption Include="AssemblyName" Value="File-Folder-Helper" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommonMark.NET" Version="0.15.1" />
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" /> <PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" /> <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.17" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="8.0.5" /> <PackageReference Include="System.Text.Json" Version="9.0.5" />
<PackageReference Include="TextCopy" Version="6.2.1" /> <PackageReference Include="TextCopy" Version="6.2.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="16.1.3" /> <PackageReference Include="YamlDotNet" Version="16.3.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -41,4 +41,31 @@ internal static class HelperDirectory
return new(results); return new(results);
} }
internal static ReadOnlyCollection<ReadOnlyCollection<string>> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter)
{
List<ReadOnlyCollection<string>> results = [];
string[] files;
if (!fileSearchFilter.Contains('*'))
fileSearchFilter = string.Concat('*', fileSearchFilter);
if (!directorySearchFilter.Contains('*'))
directorySearchFilter = string.Concat('*', directorySearchFilter);
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
results.Add(Directory.GetFiles(directory, fileSearchFilter, SearchOption.TopDirectoryOnly).AsReadOnly());
string[] directories = Directory.GetDirectories(directory, directorySearchFilter, SearchOption.TopDirectoryOnly);
foreach (string innerDirectory in directories)
{
try
{
files = Directory.GetFiles(innerDirectory, fileSearchFilter, SearchOption.AllDirectories);
if (files.Length == 0)
continue;
results.Add(files.AsReadOnly());
}
catch (UnauthorizedAccessException)
{ continue; }
}
return results.AsReadOnly();
}
} }

View File

@ -990,62 +990,6 @@ internal static partial class HelperGenealogicalDataCommunication
throw new NotSupportedException(); throw new NotSupportedException();
} }
[Obsolete]
private static PersonHour GetPersonHour(string personDisplayDirectoryName, int hour) =>
hour == 0 ? new('U', 'U', 'U') :
hour == 1 ? new('U', 'U', 'U') :
hour == 2 ? new('U', 'U', 'U') :
hour == 3 ? new('A', 'U', 'Y') :
hour == 4 ? new('A', 'F', 'Y') :
hour == 5 ? new('A', 'M', 'Y') :
hour == 6 ? new('A', 'F', 'N') :
hour == 7 ? new('A', 'M', 'N') :
hour == 13 ? new('D', 'U', 'Y') :
hour == 14 ? new('D', 'F', 'Y') :
hour == 15 ? new('D', 'M', 'Y') :
hour == 16 ? new('D', 'F', 'N') :
hour == 17 ? new('D', 'M', 'N') :
throw new NotImplementedException(personDisplayDirectoryName);
[Obsolete]
private static string[] GetNewLines(ReadOnlyCollection<string> lines, Name? name, string[] kFiles, string[] mFiles, string[] k2Files, string[] m2Files)
{
List<string> results = [];
string six;
int? birthLastLine = null;
string slugName = WindowsFileSystem().Replace(string.Concat(name?.ForwardSlashFull), "_");
string? title = name?.ForwardSlashFull is null ? null : kFiles.Contains(slugName) ? "1 TITL K" : k2Files.Contains(slugName) ? "1 TITL K2" : mFiles.Contains(slugName) ? "1 TITL M" : m2Files.Contains(slugName) ? "1 TITL M2" : null;
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].Length < 6)
throw new NotImplementedException();
results.Add(lines[i]);
six = lines[i][..6];
if (lines[i][0] == '1')
{
if (six != "1 BIRT")
continue;
for (int j = i + 1; j < lines.Count; j++)
{
results.Add(lines[j]);
if (lines[j].Length < 7)
throw new NotImplementedException();
if (lines[j][0] == '1')
{
birthLastLine = j;
if (title is not null)
results.Insert(j, title);
break;
}
i++;
}
}
}
if (title is not null && birthLastLine is null)
results.Add(title);
return results.ToArray();
}
internal static void FileSystemToGenealogicalDataCommunication(AppSettings appSettings, ILogger<Worker> logger, List<string> args) internal static void FileSystemToGenealogicalDataCommunication(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
{ {
Input input = GetInput(args); Input input = GetInput(args);

View File

@ -133,31 +133,30 @@ internal static partial class HelperKanbanMetadata
List<string> results = [kanbanIndexH1, string.Empty]; List<string> results = [kanbanIndexH1, string.Empty];
string h1; string h1;
TimeSpan timeSpan; TimeSpan timeSpan;
List<string> lines;
LineNumber lineNumber; LineNumber lineNumber;
Record[] sorted = (from l in records orderby l.GroupCount, l.FileInfo.LastWriteTime descending select l).ToArray(); Record[] sorted = (from l in records orderby l.GroupCount, l.FileInfo.LastWriteTime descending select l).ToArray();
foreach (Record record in sorted) foreach (Record record in sorted)
{ {
if (record.ItemLineNumber == 0) if (record.ItemLineNumber == 0)
throw new NotSupportedException(); throw new NotSupportedException();
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo); lineNumber = HelperMarkdown.GetLineNumbers(record.FileInfo);
if (lines.Count == 0) if (lineNumber.Lines.Count == 0)
continue; continue;
timeSpan = new(record.FileInfo.LastWriteTime.Ticks - record.FileInfo.CreationTime.Ticks); timeSpan = new(record.FileInfo.LastWriteTime.Ticks - record.FileInfo.CreationTime.Ticks);
h1 = lineNumber.H1 is null ? Path.GetFileNameWithoutExtension(record.FileInfo.Name) : lines[lineNumber.H1.Value]; h1 = lineNumber.H1 is null ? Path.GetFileNameWithoutExtension(record.FileInfo.Name) : lineNumber.Lines[lineNumber.H1.Value];
results.Add($"#{h1}"); results.Add($"#{h1}");
results.Add(string.Empty); results.Add(string.Empty);
results.Add("```yaml"); results.Add("```yaml");
results.Add($"CreationTime: {record.FileInfo.CreationTime:yyyy-MM-dd}"); results.Add($"CreationTime: {record.FileInfo.CreationTime:yyyy-MM-dd}");
results.Add($"LastWriteTime: {record.FileInfo.LastWriteTime:yyyy-MM-dd}"); results.Add($"LastWriteTime: {record.FileInfo.LastWriteTime:yyyy-MM-dd}");
results.Add($"TotalDays: {Math.Round(timeSpan.TotalDays, 2)}"); results.Add($"TotalDays: {Math.Round(timeSpan.TotalDays, 2)}");
if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value) if (lineNumber.FrontMatterYamlEnd is not null && lineNumber.Lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
{ {
for (int i = 0; i < lineNumber.FrontMatterYamlEnd; i++) for (int i = 0; i < lineNumber.FrontMatterYamlEnd; i++)
{ {
if (lines[i] == "---") if (lineNumber.Lines[i] == "---")
continue; continue;
results.Add(lines[i]); results.Add(lineNumber.Lines[i]);
} }
} }
results.Add($"status: {record.GroupCount}-{record.Group}"); results.Add($"status: {record.GroupCount}-{record.Group}");
@ -175,7 +174,7 @@ internal static partial class HelperKanbanMetadata
File.WriteAllText(file, string.Join(Environment.NewLine, results)); File.WriteAllText(file, string.Join(Environment.NewLine, results));
} }
internal static void SetMetadata(string sourceDirectory, ReadOnlyCollection<string> kanbanIndexFileLines, LineNumber kanbanIndexFileLineNumber, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles) internal static void SetMetadata(string sourceDirectory, LineNumber kanbanIndexFileLineNumber, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{ {
bool? match; bool? match;
bool gitCheck; bool gitCheck;
@ -184,23 +183,24 @@ internal static partial class HelperKanbanMetadata
List<string> lines; List<string> lines;
LineNumber lineNumber; LineNumber lineNumber;
string? directory = Path.GetDirectoryName(sourceDirectory); string? directory = Path.GetDirectoryName(sourceDirectory);
List<Record> records = GetCollectionFromIndex(sourceDirectory, kanbanIndexFileLines); List<Record> records = GetCollectionFromIndex(sourceDirectory, kanbanIndexFileLineNumber.Lines);
if (directory is not null && kanbanIndexFileLineNumber.H1 is not null) if (directory is not null && kanbanIndexFileLineNumber.H1 is not null)
{ {
string checkDirectory = Path.Combine(directory, ".vscode", "helper"); string checkDirectory = Path.Combine(directory, ".vscode", "helper");
if (Directory.Exists(checkDirectory)) if (Directory.Exists(checkDirectory))
{ {
WriteKanbanBoardFile(checkDirectory, records, kanbanIndexFileLines[kanbanIndexFileLineNumber.H1.Value]); WriteKanbanBoardFile(checkDirectory, records, kanbanIndexFileLineNumber.Lines[kanbanIndexFileLineNumber.H1.Value]);
WriteKanbanBoardYmlView(checkDirectory, records, kanbanIndexFileLines[kanbanIndexFileLineNumber.H1.Value]); WriteKanbanBoardYmlView(checkDirectory, records, kanbanIndexFileLineNumber.Lines[kanbanIndexFileLineNumber.H1.Value]);
} }
} }
foreach (Record record in records) foreach (Record record in records)
{ {
if (record.ItemLineNumber == 0) if (record.ItemLineNumber == 0)
throw new NotSupportedException(); throw new NotSupportedException();
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo); lineNumber = HelperMarkdown.GetLineNumbers(record.FileInfo);
if (lines.Count == 0) if (lineNumber.Lines.Count == 0)
continue; continue;
lines = lineNumber.Lines.ToList();
statusLine = $"status: {record.GroupCount}-{record.Group}"; statusLine = $"status: {record.GroupCount}-{record.Group}";
paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]); paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]);
match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase; match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase;
@ -235,8 +235,8 @@ internal static partial class HelperKanbanMetadata
else else
{ {
FileInfo fileInfo = new(indexFile); FileInfo fileInfo = new(indexFile);
(List<string> lines, LineNumber lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo); LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
SetMetadata(fullPath, new(lines), lineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles: new([])); SetMetadata(fullPath, lineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles: new([]));
} }
} }

View File

@ -16,8 +16,20 @@ internal static partial class HelperMarkdown
private record Input(string? Destination, private record Input(string? Destination,
string Source, string Source,
string? ReplaceWithTitle,
bool UseProcessStart); bool UseProcessStart);
private record Table(ReadOnlyCollection<string> Columns,
int ColumnsLine,
ReadOnlyCollection<ReadOnlyCollection<string>> Rows,
string? Title);
private record Block(int Line,
string? Json,
int RowCount,
ReadOnlyCollection<string>? Rows,
string? Title);
private record Record(string Directory, private record Record(string Directory,
string File, string File,
string[] Lines); string[] Lines);
@ -30,7 +42,7 @@ internal static partial class HelperMarkdown
string FileNameWithoutExtension, string FileNameWithoutExtension,
ReadOnlyDictionary<string, object> FrontMatterYaml, ReadOnlyDictionary<string, object> FrontMatterYaml,
string H1, string H1,
bool IsGitOthersModifiedAndDeletedExcludingStandard, bool? IsGitOthersModifiedAndDeletedExcludingStandard,
bool IsKanbanIndex, bool IsKanbanIndex,
bool IsKanbanMarkdown, bool IsKanbanMarkdown,
DateTime LastWriteDateTime, DateTime LastWriteDateTime,
@ -49,15 +61,27 @@ internal static partial class HelperMarkdown
private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath); private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath);
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Dictionary<string, object>))]
internal partial class DictionaryStringAndObjectSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Dictionary<string, JsonElement>))] [JsonSerializable(typeof(Dictionary<string, JsonElement>))]
internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext
{ {
} }
[GeneratedRegex("([A-Z]+(.))")]
private static partial Regex UpperCase();
[GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")] [GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")]
private static partial Regex HtmlColor(); private static partial Regex HtmlColor();
[GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
private static partial Regex InvalidCharacter();
private static MarkdownExtra GetMarkdownExtra(MarkdownFileAndLines markdownFileAndLines) private static MarkdownExtra GetMarkdownExtra(MarkdownFileAndLines markdownFileAndLines)
{ {
MarkdownExtra result; MarkdownExtra result;
@ -193,7 +217,7 @@ internal static partial class HelperMarkdown
return result; return result;
} }
private static ReadOnlyCollection<string> GetFromMatterYamlLines(List<string> lines, LineNumber lineNumber) private static ReadOnlyCollection<string> GetFromMatterYamlLines(ReadOnlyCollection<string> lines, LineNumber lineNumber)
{ {
List<string> results = []; List<string> results = [];
if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value) if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
@ -301,13 +325,13 @@ internal static partial class HelperMarkdown
if (!directoryInfo.Exists || (!string.IsNullOrEmpty(directoryInfo.LinkTarget) && !Directory.Exists(directoryInfo.LinkTarget))) if (!directoryInfo.Exists || (!string.IsNullOrEmpty(directoryInfo.LinkTarget) && !Directory.Exists(directoryInfo.LinkTarget)))
continue; continue;
collection.AddRange(GetFiles(appSettings, directoryInfo, SearchOption.TopDirectoryOnly)); collection.AddRange(GetFiles(appSettings, directoryInfo, SearchOption.TopDirectoryOnly));
foreach (FileInfo file in collection) foreach (FileInfo fileInfo in collection)
results.Add(file); results.Add(fileInfo);
} }
return new(results); return (from l in results orderby l.FullName select l).ToArray().AsReadOnly();
} }
internal static (List<string>, LineNumber) GetStatusAndFrontMatterYamlEndLineNumbers(FileInfo fileInfo) internal static LineNumber GetLineNumbers(FileInfo fileInfo)
{ {
string line; string line;
int? h1LineNumber = null; int? h1LineNumber = null;
@ -315,6 +339,10 @@ internal static partial class HelperMarkdown
int? statusLineNumber = null; int? statusLineNumber = null;
int? createdLineNumber = null; int? createdLineNumber = null;
int? updatedLineNumber = null; int? updatedLineNumber = null;
int? progressLineNumber = null;
int? completedLineNumber = null;
int? startedColumnsLineNumber = null;
int? completedColumnsLineNumber = null;
int? frontMatterYamlEndLineNumber = null; int? frontMatterYamlEndLineNumber = null;
Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default; Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default;
string[] lines = File.ReadAllLines(fileInfo.FullName, encoding); string[] lines = File.ReadAllLines(fileInfo.FullName, encoding);
@ -350,19 +378,44 @@ internal static partial class HelperMarkdown
updatedLineNumber = i; updatedLineNumber = i;
continue; continue;
} }
if (line.Length > 10 && line[..10] == "progress: ")
{
progressLineNumber = i;
continue;
}
if (line.Length > 11 && line[..11] == "completed: ")
{
completedLineNumber = i;
continue;
}
if (line.Length > 14 && line[..14] == "startedColumns")
{
startedColumnsLineNumber = i;
continue;
}
if (line.Length > 16 && line[..16] == "completedColumns")
{
completedColumnsLineNumber = i;
continue;
}
if (h1LineNumber is null && line.Length > 2 && line[0] == '#' && line[1] == ' ') if (h1LineNumber is null && line.Length > 2 && line[0] == '#' && line[1] == ' ')
{ {
h1LineNumber = i; h1LineNumber = i;
continue; continue;
} }
} }
LineNumber lineNumber = new(createdLineNumber, LineNumber lineNumber = new(Created: createdLineNumber,
h1LineNumber, Completed: completedLineNumber,
frontMatterYamlEndLineNumber, CompletedColumns: completedColumnsLineNumber,
statusLineNumber, H1: h1LineNumber,
typeLineNumber, FrontMatterYamlEnd: frontMatterYamlEndLineNumber,
updatedLineNumber); Lines: lines.AsReadOnly(),
return (lines.ToList(), lineNumber); Progress: progressLineNumber,
Status: statusLineNumber,
StartedColumns: startedColumnsLineNumber,
Type: typeLineNumber,
Updated: updatedLineNumber);
return lineNumber;
} }
private static Dictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> frontMatterYamlLines) private static Dictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> frontMatterYamlLines)
@ -387,7 +440,7 @@ internal static partial class HelperMarkdown
return results; return results;
} }
private static ReadOnlyDictionary<string, object> GetFromMatterYaml(List<string> lines, LineNumber lineNumber) private static ReadOnlyDictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> lines, LineNumber lineNumber)
{ {
Dictionary<string, object> results = []; Dictionary<string, object> results = [];
#pragma warning disable IL3050 #pragma warning disable IL3050
@ -496,7 +549,7 @@ internal static partial class HelperMarkdown
h1Check = $"# {h1[2..]}"; h1Check = $"# {h1[2..]}";
if (h1Check.Length == h1.Length && h1Check != h1) if (h1Check.Length == h1.Length && h1Check != h1)
{ {
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
lines[markdownFile.LineNumber.H1.Value] = h1Check; lines[markdownFile.LineNumber.H1.Value] = h1Check;
File.WriteAllLines(markdownFile.File, lines); File.WriteAllLines(markdownFile.File, lines);
@ -512,7 +565,7 @@ internal static partial class HelperMarkdown
checkName = Path.Combine(markdownFile.Directory, checkFileName); checkName = Path.Combine(markdownFile.Directory, checkFileName);
if (checkName == markdownFile.File) if (checkName == markdownFile.File)
continue; continue;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
File.Move(markdownFile.File, checkName); File.Move(markdownFile.File, checkName);
result += 1; result += 1;
@ -629,6 +682,7 @@ internal static partial class HelperMarkdown
Input result; Input result;
string? destination = null; string? destination = null;
bool useProcessStart = false; bool useProcessStart = false;
string? replaceWithTitle = null;
string source = Path.GetFullPath(args[0]); string source = Path.GetFullPath(args[0]);
for (int i = 1; i < args.Count; i++) for (int i = 1; i < args.Count; i++)
{ {
@ -638,6 +692,8 @@ internal static partial class HelperMarkdown
useProcessStart = args[i + 1] == "true"; useProcessStart = args[i + 1] == "true";
else if (args[i][1] == 'd') else if (args[i][1] == 'd')
destination = Path.GetFullPath(args[i + 1]); destination = Path.GetFullPath(args[i + 1]);
else if (args[i][1] == 't')
replaceWithTitle = args[i + 1];
i++; i++;
} }
} }
@ -649,36 +705,42 @@ internal static partial class HelperMarkdown
if (!Directory.Exists(destination)) if (!Directory.Exists(destination))
_ = Directory.CreateDirectory(destination); _ = Directory.CreateDirectory(destination);
} }
result = new(destination, source, useProcessStart); result = new(Destination: destination, Source: source, ReplaceWithTitle: replaceWithTitle, UseProcessStart: useProcessStart);
return result; return result;
} }
private static ReadOnlyDictionary<string, MarkdownFileAndLines> GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles) private static ReadOnlyDictionary<string, MarkdownFileAndLines> GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection<string>? gitOthersModifiedAndDeletedExcludingStandardFiles)
{ {
Dictionary<string, MarkdownFileAndLines> results = []; Dictionary<string, MarkdownFileAndLines> results = [];
string h1; string h1;
string key; string key;
string type; string type;
bool isKanbanIndex; bool isKanbanIndex;
List<string> lines;
bool isWithinSource; bool isWithinSource;
bool isKanbanMarkdown; bool isKanbanMarkdown;
LineNumber lineNumber; LineNumber lineNumber;
MarkdownFile markdownFile; MarkdownFile markdownFile;
string fileNameWithoutExtension; string fileNameWithoutExtension;
ReadOnlyCollection<string> lines;
ReadOnlyDictionary<string, object> frontMatterYaml; ReadOnlyDictionary<string, object> frontMatterYaml;
bool isGitOthersModifiedAndDeletedExcludingStandard; bool? isGitOthersModifiedAndDeletedExcludingStandard;
ReadOnlyCollection<FileInfo> files = GetFiles(appSettings, input); ReadOnlyCollection<FileInfo> files = GetFiles(appSettings, input);
foreach (FileInfo fileInfo in files) foreach (FileInfo fileInfo in files)
{ // cSpell:disable { // cSpell:disable
if (fileInfo.DirectoryName is null) if (fileInfo.DirectoryName is null)
continue; continue;
key = Path.GetRelativePath(input.Source, fileInfo.FullName); key = Path.GetRelativePath(input.Source, fileInfo.FullName);
isWithinSource = fileInfo.FullName.Contains(input.Source); if (gitOthersModifiedAndDeletedExcludingStandardFiles is null)
isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName); isGitOthersModifiedAndDeletedExcludingStandard = null;
if (!isWithinSource && results.ContainsKey(key)) else
continue; {
(lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo); isWithinSource = fileInfo.FullName.Contains(input.Source);
isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName);
if (!isWithinSource && results.ContainsKey(key))
continue;
}
lineNumber = GetLineNumbers(fileInfo);
lines = lineNumber.Lines;
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName); fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-'); h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
frontMatterYaml = GetFromMatterYaml(lines, lineNumber); frontMatterYaml = GetFromMatterYaml(lines, lineNumber);
@ -686,34 +748,34 @@ internal static partial class HelperMarkdown
(type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber); (type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber);
else else
{ {
if (!isGitOthersModifiedAndDeletedExcludingStandard) if (isGitOthersModifiedAndDeletedExcludingStandard is not null && !isGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
type = appSettings.DefaultNoteType; type = appSettings.DefaultNoteType;
File.WriteAllLines(fileInfo.FullName, ["---", $"type: {type}\"", "---", string.Empty, $"# {h1}"]); File.WriteAllLines(fileInfo.FullName, ["---", $"type: {type}\"", "---", string.Empty, $"# {h1}"]);
lines = File.ReadAllLines(fileInfo.FullName).ToList(); lines = File.ReadAllLines(fileInfo.FullName).AsReadOnly();
} }
isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md"); isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md");
isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase); isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase);
markdownFile = new(fileInfo.CreationTime, markdownFile = new(CreationDateTime: fileInfo.CreationTime,
fileInfo.DirectoryName, Directory: fileInfo.DirectoryName,
fileInfo.Extension, Extension: fileInfo.Extension,
fileInfo.FullName, File: fileInfo.FullName,
fileInfo.Name, FileName: fileInfo.Name,
fileNameWithoutExtension, FileNameWithoutExtension: fileNameWithoutExtension,
frontMatterYaml, FrontMatterYaml: frontMatterYaml,
h1, H1: h1,
isGitOthersModifiedAndDeletedExcludingStandard, IsGitOthersModifiedAndDeletedExcludingStandard: isGitOthersModifiedAndDeletedExcludingStandard,
isKanbanIndex, IsKanbanIndex: isKanbanIndex,
isKanbanMarkdown, IsKanbanMarkdown: isKanbanMarkdown,
fileInfo.LastWriteTime, LastWriteDateTime: fileInfo.LastWriteTime,
lineNumber, LineNumber: lineNumber,
type); Type: type);
results.Add(key, new(markdownFile, lines.ToArray())); results.Add(key, new(markdownFile, lines.ToArray()));
} // cSpell:restore } // cSpell:restore
return new(results); return new(results);
} }
private static int SortFrontMatter(AppSettings appSettings, ILogger<Worker> logger, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection) private static int SortFrontMatter(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{ {
int result = 0; int result = 0;
List<string> results = []; List<string> results = [];
@ -733,7 +795,7 @@ internal static partial class HelperMarkdown
markdownFile = relativeTo.Value.MarkdownFile; markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanMarkdown) if (markdownFile.IsKanbanMarkdown)
continue; continue;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
if (markdownFile.LineNumber.FrontMatterYamlEnd is null) if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
continue; continue;
@ -767,7 +829,7 @@ internal static partial class HelperMarkdown
circularReference = false; circularReference = false;
lines = relativeTo.Value.Lines; lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile; markdownFile = relativeTo.Value.MarkdownFile;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
@ -829,7 +891,7 @@ internal static partial class HelperMarkdown
found = false; found = false;
lines = relativeTo.Value.Lines; lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile; markdownFile = relativeTo.Value.MarkdownFile;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
@ -882,7 +944,7 @@ internal static partial class HelperMarkdown
write = false; write = false;
lines = relativeTo.Value.Lines; lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile; markdownFile = relativeTo.Value.MarkdownFile;
if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
@ -910,7 +972,7 @@ internal static partial class HelperMarkdown
} }
if (!write) if (!write)
continue; continue;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
File.WriteAllLines(markdownFile.File, lines); File.WriteAllLines(markdownFile.File, lines);
result += 1; result += 1;
@ -945,7 +1007,7 @@ internal static partial class HelperMarkdown
markdownFile = relativeTo.Value.MarkdownFile; markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanIndex) if (markdownFile.IsKanbanIndex)
continue; continue;
if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
if (!File.Exists(markdownFile.File)) if (!File.Exists(markdownFile.File))
continue; continue;
@ -1010,7 +1072,7 @@ internal static partial class HelperMarkdown
} }
if (!write) if (!write)
continue; continue;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
File.WriteAllLines(markdownFile.File, lines); File.WriteAllLines(markdownFile.File, lines);
result += 1; result += 1;
@ -1027,29 +1089,23 @@ internal static partial class HelperMarkdown
MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection); MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection);
if (markdownFileAndLines is not null && File.Exists(markdownFileAndLines.MarkdownFile.File)) if (markdownFileAndLines is not null && File.Exists(markdownFileAndLines.MarkdownFile.File))
{ {
ReadOnlyDictionary<string, List<Card>> columnsToCards; ReadOnlyDictionary<string, List<Card>> columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines);
string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json"); string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json");
if (File.Exists(jsonFile)) string old = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile);
File.Delete(jsonFile); string json = columnsToCards.Count == 0 ? "{}" : JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard);
columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines); if (json != old)
if (columnsToCards.Count == 0)
File.WriteAllText(jsonFile, "{}");
else
{
string json = JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard);
File.WriteAllText(jsonFile, json); File.WriteAllText(jsonFile, json);
}
} }
} }
private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, List<string> lines, LineNumber lineNumber) private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, ReadOnlyCollection<string> lines, LineNumber lineNumber)
{ {
string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value][5..].Trim().Trim('"'); string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value][5..].Trim().Trim('"');
string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..]; string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..];
return (type, h1FromFile); return (type, h1FromFile);
} }
private static int SetFrontMatterAndH1(AppSettings appSettings, ILogger<Worker> logger, Input input, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection) private static int SetFrontMatterAndH1(AppSettings appSettings, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{ {
int result = 0; int result = 0;
List<string> results = []; List<string> results = [];
@ -1080,7 +1136,7 @@ internal static partial class HelperMarkdown
createdLine = $"created: {creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}"; createdLine = $"created: {creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
updatedLine = $"updated: {markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}"; updatedLine = $"updated: {markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
if (markdownFile.IsKanbanIndex) if (markdownFile.IsKanbanIndex)
HelperKanbanMetadata.SetMetadata(markdownFile.Directory, new(lines), markdownFile.LineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles); HelperKanbanMetadata.SetMetadata(markdownFile.Directory, markdownFile.LineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (markdownFile.LineNumber.FrontMatterYamlEnd is null) if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
{ {
if (markdownFile.LineNumber.H1 is not null) if (markdownFile.LineNumber.H1 is not null)
@ -1092,7 +1148,7 @@ internal static partial class HelperMarkdown
results.Insert(0, string.Empty); results.Insert(0, string.Empty);
} }
results.Insert(0, "---"); results.Insert(0, "---");
if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
{ {
results.Insert(0, updatedLine); results.Insert(0, updatedLine);
results.Insert(0, createdLine); results.Insert(0, createdLine);
@ -1110,7 +1166,7 @@ internal static partial class HelperMarkdown
} }
if (markdownFile.LineNumber.Type is null) if (markdownFile.LineNumber.Type is null)
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine); results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine);
if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
{ {
if (markdownFile.LineNumber.Updated is null) if (markdownFile.LineNumber.Updated is null)
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine); results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine);
@ -1146,7 +1202,7 @@ internal static partial class HelperMarkdown
} }
if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results)) if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results))
continue; continue;
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value)
continue; continue;
File.WriteAllLines(markdownFile.File, results); File.WriteAllLines(markdownFile.File, results);
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime); File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
@ -1161,36 +1217,6 @@ internal static partial class HelperMarkdown
return new(result?.MarkdownFile, result?.Lines, result?.MarkdownFile.H1, result is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(result.MarkdownFile.File))); return new(result?.MarkdownFile, result?.Lines, result?.MarkdownFile.H1, result is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(result.MarkdownFile.File)));
} }
private static List<string> GetTaskArgumentsForDayHelper20240623(Input input)
{
if (string.IsNullOrEmpty(input.Destination))
throw new NotSupportedException();
List<string> results = [];
string? vscodeDirectory = Path.GetDirectoryName(input.Destination);
string? taskFile = vscodeDirectory is null ? null : Path.Combine(vscodeDirectory, "tasks.json");
if (!string.IsNullOrEmpty(taskFile) && File.Exists(taskFile))
{
string json = File.ReadAllText(taskFile);
VSCodeTasks? vsCodeTasks = JsonSerializer.Deserialize(json, VSCodeTasksSourceGenerationContext.Default.VSCodeTasks);
if (vsCodeTasks is not null && vsCodeTasks.TaskCollection is not null)
{
foreach (VSCodeTask vsCodeTask in vsCodeTasks.TaskCollection)
{
if (vsCodeTask.Arguments is null || vsCodeTask.Arguments.Count < 4 || vsCodeTask.Arguments[3] != "Day-Helper-2024-06-23")
continue;
if (results.Count > 0)
{
results.Clear();
break;
}
results.AddRange(vsCodeTask.Arguments.Skip(2));
break;
}
}
}
return results;
}
internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken) internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken)
{ {
int updated; int updated;
@ -1198,13 +1224,13 @@ internal static partial class HelperMarkdown
Input input = GetInput(args); Input input = GetInput(args);
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(input.Source, usePathCombine, cancellationToken); ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(input.Source, usePathCombine, cancellationToken);
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles); ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
updated = SetFrontMatterAndH1(appSettings, logger, input, gitOthersModifiedAndDeletedExcludingStandardFiles, relativeToCollection); updated = SetFrontMatterAndH1(appSettings, gitOthersModifiedAndDeletedExcludingStandardFiles, relativeToCollection);
if (updated != 0) if (updated != 0)
{ {
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles); relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated); logger.LogInformation("{updated} Markdown file(s) were updated", updated);
} }
updated = SortFrontMatter(appSettings, logger, input, relativeToCollection); updated = SortFrontMatter(relativeToCollection);
if (updated != 0) if (updated != 0)
{ {
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles); relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
@ -1237,10 +1263,349 @@ internal static partial class HelperMarkdown
if (!string.IsNullOrEmpty(input.Destination)) if (!string.IsNullOrEmpty(input.Destination))
{ {
SaveColumnToCards(input, relativeToCollection); SaveColumnToCards(input, relativeToCollection);
List<string> taskArgumentsForDayHelper20240623 = GetTaskArgumentsForDayHelper20240623(input); SaveTablesToJson(appSettings, logger, input);
if (taskArgumentsForDayHelper20240623.Count > 0)
HelperDay.Select(appSettings, logger, taskArgumentsForDayHelper20240623, cancellationToken: CancellationToken.None);
} }
} }
private static void SaveTablesToJson(AppSettings appSettings, ILogger<Worker> logger, Input input)
{
if (string.IsNullOrEmpty(input.Destination))
throw new NotSupportedException();
string old;
string json;
string[] lines;
string fileName;
List<Block> blocks;
string directoryName;
List<string>? jsonLines;
MarkdownFile markdownFile;
List<string> fileNames = [];
ReadOnlyCollection<Table> tables;
Dictionary<string, List<string>> results = [];
ReadOnlyCollection<Block> javaScriptObjectNotationBlocks;
ReadOnlyCollection<Block> yetAnotherMarkupLanguageBlocks;
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles: null);
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanIndex)
continue;
if (markdownFile.IsKanbanMarkdown)
continue;
if (markdownFile.FileName.StartsWith("index.yml.md"))
continue;
if (!File.Exists(markdownFile.File))
continue;
tables = GetTables(lines);
javaScriptObjectNotationBlocks = GetJavaScriptObjectNotationBlocks(lines);
yetAnotherMarkupLanguageBlocks = GetYetAnotherMarkupLanguageBlocks(lines);
if (tables.Count == 0 && javaScriptObjectNotationBlocks.Count == 0 && yetAnotherMarkupLanguageBlocks.Count == 0)
continue;
fileName = Path.Combine(input.Destination, $"{markdownFile.FileNameWithoutExtension}.json");
if (fileNames.Contains(fileName))
continue;
fileNames.Add(fileName);
blocks = [];
blocks.AddRange(GetBlocks(tables));
blocks.AddRange(javaScriptObjectNotationBlocks);
blocks.AddRange(GetBlocks(logger, markdownFile, yetAnotherMarkupLanguageBlocks));
if (blocks.Count == 0)
continue;
directoryName = Path.GetFileName(Path.Combine(input.Destination, markdownFile.Directory));
if (!results.TryGetValue(directoryName, out jsonLines))
{
results.Add(directoryName, []);
if (!results.TryGetValue(directoryName, out jsonLines))
throw new Exception();
}
json = GetJson(input, markdownFile, blocks.AsReadOnly());
jsonLines.Add(json);
}
foreach (KeyValuePair<string, List<string>> keyValuePair in results)
{
json = string.Concat('[', Environment.NewLine, string.Join($",{Environment.NewLine}", keyValuePair.Value), Environment.NewLine, ']');
fileName = Path.Combine(input.Destination, $"{keyValuePair.Key}.json");
old = !File.Exists(fileName) ? string.Empty : File.ReadAllText(fileName);
if (json != old)
{
File.WriteAllText(fileName, json);
logger.LogInformation("Updated json file for <{fileName}>", fileName);
}
}
}
private static ReadOnlyCollection<Table> GetTables(string[] lines)
{
List<Table> results = [];
Table table;
string? title;
string[] segments;
int? columnsLine = null;
List<ReadOnlyCollection<string>> rows = [];
string[]? columns = null;
for (int i = 0; i < lines.Length; i++)
{
if (columns is null && (!lines[i].StartsWith('|') || !lines[i].EndsWith('|') || i + 3 >= lines.Length))
continue;
if (columns is null && (lines[i + 1].Length < 3 || lines[i + 1][0] != '|' || (lines[i + 1][1] is not ':' and not '-')))
continue;
if (columns is null)
{
columnsLine = i;
segments = lines[i][1..^1].Split('|');
columns = (from l in segments select l.Trim()).ToArray();
i++;
continue;
}
segments = lines[i].Length < 3 ? [] : lines[i][1..^1].Split('|');
rows.Add((from l in segments select l.Trim()).ToArray().AsReadOnly());
if (columns is not null && columnsLine is not null && string.IsNullOrEmpty(lines[i]))
{
if (columnsLine.Value - 2 > 0 && lines[columnsLine.Value - 2].StartsWith('#'))
title = lines[columnsLine.Value - 2];
else if (columnsLine.Value - 4 > 0 && lines[columnsLine.Value - 4].StartsWith('#'))
title = lines[columnsLine.Value - 4];
else
title = null;
rows.RemoveAt(rows.Count - 1);
table = new(columns.AsReadOnly(), columnsLine.Value, rows.AsReadOnly(), title);
results.Add(table);
columnsLine = null;
columns = null;
rows = [];
}
}
return results.AsReadOnly();
}
private static string GetParamCase(string value)
{
string result;
StringBuilder stringBuilder = new(value);
Match[] matches = UpperCase().Matches(value).ToArray();
for (int i = matches.Length - 1; i > -1; i--)
_ = stringBuilder.Insert(matches[i].Index, '-');
string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower());
result = string.Join('-', segments).Trim('-');
return result;
}
private static ReadOnlyCollection<Block> GetBlocks(ReadOnlyCollection<Table> tables)
{
List<Block> results = [];
Block block;
string json;
foreach (Table table in tables)
{
if (table.Title is null)
continue;
if (table.Rows.Count == 0)
continue;
json = GetJson(table);
block = new(Line: table.ColumnsLine, Json: json, RowCount: table.Rows.Count, Rows: null, Title: table.Title);
results.Add(block);
}
return results.AsReadOnly();
}
private static string GetJson(Table table)
{
string result;
string line;
string value;
string[] segments;
List<string> lines = [];
for (int i = 0; i < table.Rows.Count; i++)
{
line = "{";
segments = table.Rows[i].ToArray();
if (segments.Length != table.Columns.Count)
break;
for (int c = 0; c < segments.Length; c++)
{
value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\"");
line += string.Concat('"', table.Columns[c].Trim('"'), '"', ':', '"', value, '"', ',');
}
line = string.Concat(line[..^1], '}');
lines.Add(line);
}
result = string.Concat('[', Environment.NewLine, string.Join($",{Environment.NewLine}", lines), Environment.NewLine, ']');
return result;
}
private static ReadOnlyCollection<Block> GetJavaScriptObjectNotationBlocks(string[] lines)
{
List<Block> results = [];
string json;
string? title;
int? blockLine = null;
List<string> rows = [];
Block javaScriptObjectNotationBlock;
for (int i = 0; i < lines.Length; i++)
{
if (blockLine is null && !lines[i].StartsWith("```json"))
continue;
if (blockLine is null)
{
blockLine = i;
continue;
}
rows.Add(lines[i]);
if (blockLine is not null && lines[i] == "```")
{
rows.RemoveAt(rows.Count - 1);
if (blockLine.Value - 2 > 0 && lines[blockLine.Value - 2].StartsWith('#'))
title = lines[blockLine.Value - 2];
else if (blockLine.Value - 4 > 0 && lines[blockLine.Value - 4].StartsWith('#'))
title = lines[blockLine.Value - 4];
else
title = null;
if (string.IsNullOrEmpty(title))
continue;
json = string.Join(Environment.NewLine, rows);
javaScriptObjectNotationBlock = new(Line: blockLine.Value, Json: json, RowCount: rows.Count, Rows: null, Title: title);
results.Add(javaScriptObjectNotationBlock);
blockLine = null;
rows = [];
}
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Block> GetYetAnotherMarkupLanguageBlocks(string[] lines)
{
List<Block> results = [];
string? title = null;
int? blockLine = null;
List<string> rows = [];
Block yetAnotherMarkupLanguageBlock;
if (lines[0] == "---")
{
for (int i = 1; i < lines.Length; i++)
{
rows.Add(lines[i]);
if (lines[i] == "---")
{
rows.RemoveAt(rows.Count - 1);
yetAnotherMarkupLanguageBlock = new(Line: 0, Json: null, RowCount: rows.Count, Rows: rows.AsReadOnly(), Title: title);
results.Add(yetAnotherMarkupLanguageBlock);
rows = [];
}
}
}
rows = [];
for (int i = 0; i < lines.Length; i++)
{
if (blockLine is null && !lines[i].StartsWith("```yml") && !lines[i].StartsWith("```yaml"))
continue;
if (blockLine is null)
{
blockLine = i;
continue;
}
rows.Add(lines[i]);
if (blockLine is not null && lines[i] == "```")
{
rows.RemoveAt(rows.Count - 1);
if (blockLine.Value - 2 > 0 && lines[blockLine.Value - 2].StartsWith('#'))
title = lines[blockLine.Value - 2];
else if (blockLine.Value - 4 > 0 && lines[blockLine.Value - 4].StartsWith('#'))
title = lines[blockLine.Value - 4];
else
title = null;
yetAnotherMarkupLanguageBlock = new(Line: blockLine.Value, Json: null, RowCount: rows.Count, Rows: rows.AsReadOnly(), Title: title);
results.Add(yetAnotherMarkupLanguageBlock);
blockLine = null;
rows = [];
}
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<Block> GetBlocks(ILogger<Worker> logger, MarkdownFile markdownFile, ReadOnlyCollection<Block> yetAnotherMarkupLanguageBlocks)
{
List<Block> results = [];
string key;
Block block;
string json;
string text;
Dictionary<string, object>? keyValuePairs;
Dictionary<string, object> keyValuePairsB = [];
#pragma warning disable IL3050
IDeserializer deserializer = new DeserializerBuilder().Build();
#pragma warning restore IL3050
foreach (Block yaml in yetAnotherMarkupLanguageBlocks)
{
if (yaml.Rows is null || yaml.RowCount == 0)
continue;
if (yaml.Title is null && yaml.Line != 0)
continue;
text = string.Join(Environment.NewLine, yaml.Rows);
try
{ keyValuePairs = deserializer.Deserialize<Dictionary<string, object>>(text); }
catch (Exception)
{
keyValuePairs = null;
logger.LogWarning("Invalid yaml file for <{file}>", markdownFile.FileName);
}
if (keyValuePairs is null)
continue;
keyValuePairsB.Clear();
foreach (KeyValuePair<string, object> keyValuePair in keyValuePairs)
{
key = GetParamCase(keyValuePair.Key);
if (keyValuePairsB.ContainsKey(key))
continue;
if (keyValuePair.Value is IDictionary<object, object>)
{
logger.LogWarning("yaml contains a dictionary not allowed in AOT <{file}>", markdownFile.FileName);
break;
}
else if (keyValuePair.Value is not ICollection<object> collection)
keyValuePairsB.Add(key, keyValuePair.Value);
else
keyValuePairsB.Add(key, string.Join(',', collection));
}
try
{ json = JsonSerializer.Serialize(keyValuePairsB, DictionaryStringAndObjectSourceGenerationContext.Default.DictionaryStringObject); }
catch (Exception)
{
logger.LogWarning("yaml contains special values not allowed in AOT <{file}>", markdownFile.FileName);
continue;
}
block = new(Line: yaml.Line, Json: json, RowCount: yaml.Rows.Count, Rows: null, Title: yaml.Title);
results.Add(block);
}
return results.AsReadOnly();
}
private static string GetJson(Input input, MarkdownFile markdownFile, ReadOnlyCollection<Block> blocks)
{
string result;
string paramCase;
List<string> lines = [];
string fileNameParamCase = GetParamCase(markdownFile.FileNameWithoutExtension);
foreach (Block block in blocks.OrderBy(l => l.Line))
{
paramCase = block.Title is null ? $"Line-{block.Line}" : GetParamCase(block.Title);
if (!string.IsNullOrEmpty(input.ReplaceWithTitle) && paramCase == input.ReplaceWithTitle)
paramCase = $"{fileNameParamCase}-{block.Line}";
lines.Add($"\"{paramCase}\": {block.Json}");
}
result = string.Concat('{',
Environment.NewLine,
$"\"{fileNameParamCase}\": ",
'{',
Environment.NewLine,
string.Join($",{Environment.NewLine}", lines),
Environment.NewLine,
'}',
Environment.NewLine,
'}');
return result;
}
} }

View File

@ -10,6 +10,11 @@ internal static class HelperPdfStripperWrapper
string? Key, string? Key,
char? Parser); char? Parser);
private const char _Linc = 'L';
private const char _Ghost = 'G';
private const char _Kofax = 'K';
private const char _Stripper = 'S';
private static string GetTextFromPDF(string pdfTextStripperFileName, string sourceFileNamePdf, string destinationFileName) private static string GetTextFromPDF(string pdfTextStripperFileName, string sourceFileNamePdf, string destinationFileName)
{ {
string result; string result;
@ -28,14 +33,27 @@ internal static class HelperPdfStripperWrapper
return result; return result;
} }
private static string GetLincTextFromPDF(string lincPDFCFileName, string sourceFileNamePdf, string destinationFileName)
{
string result;
if (File.Exists(destinationFileName))
File.Delete(destinationFileName);
string arguments = string.Concat("-i \"", sourceFileNamePdf, "\" -o \"", destinationFileName, "\"");
Process? process = Process.Start(lincPDFCFileName, arguments);
_ = process?.WaitForExit(30000);
if (!File.Exists(destinationFileName))
result = string.Empty;
else
result = File.ReadAllText(destinationFileName);
return result;
}
private static string GetGhostTextFromPDF(string ghostPCLFileName, string sourceFileNamePdf, string destinationFileName) private static string GetGhostTextFromPDF(string ghostPCLFileName, string sourceFileNamePdf, string destinationFileName)
{ {
string result; string result;
if (File.Exists(destinationFileName)) if (File.Exists(destinationFileName))
File.Delete(destinationFileName); File.Delete(destinationFileName);
//string arguments = $"-i \"{sourceFile}\" -o \"{result}\"";
string arguments = $"-dSAFER -dBATCH -dNOPAUSE -dFIXEDMEDIA -dFitPage -dAutoRotatePages=/All -dDEVICEWIDTHPOINTS=792 -dDEVICEHEIGHTPOINTS=612 -sOutputFile=\"{destinationFileName}\" -sDEVICE=pdfwrite \"{sourceFileNamePdf}\""; string arguments = $"-dSAFER -dBATCH -dNOPAUSE -dFIXEDMEDIA -dFitPage -dAutoRotatePages=/All -dDEVICEWIDTHPOINTS=792 -dDEVICEHEIGHTPOINTS=612 -sOutputFile=\"{destinationFileName}\" -sDEVICE=pdfwrite \"{sourceFileNamePdf}\"";
//Process process = Process.Start(configData.LincPDFCFileName, arguments);
Process? process = Process.Start(ghostPCLFileName, arguments); Process? process = Process.Start(ghostPCLFileName, arguments);
_ = process?.WaitForExit(30000); _ = process?.WaitForExit(30000);
if (!File.Exists(destinationFileName)) if (!File.Exists(destinationFileName))
@ -83,11 +101,11 @@ internal static class HelperPdfStripperWrapper
d = Path.Combine(d, directoryName); d = Path.Combine(d, directoryName);
if (!string.IsNullOrEmpty(d) && !Directory.Exists(d)) if (!string.IsNullOrEmpty(d) && !Directory.Exists(d))
_ = Directory.CreateDirectory(d); _ = Directory.CreateDirectory(d);
char? parser = p is null ? null : p == "S" ? 'S' : p == "G" ? 'G' : p == "K" ? 'K' : null; char? parser = p is null ? null : p == "S" ? _Stripper : p == "G" ? _Ghost : p == "K" ? _Kofax : p == "L" ? _Linc : null;
return new(d, k, parser); return new(d, k, parser);
} }
private static void ParseSave(ILogger log, string pdfTextStripperFileName, string ghostPCLFileName, string kofaxFileName, string destinationDirectory, char parser, string[] files) private static void ParseSave(ILogger log, string pdfTextStripperFileName, string ghostPCLFileName, string kofaxFileName, string lincPDFCFileName, string destinationDirectory, char parser, string[] files)
{ {
string text; string text;
string destinationFileName; string destinationFileName;
@ -96,12 +114,14 @@ internal static class HelperPdfStripperWrapper
foreach (string file in files) foreach (string file in files)
{ {
destinationFileName = Path.Combine(destinationDirectory, $"{file}.txt"); destinationFileName = Path.Combine(destinationDirectory, $"{file}.txt");
if (parser == 'S') if (parser == _Kofax)
text = GetTextFromPDF(pdfTextStripperFileName, file, destinationFileName);
else if (parser == 'G')
text = GetGhostTextFromPDF(ghostPCLFileName, file, destinationFileName);
else if (parser == 'K')
text = GetKofaxTextFromPDF(kofaxFileName, file, destinationFileName); text = GetKofaxTextFromPDF(kofaxFileName, file, destinationFileName);
else if (parser == _Linc)
text = GetLincTextFromPDF(lincPDFCFileName, file, destinationFileName);
else if (parser == _Ghost)
text = GetGhostTextFromPDF(ghostPCLFileName, file, destinationFileName);
else if (parser == _Stripper)
text = GetTextFromPDF(pdfTextStripperFileName, file, destinationFileName);
else else
throw new NotImplementedException(); throw new NotImplementedException();
log.LogInformation("<{file}> == {length}", Path.GetFileName(file), text.Length); log.LogInformation("<{file}> == {length}", Path.GetFileName(file), text.Length);
@ -135,35 +155,56 @@ internal static class HelperPdfStripperWrapper
internal static void ParseSave(ILogger log, List<string> args) internal static void ParseSave(ILogger log, List<string> args)
{ {
string pdfTextStripperFileName = Path.Combine(AppContext.BaseDirectory, "PDF-Text-Stripper.exe"); // <ItemGroup>
if (!File.Exists(pdfTextStripperFileName)) // <PackageReference Include="Infineon.Mesa.PDF.Text.Stripper" Version="4.8.0.1"><NoWarn>NU1701</NoWarn></PackageReference>
log.LogInformation("exe <{pdfTextStripperFileName}> doesn't exist!", pdfTextStripperFileName); // </ItemGroup>
// <ItemGroup>
// <None Include="\\mestsa003.infineon.com\EC_EAFRepository\Staging\DeploymentStorage\GhostPCL\gpcl6win64\gpcl6dll64.dll">
// <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
// </None>
// <None Include="\\mestsa003.infineon.com\EC_EAFRepository\Staging\DeploymentStorage\GhostPCL\gpcl6win64\gpcl6win64.exe">
// <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
// </None>
// <None Include="\\mestsa003.infineon.com\EC_EAFRepository\Staging\DeploymentStorage\LincPDFC\v2.6.6.21\LincPDFC.exe">
// <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
// </None>
// </ItemGroup>
string sourceDirectory = Path.GetFullPath(args[0]);
Input input = GetInput(args);
if (string.IsNullOrEmpty(input.DestinationDirectory))
log.LogInformation("-d <{DestinationDirectory}> wasn't supplied!", nameof(input.DestinationDirectory));
else else
{ {
string ghostPCLFileName = Path.Combine(AppContext.BaseDirectory, "gPcl6win64.exe"); if (input.Parser is null)
if (!File.Exists(ghostPCLFileName)) log.LogInformation("-p <{Parser}> wasn't supplied!", nameof(input.Parser));
log.LogInformation("exe <{ghostPCLFileName}> doesn't exist!", ghostPCLFileName);
else else
{ {
string kofaxFileName = "C:/Program Files (x86)/Kofax/Power PDF 50/batchConverter.com"; string pdfTextStripperFileName = Path.Combine(AppContext.BaseDirectory, "PDF-Text-Stripper.exe");
if (!File.Exists(kofaxFileName)) if (input.Parser == _Stripper && !File.Exists(pdfTextStripperFileName))
log.LogInformation("exe <{kofaxFileName}> doesn't exist!", kofaxFileName); log.LogInformation("exe <{pdfTextStripperFileName}> doesn't exist!", pdfTextStripperFileName);
else else
{ {
Input input = GetInput(args); string ghostPCLFileName = Path.Combine(AppContext.BaseDirectory, "gPcl6win64.exe");
if (string.IsNullOrEmpty(input.DestinationDirectory)) if (input.Parser == _Ghost && !File.Exists(ghostPCLFileName))
log.LogInformation("-d <{DestinationDirectory}> wasn't supplied!", nameof(input.DestinationDirectory)); log.LogInformation("exe <{ghostPCLFileName}> doesn't exist!", ghostPCLFileName);
else else
{ {
if (input.Parser is null) string kofaxFileName = "C:/Program Files (x86)/Kofax/Power PDF 50/batchConverter.com";
log.LogInformation("-p <{Parser}> wasn't supplied!", nameof(input.Parser)); if (input.Parser == _Kofax && !File.Exists(kofaxFileName))
log.LogInformation("exe <{kofaxFileName}> doesn't exist!", kofaxFileName);
else else
{ {
string[] files = Directory.GetFiles(args[0], "*.pdf", SearchOption.TopDirectoryOnly); string lincPDFCFileName = Path.Combine(AppContext.BaseDirectory, "LincPDFC.exe");
if (files.Length == 0) if (input.Parser == _Kofax && !File.Exists(lincPDFCFileName))
log.LogInformation("Length == {length}", files.Length); log.LogInformation("exe <{lincPDFCFileName}> doesn't exist!", lincPDFCFileName);
else else
ParseSave(log, pdfTextStripperFileName, ghostPCLFileName, kofaxFileName, input.DestinationDirectory, input.Parser.Value, files); {
string[] files = Directory.GetFiles(sourceDirectory, "*.pdf", SearchOption.TopDirectoryOnly);
if (files.Length == 0)
log.LogInformation("Length == {length}", files.Length);
else
ParseSave(log, pdfTextStripperFileName, ghostPCLFileName, kofaxFileName, lincPDFCFileName, input.DestinationDirectory, input.Parser.Value, files);
}
} }
} }
} }

View File

@ -1,3 +1,4 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -8,7 +9,7 @@ public record AppSettings(string Company,
string[] ExcludeDirectoryNames, string[] ExcludeDirectoryNames,
string[] ExcludeSchemes, string[] ExcludeSchemes,
string PersonBirthdayFormat, string PersonBirthdayFormat,
char[] PersonTitleFilters, string PersonTitleFilters,
string[] ValidImageFormatExtensions, string[] ValidImageFormatExtensions,
string WorkingDirectoryName) string WorkingDirectoryName)
{ {
@ -19,6 +20,35 @@ public record AppSettings(string Company,
return result; return result;
} }
private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings)
{
if (appSettings?.Company is null)
{
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue;
paths.Add(physicalFileProvider.Root);
}
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
}
}
public static AppSettings Get(IConfigurationRoot configurationRoot)
{
AppSettings? result;
#pragma warning disable IL3050, IL2026
result = configurationRoot.Get<AppSettings>();
#pragma warning restore IL3050, IL2026
PreVerify(configurationRoot, result);
if (result is null)
throw new Exception("Not set!");
return result;
}
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]

View File

@ -1,2 +0,0 @@
[*.cs]
csharp_preserve_single_line_statements = true

View File

@ -1,81 +0,0 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Binder;
public class AppSettings
{
public string? Company { get; set; }
public string? DefaultNoteType { get; set; }
public string[]? ExcludeDirectoryNames { get; set; }
public string[]? ExcludeSchemes { get; set; }
public string? PersonBirthdayFormat { get; set; }
public string? PersonTitleFilters { get; set; }
public string[]? ValidImageFormatExtensions { get; set; }
public string? WorkingDirectoryName { get; set; }
public override string ToString()
{
string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings)
{
if (appSettings?.Company is null)
{
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue;
paths.Add(physicalFileProvider.Root);
}
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
}
}
private static Models.AppSettings Get(AppSettings? appSettings)
{
Models.AppSettings result;
if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company));
if (appSettings?.DefaultNoteType is null) throw new NullReferenceException(nameof(appSettings.DefaultNoteType));
if (appSettings?.ExcludeDirectoryNames is null) throw new NullReferenceException(nameof(appSettings.ExcludeDirectoryNames));
if (appSettings?.ExcludeSchemes is null) throw new NullReferenceException(nameof(appSettings.ExcludeSchemes));
if (appSettings?.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(appSettings.PersonBirthdayFormat));
if (appSettings?.PersonTitleFilters is null) throw new NullReferenceException(nameof(appSettings.PersonTitleFilters));
if (appSettings?.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(appSettings.ValidImageFormatExtensions));
if (appSettings?.WorkingDirectoryName is null) throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
result = new(appSettings.Company,
appSettings.DefaultNoteType,
appSettings.ExcludeDirectoryNames,
appSettings.ExcludeSchemes,
appSettings.PersonBirthdayFormat,
appSettings.PersonTitleFilters.ToArray(),
appSettings.ValidImageFormatExtensions,
appSettings.WorkingDirectoryName);
return result;
}
public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
{
Models.AppSettings result;
#pragma warning disable IL3050, IL2026
AppSettings? appSettings = configurationRoot.Get<AppSettings>();
#pragma warning restore IL3050, IL2026
PreVerify(configurationRoot, appSettings);
result = Get(appSettings);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,20 +0,0 @@
// using System.Text.Json.Serialization;
// namespace File_Folder_Helper.Models;
// internal record Host([property: JsonPropertyName("a")] string? Id,
// [property: JsonPropertyName("b")] string? Colon,
// [property: JsonPropertyName("c")] string? Hyphen,
// [property: JsonPropertyName("d")] string? Line,
// [property: JsonPropertyName("e")] string? Count,
// [property: JsonPropertyName("f")] string? Segments,
// [property: JsonPropertyName("g")] string? Type,
// [property: JsonPropertyName("h")] string? Device,
// [property: JsonPropertyName("i")] string? Name,
// [property: JsonPropertyName("j")] string? Location);
// [JsonSourceGenerationOptions(WriteIndented = true, AllowTrailingCommas = true)]
// [JsonSerializable(typeof(Host[]))]
// internal partial class HostsSourceGenerationContext : JsonSerializerContext
// {
// }

View File

@ -1,11 +1,17 @@
using System.Collections.ObjectModel;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models; namespace File_Folder_Helper.Models;
internal record LineNumber(int? Created, internal record LineNumber(int? Created,
int? Completed,
int? CompletedColumns,
int? H1, int? H1,
int? FrontMatterYamlEnd, int? FrontMatterYamlEnd,
ReadOnlyCollection<string> Lines,
int? Progress,
int? Status, int? Status,
int? StartedColumns,
int? Type, int? Type,
int? Updated); int? Updated);

View File

@ -1,12 +1,15 @@
using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models; namespace File_Folder_Helper.Models;
internal record NginxFileSystem(string Name, internal record NginxFileSystem([property: JsonPropertyName("name")] string Name,
string Type, DateTime? LastModified,
string MTime, [property: JsonPropertyName("mtime")] string MTime,
float Size) Uri? URI,
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("size")] float? Length)
{ {
public override string ToString() public override string ToString()
@ -15,6 +18,25 @@ internal record NginxFileSystem(string Name,
return result; return result;
} }
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, string name, string mTime, Uri uri, string type, float? size)
{
NginxFileSystem result;
DateTime dateTime;
DateTime? nullableDateTime;
if (mTime.Length != format.Length + 4 || !DateTime.TryParseExact(mTime[..format.Length], format, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
nullableDateTime = null;
else
nullableDateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, mTime[(format.Length + 1)..], timeZoneInfo.Id);
result = new(name, nullableDateTime, mTime, uri, type, size);
return result;
}
public static string GetFormat() =>
"ddd, dd MMM yyyy HH:mm:ss";
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, Uri uri, NginxFileSystem nginxFileSystem) =>
Get(format, timeZoneInfo, nginxFileSystem.Name, nginxFileSystem.MTime, uri, nginxFileSystem.Type, nginxFileSystem.Length);
} }
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
@ -23,6 +45,12 @@ internal partial class NginxFileSystemSourceGenerationContext : JsonSerializerCo
{ {
} }
[JsonSourceGenerationOptions(WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(NginxFileSystem))]
internal partial class NginxFileSystemSingleLineSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(NginxFileSystem[]))] [JsonSerializable(typeof(NginxFileSystem[]))]
internal partial class NginxFileSystemCollectionSourceGenerationContext : JsonSerializerContext internal partial class NginxFileSystemCollectionSourceGenerationContext : JsonSerializerContext

View File

@ -12,12 +12,12 @@ internal class Program
public static void Main(string[] args) public static void Main(string[] args)
{ {
#pragma warning disable IL3050 #pragma warning disable IL3050
HostApplicationBuilder builder = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder(args); HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
#pragma warning restore IL3050 #pragma warning restore IL3050
_ = builder.Configuration.AddEnvironmentVariables(); _ = builder.Configuration.AddEnvironmentVariables();
_ = builder.Configuration.AddUserSecrets<Program>(); _ = builder.Configuration.AddUserSecrets<Program>();
_ = builder.Services.AddSingleton(args.ToList()); _ = builder.Services.AddSingleton(args.ToList());
AppSettings appSettings = Models.Binder.AppSettings.Get(builder.Configuration); AppSettings appSettings = AppSettings.Get(builder.Configuration);
_ = builder.Services.AddSingleton(appSettings); _ = builder.Services.AddSingleton(appSettings);
_ = builder.Services.AddHostedService<Worker>(); _ = builder.Services.AddHostedService<Worker>();
using IHost host = builder.Build(); using IHost host = builder.Build();

186
Scripts/descriptor.js Normal file
View File

@ -0,0 +1,186 @@
"use strict";
const twoAlphaPattern = /^[a-zA-z]{2,3}/;
const processOnlyB = /^[a-zA-z][0-9]{2,4}$/;
// const reactorNumberPattern = /^[0-9]{2}--/;
// const id = /[-]?([QP][0-9]{4,}|[0-9]{5,})[-]?/;
const normal = /^[0-9]{2}[.][0-9]{1}[.]?[0-9]{0,1}/;
const fileSystemSafe = /[\\,\/,\:,\*,\?,\"",\<,\>,\|]/;
const values = [
{ text: '', rds: '', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: '12-123456-1234', rds: '123456', psn: '1234', reactor: '12', layer: '', zone: '', employee: '', lot: '' },
{ text: '12-1234567-1234', rds: '1234567', psn: '1234', reactor: '12', layer: '', zone: '', employee: '', lot: '' },
{ text: '12-12345678-1234', rds: '12345678', psn: '1234', reactor: '12', layer: '', zone: '', employee: '', lot: '' },
{ text: '123456', rds: '123456', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: '1T123456', rds: '123456', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: 'MP', rds: '', psn: '', reactor: '', layer: '', zone: '', employee: 'MP', lot: '' },
{ text: '12-123456-1234.2-1', rds: '123456', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '12-1234567-1234.2-1', rds: '1234567', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '12-12345678-1234.2-1', rds: '12345678', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '12-123456-1234.02-1', rds: '123456', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '12-1234567-1234.02-1', rds: '1234567', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '12-12345678-1234.02-1', rds: '12345678', psn: '1234', reactor: '12', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '20', rds: '', psn: '', reactor: '20', layer: '', zone: '', employee: '', lot: '' },
{ text: '20.2', rds: '', psn: '', reactor: '20', layer: '2', zone: '', employee: '', lot: '' },
{ text: '20.2.1', rds: '', psn: '', reactor: '20', layer: '2', zone: '1', employee: '', lot: '' },
{ text: '20.1.1', rds: '', psn: '', reactor: '20', layer: '1', zone: '1', employee: '', lot: '' },
{ text: 'P2-LOW-RR', rds: '', psn: 'RR', reactor: 'P2', layer: '', zone: '', employee: '', lot: '' },
{ text: 'i171308.1.51', rds: 'i171308.1.51', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: 'o171308.1.51', rds: 'o171308.1.51', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: 'O171308.1.51', rds: 'O171308.1.51', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: '171308.1.51', rds: '171308.1.51', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: '' },
{ text: '75-QP1414-SPLIT4', rds: '', psn: 'SPLIT4', reactor: '75', layer: '', zone: '', employee: '', lot: '' },
{ text: 'B48', rds: '', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: 'B48' },
{ text: 'B48', rds: '', psn: '', reactor: '', layer: '', zone: '', employee: '', lot: 'B48' },
];
function tryParseInt(str, defaultValue) {
const parsedValue = parseInt(str);
return isNaN(parsedValue) ? defaultValue : parsedValue;
}
function getReactorAndRDS(defaultReactor, defaultRDS, text, formattedText, segments) {
let result = {};
let rds;
let reactor;
let rdsValue;
if (text == null || text.length === 0 || segments.length === 0 || formattedText == null || formattedText.length === 0)
reactor = defaultReactor;
else
reactor = segments[0];
if (segments.length <= 1 || !tryParseInt(segments[1], false) || rdsValue < 99)
rds = defaultRDS;
else
rds = segments[1];
if (reactor.length > 3) {
rds = reactor;
reactor = defaultReactor;
}
result = { reactor: reactor, rds: rds };
return result;
}
function getLayerAndPSN(defaultLayer, defaultPSN, segments) {
let result = {};
let psn;
let layer;
if (segments.length <= 2) {
psn = defaultPSN;
layer = defaultLayer;
}
else {
let segmentsB = segments[2].split('.');
psn = segmentsB[0];
if (segmentsB.length <= 1)
layer = defaultLayer;
else {
layer = segmentsB[1];
if (layer.length > 1 && layer[0] === '0')
layer = layer.substring(1);
}
}
result = { layer: layer, psn: psn };
return result;
}
function getZone(segments) {
let result = '';
if (segments.length <= 3)
result = '';
else {
result = segments[3];
if (result.length > 1 && result[0] === '0')
result = result.substring(1);
}
return result;
}
function getValue(text) {
let result = {};
let lot = '';
let psn = '';
let rds = '';
let zone = '';
let layer = '';
let reactor = '';
let employee = '';
let defaultPSN = '';
let defaultRDS = '';
let defaultZone = '';
let defaultLayer = '';
let defaultReactor = '';
let defaultEmployee = '';
if (text.match(processOnlyB)) {
lot = text.toUpperCase();
psn = defaultPSN;
rds = defaultRDS;
zone = defaultZone;
layer = defaultLayer;
reactor = defaultReactor;
employee = defaultEmployee;
}
else if (text == null || text.length === 0 || (text.length == 2 && text.match(twoAlphaPattern))) {
lot = text;
employee = lot;
psn = defaultPSN;
rds = defaultRDS;
zone = defaultZone;
layer = defaultLayer;
reactor = defaultReactor;
}
else if (text.match(normal)) {
let segments = text.split('.');
lot = text;
psn = defaultPSN;
rds = defaultRDS;
layer = segments[1];
reactor = segments[0];
employee = defaultEmployee;
if (segments.length <= 2)
zone = defaultZone;
else
zone = segments[2];
}
else {
// Remove illegal characters \/:*?"<>| found in the Lot.
lot = text.replace(fileSystemSafe, "_").split('\r')[0].split('\n')[0];
if (lot.length > 2 && lot[0] == '1' && (lot[1] == 'T' || lot[1] == 't'))
lot = lot.substring(2);
let segments = lot.split('-');
// let hasRDS = lot.match(id);
let reactorAndRDS = getReactorAndRDS(defaultReactor, defaultRDS, text, lot, segments);
reactor = reactorAndRDS.reactor;
rds = reactorAndRDS.rds;
let layerAndPSN = getLayerAndPSN(defaultLayer, defaultPSN, segments);
layer = layerAndPSN.layer;
psn = layerAndPSN.psn;
zone = getZone(segments);
if (segments.length <= 4)
employee = defaultEmployee;
else
employee = segments[4];
}
result = { rds: rds, psn: psn, reactor: reactor, layer: layer, zone: zone, employee: employee, lot: lot };
return result;
};
values.forEach(element => {
let value = getValue(element.text);
if (value.rds !== element.rds)
console.error("RDS doesn't match!");
else if (value.psn !== element.psn)
console.error("PSN doesn't match!");
else if (value.reactor !== element.reactor)
console.error("Reactor doesn't match!");
else if (value.layer !== element.layer)
console.error("Layer doesn't match!");
else if (value.zone !== element.zone)
console.error("Zone doesn't match!");
else if (value.employee !== element.employee)
console.error("Employee doesn't match!");
else if (value.lot !== element.lot)
console.error("Lot doesn't match!");
else
console.info('Match');
});

View File

@ -1,4 +1,5 @@
"use strict"; "use strict";
// DateTime normal = DateTime.Now.ToUniversalTime(); // DateTime normal = DateTime.Now.ToUniversalTime();
// logger.LogInformation("Now - ToUniversalTime: {ticks}", normal.Ticks); // logger.LogInformation("Now - ToUniversalTime: {ticks}", normal.Ticks);
// DateTime utc1970DateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // DateTime utc1970DateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
@ -14,14 +15,9 @@
// console.log("ticks: " + ticks); // console.log("ticks: " + ticks);
// var dateText = ticks + " - " + date.toString(); // var dateText = ticks + " - " + date.toString();
// console.log("dateText: " + dateText); // console.log("dateText: " + dateText);
let date = new Date(); // DateTime utcMeDateTime = new(1980, 1, 17, 0, 0, 0, DateTimeKind.Utc);
let timezoneOffset = date.getTimezoneOffset(); // long meTotalSeconds = (long)Math.Floor(fileInfo.LastWriteTime.ToUniversalTime().Subtract(utcMeDateTime).TotalSeconds);
let seconds = date.getTime().valueOf() + timezoneOffset;
let epoch = seconds * 10000;
let ticks = epoch + 621355968000000000;
let dateText = seconds + " - " + ticks + " - " + date.toString();
console.log("dateText: " + dateText);
console.log("end");
// epoch: 25201000 // epoch: 25201000
// ticks: 638665132483790000 // ticks: 638665132483790000
// dateText: 638665132483790000 - Wed Nov 06 2024 10:55:58 GMT-0700 (Mountain Standard Time) // dateText: 638665132483790000 - Wed Nov 06 2024 10:55:58 GMT-0700 (Mountain Standard Time)
@ -29,3 +25,82 @@ console.log("end");
// 638665135325760000 // 638665135325760000
// 638665136814890000 // 638665136814890000
// utc1970DateTime: 621355968000000000 // utc1970DateTime: 621355968000000000
function getDateText() {
let result;
const now = new Date();
const time = now.getTime();
const year = now.getFullYear();
const start = new Date(year, 0, 0);
const oneDay = 1000 * 60 * 60 * 24;
const timezoneOffset = now.getTimezoneOffset();
const diff = (now - start) + ((start.getTimezoneOffset() - timezoneOffset) * 60 * 1000);
const day = Math.floor(diff / oneDay);
console.log('Day of year: ' + day);
var season = year + "-";
if (day < 78)
season = season + "0.Winter";
else if (day < 124)
season = season + "1.Spring";
else if (day < 171)
season = season + "2.Spring";
else if (day < 217)
season = season + "3.Summer";
else if (day < 264)
season = season + "4.Summer";
else if (day < 309)
season = season + "5.Fall";
else if (day < 354)
season = season + "6.Fall";
else
season = season + "7.Winter";
const seconds = time.valueOf() + timezoneOffset;
const epoch = seconds * 10000;
const ticks = epoch + 621355968000000000;
result = seconds + " - " + ticks + " - " + now.toString();
return result;
}
const dateText = getDateText();
console.log("dateText: " + dateText);
function getPath() {
let result;
const original = "d:\\5-Other-Small\\Kanban\\Year-Season\\2025\\2025-0.Winter\\1737913505637";
const segments = original.split('\\');
result = segments.slice(0, -3).join('\\') + '\\2021\\2021-0.Summer\\123';
return result;
}
const path = getPath();
console.log("path: " + path);
// https://tickstodatetime.wassupy.com/?ticks=638784250251441727
function getInfinityQS(value, sequence) {
let result;
if (sequence.length < 18)
result = [value];
else {
const now = new Date();
const epochHour = 36000000000;
const epochTicks = 621355968000000000;
const timezoneHourOffset = now.getTimezoneOffset() / 60;
const timezoneOffset = timezoneHourOffset * epochHour;
const milliseconds = (sequence - epochTicks + timezoneOffset) / 10000;
const flooredMilliseconds = Math.floor(milliseconds / (60 * 1000)) * (60 * 1000);
const ceiledMilliseconds = Math.ceil(milliseconds / (60 * 1000)) * (60 * 1000);
result = [(flooredMilliseconds / 1000), (ceiledMilliseconds / 1000)];
}
return result;
}
const date = '3/26/2025';
const infinityQS = 1742853453;
const sequence = '638784250251441727';
const values = getInfinityQS(date, sequence);
console.info("InfinityQS: " + values);
if (values[0] < infinityQS && values[1] > infinityQS)
console.info("InfinityQS: Pass");
else
console.warn("InfinityQS: Fail");

50
Scripts/immich.js Normal file
View File

@ -0,0 +1,50 @@
const axios = require('axios');
const url = 'https://immich.bchs.duckdns.org';
let config = {
method: 'get',
maxBodyLength: Infinity,
// url: url + '/api/users',
url: url + '/api/assets/f89d0de1-2762-4f9e-b60e-c7eeec93c4e9',
headers: {
'Accept': 'application/json',
'x-api-key': 'Pm2CbhJvgStEPAFKRVclW88qrOAy79OeIEcfj3k'
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
// let data = JSON.stringify({
// "avatar": {
// "color": "green"
// }
// });
// let configB = {
// method: 'put',
// maxBodyLength: Infinity,
// url: url + '/api/users/me/preferences',
// headers: {
// 'Content-Type': 'application/json',
// 'Accept': 'application/json',
// 'x-api-key': 'Pm2CbhJvgStEPAFKRVclW88qrOAy79OeIEcfj3k'
// },
// data: data
// };
// axios.request(configB)
// .then((response) => {
// console.log(JSON.stringify(response.data));
// })
// .catch((error) => {
// console.log(error);
// });

7
Scripts/markdown.js Normal file
View File

@ -0,0 +1,7 @@
// import data from './oi-metrology-viewer-0-Line-yaml.json' with { type: 'json' };
// import data from '../.vscode/oi-metrology-viewer-0-Line-yaml.json' with { type: 'json' };
import data from '../.vscode/.helper/hosts-legend-table.json' with { type: 'json' };
data.forEach(element => {
console.log(element.Concat);
});

View File

@ -147,7 +147,7 @@ public class Worker : BackgroundService
_Logger.LogWarning("Must pass a argument!"); _Logger.LogWarning("Must pass a argument!");
CreateWindowsShortcut(); CreateWindowsShortcut();
} }
else if (Directory.Exists(_Args[0])) else if (Directory.Exists(_Args[0].Split('~')[0]) || Directory.Exists(_Args[0]))
{ {
if (!_ConsoleKeys.Contains(consoleKey)) if (!_ConsoleKeys.Contains(consoleKey))
{ {

6
docker-compose.yaml Normal file
View File

@ -0,0 +1,6 @@
services:
webapp:
container_name: file-folder-helper
build:
context: .
dockerfile: Dockerfile

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"rollForward": "latestMinor",
"version": "8.0.113"
}
}

6323
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@
"prettier.write": "prettier . --write" "prettier.write": "prettier . --write"
}, },
"devDependencies": { "devDependencies": {
"prettier": "3.0.0" "axios": "^1.7.9",
"jest": "^29.7.0",
"prettier": "3.0.0"
} }
} }