Вытащил солюшен на уровень выше, чтобы прощё было дотнетить
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/push Build is failing
				
			This commit is contained in:
		
							
								
								
									
										130
									
								
								Infrastructure/EntityToExcelTemplateWriter/ExcelWriter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								Infrastructure/EntityToExcelTemplateWriter/ExcelWriter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| using System.Reflection; | ||||
| using System.Text; | ||||
| using ApplicationLayer.Services.VisaApplications.NeededServices; | ||||
| using DocumentFormat.OpenXml.Packaging; | ||||
| using DocumentFormat.OpenXml.Spreadsheet; | ||||
|  | ||||
| namespace Infrastructure.EntityToExcelTemplateWriter | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Writes object to excel using template.xlsx file and reflections | ||||
|     /// </summary> | ||||
|     public class ExcelWriter : IEntityWriter | ||||
|     { | ||||
|         private const char InsertionSymbol = '$'; | ||||
|         private readonly char[] endChars = [',', ';']; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Write object to stream in Excel table format | ||||
|         /// </summary> | ||||
|         /// <param name="entity"> object to write </param> | ||||
|         /// <param name="cancellationToken"> cancellation token </param> | ||||
|         /// <returns> Stream with template.xlsx file with replaced entries like '$EntityPropName.AnotherProp' </returns> | ||||
|         /// <exception cref="NullReferenceException"> thrown when template file is incorrect </exception> | ||||
|         /// <exception cref="InvalidOperationException"> thrown if any property path in template is incorrect</exception> | ||||
|         public async Task<Stream> WriteEntityToStream(object entity, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var outStream = new MemoryStream(); | ||||
|             await using (var stream = File.Open("template.xlsx", FileMode.Open, FileAccess.Read)) | ||||
|             { | ||||
|                 await stream.CopyToAsync(outStream, cancellationToken); | ||||
|             } | ||||
|  | ||||
|             using var spreadsheetDocument = SpreadsheetDocument.Open(outStream, true); | ||||
|  | ||||
|             var workbookPart = spreadsheetDocument.WorkbookPart | ||||
|                                ?? throw new NullReferenceException("There is no workbook part in document"); | ||||
|             var shareStringTable = workbookPart.SharedStringTablePart?.SharedStringTable ?? | ||||
|                                    throw new NullReferenceException("There is no data in document"); | ||||
|             var shareStringTableItems = shareStringTable.Elements<SharedStringItem>().ToArray(); | ||||
|  | ||||
|             foreach (var item in shareStringTableItems) | ||||
|             { | ||||
|                 if (string.IsNullOrEmpty(item.InnerText)) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 var entries = item.InnerText.Split(); | ||||
|                 for (var i = 0; i < entries.Length; i++) | ||||
|                 { | ||||
|                     var entry = entries[i]; | ||||
|                     if (entry.FirstOrDefault() is not InsertionSymbol || entry.Length <= 1) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     entry = entry[1..]; | ||||
|                     var trimmedCount = entry.Length - entry.TrimEnd(endChars).Length; | ||||
|                     var trimmed = entry[^trimmedCount..]; | ||||
|                     entry = entry.TrimEnd(endChars); | ||||
|  | ||||
|                     var memberPath = entry.Split('.'); | ||||
|  | ||||
|                     var value = GetValueFor(entity, memberPath.First()); | ||||
|                     var stringToInsert = "None"; | ||||
|                     foreach (var memberName in memberPath.Skip(1)) | ||||
|                     { | ||||
|                         if (value is null) | ||||
|                         { | ||||
|                             break; | ||||
|                         } | ||||
|  | ||||
|                         value = GetValueFor(value, memberName); | ||||
|                     } | ||||
|  | ||||
|                     if (value is not null) | ||||
|                     { | ||||
|                         switch (value) | ||||
|                         { | ||||
|                             case DateTime date: | ||||
|                                 stringToInsert = date.ToShortDateString(); | ||||
|                                 break; | ||||
|                             case Enum val: | ||||
|                                 var enumString = val.ToString(); | ||||
|                                 var stringBuilder = new StringBuilder(); | ||||
|                                 for (var charIndex = 0; charIndex < enumString.Length - 1; charIndex++) | ||||
|                                 { | ||||
|                                     stringBuilder.Append(enumString[charIndex]); | ||||
|                                     if (char.IsUpper(enumString[charIndex + 1])) | ||||
|                                     { | ||||
|                                         stringBuilder.Append(' '); | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
|                                 stringBuilder.Append(enumString.Last()); | ||||
|  | ||||
|                                 stringToInsert = stringBuilder.ToString(); | ||||
|                                 break; | ||||
|                             default: | ||||
|                                 stringToInsert = value.ToString(); | ||||
|                                 break; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     entries[i] = stringToInsert! + trimmed; | ||||
|                 } | ||||
|  | ||||
|                 item.Text!.Text = string.Join(' ', entries); | ||||
|             } | ||||
|  | ||||
|             spreadsheetDocument.Save(); | ||||
|             return outStream; | ||||
|         } | ||||
|  | ||||
|         private static object? GetValueFor(object entity, string member) | ||||
|         { | ||||
|             var memberInfo = entity.GetType() | ||||
|                                  .GetMembers() | ||||
|                                  .FirstOrDefault(p => p.Name == member) | ||||
|                              ?? throw new InvalidOperationException( | ||||
|                                  $"Invalid member path in document. Not found: {member}"); | ||||
|             return memberInfo switch | ||||
|             { | ||||
|                 PropertyInfo propertyInfo => propertyInfo.GetValue(entity), | ||||
|                 MethodInfo methodInfo => methodInfo.Invoke(entity, []), | ||||
|                 _ => throw new InvalidOperationException("Only properties and methods allowed.") | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user