183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using DataModel.Database;
 | |
| using DataModel.Database.Common;
 | |
| using Microsoft.EntityFrameworkCore;
 | |
| 
 | |
| namespace Processor;
 | |
| 
 | |
| public static class SectionProcessor
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Load one layer of the tree by using the specified depth:
 | |
|     /// </summary>
 | |
|     public static IAsyncEnumerable<Section> LoadLayer(DataContext db, int depth)
 | |
|     {
 | |
|         return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.Id).AsAsyncEnumerable();
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Determine how deep the tree is.
 | |
|     /// </summary>
 | |
|     public static async ValueTask<int> GetDepth(DataContext db)
 | |
|     {
 | |
|         if(!await db.Sections.AnyAsync())
 | |
|         {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         return await db.Sections.MaxAsync(s => s.Depth);
 | |
|     }
 | |
|     
 | |
|     /// <summary>
 | |
|     /// Compute the new sections key and its depth, then store the section in the database.
 | |
|     /// </summary>
 | |
|     public static async Task<ProcessorResult<Section>> AddSection(DataContext db, string text, string? parentKey)
 | |
|     {
 | |
|         // Remove any whitespaces from the section name, regardless of how many e.g. spaces the user typed:
 | |
|         var key = string.Join('_', text.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)).ToUpperInvariant();
 | |
|         
 | |
|         // Check, if this key already exists:
 | |
|         if (await db.Sections.AnyAsync(n => n.DataKey == key))
 | |
|         {
 | |
|             var rng = new Random();
 | |
|             while (await db.Sections.AnyAsync(n => n.DataKey == key))
 | |
|             {
 | |
|                 // Add a random number to the end of the key:
 | |
|                 key += $"_{rng.Next(1, 10_000)}";
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // In the case, when the user adds a section to the root, handle the insert differently:
 | |
|         if (string.IsNullOrWhiteSpace(parentKey))
 | |
|         {
 | |
|             var rootSection = new Section
 | |
|             {
 | |
|                 Depth = 0,
 | |
|                 DataKey = key,
 | |
|                 Parent = null,
 | |
|                 Name = text.Trim(),
 | |
|                 TextElements = new(),
 | |
|             };
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 db.Sections.Add(rootSection);
 | |
|                 await db.SaveChangesAsync();
 | |
|                 return new ProcessorResult<Section>(rootSection);
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 return e.ToProcessorResult<Section>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Read the parent from the database:
 | |
|         var parent = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == parentKey);
 | |
|         if (parent is null)
 | |
|             throw new ArgumentException($"The section's parent with key {parentKey} does not exist in the database.");
 | |
| 
 | |
|         // Add the new section to the database:
 | |
|         var section = new Section
 | |
|         {
 | |
|             Name = text.Trim(),
 | |
|             DataKey = key,
 | |
|             Parent = parent,
 | |
|             TextElements = new(),
 | |
|             Depth = parent.Depth + 1,
 | |
|         };
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             await db.Sections.AddAsync(section);
 | |
|             await db.SaveChangesAsync();
 | |
|             return new ProcessorResult<Section>(section);
 | |
|         }
 | |
|         catch (Exception e)
 | |
|         {
 | |
|             return e.ToProcessorResult<Section>();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static async Task RemoveSection(DataContext db, string selectedKey)
 | |
|     {
 | |
|         // Remove the section from the database:
 | |
|         var section2Delete = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey);
 | |
|         if (section2Delete is null)
 | |
|             throw new ArgumentException($"The section with key {selectedKey} does not exist in the database.");
 | |
|         
 | |
|         // Next, remove all children of the section, and the children's children, etc.:
 | |
|         var children = await db.Sections.Where(n => n.Parent == section2Delete).ToListAsync();
 | |
|         foreach (var child in children)
 | |
|             await RemoveSection(db, child.DataKey);
 | |
| 
 | |
|         db.Sections.Remove(section2Delete);
 | |
|         await db.SaveChangesAsync();
 | |
|     }
 | |
|     
 | |
|     public static async Task<int> NumberChildren(DataContext db, string selectedKey)
 | |
|     {
 | |
|         // Read the section from the database:
 | |
|         var section = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey);
 | |
|         if (section is null)
 | |
|             throw new ArgumentException($"The section with key {selectedKey} does not exist in the database.");
 | |
|         
 | |
|         return await db.Sections.CountAsync(n => n.Parent == section);
 | |
|     }
 | |
| 
 | |
|     public static async Task<ProcessorResult<Section>> RenameSection(DataContext db, string selectedNodeKey, string alteredName)
 | |
|     {
 | |
|         // Read the section from the database:
 | |
|         var section = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedNodeKey);
 | |
|         if (section is null)
 | |
|             throw new ArgumentException($"The section with key {selectedNodeKey} does not exist in the database.");
 | |
|         
 | |
|         // Determine the new key:
 | |
|         var newKey = string.Join('_', alteredName.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)).ToUpperInvariant();
 | |
|         
 | |
|         // Check, if this key already exists:
 | |
|         if (await db.Sections.AnyAsync(n => n.DataKey == newKey))
 | |
|         {
 | |
|             var rng = new Random();
 | |
|             while (await db.Sections.AnyAsync(n => n.DataKey == newKey))
 | |
|             {
 | |
|                 // Add a random number to the end of the key:
 | |
|                 newKey += $"_{rng.Next(1, 10_000)}";
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         section.Name = alteredName;
 | |
|         section.DataKey = newKey;
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             await db.SaveChangesAsync();
 | |
|             return new ProcessorResult<Section>(section);
 | |
|         }
 | |
|         catch (Exception e)
 | |
|         {
 | |
|             return e.ToProcessorResult<Section>();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static Section GetSection(DataContext db, string sectionKey) => db.Sections.First(n => n.DataKey == sectionKey);
 | |
| 
 | |
|     public static async Task<string> GetSectionPath(DataContext db, string sectionKey)
 | |
|     {
 | |
|         var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey);
 | |
|         
 | |
|         // Ensure, that the database loaded the section's parent:
 | |
|         await db.Entry(section).Reference(n => n.Parent).LoadAsync();
 | |
| 
 | |
|         var path = section.Name;
 | |
|         while (section.Parent != null)
 | |
|         {
 | |
|             section = await db.Sections.FirstAsync(n => n.DataKey == section.Parent.DataKey);
 | |
|             
 | |
|             // Ensure, that the database loaded the section's parent:
 | |
|             await db.Entry(section).Reference(n => n.Parent).LoadAsync();
 | |
|             
 | |
|             path = $"{section.Name}/{path}";
 | |
|         }
 | |
|         
 | |
|         return $"Section's path: {path}";
 | |
|     }
 | |
| } |