< Summary

Information
Class: LGDXRobotCloud.API.Services.Navigation.WaypointsTraffic
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Navigation/MapEditorService.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 2
Coverable lines: 2
Total lines: 177
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Waypoints()100%210%
get_WaypointTraffics()100%210%

File(s)

/builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Navigation/MapEditorService.cs

#LineLine coverage
 1using LGDXRobotCloud.API.Exceptions;
 2using LGDXRobotCloud.API.Services.Administration;
 3using LGDXRobotCloud.Data.DbContexts;
 4using LGDXRobotCloud.Data.Entities;
 5using LGDXRobotCloud.Data.Models.Business.Administration;
 6using LGDXRobotCloud.Data.Models.Business.Navigation;
 7using LGDXRobotCloud.Utilities.Enums;
 8using Microsoft.EntityFrameworkCore;
 9using Microsoft.Extensions.Caching.Memory;
 10
 11namespace LGDXRobotCloud.API.Services.Navigation;
 12
 13public record WaypointsTraffic
 14{
 015  public Dictionary<int, Waypoint> Waypoints { get; set; } = null!;
 016  public Dictionary<int, HashSet<int>> WaypointTraffics { get; set; } = null!;
 17}
 18
 19public interface IMapEditorService
 20{
 21  Task<MapEditorBusinessModel> GetMapAsync(int realmId);
 22  Task<bool> UpdateMapAsync(int realmId, MapEditorUpdateBusinessModel MapEditUpdateBusinessModel);
 23
 24  Task<WaypointsTraffic> GetWaypointTrafficAsync(int realmId);
 25}
 26
 27public class MapEditorService(
 28    IActivityLogService activityLogService,
 29    IMemoryCache memoryCache,
 30    LgdxContext context
 31  ) : IMapEditorService
 32{
 33  private readonly IActivityLogService _activityLogService = activityLogService ?? throw new ArgumentNullException(nameo
 34  private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
 35  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 36
 37
 38  public async Task<MapEditorBusinessModel> GetMapAsync(int realmId)
 39  {
 40    // Check if realm exists
 41    var realm = await _context.Realms.AsNoTracking()
 42      .Where(r => r.Id == realmId)
 43      .FirstOrDefaultAsync()
 44        ?? throw new LgdxNotFound404Exception();
 45
 46    var waypoints = await _context.Waypoints.AsNoTracking()
 47      .Where(w => w.RealmId == realmId)
 48      .Select(w => new WaypointListBusinessModel
 49      {
 50        Id = w.Id,
 51        Name = w.Name,
 52        RealmId = w.RealmId,
 53        RealmName = w.Realm.Name,
 54        X = w.X,
 55        Y = w.Y,
 56        Rotation = w.Rotation,
 57      })
 58      .ToListAsync();
 59    var waypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 60      .Where(w => w.RealmId == realmId)
 61      .Select(w => new WaypointTrafficBusinessModel
 62      {
 63        Id = w.Id,
 64        WaypointFromId = w.WaypointFromId,
 65        WaypointToId = w.WaypointToId,
 66      })
 67      .ToListAsync();
 68    return new MapEditorBusinessModel
 69    {
 70      Waypoints = waypoints,
 71      WaypointTraffics = waypointTraffics,
 72    };
 73  }
 74
 75  public async Task<bool> UpdateMapAsync(int realmId, MapEditorUpdateBusinessModel mapEditorUpdateBusinessModel)
 76  {
 77    _memoryCache.Remove($"MapEditorService_InternalWaypointsTraffic_{realmId}");
 78
 79    // Sort traffics
 80    var inputWaypointTraffics = mapEditorUpdateBusinessModel.WaypointTraffics
 81      .OrderBy(w => w.WaypointFromId)
 82      .ThenBy(w => w.WaypointToId)
 83      .ToList();
 84
 85    // Get all traffics
 86    var databaseWaypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 87      .Where(w => w.RealmId == realmId)
 88      .OrderBy(w => w.WaypointFromId)
 89      .ThenBy(w => w.WaypointToId)
 90      .ToListAsync();
 91
 92    // Get traffic to add and remove
 93    List<WaypointTraffic> trafficsToAdd = [];
 94    int i = 0;
 95    int j = 0;
 96    while (i < inputWaypointTraffics.Count && j < databaseWaypointTraffics.Count)
 97    {
 98      // Same traffic, remove from list
 99      if (inputWaypointTraffics[i].WaypointFromId == databaseWaypointTraffics[j].WaypointFromId
 100        && inputWaypointTraffics[i].WaypointToId == databaseWaypointTraffics[j].WaypointToId)
 101      {
 102        inputWaypointTraffics.RemoveAt(i);
 103        databaseWaypointTraffics.RemoveAt(j);
 104      }
 105      else
 106      {
 107        if (inputWaypointTraffics[i].WaypointFromId > databaseWaypointTraffics[j].WaypointFromId
 108          && inputWaypointTraffics[i].WaypointToId > databaseWaypointTraffics[j].WaypointToId)
 109        {
 110          j++;
 111        }
 112        else
 113        {
 114          i++;
 115        }
 116      }
 117    }
 118
 119    // inputWaypointTraffics contains the traffics to add
 120    await _context.WaypointTraffics.AddRangeAsync(inputWaypointTraffics.Select(w => new WaypointTraffic
 121    {
 122      RealmId = realmId,
 123      WaypointFromId = w.WaypointFromId,
 124      WaypointToId = w.WaypointToId,
 125    }));
 126    // databaseWaypointTraffics contains the traffics to remove
 127    _context.WaypointTraffics.RemoveRange(databaseWaypointTraffics);
 128    await _context.SaveChangesAsync();
 129
 130    await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 131    {
 132      EntityName = nameof(Realm),
 133      EntityId = realmId.ToString(),
 134      Action = ActivityAction.RealmTrafficUpdated,
 135    });
 136
 137    return true;
 138  }
 139
 140  public async Task<WaypointsTraffic> GetWaypointTrafficAsync(int realmId)
 141  {
 142    if (_memoryCache.TryGetValue($"MapEditorService_InternalWaypointsTraffic_{realmId}", out WaypointsTraffic? t))
 143    {
 144      return t ?? new();
 145    }
 146
 147    var waypoints = await _context.Waypoints.AsNoTracking()
 148      .Where(w => w.RealmId == realmId)
 149      .ToListAsync();
 150    var waypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 151      .Where(w => w.RealmId == realmId)
 152      .ToListAsync();
 153
 154    var waypointsDict = waypoints.ToDictionary(w => w.Id);
 155    var waypointTrafficsDict = new Dictionary<int, HashSet<int>>();
 156    foreach (var traffic in waypointTraffics)
 157    {
 158      if (waypointTrafficsDict.TryGetValue(traffic.WaypointFromId, out HashSet<int>? neighbors))
 159      {
 160        neighbors.Add(traffic.WaypointToId);
 161        waypointTrafficsDict[traffic.WaypointFromId] = neighbors;
 162      }
 163      else
 164      {
 165        waypointTrafficsDict[traffic.WaypointFromId] = [traffic.WaypointToId];
 166      }
 167    }
 168
 169    var internalWaypointsTraffic = new WaypointsTraffic
 170    {
 171      Waypoints = waypointsDict,
 172      WaypointTraffics = waypointTrafficsDict,
 173    };
 174    _memoryCache.Set($"MapEditorService_InternalWaypointsTraffic_{realmId}", internalWaypointsTraffic);
 175    return internalWaypointsTraffic;
 176  }
 177}