feat: add "Popular routes" statistics
feat: add financial report generation feat: add automation task: indirect route search
This commit is contained in:
parent
168b1fdec3
commit
20def5de44
@ -49,6 +49,14 @@ public class MapperInitializer : Profile
|
|||||||
CreateMap<Ticket, TicketDto>().ReverseMap();
|
CreateMap<Ticket, TicketDto>().ReverseMap();
|
||||||
CreateMap<Ticket, CreateTicketDto>().ReverseMap();
|
CreateMap<Ticket, CreateTicketDto>().ReverseMap();
|
||||||
CreateMap<Ticket, UpdateTicketDto>().ReverseMap();
|
CreateMap<Ticket, UpdateTicketDto>().ReverseMap();
|
||||||
|
CreateMap<Ticket, CreateInTicketGroupTicketDto>().ReverseMap();
|
||||||
|
CreateMap<Ticket, InTicketGroupTicketDto>().ReverseMap();
|
||||||
|
|
||||||
|
CreateMap<TicketGroup, TicketGroupDto>().ReverseMap();
|
||||||
|
CreateMap<TicketGroup, CreateTicketGroupDto>().ReverseMap();
|
||||||
|
CreateMap<TicketGroup, UpdateTicketGroupDto>().ReverseMap();
|
||||||
|
CreateMap<TicketGroup, CreateTicketGroupWithTicketsDto>().ReverseMap();
|
||||||
|
CreateMap<TicketGroup, TicketGroupWithTicketsDto>().ReverseMap();
|
||||||
|
|
||||||
CreateMap<Review, ReviewDto>().ReverseMap();
|
CreateMap<Review, ReviewDto>().ReverseMap();
|
||||||
CreateMap<Review, CreateReviewDto>().ReverseMap();
|
CreateMap<Review, CreateReviewDto>().ReverseMap();
|
||||||
|
30
Server/Controllers/AutomationController.cs
Normal file
30
Server/Controllers/AutomationController.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Server.Services;
|
||||||
|
|
||||||
|
namespace Server.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class AutomationController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AutomationService _automationService;
|
||||||
|
|
||||||
|
public AutomationController(AutomationService automationService)
|
||||||
|
{
|
||||||
|
_automationService = automationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetRoute(int from, int to, DateTime date)
|
||||||
|
{
|
||||||
|
var result = await _automationService.GetRoute(from, to, date);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,10 +21,24 @@ public class ReportController : ControllerBase
|
|||||||
|
|
||||||
if (!result.IsSucceed)
|
if (!result.IsSucceed)
|
||||||
{
|
{
|
||||||
return BadRequest(result.message);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return File(result.ticketPdf, "application/pdf",
|
return File(result.ticketPdf, "application/pdf",
|
||||||
$"ticket.pdf");
|
$"ticket.pdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("report")]
|
||||||
|
public async Task<IActionResult> GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
|
{
|
||||||
|
var result = await _reportService.GetCompanyReport(companyId, fromDate, toDate);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return BadRequest(result.actionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(result.reportPdf, "application/pdf",
|
||||||
|
$"report.pdf");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,18 @@ public class StatisticsController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("routes")]
|
[HttpGet("routes")]
|
||||||
public async Task<IActionResult> GetPopularRoutes([FromQuery] int amount = 10)
|
public async Task<IActionResult> GetPopularRoutes([FromQuery] PopularRoutesParameters parameters)
|
||||||
{
|
{
|
||||||
return Ok();
|
var result = await _statisticsService.GetPopularRoutes(parameters);
|
||||||
|
|
||||||
|
if (!result.IsSucceed)
|
||||||
|
{
|
||||||
|
return BadRequest(result.actionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
|
|
||||||
|
return Ok(result.route);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("users")]
|
[HttpGet("users")]
|
||||||
@ -30,7 +39,7 @@ public class StatisticsController : ControllerBase
|
|||||||
|
|
||||||
if (!result.IsSucceed)
|
if (!result.IsSucceed)
|
||||||
{
|
{
|
||||||
return BadRequest(result.message);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
@ -45,7 +54,7 @@ public class StatisticsController : ControllerBase
|
|||||||
|
|
||||||
if (!result.IsSucceed)
|
if (!result.IsSucceed)
|
||||||
{
|
{
|
||||||
return BadRequest(result.message);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
@ -60,7 +69,7 @@ public class StatisticsController : ControllerBase
|
|||||||
|
|
||||||
if (!result.IsSucceed)
|
if (!result.IsSucceed)
|
||||||
{
|
{
|
||||||
return BadRequest(result.message);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
|
133
Server/Controllers/TicketGroupController.cs
Normal file
133
Server/Controllers/TicketGroupController.cs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Server.Services;
|
||||||
|
using SharedModels.DataTransferObjects;
|
||||||
|
using SharedModels.QueryParameters.Objects;
|
||||||
|
|
||||||
|
namespace Server.Controllers;
|
||||||
|
|
||||||
|
[Route("api/ticketGroups")]
|
||||||
|
[ApiController]
|
||||||
|
public class TicketGroupController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ITicketGroupManagementService _ticketGroupManagementService;
|
||||||
|
|
||||||
|
public TicketGroupController(ITicketGroupManagementService ticketGroupManagementService)
|
||||||
|
{
|
||||||
|
_ticketGroupManagementService = ticketGroupManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> AddTicketGroup(CreateTicketGroupDto ticketGroup)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.AddTicketGroup(ticketGroup);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetTicketGroup), new {id = result.ticketGroup.Id}, result.ticketGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("withTickets")]
|
||||||
|
public async Task<IActionResult> AddTicketGroupWithTickets(CreateTicketGroupWithTicketsDto ticketGroup)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.AddTicketGroupWithTickets(ticketGroup);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetTicketGroup), new {id = result.ticketGroup.Id}, result.ticketGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetTicketGroups([FromQuery] TicketGroupParameters parameters)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.GetTicketGroups(parameters);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
|
|
||||||
|
return Ok(result.ticketGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("withTickets")]
|
||||||
|
public async Task<IActionResult> GetTicketGroupsWithTickets([FromQuery] TicketGroupWithTicketsParameters parameters)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.GetTicketGroupsWithTickets(parameters);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||||
|
|
||||||
|
return Ok(result.ticketGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> GetTicketGroup(int id, [FromQuery] string? fields)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.GetTicketGroup(id, fields);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.ticketGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("withTickets/{id}")]
|
||||||
|
public async Task<IActionResult> GetTicketGroupWithTickets(int id, [FromQuery] string? fields)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.GetTicketGroupWithTickets(id, fields);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.ticketGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> UpdateTicketGroup(int id, UpdateTicketGroupDto ticketGroup)
|
||||||
|
{
|
||||||
|
if (id != ticketGroup.Id)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _ticketGroupManagementService.UpdateTicketGroup(ticketGroup);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.ticketGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> DeleteTicketGroup(int id)
|
||||||
|
{
|
||||||
|
var result = await _ticketGroupManagementService.DeleteTicketGroup(id);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return result.actionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -99,6 +99,7 @@ builder.Services.AddScoped<IStateManagementService, StateManagementService>();
|
|||||||
builder.Services.AddScoped<ICityManagementService, CityManagementService>();
|
builder.Services.AddScoped<ICityManagementService, CityManagementService>();
|
||||||
builder.Services.AddScoped<IAddressManagementService, AddressManagementService>();
|
builder.Services.AddScoped<IAddressManagementService, AddressManagementService>();
|
||||||
builder.Services.AddScoped<ITicketManagementService, TicketManagementService>();
|
builder.Services.AddScoped<ITicketManagementService, TicketManagementService>();
|
||||||
|
builder.Services.AddScoped<ITicketGroupManagementService, TicketGroupManagementService>();
|
||||||
builder.Services.AddScoped<IReviewManagementService, ReviewManagementService>();
|
builder.Services.AddScoped<IReviewManagementService, ReviewManagementService>();
|
||||||
builder.Services.AddScoped<ICompanyManagementService, CompanyManagementService>();
|
builder.Services.AddScoped<ICompanyManagementService, CompanyManagementService>();
|
||||||
builder.Services.AddScoped<IVehicleManagementService, VehicleManagementService>();
|
builder.Services.AddScoped<IVehicleManagementService, VehicleManagementService>();
|
||||||
@ -113,6 +114,8 @@ builder.Services.AddScoped<IDataShaper<StateDto>, DataShaper<StateDto>>();
|
|||||||
builder.Services.AddScoped<IDataShaper<CityDto>, DataShaper<CityDto>>();
|
builder.Services.AddScoped<IDataShaper<CityDto>, DataShaper<CityDto>>();
|
||||||
builder.Services.AddScoped<IDataShaper<AddressDto>, DataShaper<AddressDto>>();
|
builder.Services.AddScoped<IDataShaper<AddressDto>, DataShaper<AddressDto>>();
|
||||||
builder.Services.AddScoped<IDataShaper<TicketDto>, DataShaper<TicketDto>>();
|
builder.Services.AddScoped<IDataShaper<TicketDto>, DataShaper<TicketDto>>();
|
||||||
|
builder.Services.AddScoped<IDataShaper<TicketGroupDto>, DataShaper<TicketGroupDto>>();
|
||||||
|
builder.Services.AddScoped<IDataShaper<TicketGroupWithTicketsDto>, DataShaper<TicketGroupWithTicketsDto>>();
|
||||||
builder.Services.AddScoped<IDataShaper<ReviewDto>, DataShaper<ReviewDto>>();
|
builder.Services.AddScoped<IDataShaper<ReviewDto>, DataShaper<ReviewDto>>();
|
||||||
builder.Services.AddScoped<IDataShaper<CompanyDto>, DataShaper<CompanyDto>>();
|
builder.Services.AddScoped<IDataShaper<CompanyDto>, DataShaper<CompanyDto>>();
|
||||||
builder.Services.AddScoped<IDataShaper<VehicleDto>, DataShaper<VehicleDto>>();
|
builder.Services.AddScoped<IDataShaper<VehicleDto>, DataShaper<VehicleDto>>();
|
||||||
@ -124,6 +127,13 @@ builder.Services.AddScoped<IDataShaper<RouteAddressDto>, DataShaper<RouteAddress
|
|||||||
|
|
||||||
builder.Services.AddScoped<IPager<ExpandoObject>, Pager<ExpandoObject>>();
|
builder.Services.AddScoped<IPager<ExpandoObject>, Pager<ExpandoObject>>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<AutomationService>();
|
||||||
|
builder.Services.AddScoped<IReportService, ReportService>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<IStatisticsService, StatisticsService>();
|
||||||
|
builder.Services.AddScoped<IDataShaper<UserDto>, DataShaper<UserDto>>();
|
||||||
|
builder.Services.AddScoped<IDataShaper<ExpandoObject>, DataShaper<ExpandoObject>>();
|
||||||
|
|
||||||
// Adding DB Context with PostgreSQL
|
// Adding DB Context with PostgreSQL
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
@ -81,8 +81,12 @@ public class AddressManagementService : IAddressManagementService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addresses = addresses.Where(a =>
|
// TODO: Remove client evaluation
|
||||||
a.Name.ToLower().Contains(search.ToLower()));
|
|
||||||
|
addresses = addresses.ToArray().Where(a =>
|
||||||
|
a.Name.ToLower().Contains(search.ToLower()) ||
|
||||||
|
a.GetFullName().ToLower().Contains(search.ToLower()))
|
||||||
|
.AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterByCityId(ref IQueryable<Address> addresses,
|
void FilterByCityId(ref IQueryable<Address> addresses,
|
||||||
|
315
Server/Services/AutomationService.cs
Normal file
315
Server/Services/AutomationService.cs
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
using System.Dynamic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Server.Data;
|
||||||
|
using Server.Helpers;
|
||||||
|
using Server.Models;
|
||||||
|
|
||||||
|
namespace Server.Services;
|
||||||
|
|
||||||
|
public class AutomationService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _dbContext;
|
||||||
|
private readonly ISortHelper<ExpandoObject> _sortHelper;
|
||||||
|
|
||||||
|
public AutomationService(ApplicationDbContext dbContext,
|
||||||
|
ISortHelper<ExpandoObject> sortHelper)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_sortHelper = sortHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, List<ExpandoObject> result)> GetRoute(
|
||||||
|
int from, int to, DateTime date)
|
||||||
|
{
|
||||||
|
var dbEnrollments = await _dbContext.VehicleEnrollments
|
||||||
|
.Include(ve => ve.Tickets)
|
||||||
|
.Include(ve => ve.Vehicle)
|
||||||
|
.ThenInclude(v => v.Company)
|
||||||
|
.Include(ve => ve.RouteAddressDetails)
|
||||||
|
.Include(ve => ve.Route).ThenInclude(r => r.RouteAddresses)
|
||||||
|
.ThenInclude(ra => ra.Address).ThenInclude(a => a.City)
|
||||||
|
.ThenInclude(c => c.State).ThenInclude(s => s.Country)
|
||||||
|
.Where(ve => ve.DepartureDateTimeUtc.Date >= date.Date &&
|
||||||
|
ve.DepartureDateTimeUtc.Date <= date.AddDays(3).Date)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var toBeRemovedEnrollmentsIds = new List<int>();
|
||||||
|
|
||||||
|
var directEnrollments = new List<VehicleEnrollment>();
|
||||||
|
|
||||||
|
foreach (var e in dbEnrollments)
|
||||||
|
{
|
||||||
|
if (e.Route.RouteAddresses.Count(ra => ra.AddressId == from) == 0 ||
|
||||||
|
e.Route.RouteAddresses.Count(ra => ra.AddressId == to) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromOrder = e.Route.RouteAddresses.FirstOrDefault(rad =>
|
||||||
|
rad.AddressId == from)?.Order;
|
||||||
|
var toOrder = e.Route.RouteAddresses.FirstOrDefault(rad =>
|
||||||
|
rad.AddressId == to)?.Order;
|
||||||
|
|
||||||
|
if (fromOrder < toOrder)
|
||||||
|
{
|
||||||
|
directEnrollments.Add(e);
|
||||||
|
toBeRemovedEnrollmentsIds.Add(e.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbEnrollments.RemoveAll(e => toBeRemovedEnrollmentsIds.Any(id => id == e.Id));
|
||||||
|
toBeRemovedEnrollmentsIds.Clear();
|
||||||
|
|
||||||
|
foreach (var de in directEnrollments)
|
||||||
|
{
|
||||||
|
var routeAddresses = de.Route.RouteAddresses;
|
||||||
|
|
||||||
|
var fromOrder =
|
||||||
|
routeAddresses.First(ra => ra.AddressId == from).Order;
|
||||||
|
var toOrder =
|
||||||
|
routeAddresses.First(ra => ra.AddressId == to).Order;
|
||||||
|
|
||||||
|
directEnrollments[directEnrollments.IndexOf(de)].Route.RouteAddresses = routeAddresses
|
||||||
|
.OrderBy(ra => ra.Order)
|
||||||
|
.SkipWhile(ra => ra.Order < fromOrder)
|
||||||
|
.TakeWhile(ra => ra.Order <= toOrder)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
directEnrollments[directEnrollments.IndexOf(de)]
|
||||||
|
.DepartureDateTimeUtc = GetDepartureTime(de);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var enrollmentsWithFrom = new List<VehicleEnrollment>();
|
||||||
|
|
||||||
|
foreach (var e in dbEnrollments)
|
||||||
|
{
|
||||||
|
if (e.Route.RouteAddresses.Count(ra => ra.AddressId == from) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.Route.RouteAddresses.Any(ra => ra.AddressId == from))
|
||||||
|
{
|
||||||
|
enrollmentsWithFrom.Add(e);
|
||||||
|
toBeRemovedEnrollmentsIds.Add(e.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbEnrollments.RemoveAll(e => ToBeRemovedEnrollmentsIds.Any(id => id == e.Id));
|
||||||
|
toBeRemovedEnrollmentsIds.Clear();
|
||||||
|
|
||||||
|
foreach (var ef in enrollmentsWithFrom)
|
||||||
|
{
|
||||||
|
var routeAddresses = ef.Route.RouteAddresses;
|
||||||
|
|
||||||
|
var fromOrder =
|
||||||
|
routeAddresses.First(ra => ra.AddressId == from).Order;
|
||||||
|
|
||||||
|
enrollmentsWithFrom[enrollmentsWithFrom.IndexOf(ef)].Route.RouteAddresses = routeAddresses
|
||||||
|
.OrderBy(ra => ra.Order)
|
||||||
|
.SkipWhile(ra => ra.Order < fromOrder).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var enrollmentsWithTo = new List<VehicleEnrollment>();
|
||||||
|
|
||||||
|
foreach (var e in dbEnrollments)
|
||||||
|
{
|
||||||
|
if (e.Route.RouteAddresses.Count(ra => ra.AddressId == to) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.Route.RouteAddresses.Any(ra => ra.AddressId == to))
|
||||||
|
{
|
||||||
|
enrollmentsWithTo.Add(e);
|
||||||
|
toBeRemovedEnrollmentsIds.Add(e.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbEnrollments.RemoveAll(e => ToBeRemovedEnrollmentsIds.Any(id => id == e.Id));
|
||||||
|
toBeRemovedEnrollmentsIds.Clear();
|
||||||
|
|
||||||
|
foreach (var et in enrollmentsWithTo)
|
||||||
|
{
|
||||||
|
var routeAddresses = et.Route.RouteAddresses;
|
||||||
|
|
||||||
|
var toOrder =
|
||||||
|
routeAddresses.First(ra => ra.AddressId == to).Order;
|
||||||
|
|
||||||
|
enrollmentsWithTo[enrollmentsWithTo.IndexOf(et)].Route.RouteAddresses = routeAddresses
|
||||||
|
.OrderBy(ra => ra.Order)
|
||||||
|
.TakeWhile(ra => ra.Order <= toOrder).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var oneTransferPath = new List<List<VehicleEnrollment>>();
|
||||||
|
|
||||||
|
foreach (var ef in enrollmentsWithFrom)
|
||||||
|
{
|
||||||
|
foreach (var et in enrollmentsWithTo)
|
||||||
|
{
|
||||||
|
var efRouteAddresses = ef.Route.RouteAddresses;
|
||||||
|
var etRouteAddresses = et.Route.RouteAddresses;
|
||||||
|
|
||||||
|
var intersectionAddressId = efRouteAddresses.IntersectBy(
|
||||||
|
etRouteAddresses.Select(x => x.AddressId),
|
||||||
|
x => x.AddressId).FirstOrDefault()?.AddressId;
|
||||||
|
|
||||||
|
var toOrder = efRouteAddresses.First(ra =>
|
||||||
|
ra.AddressId == intersectionAddressId).Order;
|
||||||
|
var fromOrder = etRouteAddresses.First(ra =>
|
||||||
|
ra.AddressId == intersectionAddressId).Order;
|
||||||
|
|
||||||
|
enrollmentsWithFrom[enrollmentsWithFrom.IndexOf(ef)].Route.RouteAddresses =
|
||||||
|
efRouteAddresses.OrderBy(ra => ra.Order)
|
||||||
|
.TakeWhile(ra => ra.Order <= toOrder).ToList();
|
||||||
|
enrollmentsWithTo[enrollmentsWithTo.IndexOf(et)].Route.RouteAddresses =
|
||||||
|
etRouteAddresses.OrderBy(ra => ra.Order)
|
||||||
|
.SkipWhile(ra => ra.Order < fromOrder).ToList();
|
||||||
|
|
||||||
|
var fromArrivalTime = GetArrivalTime(ef);
|
||||||
|
var toDepartureTime = GetDepartureTime(et);
|
||||||
|
|
||||||
|
var doesIntersect = intersectionAddressId != null;
|
||||||
|
|
||||||
|
if (doesIntersect && (toDepartureTime - fromArrivalTime) >= TimeSpan.FromMinutes(5))
|
||||||
|
{
|
||||||
|
oneTransferPath.Add(new List<VehicleEnrollment> {ef, et});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var directEnrollment in directEnrollments)
|
||||||
|
{
|
||||||
|
oneTransferPath.Add(new List<VehicleEnrollment> {directEnrollment});
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<ExpandoObject>();
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
foreach (var path in oneTransferPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
var shapedPath = new ExpandoObject();
|
||||||
|
var enrollmentGroup = new ExpandoObject();
|
||||||
|
|
||||||
|
int j = 1;
|
||||||
|
foreach (var vehicleEnrollment in path)
|
||||||
|
{
|
||||||
|
var enrollment = new ExpandoObject();
|
||||||
|
|
||||||
|
enrollment.TryAdd("id", vehicleEnrollment.Id);
|
||||||
|
enrollment.TryAdd("departureTime", GetDepartureTime(vehicleEnrollment));
|
||||||
|
enrollment.TryAdd("arrivalTime", GetArrivalTime(vehicleEnrollment));
|
||||||
|
enrollment.TryAdd("departureAddressName", vehicleEnrollment.Route.RouteAddresses.First().Address.Name);
|
||||||
|
enrollment.TryAdd("departureAddressFullName", vehicleEnrollment.Route.RouteAddresses.First().Address.GetFullName());
|
||||||
|
enrollment.TryAdd("departureAddressId", vehicleEnrollment.Route.RouteAddresses.First().AddressId);
|
||||||
|
enrollment.TryAdd("arrivalAddressName", vehicleEnrollment.Route.RouteAddresses.Last().Address.Name);
|
||||||
|
enrollment.TryAdd("arrivalAddressFullName", vehicleEnrollment.Route.RouteAddresses.Last().Address.GetFullName());
|
||||||
|
enrollment.TryAdd("arrivalAddressId", vehicleEnrollment.Route.RouteAddresses.Last().AddressId);
|
||||||
|
enrollment.TryAdd("order", j);
|
||||||
|
|
||||||
|
var vehicle = new ExpandoObject();
|
||||||
|
|
||||||
|
vehicle.TryAdd("type", vehicleEnrollment.Vehicle.Type);
|
||||||
|
vehicle.TryAdd("number", vehicleEnrollment.Vehicle.Number);
|
||||||
|
|
||||||
|
enrollment.TryAdd("vehicle", vehicle);
|
||||||
|
|
||||||
|
var company = new ExpandoObject();
|
||||||
|
|
||||||
|
company.TryAdd("name", vehicleEnrollment.Vehicle.Company.Name);
|
||||||
|
|
||||||
|
enrollment.TryAdd("company", company);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enrollmentGroup.TryAdd($"enrollment{j}", enrollment);
|
||||||
|
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
enrollmentGroup.TryAdd("totalDuration", GetTotalDuration(path));
|
||||||
|
enrollmentGroup.TryAdd("totalCost", GetTotalCost(path));
|
||||||
|
|
||||||
|
shapedPath.TryAdd($"enrollmentGroup{i}", enrollmentGroup);
|
||||||
|
result.Add(shapedPath);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// result = _sortHelper.ApplySort(result[].AsQueryable(), "+cost").ToList();
|
||||||
|
|
||||||
|
return (true, null, result);
|
||||||
|
|
||||||
|
DateTime GetDepartureTime(VehicleEnrollment enrollment)
|
||||||
|
{
|
||||||
|
var departureDateTimeUtc = enrollment.DepartureDateTimeUtc;
|
||||||
|
|
||||||
|
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().Id;
|
||||||
|
|
||||||
|
foreach (var detail in enrollment.RouteAddressDetails)
|
||||||
|
{
|
||||||
|
if (detail.RouteAddressId == departureRouteAddressId)
|
||||||
|
{
|
||||||
|
departureDateTimeUtc += detail.WaitTimeSpan;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
departureDateTimeUtc += detail.TimeSpanToNextCity + detail.WaitTimeSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return departureDateTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime GetArrivalTime(VehicleEnrollment enrollment)
|
||||||
|
{
|
||||||
|
var arrivalDateTimeUtc = enrollment.DepartureDateTimeUtc;
|
||||||
|
|
||||||
|
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().Id;
|
||||||
|
|
||||||
|
foreach (var detail in enrollment.RouteAddressDetails)
|
||||||
|
{
|
||||||
|
if (detail.RouteAddressId == arrivalRouteAddressId)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
arrivalDateTimeUtc += detail.TimeSpanToNextCity + detail.WaitTimeSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrivalDateTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan GetTotalDuration(List<VehicleEnrollment> vehicleEnrollments)
|
||||||
|
{
|
||||||
|
return GetArrivalTime(vehicleEnrollments.Last()) -
|
||||||
|
GetDepartureTime(vehicleEnrollments.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetTotalCost(List<VehicleEnrollment> vehicleEnrollments)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicleEnrollments)
|
||||||
|
{
|
||||||
|
foreach (var routeAddresses in enrollment.Route.RouteAddresses)
|
||||||
|
{
|
||||||
|
if (enrollment.Route.RouteAddresses.Last().Id == routeAddresses.Id)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += routeAddresses.RouteAddressDetails.First(rad =>
|
||||||
|
rad.VehicleEnrollmentId == enrollment.Id).CostToNextCity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Server.Services;
|
namespace Server.Services;
|
||||||
|
|
||||||
public interface IReportService
|
public interface IReportService
|
||||||
{
|
{
|
||||||
Task<(bool IsSucceed, string? message, Stream ticketPdf)> GetTicket(int ticketGroupId);
|
Task<(bool IsSucceed, IActionResult? actionResult, Stream ticketPdf)>
|
||||||
|
GetTicket(int ticketGroupId);
|
||||||
|
|
||||||
Task<(bool isSucceed, string? message, Stream reportPdf)> GetCompanyReport();
|
Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
||||||
|
GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate);
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Server.Models;
|
using Server.Models;
|
||||||
using SharedModels.QueryParameters;
|
using SharedModels.QueryParameters;
|
||||||
using SharedModels.QueryParameters.Statistics;
|
using SharedModels.QueryParameters.Statistics;
|
||||||
@ -7,18 +8,19 @@ namespace Server.Services;
|
|||||||
|
|
||||||
public interface IStatisticsService
|
public interface IStatisticsService
|
||||||
{
|
{
|
||||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> route)>
|
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> route,
|
||||||
GetPopularRoutes(int amount);
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
|
GetPopularRoutes(PopularRoutesParameters parameters);
|
||||||
|
|
||||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> users,
|
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> users,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetEngagedUsers(EngagedUserParameters parameters);
|
GetEngagedUsers(EngagedUserParameters parameters);
|
||||||
|
|
||||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> companies,
|
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> companies,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetPopularCompanies(PopularCompanyParameters parameters);
|
GetPopularCompanies(PopularCompanyParameters parameters);
|
||||||
|
|
||||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> stations,
|
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> stations,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetPopularStations(PopularAddressesParameters parameters);
|
GetPopularStations(PopularAddressesParameters parameters);
|
||||||
}
|
}
|
22
Server/Services/ITicketGroupManagementService.cs
Normal file
22
Server/Services/ITicketGroupManagementService.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System.Dynamic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SharedModels.DataTransferObjects;
|
||||||
|
using SharedModels.QueryParameters;
|
||||||
|
using SharedModels.QueryParameters.Objects;
|
||||||
|
|
||||||
|
namespace Server.Services;
|
||||||
|
|
||||||
|
public interface ITicketGroupManagementService
|
||||||
|
{
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, TicketGroupDto ticketGroup)> AddTicketGroup(CreateTicketGroupDto createTicketGroupDto);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, TicketGroupWithTicketsDto ticketGroup)> AddTicketGroupWithTickets(CreateTicketGroupWithTicketsDto createTicketGroupWithTicketsDto);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> ticketGroups,
|
||||||
|
PagingMetadata<ExpandoObject> pagingMetadata)> GetTicketGroups(TicketGroupParameters parameters);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> ticketGroups,
|
||||||
|
PagingMetadata<ExpandoObject> pagingMetadata)> GetTicketGroupsWithTickets(TicketGroupWithTicketsParameters parameters);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject ticketGroup)> GetTicketGroup(int id, string? fields);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject ticketGroup)> GetTicketGroupWithTickets(int id, string? fields);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, UpdateTicketGroupDto ticketGroup)> UpdateTicketGroup(UpdateTicketGroupDto updateTicketGroupDto);
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult)> DeleteTicketGroup(int id);
|
||||||
|
Task<bool> IsTicketGroupExist(int id);
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MigraDocCore.DocumentObjectModel;
|
using MigraDocCore.DocumentObjectModel;
|
||||||
using MigraDocCore.DocumentObjectModel.Tables;
|
using MigraDocCore.DocumentObjectModel.Tables;
|
||||||
using MigraDocCore.Rendering;
|
using MigraDocCore.Rendering;
|
||||||
|
using PdfSharpCore;
|
||||||
using PdfSharpCore.Drawing;
|
using PdfSharpCore.Drawing;
|
||||||
using PdfSharpCore.Pdf;
|
using PdfSharpCore.Pdf;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
using Server.Models;
|
using Server.Models;
|
||||||
|
using Route = Server.Models.Route;
|
||||||
|
|
||||||
namespace Server.Services;
|
namespace Server.Services;
|
||||||
|
|
||||||
@ -18,8 +21,13 @@ public class ReportService : IReportService
|
|||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool IsSucceed, string? message, Stream ticketPdf)> GetTicket(int ticketGroupId)
|
public async Task<(bool IsSucceed, IActionResult? actionResult, Stream ticketPdf)> GetTicket(int ticketGroupId)
|
||||||
{
|
{
|
||||||
|
if (!await DoesTicketGroupExist(ticketGroupId))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null!);
|
||||||
|
}
|
||||||
|
|
||||||
var dbTicketGroup = await _dbContext.TicketGroups
|
var dbTicketGroup = await _dbContext.TicketGroups
|
||||||
.Include(tg => tg.User)
|
.Include(tg => tg.User)
|
||||||
.Include(tg => tg.Tickets)
|
.Include(tg => tg.Tickets)
|
||||||
@ -42,7 +50,7 @@ public class ReportService : IReportService
|
|||||||
.ThenInclude(t => t.VehicleEnrollment)
|
.ThenInclude(t => t.VehicleEnrollment)
|
||||||
.ThenInclude(ve => ve.RouteAddressDetails)
|
.ThenInclude(ve => ve.RouteAddressDetails)
|
||||||
|
|
||||||
.FirstOrDefaultAsync(tg => tg.Id == ticketGroupId);
|
.FirstAsync(tg => tg.Id == ticketGroupId);
|
||||||
|
|
||||||
// Define document
|
// Define document
|
||||||
|
|
||||||
@ -53,8 +61,7 @@ public class ReportService : IReportService
|
|||||||
// Craft document
|
// Craft document
|
||||||
|
|
||||||
var pdfPage = document.AddPage();
|
var pdfPage = document.AddPage();
|
||||||
pdfPage.Width = XUnit.FromCentimeter(21.0);
|
pdfPage.Size = PageSize.A4;
|
||||||
pdfPage.Height = XUnit.FromCentimeter(29.7);
|
|
||||||
|
|
||||||
var gfx = XGraphics.FromPdfPage(pdfPage);
|
var gfx = XGraphics.FromPdfPage(pdfPage);
|
||||||
// HACK²
|
// HACK²
|
||||||
@ -248,7 +255,8 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[4].MergeRight = 3;
|
row.Cells[4].MergeRight = 3;
|
||||||
row.Cells[4].MergeDown = 1;
|
row.Cells[4].MergeDown = 1;
|
||||||
row.Cells[4].AddParagraph($"{departureAddress}");
|
row.Cells[4].Format.Font.Size = 8;
|
||||||
|
row.Cells[4].AddParagraph($"{departureAddress.GetFullName()}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].MergeDown = 1;
|
row.Cells[8].MergeDown = 1;
|
||||||
@ -281,7 +289,8 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[4].MergeRight = 3;
|
row.Cells[4].MergeRight = 3;
|
||||||
row.Cells[4].MergeDown = 1;
|
row.Cells[4].MergeDown = 1;
|
||||||
row.Cells[4].AddParagraph($"{arrivalAddress}");
|
row.Cells[4].Format.Font.Size = 8;
|
||||||
|
row.Cells[4].AddParagraph($"{arrivalAddress.GetFullName()}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].MergeDown = 1;
|
row.Cells[8].MergeDown = 1;
|
||||||
@ -341,14 +350,16 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[1].MergeRight = 3;
|
row.Cells[1].MergeRight = 3;
|
||||||
row.Cells[1].MergeDown = 1;
|
row.Cells[1].MergeDown = 1;
|
||||||
row.Cells[1].AddParagraph($"{departureAddress}");
|
row.Cells[1].Format.Font.Size = 8;
|
||||||
|
row.Cells[1].AddParagraph($"{departureAddress.GetFullName()}");
|
||||||
|
|
||||||
row.Cells[5].MergeDown = 1;
|
row.Cells[5].MergeDown = 1;
|
||||||
row.Cells[5].AddParagraph("Куди");
|
row.Cells[5].AddParagraph("Куди");
|
||||||
|
|
||||||
row.Cells[6].MergeRight = 3;
|
row.Cells[6].MergeRight = 3;
|
||||||
row.Cells[6].MergeDown = 1;
|
row.Cells[6].MergeDown = 1;
|
||||||
row.Cells[6].AddParagraph($"{arrivalAddress}");
|
row.Cells[6].Format.Font.Size = 8;
|
||||||
|
row.Cells[6].AddParagraph($"{arrivalAddress.GetFullName()}");
|
||||||
|
|
||||||
row.Cells[10].MergeDown = 1;
|
row.Cells[10].MergeDown = 1;
|
||||||
row.Cells[10].AddParagraph("Ціна");
|
row.Cells[10].AddParagraph("Ціна");
|
||||||
@ -386,6 +397,7 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
if (routeAddress.AddressId == ticket.FirstRouteAddressId)
|
if (routeAddress.AddressId == ticket.FirstRouteAddressId)
|
||||||
{
|
{
|
||||||
|
departureDateTimeUtc += details.WaitTimeSpan;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,6 +426,7 @@ public class ReportService : IReportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
arrivalDateTimeUtc += details.TimeSpanToNextCity;
|
arrivalDateTimeUtc += details.TimeSpanToNextCity;
|
||||||
|
arrivalDateTimeUtc += details.WaitTimeSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrivalDateTimeUtc;
|
return arrivalDateTimeUtc;
|
||||||
@ -465,10 +478,706 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task<bool> DoesTicketGroupExist(int id)
|
||||||
|
{
|
||||||
|
return await _dbContext.TicketGroups.AnyAsync(tg => tg.Id == id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool isSucceed, string? message, Stream reportPdf)> GetCompanyReport()
|
public async Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
||||||
|
GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (!await DoesCompanyExist(companyId))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbCompany = await _dbContext.Companies
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Reviews)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Tickets)
|
||||||
|
.ThenInclude(t => t.TicketGroup)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Route)
|
||||||
|
.ThenInclude(r => r.RouteAddresses)
|
||||||
|
.ThenInclude(ra => ra.Address)
|
||||||
|
.ThenInclude(a => a.City)
|
||||||
|
.ThenInclude(c => c.State)
|
||||||
|
.ThenInclude(s => s.Country)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.RouteAddressDetails)
|
||||||
|
|
||||||
|
.Select(c => new {
|
||||||
|
Company = c,
|
||||||
|
VehicleEnrollments = c.Vehicles
|
||||||
|
.Select(v => new {
|
||||||
|
Vehicle = v,
|
||||||
|
VehicleEnrollments = v.VehicleEnrollments.Where(ve =>
|
||||||
|
ve.DepartureDateTimeUtc.Date >= fromDate &&
|
||||||
|
ve.DepartureDateTimeUtc <= toDate)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.Select(o => o.Company)
|
||||||
|
.FirstAsync(c => c.Id == companyId);
|
||||||
|
|
||||||
|
var routesEnrolled = new List<Route>();
|
||||||
|
foreach (var vehicle in dbCompany.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var vehicleEnrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
var route = vehicleEnrollment.Route;
|
||||||
|
if (!routesEnrolled.Contains(route))
|
||||||
|
{
|
||||||
|
routesEnrolled.Add(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var vehicleEnrolled = dbCompany.Vehicles;
|
||||||
|
|
||||||
|
// Define document
|
||||||
|
|
||||||
|
var document = new PdfDocument();
|
||||||
|
document.Info.Title = "ticket";
|
||||||
|
document.Info.Author = "auto.bus";
|
||||||
|
|
||||||
|
var doc = new Document();
|
||||||
|
doc.DefaultPageSetup.LeftMargin = Unit.FromCentimeter(1);
|
||||||
|
doc.DefaultPageSetup.RightMargin = Unit.FromCentimeter(1);
|
||||||
|
doc.DefaultPageSetup.MirrorMargins = true;
|
||||||
|
|
||||||
|
DefineStyles(doc);
|
||||||
|
CreatePage(doc);
|
||||||
|
FillPage(doc);
|
||||||
|
|
||||||
|
var docRender = new DocumentRenderer(doc);
|
||||||
|
docRender.PrepareDocument();
|
||||||
|
|
||||||
|
var pageCount = docRender.FormattedDocument.PageCount;
|
||||||
|
for (int i = 0; i < pageCount; i++)
|
||||||
|
{
|
||||||
|
var pdfPage = document.AddPage();
|
||||||
|
pdfPage.Size = PageSize.A4;
|
||||||
|
|
||||||
|
using var gfx = XGraphics.FromPdfPage(pdfPage);
|
||||||
|
|
||||||
|
// HACK²
|
||||||
|
gfx.MUH = PdfFontEncoding.Unicode;
|
||||||
|
|
||||||
|
docRender.RenderPage(gfx, i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save document
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
document.Save(memoryStream);
|
||||||
|
|
||||||
|
return (true, null, memoryStream);
|
||||||
|
|
||||||
|
void DefineStyles(Document doc)
|
||||||
|
{
|
||||||
|
var styles = doc.Styles["Normal"];
|
||||||
|
styles.Font.Name = "Courier New Cyr";
|
||||||
|
styles.ParagraphFormat.LineSpacingRule = LineSpacingRule.OnePtFive;
|
||||||
|
|
||||||
|
styles = doc.Styles.AddStyle("Table", "Normal");
|
||||||
|
styles.Font.Size = 10;
|
||||||
|
styles.ParagraphFormat.SpaceBefore = 2.5;
|
||||||
|
styles.ParagraphFormat.SpaceAfter = 2.5;
|
||||||
|
styles.ParagraphFormat.LineSpacingRule = LineSpacingRule.Single;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatePage(Document doc)
|
||||||
|
{
|
||||||
|
var section = doc.AddSection();
|
||||||
|
|
||||||
|
// Create footer
|
||||||
|
// var paragraph = section.Footers.Primary.AddParagraph();
|
||||||
|
// paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
// paragraph.AddPageField();
|
||||||
|
// section.Footers.
|
||||||
|
// section.PageSetup.DifferentFirstPageHeaderFooter = true;
|
||||||
|
|
||||||
|
|
||||||
|
var paragraph = section.AddParagraph("auto.bus");
|
||||||
|
paragraph.Format.Font.Size = 20;
|
||||||
|
paragraph.Format.Font.Bold = true;
|
||||||
|
paragraph.Format.LineSpacingRule = LineSpacingRule.OnePtFive;
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
paragraph.Format.SpaceBefore = Unit.FromCentimeter(5);
|
||||||
|
paragraph.Format.SpaceAfter = Unit.FromCentimeter(5);
|
||||||
|
|
||||||
|
paragraph = section.AddParagraph("Фінансовий звіт");
|
||||||
|
paragraph.Format.Font.Size = 20;
|
||||||
|
paragraph.Format.LineSpacingRule = LineSpacingRule.OnePtFive;
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
|
||||||
|
paragraph = section.AddParagraph($"{dbCompany.Name}");
|
||||||
|
paragraph.Format.Font.Size = 20;
|
||||||
|
paragraph.Format.LineSpacingRule = LineSpacingRule.OnePtFive;
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
paragraph.Format.SpaceAfter = Unit.FromCentimeter(5);
|
||||||
|
|
||||||
|
paragraph = section.AddParagraph($"{fromDate:dd.MM.yyyy} – {toDate:dd.MM.yyyy}");
|
||||||
|
paragraph.Format.Font.Size = 20;
|
||||||
|
paragraph.Format.LineSpacingRule = LineSpacingRule.OnePtFive;
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
|
||||||
|
Table table;
|
||||||
|
|
||||||
|
// Section and table for each enrolled route
|
||||||
|
for (int i = 0; i < routesEnrolled.Count; i++)
|
||||||
|
{
|
||||||
|
section = doc.AddSection();
|
||||||
|
|
||||||
|
// Add table and define columns
|
||||||
|
table = section.AddTable();
|
||||||
|
table.Style = "Table";
|
||||||
|
table.Borders.Color = Colors.Black;
|
||||||
|
table.Borders.Width = 0.25;
|
||||||
|
table.Borders.Left.Width = 0.5;
|
||||||
|
table.Borders.Right.Width = 0.5;
|
||||||
|
table.Rows.LeftIndent = 0;
|
||||||
|
table.Rows.Height = Unit.FromPoint(15);
|
||||||
|
table.Rows.VerticalAlignment = VerticalAlignment.Center;
|
||||||
|
|
||||||
|
for (int j = 0; j < 12; j++)
|
||||||
|
{
|
||||||
|
var column = table.AddColumn(Unit.FromCentimeter(1.583));
|
||||||
|
column.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section for total
|
||||||
|
section = doc.AddSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillPage(Document doc)
|
||||||
|
{
|
||||||
|
Section section;
|
||||||
|
Paragraph paragraph;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
foreach (var route in routesEnrolled)
|
||||||
|
{
|
||||||
|
section = doc.Sections[i];
|
||||||
|
paragraph = section.Footers.Primary.AddParagraph();
|
||||||
|
paragraph.AddPageField();
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
|
||||||
|
var table = section.LastTable;
|
||||||
|
|
||||||
|
var row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 11;
|
||||||
|
row.Cells[0].AddParagraph($"МАРШРУТ №{route.Id}");
|
||||||
|
row.Cells[0].Format.Font.Bold = true;
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 1;
|
||||||
|
row.Cells[0].AddParagraph("Відправлення");
|
||||||
|
|
||||||
|
row.Cells[2].MergeRight = 3;
|
||||||
|
row.Cells[2].AddParagraph($"{route.RouteAddresses.First().Address.GetFullName()}");
|
||||||
|
|
||||||
|
row.Cells[6].MergeRight = 1;
|
||||||
|
row.Cells[6].AddParagraph("Прибуття");
|
||||||
|
|
||||||
|
row.Cells[8].MergeRight = 3;
|
||||||
|
row.Cells[8].AddParagraph($"{route.RouteAddresses.Last().Address.GetFullName()}");
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 11;
|
||||||
|
row.Cells[0].AddParagraph("КОРОТКО");
|
||||||
|
row.Cells[0].Format.Font.Bold = true;
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 1;
|
||||||
|
row.Cells[0].AddParagraph("Поїздок проведено");
|
||||||
|
|
||||||
|
row.Cells[2].MergeRight = 1;
|
||||||
|
row.Cells[2].AddParagraph("Поїздок скасовано");
|
||||||
|
|
||||||
|
row.Cells[4].MergeRight = 1;
|
||||||
|
row.Cells[4].AddParagraph("Квитків продано");
|
||||||
|
|
||||||
|
row.Cells[6].MergeRight = 1;
|
||||||
|
row.Cells[6].AddParagraph("Неповних квитків");
|
||||||
|
|
||||||
|
row.Cells[8].MergeRight = 1;
|
||||||
|
row.Cells[8].AddParagraph("Грошей зароблено");
|
||||||
|
|
||||||
|
row.Cells[10].MergeRight = 1;
|
||||||
|
row.Cells[10].AddParagraph("Середній рейтинг");
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
row.Shading.Color = Color.FromRgbColor(25, Colors.Black);
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 1;
|
||||||
|
row.Cells[0].AddParagraph($"{GetRouteEnrollmentCount(route)}");
|
||||||
|
|
||||||
|
row.Cells[2].MergeRight = 1;
|
||||||
|
row.Cells[2].AddParagraph($"{GetRouteCanceledEnrollmentCount(route)}");
|
||||||
|
|
||||||
|
row.Cells[4].MergeRight = 1;
|
||||||
|
row.Cells[4].AddParagraph($"{GetRouteSelledTicketCount(route)}");
|
||||||
|
|
||||||
|
row.Cells[6].MergeRight = 1;
|
||||||
|
row.Cells[6].AddParagraph($"{GetRouteIndirectTicketCount(route)}");
|
||||||
|
|
||||||
|
row.Cells[8].MergeRight = 1;
|
||||||
|
row.Cells[8].AddParagraph($"{GetRouteTotalRevenu(route)}");
|
||||||
|
|
||||||
|
row.Cells[10].MergeRight = 1;
|
||||||
|
var routeAverageRating = GetRouteAvarageRating(route);
|
||||||
|
row.Cells[10].AddParagraph($"{(routeAverageRating == 0 ? "-" : routeAverageRating)}");
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 11;
|
||||||
|
row.Cells[0].AddParagraph("ДОКЛАДНО");
|
||||||
|
row.Cells[0].Format.Font.Bold = true;
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 2;
|
||||||
|
row.Cells[0].AddParagraph("Ідентифікатор, тип та номер транспорту");
|
||||||
|
|
||||||
|
row.Cells[3].MergeRight = 1;
|
||||||
|
row.Cells[3].AddParagraph("Поїздок заплановано, проведена та скасовано");
|
||||||
|
|
||||||
|
row.Cells[5].MergeRight = 2;
|
||||||
|
row.Cells[5].AddParagraph("Квитків продано та повернено, з яких неповні");
|
||||||
|
|
||||||
|
row.Cells[8].MergeRight = 1;
|
||||||
|
row.Cells[8].AddParagraph("Грошей зароблено");
|
||||||
|
|
||||||
|
row.Cells[10].MergeRight = 1;
|
||||||
|
row.Cells[10].AddParagraph("Середній рейтинг");
|
||||||
|
|
||||||
|
var isFilled = true;
|
||||||
|
foreach (var vehicle in vehicleEnrolled)
|
||||||
|
{
|
||||||
|
if (route.VehicleEnrollments.Count(ve =>
|
||||||
|
ve.VehicleId == vehicle.Id) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = table.AddRow();
|
||||||
|
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
||||||
|
isFilled = !isFilled;
|
||||||
|
|
||||||
|
row.Cells[0].MergeRight = 2;
|
||||||
|
row.Cells[0].AddParagraph($"{vehicle.Id}, {vehicle.Type}, {vehicle.Number}");
|
||||||
|
|
||||||
|
var executedEnrollmentCount = GetVehicleEnrollmentCount(vehicle, route.Id);
|
||||||
|
var canceledEnrollmentCount = GetVehicleCanceledEnrollmentCount(vehicle, route.Id);
|
||||||
|
row.Cells[3].MergeRight = 1;
|
||||||
|
row.Cells[3].AddParagraph($"{executedEnrollmentCount + canceledEnrollmentCount}, " +
|
||||||
|
$"{executedEnrollmentCount}, {canceledEnrollmentCount}");
|
||||||
|
|
||||||
|
row.Cells[5].MergeRight = 2;
|
||||||
|
row.Cells[5].AddParagraph($"{GetVehicleSelledTicketCount(vehicle, route.Id)}, {GetVehicleReturnedTicketCount(vehicle, route.Id)}; " +
|
||||||
|
$"{GetVehicleIndirectTicketCount(vehicle, route.Id)}, {GetVehicleReturnedIndirectTicketCount(vehicle, route.Id)}");
|
||||||
|
|
||||||
|
row.Cells[8].MergeRight = 1;
|
||||||
|
row.Cells[8].AddParagraph($"{GetVehicleTotalRevenue(vehicle, route.Id)}");
|
||||||
|
|
||||||
|
row.Cells[10].MergeRight = 1;
|
||||||
|
var vehicleAverageRating = GetVehicleAverageRating(vehicle, route.Id);
|
||||||
|
row.Cells[10].AddParagraph($"{(vehicleAverageRating == 0 ? "-" : vehicleAverageRating)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
section = doc.Sections[doc.Sections.Count - 1];
|
||||||
|
|
||||||
|
paragraph = section.AddParagraph("ПІДСУМОК");
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||||
|
paragraph.Format.Font.Size = 14;
|
||||||
|
paragraph.Format.Font.Bold = true;
|
||||||
|
section.AddParagraph();
|
||||||
|
|
||||||
|
var totalEnrollmentCount = GetTotalEnrollmentCount(dbCompany);
|
||||||
|
var totalCanceledEnrollmentCount = GetTotalCanceledEnrollmentCount(dbCompany);
|
||||||
|
var totalSoldTicketCount = GetTotalSoldTickets(dbCompany);
|
||||||
|
var totalReturnedTicketCount = GetTotalReturnedTicketCount(dbCompany);
|
||||||
|
var totalRevenue = GetTotalRevenu(dbCompany);
|
||||||
|
var totalAverageRating = GetTotalAverageRating(dbCompany);
|
||||||
|
|
||||||
|
paragraph = section.AddParagraph(
|
||||||
|
$"У період з {fromDate:dd.MM.yyyy} по {toDate:dd.MM.yyyy} " +
|
||||||
|
$"({(toDate - fromDate).Days} днів) компанією {dbCompany.Name} " +
|
||||||
|
$"було заплановано {(totalEnrollmentCount)} поїздки, " +
|
||||||
|
$"з яких {totalCanceledEnrollmentCount} було скасовано, " +
|
||||||
|
$"продано {totalSoldTicketCount} квитків, " +
|
||||||
|
$"з яких {totalReturnedTicketCount} було повернено. " +
|
||||||
|
$"За цей час було зароблено {totalRevenue} гривень. " +
|
||||||
|
$"Середній рейтинг по всім поїздкам: {totalAverageRating}");
|
||||||
|
paragraph.Format.Alignment = ParagraphAlignment.Justify;
|
||||||
|
paragraph.Format.Font.Size = 14;
|
||||||
|
|
||||||
|
int GetRouteEnrollmentCount(Route route)
|
||||||
|
{
|
||||||
|
return route.VehicleEnrollments.Count(ve => !ve.IsCanceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleEnrollmentCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
return vehicle.VehicleEnrollments.Count(ve =>
|
||||||
|
!ve.IsCanceled && ve.RouteId == routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalEnrollmentCount(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleEnrollmentCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetRouteCanceledEnrollmentCount(Route route)
|
||||||
|
{
|
||||||
|
return route.VehicleEnrollments.Count(ve => ve.IsCanceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleCanceledEnrollmentCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
return vehicle.VehicleEnrollments.Count(ve =>
|
||||||
|
ve.IsCanceled && ve.RouteId == routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalCanceledEnrollmentCount(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleCanceledEnrollmentCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetRouteSelledTicketCount(Route route)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in route.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleSelledTicketCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalSoldTickets(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleSelledTicketCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleReturnedTicketCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalReturnedTicketCount(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleReturnedTicketCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetRouteIndirectTicketCount(Route route)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
int departureAddressId = route.RouteAddresses.First().AddressId;
|
||||||
|
int arrivalAddressId = route.RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
foreach (var enrollment in route.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
||||||
|
t.FirstRouteAddressId != departureAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalAddressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleIndirectTicketCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
||||||
|
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
||||||
|
t.FirstRouteAddressId != departureRouteAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalRouteAddressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalIndirectTicketCount(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleIndirectTicketCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVehicleReturnedIndirectTicketCount(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
||||||
|
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
result += enrollment.Tickets.Count(t => t.IsReturned &&
|
||||||
|
(t.FirstRouteAddressId != departureRouteAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalRouteAddressId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTotalReturnedIndirectTicketCount(Company company)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleReturnedIndirectTicketCount(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetRouteTotalRevenu(Route route)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in route.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
foreach (var ticket in enrollment.Tickets)
|
||||||
|
{
|
||||||
|
result += GetTicketCost(ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetVehicleTotalRevenue(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
foreach (var ticket in enrollment.Tickets)
|
||||||
|
{
|
||||||
|
result += GetTicketCost(ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetTotalRevenu(Company company)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += GetVehicleTotalRevenue(vehicle, enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetRouteAvarageRating(Route route)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int reviewCount = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in route.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
if (enrollment.Reviews.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var review in enrollment.Reviews)
|
||||||
|
{
|
||||||
|
result += review.Rating;
|
||||||
|
reviewCount += enrollment.Reviews.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= reviewCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetVehicleAverageRating(Vehicle vehicle, int routeId)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int reviewCount = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments
|
||||||
|
.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
reviewCount += enrollment.Reviews.Count;
|
||||||
|
|
||||||
|
foreach (var review in enrollment.Reviews)
|
||||||
|
{
|
||||||
|
result += review.Rating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= reviewCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return Math.Round(result, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetTotalAverageRating(Company company)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int enrollmentCount = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in company.Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
if (enrollment.Reviews.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += GetVehicleAverageRating(vehicle, enrollment.RouteId);
|
||||||
|
enrollmentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= enrollmentCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: repeated function, yoinked from ticket generation
|
||||||
|
double GetTicketCost(Ticket ticket)
|
||||||
|
{
|
||||||
|
double cost = 0;
|
||||||
|
|
||||||
|
var routeAddresses = ticket.VehicleEnrollment.Route.RouteAddresses
|
||||||
|
.OrderBy(ra => ra.Order)
|
||||||
|
.SkipWhile(ra => ra.AddressId != ticket.FirstRouteAddressId)
|
||||||
|
.TakeWhile(ra => ra.AddressId != ticket.LastRouteAddressId)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var routeAddress in routeAddresses)
|
||||||
|
{
|
||||||
|
var details = routeAddress.RouteAddressDetails
|
||||||
|
.First(rad => rad.RouteAddressId == routeAddress.Id);
|
||||||
|
|
||||||
|
cost += details.CostToNextCity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<bool> DoesCompanyExist(int id)
|
||||||
|
{
|
||||||
|
return await _dbContext.Companies.AnyAsync(c => c.Id == id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -49,8 +49,6 @@ public class RouteManagementService : IRouteManagementService
|
|||||||
{
|
{
|
||||||
var route = _mapper.Map<Route>(createRouteWithAddressesDto);
|
var route = _mapper.Map<Route>(createRouteWithAddressesDto);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await _dbContext.Routes.AddAsync(route);
|
await _dbContext.Routes.AddAsync(route);
|
||||||
await _dbContext.SaveChangesAsync();
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
@ -78,9 +76,9 @@ public class RouteManagementService : IRouteManagementService
|
|||||||
{
|
{
|
||||||
shapedData = _routeSortHelper.ApplySort(shapedData, parameters.Sort);
|
shapedData = _routeSortHelper.ApplySort(shapedData, parameters.Sort);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return (false, new BadRequestObjectResult("Invalid sorting string"), null, null)!;
|
return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber,
|
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber,
|
||||||
@ -133,7 +131,7 @@ public class RouteManagementService : IRouteManagementService
|
|||||||
{
|
{
|
||||||
shapedData = _routeSortHelper.ApplySort(shapedData, parameters.Sort);
|
shapedData = _routeSortHelper.ApplySort(shapedData, parameters.Sort);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return (false, new BadRequestObjectResult("Invalid sorting string"), null, null)!;
|
return (false, new BadRequestObjectResult("Invalid sorting string"), null, null)!;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
using Server.Helpers;
|
using Server.Helpers;
|
||||||
using Server.Models;
|
|
||||||
using SharedModels.DataTransferObjects;
|
using SharedModels.DataTransferObjects;
|
||||||
using SharedModels.QueryParameters;
|
using SharedModels.QueryParameters;
|
||||||
using SharedModels.QueryParameters.Statistics;
|
using SharedModels.QueryParameters.Statistics;
|
||||||
@ -17,11 +17,14 @@ public class StatisticsService : IStatisticsService
|
|||||||
private readonly IDataShaper<UserDto> _userDataShaper;
|
private readonly IDataShaper<UserDto> _userDataShaper;
|
||||||
private readonly IDataShaper<CompanyDto> _companyDataShaper;
|
private readonly IDataShaper<CompanyDto> _companyDataShaper;
|
||||||
private readonly IDataShaper<AddressDto> _addressDataShaper;
|
private readonly IDataShaper<AddressDto> _addressDataShaper;
|
||||||
|
private readonly IDataShaper<ExpandoObject> _expandoDataShaper;
|
||||||
private readonly IPager<ExpandoObject> _pager;
|
private readonly IPager<ExpandoObject> _pager;
|
||||||
|
private readonly ISortHelper<ExpandoObject> _sortHelper;
|
||||||
|
|
||||||
public StatisticsService(ApplicationDbContext dbContext, IMapper mapper,
|
public StatisticsService(ApplicationDbContext dbContext, IMapper mapper,
|
||||||
IDataShaper<UserDto> userDataShaper, IDataShaper<CompanyDto> companyDataShaper,
|
IDataShaper<UserDto> userDataShaper, IDataShaper<CompanyDto> companyDataShaper,
|
||||||
IDataShaper<AddressDto> addressDataShaper, IPager<ExpandoObject> pager)
|
IDataShaper<AddressDto> addressDataShaper, IPager<ExpandoObject> pager,
|
||||||
|
IDataShaper<ExpandoObject> expandoDataShaper, ISortHelper<ExpandoObject> sortHelper)
|
||||||
{
|
{
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
@ -29,25 +32,131 @@ public class StatisticsService : IStatisticsService
|
|||||||
_companyDataShaper = companyDataShaper;
|
_companyDataShaper = companyDataShaper;
|
||||||
_addressDataShaper = addressDataShaper;
|
_addressDataShaper = addressDataShaper;
|
||||||
_pager = pager;
|
_pager = pager;
|
||||||
|
_expandoDataShaper = expandoDataShaper;
|
||||||
|
_sortHelper = sortHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popularity is measured in number of purchased tickets
|
// Popularity is measured in number of purchased tickets
|
||||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> route)>
|
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> route,
|
||||||
GetPopularRoutes(int amount)
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
|
GetPopularRoutes(PopularRoutesParameters parameters)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
parameters.Days ??= parameters.DefaultDays;
|
||||||
|
var fromDateUtc = DateTime.UtcNow.Date - TimeSpan.FromDays((double) parameters.Days);
|
||||||
|
|
||||||
|
var dbTicketGroupsArray = await _dbContext.TicketGroups
|
||||||
|
.Include(tg => tg.Tickets)
|
||||||
|
.Where(tg => tg.Tickets.First().PurchaseDateTimeUtc >= fromDateUtc)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var depArrCombCountDict = new Dictionary<(int, int), int>();
|
||||||
|
|
||||||
|
foreach (var tg in dbTicketGroupsArray)
|
||||||
|
{
|
||||||
|
// TODO: implement ticket ordering
|
||||||
|
var departureAddress = tg.Tickets.OrderBy(t => t.Id).First().FirstRouteAddressId;
|
||||||
|
var arrivalAddress = tg.Tickets.OrderBy(t => t.Id).Last().LastRouteAddressId;
|
||||||
|
|
||||||
|
if (!depArrCombCountDict.ContainsKey((departureAddress, arrivalAddress)))
|
||||||
|
{
|
||||||
|
depArrCombCountDict.Add((departureAddress, arrivalAddress), 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
depArrCombCountDict[(departureAddress, arrivalAddress)] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
depArrCombCountDict = depArrCombCountDict
|
||||||
|
.OrderByDescending(a => a.Value)
|
||||||
|
.Take(parameters.Amount)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
|
var addressIds = new List<int>();
|
||||||
|
foreach (var depArrAddressIds in depArrCombCountDict.Keys)
|
||||||
|
{
|
||||||
|
var (departureAddressId, arrivalAddressId) = depArrAddressIds;
|
||||||
|
|
||||||
|
if (!addressIds.Contains(departureAddressId))
|
||||||
|
{
|
||||||
|
addressIds.Add(departureAddressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addressIds.Contains(arrivalAddressId))
|
||||||
|
{
|
||||||
|
addressIds.Add(arrivalAddressId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbAddressArray = await _dbContext.Addresses
|
||||||
|
.Include(a => a.City)
|
||||||
|
.ThenInclude(c => c.State)
|
||||||
|
.ThenInclude(s => s.Country)
|
||||||
|
.Where(a => addressIds.Contains(a.Id))
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var derArrCount = new List<ExpandoObject>();
|
||||||
|
|
||||||
|
foreach (var depArrCount in depArrCombCountDict)
|
||||||
|
{
|
||||||
|
var obj = new ExpandoObject();
|
||||||
|
|
||||||
|
var departureAddressId = depArrCount.Key.Item1;
|
||||||
|
var departureAddress = dbAddressArray
|
||||||
|
.First(a => a.Id == departureAddressId);
|
||||||
|
var arrivalAddressId = depArrCount.Key.Item2;
|
||||||
|
var arrivalAddress = dbAddressArray
|
||||||
|
.First(a => a.Id == arrivalAddressId);
|
||||||
|
var count = depArrCount.Value;
|
||||||
|
|
||||||
|
var fields = parameters.Fields!.Split(',');
|
||||||
|
|
||||||
|
if (fields.Any(f => f.ToLower() == "departureAddressId".ToLower()))
|
||||||
|
obj.TryAdd("departureAddressId", departureAddressId);
|
||||||
|
if (fields.Any(f => f.ToLower() == "departureAddress".ToLower()))
|
||||||
|
obj.TryAdd("departureAddress", departureAddress.GetFullName());
|
||||||
|
if (fields.Any(f => f.ToLower() == "arrivalAddressId".ToLower()))
|
||||||
|
obj.TryAdd("arrivalAddressId", arrivalAddressId);
|
||||||
|
if (fields.Any(f => f.ToLower() == "arrivalAddress".ToLower()))
|
||||||
|
obj.TryAdd("arrivalAddress", arrivalAddress.GetFullName());
|
||||||
|
if (fields.Any(f => f.ToLower() == "count".ToLower()))
|
||||||
|
obj.TryAdd("count", count);
|
||||||
|
|
||||||
|
derArrCount.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = derArrCount.Count;
|
||||||
|
for (int i = 0; i < n - 1; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < n - i - 1; j++)
|
||||||
|
{
|
||||||
|
if ((int) (derArrCount[j] as IDictionary<string, object>)["count"] > (int) (derArrCount[j + 1] as IDictionary<string, object>)["count"])
|
||||||
|
{
|
||||||
|
// swap temp and arr[i]
|
||||||
|
(derArrCount[j], derArrCount[j + 1]) = (derArrCount[j + 1], derArrCount[j]);
|
||||||
|
(derArrCount[j], derArrCount[j + 1]) = (derArrCount[j + 1], derArrCount[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = derArrCount.AsQueryable();
|
||||||
|
|
||||||
|
var pagingMetadata = _pager.ApplyPaging(ref result,
|
||||||
|
parameters.PageNumber, parameters.PageSize);
|
||||||
|
|
||||||
|
return (true, null, result.AsEnumerable(), pagingMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engagement is measured in number of purchases made in past N days
|
// Engagement is measured in number of purchases made in past N days
|
||||||
// One purchase contains one (direct route) or more (route with transfers) tickets
|
// One purchase contains one (direct route) or more (route with transfers) tickets
|
||||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> users,
|
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> users,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetEngagedUsers(EngagedUserParameters parameters)
|
GetEngagedUsers(EngagedUserParameters parameters)
|
||||||
{
|
{
|
||||||
var fromDateUtc =
|
parameters.Days ??= parameters.DefaultDays;
|
||||||
DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays);
|
var fromDateUtc = DateTime.UtcNow.Date - TimeSpan.FromDays((double) parameters.Days);
|
||||||
|
|
||||||
var resultObjects = _dbContext.Users
|
var dbUsers = _dbContext.Users
|
||||||
.Include(u => u.TicketGroups)
|
.Include(u => u.TicketGroups)
|
||||||
.ThenInclude(tg => tg.Tickets)
|
.ThenInclude(tg => tg.Tickets)
|
||||||
.Select(u => new
|
.Select(u => new
|
||||||
@ -57,10 +166,9 @@ public class StatisticsService : IStatisticsService
|
|||||||
tg.Tickets.First().PurchaseDateTimeUtc >= fromDateUtc)
|
tg.Tickets.First().PurchaseDateTimeUtc >= fromDateUtc)
|
||||||
})
|
})
|
||||||
.OrderByDescending(o => o.TicketGroups.Count())
|
.OrderByDescending(o => o.TicketGroups.Count())
|
||||||
.Take(parameters.Amount);
|
.Take(parameters.Amount)
|
||||||
|
.Select(i => i.User);
|
||||||
|
|
||||||
|
|
||||||
var dbUsers = resultObjects.Select(i => i.User);
|
|
||||||
var userDtos = _mapper.ProjectTo<UserDto>(dbUsers).ToArray();
|
var userDtos = _mapper.ProjectTo<UserDto>(dbUsers).ToArray();
|
||||||
var shapedDataArray = _userDataShaper
|
var shapedDataArray = _userDataShaper
|
||||||
.ShapeData(userDtos, parameters.Fields ?? parameters.DefaultFields)
|
.ShapeData(userDtos, parameters.Fields ?? parameters.DefaultFields)
|
||||||
@ -86,7 +194,7 @@ public class StatisticsService : IStatisticsService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Popularity is measured in average rating of all VehicleEnrollments of a company
|
// Popularity is measured in average rating of all VehicleEnrollments of a company
|
||||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> companies,
|
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> companies,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetPopularCompanies(PopularCompanyParameters parameters)
|
GetPopularCompanies(PopularCompanyParameters parameters)
|
||||||
{
|
{
|
||||||
@ -105,28 +213,22 @@ public class StatisticsService : IStatisticsService
|
|||||||
|
|
||||||
for (int i = 0; i < dbCompaniesArray.Length; i++)
|
for (int i = 0; i < dbCompaniesArray.Length; i++)
|
||||||
{
|
{
|
||||||
double tempC = 0;
|
double result = 0;
|
||||||
|
int reviewCount = 0;
|
||||||
|
|
||||||
foreach (var v in dbCompaniesArray[i].Vehicles)
|
foreach (var v in dbCompaniesArray[i].Vehicles)
|
||||||
{
|
{
|
||||||
double tempV = 0;
|
|
||||||
|
|
||||||
foreach (var ve in v.VehicleEnrollments)
|
foreach (var ve in v.VehicleEnrollments)
|
||||||
{
|
{
|
||||||
double tempVE = 0;
|
|
||||||
|
|
||||||
foreach (var r in ve.Reviews)
|
foreach (var r in ve.Reviews)
|
||||||
{
|
{
|
||||||
tempVE += r.Rating;
|
result += r.Rating;
|
||||||
|
reviewCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempV += tempVE / ve.Reviews.Count;
|
companiesAvgRatings[i] = reviewCount != 0 ? Math.Round(result / reviewCount, 5) : 0;
|
||||||
}
|
|
||||||
|
|
||||||
tempC += tempV / v.VehicleEnrollments.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
companiesAvgRatings[i] = tempC / dbCompaniesArray[i].Vehicles.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort companiesAvgRatings and apply the same sorting to dbCompaniesArray
|
// Sort companiesAvgRatings and apply the same sorting to dbCompaniesArray
|
||||||
@ -176,14 +278,12 @@ public class StatisticsService : IStatisticsService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Popularity is measured in number tickets in which the address is the first or last station
|
// Popularity is measured in number tickets in which the address is the first or last station
|
||||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> stations,
|
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> stations,
|
||||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
GetPopularStations(PopularAddressesParameters parameters)
|
GetPopularStations(PopularAddressesParameters parameters)
|
||||||
{
|
{
|
||||||
// throw new NotImplementedException();
|
parameters.Days ??= parameters.DefaultDays;
|
||||||
|
var fromDateUtc = DateTime.UtcNow.Date - TimeSpan.FromDays((double) parameters.Days);
|
||||||
var fromDateUtc =
|
|
||||||
DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays);
|
|
||||||
|
|
||||||
var dbTicketGroupsArray = await _dbContext.TicketGroups
|
var dbTicketGroupsArray = await _dbContext.TicketGroups
|
||||||
.Include(tg => tg.Tickets)
|
.Include(tg => tg.Tickets)
|
||||||
@ -195,22 +295,25 @@ public class StatisticsService : IStatisticsService
|
|||||||
|
|
||||||
foreach (var tg in dbTicketGroupsArray)
|
foreach (var tg in dbTicketGroupsArray)
|
||||||
{
|
{
|
||||||
if (!addressCountDict.ContainsKey(tg.Tickets.First().FirstRouteAddressId))
|
// TODO: implement ticket ordering
|
||||||
|
var tickets = tg.Tickets.OrderBy(t => t.Id).ToArray();
|
||||||
|
|
||||||
|
if (!addressCountDict.ContainsKey(tickets.First().FirstRouteAddressId))
|
||||||
{
|
{
|
||||||
addressCountDict.Add(tg.Tickets.First().FirstRouteAddressId, 1);
|
addressCountDict.Add(tickets.First().FirstRouteAddressId, 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addressCountDict[tg.Tickets.First().FirstRouteAddressId] += 1;
|
addressCountDict[tickets.First().FirstRouteAddressId] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!addressCountDict.ContainsKey(tg.Tickets.Last().LastRouteAddressId))
|
if (!addressCountDict.ContainsKey(tickets.Last().LastRouteAddressId))
|
||||||
{
|
{
|
||||||
addressCountDict.Add(tg.Tickets.Last().LastRouteAddressId, 1);
|
addressCountDict.Add(tickets.Last().LastRouteAddressId, 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addressCountDict[tg.Tickets.Last().LastRouteAddressId] += 1;
|
addressCountDict[tickets.Last().LastRouteAddressId] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +331,9 @@ public class StatisticsService : IStatisticsService
|
|||||||
// Get top addresses from database ordered by Id (same as
|
// Get top addresses from database ordered by Id (same as
|
||||||
// addressIds addressCountDict and )
|
// addressIds addressCountDict and )
|
||||||
var dbAddressesArray = await _dbContext.Addresses
|
var dbAddressesArray = await _dbContext.Addresses
|
||||||
|
.Include(a => a.City)
|
||||||
|
.ThenInclude(c => c.State)
|
||||||
|
.ThenInclude(s => s.Country)
|
||||||
.Where(a => addressIds.Any(id => a.Id == id))
|
.Where(a => addressIds.Any(id => a.Id == id))
|
||||||
.OrderBy(a => a.Id).ToArrayAsync();
|
.OrderBy(a => a.Id).ToArrayAsync();
|
||||||
|
|
||||||
@ -240,9 +346,10 @@ public class StatisticsService : IStatisticsService
|
|||||||
{
|
{
|
||||||
if (addressCountArray[j] > addressCountArray[j + 1])
|
if (addressCountArray[j] > addressCountArray[j + 1])
|
||||||
{
|
{
|
||||||
// swap temp and arr[i]
|
(addressCountArray[j], addressCountArray[j + 1]) =
|
||||||
(addressCountArray[j], addressCountArray[j + 1]) = (addressCountArray[j + 1], addressCountArray[j]);
|
(addressCountArray[j + 1], addressCountArray[j]);
|
||||||
(dbAddressesArray[j], dbAddressesArray[j + 1]) = (dbAddressesArray[j + 1], dbAddressesArray[j]);
|
(dbAddressesArray[j], dbAddressesArray[j + 1]) =
|
||||||
|
(dbAddressesArray[j + 1], dbAddressesArray[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
220
Server/Services/TicketGroupManagementService.cs
Normal file
220
Server/Services/TicketGroupManagementService.cs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
using System.Dynamic;
|
||||||
|
using AutoMapper;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Server.Data;
|
||||||
|
using Server.Helpers;
|
||||||
|
using Server.Models;
|
||||||
|
using SharedModels.DataTransferObjects;
|
||||||
|
using SharedModels.QueryParameters;
|
||||||
|
using SharedModels.QueryParameters.Objects;
|
||||||
|
|
||||||
|
namespace Server.Services;
|
||||||
|
|
||||||
|
public class TicketGroupManagementService : ITicketGroupManagementService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _dbContext;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ISortHelper<ExpandoObject> _ticketGroupSortHelper;
|
||||||
|
private readonly IDataShaper<TicketGroupDto> _ticketGroupDataShaper;
|
||||||
|
private readonly IDataShaper<TicketGroupWithTicketsDto> _ticketGroupWithTicketsDataShaper;
|
||||||
|
private readonly IPager<ExpandoObject> _pager;
|
||||||
|
|
||||||
|
public TicketGroupManagementService(ApplicationDbContext dbContext, IMapper mapper,
|
||||||
|
ISortHelper<ExpandoObject> ticketGroupSortHelper, IDataShaper<TicketGroupDto> ticketGroupDataShaper,
|
||||||
|
IDataShaper<TicketGroupWithTicketsDto> ticketGroupWithTicketsDataShaper, IPager<ExpandoObject> pager)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_mapper = mapper;
|
||||||
|
_ticketGroupSortHelper = ticketGroupSortHelper;
|
||||||
|
_ticketGroupDataShaper = ticketGroupDataShaper;
|
||||||
|
_ticketGroupWithTicketsDataShaper = ticketGroupWithTicketsDataShaper;
|
||||||
|
_pager = pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, TicketGroupDto ticketGroup)> AddTicketGroup(CreateTicketGroupDto createTicketGroupDto)
|
||||||
|
{
|
||||||
|
var ticketGroup = _mapper.Map<TicketGroup>(createTicketGroupDto);
|
||||||
|
|
||||||
|
await _dbContext.TicketGroups.AddAsync(ticketGroup);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return (true, null, _mapper.Map<TicketGroupDto>(ticketGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, TicketGroupWithTicketsDto ticketGroup)> AddTicketGroupWithTickets(
|
||||||
|
CreateTicketGroupWithTicketsDto createTicketGroupWithTicketsDto)
|
||||||
|
{
|
||||||
|
var ticketGroup = _mapper.Map<TicketGroup>(createTicketGroupWithTicketsDto);
|
||||||
|
|
||||||
|
await _dbContext.TicketGroups.AddAsync(ticketGroup);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
ticketGroup = await _dbContext.TicketGroups
|
||||||
|
.Include(tg => tg.Tickets)
|
||||||
|
.FirstAsync(tg => tg.Id == ticketGroup.Id);
|
||||||
|
|
||||||
|
return (true, null, _mapper.Map<TicketGroupWithTicketsDto>(ticketGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> ticketGroups,
|
||||||
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
|
GetTicketGroups(TicketGroupParameters parameters)
|
||||||
|
{
|
||||||
|
var dbTicketGroups = _dbContext.TicketGroups.AsQueryable();
|
||||||
|
|
||||||
|
FilterTicketGroupsByUserId(ref dbTicketGroups, parameters.UserId);
|
||||||
|
|
||||||
|
var ticketGroupDtos = _mapper.ProjectTo<TicketGroupDto>(dbTicketGroups);
|
||||||
|
var shapedData = _ticketGroupDataShaper.ShapeData(ticketGroupDtos, parameters.Fields).AsQueryable();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shapedData = _ticketGroupSortHelper.ApplySort(shapedData, parameters.Sort);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber,
|
||||||
|
parameters.PageSize);
|
||||||
|
|
||||||
|
return (true, null, shapedData, pagingMetadata);
|
||||||
|
|
||||||
|
void FilterTicketGroupsByUserId(ref IQueryable<TicketGroup> ticketGroups,
|
||||||
|
string? userId)
|
||||||
|
{
|
||||||
|
if (!ticketGroups.Any() || String.IsNullOrWhiteSpace(userId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticketGroups = ticketGroups.Where(tg => tg.UserId == userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> ticketGroups,
|
||||||
|
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||||
|
GetTicketGroupsWithTickets(TicketGroupWithTicketsParameters parameters)
|
||||||
|
{
|
||||||
|
var dbTicketGroups = _dbContext.TicketGroups
|
||||||
|
.Include(tg => tg.Tickets)
|
||||||
|
.AsQueryable();
|
||||||
|
|
||||||
|
FilterTicketGroupsByUserId(ref dbTicketGroups, parameters.UserId);
|
||||||
|
|
||||||
|
var ticketGroupDtos = _mapper.ProjectTo<TicketGroupWithTicketsDto>(dbTicketGroups);
|
||||||
|
var shapedData = _ticketGroupWithTicketsDataShaper.ShapeData(ticketGroupDtos, parameters.Fields).AsQueryable();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shapedData = _ticketGroupSortHelper.ApplySort(shapedData, parameters.Sort);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return (false, new BadRequestObjectResult("Invalid sorting string"), null, null)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber,
|
||||||
|
parameters.PageSize);
|
||||||
|
|
||||||
|
return (true, null, shapedData, pagingMetadata);
|
||||||
|
|
||||||
|
void FilterTicketGroupsByUserId(ref IQueryable<TicketGroup> ticketGroups,
|
||||||
|
string? userId)
|
||||||
|
{
|
||||||
|
if (!ticketGroups.Any() || String.IsNullOrWhiteSpace(userId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticketGroups = ticketGroups.Where(tg => tg.UserId == userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject ticketGroup)> GetTicketGroup(int id, string? fields)
|
||||||
|
{
|
||||||
|
if (!await IsTicketGroupExist(id))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbTicketGroup = await _dbContext.TicketGroups.Where(tg => tg.Id == id)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(fields))
|
||||||
|
{
|
||||||
|
fields = RouteParameters.DefaultFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticketGroupDto = _mapper.Map<TicketGroupDto>(dbTicketGroup);
|
||||||
|
var shapedRouteData = _ticketGroupDataShaper.ShapeData(ticketGroupDto, fields);
|
||||||
|
|
||||||
|
return (true, null, shapedRouteData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject ticketGroup)> GetTicketGroupWithTickets(int id, string? fields)
|
||||||
|
{
|
||||||
|
if (!await IsTicketGroupExist(id))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbTicketGroup = await _dbContext.TicketGroups.Where(tg => tg.Id == id)
|
||||||
|
.Include(tg => tg.Tickets)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(fields))
|
||||||
|
{
|
||||||
|
fields = RouteParameters.DefaultFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticketGroupDto = _mapper.Map<TicketGroupDto>(dbTicketGroup);
|
||||||
|
var shapedRouteData = _ticketGroupDataShaper.ShapeData(ticketGroupDto, fields);
|
||||||
|
|
||||||
|
return (true, null, shapedRouteData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, UpdateTicketGroupDto ticketGroup)> UpdateTicketGroup(UpdateTicketGroupDto updateTicketGroupDto)
|
||||||
|
{
|
||||||
|
var ticketGroup = _mapper.Map<TicketGroup>(updateTicketGroupDto);
|
||||||
|
_dbContext.Entry(ticketGroup).State = EntityState.Modified;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateConcurrencyException)
|
||||||
|
{
|
||||||
|
if (!await IsTicketGroupExist(updateTicketGroupDto.Id))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbTicketGroup = await _dbContext.TicketGroups.FirstAsync(r => r.Id == ticketGroup.Id);
|
||||||
|
|
||||||
|
return (true, null, _mapper.Map<UpdateTicketGroupDto>(dbTicketGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteTicketGroup(int id)
|
||||||
|
{
|
||||||
|
var dbTicketGroup = await _dbContext.TicketGroups.FirstOrDefaultAsync(tg => tg.Id == id);
|
||||||
|
|
||||||
|
if (dbTicketGroup == null)
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
_dbContext.TicketGroups.Remove(dbTicketGroup);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return (true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsTicketGroupExist(int id)
|
||||||
|
{
|
||||||
|
return await _dbContext.TicketGroups.AnyAsync(tg => tg.Id == id);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ namespace SharedModels.DataTransferObjects;
|
|||||||
public class AddressDto : CreateAddressDto
|
public class AddressDto : CreateAddressDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string FullName = null!;
|
public string FullName { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateAddressDto
|
public class CreateAddressDto
|
||||||
|
@ -15,10 +15,16 @@ public class TicketDto : CreateTicketDto
|
|||||||
public class CreateTicketDto
|
public class CreateTicketDto
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public string UserId { get; set; } = null!;
|
public int TicketGroupId { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public int VehicleEnrollmentId { get; set; }
|
public int VehicleEnrollmentId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int FirstRouteAddressId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int LastRouteAddressId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateTicketDto
|
public class UpdateTicketDto
|
||||||
@ -26,11 +32,36 @@ public class UpdateTicketDto
|
|||||||
[Required]
|
[Required]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public string? UserId { get; set; } = null!;
|
[Required]
|
||||||
public int? VehicleEnrollmentId { get; set; }
|
public int TicketGroupId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int VehicleEnrollmentId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
[DataType(DataType.DateTime)]
|
[DataType(DataType.DateTime)]
|
||||||
public DateTime PurchaseDateTimeUtc { get; set; }
|
public DateTime PurchaseDateTimeUtc { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
public bool IsReturned { get; set; } = false;
|
public bool IsReturned { get; set; } = false;
|
||||||
|
|
||||||
|
[Required]
|
||||||
public bool IsMissed { get; set; } = false;
|
public bool IsMissed { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CreateInTicketGroupTicketDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public int VehicleEnrollmentId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int FirstRouteAddressId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int LastRouteAddressId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InTicketGroupTicketDto : CreateInTicketGroupTicketDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
@ -1,6 +1,35 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace SharedModels.DataTransferObjects;
|
namespace SharedModels.DataTransferObjects;
|
||||||
|
|
||||||
public class TicketGroupDto
|
public class TicketGroupDto : CreateTicketGroupDto
|
||||||
{
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateTicketGroupDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string UserId { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateTicketGroupDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string UserId { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TicketGroupWithTicketsDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public IList<InTicketGroupTicketDto> Tickets { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateTicketGroupWithTicketsDto : CreateTicketGroupDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public IList<CreateInTicketGroupTicketDto> Tickets { get; set; } = null!;
|
||||||
}
|
}
|
@ -36,9 +36,7 @@ public class CreateVehicleEnrollmentWithDetailsDto : CreateVehicleEnrollmentDto
|
|||||||
public IList<CreateRouteAddressDetailsInVehicleEnrollmentDto> RouteAddressDetails { get; set; } = null!;
|
public IList<CreateRouteAddressDetailsInVehicleEnrollmentDto> RouteAddressDetails { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleEnrollmentWithDetailsDto
|
public class VehicleEnrollmentWithDetailsDto : VehicleEnrollmentDto
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public IList<RouteAddressDetailsInVehicleEnrollmentDto> RouteAddressDetails { get; set; } = null!;
|
public IList<RouteAddressDetailsInVehicleEnrollmentDto> RouteAddressDetails { get; set; } = null!;
|
||||||
}
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
namespace SharedModels.QueryParameters.Objects;
|
||||||
|
|
||||||
|
public class TicketGroupParameters : ParametersBase
|
||||||
|
{
|
||||||
|
public const string DefaultFields = "id,userId";
|
||||||
|
|
||||||
|
public TicketGroupParameters()
|
||||||
|
{
|
||||||
|
Fields = DefaultFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? UserId { get; set; }
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
namespace SharedModels.QueryParameters.Objects;
|
||||||
|
|
||||||
|
public class TicketGroupWithTicketsParameters : ParametersBase
|
||||||
|
{
|
||||||
|
public const string DefaultFields = "id,userId,tickets";
|
||||||
|
|
||||||
|
public TicketGroupWithTicketsParameters()
|
||||||
|
{
|
||||||
|
Fields = DefaultFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? UserId { get; set; }
|
||||||
|
}
|
@ -2,7 +2,7 @@ namespace SharedModels.QueryParameters.Statistics;
|
|||||||
|
|
||||||
public class PopularAddressesParameters : ParametersBase
|
public class PopularAddressesParameters : ParametersBase
|
||||||
{
|
{
|
||||||
public readonly string DefaultFields = "id,name,purchaseCount";
|
public readonly string DefaultFields = "id,name,fullName,purchaseCount";
|
||||||
public readonly int DefaultDays = 60;
|
public readonly int DefaultDays = 60;
|
||||||
|
|
||||||
public PopularAddressesParameters()
|
public PopularAddressesParameters()
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
namespace SharedModels.QueryParameters.Statistics;
|
||||||
|
|
||||||
|
public class PopularRoutesParameters : ParametersBase
|
||||||
|
{
|
||||||
|
public readonly string DefaultFields = "departureAddressId,departureAddress," +
|
||||||
|
"arrivalAddressId,arrivalAddress," +
|
||||||
|
"count";
|
||||||
|
public readonly int DefaultDays = 60;
|
||||||
|
|
||||||
|
public PopularRoutesParameters()
|
||||||
|
{
|
||||||
|
Fields = DefaultFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? Days { get; set; }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user