< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)0%4260%
GetMapAsync()0%620%
UpdateMapAsync()0%156120%
GetWaypointTrafficAsync()0%7280%

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{
 15  public Dictionary<int, Waypoint> Waypoints { get; set; } = null!;
 16  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
 027public class MapEditorService(
 028    IActivityLogService activityLogService,
 029    IMemoryCache memoryCache,
 030    LgdxContext context
 031  ) : IMapEditorService
 32{
 033  private readonly IActivityLogService _activityLogService = activityLogService ?? throw new ArgumentNullException(nameo
 034  private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
 035  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 36
 37
 38  public async Task<MapEditorBusinessModel> GetMapAsync(int realmId)
 039  {
 40    // Check if realm exists
 041    var realm = await _context.Realms.AsNoTracking()
 042      .Where(r => r.Id == realmId)
 043      .FirstOrDefaultAsync()
 044        ?? throw new LgdxNotFound404Exception();
 45
 046    var waypoints = await _context.Waypoints.AsNoTracking()
 047      .Where(w => w.RealmId == realmId)
 048      .Select(w => new WaypointListBusinessModel
 049      {
 050        Id = w.Id,
 051        Name = w.Name,
 052        RealmId = w.RealmId,
 053        RealmName = w.Realm.Name,
 054        X = w.X,
 055        Y = w.Y,
 056        Rotation = w.Rotation,
 057      })
 058      .ToListAsync();
 059    var waypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 060      .Where(w => w.RealmId == realmId)
 061      .Select(w => new WaypointTrafficBusinessModel
 062      {
 063        Id = w.Id,
 064        WaypointFromId = w.WaypointFromId,
 065        WaypointToId = w.WaypointToId,
 066      })
 067      .ToListAsync();
 068    return new MapEditorBusinessModel
 069    {
 070      Waypoints = waypoints,
 071      WaypointTraffics = waypointTraffics,
 072    };
 073  }
 74
 75  public async Task<bool> UpdateMapAsync(int realmId, MapEditorUpdateBusinessModel mapEditorUpdateBusinessModel)
 076  {
 077    _memoryCache.Remove($"MapEditorService_InternalWaypointsTraffic_{realmId}");
 78
 79    // Sort traffics
 080    var inputWaypointTraffics = mapEditorUpdateBusinessModel.WaypointTraffics
 081      .OrderBy(w => w.WaypointFromId)
 082      .ThenBy(w => w.WaypointToId)
 083      .ToList();
 84
 85    // Get all traffics
 086    var databaseWaypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 087      .Where(w => w.RealmId == realmId)
 088      .OrderBy(w => w.WaypointFromId)
 089      .ThenBy(w => w.WaypointToId)
 090      .ToListAsync();
 91
 92    // Get traffic to add and remove
 093    List<WaypointTraffic> trafficsToAdd = [];
 094    int i = 0;
 095    int j = 0;
 096    while (i < inputWaypointTraffics.Count && j < databaseWaypointTraffics.Count)
 097    {
 98      // Same traffic, remove from list
 099      if (inputWaypointTraffics[i].WaypointFromId == databaseWaypointTraffics[j].WaypointFromId
 0100        && inputWaypointTraffics[i].WaypointToId == databaseWaypointTraffics[j].WaypointToId)
 0101      {
 0102        inputWaypointTraffics.RemoveAt(i);
 0103        databaseWaypointTraffics.RemoveAt(j);
 0104      }
 105      else
 0106      {
 0107        if (inputWaypointTraffics[i].WaypointFromId > databaseWaypointTraffics[j].WaypointFromId
 0108          && inputWaypointTraffics[i].WaypointToId > databaseWaypointTraffics[j].WaypointToId)
 0109        {
 0110          j++;
 0111        }
 112        else
 0113        {
 0114          i++;
 0115        }
 0116      }
 0117    }
 118
 119    // inputWaypointTraffics contains the traffics to add
 0120    await _context.WaypointTraffics.AddRangeAsync(inputWaypointTraffics.Select(w => new WaypointTraffic
 0121    {
 0122      RealmId = realmId,
 0123      WaypointFromId = w.WaypointFromId,
 0124      WaypointToId = w.WaypointToId,
 0125    }));
 126    // databaseWaypointTraffics contains the traffics to remove
 0127    _context.WaypointTraffics.RemoveRange(databaseWaypointTraffics);
 0128    await _context.SaveChangesAsync();
 129
 0130    await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 0131    {
 0132      EntityName = nameof(Realm),
 0133      EntityId = realmId.ToString(),
 0134      Action = ActivityAction.RealmTrafficUpdated,
 0135    });
 136
 0137    return true;
 0138  }
 139
 140  public async Task<WaypointsTraffic> GetWaypointTrafficAsync(int realmId)
 0141  {
 0142    if (_memoryCache.TryGetValue($"MapEditorService_InternalWaypointsTraffic_{realmId}", out WaypointsTraffic? t))
 0143    {
 0144      return t ?? new();
 145    }
 146
 0147    var waypoints = await _context.Waypoints.AsNoTracking()
 0148      .Where(w => w.RealmId == realmId)
 0149      .ToListAsync();
 0150    var waypointTraffics = await _context.WaypointTraffics.AsNoTracking()
 0151      .Where(w => w.RealmId == realmId)
 0152      .ToListAsync();
 153
 0154    var waypointsDict = waypoints.ToDictionary(w => w.Id);
 0155    var waypointTrafficsDict = new Dictionary<int, HashSet<int>>();
 0156    foreach (var traffic in waypointTraffics)
 0157    {
 0158      if (waypointTrafficsDict.TryGetValue(traffic.WaypointFromId, out HashSet<int>? neighbors))
 0159      {
 0160        neighbors.Add(traffic.WaypointToId);
 0161        waypointTrafficsDict[traffic.WaypointFromId] = neighbors;
 0162      }
 163      else
 0164      {
 0165        waypointTrafficsDict[traffic.WaypointFromId] = [traffic.WaypointToId];
 0166      }
 0167    }
 168
 0169    var internalWaypointsTraffic = new WaypointsTraffic
 0170    {
 0171      Waypoints = waypointsDict,
 0172      WaypointTraffics = waypointTrafficsDict,
 0173    };
 0174    _memoryCache.Set($"MapEditorService_InternalWaypointsTraffic_{realmId}", internalWaypointsTraffic);
 0175    return internalWaypointsTraffic;
 0176  }
 177}