Compare commits
	
		
			54 Commits
		
	
	
		
			9f4286e3e9
			...
			10-24-a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b29ef7682d | |||
| 46590d48cd | |||
| 7a2492f14b | |||
| 55aa7db97e | |||
| 6102da7266 | |||
| 8ec89953bc | |||
| 88bdd33285 | |||
| e74a0ccdce | |||
| 341cc93a0a | |||
| 84d85094a3 | |||
| c3acd17a0e | |||
| 8ecea05fe8 | |||
| d57be048e7 | |||
| 89bb87bd14 | |||
| e7c1fd2221 | |||
| fc4449f515 | |||
| 6ce6c28db1 | |||
| 2e4aa313fa | |||
| 116d5e9734 | |||
| 5ca22b3792 | |||
| b98d1e480b | |||
| 404ffe18f5 | |||
| 5becc546b2 | |||
| 0f9d004122 | |||
| dd5baba9bc | |||
| 43527b3356 | |||
| 8ca489d818 | |||
| 8f22f188a2 | |||
| d23f802cdb | |||
| 74e9fc33af | |||
| aa6461c62d | |||
| fad2db46b5 | |||
| cc9c5013a9 | |||
| 23c0ff9683 | |||
| 919279a917 | |||
| 0621d0f07e | |||
| b74a6a5267 | |||
| 3e2fb15db0 | |||
| 6783621dab | |||
| 38ab4424bc | |||
| e89e11dcf6 | |||
| 384c83304b | |||
| 30931eda9c | |||
| 55adcb69aa | |||
| d9e394f446 | |||
| dd4a16117c | |||
| a156ff8dcb | |||
| b771761936 | |||
| 264b6319cb | |||
| 7aada4303e | |||
| 618fa0d55f | |||
| 930963965d | |||
| 9d612d3d3d | |||
| 3070fee04c | 
| @ -120,7 +120,7 @@ dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template | ||||
| 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 = warning # Using directive is unnecessary | ||||
| dotnet_diagnostic.IDE0005.severity = none # 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) | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -336,3 +336,4 @@ ASALocalRun/ | ||||
| .extensions-vscode-oss | ||||
| .extensions-vscode-insiders | ||||
| .vscode/.UserSecrets/secrets.json | ||||
| .vscode/.helper | ||||
|  | ||||
							
								
								
									
										4
									
								
								.vscode/equipment-automation-framework-cell-instance-status.http
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.vscode/equipment-automation-framework-cell-instance-status.http
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| @host = eaf-prod.mes.infineon.com:9003 | ||||
|  | ||||
| POST {{host}}/StatusQuery | ||||
| Accept: application/json | ||||
							
								
								
									
										110
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										110
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @ -1,43 +1,71 @@ | ||||
| { | ||||
|     // Use IntelliSense to learn about possible attributes. | ||||
|     // Hover to view descriptions of existing attributes. | ||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|     "version": "0.2.0", | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "name": ".NET Core Launch (console)", | ||||
|             "type": "coreclr", | ||||
|             "request": "launch", | ||||
|             "preLaunchTask": "build", | ||||
|             "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", | ||||
|             "args": [ | ||||
|                 "s", | ||||
|                 "X", | ||||
|                 "Y:/TRendlog", | ||||
|                 "Day-Helper-2025-01-14", | ||||
|                 "New folder*|202*", | ||||
|                 "yyyy-MM-dd", | ||||
|                 "444", | ||||
|                 "555", | ||||
|                 "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}" | ||||
|         } | ||||
|     ] | ||||
|   // Use IntelliSense to learn about possible attributes. | ||||
|   // Hover to view descriptions of existing attributes. | ||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|   "version": "0.2.0", | ||||
|   "configurations": [ | ||||
|     { | ||||
|       "name": ".NET Core Launch (console)", | ||||
|       "type": "coreclr", | ||||
|       "request": "launch", | ||||
|       "preLaunchTask": "Build", | ||||
|       "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", | ||||
|       "args": [ | ||||
|         "s", | ||||
|         "X", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/JavaScriptObjectNotation", | ||||
|         "Day-Helper-2025-10-22", | ||||
|         "*.json", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/WorkWeek", | ||||
|         "s", | ||||
|         "X", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/PollPath", | ||||
|         "Day-Helper-2025-07-01", | ||||
|         "*.pdsf", | ||||
|         "987654321", | ||||
|         "Time", | ||||
|         ".json", | ||||
|         "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", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/JavaScriptObjectNotation", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Markdown", | ||||
|         "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/KeyValuePairs", | ||||
|         "654321", | ||||
|         ".json" | ||||
|       ], | ||||
|       "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 | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										34
									
								
								.vscode/mklink.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								.vscode/mklink.md
									
									
									
									
										vendored
									
									
								
							| @ -14,18 +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" | ||||
| ``` | ||||
|  | ||||
| ```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" | ||||
| mkdir "C:\Users\phares\.vscode\extensions\ifx.type-script-helper-1.111.0\net8.0\win-x64\publish" | ||||
| mkdir "C:\Users\phares\.vscode-oss\extensions\ifx.type-script-helper-1.111.0\net8.0\win-x64\publish" | ||||
| mkdir "C:\Users\phares\.vscode-insiders\extensions\ifx.type-script-helper-1.111.0\net8.0\win-x64\publish" | ||||
| mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode" "C:\Users\phares\.vscode\extensions\ifx.type-script-helper-1.111.0" | ||||
| mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss" "C:\Users\phares\.vscode-oss\extensions\ifx.type-script-helper-1.111.0" | ||||
| mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders" "C:\Users\phares\.vscode-insiders\extensions\ifx.type-script-helper-1.111.0" | ||||
| ``` | ||||
|  | ||||
| ```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" | ||||
| ``` | ||||
|  | ||||
| ```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" | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										22
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,12 @@ | ||||
| { | ||||
|     "files.associations": { | ||||
|         "*.container": "ini", | ||||
|         "*.org": "ini", | ||||
|         "*.net": "ini", | ||||
|         "*.xmp": "xml", | ||||
|         "podman": "ini", | ||||
|         "default": "ini" | ||||
|     }, | ||||
|     "[markdown]": { | ||||
|         "editor.wordWrap": "off" | ||||
|     }, | ||||
| @ -10,16 +18,21 @@ | ||||
|       "**/node_modules": true | ||||
|     }, | ||||
|     "cSpell.words": [ | ||||
|       "abcdefghiklmnopqrstuvwxyz", | ||||
|       "Acks", | ||||
|       "ASPNETCORE", | ||||
|       "BIORAD", | ||||
|       "BIRT", | ||||
|       "CHIL", | ||||
|       "DEAT", | ||||
|       "digi", | ||||
|       "endianness", | ||||
|       "Exif", | ||||
|       "FAMC", | ||||
|       "FAMS", | ||||
|       "Gatus", | ||||
|       "GIVN", | ||||
|       "HGCV", | ||||
|       "HUSB", | ||||
|       "Immich", | ||||
|       "INDI", | ||||
| @ -27,11 +40,18 @@ | ||||
|       "Kanban", | ||||
|       "kanbn", | ||||
|       "Kofax", | ||||
|       "Linc", | ||||
|       "Makernote", | ||||
|       "mesfs", | ||||
|       "mestsa", | ||||
|       "mklink", | ||||
|       "netrm", | ||||
|       "NpgSql", | ||||
|       "NSFX", | ||||
|       "OBJE", | ||||
|       "onenote", | ||||
|       "PDFC", | ||||
|       "pdsf", | ||||
|       "Permyriad", | ||||
|       "pged", | ||||
|       "Phares", | ||||
| @ -39,9 +59,11 @@ | ||||
|       "Reparse", | ||||
|       "Rijndael", | ||||
|       "Serilog", | ||||
|       "startable", | ||||
|       "SUBM", | ||||
|       "SURN", | ||||
|       "SYSLIB", | ||||
|       "TENCOR", | ||||
|       "VSTS", | ||||
|       "WIQL", | ||||
|       "WSJF" | ||||
|  | ||||
							
								
								
									
										392
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										392
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @ -1,22 +1,135 @@ | ||||
| { | ||||
|     "version": "2.0.0", | ||||
|     "inputs": [ | ||||
|         { | ||||
|             "default": "Development", | ||||
|             "description": "Which ASP Net Core Environment?", | ||||
|             "id": "ASPNETCORE_ENVIRONMENT", | ||||
|             "options": [ | ||||
|                 "Development", | ||||
|                 "Production" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{AssemblyTitle}", | ||||
|             "description": "What Assembly Title?", | ||||
|             "id": "AssemblyTitle", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{Build.BuildId}", | ||||
|             "description": "Which Build BuildId?", | ||||
|             "id": "Build.BuildId", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{Build.Reason}", | ||||
|             "description": "Which Build Reason?", | ||||
|             "id": "Build.Reason", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{Build.Repository.Id}", | ||||
|             "description": "Which Build Repository Id?", | ||||
|             "id": "Build.Repository.Id", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{Build.Repository.Name}", | ||||
|             "description": "Which Build Repository Name?", | ||||
|             "id": "Build.Repository.Name", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{Build.SourceVersion}", | ||||
|             "description": "Which Build Source Version?", | ||||
|             "id": "Build.SourceVersion", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "Debug", | ||||
|             "description": "Which Configuration?", | ||||
|             "id": "Configuration", | ||||
|             "options": [ | ||||
|                 "Debug", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "net8.0", | ||||
|             "description": "Which Core Version?", | ||||
|             "id": "CoreVersion", | ||||
|             "options": [ | ||||
|                 "net8.0" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/MSBuild/Current/Bin/MSBuild.exe", | ||||
|             "description": "Which MS Build?", | ||||
|             "id": "MSBuild", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "https://artifactory.intra.infineon.com/artifactory/api/nuget/ngt-fi-package-main-vir/", | ||||
|             "description": "Which Nuget Source?", | ||||
|             "id": "NugetSource", | ||||
|             "type": "promptString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "win-x64", | ||||
|             "description": "Which Runtime?", | ||||
|             "id": "Runtime", | ||||
|             "options": [ | ||||
|                 "win-x64", | ||||
|                 "win-x32", | ||||
|                 "linux-x64", | ||||
|                 "linux-x32" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "L:/", | ||||
|             "description": "Which System DefaultWorkingDirectory?", | ||||
|             "id": "System.DefaultWorkingDirectory", | ||||
|             "options": [ | ||||
|                 "L:/", | ||||
|                 "D:/", | ||||
|                 "C:/" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "v4.8", | ||||
|             "description": "Which Core Target Framework Version?", | ||||
|             "id": "TargetFrameworkVersion", | ||||
|             "options": [ | ||||
|                 "v4.8" | ||||
|             ], | ||||
|             "type": "pickString" | ||||
|         }, | ||||
|         { | ||||
|             "default": "{UserSecretsId}", | ||||
|             "description": "Which Core User Secrets Id?", | ||||
|             "id": "UserSecretsId", | ||||
|             "type": "promptString" | ||||
|         } | ||||
|     ], | ||||
|     "tasks": [ | ||||
|         { | ||||
|             "label": "User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "User Secrets Init", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "User Secrets Set", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
| @ -25,12 +138,12 @@ | ||||
|                 "_UserSecretsId", | ||||
|                 "0c43f9aa-96e9-4298-967c-ed069d79e262" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "User Secrets Set", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
| @ -40,58 +153,135 @@ | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Format", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Format Whitespaces", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Build", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "publish", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "-r", | ||||
|                 "linux-x64", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "label": "Build Linux", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "login", | ||||
|                 "gitea.phares.duckdns.org:443" | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Login", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "-t", | ||||
|                 "file-folder-helper", | ||||
|                 "." | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Build", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "image", | ||||
|                 "ls" | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Image List", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "run", | ||||
|                 "-p", | ||||
|                 "5001:5001", | ||||
|                 "--name", | ||||
|                 "file-folder-helper-001", | ||||
|                 "a3de856b5731" | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Run", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "tag", | ||||
|                 "a3de856b5731", | ||||
|                 "gitea.phares.duckdns.org:443/phares3757/file-folder-helper:latest" | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Tag", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "push", | ||||
|                 "gitea.phares.duckdns.org:443/phares3757/file-folder-helper:latest" | ||||
|             ], | ||||
|             "command": "podman", | ||||
|             "label": "Podman Push", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Publish", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Watch", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
| @ -99,90 +289,80 @@ | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|             "command": "dotnet", | ||||
|             "label": "Publish AOT", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "File-Folder-Helper AOT s J Verdaccio", | ||||
|             "type": "shell", | ||||
|             "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", | ||||
|             "args": [ | ||||
|                 "s", | ||||
|                 "J", | ||||
|                 "L:/Verdaccio/storage", | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "linux-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/File-Folder-Helper.csproj" | ||||
|             ], | ||||
|             "problemMatcher": [] | ||||
|         }, | ||||
|         { | ||||
|             "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": [] | ||||
|             "command": "dotnet", | ||||
|             "label": "Publish AOT Linux", | ||||
|             "problemMatcher": "$msCompile", | ||||
|             "type": "process" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Kanbn Console", | ||||
|             "type": "npm", | ||||
|             "problemMatcher": [], | ||||
|             "script": "kanbn.board", | ||||
|             "problemMatcher": [] | ||||
|             "type": "npm" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Kanbn Write Boad", | ||||
|             "type": "shell", | ||||
|             "command": "& kanbn board -j | L:/Git/kanbn2md/kanbn2md.exe >.kanbn/board.md", | ||||
|             "problemMatcher": [] | ||||
|             "label": "Kanbn Write Boad", | ||||
|             "problemMatcher": [], | ||||
|             "type": "shell" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Kanbn Write json", | ||||
|             "type": "npm", | ||||
|             "problemMatcher": [], | ||||
|             "script": "kanbn.board.json", | ||||
|             "problemMatcher": [] | ||||
|             "type": "npm" | ||||
|         }, | ||||
|         { | ||||
|             "command": "npx jest", | ||||
|             "label": "Jest", | ||||
|             "problemMatcher": [], | ||||
|             "type": "shell" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "s", | ||||
|                 "X", | ||||
|                 "L:/DevOps/Mesa_FI/File-Folder-Helper", | ||||
|                 "Day-Helper-2025-03-20", | ||||
|                 "false", | ||||
|                 "4" | ||||
|             ], | ||||
|             "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", | ||||
|             "label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20", | ||||
|             "problemMatcher": [], | ||||
|             "type": "shell" | ||||
|         }, | ||||
|         { | ||||
|             "args": [ | ||||
|                 "s", | ||||
|                 "X", | ||||
|                 "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/JavaScriptObjectNotation", | ||||
|                 "Day-Helper-2025-10-22", | ||||
|                 "*.json", | ||||
|                 "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/WorkWeek" | ||||
|             ], | ||||
|             "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", | ||||
|             "label": "File-Folder-Helper AOT s X Day-Helper-2025-10-22", | ||||
|             "problemMatcher": [], | ||||
|             "type": "shell" | ||||
|         } | ||||
|     ] | ||||
|     ], | ||||
|     "version": "2.0.0" | ||||
| } | ||||
| @ -1,7 +1,5 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI1; | ||||
| @ -16,12 +14,6 @@ internal static partial class Helper20240108 | ||||
|                           int ParameterCount, | ||||
|                           int StartLine); | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Method[]))] | ||||
|     private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     [GeneratedRegex(@"(?<method>[A-Z]{1}[A-Za-z_0-9]*)\(")] | ||||
|     private static partial Regex CSharpMethodName(); | ||||
|  | ||||
| @ -248,8 +240,6 @@ internal static partial class Helper20240108 | ||||
|     { | ||||
|         bool result; | ||||
|         List<string> results = []; | ||||
|         if (methods.Count == 0) | ||||
|             File.WriteAllText(".vscode/.json", JsonSerializer.Serialize(methods.ToArray(), MethodCollectionCommonSourceGenerationContext.Default.MethodArray)); | ||||
|         ReadOnlyCollection<int> methodLines = GetMethodLines(methods); | ||||
|         int minMethodLines = methodLines.Min(); | ||||
|         for (int i = 0; i < minMethodLines; i++) | ||||
|  | ||||
| @ -42,7 +42,7 @@ internal static partial class Helper20240403 | ||||
|         string keyIndex = args[5]; | ||||
|         string directory = args[0]; | ||||
|         logger.LogInformation(directory); | ||||
|         string[] columns = args[4].Split('|'); | ||||
|         string[] columns = args[4].Split('~'); | ||||
|         DynamicHostConfigurationProtocolConfiguration dynamicHostConfigurationProtocolConfiguration = new(columns, directory, ignore, int.Parse(keyIndex), pattern, primary); | ||||
|         AlertIfNewDeviceIsConnected(dynamicHostConfigurationProtocolConfiguration, logger); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										296
									
								
								ADO2024/PI2/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2024/PI2/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
| @ -3,11 +3,9 @@ using System.Diagnostics; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240429 | ||||
| { | ||||
| internal static partial class Helper20240429 { | ||||
|  | ||||
|     internal static void GitConfigCleanUp(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void GitConfigCleanUp(ILogger<Worker> logger, List<string> args) { | ||||
|         string[] files; | ||||
|         Process? process; | ||||
|         string? directory; | ||||
| @ -19,34 +17,34 @@ internal static partial class Helper20240429 | ||||
|         string[] removeRemotes = args[4].Split(','); | ||||
|         string systemVolumeInformation = Path.Combine(root, args[2]); | ||||
|         string[] subDirectories = Directory.GetDirectories(root, "*", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string subDirectory in subDirectories) | ||||
|         { | ||||
|             if (subDirectory == systemVolumeInformation) | ||||
|         foreach (string subDirectory in subDirectories) { | ||||
|             if (subDirectory == systemVolumeInformation) { | ||||
|                 continue; | ||||
|             } | ||||
|             files = Directory.GetFiles(subDirectory, searchPattern, SearchOption.AllDirectories); | ||||
|             foreach (string file in files) | ||||
|             { | ||||
|             foreach (string file in files) { | ||||
|                 directory = Path.GetDirectoryName(file); | ||||
|                 if (directory is null) | ||||
|                 if (directory is null) { | ||||
|                     continue; | ||||
|                 foreach (string removeRemote in removeRemotes) | ||||
|                 { | ||||
|                     processStartInfo = new() | ||||
|                     { | ||||
|                 } | ||||
|                 foreach (string removeRemote in removeRemotes) { | ||||
|                     processStartInfo = new() { | ||||
|                         FileName = "git", | ||||
|                         WorkingDirectory = directory, | ||||
|                         Arguments = $"remote rm {removeRemote}", | ||||
|                         RedirectStandardError = true | ||||
|                     }; | ||||
|                     process = Process.Start(processStartInfo); | ||||
|                     if (process is null) | ||||
|                     if (process is null) { | ||||
|                         continue; | ||||
|                     } | ||||
| #pragma warning disable IDE0058 | ||||
|                     process.WaitForExit(7000); | ||||
| #pragma warning restore IDE0058 | ||||
|                     standardError = process.StandardError.ReadToEnd(); | ||||
|                     if (!standardError.Contains(ignoreError)) | ||||
|                     if (!standardError.Contains(ignoreError)) { | ||||
|                         logger.LogInformation(standardError); | ||||
|                     } | ||||
|                     logger.LogInformation("for <{directoryName}> remote rm {removeRemote}", directory, removeRemote); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -2,33 +2,33 @@ using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240510 | ||||
| { | ||||
| internal static partial class Helper20240510 { | ||||
|  | ||||
|     internal static void PullIconsForBLM(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void PullIconsForBLM(ILogger<Worker> logger, List<string> args) { | ||||
|         string fileName; | ||||
|         FileInfo fileInfo; | ||||
|         string searchPattern = args[4]; | ||||
|         string sourceDirectory = args[3]; | ||||
|         string root = Path.GetFullPath(args[0]); | ||||
|         string createDirectory = Path.Combine(root, args[2]); | ||||
|         if (!Directory.Exists(createDirectory)) | ||||
|         if (!Directory.Exists(createDirectory)) { | ||||
|             _ = Directory.CreateDirectory(createDirectory); | ||||
|         } | ||||
|         string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             fileName = Path.GetFileName(file); | ||||
|             fileInfo = new(Path.Combine(createDirectory, fileName)); | ||||
|             if (fileInfo.Exists && fileInfo.LastWriteTime == new FileInfo(file).LastWriteTime) | ||||
|             if (fileInfo.Exists && fileInfo.LastWriteTime == new FileInfo(file).LastWriteTime) { | ||||
|                 continue; | ||||
|             } | ||||
|             File.Copy(file, fileInfo.FullName, overwrite: true); | ||||
|             logger.LogInformation("<{fileName}> copied", fileName); | ||||
|         } | ||||
|         logger.LogWarning("What reactor is this near?"); | ||||
|         string? reactor = Console.ReadLine(); | ||||
|         if (!string.IsNullOrEmpty(reactor)) | ||||
|         if (!string.IsNullOrEmpty(reactor)) { | ||||
|             _ = Directory.CreateDirectory(Path.Combine(sourceDirectory, Environment.MachineName, reactor)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -4,11 +4,9 @@ using System.Text.Json; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240513 | ||||
| { | ||||
| internal static partial class Helper20240513 { | ||||
|  | ||||
|     internal static void PersonKeyToName(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void PersonKeyToName(ILogger<Worker> logger, List<string> args) { | ||||
|         Person? person; | ||||
|         string directoryName; | ||||
|         string checkDirectory; | ||||
| @ -17,20 +15,21 @@ internal static partial class Helper20240513 | ||||
|         Dictionary<string, Person> keyValuePairs = []; | ||||
|         string[] directories = Directory.GetDirectories(root, "*", SearchOption.TopDirectoryOnly); | ||||
|         Dictionary<long, Person> people = JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException(); | ||||
|         foreach (KeyValuePair<long, Person> keyValuePair in people) | ||||
|         { | ||||
|             if (keyValuePair.Value.Birth?.Note is null) | ||||
|         foreach (KeyValuePair<long, Person> keyValuePair in people) { | ||||
|             if (keyValuePair.Value.Birth?.Note is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             keyValuePairs.Add(keyValuePair.Value.Birth.Note, keyValuePair.Value); | ||||
|         } | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|         foreach (string directory in directories) { | ||||
|             directoryName = Path.GetFileName(directory); | ||||
|             if (!keyValuePairs.TryGetValue(directoryName, out person) || person.Name?.ForwardSlashFull is null) | ||||
|             if (!keyValuePairs.TryGetValue(directoryName, out person) || person.Name?.ForwardSlashFull is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             checkDirectory = Path.Combine(root, $"{person.Name.ForwardSlashFull.Replace('/', '-')}{directoryName}-{person.Id}"); | ||||
|             if (Directory.Exists(checkDirectory)) | ||||
|             if (Directory.Exists(checkDirectory)) { | ||||
|                 continue; | ||||
|             } | ||||
|             Directory.Move(directory, checkDirectory); | ||||
|             logger.LogInformation("<{directory}> was moved", directory); | ||||
|         } | ||||
|  | ||||
| @ -5,16 +5,14 @@ using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240517 | ||||
| { | ||||
| internal static partial class Helper20240517 { | ||||
|  | ||||
|     private record ContentSignature([property: JsonPropertyName("contentSignature")] string Value, | ||||
|                                    [property: JsonPropertyName("contentSignatureType")] string ContentSignatureType); | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(ContentSignature))] | ||||
|     private partial class ContentSignatureGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ContentSignatureGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record Type([property: JsonPropertyName("count")] int Count, | ||||
| @ -23,8 +21,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Type))] | ||||
|     private partial class TypeGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class TypeGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record ImageAmazon([property: JsonPropertyName("colorSpace")] string ColorSpace, | ||||
| @ -54,8 +51,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(ImageAmazon))] | ||||
|     private partial class ImageAmazonGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ImageAmazonGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record ContentProperties([property: JsonPropertyName("contentDate")] DateTime ContentDate, | ||||
| @ -69,16 +65,14 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(ContentProperties))] | ||||
|     private partial class ContentPropertiesGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ContentPropertiesGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record XAccntParentMap(); | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(XAccntParentMap))] | ||||
|     private partial class XAccntParentMapGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class XAccntParentMapGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record Datum([property: JsonPropertyName("accessRuleIds")] IReadOnlyList<object> AccessRuleIds, | ||||
| @ -111,14 +105,12 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Datum))] | ||||
|     private partial class DatumGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class DatumGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Dictionary<string, Datum>))] | ||||
|     private partial class DictionaryDatumGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class DictionaryDatumGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record LocationAmazon([property: JsonPropertyName("count")] int Count, | ||||
| @ -127,8 +119,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(LocationAmazon))] | ||||
|     private partial class LocationAmazonGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class LocationAmazonGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record LocationInfo([property: JsonPropertyName("city")] string City, | ||||
| @ -139,8 +130,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(LocationInfo))] | ||||
|     private partial class LocationInfoGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class LocationInfoGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record SearchData([property: JsonPropertyName("clusterName")] string ClusterName, | ||||
| @ -150,8 +140,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(SearchData))] | ||||
|     private partial class SearchDataGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class SearchDataGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record AllPerson([property: JsonPropertyName("count")] int Count, | ||||
| @ -160,8 +149,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(AllPerson))] | ||||
|     private partial class AllPersonGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class AllPersonGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record PersonAmazon([property: JsonPropertyName("count")] int Count, | ||||
| @ -170,8 +158,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(PersonAmazon))] | ||||
|     private partial class PersonAmazonGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class PersonAmazonGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record ClusterId([property: JsonPropertyName("count")] int Count, | ||||
| @ -180,8 +167,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(ClusterId))] | ||||
|     private partial class ClusterIdGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ClusterIdGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record Thing([property: JsonPropertyName("count")] int Count, | ||||
| @ -190,8 +176,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Thing))] | ||||
|     private partial class ThingGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ThingGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record Time([property: JsonPropertyName("count")] int Count, | ||||
| @ -200,8 +185,7 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Time))] | ||||
|     private partial class TimeGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class TimeGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record ParentMap([property: JsonPropertyName("FOLDER")] IReadOnlyList<string> FOLDER); | ||||
| @ -216,14 +200,12 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Aggregations))] | ||||
|     private partial class AggregationsGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class AggregationsGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(ParentMap))] | ||||
|     private partial class ParentMapGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class ParentMapGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record RootAmazon([property: JsonPropertyName("aggregations")] Aggregations Aggregations, | ||||
| @ -232,12 +214,23 @@ internal static partial class Helper20240517 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(RootAmazon))] | ||||
|     private partial class RootAmazonGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class RootAmazonGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static void SaveAmazon(string destination, string harFile) | ||||
|     { | ||||
|     internal static void SaveAmazon(ILogger<Worker> logger, List<string> args) { | ||||
|         string root = Path.GetFullPath(args[0]); | ||||
|         string destination = Path.GetFullPath(args[2]); | ||||
|         if (string.IsNullOrEmpty(root)) { | ||||
|             throw new NullReferenceException(nameof(root)); | ||||
|         } | ||||
|         string[] harFiles = Directory.GetFiles(root, "*.har", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string harFile in harFiles) { | ||||
|             SaveAmazon(destination, harFile); | ||||
|         } | ||||
|         logger?.LogInformation("{harFiles} count", harFiles.Length); | ||||
|     } | ||||
|  | ||||
|     private static void SaveAmazon(string destination, string harFile) { | ||||
|         string offset; | ||||
|         string personId; | ||||
|         RootAmazon amazon; | ||||
| @ -247,37 +240,43 @@ internal static partial class Helper20240517 | ||||
|         PersonAmazon personAmazon; | ||||
|         Dictionary<string, string> keyValuePairs = []; | ||||
|         ReadOnlyCollection<(string Url, string AggregationLine)> aggregationLines = GetAggregationLines(harFile); | ||||
|         foreach ((string url, string aggregationLine) in aggregationLines) | ||||
|         { | ||||
|             if (aggregationLine.Contains(",\"category\":\"allPeople\"}")) | ||||
|         foreach ((string url, string aggregationLine) in aggregationLines) { | ||||
|             if (aggregationLine.Contains(",\"category\":\"allPeople\"}")) { | ||||
|                 continue; | ||||
|             } | ||||
|             amazon = JsonSerializer.Deserialize(aggregationLine, RootAmazonGenerationContext.Default.RootAmazon) ?? throw new Exception(); | ||||
|             if (amazon.Aggregations?.People is null || amazon.Aggregations.People.Count < 1) | ||||
|             if (amazon.Aggregations?.People is null || amazon.Aggregations.People.Count < 1) { | ||||
|                 continue; | ||||
|             } | ||||
|             personAmazon = amazon.Aggregations.People[0]; | ||||
|             if (!url.Contains(personAmazon.Match)) | ||||
|             if (!url.Contains(personAmazon.Match)) { | ||||
|                 continue; | ||||
|             } | ||||
|             personDirectory = Path.Combine(destination, personAmazon.SearchData.ClusterName); | ||||
|             _ = Directory.CreateDirectory(personDirectory); | ||||
|             personIdFile = Path.Combine(personDirectory, $"000) {personAmazon.Match}.json"); | ||||
|             _ = keyValuePairs.TryAdd(personAmazon.Match, personAmazon.SearchData.ClusterName); | ||||
|             SaveAmazon(amazon.Data, personIdFile); | ||||
|         } | ||||
|         foreach ((string url, string aggregationLine) in aggregationLines) | ||||
|         { | ||||
|             if (aggregationLine.Contains(",\"category\":\"allPeople\"}")) | ||||
|         foreach ((string url, string aggregationLine) in aggregationLines) { | ||||
|             if (aggregationLine.Contains(",\"category\":\"allPeople\"}")) { | ||||
|                 continue; | ||||
|             } | ||||
|             amazon = JsonSerializer.Deserialize(aggregationLine, RootAmazonGenerationContext.Default.RootAmazon) ?? throw new Exception(); | ||||
|             if (amazon.Aggregations?.People is not null && amazon.Aggregations.People.Count > 0) | ||||
|             if (amazon.Aggregations?.People is not null && amazon.Aggregations.People.Count > 0) { | ||||
|                 continue; | ||||
|             if (!url.Contains("offset=")) | ||||
|             } | ||||
|             if (!url.Contains("offset=")) { | ||||
|                 continue; | ||||
|             } | ||||
|             offset = url.Split("offset=")[1]; | ||||
|             if (!url.Contains("people%3A(")) | ||||
|             if (!url.Contains("people%3A(")) { | ||||
|                 continue; | ||||
|             } | ||||
|             personId = url.Split("people%3A(")[1].Split(')')[0]; | ||||
|             if (!keyValuePairs.TryGetValue(personId, out personName)) | ||||
|             if (!keyValuePairs.TryGetValue(personId, out personName)) { | ||||
|                 continue; | ||||
|             } | ||||
|             personDirectory = Path.Combine(destination, personName); | ||||
|             _ = Directory.CreateDirectory(personDirectory); | ||||
|             personIdFile = Path.Combine(personDirectory, $"{offset.Split('&')[0]}) {personId}.json"); | ||||
| @ -285,46 +284,37 @@ internal static partial class Helper20240517 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static void SaveAmazon(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|         string root = Path.GetFullPath(args[0]); | ||||
|         string destination = Path.GetFullPath(args[2]); | ||||
|         if (string.IsNullOrEmpty(root)) | ||||
|             throw new NullReferenceException(nameof(root)); | ||||
|         string[] harFiles = Directory.GetFiles(root, "*.har", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string harFile in harFiles) | ||||
|             SaveAmazon(destination, harFile); | ||||
|         logger?.LogInformation("{harFiles} count", harFiles.Length); | ||||
|     } | ||||
|  | ||||
|     private static void SaveAmazon(IReadOnlyList<Datum> data, string personIdFile) | ||||
|     { | ||||
|     private static void SaveAmazon(IReadOnlyList<Datum> data, string personIdFile) { | ||||
|         string json; | ||||
|         Dictionary<string, Datum> keyValuePairs = []; | ||||
|         foreach (Datum datum in data) | ||||
|         foreach (Datum datum in data) { | ||||
|             _ = keyValuePairs.TryAdd(datum.Name.Split('.')[0], datum); | ||||
|         } | ||||
|         json = JsonSerializer.Serialize(keyValuePairs, DictionaryDatumGenerationContext.Default.DictionaryStringDatum); | ||||
|         File.WriteAllText(personIdFile, json); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<(string, string)> GetAggregationLines(string harFile) | ||||
|     { | ||||
|     private static ReadOnlyCollection<(string, string)> GetAggregationLines(string harFile) { | ||||
|         List<(string, string)> results = []; | ||||
|         if (!File.Exists(harFile)) | ||||
|         if (!File.Exists(harFile)) { | ||||
|             throw new Exception(); | ||||
|         } | ||||
|         string lastUrl = string.Empty; | ||||
|         string text = "\"text\": \"{"; | ||||
|         string[] lines = File.ReadAllLines(harFile); | ||||
|         foreach (string line in lines) | ||||
|         { | ||||
|             if (line.Contains("\"url\": \"")) | ||||
|         foreach (string line in lines) { | ||||
|             if (line.Contains("\"url\": \"")) { | ||||
|                 lastUrl = line; | ||||
|             if (!line.Contains(text)) | ||||
|             } | ||||
|             if (!line.Contains(text)) { | ||||
|                 continue; | ||||
|             if (!line.Contains("aggregations")) | ||||
|             } | ||||
|             if (!line.Contains("aggregations")) { | ||||
|                 continue; | ||||
|             if (lastUrl.Contains("search?asset=NONE")) | ||||
|             } | ||||
|             if (lastUrl.Contains("search?asset=NONE")) { | ||||
|                 continue; | ||||
|             } | ||||
|             results.Add(new(lastUrl, line.Trim()[(text.Length - 1)..^1].Replace("\\\"", "\""))); | ||||
|             lastUrl = string.Empty; | ||||
|         } | ||||
|  | ||||
| @ -1,35 +1,58 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Globalization; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240518 | ||||
| { | ||||
| internal static partial class Helper20240518 { | ||||
|  | ||||
|     internal static void PersonKeyToImmichImport(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void PersonKeyToImmichImport(ILogger<Worker> logger, List<string> args) { | ||||
|         string json; | ||||
|         string name; | ||||
|         Person person; | ||||
|         string birthDate; | ||||
|         string ownerId = args[5]; | ||||
|         List<string> inserts = []; | ||||
|         string tableName = args[3]; | ||||
|         string birthDateFormat = args[6]; | ||||
|         string[] columns = args[4].Split(','); | ||||
|         string json = File.ReadAllText(args[2]); | ||||
|         string root = Path.GetFullPath(args[0]); | ||||
|         Dictionary<long, Person> people = JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException(); | ||||
|         foreach (KeyValuePair<long, Person> keyValuePair in people) | ||||
|         { | ||||
|             if (keyValuePair.Value.Birth?.Note is null || keyValuePair.Value.Name?.ForwardSlashFull is null || keyValuePair.Value.Birth?.Date is null) | ||||
|         Dictionary<string, string> keyValuePairs = []; | ||||
|         json = File.ReadAllText(Path.Combine(root, args[2])); | ||||
|         DateOnly minimumBirthDate = DateOnly.ParseExact(args[7], birthDateFormat, CultureInfo.InvariantCulture); | ||||
|         Dictionary<long, Person> people = JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? | ||||
|             throw new NullReferenceException(); | ||||
|         foreach (KeyValuePair<long, Person> keyValuePair in people) { | ||||
|             person = keyValuePair.Value; | ||||
|             if (string.IsNullOrEmpty(person.Birth?.Note) || string.IsNullOrEmpty(person.Name?.ForwardSlashFull) || person.Birth.Date < minimumBirthDate) { | ||||
|                 continue; | ||||
|             birthDate = keyValuePair.Value.Birth.Date.Value.ToString(birthDateFormat); | ||||
|             name = keyValuePair.Value.Name.ForwardSlashFull.Replace("/", string.Empty); | ||||
|             } | ||||
|             name = string.IsNullOrEmpty(person.Name.Suffix) ? person.Name.ForwardSlashFull : $"{person.Name.ForwardSlashFull} {person.Name.Suffix}"; | ||||
|             keyValuePairs.Add(person.Birth.Note, name); | ||||
|         } | ||||
|         json = JsonSerializer.Serialize(keyValuePairs, DictionaryStringStringSourceGenerationContext.Default.DictionaryStringString); | ||||
|         string jsonFile = Path.Combine(root, $"{DateTime.Now.Ticks}.json"); | ||||
|         logger.LogInformation("<{file}> saved", jsonFile); | ||||
|         File.WriteAllText(jsonFile, json); | ||||
|         foreach (KeyValuePair<long, Person> keyValuePair in people) { | ||||
|             person = keyValuePair.Value; | ||||
|             if (string.IsNullOrEmpty(person.Birth?.Note) || string.IsNullOrEmpty(person.Name?.ForwardSlashFull) || person.Birth?.Date is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             birthDate = person.Birth.Date.Value.ToString(birthDateFormat); | ||||
|             name = person.Name.ForwardSlashFull.Replace("/", string.Empty); | ||||
|             inserts.Add($"insert into \"{tableName}\" (\"{string.Join("\", \"", columns)}\") values ('{ownerId}', '{name}', '{birthDate}');"); | ||||
|         } | ||||
|         string file = Path.Combine(root, $"{DateTime.Now.Ticks}.sql"); | ||||
|         logger.LogInformation("<{file}> saved", file); | ||||
|         File.WriteAllLines(file, inserts); | ||||
|         string sqlFile = Path.Combine(root, $"{DateTime.Now.Ticks}.sql"); | ||||
|         logger.LogInformation("<{file}> saved", sqlFile); | ||||
|         File.WriteAllLines(sqlFile, inserts); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
| [JsonSerializable(typeof(Dictionary<string, string>))] | ||||
| internal partial class DictionaryStringStringSourceGenerationContext : JsonSerializerContext { | ||||
| } | ||||
| @ -4,45 +4,27 @@ using System.Collections.ObjectModel; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240519 | ||||
| { | ||||
| internal static partial class Helper20240519 { | ||||
|  | ||||
|     private record Record(long Length, long Ticks); | ||||
|  | ||||
|     private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(string source, string[] sourceFiles) | ||||
|     { | ||||
|         Dictionary<string, Record> results = []; | ||||
|         string key; | ||||
|         Record? record; | ||||
|         FileInfo fileInfo; | ||||
|         int sourceLength = source.Length; | ||||
|         foreach (string sourceFile in sourceFiles) | ||||
|         { | ||||
|             fileInfo = new(sourceFile); | ||||
|             key = sourceFile[sourceLength..]; | ||||
|             if (results.TryGetValue(key, out record)) | ||||
|                 throw new NotSupportedException(); | ||||
|             results.Add(key, new(fileInfo.Length, fileInfo.LastWriteTime.Ticks)); | ||||
|         } | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     internal static void FindReplaceDirectoryName(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void FindReplaceDirectoryName(ILogger<Worker> logger, List<string> args) { | ||||
|         string checkDirectory; | ||||
|         string replaceText = args[3]; | ||||
|         string[] findTexts = args[2].Split(','); | ||||
|         string root = Path.GetFullPath(args[0]); | ||||
|         string[] directories = Directory.GetDirectories(root, "*", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|         foreach (string directory in directories) { | ||||
|             checkDirectory = directory; | ||||
|             foreach (string findText in findTexts) | ||||
|             foreach (string findText in findTexts) { | ||||
|                 checkDirectory = checkDirectory.Replace(findText, replaceText); | ||||
|             if (checkDirectory == directory) | ||||
|             } | ||||
|             if (checkDirectory == directory) { | ||||
|                 continue; | ||||
|             if (Directory.Exists(checkDirectory)) | ||||
|             } | ||||
|             if (Directory.Exists(checkDirectory)) { | ||||
|                 continue; | ||||
|             } | ||||
|             logger.LogInformation("<{directory}> to <{checkDirectory}>", directory, checkDirectory); | ||||
|             Directory.Move(directory, checkDirectory); | ||||
|         } | ||||
| @ -57,24 +39,44 @@ internal static partial class Helper20240519 | ||||
|         ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(source, sourceFiles); | ||||
|         string[] compareFiles = Directory.GetFiles(compare, "*", SearchOption.AllDirectories); | ||||
|         int compareLength = compare.Length; | ||||
|         foreach (string compareFile in compareFiles) | ||||
|         { | ||||
|         foreach (string compareFile in compareFiles) { | ||||
|             fileInfo = new(compareFile); | ||||
|             key = compareFile[compareLength..]; | ||||
|             if (!keyValuePairs.TryGetValue(key, out record)) | ||||
|             if (!keyValuePairs.TryGetValue(key, out record)) { | ||||
|                 continue; | ||||
|             if (fileInfo.Length != record.Length || fileInfo.LastWriteTime.Ticks != record.Ticks) | ||||
|             } | ||||
|             if (fileInfo.Length != record.Length || fileInfo.LastWriteTime.Ticks != record.Ticks) { | ||||
|                 continue; | ||||
|             } | ||||
|             checkFile = $"{target}{key}"; | ||||
|             checkDirectory = Path.GetDirectoryName(checkFile) ?? throw new NotSupportedException(); | ||||
|             if (!Directory.Exists(checkDirectory)) | ||||
|             if (!Directory.Exists(checkDirectory)) { | ||||
|                 _ = Directory.CreateDirectory(checkDirectory); | ||||
|             if (File.Exists(checkFile)) | ||||
|             } | ||||
|             if (File.Exists(checkFile)) { | ||||
|                 continue; | ||||
|             } | ||||
|             logger.LogInformation("<{compareFile}> to <{checkFile}>", compareFile, checkFile); | ||||
|             File.Move(compareFile, checkFile); | ||||
|         } | ||||
|         HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, compare); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(string source, string[] sourceFiles) { | ||||
|         Dictionary<string, Record> results = []; | ||||
|         string key; | ||||
|         Record? record; | ||||
|         FileInfo fileInfo; | ||||
|         int sourceLength = source.Length; | ||||
|         foreach (string sourceFile in sourceFiles) { | ||||
|             fileInfo = new(sourceFile); | ||||
|             key = sourceFile[sourceLength..]; | ||||
|             if (results.TryGetValue(key, out record)) { | ||||
|                 throw new NotSupportedException(); | ||||
|             } | ||||
|             results.Add(key, new(fileInfo.Length, fileInfo.LastWriteTime.Ticks)); | ||||
|         } | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -6,66 +6,23 @@ using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240520 | ||||
| { | ||||
| internal static partial class Helper20240520 { | ||||
|  | ||||
|     private record RecordA(string Directory, string Extension, string SourceFile, Identifier Identifier); | ||||
|  | ||||
|     private record RecordB(ReadOnlyDictionary<int, Identifier> IdTo, ReadOnlyDictionary<long, Identifier> LengthTo, ReadOnlyDictionary<string, Identifier> PaddedTo); | ||||
|  | ||||
|     internal sealed record Identifier(int Id, long Length, string PaddedId, long Ticks) | ||||
|     { | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Identifier))] | ||||
|     internal partial class IdentifierSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     internal partial class IdentifierSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Identifier[]))] | ||||
|     internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static RecordB GetRecordB(string jsonFile) | ||||
|     { | ||||
|         RecordB result; | ||||
|         Dictionary<int, Identifier> idTo = []; | ||||
|         Dictionary<long, Identifier> lengthTo = []; | ||||
|         Dictionary<string, Identifier> paddedTo = []; | ||||
|         string? json = !File.Exists(jsonFile) ? null : File.ReadAllText(jsonFile); | ||||
|         Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); | ||||
|         if (identifiers is null && !string.IsNullOrEmpty(jsonFile)) | ||||
|             throw new Exception($"Invalid {nameof(jsonFile)}"); | ||||
|         if (identifiers is not null) | ||||
|         { | ||||
|             foreach (Identifier identifier in identifiers) | ||||
|             { | ||||
|                 idTo.Add(identifier.Id, identifier); | ||||
|                 paddedTo.Add(identifier.PaddedId, identifier); | ||||
|                 if (lengthTo.ContainsKey(identifier.Length)) | ||||
|                 { | ||||
|                     _ = lengthTo.Remove(identifier.Length); | ||||
|                     continue; | ||||
|                 } | ||||
|                 lengthTo.Add(identifier.Length, identifier); | ||||
|             } | ||||
|         } | ||||
|         result = new(new(idTo), new(lengthTo), new(paddedTo)); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal static void IdentifierRename(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void IdentifierRename(ILogger<Worker> logger, List<string> args) { | ||||
|         int id; | ||||
|         string key; | ||||
|         RecordA recordA; | ||||
| @ -87,50 +44,48 @@ internal static partial class Helper20240520 | ||||
|         string[] sourceFiles = Directory.GetFiles(source, "*", SearchOption.AllDirectories); | ||||
|         logger.LogInformation("Found {files}(s)", sourceFiles.Length); | ||||
|         int sourceLength = source.Length; | ||||
|         foreach (string sourceFile in sourceFiles) | ||||
|         { | ||||
|         foreach (string sourceFile in sourceFiles) { | ||||
|             fileInfo = new(sourceFile); | ||||
|             fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName); | ||||
|             if (fileInfo.Directory is null) | ||||
|             if (fileInfo.Directory is null) { | ||||
|                 throw new NotSupportedException(); | ||||
|             if (option == "Padded") | ||||
|             { | ||||
|                 if (fileNameWithoutExtension.Length < intMinValueLength) | ||||
|             } | ||||
|             if (option == "Padded") { | ||||
|                 if (fileNameWithoutExtension.Length < intMinValueLength) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 key = fileNameWithoutExtension; | ||||
|                 if (recordB.PaddedTo.TryGetValue(key, out identifier)) | ||||
|                 { | ||||
|                 if (recordB.PaddedTo.TryGetValue(key, out identifier)) { | ||||
|                     recordACollection.Add(new($"{destination}{fileInfo.Directory.FullName[sourceLength..]}", fileInfo.Extension, fileInfo.FullName, identifier)); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             if (option == "Length") | ||||
|             { | ||||
|                 if (recordB.LengthTo.TryGetValue(fileInfo.Length, out identifier)) | ||||
|                 { | ||||
|             if (option == "Length") { | ||||
|                 if (recordB.LengthTo.TryGetValue(fileInfo.Length, out identifier)) { | ||||
|                     checkDirectory = $"{destination}{fileInfo.Directory.FullName[sourceLength..]}"; | ||||
|                     if (!Directory.Exists(checkDirectory)) | ||||
|                     if (!Directory.Exists(checkDirectory)) { | ||||
|                         _ = Directory.CreateDirectory(checkDirectory); | ||||
|                     } | ||||
|                     checkFile = Path.Combine(checkDirectory, $"{identifier.PaddedId}{fileInfo.Extension}"); | ||||
|                     if (File.Exists(checkFile)) | ||||
|                     if (File.Exists(checkFile)) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     File.Copy(fileInfo.FullName, checkFile); | ||||
|                     logger.LogInformation("<{fileInfo.FullName}> was moved to <{checkFile}>", fileInfo.FullName, checkFile); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             if (option == "Id") | ||||
|             { | ||||
|                 if (int.TryParse(fileNameWithoutExtension, out id)) | ||||
|                 { | ||||
|                     if (recordB.IdTo.TryGetValue(id, out identifier)) | ||||
|                     { | ||||
|             if (option == "Id") { | ||||
|                 if (int.TryParse(fileNameWithoutExtension, out id)) { | ||||
|                     if (recordB.IdTo.TryGetValue(id, out identifier)) { | ||||
|                         checkDirectory = $"{destination}{fileInfo.Directory.FullName[sourceLength..]}"; | ||||
|                         if (!Directory.Exists(checkDirectory)) | ||||
|                         if (!Directory.Exists(checkDirectory)) { | ||||
|                             _ = Directory.CreateDirectory(checkDirectory); | ||||
|                         } | ||||
|                         checkFile = Path.Combine(checkDirectory, $"{identifier.PaddedId}{fileInfo.Extension}"); | ||||
|                         if (File.Exists(checkFile)) | ||||
|                         if (File.Exists(checkFile)) { | ||||
|                             continue; | ||||
|                         } | ||||
|                         File.Move(fileInfo.FullName, checkFile); | ||||
|                         logger.LogInformation("<{fileInfo.FullName}> was moved to <{checkFile}>", fileInfo.FullName, checkFile); | ||||
|                         continue; | ||||
| @ -138,18 +93,19 @@ internal static partial class Helper20240520 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (option == "Padded") | ||||
|         { | ||||
|             if (!isOffsetDeterministicHashCode) | ||||
|         if (option == "Padded") { | ||||
|             if (!isOffsetDeterministicHashCode) { | ||||
|                 recordACollection = (from l in recordACollection orderby l.Identifier.Ticks select l).ToList(); | ||||
|             for (int i = 0; i < recordACollection.Count; i++) | ||||
|             { | ||||
|             } | ||||
|             for (int i = 0; i < recordACollection.Count; i++) { | ||||
|                 recordA = recordACollection[i]; | ||||
|                 if (!Directory.Exists(recordA.Directory)) | ||||
|                 if (!Directory.Exists(recordA.Directory)) { | ||||
|                     _ = Directory.CreateDirectory(recordA.Directory); | ||||
|                 } | ||||
|                 checkFile = Path.Combine(recordA.Directory, isOffsetDeterministicHashCode ? $"{recordA.Identifier.PaddedId}{recordA.Extension}" : $"{offset + i}{recordA.Identifier.PaddedId}{recordA.Extension}"); | ||||
|                 if (File.Exists(checkFile)) | ||||
|                 if (File.Exists(checkFile)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 File.Move(recordA.SourceFile, checkFile); | ||||
|                 logger.LogInformation("<{recordA.SourceFile}> was moved to <{checkFile}>", recordA.SourceFile, checkFile); | ||||
|             } | ||||
| @ -157,4 +113,37 @@ internal static partial class Helper20240520 | ||||
|         HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, source); | ||||
|     } | ||||
|  | ||||
|     private static RecordB GetRecordB(string jsonFile) { | ||||
|         RecordB result; | ||||
|         Dictionary<int, Identifier> idTo = []; | ||||
|         Dictionary<long, Identifier> lengthTo = []; | ||||
|         Dictionary<string, Identifier> paddedTo = []; | ||||
|         string? json = !File.Exists(jsonFile) ? null : File.ReadAllText(jsonFile); | ||||
|         Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); | ||||
|         if (identifiers is null && !string.IsNullOrEmpty(jsonFile)) { | ||||
|             throw new Exception($"Invalid {nameof(jsonFile)}"); | ||||
|         } | ||||
|         if (identifiers is not null) { | ||||
|             foreach (Identifier identifier in identifiers) { | ||||
|                 idTo.Add(identifier.Id, identifier); | ||||
|                 paddedTo.Add(identifier.PaddedId, identifier); | ||||
|                 if (lengthTo.ContainsKey(identifier.Length)) { | ||||
|                     _ = lengthTo.Remove(identifier.Length); | ||||
|                     continue; | ||||
|                 } | ||||
|                 lengthTo.Add(identifier.Length, identifier); | ||||
|             } | ||||
|         } | ||||
|         result = new(new(idTo), new(lengthTo), new(paddedTo)); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal sealed record Identifier(int Id, long Length, string PaddedId, long Ticks) { | ||||
|  | ||||
|         public override string ToString() { | ||||
|             string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier); | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -2,6 +2,7 @@ using File_Folder_Helper.Helpers; | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Diagnostics; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| @ -9,8 +10,7 @@ using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240623 | ||||
| { | ||||
| internal static partial class Helper20240623 { | ||||
|  | ||||
|     [GeneratedRegex("([A-Z]+(.))")] | ||||
|     private static partial Regex UpperCase(); | ||||
| @ -19,12 +19,12 @@ internal static partial class Helper20240623 | ||||
|     private static partial Regex InvalidCharacter(); | ||||
|  | ||||
|     private record H1AndParamCase(string H1, string ParamCase); | ||||
|     private record SubTaskLine(string Text, bool Done, long? Ticks, int? Line); | ||||
|     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, | ||||
|                          string? DestinationDirectory, | ||||
|                          ReadOnlyCollection<string> DestinationDirectories, | ||||
|                          string DirectoryFilter, | ||||
|                          string Done, | ||||
|                          string IndexFile, | ||||
| @ -35,35 +35,34 @@ internal static partial class Helper20240623 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Input))] | ||||
|     private partial class InputSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class InputSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static Record GetRecord(Input input, FileInfo fileInfo) | ||||
|     { | ||||
|     private static Record GetRecord(Input input, FileInfo fileInfo) { | ||||
|         Record result; | ||||
|         string line; | ||||
|         int? stopLine = null; | ||||
|         int? subTasksLine = null; | ||||
|         int? codeInsidersLine = null; | ||||
|         LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo); | ||||
|         for (int i = 0; i < lineNumber.Lines.Count; i++) | ||||
|         { | ||||
|             if (lineNumber.Lines[i].StartsWith(input.CodeInsiders) && lineNumber.Lines[i][^1] == '"') | ||||
|             { | ||||
|                 if (lineNumber.Lines.Count > i + 1 && lineNumber.Lines[i + 1] == "```") | ||||
|                     codeInsidersLine = i; | ||||
|         for (int i = 0; i < lineNumber.Lines.Count; i++) { | ||||
|             line = lineNumber.Lines[i]; | ||||
|             if (line.StartsWith(input.CodeInsiders) && line[^1] == ')') { | ||||
|                 codeInsidersLine = i; | ||||
|             } | ||||
|             if (lineNumber.Lines[i] != input.SubTasks) | ||||
|  | ||||
|             if (line != input.SubTasks) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             subTasksLine = i; | ||||
|             if (codeInsidersLine is null) | ||||
|             if (codeInsidersLine is null) { | ||||
|                 break; | ||||
|             if (lineNumber.Lines.Count > i) | ||||
|             { | ||||
|                 for (int j = i + 1; j < lineNumber.Lines.Count; j++) | ||||
|                 { | ||||
|                     if (lineNumber.Lines[j].Length > 0 && lineNumber.Lines[j][0] == '#') | ||||
|                     { | ||||
|             } | ||||
|  | ||||
|             if (lineNumber.Lines.Count > i) { | ||||
|                 for (int j = i + 1; j < lineNumber.Lines.Count; j++) { | ||||
|                     if (lineNumber.Lines[j].Length > 0 && lineNumber.Lines[j][0] == '#') { | ||||
|                         stopLine = j; | ||||
|                         break; | ||||
|                     } | ||||
| @ -76,22 +75,18 @@ internal static partial class Helper20240623 | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static List<Record> GetRecords(Input input) | ||||
|     { | ||||
|     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))) | ||||
|         { | ||||
|         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) | ||||
|             { | ||||
|             foreach (string checkDirectory in checkDirectories) { | ||||
|                 directoryName = Path.GetFileName(checkDirectory); | ||||
|                 if (directoryName.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase)) | ||||
|                 { | ||||
|                 if (directoryName.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase)) { | ||||
|                     sourceDirectory = checkDirectory; | ||||
|                     break; | ||||
|                 } | ||||
| @ -99,10 +94,10 @@ internal static partial class Helper20240623 | ||||
|         } | ||||
|         string[] subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         List<string> files = Directory.GetFiles(sourceDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly).ToList(); | ||||
|         foreach (string subDirectory in subDirectories) | ||||
|         foreach (string subDirectory in subDirectories) { | ||||
|             files.AddRange(Directory.GetFiles(subDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly)); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         } | ||||
|         foreach (string file in files) { | ||||
|             fileInfo = new(file); | ||||
|             record = GetRecord(input, fileInfo); | ||||
|             results.Add(record); | ||||
| @ -110,60 +105,61 @@ internal static partial class Helper20240623 | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static string GetParamCase(string value) | ||||
|     { | ||||
|     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--) | ||||
|         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? foundDone, string fallbackLine, Record record) | ||||
|     { | ||||
|     private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, bool? foundStarted, bool? foundCompleted, string fallbackLine, Record record) { | ||||
|         List<SubTaskLine> results = []; | ||||
|         char done; | ||||
|         string line; | ||||
|         string text; | ||||
|         bool doneValue; | ||||
|         bool startedValue; | ||||
|         bool completedValue; | ||||
|         SubTaskLine subTaskLine; | ||||
|         bool foundSubTasks = false; | ||||
|         int tasksZeroLength = input.Tasks[0].Length; | ||||
|         long ticks = record.FileInfo.LastWriteTime.Ticks; | ||||
|         for (int i = 0; i < record.LineNumber.Lines.Count; i++) | ||||
|         { | ||||
|         for (int i = 0; i < record.LineNumber.Lines.Count; i++) { | ||||
|             line = record.LineNumber.Lines[i]; | ||||
|             if (!foundSubTasks && line == input.SubTasks) | ||||
|             if (!foundSubTasks && line == input.SubTasks) { | ||||
|                 foundSubTasks = true; | ||||
|             if (!foundSubTasks) | ||||
|             } | ||||
|             if (!foundSubTasks) { | ||||
|                 continue; | ||||
|             if (line.Length <= tasksZeroLength || !line.StartsWith(input.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; | ||||
|             doneValue = foundDone is not null && foundDone.Value; | ||||
|             subTaskLine = new($"  {line}", doneValue, ticks, i); | ||||
|             } | ||||
|             startedValue = foundStarted is not null && foundStarted.Value; | ||||
|             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; | ||||
|         if (record.LineNumber.H1 is null) | ||||
|             subTaskLine = new(fallbackLine, doneValue, ticks, Line: null); | ||||
|         else | ||||
|         { | ||||
|             done = foundDone is null || !foundDone.Value ? ' ' : 'x'; | ||||
|         startedValue = foundStarted is not null && foundStarted.Value; | ||||
|         completedValue = foundCompleted is not null && foundCompleted.Value; | ||||
|         if (record.LineNumber.H1 is null) { | ||||
|             subTaskLine = new(Text: fallbackLine, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: null); | ||||
|         } else { | ||||
|             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, doneValue, ticks, Line: 0); | ||||
|             subTaskLine = new(Text: text, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: 0); | ||||
|         } | ||||
|         results.Add(subTaskLine); | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     private static string GetSeasonName(int dayOfYear) | ||||
|     { | ||||
|         string result = dayOfYear switch | ||||
|         { | ||||
|     private static string GetSeasonName(int dayOfYear) { | ||||
|         string result = dayOfYear switch { | ||||
|             < 78 => "0.Winter", | ||||
|             < 124 => "1.Spring", | ||||
|             < 171 => "2.Spring", | ||||
| @ -185,7 +181,7 @@ internal static partial class Helper20240623 | ||||
|             "  - Done", | ||||
|             "---", | ||||
|             string.Empty, | ||||
|             $"# {h1}", | ||||
|             h1[0] == '#' ? h1 : $"# {h1}", | ||||
|             string.Empty, | ||||
|             "## Backlog", | ||||
|             string.Empty, | ||||
| @ -242,12 +238,12 @@ internal static partial class Helper20240623 | ||||
|                     "Day-Helper-2024-06-23", | ||||
|                     "*.md", | ||||
|                     "##_Sub-tasks", | ||||
|                     "code-insiders", | ||||
|                     "-_[code-insiders](", | ||||
|                     "index.md", | ||||
|                     "-_[,](", | ||||
|                     "##_Done", | ||||
|                     ".kan", | ||||
|                     "D:/5-Other-Small/Kanban/Year-Season", | ||||
|                     "J:/5-Other-Small/Kanban/Year-Season", | ||||
|                     "316940400000" | ||||
|                 ], | ||||
|                 "problemMatcher": [] | ||||
| @ -256,21 +252,20 @@ internal static partial class Helper20240623 | ||||
|     } | ||||
|     """.Replace("{}", directory.Replace('\\', '/')); | ||||
|  | ||||
|     private static void FileWriteAllText(string path, string contents) | ||||
|     { | ||||
|     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) | ||||
|         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) | ||||
|     { | ||||
|     private static ReadOnlyCollection<H1AndParamCase> GetH1ParamCaseCollection(Input input, ReadOnlyCollection<string> lines) { | ||||
|         List<H1AndParamCase> results = []; | ||||
|         string h1; | ||||
|         string line; | ||||
| @ -278,18 +273,21 @@ internal static partial class Helper20240623 | ||||
|         bool foundSubTasks = false; | ||||
|         H1AndParamCase h1AndParamCase; | ||||
|         int tasksZeroLength = input.Tasks[0].Length; | ||||
|         for (int i = 0; i < lines.Count; i++) | ||||
|         { | ||||
|         for (int i = 0; i < lines.Count; i++) { | ||||
|             line = lines[i]; | ||||
|             if (!foundSubTasks && line == input.SubTasks) | ||||
|             if (!foundSubTasks && line == input.SubTasks) { | ||||
|                 foundSubTasks = true; | ||||
|             if (!foundSubTasks) | ||||
|             } | ||||
|             if (!foundSubTasks) { | ||||
|                 continue; | ||||
|             if (line.Length <= tasksZeroLength || !line.StartsWith(input.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; | ||||
|             } | ||||
|             h1 = line[(tasksZeroLength + 3)..]; | ||||
|             if (string.IsNullOrEmpty(h1)) | ||||
|             if (string.IsNullOrEmpty(h1)) { | ||||
|                 continue; | ||||
|             } | ||||
|             paramCase = GetParamCase(h1); | ||||
|             h1AndParamCase = new(h1, paramCase); | ||||
|             results.Add(h1AndParamCase); | ||||
| @ -297,23 +295,24 @@ internal static partial class Helper20240623 | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void CreateFiles(string directory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection) | ||||
|     { | ||||
|         foreach (H1AndParamCase h1ParamCase in h1ParamCaseCollection) | ||||
|     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) | ||||
|     { | ||||
|     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)) | ||||
|         if (!Directory.Exists(tasksKanbanDirectory)) { | ||||
|             _ = Directory.CreateDirectory(tasksKanbanDirectory); | ||||
|         } | ||||
|         string verifiedVisualStudioCodeDirectory = Path.Combine(verifiedDirectory, ".vscode"); | ||||
|         if (!Directory.Exists(verifiedVisualStudioCodeDirectory)) | ||||
|         if (!Directory.Exists(verifiedVisualStudioCodeDirectory)) { | ||||
|             _ = Directory.CreateDirectory(verifiedVisualStudioCodeDirectory); | ||||
|         } | ||||
|         result = Path.Combine(kanbanDirectory, "index.md"); | ||||
|         CreateFiles(tasksKanbanDirectory, h1ParamCaseCollection); | ||||
|         FileWriteAllText(result, indexLines); | ||||
| @ -323,103 +322,153 @@ internal static partial class Helper20240623 | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, FileInfo fileInfo, LineNumber lineNumber) | ||||
|     { | ||||
|         List<SubTaskLine> results = []; | ||||
|         char done; | ||||
|         FileInfo f; | ||||
|         Record record; | ||||
|         bool doneValue; | ||||
|     private static ReadOnlyCollection<string> GetXColumns(Input input, int frontMatterYamlEnd, int value, ReadOnlyCollection<string> lines) { | ||||
|         List<string> results = []; | ||||
|         string[] segments; | ||||
|         string fallbackLine; | ||||
|         bool? foundDone = null; | ||||
|         ReadOnlyCollection<SubTaskLine> subTaskLines; | ||||
|         for (int i = 0; i < lineNumber.Lines.Count; i++) | ||||
|         { | ||||
|             if (lineNumber.Lines[i] == input.Done) | ||||
|                 foundDone = true; | ||||
|             segments = lineNumber.Lines[i].Split(input.Tasks[1]); | ||||
|             doneValue = foundDone is not null && foundDone.Value; | ||||
|             if (segments.Length > 2 || !segments[0].StartsWith(input.Tasks[0])) | ||||
|                 continue; | ||||
|             done = foundDone is null || !foundDone.Value ? ' ' : 'x'; | ||||
|             fallbackLine = $"- [{done}] {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(fallbackLine, doneValue, Ticks: null, Line: null)); | ||||
|                 continue; | ||||
|         for (int i = value + 1; i < frontMatterYamlEnd; i++) { | ||||
|             segments = lines[i].Replace("\t", "  ").Split("  - "); | ||||
|             if (segments.Length != 2) { | ||||
|                 break; | ||||
|             } | ||||
|             record = GetRecord(input, f); | ||||
|             if (lineNumber.H1 is not null && record.LineNumber.H1 is not null) | ||||
|             { | ||||
|                 string a = lineNumber.Lines[lineNumber.H1.Value]; | ||||
|                 string b = record.LineNumber.Lines[record.LineNumber.H1.Value]; | ||||
|                 if (b != a) | ||||
|                 { | ||||
|                     if (b != a) | ||||
|                     { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             subTaskLines = GetSubTaskLines(input, doneValue, fallbackLine, record); | ||||
|             for (int j = subTaskLines.Count - 1; j >= 0; j--) | ||||
|                 results.Add(subTaskLines[j]); | ||||
|             results.Add($"## {segments[1].Replace("'", string.Empty)}"); | ||||
|         } | ||||
|         if (results.Count == 0) { | ||||
|             results.Add(input.Done); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static Input GetInput(List<string> args) | ||||
|     { | ||||
|     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 codeInsiders = $"{args[4]} \""; | ||||
|         string done = args[7].Replace('_', ' '); | ||||
|         string subTasks = args[3].Replace('_', ' '); | ||||
|         string codeInsiders = args[4].Replace('_', ' '); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string? destinationDirectory = args.Count < 8 ? null : Path.GetFullPath(args[9]); | ||||
|         long? afterEpochTotalMilliseconds = args.Count < 9 ? null : long.Parse(args[10]); | ||||
|         ReadOnlyCollection<string> tasks = args[6].Split(',').Select(l => l.Replace('_', ' ')).ToArray().AsReadOnly(); | ||||
|         Input input = new(afterEpochTotalMilliseconds, codeInsiders, destinationDirectory, directoryFilter, done, indexFile, searchPattern, subTasks, sourceDirectory, tasks); | ||||
|         if (input.Tasks[0] != "- [" || input.Tasks[1] != "](") | ||||
|         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) | ||||
|     { | ||||
|     private static string? MaybeWriteAndGetIndexFile(Input input, Record record, string? checkDirectory) { | ||||
|         string? result; | ||||
|         if (string.IsNullOrEmpty(checkDirectory) || input.AfterEpochTotalMilliseconds is null || string.IsNullOrEmpty(input.DestinationDirectory) || !checkDirectory.Contains(input.DestinationDirectory)) | ||||
|         if (string.IsNullOrEmpty(checkDirectory) || input.AfterEpochTotalMilliseconds is null || input.DestinationDirectories.Count == 0) { | ||||
|             result = null; | ||||
|         else | ||||
|         { | ||||
|             if (record.LineNumber.H1 is null) | ||||
|         } else { | ||||
|             if (!input.DestinationDirectories.Any(checkDirectory.Contains)) { | ||||
|                 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) | ||||
|             } else { | ||||
|                 if (record.LineNumber.H1 is null) { | ||||
|                     result = null; | ||||
|                 else | ||||
|                 { | ||||
|                     ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection = GetH1ParamCaseCollection(input, record.LineNumber.Lines); | ||||
|                     if (h1ParamCaseCollection.Count == 0) | ||||
|                 } 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 | ||||
|                     { | ||||
|                         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())) | ||||
|                     } else { | ||||
|                         ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection = GetH1ParamCaseCollection(input, record.LineNumber.Lines); | ||||
|                         if (h1ParamCaseCollection.Count == 0) { | ||||
|                             result = null; | ||||
|                         else | ||||
|                             result = WriteAndGetIndexFile(h1, checkDirectory, h1ParamCaseCollection); | ||||
|                         } 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); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @ -427,50 +476,50 @@ internal static partial class Helper20240623 | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static bool FileWrite(Record record, List<string> newLines, double percent) | ||||
|     { | ||||
|     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) | ||||
|         { | ||||
|         if (record.StopLine is not null && record.SubTasksLine is not null) { | ||||
|             string contents; | ||||
|             string progressLine; | ||||
|             List<string> resultLines; | ||||
|             resultLines = record.LineNumber.Lines.ToList(); | ||||
|             if (record.LineNumber.FrontMatterYamlEnd is not null) | ||||
|             { | ||||
|             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) | ||||
|                 if (record.LineNumber.Progress is not null) { | ||||
|                     resultLines[record.LineNumber.Progress.Value] = progressLine; | ||||
|                 else | ||||
|                 { | ||||
|                 } 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) | ||||
|                 { | ||||
|                 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) | ||||
|                 { | ||||
|                 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--) | ||||
|             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) | ||||
|                 } | ||||
|                 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++) | ||||
|                 } | ||||
|                 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); | ||||
| @ -479,130 +528,194 @@ internal static partial class Helper20240623 | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static FileInfo GetIndexFileInfo(Input input, Record record, string codeInsidersLine) | ||||
|     { | ||||
|     private static string? GetInferredCheckDirectory(string directory) { | ||||
|         string? result = null; | ||||
|         DirectoryInfo directoryInfo; | ||||
|         List<string> directoryNames = []; | ||||
|         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); | ||||
|                     if (string.IsNullOrEmpty(result)) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (!string.IsNullOrEmpty(result)) { | ||||
|                     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; | ||||
|         string? checkDirectory = codeInsidersLine[input.CodeInsiders.Length..^1]; | ||||
|         if (!Directory.Exists(checkDirectory)) | ||||
|         { | ||||
|             if (!string.IsNullOrEmpty(input.DestinationDirectory) && checkDirectory.Contains(input.DestinationDirectory)) | ||||
|                 _ = Directory.CreateDirectory(checkDirectory); | ||||
|         if (record.CodeInsidersLine is null) { | ||||
|             throw new Exception(); | ||||
|         } | ||||
|         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); | ||||
|         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 (results.Count == 0) | ||||
|             { | ||||
|         } | ||||
|         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)) | ||||
|                 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; | ||||
|         double percent; | ||||
|         double doneCount; | ||||
|         string replace; | ||||
|         FileInfo fileInfo; | ||||
|         double startedCount; | ||||
|         List<Record> records; | ||||
|         double completedCount; | ||||
|         LineNumber lineNumber; | ||||
|         List<string> newLines; | ||||
|         bool reloadAny = false; | ||||
|         string? checkDirectory; | ||||
|         string codeInsidersLine; | ||||
|         List<string> oldLines = []; | ||||
|         Input input = GetInput(args); | ||||
|         string fileNameWithoutExtension; | ||||
|         long ticks = DateTime.Now.Ticks; | ||||
|         ReadOnlyCollection<SubTaskLine> subTaskLines; | ||||
|         for (int z = 0; z < 9; z++) | ||||
|         { | ||||
|         for (int z = 0; z < 9; z++) { | ||||
|             records = GetRecords(input); | ||||
|             foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l) | ||||
|             { | ||||
|                 if (record.SubTasksLine is null) | ||||
|             foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l) { | ||||
|                 if (record.SubTasksLine is null) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 fileNameWithoutExtension = Path.GetFileNameWithoutExtension(record.FileInfo.FullName); | ||||
|                 if (record.CodeInsidersLine is not null) | ||||
|                 if (record.CodeInsidersLine is not null) { | ||||
|                     logger.LogInformation("<{file}> has [{subTasks}]", fileNameWithoutExtension, input.SubTasks); | ||||
|                 else | ||||
|                 { | ||||
|                 } else { | ||||
|                     logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", fileNameWithoutExtension, input.SubTasks, input.CodeInsiders); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (record.StopLine is null) | ||||
|                 if (record.StopLine is null) { | ||||
|                     continue; | ||||
|                 codeInsidersLine = record.LineNumber.Lines[record.CodeInsidersLine.Value]; | ||||
|                 fileInfo = GetIndexFileInfo(input, record, codeInsidersLine); | ||||
|                 if (!fileInfo.Exists) | ||||
|                 { | ||||
|                     checkDirectory = codeInsidersLine[input.CodeInsiders.Length..^1]; | ||||
|                     logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", Path.GetFileName(checkDirectory), input.IndexFile); | ||||
|                 } | ||||
|                 fileInfo = GetIndexFileInfo(logger, input, record); | ||||
|                 if (!fileInfo.Exists) { | ||||
|                     logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", fileInfo.DirectoryName, input.IndexFile); | ||||
|                     continue; | ||||
|                 } | ||||
|                 oldLines.Clear(); | ||||
|                 checkDirectory = fileInfo.DirectoryName; | ||||
|                 lineNumber = HelperMarkdown.GetLineNumbers(fileInfo); | ||||
|                 subTaskLines = GetSubTaskLines(input, fileInfo, lineNumber); | ||||
|                 if (subTaskLines.Count == 0) | ||||
|                 if (subTaskLines.Count == 0) { | ||||
|                     continue; | ||||
|                 lineCheck = 0; | ||||
|                 for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++) | ||||
|                     oldLines.Add(record.LineNumber.Lines[i]); | ||||
|                 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.Done descending, l.Ticks, l.Line select l.Text).ToList(); | ||||
|                 if (subTaskLines.Count == 0) | ||||
|                     percent = 0; | ||||
|                 else | ||||
|                 { | ||||
|                     allCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 select 1).Count(); | ||||
|                     doneCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Done select 1).Count(); | ||||
|                     // done = allCount != doneCount ? ' ' : 'x'; | ||||
|                     percent = allCount == 0 ? 0 : Math.Round(doneCount / allCount, 3); | ||||
|                     // newLines.Insert(0, $"- [{done}] Sub-tasks {doneCount} of {allCount} [{percent * 100}%]"); | ||||
|                 } | ||||
|                 if (newLines.Count == oldLines.Count) | ||||
|                 { | ||||
|                     for (int i = 0; i < newLines.Count; i++) | ||||
|                     { | ||||
|                         if (newLines[i] != record.LineNumber.Lines[record.SubTasksLine.Value + 1 + i]) | ||||
|                 lineCheck = 0; | ||||
|                 for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++) { | ||||
|                     oldLines.Add(record.LineNumber.Lines[i]); | ||||
|                 } | ||||
|                 if (subTaskLines.Count == 0) { | ||||
|                     percent = 0; | ||||
|                     replace = "0"; | ||||
|                 } 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) | ||||
|                     if (lineCheck == newLines.Count) { | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 if (string.IsNullOrEmpty(checkDirectory)) | ||||
|                 if (string.IsNullOrEmpty(checkDirectory)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString()); | ||||
|                 _ = Directory.CreateDirectory(checkDirectory); | ||||
|                 Thread.Sleep(500); | ||||
|                 Directory.Delete(checkDirectory); | ||||
|                 reload = FileWrite(record, newLines, percent); | ||||
|                 if (!reloadAny && reload) | ||||
|                 reload = FileWrite(ticks, record, newLines, percent); | ||||
|                 if (!reloadAny && reload) { | ||||
|                     reloadAny = true; | ||||
|                 } | ||||
|             } | ||||
|             if (!reloadAny) | ||||
|             if (!reloadAny) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -2,11 +2,21 @@ using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240624 | ||||
| { | ||||
| internal static partial class Helper20240624 { | ||||
|  | ||||
|     private static void MoveUpOndDirectory(ILogger<Worker> logger, string sourceDirectory, string[] files) | ||||
|     { | ||||
|     internal static void MoveUpOneDirectory(ILogger<Worker> logger, List<string> args) { | ||||
|         string searchPattern = args[2]; | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string[] deleteMatchingIdsDirectoryNames = args[3].Split(','); | ||||
|         string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); | ||||
|         if (deleteMatchingIdsDirectoryNames.Length == 0) { | ||||
|             MoveUpOndDirectory(logger, sourceDirectory, files); | ||||
|         } else { | ||||
|             Distinct(args, sourceDirectory, deleteMatchingIdsDirectoryNames, files); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void MoveUpOndDirectory(ILogger<Worker> logger, string sourceDirectory, string[] files) { | ||||
|         string? match; | ||||
|         string checkFile; | ||||
|         FileInfo fileInfoA; | ||||
| @ -14,75 +24,64 @@ internal static partial class Helper20240624 | ||||
|         string? checkDirectory; | ||||
|         List<string> deleteFiles = []; | ||||
|         Dictionary<string, string> keyValuePairs = []; | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             checkDirectory = Path.GetDirectoryName(Path.GetDirectoryName(file)) ?? throw new NotSupportedException(); | ||||
|             checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); | ||||
|             if (File.Exists(checkFile)) | ||||
|             if (File.Exists(checkFile)) { | ||||
|                 throw new NotSupportedException(); | ||||
|             if (keyValuePairs.TryGetValue(checkFile, out match)) | ||||
|             { | ||||
|             } | ||||
|             if (keyValuePairs.TryGetValue(checkFile, out match)) { | ||||
|                 fileInfoA = new(file); | ||||
|                 fileInfoB = new(match); | ||||
|                 if (fileInfoA.Length != fileInfoB.Length) | ||||
|                 if (fileInfoA.Length != fileInfoB.Length) { | ||||
|                     throw new NotSupportedException("Files don't match!"); | ||||
|                 } | ||||
|                 logger.LogWarning("<{file}> already exists!", file); | ||||
|                 deleteFiles.Add(file); | ||||
|                 continue; | ||||
|             } | ||||
|             keyValuePairs.Add(checkFile, file); | ||||
|         } | ||||
|         foreach (string file in deleteFiles) | ||||
|         foreach (string file in deleteFiles) { | ||||
|             File.Delete(file); | ||||
|         foreach (KeyValuePair<string, string> keyValuePair in keyValuePairs) | ||||
|         } | ||||
|         foreach (KeyValuePair<string, string> keyValuePair in keyValuePairs) { | ||||
|             File.Move(keyValuePair.Value, keyValuePair.Key); | ||||
|         } | ||||
|         Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); | ||||
|     } | ||||
|  | ||||
|     private static void Distinct(List<string> args, string sourceDirectory, string[] deleteMatchingIdsDirectoryNames, string[] files) | ||||
|     { | ||||
|     private static void Distinct(List<string> args, string sourceDirectory, string[] deleteMatchingIdsDirectoryNames, string[] files) { | ||||
|         string fileName; | ||||
|         string directory; | ||||
|         List<string> distinct = []; | ||||
|         List<string> duplicate = []; | ||||
|         string[] keepMatchingIdsDirectoryNames = args[4].Split(','); | ||||
|         if (deleteMatchingIdsDirectoryNames.Length != keepMatchingIdsDirectoryNames.Length) | ||||
|         if (deleteMatchingIdsDirectoryNames.Length != keepMatchingIdsDirectoryNames.Length) { | ||||
|             throw new NotSupportedException("Check arg lengths!"); | ||||
|         } | ||||
|         string[] keepMatchingIdsDirectories = keepMatchingIdsDirectoryNames.Select(l => Path.Combine(sourceDirectory, l)).ToArray(); | ||||
|         string[] deleteMatchingIdsDirectories = deleteMatchingIdsDirectoryNames.Select(l => Path.Combine(sourceDirectory, l)).ToArray(); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             fileName = Path.GetFileName(file); | ||||
|             if (distinct.Contains(fileName)) | ||||
|             { | ||||
|             if (distinct.Contains(fileName)) { | ||||
|                 duplicate.Add(fileName); | ||||
|                 continue; | ||||
|             } | ||||
|             distinct.Add(Path.GetFileName(file)); | ||||
|         } | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             fileName = Path.GetFileName(file); | ||||
|             directory = Path.GetDirectoryName(file) ?? throw new NotSupportedException(); | ||||
|             if (!duplicate.Contains(fileName)) | ||||
|             if (!duplicate.Contains(fileName)) { | ||||
|                 continue; | ||||
|             if (deleteMatchingIdsDirectories.Contains(directory)) | ||||
|             } | ||||
|             if (deleteMatchingIdsDirectories.Contains(directory)) { | ||||
|                 File.Move(file, $"{file}.del"); | ||||
|             else if (!keepMatchingIdsDirectories.Contains(directory)) | ||||
|             } else if (!keepMatchingIdsDirectories.Contains(directory)) { | ||||
|                 throw new NotSupportedException($"Missing <{Path.GetFileName(directory)}> as a directory for {fileName}"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static void MoveUpOneDirectory(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|         string searchPattern = args[2]; | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string[] deleteMatchingIdsDirectoryNames = args[3].Split(','); | ||||
|         string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); | ||||
|         if (deleteMatchingIdsDirectoryNames.Length == 0) | ||||
|             MoveUpOndDirectory(logger, sourceDirectory, files); | ||||
|         else | ||||
|             Distinct(args, sourceDirectory, deleteMatchingIdsDirectoryNames, files); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -3,11 +3,9 @@ using System.Collections.ObjectModel; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240711 | ||||
| { | ||||
| internal static partial class Helper20240711 { | ||||
|  | ||||
|     internal static void GitRemoteRemove(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void GitRemoteRemove(ILogger<Worker> logger, List<string> args) { | ||||
|         string line; | ||||
|         string[] lines; | ||||
|         bool branchCheck; | ||||
| @ -27,52 +25,56 @@ internal static partial class Helper20240711 | ||||
|         string extension = args[7].Length > 2 ? args[7] : string.Empty; | ||||
|         string[] files = Directory.EnumerateFiles(sourceDirectory, searchPattern, new EnumerationOptions() { IgnoreInaccessible = true, RecurseSubdirectories = true, AttributesToSkip = FileAttributes.None }).ToArray(); | ||||
|         logger.LogInformation("Found {files} file(s)", files.Length); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             branchCheck = false; | ||||
|             remoteCheck = false; | ||||
|             lines = File.ReadAllLines(file); | ||||
|             for (int i = 0; i < lines.Length; i++) | ||||
|             { | ||||
|             for (int i = 0; i < lines.Length; i++) { | ||||
|                 line = lines[i]; | ||||
|                 if (!line.Contains(remoteToRemove)) | ||||
|                 if (!line.Contains(remoteToRemove)) { | ||||
|                     continue; | ||||
|                 if (!lines[i - 1].Contains(remoteToRemoveFilter)) | ||||
|                 } | ||||
|                 if (!lines[i - 1].Contains(remoteToRemoveFilter)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 remoteCheck = true; | ||||
|                 break; | ||||
|             } | ||||
|             for (int i = 0; i < lines.Length; i++) | ||||
|             { | ||||
|             for (int i = 0; i < lines.Length; i++) { | ||||
|                 line = lines[i]; | ||||
|                 if (!line.Contains(branchName)) | ||||
|                 if (!line.Contains(branchName)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 branchCheck = true; | ||||
|                 break; | ||||
|             } | ||||
|             if (!remoteCheck) | ||||
|             if (!remoteCheck) { | ||||
|                 continue; | ||||
|             } | ||||
|             directory = Path.GetDirectoryName(file); | ||||
|             if (directory is null) | ||||
|             if (directory is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             parentDirectory = Path.GetDirectoryName(directory); | ||||
|             if (parentDirectory is null) | ||||
|             if (parentDirectory is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             parentDirectoryName = Path.GetFileName(parentDirectory).ToLower(); | ||||
|             messages = Helpers.HelperGit.RemoteRemove(parentDirectory, lastRemoteSegment, CancellationToken.None); | ||||
|             foreach (string message in messages) | ||||
|             foreach (string message in messages) { | ||||
|                 logger.LogInformation("{function} => {parentDirectoryName}: [{message}]", nameof(Helpers.HelperGit.RemoteRemove), parentDirectoryName, message); | ||||
|             } | ||||
|             messages = Helpers.HelperGit.RemoteAdd(parentDirectory, remoteToAddName, $"{remoteToAddUrl}{parentDirectoryName}{extension}", CancellationToken.None); | ||||
|             foreach (string message in messages) | ||||
|             foreach (string message in messages) { | ||||
|                 logger.LogInformation("{function} => {parentDirectoryName}: [{message}]", nameof(Helpers.HelperGit.RemoteAdd), parentDirectoryName, message); | ||||
|             if (!branchCheck) | ||||
|             } | ||||
|             if (!branchCheck) { | ||||
|                 continue; | ||||
|             try | ||||
|             { messages = Helpers.HelperGit.PushBranch(parentDirectory, remoteToAddName, branchName, CancellationToken.None); } | ||||
|             catch (Exception ex) | ||||
|             { messages = new([ex.Message]); } | ||||
|             foreach (string message in messages) | ||||
|             } | ||||
|             try { messages = Helpers.HelperGit.PushBranch(parentDirectory, remoteToAddName, branchName, CancellationToken.None); } catch (Exception ex) { messages = new([ex.Message]); } | ||||
|             foreach (string message in messages) { | ||||
|                 logger.LogInformation("{function} => {parentDirectoryName}: [{message}]", nameof(Helpers.HelperGit.PushBranch), parentDirectoryName, message); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -5,8 +5,7 @@ using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240718 | ||||
| { | ||||
| internal static partial class Helper20240718 { | ||||
|  | ||||
|     private record Host([property: JsonPropertyName("a")] string? Id, | ||||
|                        [property: JsonPropertyName("b")] string? Colon, | ||||
| @ -21,67 +20,10 @@ internal static partial class Helper20240718 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true, AllowTrailingCommas = true)] | ||||
|     [JsonSerializable(typeof(Host[]))] | ||||
|     private partial class HostsSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class HostsSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static Host[] GetHosts(ILogger<Worker> logger, string file) | ||||
|     { | ||||
|         Host[] results; | ||||
|         string lines = File.ReadAllText(file); | ||||
|         string json = $"[{lines.Replace("\r\n", ",")}]"; | ||||
|         logger.LogDebug(lines); | ||||
|         results = JsonSerializer.Deserialize(json, HostsSourceGenerationContext.Default.HostArray) ?? throw new NullReferenceException(); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<string> GetLines(Host[] hosts, string title, string wired) | ||||
|     { | ||||
|         List<string> results = [$"# {title}", string.Empty, "```mermaid", "flowchart TB", $"  subgraph {title}"]; | ||||
|         int id; | ||||
|         string check; | ||||
|         List<int> distinct = []; | ||||
|         string newLine = $"{Environment.NewLine}    "; | ||||
|         foreach (Host host in hosts) | ||||
|         { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Hyphen.Length != 17) | ||||
|                 continue; | ||||
|             if (!int.TryParse(host.Id, out id)) | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             if (distinct.Contains(id)) | ||||
|                 throw new NotSupportedException($"{id} is not distinct!"); | ||||
|             distinct.Add(id); | ||||
|             results.Add($"    {id}(fa:{host.Type}{newLine}{host.Colon}{newLine}{host.Hyphen}{newLine}{host.Device}{newLine}https://{host.Name}/)"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add($"  subgraph {title}"); | ||||
|         foreach (Host host in from l in hosts orderby l.Location, l.Type, l.Line select l) | ||||
|         { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Hyphen.Length != 17) | ||||
|                 continue; | ||||
|             if (!int.TryParse(host.Id, out id)) | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             check = host.Type == wired ? "-->" : "-.->"; | ||||
|             results.Add($"    {id} {check} |{id}| {host.Location}{host.Type}{host.Line}"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add($"  subgraph {title}"); | ||||
|         foreach (Host host in from l in hosts orderby l.Line, l.Location, l.Type select l) | ||||
|         { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Line is null || host.Hyphen.Length != 17) | ||||
|                 continue; | ||||
|             if (!int.TryParse(host.Id, out id)) | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             check = host.Type == wired ? "-->" : "-.->"; | ||||
|             results.Add($"    {host.Location}{host.Type}{host.Line} {check} Line{host.Line}"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add("```"); | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     internal static void JsonToMarkdown(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void JsonToMarkdown(ILogger<Worker> logger, List<string> args) { | ||||
|         Host[] hosts; | ||||
|         string title = args[3]; | ||||
|         string wired = args[4]; | ||||
| @ -90,12 +32,68 @@ internal static partial class Helper20240718 | ||||
|         ReadOnlyCollection<string> lines; | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         foreach (string file in files) { | ||||
|             hosts = GetHosts(logger, file); | ||||
|             lines = GetLines(hosts, title, wired); | ||||
|             File.WriteAllText($"{file}{extension}", string.Join(Environment.NewLine, lines)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static Host[] GetHosts(ILogger<Worker> logger, string file) { | ||||
|         Host[] results; | ||||
|         string lines = File.ReadAllText(file); | ||||
|         string json = $"[{lines.Replace("\r\n", ",")}]"; | ||||
|         logger.LogDebug(lines); | ||||
|         results = JsonSerializer.Deserialize(json, HostsSourceGenerationContext.Default.HostArray) ?? throw new NullReferenceException(); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<string> GetLines(Host[] hosts, string title, string wired) { | ||||
|         List<string> results = [$"# {title}", string.Empty, "```mermaid", "flowchart TB", $"  subgraph {title}"]; | ||||
|         int id; | ||||
|         string check; | ||||
|         List<int> distinct = []; | ||||
|         string newLine = $"{Environment.NewLine}    "; | ||||
|         foreach (Host host in hosts) { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Hyphen.Length != 17) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!int.TryParse(host.Id, out id)) { | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             } | ||||
|             if (distinct.Contains(id)) { | ||||
|                 throw new NotSupportedException($"{id} is not distinct!"); | ||||
|             } | ||||
|             distinct.Add(id); | ||||
|             results.Add($"    {id}(fa:{host.Type}{newLine}{host.Colon}{newLine}{host.Hyphen}{newLine}{host.Device}{newLine}https://{host.Name}/)"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add($"  subgraph {title}"); | ||||
|         foreach (Host host in from l in hosts orderby l.Location, l.Type, l.Line select l) { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Hyphen.Length != 17) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!int.TryParse(host.Id, out id)) { | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             } | ||||
|             check = host.Type == wired ? "-->" : "-.->"; | ||||
|             results.Add($"    {id} {check} |{id}| {host.Location}{host.Type}{host.Line}"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add($"  subgraph {title}"); | ||||
|         foreach (Host host in from l in hosts orderby l.Line, l.Location, l.Type select l) { | ||||
|             if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Line is null || host.Hyphen.Length != 17) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!int.TryParse(host.Id, out id)) { | ||||
|                 throw new NotSupportedException($"{host.Id} is not a number"); | ||||
|             } | ||||
|             check = host.Type == wired ? "-->" : "-.->"; | ||||
|             results.Add($"    {host.Location}{host.Type}{host.Line} {check} Line{host.Line}"); | ||||
|         } | ||||
|         results.Add("  end"); | ||||
|         results.Add("```"); | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -3,11 +3,9 @@ using System.Diagnostics; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI2; | ||||
|  | ||||
| internal static partial class Helper20240728 | ||||
| { | ||||
| internal static partial class Helper20240728 { | ||||
|  | ||||
|     internal static void DownloadSslCertificates(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     internal static void DownloadSslCertificates(ILogger<Worker> logger, List<string> args) { | ||||
|         string file; | ||||
|         Process? process; | ||||
|         string[] segments; | ||||
| @ -23,8 +21,7 @@ internal static partial class Helper20240728 | ||||
|         int waitForExit = int.Parse(args[5]); | ||||
|         string[] subdomains = args[3].Split(','); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         ProcessStartInfo processStartInfo = new() | ||||
|         { | ||||
|         ProcessStartInfo processStartInfo = new() { | ||||
|             CreateNoWindow = true, | ||||
|             RedirectStandardError = true, | ||||
|             RedirectStandardOutput = true, | ||||
| @ -32,28 +29,29 @@ internal static partial class Helper20240728 | ||||
|             FileName = args[4], | ||||
|             WorkingDirectory = sourceDirectory | ||||
|         }; | ||||
|         foreach (string subdomain in subdomains) | ||||
|         { | ||||
|         foreach (string subdomain in subdomains) { | ||||
|             argumentSegment = $"{subdomain}.{domain}:443 -servername {subdomain}.{domain}"; | ||||
|             processStartInfo.Arguments = $"s_client -connect {subdomain}.{domain}:443 -servername {subdomain}.{domain}"; | ||||
|             process = Process.Start(processStartInfo); | ||||
|             if (process is null) | ||||
|             if (process is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             _ = process.WaitForExit(waitForExit); | ||||
|             process.Kill(entireProcessTree: true); | ||||
|             standardOutput = process.StandardOutput.ReadToEnd(); | ||||
|             if (!standardOutput.Contains(beginCertificate) || !standardOutput.Contains(endCertificate)) | ||||
|             { | ||||
|             if (!standardOutput.Contains(beginCertificate) || !standardOutput.Contains(endCertificate)) { | ||||
|                 standardError = process.StandardError.ReadToEnd(); | ||||
|                 logger.LogWarning($"Error: {subdomain}{Environment.NewLine}{standardOutput}{Environment.NewLine}{standardError}"); | ||||
|                 continue; | ||||
|             } | ||||
|             segments = standardOutput.Split(beginCertificate); | ||||
|             if (segments.Length != 2) | ||||
|             if (segments.Length != 2) { | ||||
|                 break; | ||||
|             } | ||||
|             segments = segments[1].Split(endCertificate); | ||||
|             if (segments.Length != 2) | ||||
|             if (segments.Length != 2) { | ||||
|                 break; | ||||
|             } | ||||
|             lines.Add($"{logSegment} \"{store}\" {subdomain}.{domain}.cert"); | ||||
|             file = Path.Combine(sourceDirectory, $"{subdomain}.{domain}.cert"); | ||||
|             File.WriteAllText(file, $"{beginCertificate}{segments[0]}{endCertificate}{Environment.NewLine}"); | ||||
|  | ||||
| @ -701,7 +701,7 @@ internal static partial class Helper20240911 | ||||
|         List<char> spaces = []; | ||||
|         List<string> lines = []; | ||||
|         ReadOnlyCollection<WorkItem> results; | ||||
|         string[] workItemTypes = args[4].Split('|'); | ||||
|         string[] workItemTypes = args[4].Split('~'); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string destinationDirectory = Path.GetFullPath(args[6]); | ||||
|         if (!Directory.Exists(destinationDirectory)) | ||||
|  | ||||
							
								
								
									
										296
									
								
								ADO2024/PI4/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2024/PI4/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
| @ -1,12 +1,15 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| #if WorkItems | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| #endif | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241108 | ||||
| { | ||||
| internal static partial class Helper20241108 { | ||||
|  | ||||
| #if WorkItems | ||||
|  | ||||
|     private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked, | ||||
|                              [property: JsonPropertyName("name")] string Name, | ||||
| @ -45,13 +48,11 @@ internal static partial class Helper20241108 | ||||
|                             string Title, | ||||
|                             string? Violation, | ||||
|                             long? WeightedShortestJobFirst, | ||||
|                             string WorkItemType) | ||||
|     { | ||||
|                             string WorkItemType) { | ||||
|  | ||||
|         public override string ToString() => $"{Id} - {WorkItemType} - {Title}"; | ||||
|  | ||||
|         public static WorkItem Get(WorkItem workItem, Relation[] relations) | ||||
|         { | ||||
|         public static WorkItem Get(WorkItem workItem, Relation[] relations) { | ||||
|             WorkItem result = new(workItem.ActivatedDate, | ||||
|                                   workItem.AreaPath, | ||||
|                                   workItem.AssignedTo, | ||||
| @ -83,8 +84,7 @@ internal static partial class Helper20241108 | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public static WorkItem? GetWithOutRelations(WorkItem? workItem) | ||||
|         { | ||||
|         public static WorkItem? GetWithOutRelations(WorkItem? workItem) { | ||||
|             WorkItem? result = workItem is null ? null : new(workItem.ActivatedDate, | ||||
|                                                              workItem.AreaPath, | ||||
|                                                              workItem.AssignedTo, | ||||
| @ -120,21 +120,17 @@ internal static partial class Helper20241108 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(WorkItem))] | ||||
|     private partial class WorkItemSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class WorkItemSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(WorkItem[]))] | ||||
|     private partial class WorkItemCollectionSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class WorkItemCollectionSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record Record(WorkItem WorkItem, WorkItem? Parent, Record[]? Children, Record[]? Related, Record[]? Successors) | ||||
|     { | ||||
|     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) { | ||||
|             Record result; | ||||
|             WorkItem workItem = new(record.WorkItem.ActivatedDate, | ||||
|                                     record.WorkItem.AreaPath, | ||||
| @ -168,8 +164,7 @@ internal static partial class Helper20241108 | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         private static Record Get(Record record, bool keepRelations) | ||||
|         { | ||||
|         private static Record Get(Record record, bool keepRelations) { | ||||
|             Record result; | ||||
|             Record[]? childRecords; | ||||
|             Record[]? relatedRecords; | ||||
| @ -179,8 +174,7 @@ internal static partial class Helper20241108 | ||||
|             WorkItem? workItem = keepRelations ? record.WorkItem : WorkItem.GetWithOutRelations(record.WorkItem) ?? throw new Exception(); | ||||
|             if (record.Children is null) | ||||
|                 childRecords = null; | ||||
|             else | ||||
|             { | ||||
|             else { | ||||
|                 relationRecords = []; | ||||
|                 foreach (Record r in record.Children) | ||||
|                     relationRecords.Add(Get(r, keepRelations)); | ||||
| @ -188,8 +182,7 @@ internal static partial class Helper20241108 | ||||
|             } | ||||
|             if (record.Related is null) | ||||
|                 relatedRecords = null; | ||||
|             else | ||||
|             { | ||||
|             else { | ||||
|                 relationRecords = []; | ||||
|                 foreach (Record r in record.Related) | ||||
|                     relationRecords.Add(Get(r, keepRelations)); | ||||
| @ -197,8 +190,7 @@ internal static partial class Helper20241108 | ||||
|             } | ||||
|             if (record.Successors is null) | ||||
|                 successorRecords = null; | ||||
|             else | ||||
|             { | ||||
|             else { | ||||
|                 relationRecords = []; | ||||
|                 foreach (Record r in record.Successors) | ||||
|                     relationRecords.Add(Get(r, keepRelations)); | ||||
| @ -208,16 +200,14 @@ internal static partial class Helper20241108 | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         internal static Record Get(WorkItem workItem, WorkItem? parent, ReadOnlyCollection<Record>? children, ReadOnlyCollection<Record>? related, ReadOnlyCollection<Record>? successors, bool keepRelations) | ||||
|         { | ||||
|         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; | ||||
|         } | ||||
|  | ||||
|         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 = []; | ||||
|             Record record; | ||||
|             nests.Add(true); | ||||
| @ -227,11 +217,9 @@ internal static partial class Helper20241108 | ||||
|             ReadOnlyCollection<Record>? childRecords; | ||||
|             ReadOnlyCollection<Record>? relatedRecords; | ||||
|             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(); | ||||
|                 foreach (Relation relation in workItem.Relations) | ||||
|                 { | ||||
|                 foreach (Relation relation in workItem.Relations) { | ||||
|                     if (relation.Attributes.Name != relationName) | ||||
|                         continue; | ||||
|                     if (workItem.Parent is not null && relation.Id == workItem.Parent.Value) | ||||
| @ -241,8 +229,7 @@ internal static partial class Helper20241108 | ||||
|                     collection.Add(relationWorkItem); | ||||
|                 } | ||||
|                 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 > 500) | ||||
|                         break; | ||||
|                     if (w.Parent is null) | ||||
| @ -264,12 +251,10 @@ internal static partial class Helper20241108 | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Record[]))] | ||||
|     private partial class RecordCollectionBCommonSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class RecordCollectionBCommonSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<int, Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, bool keepRelations) | ||||
|     { | ||||
|     private static ReadOnlyDictionary<int, Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, bool keepRelations) { | ||||
|         Dictionary<int, Record> results = []; | ||||
|         Record record; | ||||
|         List<bool> nests = []; | ||||
| @ -277,23 +262,19 @@ internal static partial class Helper20241108 | ||||
|         ReadOnlyCollection<Record> childRecords; | ||||
|         ReadOnlyCollection<Record> relatedRecords; | ||||
|         ReadOnlyCollection<Record> successorRecords; | ||||
|         foreach (KeyValuePair<int, WorkItem> keyValuePair in keyValuePairs) | ||||
|         { | ||||
|         foreach (KeyValuePair<int, WorkItem> keyValuePair in keyValuePairs) { | ||||
|             nests.Clear(); | ||||
|             if (keyValuePair.Value.Parent is null) | ||||
|                 parentWorkItem = null; | ||||
|             else | ||||
|                 _ = keyValuePairs.TryGetValue(keyValuePair.Value.Parent.Value, out parentWorkItem); | ||||
|             try | ||||
|             { | ||||
|             try { | ||||
|                 childRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Child", nests, keepRelations); // Forward | ||||
|                 // records = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse | ||||
|                 relatedRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Related", nests, keepRelations); // Related | ||||
|                 successorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Successor", nests, keepRelations); // Forward | ||||
|                 record = Record.Get(keyValuePair.Value, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { | ||||
|             } catch (Exception) { | ||||
|                 record = new(keyValuePair.Value, parentWorkItem, [], [], []); | ||||
|             } | ||||
|             results.Add(keyValuePair.Key, record); | ||||
| @ -301,8 +282,7 @@ internal static partial class Helper20241108 | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<int, Record> GetWorkItems(ReadOnlyCollection<WorkItem> workItems, bool keepRelations) | ||||
|     { | ||||
|     private static ReadOnlyDictionary<int, Record> GetWorkItems(ReadOnlyCollection<WorkItem> workItems, bool keepRelations) { | ||||
|         ReadOnlyDictionary<int, Record> results; | ||||
|         Dictionary<int, WorkItem> keyValuePairs = []; | ||||
|         foreach (WorkItem workItem in workItems) | ||||
| @ -311,14 +291,12 @@ internal static partial class Helper20241108 | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<WorkItem>? GetWorkItems(string fileName, string sourceDirectory) | ||||
|     { | ||||
|     private static ReadOnlyCollection<WorkItem>? GetWorkItems(string fileName, string sourceDirectory) { | ||||
|         WorkItem[]? results; | ||||
|         string? checkFile = null; | ||||
|         string? checkDirectory = sourceDirectory; | ||||
|         string? pathRoot = Path.GetPathRoot(sourceDirectory); | ||||
|         for (int i = 0; i < int.MaxValue; i++) | ||||
|         { | ||||
|         for (int i = 0; i < int.MaxValue; i++) { | ||||
|             checkDirectory = Path.GetDirectoryName(checkDirectory); | ||||
|             if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot) | ||||
|                 break; | ||||
| @ -329,16 +307,14 @@ internal static partial class Helper20241108 | ||||
|         } | ||||
|         if (checkFile is null) | ||||
|             results = null; | ||||
|         else | ||||
|         { | ||||
|         else { | ||||
|             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]; | ||||
|         bool keepRelations = true; | ||||
|         string fileName = args[2]; | ||||
| @ -347,24 +323,30 @@ internal static partial class Helper20241108 | ||||
|         string idCheck = sourceDirectoryName.Split('-', StringSplitOptions.None)[0]; | ||||
|         if (!int.TryParse(idCheck, out int id)) | ||||
|             logger.LogInformation("Not valid directory!"); | ||||
|         else | ||||
|         { | ||||
|         else { | ||||
|             ReadOnlyCollection<WorkItem>? workItems = GetWorkItems(fileName, sourceDirectory); | ||||
|             if (workItems is null) | ||||
|                 logger.LogInformation("No file found!"); | ||||
|             else | ||||
|             { | ||||
|             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 | ||||
|                 { | ||||
|                 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 | ||||
|  | ||||
| } | ||||
| @ -2,13 +2,11 @@ using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241115 | ||||
| { | ||||
| internal static partial class Helper20241115 { | ||||
|  | ||||
| #if BIORAD | ||||
|  | ||||
|     internal static void ScanPast(string text, int[] i, string search) | ||||
|     { | ||||
|     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; | ||||
| @ -16,15 +14,13 @@ internal static partial class Helper20241115 | ||||
|             i[0] = text.Length; | ||||
|     } | ||||
|  | ||||
|     internal static void GetComplete(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     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 | ||||
|         { | ||||
|         else { | ||||
|             List<string> group = []; | ||||
|             string text = File.ReadAllText(files[0]); | ||||
|             int[] i = [0]; | ||||
| @ -34,12 +30,10 @@ internal static partial class Helper20241115 | ||||
|             string[] segments = text.Substring(i[0]).Split('*'); | ||||
|             string[] segmentsB; | ||||
|             string[] segmentsC; | ||||
|             foreach (string segment in segments) | ||||
|             { | ||||
|             foreach (string segment in segments) { | ||||
|                 segmentsB = segment.Split(Environment.NewLine); | ||||
|                 segmentsC = segmentsB[0].Split(' '); | ||||
|                 if (segment.Contains("Group")) | ||||
|                 { | ||||
|                 if (segment.Contains("Group")) { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @ -47,13 +41,13 @@ internal static partial class Helper20241115 | ||||
|  | ||||
| #else | ||||
|  | ||||
|     internal static void GetComplete(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     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) | ||||
|         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]); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -3,19 +3,17 @@ using System.Text; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241204 | ||||
| { | ||||
| internal static partial class Helper20241204 { | ||||
|  | ||||
|     internal static void ConvertToUTF8(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     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) | ||||
|         if (files.Length == 0) { | ||||
|             logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         } | ||||
|         foreach (string file in files) { | ||||
|             text = File.ReadAllText(file); | ||||
|             File.WriteAllText(file, text, Encoding.UTF8); | ||||
|         } | ||||
|  | ||||
| @ -2,33 +2,33 @@ using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241212 | ||||
| { | ||||
| internal static partial class Helper20241212 { | ||||
|  | ||||
|     internal static void Rename(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|     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[] searchPatterns = args[3].Split('~'); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); | ||||
|         if (files.Length == 0) | ||||
|         if (files.Length == 0) { | ||||
|             logger.LogError("No files found in {sourceDirectory} with search pattern {searchPattern}", sourceDirectory, searchPattern); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         } | ||||
|         foreach (string file in files) { | ||||
|             fileName = Path.GetFileName(file); | ||||
|             directoryName = Path.GetDirectoryName(file) ?? throw new Exception(); | ||||
|             newFileName = fileName; | ||||
|             foreach (string pattern in searchPatterns) | ||||
|             foreach (string pattern in searchPatterns) { | ||||
|                 newFileName = newFileName.Replace(pattern, ""); | ||||
|             } | ||||
|             newFile = Path.Combine(directoryName, newFileName); | ||||
|             if (File.Exists(newFile)) | ||||
|             if (File.Exists(newFile)) { | ||||
|                 logger.LogError("File {newFile} already exists", newFile); | ||||
|             else | ||||
|             } else { | ||||
|                 File.Move(file, newFile); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| using DiscUtils.Iso9660; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Renci.SshNet; | ||||
| using Renci.SshNet.Sftp; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Diagnostics; | ||||
| using System.IO.Compression; | ||||
| @ -10,373 +8,340 @@ using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241217 | ||||
| { | ||||
| internal static partial class Helper20241217 { | ||||
|  | ||||
|     private record Record(string Directory, Job? Job, string Path); | ||||
|     private record Job(string AlternatePath, string Directory, string Extension, File[] Files, int FilesCount, double FilesTotalLength, int Keep, Target[] Targets); | ||||
|     private record SecureShell(string Host, string Key, string Path, bool Required, string User); | ||||
|     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 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 | ||||
|     { | ||||
|     private partial class JobSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(File[]))] | ||||
|     private partial class FilesSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     private partial class FilesSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static IEnumerable<Record> GetRecords(string directory, string searchPattern) | ||||
|     { | ||||
|     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); | ||||
|                 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 (snap2HyperTextMarkupLanguage is not null && System.IO.File.Exists(snap2HyperTextMarkupLanguage)) { | ||||
|                     if (!areTheyTheSame || (areTheyTheSame && !System.IO.File.Exists(record.Snap2HyperTextMarkupLanguage))) { | ||||
|                         WriteSnap2HyperTextMarkupLanguage(logger, snap2HyperTextMarkupLanguage, record); | ||||
|                     } | ||||
|                 } | ||||
|                 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.LogWarning("Writing <{path}> extension", path); | ||||
|                 WritePassedExtension(record, files, record.DirectoryName, path); | ||||
|                 logger.LogWarning("Wrote <{path}> extension", path); | ||||
|                 logger.LogWarning("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; | ||||
|         IEnumerable<string> files = Directory.EnumerateFiles(directory, searchPattern, new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true }); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|         string sourceDirectory; | ||||
|         string destinationDirectory; | ||||
|         string snap2HyperTextMarkupLanguage; | ||||
|         foreach (string file in files) { | ||||
|             fileName = Path.GetFileName(file); | ||||
|             directoryName = Path.GetDirectoryName(file) ?? throw new Exception(); | ||||
|             if (!fileName.StartsWith('.')) | ||||
|             { | ||||
|                 System.IO.File.Move(file, Path.Combine(directoryName, $".{fileName}")); | ||||
|             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); | ||||
|             job = JsonSerializer.Deserialize(json, JobSourceGenerationContext.Default.Job); | ||||
|             record = new(directoryName, job, 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 ReadOnlyCollection<File> GetFiles(string directory, string searchPattern, string[] ignoreFileNames) | ||||
|     { | ||||
|     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) | ||||
|         foreach (FileInfo fileInfo in fileInfoCollection) { | ||||
|             if (fileInfo.Name == searchPattern) { | ||||
|                 continue; | ||||
|             if (ignoreFileNames.Any(l => l == fileInfo.Name)) | ||||
|             } | ||||
|             if (ignoreFileNames.Any(l => l == fileInfo.Name)) { | ||||
|                 continue; | ||||
|             if (!string.IsNullOrEmpty(fileInfo.LinkTarget)) | ||||
|             } | ||||
|             if (!string.IsNullOrEmpty(fileInfo.LinkTarget)) { | ||||
|                 continue; | ||||
|             } | ||||
|             relativePath = Path.GetRelativePath(directory, fileInfo.FullName).Replace(';', '_'); | ||||
|             if (relativePath.StartsWith("..")) | ||||
|             if (relativePath.StartsWith("..")) { | ||||
|                 relativePath = relativePath[3..]; | ||||
|             file = new(fileInfo.LastWriteTime.Ticks, fileInfo.Length, relativePath); | ||||
|             } | ||||
|             file = new(LastWriteTicks: fileInfo.LastWriteTime.Ticks, Length: fileInfo.Length, RelativePath: relativePath); | ||||
|             results.Add(file); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<File> GetFiles(string searchPattern, string[] ignoreFileNames, Record record) => | ||||
|         GetFiles(record.Directory, searchPattern, ignoreFileNames); | ||||
|  | ||||
|     private static string? GetJsonIfNotEqual(string searchPattern, string[] ignoreFileNames, Record record, Job job, ReadOnlyCollection<File> files) | ||||
|     { | ||||
|         string? result; | ||||
|         string? jsonNew; | ||||
|         string? jsonOld; | ||||
|         string fileName; | ||||
|         int ignoreCount = 0; | ||||
|         double filesTotalLengthNew = 0; | ||||
|         File[] filesArray = files.ToArray(); | ||||
|         double filesTotalLengthOld = job.FilesTotalLength; | ||||
|         foreach (File file in files) | ||||
|             filesTotalLengthNew += file.Length; | ||||
|         Job jobNew = new(job.AlternatePath, | ||||
|                          record.Directory, | ||||
|                          job.Extension, | ||||
|                          filesArray, | ||||
|                          files.Count, | ||||
|                          filesTotalLengthNew, | ||||
|                          job.Keep, | ||||
|                          job.Targets); | ||||
|         result = JsonSerializer.Serialize(jobNew, JobSourceGenerationContext.Default.Job); | ||||
|         if (filesTotalLengthNew != filesTotalLengthOld) | ||||
|         { | ||||
|             filesTotalLengthOld = 0; | ||||
|             foreach (File file in job.Files) | ||||
|             { | ||||
|                 fileName = Path.GetFileName(file.RelativePath); | ||||
|                 if (fileName == searchPattern || ignoreFileNames.Any(l => l == fileName)) | ||||
|                 { | ||||
|                     ignoreCount += 1; | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (file.Length == 0) | ||||
|                 { | ||||
|                     ignoreCount += 1; | ||||
|                     continue; | ||||
|                 } | ||||
|                 filesTotalLengthOld += file.Length; | ||||
|             } | ||||
|         } | ||||
|         if (filesTotalLengthNew != filesTotalLengthOld || files.Count != (job.Files.Length - ignoreCount)) | ||||
|         { | ||||
|             jsonNew = null; | ||||
|             jsonOld = null; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             jsonNew = JsonSerializer.Serialize((from l in filesArray orderby l.RelativePath.Length, l.RelativePath select l).ToArray(), FilesSourceGenerationContext.Default.FileArray); | ||||
|             jsonOld = JsonSerializer.Serialize((from l in job.Files orderby l.RelativePath.Length, l.RelativePath where l.RelativePath != searchPattern select l).ToArray(), FilesSourceGenerationContext.Default.FileArray); | ||||
|         } | ||||
|         if (!string.IsNullOrEmpty(jsonNew) && !string.IsNullOrEmpty(jsonOld) && jsonNew == jsonOld) | ||||
|             result = null; | ||||
|     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 void WriteISO(Record record, ReadOnlyCollection<File> files, string path, string directoryName) | ||||
|     { | ||||
|     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.LogWarning("<{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.LogWarning("<{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.LogWarning("<{directory}> file serialized are different {filesTotalLengthNew} != {filesTotalLengthOld}", record.SourceDirectory, filesTotalLengthNew, filesTotalLengthOld); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     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 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.Directory, file.RelativePath)); | ||||
|         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) | ||||
|     { | ||||
|     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) | ||||
|         { | ||||
|         foreach (File file in files) { | ||||
|             directoryEntry = Path.GetDirectoryName(file.RelativePath) ?? throw new Exception(); | ||||
|             if (!directoryEntries.Contains(directoryEntry)) | ||||
|             if (!directoryEntries.Contains(directoryEntry)) { | ||||
|                 continue; | ||||
|             } | ||||
|             directoryEntries.Add(directoryEntry); | ||||
|             _ = zip.CreateEntry(file.RelativePath); | ||||
|         } | ||||
|         foreach (File file in files) | ||||
|             _ = zip.CreateEntryFromFile(Path.Combine(record.Directory, file.RelativePath), file.RelativePath); | ||||
|     } | ||||
|  | ||||
|     private static void WriteExtension(Record record, Job job, ReadOnlyCollection<File> files, string path) | ||||
|     { | ||||
|         string directoryName = Path.GetFileName(record.Directory); | ||||
|         if (job.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase)) | ||||
|             WriteISO(record, files, path, directoryName); | ||||
|         else if (job.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)) | ||||
|             WriteZIP(record, files, path); | ||||
|         else | ||||
|             throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     private static void PushTo(Job job, SecureShell secureShell, string path) | ||||
|     { | ||||
|         string remotePath = string.Concat(secureShell.Path, '/', Path.GetFileName(path)); | ||||
|         using SftpClient client = new(secureShell.Host, secureShell.User, new PrivateKeyFile(secureShell.Key)); | ||||
|         client.Connect(); | ||||
|         if (job.Files.Length == 0) | ||||
|         { | ||||
|             string directoryName = Path.GetDirectoryName(secureShell.Path) ?? throw new Exception(); | ||||
|             try | ||||
|             { client.CreateDirectory(Path.GetDirectoryName(directoryName) ?? throw new Exception()); } | ||||
|             catch (Exception) { } | ||||
|             try | ||||
|             { client.CreateDirectory(directoryName); } | ||||
|             catch (Exception) { } | ||||
|             try | ||||
|             { client.CreateDirectory(secureShell.Path); } | ||||
|             catch (Exception) { } | ||||
|         foreach (File file in files) { | ||||
|             _ = zip.CreateEntryFromFile(Path.Combine(record.SourceDirectory, file.RelativePath), file.RelativePath); | ||||
|         } | ||||
|         using FileStream fileStream = System.IO.File.OpenRead(path); | ||||
|         client.UploadFile(fileStream, remotePath); | ||||
|     } | ||||
|  | ||||
|     private static void PushTo(ServerMessageBlock serverMessageBlock, string path) | ||||
|     { | ||||
|         string remotePath = Path.Combine(serverMessageBlock.Path, Path.GetFileName(path)); | ||||
|         System.IO.File.Copy(path, remotePath); | ||||
|     } | ||||
|  | ||||
|     private static void PushTo(string directory, string path) | ||||
|     { | ||||
|         string remotePath = Path.Combine(directory, Path.GetFileName(path)); | ||||
|         System.IO.File.Copy(path, remotePath); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<Exception> PushTo(Job job, string path) | ||||
|     { | ||||
|         List<Exception> results = []; | ||||
|         foreach (Target target in job.Targets) | ||||
|         { | ||||
|             if (target.SecureShell is not null) | ||||
|             { | ||||
|                 try | ||||
|                 { PushTo(job, target.SecureShell, path); } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (target.SecureShell.Required) | ||||
|                         results.Add(ex); | ||||
|                 } | ||||
|             } | ||||
|             else if (target.ServerMessageBlock is not null) | ||||
|             { | ||||
|                 try | ||||
|                 { PushTo(target.ServerMessageBlock, path); } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (target.ServerMessageBlock.Required) | ||||
|                         results.Add(ex); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|                 throw new NotImplementedException(); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void DeleteOld(Job job, SecureShell secureShell, string path) | ||||
|     { | ||||
|         List<string> results = []; | ||||
|         using SftpClient client = new(secureShell.Host, secureShell.User, new PrivateKeyFile(secureShell.Key)); | ||||
|         client.Connect(); | ||||
|         foreach (ISftpFile file in client.ListDirectory(secureShell.Path)) | ||||
|         { | ||||
|             if (file.Name == path) | ||||
|                 continue; | ||||
|             if (!file.Name.EndsWith(job.Extension, StringComparison.OrdinalIgnoreCase)) | ||||
|                 continue; | ||||
|             results.Add(file.FullName); | ||||
|         } | ||||
|         for (int i = job.Keep - 1; i < results.Count; i++) | ||||
|             client.DeleteFile(results[i]); | ||||
|     } | ||||
|  | ||||
|     private static void DeleteOld(Job job, ServerMessageBlock serverMessageBlock, string path) | ||||
|     { | ||||
|         List<string> results = []; | ||||
|         string[] files = Directory.GetFiles(serverMessageBlock.Path, $"*{job.Extension}", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|             if (file == path) | ||||
|                 continue; | ||||
|             results.Add(file); | ||||
|         } | ||||
|         for (int i = job.Keep - 1; i < results.Count; i++) | ||||
|             System.IO.File.Delete(results[i]); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<Exception> DeleteOld(Job job, string path) | ||||
|     { | ||||
|         List<Exception> results = []; | ||||
|         foreach (Target target in job.Targets) | ||||
|         { | ||||
|             if (target.SecureShell is not null) | ||||
|             { | ||||
|                 try | ||||
|                 { DeleteOld(job, target.SecureShell, path); } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (target.SecureShell.Required) | ||||
|                         results.Add(ex); | ||||
|                 } | ||||
|             } | ||||
|             else if (target.ServerMessageBlock is not null) | ||||
|             { | ||||
|                 try | ||||
|                 { DeleteOld(job, target.ServerMessageBlock, path); } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     if (target.ServerMessageBlock.Required) | ||||
|                         results.Add(ex); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|                 throw new NotImplementedException(); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void Verify(string searchPattern, string[] ignoreFileNames) | ||||
|     { | ||||
|         List<Target> targets = [ | ||||
|             new(new SecureShell("free.file.sync.root", "C:/Users/phares/.ssh/id_ed25519", "\\home", true, "root"), 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); | ||||
|         ReadOnlyCollection<File> files = GetFiles(directory, searchPattern, ignoreFileNames); | ||||
|         double filesTotalLength = 0; | ||||
|         foreach (File file in files) | ||||
|             filesTotalLength += file.Length; | ||||
|         Job job = new( | ||||
|             "C:/Users/phares", | ||||
|             directory, | ||||
|             "*.iso", | ||||
|             files.ToArray(), | ||||
|             files.Count, | ||||
|             filesTotalLength, | ||||
|             3, | ||||
|             targets.ToArray()); | ||||
|         string json = JsonSerializer.Serialize(job, JobSourceGenerationContext.Default.Job); | ||||
|         System.IO.File.WriteAllText(Path.Combine(directory, "verify.json"), json); | ||||
|     } | ||||
|  | ||||
|     internal static void Backup(ILogger<Worker> logger, List<string> args) | ||||
|     { | ||||
|         string path; | ||||
|         string? json; | ||||
|         string directoryName; | ||||
|         ReadOnlyCollection<File> files; | ||||
|         string searchPattern = args[2]; | ||||
|         ReadOnlyCollection<Exception> exceptions; | ||||
|         string[] ignoreFileNames = args[3].Split('|'); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0]); | ||||
|         logger.LogInformation("Searching <{sourceDirectory}> with search pattern {searchPattern}", args[0], searchPattern); | ||||
|         if (Debugger.IsAttached) | ||||
|             Verify(searchPattern, ignoreFileNames); | ||||
|         IEnumerable<Record> records = GetRecords(sourceDirectory, searchPattern); | ||||
|         foreach (Record record in records) | ||||
|         { | ||||
|             if (record.Job is null || record.Job.Targets.Length == 0 || string.IsNullOrEmpty(record.Job.Extension)) | ||||
|                 continue; | ||||
|             logger.LogInformation("Searching <{directory}>", record.Directory); | ||||
|             files = GetFiles(searchPattern, ignoreFileNames, record); | ||||
|             json = GetJsonIfNotEqual(searchPattern, ignoreFileNames, record, record.Job, files); | ||||
|             if (string.IsNullOrEmpty(json)) | ||||
|                 continue; | ||||
|             directoryName = Path.GetFileName(record.Directory); | ||||
|             path = Path.Combine(record.Directory, $"{directoryName}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}{record.Job.Extension}"); | ||||
|             logger.LogInformation("Writing <{directory}> extension", record.Directory); | ||||
|             WriteExtension(record, record.Job, files, path); | ||||
|             logger.LogInformation("Pushing <{directory}> extension", record.Directory); | ||||
|             exceptions = PushTo(record.Job, path); | ||||
|             if (exceptions.Count != 0) | ||||
|             { | ||||
|                 foreach (Exception exception in exceptions) | ||||
|                     logger.LogError(exception, exception.Message); | ||||
|                 PushTo(record.Job.AlternatePath, path); | ||||
|             } | ||||
|             System.IO.File.WriteAllText(record.Path, json); | ||||
|             System.IO.File.Delete(path); | ||||
|             logger.LogInformation("Deleting old <{directory}> extension", record.Directory); | ||||
|             exceptions = DeleteOld(record.Job, path); | ||||
|             if (exceptions.Count != 0) | ||||
|             { | ||||
|                 foreach (Exception exception in exceptions) | ||||
|                     logger.LogError(exception, exception.Message); | ||||
|             } | ||||
|         } | ||||
|         if (Debugger.IsAttached && records.Count() == 0) | ||||
|         { | ||||
|             files = GetFiles(sourceDirectory, searchPattern, ignoreFileNames); | ||||
|             json = JsonSerializer.Serialize(files.ToArray(), FilesSourceGenerationContext.Default.FileArray); | ||||
|             System.IO.File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json); | ||||
|     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"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -9,160 +9,13 @@ using System.Text.Json; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2024.PI4; | ||||
|  | ||||
| internal static partial class Helper20241224 | ||||
| { | ||||
| 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(); | ||||
|             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) | ||||
|     { | ||||
|     internal static void Compare(ILogger<Worker> logger, List<string> args) { | ||||
|         string host = args[2]; | ||||
|         string rootDirectoryName = args[3]; | ||||
|         string format = NginxFileSystem.GetFormat(); | ||||
| @ -173,8 +26,7 @@ internal static partial class Helper20241224 | ||||
| #if ShellProgressBar | ||||
|         ProgressBar progressBar = new(records.Count, "Downloading", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); | ||||
| #endif | ||||
|         foreach (Record record in records) | ||||
|         { | ||||
|         foreach (Record record in records) { | ||||
| #if ShellProgressBar | ||||
|             progressBar.Tick(); | ||||
| #endif | ||||
| @ -183,13 +35,146 @@ internal static partial class Helper20241224 | ||||
| #if ShellProgressBar | ||||
|         progressBar.Dispose(); | ||||
| #endif | ||||
|         if (Debugger.IsAttached) | ||||
|         { | ||||
|         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)) | ||||
|             if (!string.IsNullOrEmpty(json)) { | ||||
|                 File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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 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 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										296
									
								
								ADO2025/PI4/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2025/PI4/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
| @ -3,52 +3,9 @@ using System.Collections.ObjectModel; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI4; | ||||
|  | ||||
| internal static partial class Helper20250101 | ||||
| { | ||||
| 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) | ||||
|     { | ||||
|     internal static void MoveToDelete(ILogger<Worker> logger, List<string> args) { | ||||
|         string split = args[3]; | ||||
|         string appendage = args[4]; | ||||
|         string searchPattern = args[2]; | ||||
| @ -58,4 +15,42 @@ internal static partial class Helper20250101 | ||||
|         MoveToDelete(logger, appendage, keyValuePairs); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -2,53 +2,50 @@ using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI4; | ||||
|  | ||||
| internal static partial class Helper20250114 | ||||
| { | ||||
| internal static partial class Helper20250114 { | ||||
|  | ||||
|     private static void Rename(string[] directories, string 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void Rename(string[] directories, string dateFormat) { | ||||
|         string[] files; | ||||
|         DateTime dateTime; | ||||
|         FileInfo fileInfo; | ||||
|         string checkDirectory; | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|         foreach (string directory in directories) { | ||||
|             dateTime = DateTime.MinValue; | ||||
|             files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); | ||||
|             foreach (string file in files) | ||||
|             { | ||||
|             foreach (string file in files) { | ||||
|                 fileInfo = new(file); | ||||
|                 if (dateTime > fileInfo.LastWriteTime) | ||||
|                 if (dateTime > fileInfo.LastWriteTime) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 dateTime = fileInfo.LastWriteTime; | ||||
|             } | ||||
|             if (dateTime == DateTime.MinValue) | ||||
|             if (dateTime == DateTime.MinValue) { | ||||
|                 continue; | ||||
|             } | ||||
|             checkDirectory = Path.Combine(Path.GetDirectoryName(directory) ?? throw new Exception(), dateTime.ToString(dateFormat)); | ||||
|             if (checkDirectory != directory) | ||||
|             { | ||||
|                 if (Directory.Exists(checkDirectory)) | ||||
|             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) | ||||
|     { | ||||
|     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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										90
									
								
								ADO2025/PI4/Helper-2025-01-26.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								ADO2025/PI4/Helper-2025-01-26.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Globalization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI4; | ||||
|  | ||||
| internal static partial class Helper20250126 { | ||||
|  | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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(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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										301
									
								
								ADO2025/PI4/Helper-2025-02-04.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								ADO2025/PI4/Helper-2025-02-04.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | ||||
| 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 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 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)); | ||||
|  | ||||
|     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 ? "J:/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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     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 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 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 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 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; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										296
									
								
								ADO2025/PI5/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2025/PI5/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
							
								
								
									
										87
									
								
								ADO2025/PI5/Helper-2025-02-18.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								ADO2025/PI5/Helper-2025-02-18.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Globalization; | ||||
|  | ||||
| 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 | ||||
							
								
								
									
										389
									
								
								ADO2025/PI5/Helper-2025-02-19.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								ADO2025/PI5/Helper-2025-02-19.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,389 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										119
									
								
								ADO2025/PI5/Helper-2025-02-28.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								ADO2025/PI5/Helper-2025-02-28.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
|  | ||||
| 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}]"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										59
									
								
								ADO2025/PI5/Helper-2025-03-01.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ADO2025/PI5/Helper-2025-03-01.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
|  | ||||
| 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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										143
									
								
								ADO2025/PI5/Helper-2025-03-05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								ADO2025/PI5/Helper-2025-03-05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
|  | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										89
									
								
								ADO2025/PI5/Helper-2025-03-06.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								ADO2025/PI5/Helper-2025-03-06.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| 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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										48
									
								
								ADO2025/PI5/Helper-2025-03-15.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								ADO2025/PI5/Helper-2025-03-15.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| 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); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										567
									
								
								ADO2025/PI5/Helper-2025-03-20.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								ADO2025/PI5/Helper-2025-03-20.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,567 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Diagnostics; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										142
									
								
								ADO2025/PI5/Helper-2025-03-21.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								ADO2025/PI5/Helper-2025-03-21.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | ||||
| using File_Folder_Helper.Helpers; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
|  | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										242
									
								
								ADO2025/PI5/Helper-2025-04-04.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								ADO2025/PI5/Helper-2025-04-04.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,242 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| 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)); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										740
									
								
								ADO2025/PI5/Helper-2025-04-07.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										740
									
								
								ADO2025/PI5/Helper-2025-04-07.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,740 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.FileSystemGlobbing; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| #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; | ||||
|         string url; | ||||
|         Task<string> response; | ||||
|         HttpClient httpClient = new(); | ||||
|         Task<HttpResponseMessage> httpResponseMessage; | ||||
|         if (rootUniformResourceLocator.EndsWith('/')) { | ||||
|             url = new($"{rootUniformResourceLocator[..^1]}/{fileName}"); | ||||
|         } else { | ||||
|             url = new($"{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(); | ||||
|                     if (string.IsNullOrEmpty(response.Result)) { | ||||
|                         result = null; | ||||
|                     } else { | ||||
|                         result = 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 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; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										53
									
								
								ADO2025/PI5/Helper-2025-04-21.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								ADO2025/PI5/Helper-2025-04-21.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								ADO2025/PI5/Helper-2025-04-29.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								ADO2025/PI5/Helper-2025-04-29.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
|  | ||||
| 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 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); | ||||
|         } | ||||
|     } | ||||
|     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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										87
									
								
								ADO2025/PI5/Helper-2025-05-05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								ADO2025/PI5/Helper-2025-05-05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| #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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										296
									
								
								ADO2025/PI6/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2025/PI6/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
							
								
								
									
										245
									
								
								ADO2025/PI6/Envelope.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								ADO2025/PI6/Envelope.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,245 @@ | ||||
| #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 => headerField; | ||||
|         set => headerField = value; | ||||
|     } | ||||
|  | ||||
|     /// <remarks/> | ||||
|     public EnvelopeBody Body { | ||||
|         get => bodyField; | ||||
|         set => 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 => sequenceField; | ||||
|         set => sequenceField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlElement(Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm")] | ||||
|     public SequenceAcknowledgement SequenceAcknowledgement { | ||||
|         get => sequenceAcknowledgementField; | ||||
|         set => sequenceAcknowledgementField = value; | ||||
|     } | ||||
|  | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlElement(Namespace = "http://www.w3.org/2005/08/addressing")] | ||||
|     public Action Action { | ||||
|         get => actionField; | ||||
|         set => actionField = value; | ||||
|     } | ||||
|  | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlElement(Namespace = "http://www.w3.org/2005/08/addressing")] | ||||
|     public string RelatesTo { | ||||
|         get => relatesToField; | ||||
|         set => 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 => identifierField; | ||||
|         set => identifierField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     public byte MessageNumber { | ||||
|         get => messageNumberField; | ||||
|         set => messageNumberField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     public object LastMessage { | ||||
|         get => lastMessageField; | ||||
|         set => 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 => mustUnderstandField; | ||||
|         set => 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 => identifierField; | ||||
|         set => identifierField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     public SequenceAcknowledgementAcknowledgementRange AcknowledgementRange { | ||||
|         get => acknowledgementRangeField; | ||||
|         set => acknowledgementRangeField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlElement(Namespace = "http://schemas.microsoft.com/ws/2006/05/rm")] | ||||
|     public byte BufferRemaining { | ||||
|         get => bufferRemainingField; | ||||
|         set => 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 => lowerField; | ||||
|         set => lowerField = value; | ||||
|     } | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlAttribute()] | ||||
|     public byte Upper { | ||||
|         get => upperField; | ||||
|         set => 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 => mustUnderstandField; | ||||
|         set => mustUnderstandField = value; | ||||
|     } | ||||
|  | ||||
|     /// <remarks/> | ||||
|     [System.Xml.Serialization.XmlText()] | ||||
|     public string Value { | ||||
|         get => valueField; | ||||
|         set => 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 => createSequenceResponseField; | ||||
|         set => 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 => identifierField; | ||||
|         set => identifierField = value; | ||||
|     } | ||||
|  | ||||
|     /// <remarks/> | ||||
|     public CreateSequenceResponseAccept Accept { | ||||
|         get => acceptField; | ||||
|         set => 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 => acksToField; | ||||
|         set => 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 => addressField; | ||||
|         set => addressField = value; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										536
									
								
								ADO2025/PI6/Helper-2025-05-19.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										536
									
								
								ADO2025/PI6/Helper-2025-05-19.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,536 @@ | ||||
| using Microsoft.Extensions.FileSystemGlobbing; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using System.Web; | ||||
|  | ||||
| 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; | ||||
|         if (string.IsNullOrEmpty(json)) { | ||||
|             input = Input.Get(args); | ||||
|         } else { | ||||
|             input = 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() { | ||||
|                     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 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 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) { | ||||
|             if (string.IsNullOrEmpty(directory)) { | ||||
|                 continue; | ||||
|             } | ||||
|             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 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 || string.IsNullOrEmpty(directory)) { | ||||
|                 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() { | ||||
|                     { new ByteArrayContent(File.ReadAllBytes(verb.File)), "formFiles", verb.Multipart }, | ||||
|                     { 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 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										189
									
								
								ADO2025/PI6/Helper-2025-05-21.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								ADO2025/PI6/Helper-2025-05-21.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,189 @@ | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Globalization; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| 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 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) { | ||||
|         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 datePattern = args[5]; | ||||
|         string searchPattern = args[2]; | ||||
|         string searchPatternB = args[3]; | ||||
|         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); | ||||
|         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) ?? throw new Exception()); | ||||
|         } | ||||
|         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) { | ||||
|         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.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, DateTime transactionDate, DateTime effectiveDate) { | ||||
|         RecordB? result = null; | ||||
|         string line; | ||||
|         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); | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										391
									
								
								ADO2025/PI6/Helper-2025-06-01.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								ADO2025/PI6/Helper-2025-06-01.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,391 @@ | ||||
|  | ||||
| using IFX.Shared.PasteSpecialXml.EAF.XML.API.Envelope; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Globalization; | ||||
| using System.Net; | ||||
| using System.Text; | ||||
| using System.Xml; | ||||
| using System.Xml.Serialization; | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     internal static void EquipmentAutomationFrameworkStatus(ILogger<Worker> logger, List<string> args) { | ||||
|         Status status; | ||||
|         Record? record; | ||||
|         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 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 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 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 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 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 Stream ToStream(string value) { | ||||
|         MemoryStream memoryStream = new(); | ||||
|         StreamWriter streamWriter = new(memoryStream); | ||||
|         streamWriter.Write(value); | ||||
|         streamWriter.Flush(); | ||||
|         memoryStream.Position = 0; | ||||
|         return memoryStream; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										544
									
								
								ADO2025/PI6/Helper-2025-06-02.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										544
									
								
								ADO2025/PI6/Helper-2025-06-02.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,544 @@ | ||||
|  | ||||
| using IFX.Shared.PasteSpecialXml.EAF.XML.API.Envelope; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Globalization; | ||||
| using System.Net; | ||||
| using System.Text; | ||||
| using System.Xml; | ||||
| using System.Xml.Serialization; | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     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 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 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 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 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 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 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 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 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); | ||||
|     } | ||||
|  | ||||
|     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 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 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										90
									
								
								ADO2025/PI6/Helper-2025-06-18.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								ADO2025/PI6/Helper-2025-06-18.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
|  | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										86
									
								
								ADO2025/PI6/Helper-2025-06-28.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								ADO2025/PI6/Helper-2025-06-28.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| using DiscUtils.Iso9660; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
|  | ||||
| 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; | ||||
|         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 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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										430
									
								
								ADO2025/PI6/Helper-2025-07-01.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								ADO2025/PI6/Helper-2025-07-01.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,430 @@ | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| 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; | ||||
|         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 (string.IsNullOrEmpty(directoryName)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 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(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.Value); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     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, 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 { | ||||
|                 results.Add($"## Footer{Environment.NewLine}"); | ||||
|                 results.Insert(0, $"# {name}{Environment.NewLine}"); | ||||
|                 for (int i = columnTitlesLine + 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 ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetKeyValuePairs(int columnTitlesLine, string[] lines) { | ||||
|         Dictionary<string, ReadOnlyCollection<string>> results = []; | ||||
|         string[] segments; | ||||
|         List<List<string>> collections = []; | ||||
|         string[] columns = lines[columnTitlesLine].Split('\t'); | ||||
|         foreach (string _ 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? 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 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 string? GetPipeTable(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()); | ||||
|             } | ||||
|             if (columns is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										114
									
								
								ADO2025/PI6/Helper-2025-07-05.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								ADO2025/PI6/Helper-2025-07-05.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Drawing; | ||||
|  | ||||
| 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) { | ||||
| #if SystemDrawingCommon | ||||
|         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}"); | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										233
									
								
								ADO2025/PI6/Helper-2025-07-09.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								ADO2025/PI6/Helper-2025-07-09.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,233 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| 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 extension = args[4]; | ||||
|         string searchPattern = args[2]; | ||||
|         int sizeFilter = int.Parse(args[3]); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); | ||||
|         string destinationDirectory = Path.GetFullPath(args[6].Split('~')[0]); | ||||
|         string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         JavaScriptObjectNotationTo(sourceDirectory, searchPattern, sizeFilter, extension, destinationDirectory, directories.AsReadOnly()); | ||||
|         Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); | ||||
|     } | ||||
|  | ||||
|     private static void JavaScriptObjectNotationTo(string sourceDirectory, string searchPattern, int sizeFilter, string extension, string destinationDirectory, ReadOnlyCollection<string> directories) { | ||||
|         Raw? raw; | ||||
|         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 (string.IsNullOrEmpty(directoryName)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 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; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										98
									
								
								ADO2025/PI6/Helper-2025-07-10.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								ADO2025/PI6/Helper-2025-07-10.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| 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 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, searchPattern, directories.AsReadOnly()); | ||||
|         Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); | ||||
|     } | ||||
|  | ||||
|     private static void LogToTrace(ILogger<Worker> logger, string searchPattern, ReadOnlyCollection<string> directories) { | ||||
|         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); | ||||
|                 if (string.IsNullOrEmpty(directoryName)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 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; | ||||
|         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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										441
									
								
								ADO2025/PI6/Helper-2025-07-20.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								ADO2025/PI6/Helper-2025-07-20.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,441 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Phares.Metadata.Models.Stateless; | ||||
| using Phares.Shared.Models; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Diagnostics; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI6; | ||||
|  | ||||
| internal static partial class Helper20250720 { | ||||
|  | ||||
|     private record Helper20250720Settings(ResultSettings? ResultSettings, MetadataSettings? MetadataSettings); | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Helper20250720Settings))] | ||||
|     private partial class Helper20250720SettingsSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<ExifDirectory> exifDirectories, ReadOnlyCollection<string> digiKamFiles) { | ||||
|         ReadOnlyDictionary<string, FaceFile> keyValuePairs = GetKeyValuePairs(logger, exifDirectories); | ||||
|         if (keyValuePairs.Count > 0) { | ||||
|             WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, digiKamFiles, keyValuePairs); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<string> digiKamFiles, ReadOnlyDictionary<string, FaceFile> keyValuePairs) { | ||||
| #if xmp | ||||
|         IXmpMeta xmp; | ||||
|         using FileStream stream = File.OpenRead(digiKamFile); | ||||
|         xmp = XmpMetaFactory.Parse(stream); | ||||
|         foreach (var property in xmp.Properties) { | ||||
|             logger.LogDebug("Path={property.Path} Namespace={property.Namespace} Value={property.Value}", property.Path, property.Namespace, property.Value); | ||||
|         } | ||||
|         xmp.Sort(); | ||||
|         SerializeOptions serializeOptions = new(SerializeOptions.EncodeUtf8); | ||||
|         string check = XmpMetaFactory.SerializeToString(xmp, serializeOptions); | ||||
|         File.WriteAllText(".xmp", check); | ||||
| #endif | ||||
|         string[] requiredLines = [ | ||||
|             "xmlns:digiKam=\"http://www.digikam.org/ns/1.0/\"", | ||||
|             "xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"", | ||||
|             "xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"", | ||||
|             "xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"", | ||||
|             "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"", | ||||
|             "xmlns:acdsee=\"http://ns.acdsee.com/iptc/1.0/\"", | ||||
|             "xmlns:lr=\"http://ns.adobe.com/lightroom/1.0/\"", | ||||
|             "xmlns:MP=\"http://ns.microsoft.com/photo/1.2/\"", | ||||
|             "xmlns:stArea=\"http://ns.adobe.com/xmp/sType/Area#\"", | ||||
|             "xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\"", | ||||
|             "xmlns:MicrosoftPhoto=\"http://ns.microsoft.com/photo/1.0/\"", | ||||
|             "xmlns:MPReg=\"http://ns.microsoft.com/photo/1.2/t/Region#\"", | ||||
|             "xmlns:stDim=\"http://ns.adobe.com/xap/1.0/sType/Dimensions#\"", | ||||
|             "xmlns:MPRI=\"http://ns.microsoft.com/photo/1.2/t/RegionInfo#\"", | ||||
|             "xmlns:mediapro=\"http://ns.iview-multimedia.com/mediapro/1.0/\"", | ||||
|             "xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\"", | ||||
|             "</rdf:RDF>" | ||||
|         ]; | ||||
|         foreach (string digiKamFile in digiKamFiles) { | ||||
|             string[] lines = File.ReadAllLines(digiKamFile); | ||||
|             List<string> trimmed = lines.Select(l => l.Trim()).ToList(); | ||||
|             int? digiKamLine = GetMatchingLine(requiredLines[0], trimmed.AsReadOnly()); | ||||
|             if (digiKamLine is null) { | ||||
|                 logger.LogError("{fileNameFirstSegment}) Didn't fine digiKam line!", fileNameFirstSegment); | ||||
|             } else { | ||||
|                 foreach (string requiredLine in requiredLines) { | ||||
|                     if (!trimmed.Contains(requiredLine)) { | ||||
|                         trimmed.Insert(digiKamLine.Value + 1, requiredLine); | ||||
|                     } | ||||
|                 } | ||||
|                 int? rdfLine = GetMatchingLine(requiredLines[^1], trimmed.AsReadOnly()); | ||||
|                 if (rdfLine is null) { | ||||
|                     logger.LogError("{fileNameFirstSegment}) Didn't fine description line!", fileNameFirstSegment); | ||||
|                 } else { | ||||
|                     WriteFaceData(outputDirectoryName, originalFile, keyValuePairs, digiKamFile, trimmed, rdfLine.Value); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static void WriteFaceData(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[6]); | ||||
|         logger.LogInformation(args[7]); | ||||
|         logger.LogInformation(args[8]); | ||||
|         string searchPattern = args[5]; | ||||
|         string searchPatternXMP = args[7]; | ||||
|         string outputDirectoryName = args[8]; | ||||
|         string jsonFile = Path.GetFullPath(args[3]); | ||||
|         string digiKamFile = Path.GetFullPath(args[6]); | ||||
|         FileInfo faceFileInfo = new(Path.GetFullPath(args[4])); | ||||
|         string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); | ||||
|         string pathRoot = Path.GetPathRoot(faceFileInfo.FullName) ?? throw new Exception(); | ||||
|         string? checkDirectory = Path.GetDirectoryName(digiKamFile.Replace("{}", outputDirectoryName)); | ||||
|         string originalFile = Path.Combine(sourceDirectory, args[2]); | ||||
|         string pathRootXMP = digiKamFile[..5]; | ||||
|         if (!File.Exists(jsonFile)) { | ||||
|             logger.LogError("json file doesn't exist! <{jsonFile}>", jsonFile); | ||||
|         } else if (!File.Exists(digiKamFile)) { | ||||
|             logger.LogError("digiKam file doesn't exist! <{digiKamFile}>", digiKamFile); | ||||
|         } else if (!File.Exists(originalFile)) { | ||||
|             logger.LogError("Original file doesn't exist! <{checkFile}>", originalFile); | ||||
|         } else if (!Directory.Exists(checkDirectory)) { | ||||
|             logger.LogError("checkDirectory doesn't exist! <{checkDirectory}>", checkDirectory); | ||||
|         } else if (!faceFileInfo.Exists) { | ||||
|             logger.LogError("Face file doesn't exist! <{faceFileInfo}>", faceFileInfo.FullName); | ||||
|         } else { | ||||
|             string json = File.ReadAllText(jsonFile); | ||||
|             Helper20250720Settings? settings = JsonSerializer.Deserialize(json, Helper20250720SettingsSourceGenerationContext.Default.Helper20250720Settings); | ||||
|             if (settings?.ResultSettings is null || settings.ResultSettings.ResultAllInOneSubdirectoryLength < 1 || settings.MetadataSettings is null) { | ||||
|                 throw new Exception(nameof(Helper20250720Settings)); | ||||
|             } | ||||
|             WriteFaceData(logger, outputDirectoryName, originalFile, faceFileInfo, digiKamFile, settings.ResultSettings, settings.MetadataSettings); | ||||
|             ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP = GetKeyValuePairs(searchPatternXMP, pathRootXMP); | ||||
|             ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs = GetKeyValuePairs(searchPattern, pathRoot, settings.ResultSettings, settings.MetadataSettings); | ||||
|             if (keyValuePairs.Count == 0) { | ||||
|                 logger.LogError("Didn't find any valid file(s)!"); | ||||
|             } else { | ||||
|                 WriteFaceData(logger, outputDirectoryName, keyValuePairs, keyValuePairsXMP); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string originalFile, FileInfo faceFileInfo, string digiKamFile, ResultSettings resultSettings, MetadataSettings metadataSettings) { | ||||
|         ExifDirectory? exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo); | ||||
|         if (exifDirectory is null) { | ||||
|             logger.LogError("exifDirectory is null!"); | ||||
|         } else { | ||||
|             long id; | ||||
|             string fileNameWithoutExtension; | ||||
|             fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName); | ||||
|             if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out id)) { | ||||
|                 id = -1; | ||||
|             } | ||||
|             long? fileNameFirstSegment = id == -1 ? null : id; | ||||
|             ReadOnlyCollection<string> digiKamFiles = new([digiKamFile]); | ||||
|             ReadOnlyCollection<ExifDirectory> exifDirectories = new([exifDirectory]); | ||||
|             WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, exifDirectories, digiKamFiles); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void WriteFaceData(string outputDirectoryName, string? originalFile, ReadOnlyDictionary<string, FaceFile> keyValuePairs, string digiKamFile, List<string> trimmed, int rdfLine) { | ||||
|         if (keyValuePairs.Count == 0) { | ||||
|             throw new Exception(); | ||||
|         } | ||||
|         double h; | ||||
|         double w; | ||||
|         double mpTop; | ||||
|         double width; | ||||
|         double height; | ||||
|         double mpLeft; | ||||
|         double mwgTop; | ||||
|         double mwgLeft; | ||||
|         string personKey; | ||||
|         FaceFile faceFile; | ||||
|         string descriptionLine = "</rdf:Description>"; | ||||
|         faceFile = keyValuePairs.ElementAt(0).Value; | ||||
|         if (faceFile.Location is null || faceFile.OutputResolution is null) { | ||||
|             throw new Exception(); | ||||
|         } | ||||
|         List<string> regionLines = [ | ||||
|             "<MP:RegionInfo rdf:parseType=\"Resource\">", | ||||
|             "<MPRI:Regions>", | ||||
|             "<rdf:Bag>" | ||||
|         ]; | ||||
|         List<string> regionsLinesB = [ | ||||
|             "<mwg-rs:Regions rdf:parseType=\"Resource\">", | ||||
|             "<mwg-rs:AppliedToDimensions", | ||||
|             $"stDim:w=\"{faceFile.OutputResolution.Width}\"", | ||||
|             $"stDim:h=\"{faceFile.OutputResolution.Height}\"", | ||||
|             "stDim:unit=\"pixel\"/>", | ||||
|             "<mwg-rs:RegionList>", | ||||
|             "<rdf:Bag>" | ||||
|         ]; | ||||
|         List<string> digiKamLines = ["<digiKam:TagsList>", "<rdf:Seq>"]; | ||||
|         List<string> microsoftPhotoLines = ["<MicrosoftPhoto:LastKeywordXMP>", "<rdf:Bag>"]; | ||||
|         List<string> hierarchicalSubjectLines = ["<lr:hierarchicalSubject>", "<rdf:Bag>"]; | ||||
|         List<string> catalogSetLines = ["<mediapro:CatalogSets>", "<rdf:Bag>"]; | ||||
|         List<string> subjectLines = ["<dc:subject>", "<rdf:Bag>"]; | ||||
|         foreach (KeyValuePair<string, FaceFile> keyValuePair in keyValuePairs) { | ||||
|             personKey = keyValuePair.Key; | ||||
|             faceFile = keyValuePair.Value; | ||||
|             if (faceFile.Location is null || faceFile.OutputResolution is null) { | ||||
|                 throw new Exception(); | ||||
|             } | ||||
|             width = faceFile.Location.Right - faceFile.Location.Left; | ||||
|             height = faceFile.Location.Bottom - faceFile.Location.Top; | ||||
|             if (!string.IsNullOrEmpty(originalFile) && File.Exists(originalFile)) { | ||||
|                 Extract(file: originalFile, | ||||
|                         width: width, | ||||
|                         height: height, | ||||
|                         left: faceFile.Location.Left, | ||||
|                         top: faceFile.Location.Top, | ||||
|                         suffix: $"-{keyValuePair.Key}-whole-percentages.jpg"); | ||||
|             } | ||||
|             w = width / faceFile.OutputResolution.Width; | ||||
|             h = height / faceFile.OutputResolution.Height; | ||||
|             if (w == 0 || h == 0) { | ||||
|                 throw new NotImplementedException(); | ||||
|             } | ||||
|             mpLeft = (double)faceFile.Location.Left / faceFile.OutputResolution.Width; | ||||
|             mpTop = (double)faceFile.Location.Top / faceFile.OutputResolution.Height; | ||||
|             mwgLeft = (faceFile.Location.Left + (width * .5)) / faceFile.OutputResolution.Width; | ||||
|             mwgTop = (faceFile.Location.Top + (height * .5)) / faceFile.OutputResolution.Height; | ||||
|             // <MP:RegionInfo rdf:parseType="Resource"> | ||||
|             //     <MPRI:Regions> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li | ||||
|             //     MPReg:PersonDisplayName="{personKey}" | ||||
|             //     MPReg:Rectangle="{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}"/> | ||||
|             //     </rdf:Bag> | ||||
|             //     </MPRI:Regions> | ||||
|             // </MP:RegionInfo> | ||||
|             regionLines.Add("<rdf:li"); | ||||
|             regionLines.Add($"MPReg:PersonDisplayName=\"{personKey}\""); | ||||
|             regionLines.Add($"MPReg:Rectangle=\"{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}\"/>"); | ||||
|             // <mwg-rs:Regions rdf:parseType="Resource"> | ||||
|             //     <mwg-rs:AppliedToDimensions | ||||
|             //     stDim:w="{faceFile.OutputResolution.Width}" | ||||
|             //     stDim:h="{faceFile.OutputResolution.Height}" | ||||
|             //     stDim:unit="pixel"/> | ||||
|             //     <mwg-rs:RegionList> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li> | ||||
|             //     <rdf:Description | ||||
|             //         mwg-rs:Name="{personKey}" | ||||
|             //         mwg-rs:Type="Face"> | ||||
|             //     <mwg-rs:Area | ||||
|             //         stArea:x="{mwgLeft:0.000000}" | ||||
|             //         stArea:y="{mwgTop:0.000000}" | ||||
|             //         stArea:w="{w:0.000000}" | ||||
|             //         stArea:h="{h:0.000000}" | ||||
|             //         stArea:unit="normalized"/> | ||||
|             //     </rdf:Description> | ||||
|             //     </rdf:li> | ||||
|             //     </rdf:Bag> | ||||
|             //     </mwg-rs:RegionList> | ||||
|             // </mwg-rs:Regions> | ||||
|             regionsLinesB.Add("<rdf:li>"); | ||||
|             regionsLinesB.Add("<rdf:Description"); | ||||
|             regionsLinesB.Add($"mwg-rs:Name=\"{personKey}\""); | ||||
|             regionsLinesB.Add("mwg-rs:Type=\"Face\">"); | ||||
|             regionsLinesB.Add("<mwg-rs:Area"); | ||||
|             regionsLinesB.Add($"stArea:x=\"{mwgLeft:0.000000}\""); | ||||
|             regionsLinesB.Add($"stArea:y=\"{mwgTop:0.000000}\""); | ||||
|             regionsLinesB.Add($"stArea:w=\"{w:0.000000}\""); | ||||
|             regionsLinesB.Add($"stArea:h=\"{h:0.000000}\""); | ||||
|             regionsLinesB.Add("stArea:unit=\"normalized\"/>"); | ||||
|             regionsLinesB.Add("</rdf:Description>"); | ||||
|             regionsLinesB.Add("</rdf:li>"); | ||||
|             // <digiKam:TagsList> | ||||
|             //     <rdf:Seq> | ||||
|             //     <rdf:li>People/{personKey}</rdf:li> | ||||
|             //     </rdf:Seq> | ||||
|             // </digiKam:TagsList> | ||||
|             digiKamLines.Add($"<rdf:li>People/{personKey}</rdf:li>"); | ||||
|             // <MicrosoftPhoto:LastKeywordXMP> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li>People/{personKey}</rdf:li> | ||||
|             //     </rdf:Bag> | ||||
|             // </MicrosoftPhoto:LastKeywordXMP> | ||||
|             microsoftPhotoLines.Add($"<rdf:li>People/{personKey}</rdf:li>"); | ||||
|             // <lr:hierarchicalSubject> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li>People|{personKey}</rdf:li> | ||||
|             //     </rdf:Bag> | ||||
|             // </lr:hierarchicalSubject> | ||||
|             hierarchicalSubjectLines.Add($"<rdf:li>People|{personKey}</rdf:li>"); | ||||
|             // <mediapro:CatalogSets> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li>People|{personKey}</rdf:li> | ||||
|             //     </rdf:Bag> | ||||
|             // </mediapro:CatalogSets> | ||||
|             catalogSetLines.Add($"<rdf:li>People|{personKey}</rdf:li>"); | ||||
|             // <dc:subject> | ||||
|             //     <rdf:Bag> | ||||
|             //     <rdf:li>{personKey}</rdf:li> | ||||
|             //     </rdf:Bag> | ||||
|             // </dc:subject> | ||||
|             subjectLines.Add($"<rdf:li>{personKey}</rdf:li>"); | ||||
|         } | ||||
|         regionLines.AddRange(["</rdf:Bag>", "</MPRI:Regions>", "</MP:RegionInfo>"]); | ||||
|         regionsLinesB.AddRange(["</rdf:Bag>", "</mwg-rs:RegionList>", "</mwg-rs:Regions>"]); | ||||
|         digiKamLines.AddRange(["</rdf:Seq>", "</digiKam:TagsList>"]); | ||||
|         microsoftPhotoLines.AddRange(["</rdf:Bag>", "</MicrosoftPhoto:LastKeywordXMP>"]); | ||||
|         hierarchicalSubjectLines.AddRange(["</rdf:Bag>", "</lr:hierarchicalSubject>"]); | ||||
|         catalogSetLines.AddRange(["</rdf:Bag>", "</mediapro:CatalogSets>"]); | ||||
|         subjectLines.AddRange(["</rdf:Bag>", "</dc:subject>"]); | ||||
|         List<string> lines = []; | ||||
|         lines.AddRange(regionLines); | ||||
|         lines.AddRange(regionsLinesB); | ||||
|         lines.AddRange(digiKamLines); | ||||
|         lines.AddRange(microsoftPhotoLines); | ||||
|         lines.AddRange(hierarchicalSubjectLines); | ||||
|         lines.AddRange(catalogSetLines); | ||||
|         lines.AddRange(subjectLines); | ||||
|         string text = string.Join(Environment.NewLine, lines); | ||||
|         if (trimmed[rdfLine - 1] != descriptionLine) { | ||||
|             trimmed[rdfLine - 1] = $"{trimmed[rdfLine - 1][..^2]}>"; | ||||
|             trimmed.Insert(rdfLine, descriptionLine); | ||||
|             rdfLine++; | ||||
|         } | ||||
|         trimmed.Insert(rdfLine - 1, text); | ||||
|         string allText = string.Join(Environment.NewLine, trimmed); | ||||
|         File.WriteAllText(digiKamFile.Replace("{}", outputDirectoryName), allText); | ||||
|         if (!string.IsNullOrEmpty(originalFile)) { | ||||
|             if (Debugger.IsAttached) { | ||||
|                 File.WriteAllText(".xml", allText); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<long, ReadOnlyCollection<string>> GetKeyValuePairs(string searchPattern, string pathRoot) { | ||||
|         Dictionary<long, ReadOnlyCollection<string>> results = []; | ||||
|         long fileNameFirstSegment; | ||||
|         List<string>? collection; | ||||
|         string fileNameWithoutExtension; | ||||
|         Dictionary<long, List<string>> keyValuePairs = []; | ||||
|         string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories); | ||||
|         foreach (string file in files) { | ||||
|             fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); | ||||
|             if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) { | ||||
|                 keyValuePairs.Add(fileNameFirstSegment, []); | ||||
|                 if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) { | ||||
|                     throw new Exception(); | ||||
|                 } | ||||
|             } | ||||
|             collection.Add(file); | ||||
|         } | ||||
|         foreach (KeyValuePair<long, List<string>> keyValuePair in keyValuePairs) { | ||||
|             results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<string, FaceFile> GetKeyValuePairs(ILogger<Worker> logger, ReadOnlyCollection<ExifDirectory> exifDirectories) { | ||||
|         Dictionary<string, FaceFile> results = []; | ||||
|         string? personKey; | ||||
|         FaceFile? faceFile; | ||||
|         foreach (ExifDirectory exifDirectory in exifDirectories) { | ||||
|             faceFile = IMetadata.GetFaceFile(exifDirectory); | ||||
|             if (faceFile is null) { | ||||
|                 logger.LogError("faceFile is null!"); | ||||
|             } else if (faceFile.Location is null) { | ||||
|                 logger.LogError("faceFile location is null!"); | ||||
|             } else if (faceFile.OutputResolution?.Orientation is not 0 and not 1) { | ||||
|                 logger.LogWarning("faceFile output-resolution orientation is not aloud!"); | ||||
|             } else { | ||||
|                 personKey = Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(exifDirectory.FilePath.FullName)))); | ||||
|                 if (string.IsNullOrEmpty(personKey)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (results.ContainsKey(personKey)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 results.Add(personKey, faceFile); | ||||
|             } | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> GetKeyValuePairs(string searchPattern, string pathRoot, ResultSettings resultSettings, MetadataSettings metadataSettings) { | ||||
|         Dictionary<long, ReadOnlyCollection<ExifDirectory>> results = []; | ||||
|         FileInfo faceFileInfo; | ||||
|         long fileNameFirstSegment; | ||||
|         ExifDirectory? exifDirectory; | ||||
|         List<ExifDirectory>? collection; | ||||
|         string fileNameWithoutExtension; | ||||
|         Dictionary<long, List<ExifDirectory>> keyValuePairs = []; | ||||
|         string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories); | ||||
|         foreach (string file in files) { | ||||
|             faceFileInfo = new(file); | ||||
|             fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName); | ||||
|             if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) { | ||||
|                 continue; | ||||
|             } | ||||
|             exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo); | ||||
|             if (exifDirectory is null) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) { | ||||
|                 keyValuePairs.Add(fileNameFirstSegment, []); | ||||
|                 if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) { | ||||
|                     throw new Exception(); | ||||
|                 } | ||||
|             } | ||||
|             collection.Add(exifDirectory); | ||||
|         } | ||||
|         foreach (KeyValuePair<long, List<ExifDirectory>> keyValuePair in keyValuePairs) { | ||||
|             results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs, ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP) { | ||||
|         ReadOnlyCollection<string>? digiKamFiles; | ||||
|         foreach (KeyValuePair<long, ReadOnlyCollection<ExifDirectory>> keyValuePair in keyValuePairs) { | ||||
|             if (!keyValuePairsXMP.TryGetValue(keyValuePair.Key, out digiKamFiles)) { | ||||
|                 logger.LogWarning("{fileNameFirstSegment}) Didn't find a matching file!", keyValuePair.Key); | ||||
|             } else { | ||||
|                 string? originalFile = null; | ||||
|                 WriteFaceData(logger, outputDirectoryName, originalFile, keyValuePair.Key, keyValuePair.Value, digiKamFiles); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static int? GetMatchingLine(string digiKamLine, ReadOnlyCollection<string> lines) { | ||||
|         int? result = null; | ||||
|         for (int i = 0; i < lines.Count; i++) { | ||||
|             if (lines[i] == digiKamLine) { | ||||
|                 result = i; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static void Extract(string file, double width, double height, int left, int top, string suffix) { | ||||
| #if SystemDrawingCommon | ||||
|         Rectangle rectangle = new(left, top, width, height); | ||||
|         using (Bitmap source = new(file)) { | ||||
|             using (Bitmap bitmap = new(width, height)) { | ||||
|                 using (Graphics graphics = Graphics.FromImage(bitmap)) { | ||||
|                     graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); | ||||
|                 } | ||||
|                 bitmap.Save($"{file}{suffix}"); | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										123
									
								
								ADO2025/PI6/Helper-2025-07-26.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								ADO2025/PI6/Helper-2025-07-26.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Phares.Shared.Models; | ||||
| using Phares.Shared.Models.Stateless; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI6; | ||||
|  | ||||
| internal static partial class Helper20250726 { | ||||
|  | ||||
|     private record Helper20250726Settings(ResultSettings? ResultSettings, MetadataSettings? MetadataSettings); | ||||
|  | ||||
|     private record Record(CombinedEnumAndIndex CombinedEnumAndIndex, FilePath FilePath, bool HasFlagHidden); | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
|     [JsonSerializable(typeof(Helper20250726Settings))] | ||||
|     private partial class Helper20250726SettingsSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     internal static void CopyToCombinedEnumAndIndexFormat(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]); | ||||
|         string[] files; | ||||
|         string[] searchPatterns = args[4].Split('~'); | ||||
|         string jsonFile = Path.GetFullPath(args[3]); | ||||
|         if (!File.Exists(jsonFile)) { | ||||
|             throw new Exception($"json file doesn't exist! <{jsonFile}>"); | ||||
|         } | ||||
|         ReadOnlyCollection<Record> records; | ||||
|         string json = File.ReadAllText(jsonFile); | ||||
|         string destinationDirectoryName = args[6]; | ||||
|         ReadOnlyDictionary<byte, ReadOnlyCollection<string>> keyValues; | ||||
|         string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); | ||||
|         string destinationDirectory = Path.GetFullPath(args[5].Split('~')[0]); | ||||
|         long maxSize = long.Parse(args[2], System.Globalization.NumberStyles.Float); | ||||
|         ReadOnlyDictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs; | ||||
|         Helper20250726Settings? settings = JsonSerializer.Deserialize(json, Helper20250726SettingsSourceGenerationContext.Default.Helper20250726Settings); | ||||
|         if (settings?.ResultSettings is null || settings.ResultSettings.ResultAllInOneSubdirectoryLength < 1 || settings.MetadataSettings is null) { | ||||
|             throw new Exception(nameof(Helper20250726Settings)); | ||||
|         } | ||||
|         foreach (string searchPattern in searchPatterns) { | ||||
|             files = Directory.GetFiles(sourceDirectory, $"*{searchPattern}", SearchOption.AllDirectories); | ||||
|             if (files.Length == 0) { | ||||
|                 logger.LogInformation($"Didn't find any {searchPattern} files"); | ||||
|                 continue; | ||||
|             } | ||||
|             logger.LogInformation($"Found {files.Length} {searchPattern} files"); | ||||
|             keyValuePairs = GetKeyValuePairs(settings.ResultSettings, destinationDirectory, destinationDirectoryName); | ||||
|             records = GetRecords(logger, settings.ResultSettings, settings.MetadataSettings, files); | ||||
|             keyValues = keyValuePairs.ElementAt(0).Value; | ||||
|             CopyToCombinedEnumAndIndexFormat(logger, maxSize, records, keyValues); | ||||
|         } | ||||
|         Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, destinationDirectory); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> GetKeyValuePairs(ResultSettings resultSettings, string destinationDirectory, string destinationDirectoryName) { | ||||
|         Dictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = []; | ||||
|         ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs = IPath.GetKeyValuePairs(resultSettings, destinationDirectory, [destinationDirectoryName]); | ||||
|         foreach (KeyValuePair<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs) { | ||||
|             foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValue in keyValuePair.Value) { | ||||
|                 if (keyValue.Key != destinationDirectoryName) { | ||||
|                     throw new Exception("Never should happen!"); | ||||
|                 } | ||||
|                 results.Add(keyValuePair.Key, keyValue.Value); | ||||
|             } | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyCollection<Record> GetRecords(ILogger<Worker> logger, ResultSettings resultSettings, MetadataSettings metadataSettings, string[] files) { | ||||
|         List<Record> results = []; | ||||
|         Record record; | ||||
|         FileInfo fileInfo; | ||||
|         FilePath filePath; | ||||
|         bool hasFlagHidden; | ||||
|         FileHolder fileHolder; | ||||
|         CombinedEnumAndIndex combinedEnumAndIndex; | ||||
|         foreach (string file in files) { | ||||
|             fileInfo = new(file); | ||||
|             fileHolder = FileHolder.Get(fileInfo, id: null); | ||||
|             filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); | ||||
|             if (!filePath.IsIntelligentIdFormat) { | ||||
|                 logger.LogWarning("<{file}> skipped because of name format!", filePath.Name); | ||||
|                 continue; | ||||
|             } | ||||
|             hasFlagHidden = fileInfo.Attributes.HasFlag(FileAttributes.Hidden); | ||||
|             combinedEnumAndIndex = IPath.GetCombinedEnumAndIndex(resultSettings, filePath); | ||||
|             record = new(CombinedEnumAndIndex: combinedEnumAndIndex, FilePath: filePath, HasFlagHidden: hasFlagHidden); | ||||
|             results.Add(record); | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void CopyToCombinedEnumAndIndexFormat(ILogger<Worker> logger, long maxSize, ReadOnlyCollection<Record> records, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> keyValuePairs) { | ||||
|         string checkFile; | ||||
|         FileInfo fileInfo; | ||||
|         FileAttributes fileAttributes; | ||||
|         foreach (Record record in records) { | ||||
|             if (record.FilePath.Length > maxSize) { | ||||
|                 logger.LogWarning("<{file}> skipped because it is over {size}!", record.FilePath.Name, maxSize); | ||||
|                 continue; | ||||
|             } | ||||
|             checkFile = Path.Combine(keyValuePairs[record.CombinedEnumAndIndex.Enum][record.CombinedEnumAndIndex.Index], record.FilePath.Name); | ||||
|             if (File.Exists(checkFile)) { | ||||
|                 logger.LogWarning("<{file}> skipped because it already exists!", record.FilePath.Name); | ||||
|                 continue; | ||||
|             } | ||||
|             File.Copy(record.FilePath.FullName, checkFile); | ||||
|             if (record.HasFlagHidden) { | ||||
|                 fileInfo = new(checkFile); | ||||
|                 fileAttributes = fileInfo.Attributes & ~FileAttributes.Hidden; | ||||
|                 File.SetAttributes(fileInfo.FullName, fileAttributes); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										296
									
								
								ADO2025/PI7/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ADO2025/PI7/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| [*.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 = true | ||||
| 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_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 = warning # 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 = warning # 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 = warning # 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_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 = false | ||||
| dotnet_sort_system_directives_first = false | ||||
| 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 | ||||
							
								
								
									
										70
									
								
								ADO2025/PI7/Helper-2025-09-08.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								ADO2025/PI7/Helper-2025-09-08.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI7; | ||||
|  | ||||
| internal static partial class Helper20250908 { | ||||
|  | ||||
|     internal static void DebugProxyPass(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]); | ||||
|         string debug; | ||||
|         string domain; | ||||
|         string[] lines; | ||||
|         string[] files; | ||||
|         string fileName; | ||||
|         string[] segments; | ||||
|         string[] segmentsB; | ||||
|         string includePath = args[3]; | ||||
|         string searchString = args[4]; | ||||
|         string searchPattern = args[2]; | ||||
|         string searchStringBreak = args[6]; | ||||
|         string[] searchStrings = args[5].Split('~'); | ||||
|         string[] sourceDirectories = Path.GetFullPath(args[0]).Split('~'); | ||||
|         foreach (string sourceDirectory in sourceDirectories) { | ||||
|             files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); | ||||
|             logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); | ||||
|             foreach (string file in files) { | ||||
|                 debug = string.Empty; | ||||
|                 domain = string.Empty; | ||||
|                 lines = File.ReadAllLines(file); | ||||
|                 fileName = Path.GetFileName(file); | ||||
|                 foreach (string line in lines) { | ||||
|                     if (line.Contains(searchStringBreak)) { | ||||
|                         if (!string.IsNullOrEmpty(domain)) { | ||||
|                             domain = $"{domain}/#has-{searchStringBreak}"; | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                     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) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     if (segments[1][0] is not ' ' and not '\t') { | ||||
|                         continue; | ||||
|                     } | ||||
|                     segmentsB = segments[1].Trim().TrimEnd(';').TrimEnd('/').Split(':'); | ||||
|                     if (segmentsB.Length == 1 || string.IsNullOrEmpty(segmentsB[0]) || segmentsB[0][0] == '/') { | ||||
|                         debug = segmentsB[0]; | ||||
|                     } else { | ||||
|                         debug = $"{segmentsB[0]}:{segmentsB[^1]}"; | ||||
|                     } | ||||
|                 } | ||||
|                 logger.LogInformation("include {includePath}{fileName}; # https://{domain} # {debug}", | ||||
|                                       includePath, | ||||
|                                       fileName, | ||||
|                                       domain, | ||||
|                                       debug); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										272
									
								
								ADO2025/PI7/Helper-2025-09-26.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								ADO2025/PI7/Helper-2025-09-26.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,272 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Globalization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI7; | ||||
|  | ||||
| internal static partial class Helper20250926 { | ||||
|  | ||||
|     internal static void RenameThenFindFirstAndLast(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]); | ||||
|         const char star = '*'; | ||||
|         const char underscore = '_'; | ||||
|         string dateFormat = args[4]; | ||||
|         string searchString = args[3]; | ||||
|         int seconds = int.Parse(args[5]); | ||||
|         string[] searchPatterns = args[2].Split('~'); | ||||
|         string key = searchString.ToLower().Split(' ')[0]; | ||||
|         string[] sourceDirectories = Path.GetFullPath(args[0]).Split('~'); | ||||
|         if (args.Count == 999) { | ||||
|             Redo(logger, star, underscore, dateFormat, searchString, key, searchPatterns, sourceDirectories); | ||||
|         } | ||||
|         Rename(logger, star, underscore, dateFormat, searchString, key, searchPatterns, sourceDirectories); | ||||
|         if (seconds > 0) { | ||||
|             FindFirstAndLast(logger, star, underscore, dateFormat, searchString, seconds, searchPatterns, sourceDirectories); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void Redo(ILogger<Worker> logger, char star, char underscore, string dateFormat, string searchString, string key, string[] searchPatterns, string[] sourceDirectories) { | ||||
|         bool found; | ||||
|         string check; | ||||
|         string[] files; | ||||
|         string[] lines; | ||||
|         string fileName; | ||||
|         string checkFile; | ||||
|         string checkPath; | ||||
|         DateTime lastDate; | ||||
|         DateTime firstDate; | ||||
|         string directoryName; | ||||
|         string[] directories; | ||||
|         string? directoryPath; | ||||
|         foreach (string sourceDirectory in sourceDirectories) { | ||||
|             directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string searchPattern in searchPatterns) { | ||||
|                 if (searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { | ||||
|                     throw new Exception($"{nameof(searchPattern)} must not contain {underscore} and must contain {star}"); | ||||
|                 } | ||||
|                 foreach (string directory in directories) { | ||||
|                     directoryName = Path.GetFileName(directory); | ||||
|                     files = Directory.GetFiles(directory, searchPattern, SearchOption.AllDirectories); | ||||
|                     logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); | ||||
|                     foreach (string file in files) { | ||||
|                         found = false; | ||||
|                         lastDate = DateTime.MinValue; | ||||
|                         firstDate = DateTime.MinValue; | ||||
|                         fileName = Path.GetFileName(file); | ||||
|                         directoryPath = Path.GetDirectoryName(file); | ||||
|                         if (string.IsNullOrEmpty(directoryPath)) { | ||||
|                             logger.LogInformation("skipped (empty) '{directoryPath}'", directoryPath); | ||||
|                             continue; | ||||
|                         } | ||||
|                         try { | ||||
|                             lines = File.ReadAllLines(file); | ||||
|                             foreach (string line in lines) { | ||||
|                                 if (!found && line.Contains(searchString)) { | ||||
|                                     found = true; | ||||
|                                 } | ||||
|                                 if (line.Length < dateFormat.Length) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 check = line[..dateFormat.Length]; | ||||
|                                 if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime logDate)) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 if (firstDate == DateTime.MinValue) { | ||||
|                                     firstDate = logDate; | ||||
|                                 } | ||||
|                                 lastDate = logDate; | ||||
|                             } | ||||
|                             if (!found) { | ||||
|                                 checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}.log"; | ||||
|                             } else { | ||||
|                                 checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}~~~{key}.log"; | ||||
|                             } | ||||
|                             checkPath = Path.Combine(directoryPath, checkFile); | ||||
|                             if (checkFile == fileName) { | ||||
|                                 logger.LogDebug("skipped (match) '{checkFile}'", checkFile); | ||||
|                             } else if (File.Exists(checkPath)) { | ||||
|                                 logger.LogInformation("skipped (exists) '{checkPath}'", checkPath); | ||||
|                             } else { | ||||
|                                 logger.LogInformation("rename to '{checkPath}'", checkPath); | ||||
|                                 File.Move(file, checkPath); | ||||
|                             } | ||||
|                         } catch { | ||||
|                             logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", file, (DateTime.Now - new FileInfo(file).LastWriteTime).TotalHours); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void Rename(ILogger<Worker> logger, char star, char underscore, string dateFormat, string searchString, string key, string[] searchPatterns, string[] sourceDirectories) { | ||||
|         bool found; | ||||
|         string check; | ||||
|         string[] files; | ||||
|         string[] lines; | ||||
|         string checkFile; | ||||
|         string checkPath; | ||||
|         DateTime lastDate; | ||||
|         string[] segments; | ||||
|         DateTime firstDate; | ||||
|         string[] segmentsB; | ||||
|         string directoryName; | ||||
|         string[] directories; | ||||
|         string destinationDirectory; | ||||
|         FileInfo[] fileInfoCollection; | ||||
|         foreach (string sourceDirectory in sourceDirectories) { | ||||
|             directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string searchPattern in searchPatterns) { | ||||
|                 if (!searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { | ||||
|                     throw new Exception($"{nameof(searchPattern)} must contain {underscore} and {star}"); | ||||
|                 } | ||||
|                 segments = searchPattern.Split(star); | ||||
|                 segmentsB = segments[0].Split(underscore); | ||||
|                 foreach (string directory in directories) { | ||||
|                     directoryName = Path.GetFileName(directory); | ||||
|                     destinationDirectory = Path.Combine(directory, segmentsB[0], segmentsB[1]); | ||||
|                     if (!Directory.Exists(destinationDirectory)) { | ||||
|                         _ = Directory.CreateDirectory(destinationDirectory); | ||||
|                     } | ||||
|                     files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly); | ||||
|                     logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); | ||||
|                     fileInfoCollection = files.Select(f => new FileInfo(f)).ToArray(); | ||||
|                     foreach (FileInfo fileInfo in fileInfoCollection.OrderBy(f => f.LastWriteTime)) { | ||||
|                         if (fileInfo.Length == 0) { | ||||
|                             logger.LogDebug("skipped (0k) '{FileInfo}'", fileInfo.FullName); | ||||
|                             continue; | ||||
|                         } else if (fileInfo.LastWriteTime > DateTime.Now.AddMinutes(-1)) { | ||||
|                             logger.LogDebug("skipped (too new) '{FileInfo}'", fileInfo.FullName); | ||||
|                             continue; | ||||
|                         } | ||||
|                         found = false; | ||||
|                         lastDate = DateTime.MinValue; | ||||
|                         firstDate = DateTime.MinValue; | ||||
|                         try { | ||||
|                             lines = File.ReadAllLines(fileInfo.FullName); | ||||
|                             foreach (string line in lines) { | ||||
|                                 if (!found && line.Contains(searchString)) { | ||||
|                                     found = true; | ||||
|                                 } | ||||
|                                 if (line.Length < dateFormat.Length) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 check = line[..dateFormat.Length]; | ||||
|                                 if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime logDate)) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 if (firstDate == DateTime.MinValue) { | ||||
|                                     firstDate = logDate; | ||||
|                                 } | ||||
|                                 lastDate = logDate; | ||||
|                             } | ||||
|                             if (!found) { | ||||
|                                 checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}.log"; | ||||
|                             } else { | ||||
|                                 checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}~~~{key}.log"; | ||||
|                             } | ||||
|                             checkPath = Path.Combine(destinationDirectory, checkFile); | ||||
|                             if (checkFile == fileInfo.Name) { | ||||
|                                 logger.LogDebug("skipped (match) '{checkFile}'", checkFile); | ||||
|                             } else if (File.Exists(checkPath)) { | ||||
|                                 logger.LogInformation("skipped (exists) '{checkPath}'", checkPath); | ||||
|                             } else { | ||||
|                                 logger.LogInformation("rename to '{checkPath}'", checkPath); | ||||
|                                 File.Move(fileInfo.FullName, checkPath); | ||||
|                             } | ||||
|                         } catch { | ||||
|                             logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", (DateTime.Now - fileInfo.LastWriteTime).TotalHours, fileInfo.FullName); | ||||
|                         } | ||||
|                     } | ||||
|                     Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void FindFirstAndLast(ILogger<Worker> logger, char star, char underscore, string dateFormat, string searchString, int seconds, string[] searchPatterns, string[] sourceDirectories) { | ||||
|         string line; | ||||
|         string check; | ||||
|         string[] files; | ||||
|         string[] lines; | ||||
|         DateTime lastDate; | ||||
|         double totalHours; | ||||
|         string[] segments; | ||||
|         TimeSpan timeSpan; | ||||
|         DateTime firstDate; | ||||
|         string[] segmentsB; | ||||
|         string directoryName; | ||||
|         string[] directories; | ||||
|         string searchPatternB; | ||||
|         string destinationDirectory; | ||||
|         FileInfo[] fileInfoCollection; | ||||
|         DateTime logDate = DateTime.MinValue; | ||||
|         foreach (string sourceDirectory in sourceDirectories) { | ||||
|             directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string searchPattern in searchPatterns) { | ||||
|                 if (!searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { | ||||
|                     throw new Exception($"{nameof(searchPattern)} must contain {underscore} and {star}"); | ||||
|                 } | ||||
|                 segments = searchPattern.TrimEnd(star).Split(star); | ||||
|                 segmentsB = segments[0].Split(underscore); | ||||
|                 foreach (string directory in directories) { | ||||
|                     directoryName = Path.GetFileName(directory); | ||||
|                     destinationDirectory = Path.Combine(directory, segmentsB[0], segmentsB[1]); | ||||
|                     if (!Directory.Exists(destinationDirectory)) { | ||||
|                         logger.LogInformation("skipped (doesn't exist) '{destinationDirectory}'", destinationDirectory); | ||||
|                         continue; | ||||
|                     } | ||||
|                     searchPatternB = $"*{segments[^1]}*"; | ||||
|                     files = Directory.GetFiles(destinationDirectory, searchPatternB, SearchOption.TopDirectoryOnly); | ||||
|                     logger.LogInformation("For {destinationDirectory} with search pattern '{SearchPatternB}' found {files} file(s)", destinationDirectory, searchPatternB, files.Length); | ||||
|                     fileInfoCollection = files.Select(f => new FileInfo(f)).ToArray(); | ||||
|                     foreach (FileInfo fileInfo in fileInfoCollection.OrderBy(f => f.LastWriteTime)) { | ||||
|                         if (fileInfo.Length == 0) { | ||||
|                             logger.LogDebug("skipped (0k) '{FileInfo}'", fileInfo.FullName); | ||||
|                             continue; | ||||
|                         } else if (fileInfo.LastWriteTime > DateTime.Now.AddMinutes(-1)) { | ||||
|                             logger.LogDebug("skipped (too new) '{FileInfo}'", fileInfo.FullName); | ||||
|                             continue; | ||||
|                         } | ||||
|                         lastDate = DateTime.MinValue; | ||||
|                         firstDate = DateTime.MinValue; | ||||
|                         try { | ||||
|                             lines = File.ReadAllLines(fileInfo.FullName); | ||||
|                             for (int i = 0; i < lines.Length; i++) { | ||||
|                                 line = lines[i]; | ||||
|                                 if (!line.Contains(searchString) || lines[i - 1].Length < dateFormat.Length) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 check = lines[i - 1][..dateFormat.Length]; | ||||
|                                 if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out logDate)) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 timeSpan = logDate - lastDate; | ||||
|                                 if (firstDate == DateTime.MinValue || timeSpan.TotalSeconds > seconds) { | ||||
|                                     if (firstDate != DateTime.MinValue) { | ||||
|                                         totalHours = Math.Round((lastDate - firstDate).TotalHours, 2); | ||||
|                                         logger.LogInformation("Ran for {totalHours} hour(s) {logDate} - {firstDate} {file}", totalHours, logDate.ToString("HH:mm:ss"), firstDate.ToString("HH:mm:ss"), fileInfo.FullName[directory.Length..]); | ||||
|                                     } | ||||
|                                     firstDate = logDate; | ||||
|                                 } | ||||
|                                 lastDate = logDate; | ||||
|                             } | ||||
|                             if (firstDate != DateTime.MinValue && logDate != DateTime.MinValue) { | ||||
|                                 totalHours = Math.Round((lastDate - firstDate).TotalHours, 2); | ||||
|                                 logger.LogInformation("Ran for {totalHours} hour(s) {logDate} - {firstDate} {file}", totalHours, logDate.ToString("HH:mm:ss"), firstDate.ToString("HH:mm:ss"), fileInfo.FullName[directory.Length..]); | ||||
|                             } | ||||
|                         } catch { | ||||
|                             logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", (DateTime.Now - fileInfo.LastWriteTime).TotalHours, fileInfo.FullName); | ||||
|                         } | ||||
|                     } | ||||
|                     Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										136
									
								
								ADO2025/PI7/Helper-2025-10-22.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								ADO2025/PI7/Helper-2025-10-22.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Globalization; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.ADO2025.PI7; | ||||
|  | ||||
| internal static partial class Helper20251022 { | ||||
|  | ||||
|     private record Root(int Count, JsonElement[] Records, ProcessDataStandardFormat? ProcessDataStandardFormat) { | ||||
|  | ||||
|         internal static Root? Get(string file) { | ||||
|             Root? result; | ||||
|             string? json = File.ReadAllText(file); | ||||
|             result = JsonSerializer.Deserialize(json, Helper20251022RootSourceGenerationContext.Default.Root); | ||||
|             if (result is null || result.ProcessDataStandardFormat is null || result.Records.Length == 0) { | ||||
|                 result = null; | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     [JsonSerializable(typeof(Root))] | ||||
|     private partial class Helper20251022RootSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     [JsonSourceGenerationOptions(WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     [JsonSerializable(typeof(JsonElement))] | ||||
|     private partial class Helper20251022JsonElementSourceGenerationContext : JsonSerializerContext { | ||||
|     } | ||||
|  | ||||
|     private record ProcessDataStandardFormat([property: JsonPropertyName("Footer")] JsonElement? Footer, [property: JsonPropertyName("LOGISTICS_1")] JsonElement? Logistics); | ||||
|  | ||||
|     internal static void CombineFiles(ILogger<Worker> logger, List<string> args) { | ||||
|         logger.LogInformation(args[0]); | ||||
|         logger.LogInformation(args[1]); | ||||
|         logger.LogInformation(args[2]); | ||||
|         logger.LogInformation(args[3]); | ||||
|         string searchPattern = args[2]; | ||||
|         string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); | ||||
|         string destinationDirectory = Path.GetFullPath(args[3].Split('~')[0]); | ||||
|         string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         ReadOnlyDictionary<string, List<string>> files = GetFiles(logger, searchPattern, directories.AsReadOnly()); | ||||
|         CombineFiles(logger, destinationDirectory, files); | ||||
|     } | ||||
|  | ||||
|     private static ReadOnlyDictionary<string, List<string>> GetFiles(ILogger<Worker> logger, string searchPattern, ReadOnlyCollection<string> directories) { | ||||
|         Dictionary<string, List<string>> results = []; | ||||
|         string key; | ||||
|         string[] files; | ||||
|         FileInfo fileInfo; | ||||
|         string[] segments; | ||||
|         string weekOfYear; | ||||
|         Calendar calendar = new CultureInfo("en-US").Calendar; | ||||
|         List<string>? collection; | ||||
|         foreach (string directory in directories) { | ||||
|             files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly); | ||||
|             files = files.OrderBy(l => l).ThenBy(l => l.Length).ToArray(); | ||||
|             for (int i = 0; i < files.Length; i++) { | ||||
|                 fileInfo = new(files[i]); | ||||
|                 segments = fileInfo.Name.Split('_'); | ||||
|                 if (segments.Length < 2) { | ||||
|                     logger.LogWarning("{fileInfoName} does not have enough segments!", fileInfo.Name); | ||||
|                     continue; | ||||
|                 } | ||||
|                 weekOfYear = $"{fileInfo.LastWriteTime.Year}_Week_{calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}"; | ||||
|                 key = $"{segments[0]}_{weekOfYear}"; | ||||
|                 if (!results.TryGetValue(key, out collection)) { | ||||
|                     collection = []; | ||||
|                     results.Add(key, collection); | ||||
|                 } | ||||
|                 collection.Add(files[i]); | ||||
|             } | ||||
|         } | ||||
|         return results.AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     private static void CombineFiles(ILogger<Worker> logger, string destinationDirectory, ReadOnlyDictionary<string, List<string>> files) { | ||||
|         Root? root; | ||||
|         string json; | ||||
|         string[] lines; | ||||
|         string fileName; | ||||
|         string? jsonFooter; | ||||
|         string? jsonLogistics; | ||||
|         JsonElement jsonElementB; | ||||
|         List<string> segments = []; | ||||
|         List<string> jsonLines = []; | ||||
|         if (!Directory.Exists(destinationDirectory)) { | ||||
|             _ = Directory.CreateDirectory(destinationDirectory); | ||||
|         } | ||||
|         foreach (KeyValuePair<string, List<string>> keyValuePair in files) { | ||||
|             jsonLines.Clear(); | ||||
|             fileName = Path.Combine(destinationDirectory, $"{keyValuePair.Key}.json"); | ||||
|             foreach (string file in keyValuePair.Value) { | ||||
|                 root = Root.Get(file); | ||||
|                 if (root is null) { | ||||
|                     logger.LogWarning("result is null"); | ||||
|                     continue; | ||||
|                 } | ||||
|                 foreach (JsonElement jsonElement in root.Records) { | ||||
|                     jsonFooter = root.ProcessDataStandardFormat?.Footer?.ToString(); | ||||
|                     jsonLogistics = root.ProcessDataStandardFormat?.Logistics?.ToString(); | ||||
|                     if (string.IsNullOrEmpty(jsonFooter) && string.IsNullOrEmpty(jsonLogistics)) { | ||||
|                         jsonLines.Add(jsonElement.ToString()); | ||||
|                     } else { | ||||
|                         segments.Clear(); | ||||
|                         segments.Add(jsonElement.ToString()[..^1]); | ||||
|                         if (!string.IsNullOrEmpty(jsonFooter)) { | ||||
|                             segments.Add(","); | ||||
|                             lines = jsonFooter[1..^1].Split(Environment.NewLine); | ||||
|                             segments.AddRange(lines.Select(l => l.Trim())); | ||||
|                         } | ||||
|                         if (!string.IsNullOrEmpty(jsonLogistics)) { | ||||
|                             segments.Add(","); | ||||
|                             lines = jsonLogistics[1..^1].Split(Environment.NewLine); | ||||
|                             segments.AddRange(lines.Select(l => l.Trim())); | ||||
|                         } | ||||
|                         segments.Add("}"); | ||||
|                         jsonElementB = JsonSerializer.Deserialize(string.Join(' ', segments), Helper20251022JsonElementSourceGenerationContext.Default.JsonElement); | ||||
|                         jsonLines.Add(jsonElementB.ToString()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (jsonLines.Count == 0) { | ||||
|                 logger.LogWarning("jsonLines is empty"); | ||||
|                 continue; | ||||
|             } | ||||
|             json = string.Concat('[', Environment.NewLine, string.Join($",{Environment.NewLine}", jsonLines), Environment.NewLine, ']'); | ||||
|             File.WriteAllText(fileName, json); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -129,6 +129,68 @@ internal static class HelperDay | ||||
|             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 if (args[1] == "Day-Helper-2025-07-20") | ||||
|             ADO2025.PI6.Helper20250720.WriteFaceData(logger, args); | ||||
|         else if (args[1] == "Day-Helper-2025-07-26") | ||||
|             ADO2025.PI6.Helper20250726.CopyToCombinedEnumAndIndexFormat(logger, args); | ||||
|         else if (args[1] == "Day-Helper-2025-09-08") | ||||
|             ADO2025.PI7.Helper20250908.DebugProxyPass(logger, args); | ||||
|         else if (args[1] == "Day-Helper-2025-09-26") | ||||
|             ADO2025.PI7.Helper20250926.RenameThenFindFirstAndLast(logger, args); | ||||
|         else if (args[1] == "Day-Helper-2025-10-22") | ||||
|             ADO2025.PI7.Helper20251022.CombineFiles(logger, args); | ||||
|         else | ||||
|             throw new Exception(appSettings.Company); | ||||
|     } | ||||
|  | ||||
| @ -25,7 +25,6 @@ internal static class Helper20231010 | ||||
|             dateTime = new(ticks); | ||||
|             logger.LogInformation("{directory.Name} at {LastWriteTime} took {TotalMinutes} minutes(s)", directoryInfo.Name.PadRight(padLength, ' '), directoryInfo.LastWriteTime, Math.Round(timeSpan.TotalMinutes, 3)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -65,7 +65,7 @@ internal static class Helper20231130 | ||||
|         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 = []; | ||||
|         string[] values; | ||||
| @ -79,7 +79,7 @@ internal static class Helper20231130 | ||||
|             keyColumnValue = values[keyColumnIndex]; | ||||
|             if (string.IsNullOrEmpty(keyColumnValue)) | ||||
|                 continue; | ||||
|             if (!systemStates.TryGetValue(keyColumnValue, out systemState)) | ||||
|             if (!systemStateToNames.TryGetValue(keyColumnValue, out systemState)) | ||||
|                 continue; | ||||
|             if (results.Contains(systemState)) | ||||
|                 continue; | ||||
| @ -106,7 +106,7 @@ internal static class Helper20231130 | ||||
|         string missingKeyDirectory = Path.Combine(sourceDirectory, "Missing-Key"); | ||||
|         if (!Directory.Exists(missingKeyDirectory)) | ||||
|             _ = Directory.CreateDirectory(missingKeyDirectory); | ||||
|         ReadOnlyDictionary<string, string> systemStates = GetSystemStates(); | ||||
|         ReadOnlyDictionary<string, string> systemStateToNames = GetSystemStates(); | ||||
|         ReadOnlyCollection<Record> records = GetRecords(sourceDirectory, timestampFormat); | ||||
|         foreach (Record record in records) | ||||
|         { | ||||
| @ -132,7 +132,7 @@ internal static class Helper20231130 | ||||
|                 continue; | ||||
|             } | ||||
|             logger.LogInformation("{timestamp} triggered", record.TimeStamp); | ||||
|             systemStateValues = GetSystemStateValues(lines, columns, keyColumnIndex.Value, systemStates); | ||||
|             systemStateValues = GetSystemStateValues(lines, columns, keyColumnIndex.Value, systemStateToNames); | ||||
|             if (systemStateValues.Count == 0) | ||||
|             { | ||||
|                 File.Move(record.File, Path.Combine(sourceDirectory, missingKeyDirectory, record.FileName)); | ||||
| @ -142,7 +142,6 @@ internal static class Helper20231130 | ||||
|             systemState = string.Join('-', systemStateValues); | ||||
|             checkFileName = Path.Combine(Path.GetDirectoryName(record.File) ?? throw new Exception(), $"{record.Equipment}-{record.TimeStamp}-{systemState}.pdsf"); | ||||
|             File.WriteAllLines(checkFileName, lines); | ||||
|             File.Delete(record.File); | ||||
|             if (DateTime.TryParseExact(record.TimeStamp, timestampFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) | ||||
|                 File.SetLastWriteTime(checkFileName, dateTime); | ||||
|         } | ||||
|  | ||||
							
								
								
									
										25
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Dockerfile
									
									
									
									
									
										Normal 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" ] | ||||
| @ -14,14 +14,14 @@ | ||||
|     </ItemGroup> | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" /> | ||||
|         <PackageReference Include="MetadataExtractor" Version="2.8.1" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" /> | ||||
|         <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" /> | ||||
|         <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.11" /> | ||||
|         <PackageReference Include="SSH.NET" Version="2024.2.0" /> | ||||
|         <PackageReference Include="System.Text.Json" Version="8.0.5" /> | ||||
|         <PackageReference Include="Phares.Metadata" Version="8.0.118.14905" /> | ||||
|         <PackageReference Include="Phares.Shared" Version="8.0.118.14905" /> | ||||
|         <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.21" /> | ||||
|         <PackageReference Include="System.Text.Json" Version="9.0.7" /> | ||||
|         <PackageReference Include="TextCopy" Version="6.2.1" /> | ||||
|         <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> | ||||
|         <PackageReference Include="YamlDotNet" Version="16.2.1" /> | ||||
|         <PackageReference Include="YamlDotNet" Version="16.3.0" /> | ||||
|     </ItemGroup> | ||||
| </Project> | ||||
| @ -1,126 +0,0 @@ | ||||
| using System.Drawing; | ||||
|  | ||||
| namespace File_Folder_Helper.Helpers.Exif; | ||||
|  | ||||
| internal static class Dimensions | ||||
| { | ||||
|  | ||||
| #pragma warning disable IDE0230 | ||||
|     private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new() | ||||
|     { | ||||
|         { new byte[] { 0x42, 0x4D }, DecodeBitmap }, | ||||
|         { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, | ||||
|         { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, | ||||
|         { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, | ||||
|         { new byte[] { 0xff, 0xd8 }, DecodeJfif }, | ||||
|         { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, | ||||
|     }; | ||||
| #pragma warning restore IDE0230 | ||||
|  | ||||
|     private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) | ||||
|     { | ||||
|         for (int i = 0; i < thatBytes.Length; i += 1) | ||||
|         { | ||||
|             if (thisBytes[i] == thatBytes[i]) | ||||
|                 continue; | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private static short ReadLittleEndianInt16(BinaryReader binaryReader) | ||||
|     { | ||||
|         byte[] bytes = new byte[sizeof(short)]; | ||||
|         for (int i = 0; i < sizeof(short); i += 1) | ||||
|             bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); | ||||
|         return BitConverter.ToInt16(bytes, 0); | ||||
|     } | ||||
|  | ||||
|     private static int ReadLittleEndianInt32(BinaryReader binaryReader) | ||||
|     { | ||||
|         byte[] bytes = new byte[sizeof(int)]; | ||||
|         for (int i = 0; i < sizeof(int); i += 1) | ||||
|             bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); | ||||
|         return BitConverter.ToInt32(bytes, 0); | ||||
|     } | ||||
|  | ||||
|     private static Size? DecodeBitmap(BinaryReader binaryReader) | ||||
|     { | ||||
|         _ = binaryReader.ReadBytes(16); | ||||
|         int width = binaryReader.ReadInt32(); | ||||
|         int height = binaryReader.ReadInt32(); | ||||
|         return new Size(width, height); | ||||
|     } | ||||
|  | ||||
|     private static Size? DecodeGif(BinaryReader binaryReader) | ||||
|     { | ||||
|         int width = binaryReader.ReadInt16(); | ||||
|         int height = binaryReader.ReadInt16(); | ||||
|         return new Size(width, height); | ||||
|     } | ||||
|  | ||||
|     private static Size? DecodePng(BinaryReader binaryReader) | ||||
|     { | ||||
|         _ = binaryReader.ReadBytes(8); | ||||
|         int width = ReadLittleEndianInt32(binaryReader); | ||||
|         int height = ReadLittleEndianInt32(binaryReader); | ||||
|         return new Size(width, height); | ||||
|     } | ||||
|  | ||||
|     private static Size? DecodeJfif(BinaryReader binaryReader) | ||||
|     { | ||||
|         while (binaryReader.ReadByte() == 0xff) | ||||
|         { | ||||
|             byte marker = binaryReader.ReadByte(); | ||||
|             short chunkLength = ReadLittleEndianInt16(binaryReader); | ||||
|             if (marker == 0xc0) | ||||
|             { | ||||
|                 _ = binaryReader.ReadByte(); | ||||
|                 int height = ReadLittleEndianInt16(binaryReader); | ||||
|                 int width = ReadLittleEndianInt16(binaryReader); | ||||
|                 return new Size(width, height); | ||||
|             } | ||||
|             if (chunkLength >= 0) | ||||
|                 _ = binaryReader.ReadBytes(chunkLength - 2); | ||||
|             else | ||||
|             { | ||||
|                 ushort uChunkLength = (ushort)chunkLength; | ||||
|                 _ = binaryReader.ReadBytes(uChunkLength - 2); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private static Size? DecodeWebP(BinaryReader binaryReader) | ||||
|     { | ||||
|         _ = binaryReader.ReadUInt32(); // Size | ||||
|         _ = binaryReader.ReadBytes(15); // WEBP, VP8 + more | ||||
|         _ = binaryReader.ReadBytes(3); // SYNC | ||||
|         int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width | ||||
|         int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height | ||||
|         return new Size(width, height); | ||||
|     } | ||||
|  | ||||
|     internal static Size? GetDimensions(BinaryReader binaryReader) | ||||
|     { | ||||
|         int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; | ||||
|         byte[] magicBytes = new byte[maxMagicBytesLength]; | ||||
|         for (int i = 0; i < maxMagicBytesLength; i += 1) | ||||
|         { | ||||
|             magicBytes[i] = binaryReader.ReadByte(); | ||||
|             foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders) | ||||
|             { | ||||
|                 if (StartsWith(magicBytes, kvPair.Key)) | ||||
|                     return kvPair.Value(binaryReader); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     internal static Size? GetDimensions(string path) | ||||
|     { | ||||
|         using BinaryReader binaryReader = new(File.OpenRead(path)); | ||||
|         return GetDimensions(binaryReader); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,534 +0,0 @@ | ||||
| using MetadataExtractor; | ||||
| using MetadataExtractor.Formats.Exif; | ||||
| using MetadataExtractor.Formats.Exif.Makernotes; | ||||
| using System.Globalization; | ||||
|  | ||||
| namespace File_Folder_Helper.Helpers.Exif; | ||||
|  | ||||
| internal abstract class Exif | ||||
| { | ||||
|  | ||||
|     private static DateTime? GetDateTime(string? value) | ||||
|     { | ||||
|         DateTime? result; | ||||
|         string dateTimeFormat = "yyyy:MM:dd HH:mm:ss"; | ||||
|         string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; | ||||
|         if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) | ||||
|             result = dateTime; | ||||
|         else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) | ||||
|             result = dateTime; | ||||
|         else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) | ||||
|             result = dateTime; | ||||
|         else | ||||
|             result = null; | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.AviDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories) | ||||
|         { | ||||
|             if (aviDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? dateTimeOriginal; | ||||
|             string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration); | ||||
|             string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight); | ||||
|             string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth); | ||||
|             if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime)) | ||||
|                 dateTimeOriginal = checkDateTime; | ||||
|             else | ||||
|                 dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); | ||||
|             if (dateTimeOriginal is null && duration is null && height is null && width is null) | ||||
|                 continue; | ||||
|             results.Add(new(dateTimeOriginal, duration, height, width)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.ExifDirectoryBase> results = []; | ||||
|         IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>(); | ||||
|         foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) | ||||
|         { | ||||
|             if (exifDirectoryBase.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? dateTime; | ||||
|             DateTime checkDateTime; | ||||
|             DateTime? dateTimeOriginal; | ||||
|             DateTime? dateTimeDigitized; | ||||
|             string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture); | ||||
|             string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes); | ||||
|             string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist); | ||||
|             string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample); | ||||
|             string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber); | ||||
|             string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName); | ||||
|             string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel); | ||||
|             string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression); | ||||
|             string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright); | ||||
|             string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName); | ||||
|             string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion); | ||||
|             string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime); | ||||
|             string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource); | ||||
|             string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription); | ||||
|             string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight); | ||||
|             string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber); | ||||
|             string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId); | ||||
|             string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth); | ||||
|             string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed); | ||||
|             string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake); | ||||
|             string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel); | ||||
|             string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber); | ||||
|             string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake); | ||||
|             string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote); | ||||
|             string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel); | ||||
|             string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation); | ||||
|             int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation); | ||||
|             string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating); | ||||
|             string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent); | ||||
|             string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification); | ||||
|             string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed); | ||||
|             string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware); | ||||
|             string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone); | ||||
|             string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized); | ||||
|             string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal); | ||||
|             string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment); | ||||
|             string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor); | ||||
|             string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment); | ||||
|             string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords); | ||||
|             string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject); | ||||
|             string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle); | ||||
|             string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution); | ||||
|             string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution); | ||||
|             if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime)) | ||||
|                 dateTime = checkDateTime; | ||||
|             else | ||||
|                 dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime)); | ||||
|             if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime)) | ||||
|                 dateTimeOriginal = checkDateTime; | ||||
|             else | ||||
|                 dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal)); | ||||
|             if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime)) | ||||
|                 dateTimeDigitized = checkDateTime; | ||||
|             else | ||||
|                 dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized)); | ||||
|             if (userComment is not null && userComment.Length > 255) | ||||
|                 userComment = "..."; | ||||
|             if (aperture is null | ||||
|                 && applicationNotes is null | ||||
|                 && artist is null | ||||
|                 && bitsPerSample is null | ||||
|                 && bodySerialNumber is null | ||||
|                 && cameraOwnerName is null | ||||
|                 && compressedAverageBitsPerPixel is null | ||||
|                 && compression is null | ||||
|                 && copyright is null | ||||
|                 && dateTime is null | ||||
|                 && dateTimeDigitized is null | ||||
|                 && dateTimeOriginal is null | ||||
|                 && documentName is null | ||||
|                 && exifVersion is null | ||||
|                 && exposureTime is null | ||||
|                 && fileSource is null | ||||
|                 && imageDescription is null | ||||
|                 && imageHeight is null | ||||
|                 && imageNumber is null | ||||
|                 && imageUniqueId is null | ||||
|                 && imageWidth is null | ||||
|                 && isoSpeed is null | ||||
|                 && lensMake is null | ||||
|                 && lensModel is null | ||||
|                 && lensSerialNumber is null | ||||
|                 && make is null | ||||
|                 && makerNote is null | ||||
|                 && model is null | ||||
|                 && orientation is null | ||||
|                 && orientationValue is null | ||||
|                 && rating is null | ||||
|                 && ratingPercent is null | ||||
|                 && securityClassification is null | ||||
|                 && shutterSpeed is null | ||||
|                 && software is null | ||||
|                 && timeZone is null | ||||
|                 && timeZoneDigitized is null | ||||
|                 && timeZoneOriginal is null | ||||
|                 && userComment is null | ||||
|                 && winAuthor is null | ||||
|                 && winComment is null | ||||
|                 && winKeywords is null | ||||
|                 && winSubject is null | ||||
|                 && winTitle is null | ||||
|                 && xResolution is not null | ||||
|                 && yResolution is null) | ||||
|                 continue; | ||||
|             results.Add(new(aperture, | ||||
|                             applicationNotes, | ||||
|                             artist, | ||||
|                             bitsPerSample, | ||||
|                             bodySerialNumber, | ||||
|                             cameraOwnerName, | ||||
|                             compressedAverageBitsPerPixel, | ||||
|                             compression, | ||||
|                             copyright, | ||||
|                             dateTime, | ||||
|                             dateTimeDigitized, | ||||
|                             dateTimeOriginal, | ||||
|                             documentName, | ||||
|                             exifVersion, | ||||
|                             exposureTime, | ||||
|                             fileSource, | ||||
|                             imageDescription, | ||||
|                             imageHeight, | ||||
|                             imageNumber, | ||||
|                             imageUniqueId, | ||||
|                             imageWidth, | ||||
|                             isoSpeed, | ||||
|                             lensMake, | ||||
|                             lensModel, | ||||
|                             lensSerialNumber, | ||||
|                             make, | ||||
|                             makerNote, | ||||
|                             model, | ||||
|                             orientation, | ||||
|                             orientationValue, | ||||
|                             rating, | ||||
|                             ratingPercent, | ||||
|                             securityClassification, | ||||
|                             shutterSpeed, | ||||
|                             software, | ||||
|                             timeZone, | ||||
|                             timeZoneDigitized, | ||||
|                             timeZoneOriginal, | ||||
|                             userComment, | ||||
|                             winAuthor, | ||||
|                             winComment, | ||||
|                             winKeywords, | ||||
|                             winSubject, | ||||
|                             winTitle, | ||||
|                             xResolution, | ||||
|                             yResolution)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.FileMetadataDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories) | ||||
|         { | ||||
|             if (fileMetadataDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? fileModifiedDate; | ||||
|             string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName); | ||||
|             string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize); | ||||
|             if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime)) | ||||
|                 fileModifiedDate = checkDateTime; | ||||
|             else | ||||
|                 fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate)); | ||||
|             if (fileName is null || !file.EndsWith(fileName)) | ||||
|                 throw new NotSupportedException($"!{file}.EndsWith({fileName})"); | ||||
|             if (fileModifiedDate is null && fileName is null && fileSize is null) | ||||
|                 continue; | ||||
|             results.Add(new(fileModifiedDate, fileName, fileSize)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.GifHeaderDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories) | ||||
|         { | ||||
|             if (gifHeaderDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight); | ||||
|             string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth); | ||||
|             if (imageHeight is null && imageWidth is null) | ||||
|                 continue; | ||||
|             results.Add(new(imageHeight, imageWidth)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.GpsDirectory> results = []; | ||||
|         IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>(); | ||||
|         foreach (GpsDirectory gpsDirectory in gpsDirectories) | ||||
|         { | ||||
|             if (gpsDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? timeStamp; | ||||
|             string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude); | ||||
|             string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude); | ||||
|             string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef); | ||||
|             string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude); | ||||
|             string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef); | ||||
|             if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime)) | ||||
|                 timeStamp = checkDateTime; | ||||
|             else | ||||
|                 timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp)); | ||||
|             if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null) | ||||
|                 continue; | ||||
|             results.Add(new(altitude, | ||||
|                             latitude, | ||||
|                             latitudeRef, | ||||
|                             longitude, | ||||
|                             longitudeRef, | ||||
|                             timeStamp)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.JpegDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories) | ||||
|         { | ||||
|             if (jpegDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight); | ||||
|             string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth); | ||||
|             if (imageHeight is null && imageWidth is null) | ||||
|                 continue; | ||||
|             results.Add(new(imageHeight, imageWidth)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.MakernoteDirectory> results = []; | ||||
|         IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>(); | ||||
|         foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories) | ||||
|         { | ||||
|             if (appleMakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = null; | ||||
|             string? firmwareVersion = null; | ||||
|             string? qualityAndFileFormat = null; | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<CanonMakernoteDirectory> canonMakernoteDirectories = directories.OfType<CanonMakernoteDirectory>(); | ||||
|         foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories) | ||||
|         { | ||||
|             if (canonMakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagModelId); | ||||
|             string? firmwareVersion = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagCanonFirmwareVersion); | ||||
|             string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality); | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<NikonType2MakernoteDirectory> nikonType2MakernoteDirectories = directories.OfType<NikonType2MakernoteDirectory>(); | ||||
|         foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories) | ||||
|         { | ||||
|             if (nikonType2MakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagCameraSerialNumber); | ||||
|             string? firmwareVersion = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagFirmwareVersion); | ||||
|             string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat); | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<OlympusMakernoteDirectory> olympusMakernoteDirectories = directories.OfType<OlympusMakernoteDirectory>(); | ||||
|         foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories) | ||||
|         { | ||||
|             if (olympusMakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagSerialNumber1); | ||||
|             string? firmwareVersion = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagBodyFirmwareVersion); | ||||
|             string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality); | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<PanasonicMakernoteDirectory> panasonicMakernoteDirectories = directories.OfType<PanasonicMakernoteDirectory>(); | ||||
|         foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories) | ||||
|         { | ||||
|             if (panasonicMakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagInternalSerialNumber); | ||||
|             string? firmwareVersion = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagFirmwareVersion); | ||||
|             string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode); | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<SamsungType2MakernoteDirectory> samsungType2MakernoteDirectories = directories.OfType<SamsungType2MakernoteDirectory>(); | ||||
|         foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories) | ||||
|         { | ||||
|             if (samsungType2MakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagSerialNumber); | ||||
|             string? firmwareVersion = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagFirmwareName); | ||||
|             string? qualityAndFileFormat = null; | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         IEnumerable<SonyType6MakernoteDirectory> sonyType6MakernoteDirectories = directories.OfType<SonyType6MakernoteDirectory>(); | ||||
|         foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories) | ||||
|         { | ||||
|             if (sonyType6MakernoteDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? cameraSerialNumber = null; | ||||
|             string? firmwareVersion = null; | ||||
|             string? qualityAndFileFormat = null; | ||||
|             if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) | ||||
|                 continue; | ||||
|             results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.PhotoshopDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory> photoshopDirectories = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory photoshopDirectory in photoshopDirectories) | ||||
|         { | ||||
|             if (photoshopDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? jpegQuality = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality); | ||||
|             string? url = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl); | ||||
|             if (jpegQuality is null && url is null) | ||||
|                 continue; | ||||
|             results.Add(new(jpegQuality, url)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.PngDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.Png.PngDirectory> pngDirectories = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.Png.PngDirectory pngDirectory in pngDirectories) | ||||
|         { | ||||
|             if (pngDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight); | ||||
|             string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth); | ||||
|             string? textualData = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData); | ||||
|             if (imageHeight is null && imageWidth is null && textualData is null) | ||||
|                 continue; | ||||
|             results.Add(new(imageHeight, imageWidth, textualData)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.QuickTimeMovieHeaderDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories) | ||||
|         { | ||||
|             if (quickTimeMovieHeaderDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? created; | ||||
|             if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) | ||||
|                 created = checkDateTime; | ||||
|             else | ||||
|                 created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); | ||||
|             if (created is null) | ||||
|                 continue; | ||||
|             results.Add(new(created)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.QuickTimeTrackHeaderDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories) | ||||
|         { | ||||
|             if (quickTimeTrackHeaderDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             DateTime? created; | ||||
|             if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) | ||||
|                 created = checkDateTime; | ||||
|             else | ||||
|                 created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); | ||||
|             if (created is null) | ||||
|                 continue; | ||||
|             results.Add(new(created)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         List<Models.Exif.WebPDirectory> results = []; | ||||
|         IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>(); | ||||
|         foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories) | ||||
|         { | ||||
|             if (webPDirectory.Tags.Count == 0) | ||||
|                 continue; | ||||
|             string? imageHeight = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight); | ||||
|             string? imageWidth = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth); | ||||
|             if (imageHeight is null && imageWidth is null) | ||||
|                 continue; | ||||
|             results.Add(new(imageHeight, imageWidth)); | ||||
|         } | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static Models.Exif.ExifDirectory Covert(FileInfo fileInfo, System.Drawing.Size? size, int id, IReadOnlyList<MetadataExtractor.Directory> directories) | ||||
|     { | ||||
|         Models.Exif.ExifDirectory result; | ||||
|         Models.Exif.AviDirectory[] aviDirectories = GetAviDirectories(directories); | ||||
|         Models.Exif.GpsDirectory[] gpsDirectories = GetGpsDirectories(directories); | ||||
|         Models.Exif.PngDirectory[] pngDirectories = GetPngDirectories(directories); | ||||
|         Models.Exif.JpegDirectory[] jpegDirectories = GetJpegDirectories(directories); | ||||
|         Models.Exif.WebPDirectory[] webPDirectories = GetWebPDirectories(directories); | ||||
|         Models.Exif.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories); | ||||
|         Models.Exif.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories); | ||||
|         Models.Exif.MakernoteDirectory[] MakernoteDirectories = GetMakernoteDirectories(directories); | ||||
|         Models.Exif.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories); | ||||
|         Models.Exif.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(fileInfo.FullName, directories); | ||||
|         Models.Exif.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories); | ||||
|         Models.Exif.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories); | ||||
|         result = new(aviDirectories, | ||||
|                      exifBaseDirectories, | ||||
|                      fileMetadataDirectories, | ||||
|                      gifHeaderDirectories, | ||||
|                      gpsDirectories, | ||||
|                      size?.Height, | ||||
|                      id, | ||||
|                      jpegDirectories, | ||||
|                      MakernoteDirectories, | ||||
|                      fileInfo.Name, | ||||
|                      photoshopDirectories, | ||||
|                      pngDirectories, | ||||
|                      quickTimeMovieHeaderDirectories, | ||||
|                      quickTimeTrackHeaderDirectories, | ||||
|                      webPDirectories, | ||||
|                      size?.Width); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal static Models.Exif.ExifDirectory GetExifDirectory(FileInfo fileInfo) | ||||
|     { | ||||
|         Models.Exif.ExifDirectory result; | ||||
|         int id = 1; | ||||
|         System.Drawing.Size? size; | ||||
|         try | ||||
|         { size = Dimensions.GetDimensions(fileInfo.FullName); } | ||||
|         catch (Exception) | ||||
|         { size = null; } | ||||
|         IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(fileInfo.FullName); | ||||
|         result = Covert(fileInfo, size, id, directories); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,55 +0,0 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using File_Folder_Helper.Models.Exif; | ||||
| using File_Folder_Helper.Models.Face; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace File_Folder_Helper.Helpers.Exif; | ||||
|  | ||||
| internal static class HelperExif | ||||
| { | ||||
|  | ||||
|     private static ReadOnlyCollection<FaceFile> GetCollection(ExifDirectoryBase[]? exifDirectoryBases) | ||||
|     { | ||||
|         List<FaceFile> results = []; | ||||
|         if (exifDirectoryBases is not null) | ||||
|         { | ||||
|             string? json; | ||||
|             FaceFile[]? collection; | ||||
|             foreach (ExifDirectoryBase exifDirectoryBase in exifDirectoryBases) | ||||
|             { | ||||
|                 json = exifDirectoryBase.Artist; | ||||
|                 if (string.IsNullOrEmpty(json)) | ||||
|                     continue; | ||||
|                 collection = JsonSerializer.Deserialize(json, FaceFileCollectionGenerationContext.Default.FaceFileArray); | ||||
|                 if (collection is null) | ||||
|                     continue; | ||||
|                 results.AddRange(collection); | ||||
|             } | ||||
|         } | ||||
|         return new(results); | ||||
|     } | ||||
|  | ||||
|     internal static void DragAndDrop(ILogger<Worker> logger, string argZero) | ||||
|     { | ||||
|         FileInfo fileInfo = new(argZero); | ||||
|         logger.LogInformation("<{argZero}> exists", argZero); | ||||
|         ExifDirectory exifDirectory = Exif.GetExifDirectory(fileInfo); | ||||
|         string directory = Path.GetDirectoryName(argZero) ?? throw new Exception(); | ||||
|         string[] files = Directory.GetFiles(directory, "*.json", SearchOption.TopDirectoryOnly); | ||||
|         string? json = files.Length != 1 ? string.Empty : File.ReadAllText(files[0]); | ||||
|         ReadOnlyCollection<FaceFile> collection = GetCollection(exifDirectory.ExifBaseDirectories); | ||||
|         logger.LogInformation("<{collection}> value", collection.Count); | ||||
|         Dictionary<long, Person>? people = string.IsNullOrEmpty(json) ? [] : JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException(); | ||||
|         logger.LogInformation("<{people}> value", people?.Count); | ||||
|         foreach (FaceFile faceFile in collection) | ||||
|         { | ||||
|             if (faceFile.MappingFromPerson is null) | ||||
|                 logger.LogInformation("<{Confidence}> value", faceFile.Location?.Confidence); | ||||
|             else | ||||
|                 logger.LogInformation("<{DisplayDirectoryName}> value", faceFile.MappingFromPerson.DisplayDirectoryName); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										43
									
								
								Helpers/ExifHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Helpers/ExifHelper.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| using File_Folder_Helper.Models; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Phares.Metadata.Models.Stateless; | ||||
| using Phares.Shared.Models; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace File_Folder_Helper.Helpers; | ||||
|  | ||||
| internal static partial class ExifHelper | ||||
| { | ||||
|  | ||||
|     internal static void DragAndDrop(ILogger<Worker> logger, string argZero) | ||||
|     { | ||||
|         string? json; | ||||
|         string secrets = "L:/Git/AA/Rename/.vscode/.UserSecrets/secrets.json"; | ||||
|         json = !File.Exists(secrets) ? null : File.ReadAllText(secrets); | ||||
|         if (string.IsNullOrEmpty(json)) | ||||
|             throw new Exception($"Use mklink to map user secrets for rename!"); | ||||
|         ResultSettings? resultSettings = JsonSerializer.Deserialize(json, ResultSettingsSourceGenerationContext.Default.ResultSettings) ?? | ||||
|             throw new Exception(nameof(ResultSettings)); | ||||
|         MetadataSettings? metadataSettings = JsonSerializer.Deserialize(json, MetadataSettingsSourceGenerationContext.Default.MetadataSettings) ?? | ||||
|             throw new Exception(nameof(MetadataSettings)); | ||||
|         FileInfo fileInfo = new(argZero); | ||||
|         logger.LogInformation("<{argZero}> exists", argZero); | ||||
|         if (!string.IsNullOrEmpty(fileInfo.Directory?.FullName)) | ||||
|         { | ||||
|             string[] files = Directory.GetFiles(fileInfo.Directory.FullName, "*.json", SearchOption.TopDirectoryOnly); | ||||
|             json = files.Length != 1 ? string.Empty : File.ReadAllText(files[0]); | ||||
|             Dictionary<long, Person>? people = string.IsNullOrEmpty(json) ? [] : JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException(); | ||||
|             logger.LogInformation("<{people}> value", people?.Count); | ||||
|         } | ||||
|         ExifDirectory? exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, fileInfo); | ||||
|         FaceFile? faceFile = IMetadata.GetFaceFile(exifDirectory); | ||||
|         if (faceFile is not null) | ||||
|         { | ||||
|             if (faceFile.MappingFromPerson is null) | ||||
|                 logger.LogInformation("<{Confidence}> value", faceFile.Location?.Confidence); | ||||
|             else | ||||
|                 logger.LogInformation("<{DisplayDirectoryName}> value", faceFile.MappingFromPerson.DisplayDirectoryName); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -41,4 +41,31 @@ internal static class HelperDirectory | ||||
|         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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -16,8 +16,20 @@ internal static partial class HelperMarkdown | ||||
|  | ||||
|     private record Input(string? Destination, | ||||
|                          string Source, | ||||
|                          string? ReplaceWithTitle, | ||||
|                          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, | ||||
|                           string File, | ||||
|                           string[] Lines); | ||||
| @ -30,7 +42,7 @@ internal static partial class HelperMarkdown | ||||
|                                 string FileNameWithoutExtension, | ||||
|                                 ReadOnlyDictionary<string, object> FrontMatterYaml, | ||||
|                                 string H1, | ||||
|                                 bool IsGitOthersModifiedAndDeletedExcludingStandard, | ||||
|                                 bool? IsGitOthersModifiedAndDeletedExcludingStandard, | ||||
|                                 bool IsKanbanIndex, | ||||
|                                 bool IsKanbanMarkdown, | ||||
|                                 DateTime LastWriteDateTime, | ||||
| @ -49,15 +61,27 @@ internal static partial class HelperMarkdown | ||||
|  | ||||
|     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)] | ||||
|     [JsonSerializable(typeof(Dictionary<string, JsonElement>))] | ||||
|     internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     [GeneratedRegex("([A-Z]+(.))")] | ||||
|     private static partial Regex UpperCase(); | ||||
|  | ||||
|     [GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")] | ||||
|     private static partial Regex HtmlColor(); | ||||
|  | ||||
|     [GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")] | ||||
|     private static partial Regex InvalidCharacter(); | ||||
|  | ||||
|     private static MarkdownExtra GetMarkdownExtra(MarkdownFileAndLines markdownFileAndLines) | ||||
|     { | ||||
|         MarkdownExtra result; | ||||
| @ -301,10 +325,10 @@ internal static partial class HelperMarkdown | ||||
|             if (!directoryInfo.Exists || (!string.IsNullOrEmpty(directoryInfo.LinkTarget) && !Directory.Exists(directoryInfo.LinkTarget))) | ||||
|                 continue; | ||||
|             collection.AddRange(GetFiles(appSettings, directoryInfo, SearchOption.TopDirectoryOnly)); | ||||
|             foreach (FileInfo file in collection) | ||||
|                 results.Add(file); | ||||
|             foreach (FileInfo fileInfo in collection) | ||||
|                 results.Add(fileInfo); | ||||
|         } | ||||
|         return new(results); | ||||
|         return (from l in results orderby l.FullName select l).ToArray().AsReadOnly(); | ||||
|     } | ||||
|  | ||||
|     internal static LineNumber GetLineNumbers(FileInfo fileInfo) | ||||
| @ -317,6 +341,8 @@ internal static partial class HelperMarkdown | ||||
|         int? updatedLineNumber = null; | ||||
|         int? progressLineNumber = null; | ||||
|         int? completedLineNumber = null; | ||||
|         int? startedColumnsLineNumber = null; | ||||
|         int? completedColumnsLineNumber = null; | ||||
|         int? frontMatterYamlEndLineNumber = null; | ||||
|         Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default; | ||||
|         string[] lines = File.ReadAllLines(fileInfo.FullName, encoding); | ||||
| @ -362,21 +388,33 @@ internal static partial class HelperMarkdown | ||||
|                 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] == ' ') | ||||
|             { | ||||
|                 h1LineNumber = i; | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|         LineNumber lineNumber = new(createdLineNumber, | ||||
|                                     completedLineNumber, | ||||
|                                     h1LineNumber, | ||||
|                                     frontMatterYamlEndLineNumber, | ||||
|                                     lines.AsReadOnly(), | ||||
|                                     progressLineNumber, | ||||
|                                     statusLineNumber, | ||||
|                                     typeLineNumber, | ||||
|                                     updatedLineNumber); | ||||
|         LineNumber lineNumber = new(Created: createdLineNumber, | ||||
|                                     Completed: completedLineNumber, | ||||
|                                     CompletedColumns: completedColumnsLineNumber, | ||||
|                                     H1: h1LineNumber, | ||||
|                                     FrontMatterYamlEnd: frontMatterYamlEndLineNumber, | ||||
|                                     Lines: lines.AsReadOnly(), | ||||
|                                     Progress: progressLineNumber, | ||||
|                                     Status: statusLineNumber, | ||||
|                                     StartedColumns: startedColumnsLineNumber, | ||||
|                                     Type: typeLineNumber, | ||||
|                                     Updated: updatedLineNumber); | ||||
|         return lineNumber; | ||||
|     } | ||||
|  | ||||
| @ -511,7 +549,7 @@ internal static partial class HelperMarkdown | ||||
|                     h1Check = $"# {h1[2..]}"; | ||||
|                     if (h1Check.Length == h1.Length && h1Check != h1) | ||||
|                     { | ||||
|                         if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|                         if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                             continue; | ||||
|                         lines[markdownFile.LineNumber.H1.Value] = h1Check; | ||||
|                         File.WriteAllLines(markdownFile.File, lines); | ||||
| @ -527,7 +565,7 @@ internal static partial class HelperMarkdown | ||||
|             checkName = Path.Combine(markdownFile.Directory, checkFileName); | ||||
|             if (checkName == markdownFile.File) | ||||
|                 continue; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             File.Move(markdownFile.File, checkName); | ||||
|             result += 1; | ||||
| @ -644,6 +682,7 @@ internal static partial class HelperMarkdown | ||||
|         Input result; | ||||
|         string? destination = null; | ||||
|         bool useProcessStart = false; | ||||
|         string? replaceWithTitle = null; | ||||
|         string source = Path.GetFullPath(args[0]); | ||||
|         for (int i = 1; i < args.Count; i++) | ||||
|         { | ||||
| @ -653,6 +692,8 @@ internal static partial class HelperMarkdown | ||||
|                     useProcessStart = args[i + 1] == "true"; | ||||
|                 else if (args[i][1] == 'd') | ||||
|                     destination = Path.GetFullPath(args[i + 1]); | ||||
|                 else if (args[i][1] == 't') | ||||
|                     replaceWithTitle = args[i + 1]; | ||||
|                 i++; | ||||
|             } | ||||
|         } | ||||
| @ -664,11 +705,11 @@ internal static partial class HelperMarkdown | ||||
|             if (!Directory.Exists(destination)) | ||||
|                 _ = Directory.CreateDirectory(destination); | ||||
|         } | ||||
|         result = new(destination, source, useProcessStart); | ||||
|         result = new(Destination: destination, Source: source, ReplaceWithTitle: replaceWithTitle, UseProcessStart: useProcessStart); | ||||
|         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 = []; | ||||
|         string h1; | ||||
| @ -682,17 +723,22 @@ internal static partial class HelperMarkdown | ||||
|         string fileNameWithoutExtension; | ||||
|         ReadOnlyCollection<string> lines; | ||||
|         ReadOnlyDictionary<string, object> frontMatterYaml; | ||||
|         bool isGitOthersModifiedAndDeletedExcludingStandard; | ||||
|         bool? isGitOthersModifiedAndDeletedExcludingStandard; | ||||
|         ReadOnlyCollection<FileInfo> files = GetFiles(appSettings, input); | ||||
|         foreach (FileInfo fileInfo in files) | ||||
|         { // cSpell:disable | ||||
|             if (fileInfo.DirectoryName is null) | ||||
|                 continue; | ||||
|             key = Path.GetRelativePath(input.Source, fileInfo.FullName); | ||||
|             isWithinSource = fileInfo.FullName.Contains(input.Source); | ||||
|             isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName); | ||||
|             if (!isWithinSource && results.ContainsKey(key)) | ||||
|                 continue; | ||||
|             if (gitOthersModifiedAndDeletedExcludingStandardFiles is null) | ||||
|                 isGitOthersModifiedAndDeletedExcludingStandard = null; | ||||
|             else | ||||
|             { | ||||
|                 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); | ||||
| @ -702,7 +748,7 @@ internal static partial class HelperMarkdown | ||||
|                 (type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber); | ||||
|             else | ||||
|             { | ||||
|                 if (!isGitOthersModifiedAndDeletedExcludingStandard) | ||||
|                 if (isGitOthersModifiedAndDeletedExcludingStandard is not null && !isGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                     continue; | ||||
|                 type = appSettings.DefaultNoteType; | ||||
|                 File.WriteAllLines(fileInfo.FullName, ["---", $"type: {type}\"", "---", string.Empty, $"# {h1}"]); | ||||
| @ -710,20 +756,20 @@ internal static partial class HelperMarkdown | ||||
|             } | ||||
|             isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md"); | ||||
|             isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase); | ||||
|             markdownFile = new(fileInfo.CreationTime, | ||||
|                                fileInfo.DirectoryName, | ||||
|                                fileInfo.Extension, | ||||
|                                fileInfo.FullName, | ||||
|                                fileInfo.Name, | ||||
|                                fileNameWithoutExtension, | ||||
|                                frontMatterYaml, | ||||
|                                h1, | ||||
|                                isGitOthersModifiedAndDeletedExcludingStandard, | ||||
|                                isKanbanIndex, | ||||
|                                isKanbanMarkdown, | ||||
|                                fileInfo.LastWriteTime, | ||||
|                                lineNumber, | ||||
|                                type); | ||||
|             markdownFile = new(CreationDateTime: fileInfo.CreationTime, | ||||
|                                Directory: fileInfo.DirectoryName, | ||||
|                                Extension: fileInfo.Extension, | ||||
|                                File: fileInfo.FullName, | ||||
|                                FileName: fileInfo.Name, | ||||
|                                FileNameWithoutExtension: fileNameWithoutExtension, | ||||
|                                FrontMatterYaml: frontMatterYaml, | ||||
|                                H1: h1, | ||||
|                                IsGitOthersModifiedAndDeletedExcludingStandard: isGitOthersModifiedAndDeletedExcludingStandard, | ||||
|                                IsKanbanIndex: isKanbanIndex, | ||||
|                                IsKanbanMarkdown: isKanbanMarkdown, | ||||
|                                LastWriteDateTime: fileInfo.LastWriteTime, | ||||
|                                LineNumber: lineNumber, | ||||
|                                Type: type); | ||||
|             results.Add(key, new(markdownFile, lines.ToArray())); | ||||
|         } // cSpell:restore | ||||
|         return new(results); | ||||
| @ -749,7 +795,7 @@ internal static partial class HelperMarkdown | ||||
|             markdownFile = relativeTo.Value.MarkdownFile; | ||||
|             if (markdownFile.IsKanbanMarkdown) | ||||
|                 continue; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             if (markdownFile.LineNumber.FrontMatterYamlEnd is null) | ||||
|                 continue; | ||||
| @ -783,7 +829,7 @@ internal static partial class HelperMarkdown | ||||
|             circularReference = false; | ||||
|             lines = relativeTo.Value.Lines; | ||||
|             markdownFile = relativeTo.Value.MarkdownFile; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             for (int i = 0; i < lines.Length; i++) | ||||
|             { | ||||
| @ -845,7 +891,7 @@ internal static partial class HelperMarkdown | ||||
|             found = false; | ||||
|             lines = relativeTo.Value.Lines; | ||||
|             markdownFile = relativeTo.Value.MarkdownFile; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             for (int i = 0; i < lines.Length; i++) | ||||
|             { | ||||
| @ -898,7 +944,7 @@ internal static partial class HelperMarkdown | ||||
|             write = false; | ||||
|             lines = relativeTo.Value.Lines; | ||||
|             markdownFile = relativeTo.Value.MarkdownFile; | ||||
|             if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             for (int i = 0; i < lines.Length; i++) | ||||
|             { | ||||
| @ -926,7 +972,7 @@ internal static partial class HelperMarkdown | ||||
|             } | ||||
|             if (!write) | ||||
|                 continue; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             File.WriteAllLines(markdownFile.File, lines); | ||||
|             result += 1; | ||||
| @ -961,7 +1007,7 @@ internal static partial class HelperMarkdown | ||||
|             markdownFile = relativeTo.Value.MarkdownFile; | ||||
|             if (markdownFile.IsKanbanIndex) | ||||
|                 continue; | ||||
|             if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             if (!File.Exists(markdownFile.File)) | ||||
|                 continue; | ||||
| @ -1026,7 +1072,7 @@ internal static partial class HelperMarkdown | ||||
|             } | ||||
|             if (!write) | ||||
|                 continue; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             File.WriteAllLines(markdownFile.File, lines); | ||||
|             result += 1; | ||||
| @ -1043,18 +1089,12 @@ internal static partial class HelperMarkdown | ||||
|         MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection); | ||||
|         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"); | ||||
|             if (File.Exists(jsonFile)) | ||||
|                 File.Delete(jsonFile); | ||||
|             columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines); | ||||
|             if (columnsToCards.Count == 0) | ||||
|                 File.WriteAllText(jsonFile, "{}"); | ||||
|             else | ||||
|             { | ||||
|                 string json = JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard); | ||||
|             string old = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile); | ||||
|             string json = columnsToCards.Count == 0 ? "{}" : JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard); | ||||
|             if (json != old) | ||||
|                 File.WriteAllText(jsonFile, json); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -1108,7 +1148,7 @@ internal static partial class HelperMarkdown | ||||
|                     results.Insert(0, string.Empty); | ||||
|                 } | ||||
|                 results.Insert(0, "---"); | ||||
|                 if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|                 if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 { | ||||
|                     results.Insert(0, updatedLine); | ||||
|                     results.Insert(0, createdLine); | ||||
| @ -1126,7 +1166,7 @@ internal static partial class HelperMarkdown | ||||
|                 } | ||||
|                 if (markdownFile.LineNumber.Type is null) | ||||
|                     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) | ||||
|                         results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine); | ||||
| @ -1162,7 +1202,7 @@ internal static partial class HelperMarkdown | ||||
|             } | ||||
|             if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results)) | ||||
|                 continue; | ||||
|             if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) | ||||
|             if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) | ||||
|                 continue; | ||||
|             File.WriteAllLines(markdownFile.File, results); | ||||
|             File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime); | ||||
| @ -1177,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))); | ||||
|     } | ||||
|  | ||||
|     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) | ||||
|     { | ||||
|         int updated; | ||||
| @ -1253,10 +1263,349 @@ internal static partial class HelperMarkdown | ||||
|         if (!string.IsNullOrEmpty(input.Destination)) | ||||
|         { | ||||
|             SaveColumnToCards(input, relativeToCollection); | ||||
|             List<string> taskArgumentsForDayHelper20240623 = GetTaskArgumentsForDayHelper20240623(input); | ||||
|             if (taskArgumentsForDayHelper20240623.Count > 0) | ||||
|                 HelperDay.Select(appSettings, logger, taskArgumentsForDayHelper20240623, cancellationToken: CancellationToken.None); | ||||
|             SaveTablesToJson(appSettings, logger, input); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -10,6 +10,11 @@ internal static class HelperPdfStripperWrapper | ||||
|                          string? Key, | ||||
|                          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) | ||||
|     { | ||||
|         string result; | ||||
| @ -28,14 +33,27 @@ internal static class HelperPdfStripperWrapper | ||||
|         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) | ||||
|     { | ||||
|         string result; | ||||
|         if (File.Exists(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}\""; | ||||
|         //Process process = Process.Start(configData.LincPDFCFileName, arguments); | ||||
|         Process? process = Process.Start(ghostPCLFileName, arguments); | ||||
|         _ = process?.WaitForExit(30000); | ||||
|         if (!File.Exists(destinationFileName)) | ||||
| @ -83,11 +101,11 @@ internal static class HelperPdfStripperWrapper | ||||
|             d = Path.Combine(d, directoryName); | ||||
|         if (!string.IsNullOrEmpty(d) && !Directory.Exists(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); | ||||
|     } | ||||
|  | ||||
|     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 destinationFileName; | ||||
| @ -96,12 +114,14 @@ internal static class HelperPdfStripperWrapper | ||||
|         foreach (string file in files) | ||||
|         { | ||||
|             destinationFileName = Path.Combine(destinationDirectory, $"{file}.txt"); | ||||
|             if (parser == 'S') | ||||
|                 text = GetTextFromPDF(pdfTextStripperFileName, file, destinationFileName); | ||||
|             else if (parser == 'G') | ||||
|                 text = GetGhostTextFromPDF(ghostPCLFileName, file, destinationFileName); | ||||
|             else if (parser == 'K') | ||||
|             if (parser == _Kofax) | ||||
|                 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 | ||||
|                 throw new NotImplementedException(); | ||||
|             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) | ||||
|     { | ||||
|         string pdfTextStripperFileName = Path.Combine(AppContext.BaseDirectory, "PDF-Text-Stripper.exe"); | ||||
|         if (!File.Exists(pdfTextStripperFileName)) | ||||
|             log.LogInformation("exe <{pdfTextStripperFileName}> doesn't exist!", pdfTextStripperFileName); | ||||
|         // <ItemGroup> | ||||
|         //     <PackageReference Include="Infineon.Mesa.PDF.Text.Stripper" Version="4.8.0.1"><NoWarn>NU1701</NoWarn></PackageReference> | ||||
|         // </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 | ||||
|         { | ||||
|             string ghostPCLFileName = Path.Combine(AppContext.BaseDirectory, "gPcl6win64.exe"); | ||||
|             if (!File.Exists(ghostPCLFileName)) | ||||
|                 log.LogInformation("exe <{ghostPCLFileName}> doesn't exist!", ghostPCLFileName); | ||||
|             if (input.Parser is null) | ||||
|                 log.LogInformation("-p <{Parser}> wasn't supplied!", nameof(input.Parser)); | ||||
|             else | ||||
|             { | ||||
|                 string kofaxFileName = "C:/Program Files (x86)/Kofax/Power PDF 50/batchConverter.com"; | ||||
|                 if (!File.Exists(kofaxFileName)) | ||||
|                     log.LogInformation("exe <{kofaxFileName}> doesn't exist!", kofaxFileName); | ||||
|                 string pdfTextStripperFileName = Path.Combine(AppContext.BaseDirectory, "PDF-Text-Stripper.exe"); | ||||
|                 if (input.Parser == _Stripper && !File.Exists(pdfTextStripperFileName)) | ||||
|                     log.LogInformation("exe <{pdfTextStripperFileName}> doesn't exist!", pdfTextStripperFileName); | ||||
|                 else | ||||
|                 { | ||||
|                     Input input = GetInput(args); | ||||
|                     if (string.IsNullOrEmpty(input.DestinationDirectory)) | ||||
|                         log.LogInformation("-d <{DestinationDirectory}> wasn't supplied!", nameof(input.DestinationDirectory)); | ||||
|                     string ghostPCLFileName = Path.Combine(AppContext.BaseDirectory, "gPcl6win64.exe"); | ||||
|                     if (input.Parser == _Ghost && !File.Exists(ghostPCLFileName)) | ||||
|                         log.LogInformation("exe <{ghostPCLFileName}> doesn't exist!", ghostPCLFileName); | ||||
|                     else | ||||
|                     { | ||||
|                         if (input.Parser is null) | ||||
|                             log.LogInformation("-p <{Parser}> wasn't supplied!", nameof(input.Parser)); | ||||
|                         string kofaxFileName = "C:/Program Files (x86)/Kofax/Power PDF 50/batchConverter.com"; | ||||
|                         if (input.Parser == _Kofax && !File.Exists(kofaxFileName)) | ||||
|                             log.LogInformation("exe <{kofaxFileName}> doesn't exist!", kofaxFileName); | ||||
|                         else | ||||
|                         { | ||||
|                             string[] files = Directory.GetFiles(args[0], "*.pdf", SearchOption.TopDirectoryOnly); | ||||
|                             if (files.Length == 0) | ||||
|                                 log.LogInformation("Length == {length}", files.Length); | ||||
|                             string lincPDFCFileName = Path.Combine(AppContext.BaseDirectory, "LincPDFC.exe"); | ||||
|                             if (input.Parser == _Kofax && !File.Exists(lincPDFCFileName)) | ||||
|                                 log.LogInformation("exe <{lincPDFCFileName}> doesn't exist!", lincPDFCFileName); | ||||
|                             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); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @ -1,24 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record AviDirectory(DateTime? DateTimeOriginal, | ||||
|                            string? Duration, | ||||
|                            string? Height, | ||||
|                            string? Width) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, AviDirectorySourceGenerationContext.Default.AviDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(AviDirectory))] | ||||
| public partial class AviDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,36 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record ExifDirectory(AviDirectory[] AviDirectories, | ||||
|                             ExifDirectoryBase[] ExifBaseDirectories, | ||||
|                             FileMetadataDirectory[] FileMetadataDirectories, | ||||
|                             GifHeaderDirectory[] GifHeaderDirectories, | ||||
|                             GpsDirectory[] GpsDirectories, | ||||
|                             int? Height, | ||||
|                             int? Id, | ||||
|                             JpegDirectory[] JpegDirectories, | ||||
|                             MakernoteDirectory[] MakernoteDirectories, | ||||
|                             string OriginalFileName, | ||||
|                             PhotoshopDirectory[] PhotoshopDirectories, | ||||
|                             PngDirectory[] PngDirectories, | ||||
|                             QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories, | ||||
|                             QuickTimeTrackHeaderDirectory[] QuickTimeTrackHeaderDirectories, | ||||
|                             WebPDirectory[] WebPDirectories, | ||||
|                             int? Width) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, ExifDirectorySourceGenerationContext.Default.ExifDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
| [JsonSerializable(typeof(ExifDirectory))] | ||||
| public partial class ExifDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,66 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record ExifDirectoryBase(string? Aperture, | ||||
|                                 string? ApplicationNotes, | ||||
|                                 string? Artist, | ||||
|                                 string? BitsPerSample, | ||||
|                                 string? BodySerialNumber, | ||||
|                                 string? CameraOwnerName, | ||||
|                                 string? CompressedAverageBitsPerPixel, | ||||
|                                 string? Compression, | ||||
|                                 string? Copyright, | ||||
|                                 DateTime? DateTime, | ||||
|                                 DateTime? DateTimeDigitized, | ||||
|                                 DateTime? DateTimeOriginal, | ||||
|                                 string? DocumentName, | ||||
|                                 string? ExifVersion, | ||||
|                                 string? ExposureTime, | ||||
|                                 string? FileSource, | ||||
|                                 string? ImageDescription, | ||||
|                                 string? ImageHeight, | ||||
|                                 string? ImageNumber, | ||||
|                                 string? ImageUniqueId, | ||||
|                                 string? ImageWidth, | ||||
|                                 string? IsoSpeed, | ||||
|                                 string? LensMake, | ||||
|                                 string? LensModel, | ||||
|                                 string? LensSerialNumber, | ||||
|                                 string? Make, | ||||
|                                 string? MakerNote, | ||||
|                                 string? Model, | ||||
|                                 string? Orientation, | ||||
|                                 int? OrientationValue, | ||||
|                                 string? Rating, | ||||
|                                 string? RatingPercent, | ||||
|                                 string? SecurityClassification, | ||||
|                                 string? ShutterSpeed, | ||||
|                                 string? Software, | ||||
|                                 string? TimeZone, | ||||
|                                 string? TimeZoneDigitized, | ||||
|                                 string? TimeZoneOriginal, | ||||
|                                 string? UserComment, | ||||
|                                 string? WinAuthor, | ||||
|                                 string? WinComment, | ||||
|                                 string? WinKeywords, | ||||
|                                 string? WinSubject, | ||||
|                                 string? WinTitle, | ||||
|                                 string? XResolution, | ||||
|                                 string? YResolution) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(ExifDirectoryBase))] | ||||
| public partial class ExifDirectoryBaseSourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record FileMetadataDirectory(DateTime? FileModifiedDate, | ||||
|                                     string? FileName, | ||||
|                                     string? FileSize) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, FileMetadataDirectorySourceGenerationContext.Default.FileMetadataDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(FileMetadataDirectory))] | ||||
| public partial class FileMetadataDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record GifHeaderDirectory(string? ImageHeight, | ||||
|                                  string? ImageWidth) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, GifHeaderDirectorySourceGenerationContext.Default.GifHeaderDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(GifHeaderDirectory))] | ||||
| public partial class GifHeaderDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record GpsDirectory(string? Altitude, | ||||
|                            string? Latitude, | ||||
|                            string? LatitudeRef, | ||||
|                            string? Longitude, | ||||
|                            string? LongitudeRef, | ||||
|                            DateTime? TimeStamp) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, GpsDirectorySourceGenerationContext.Default.GpsDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(GpsDirectory))] | ||||
| public partial class GpsDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record JpegDirectory(string? ImageHeight, | ||||
|                             string? ImageWidth) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, JpegDirectorySourceGenerationContext.Default.JpegDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(JpegDirectory))] | ||||
| public partial class JpegDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record MakernoteDirectory(string? CameraSerialNumber, | ||||
|                                  string? FirmwareVersion, | ||||
|                                  string? QualityAndFileFormat) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, MakernoteDirectorySourceGenerationContext.Default.MakernoteDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(MakernoteDirectory))] | ||||
| public partial class MakernoteDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record PhotoshopDirectory(string? JpegQuality, | ||||
|                                  string? Url) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, PhotoshopDirectorySourceGenerationContext.Default.PhotoshopDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(PhotoshopDirectory))] | ||||
| public partial class PhotoshopDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record PngDirectory(string? ImageHeight, | ||||
|                            string? ImageWidth, | ||||
|                            string? TextualData) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, PngDirectorySourceGenerationContext.Default.PngDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(PngDirectory))] | ||||
| public partial class PngDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record QuickTimeMovieHeaderDirectory(DateTime? Created) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, QuickTimeMovieHeaderDirectorySourceGenerationContext.Default.QuickTimeMovieHeaderDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(QuickTimeMovieHeaderDirectory))] | ||||
| public partial class QuickTimeMovieHeaderDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record QuickTimeTrackHeaderDirectory(DateTime? Created) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, QuickTimeTrackHeaderDirectorySourceGenerationContext.Default.QuickTimeTrackHeaderDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(QuickTimeTrackHeaderDirectory))] | ||||
| public partial class QuickTimeTrackHeaderDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Exif; | ||||
|  | ||||
| public record WebPDirectory(string? ImageHeight, | ||||
|                             string? ImageWidth) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, WebPDirectorySourceGenerationContext.Default.WebPDirectory); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(WebPDirectory))] | ||||
| public partial class WebPDirectorySourceGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| public record FaceEncoding(double[] RawEncoding, int Size); | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = false)] | ||||
| [JsonSerializable(typeof(FaceEncoding))] | ||||
| public partial class FaceEncodingGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| public record FaceFile(int? AreaPermyriad, | ||||
|                        int? ConfidencePercent, | ||||
|                        string? DMS, | ||||
|                        DateTime DateTime, | ||||
|                        FaceEncoding? FaceEncoding, | ||||
|                        Dictionary<FacePart, FacePoint[]>? FaceParts, | ||||
|                        Location? Location, | ||||
|                        string? Maker, | ||||
|                        MappingFromPerson? MappingFromPerson, | ||||
|                        string? Model, | ||||
|                        OutputResolution? OutputResolution); | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = false)] | ||||
| [JsonSerializable(typeof(FaceFile))] | ||||
| public partial class FaceFileGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
| [JsonSerializable(typeof(FaceFile[]))] | ||||
| public partial class FaceFileCollectionGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] | ||||
| [JsonSerializable(typeof(FaceFile[]))] | ||||
| public partial class FaceFileCollectionWriteIndentedGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| /// <summary> | ||||
| /// Specifies the part of face. | ||||
| /// </summary> | ||||
| public enum FacePart | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the chin. | ||||
|     /// </summary> | ||||
|     Chin = 0, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the left eyebrow. | ||||
|     /// </summary> | ||||
|     LeftEyebrow = 17, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the right eyebrow. | ||||
|     /// </summary> | ||||
|     RightEyebrow = 22, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the nose bridge. | ||||
|     /// </summary> | ||||
|     NoseBridge = 27, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the nose tip. | ||||
|     /// </summary> | ||||
|     NoseTip = 31, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the left eye. | ||||
|     /// </summary> | ||||
|     LeftEye = 36, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the right eye. | ||||
|     /// </summary> | ||||
|     RightEye = 42, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the top lip. | ||||
|     /// </summary> | ||||
|     TopLip = 48, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Specifies the bottom lip. | ||||
|     /// </summary> | ||||
|     BottomLip = 55 | ||||
|  | ||||
| } | ||||
| @ -1,39 +0,0 @@ | ||||
| using System.Drawing; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| [method: JsonConstructor] | ||||
| public class FacePoint(int index, int x, int y) | ||||
| { | ||||
|     public int Index { get; } = index; | ||||
|     public int X { get; } = x; | ||||
|     public int Y { get; } = y; | ||||
|  | ||||
|     private readonly Point _Point = new(x, y); | ||||
|  | ||||
|     public override bool Equals(object? obj) => obj is FacePoint point && Equals(point); | ||||
|  | ||||
| #pragma warning disable IDE0070 | ||||
|     public override int GetHashCode() | ||||
| #pragma warning restore IDE0070 | ||||
|     { | ||||
|         int hashCode = 1861411795; | ||||
|         hashCode = (hashCode * -1521134295) + _Point.GetHashCode(); | ||||
|         hashCode = (hashCode * -1521134295) + Index.GetHashCode(); | ||||
|         return hashCode; | ||||
|     } | ||||
|  | ||||
|     public bool Equals(FacePoint? facePoint) | ||||
|     { | ||||
|         return facePoint is not null | ||||
|             && X == facePoint.X | ||||
|             && Y == facePoint.Y | ||||
|             && Index == facePoint.Index; | ||||
|     } | ||||
|  | ||||
|     public static bool operator ==(FacePoint point1, FacePoint point2) => point1.Equals(point2); | ||||
|  | ||||
|     public static bool operator !=(FacePoint point1, FacePoint point2) => !(point1 == point2); | ||||
|  | ||||
| } | ||||
| @ -1,42 +0,0 @@ | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| [method: JsonConstructor] | ||||
| public class Location(int bottom, double confidence, int left, int right, int top) : IEquatable<Location> | ||||
| { | ||||
|  | ||||
|     public int Bottom { init; get; } = bottom; | ||||
|     public double Confidence { init; get; } = confidence; | ||||
|     public int Left { init; get; } = left; | ||||
|     public int Right { init; get; } = right; | ||||
|     public int Top { init; get; } = top; | ||||
|  | ||||
|     public override bool Equals(object? obj) => Equals(obj as Location); | ||||
|  | ||||
| #pragma warning disable IDE0070 | ||||
|     public override int GetHashCode() | ||||
| #pragma warning restore IDE0070 | ||||
|     { | ||||
|         int hashCode = -773114317; | ||||
|         hashCode = (hashCode * -1521134295) + Bottom.GetHashCode(); | ||||
|         hashCode = (hashCode * -1521134295) + Left.GetHashCode(); | ||||
|         hashCode = (hashCode * -1521134295) + Right.GetHashCode(); | ||||
|         hashCode = (hashCode * -1521134295) + Top.GetHashCode(); | ||||
|         return hashCode; | ||||
|     } | ||||
|  | ||||
|     public bool Equals(Location? location) | ||||
|     { | ||||
|         return location is not null | ||||
|             && Bottom == location.Bottom | ||||
|             && Left == location.Left | ||||
|             && Right == location.Right | ||||
|             && Top == location.Top; | ||||
|     } | ||||
|  | ||||
|     public static bool operator ==(Location location1, Location location2) => EqualityComparer<Location>.Default.Equals(location1, location2); | ||||
|  | ||||
|     public static bool operator !=(Location location1, Location location2) => !(location1 == location2); | ||||
|  | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| public record MappingFromPerson(int? ApproximateYears, | ||||
|                                 string DisplayDirectoryName, | ||||
|                                 long PersonKey, | ||||
|                                 string SegmentB) | ||||
| { | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, MappingFromPersonGenerationContext.Default.MappingFromPerson); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(MappingFromPerson))] | ||||
| public partial class MappingFromPersonGenerationContext : JsonSerializerContext | ||||
| { | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| namespace File_Folder_Helper.Models.Face; | ||||
|  | ||||
| public record OutputResolution(int Height, | ||||
|                                int Orientation, | ||||
|                                int Width); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user