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, CreateTicketDto>().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, 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)
|
||||
{
|
||||
return BadRequest(result.message);
|
||||
return BadRequest(result.actionResult);
|
||||
}
|
||||
|
||||
return File(result.ticketPdf, "application/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")]
|
||||
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")]
|
||||
@ -30,7 +39,7 @@ public class StatisticsController : ControllerBase
|
||||
|
||||
if (!result.IsSucceed)
|
||||
{
|
||||
return BadRequest(result.message);
|
||||
return BadRequest(result.actionResult);
|
||||
}
|
||||
|
||||
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||
@ -45,7 +54,7 @@ public class StatisticsController : ControllerBase
|
||||
|
||||
if (!result.IsSucceed)
|
||||
{
|
||||
return BadRequest(result.message);
|
||||
return BadRequest(result.actionResult);
|
||||
}
|
||||
|
||||
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
|
||||
@ -60,7 +69,7 @@ public class StatisticsController : ControllerBase
|
||||
|
||||
if (!result.IsSucceed)
|
||||
{
|
||||
return BadRequest(result.message);
|
||||
return BadRequest(result.actionResult);
|
||||
}
|
||||
|
||||
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<IAddressManagementService, AddressManagementService>();
|
||||
builder.Services.AddScoped<ITicketManagementService, TicketManagementService>();
|
||||
builder.Services.AddScoped<ITicketGroupManagementService, TicketGroupManagementService>();
|
||||
builder.Services.AddScoped<IReviewManagementService, ReviewManagementService>();
|
||||
builder.Services.AddScoped<ICompanyManagementService, CompanyManagementService>();
|
||||
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<AddressDto>, DataShaper<AddressDto>>();
|
||||
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<CompanyDto>, DataShaper<CompanyDto>>();
|
||||
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<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
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
|
@ -81,8 +81,12 @@ public class AddressManagementService : IAddressManagementService
|
||||
return;
|
||||
}
|
||||
|
||||
addresses = addresses.Where(a =>
|
||||
a.Name.ToLower().Contains(search.ToLower()));
|
||||
// TODO: Remove client evaluation
|
||||
|
||||
addresses = addresses.ToArray().Where(a =>
|
||||
a.Name.ToLower().Contains(search.ToLower()) ||
|
||||
a.GetFullName().ToLower().Contains(search.ToLower()))
|
||||
.AsQueryable();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 Microsoft.AspNetCore.Mvc;
|
||||
using Server.Models;
|
||||
using SharedModels.QueryParameters;
|
||||
using SharedModels.QueryParameters.Statistics;
|
||||
@ -7,18 +8,19 @@ namespace Server.Services;
|
||||
|
||||
public interface IStatisticsService
|
||||
{
|
||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> route)>
|
||||
GetPopularRoutes(int amount);
|
||||
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> route,
|
||||
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)>
|
||||
GetEngagedUsers(EngagedUserParameters parameters);
|
||||
|
||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> companies,
|
||||
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> companies,
|
||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
GetPopularCompanies(PopularCompanyParameters parameters);
|
||||
|
||||
Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> stations,
|
||||
Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> stations,
|
||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
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 MigraDocCore.DocumentObjectModel;
|
||||
using MigraDocCore.DocumentObjectModel.Tables;
|
||||
using MigraDocCore.Rendering;
|
||||
using PdfSharpCore;
|
||||
using PdfSharpCore.Drawing;
|
||||
using PdfSharpCore.Pdf;
|
||||
using Server.Data;
|
||||
using Server.Models;
|
||||
using Route = Server.Models.Route;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
@ -18,8 +21,13 @@ public class ReportService : IReportService
|
||||
_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
|
||||
.Include(tg => tg.User)
|
||||
.Include(tg => tg.Tickets)
|
||||
@ -42,7 +50,7 @@ public class ReportService : IReportService
|
||||
.ThenInclude(t => t.VehicleEnrollment)
|
||||
.ThenInclude(ve => ve.RouteAddressDetails)
|
||||
|
||||
.FirstOrDefaultAsync(tg => tg.Id == ticketGroupId);
|
||||
.FirstAsync(tg => tg.Id == ticketGroupId);
|
||||
|
||||
// Define document
|
||||
|
||||
@ -53,8 +61,7 @@ public class ReportService : IReportService
|
||||
// Craft document
|
||||
|
||||
var pdfPage = document.AddPage();
|
||||
pdfPage.Width = XUnit.FromCentimeter(21.0);
|
||||
pdfPage.Height = XUnit.FromCentimeter(29.7);
|
||||
pdfPage.Size = PageSize.A4;
|
||||
|
||||
var gfx = XGraphics.FromPdfPage(pdfPage);
|
||||
// HACK²
|
||||
@ -248,7 +255,8 @@ public class ReportService : IReportService
|
||||
|
||||
row.Cells[4].MergeRight = 3;
|
||||
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].MergeDown = 1;
|
||||
@ -281,7 +289,8 @@ public class ReportService : IReportService
|
||||
|
||||
row.Cells[4].MergeRight = 3;
|
||||
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].MergeDown = 1;
|
||||
@ -341,14 +350,16 @@ public class ReportService : IReportService
|
||||
|
||||
row.Cells[1].MergeRight = 3;
|
||||
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].AddParagraph("Куди");
|
||||
|
||||
row.Cells[6].MergeRight = 3;
|
||||
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].AddParagraph("Ціна");
|
||||
@ -386,6 +397,7 @@ public class ReportService : IReportService
|
||||
|
||||
if (routeAddress.AddressId == ticket.FirstRouteAddressId)
|
||||
{
|
||||
departureDateTimeUtc += details.WaitTimeSpan;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -414,6 +426,7 @@ public class ReportService : IReportService
|
||||
}
|
||||
|
||||
arrivalDateTimeUtc += details.TimeSpanToNextCity;
|
||||
arrivalDateTimeUtc += details.WaitTimeSpan;
|
||||
}
|
||||
|
||||
return arrivalDateTimeUtc;
|
||||
@ -465,10 +478,706 @@ public class ReportService : IReportService
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
await _dbContext.Routes.AddAsync(route);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
@ -78,9 +76,9 @@ public class RouteManagementService : IRouteManagementService
|
||||
{
|
||||
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,
|
||||
@ -133,7 +131,7 @@ public class RouteManagementService : IRouteManagementService
|
||||
{
|
||||
shapedData = _routeSortHelper.ApplySort(shapedData, parameters.Sort);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
return (false, new BadRequestObjectResult("Invalid sorting string"), null, null)!;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
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.Statistics;
|
||||
@ -17,11 +17,14 @@ public class StatisticsService : IStatisticsService
|
||||
private readonly IDataShaper<UserDto> _userDataShaper;
|
||||
private readonly IDataShaper<CompanyDto> _companyDataShaper;
|
||||
private readonly IDataShaper<AddressDto> _addressDataShaper;
|
||||
private readonly IDataShaper<ExpandoObject> _expandoDataShaper;
|
||||
private readonly IPager<ExpandoObject> _pager;
|
||||
private readonly ISortHelper<ExpandoObject> _sortHelper;
|
||||
|
||||
public StatisticsService(ApplicationDbContext dbContext, IMapper mapper,
|
||||
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;
|
||||
_mapper = mapper;
|
||||
@ -29,25 +32,131 @@ public class StatisticsService : IStatisticsService
|
||||
_companyDataShaper = companyDataShaper;
|
||||
_addressDataShaper = addressDataShaper;
|
||||
_pager = pager;
|
||||
_expandoDataShaper = expandoDataShaper;
|
||||
_sortHelper = sortHelper;
|
||||
}
|
||||
|
||||
// Popularity is measured in number of purchased tickets
|
||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> route)>
|
||||
GetPopularRoutes(int amount)
|
||||
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> route,
|
||||
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
|
||||
// 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)>
|
||||
GetEngagedUsers(EngagedUserParameters parameters)
|
||||
{
|
||||
var fromDateUtc =
|
||||
DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays);
|
||||
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)
|
||||
.ThenInclude(tg => tg.Tickets)
|
||||
.Select(u => new
|
||||
@ -57,10 +166,9 @@ public class StatisticsService : IStatisticsService
|
||||
tg.Tickets.First().PurchaseDateTimeUtc >= fromDateUtc)
|
||||
})
|
||||
.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 shapedDataArray = _userDataShaper
|
||||
.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
|
||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> companies,
|
||||
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> companies,
|
||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
GetPopularCompanies(PopularCompanyParameters parameters)
|
||||
{
|
||||
@ -105,28 +213,22 @@ public class StatisticsService : IStatisticsService
|
||||
|
||||
for (int i = 0; i < dbCompaniesArray.Length; i++)
|
||||
{
|
||||
double tempC = 0;
|
||||
double result = 0;
|
||||
int reviewCount = 0;
|
||||
|
||||
foreach (var v in dbCompaniesArray[i].Vehicles)
|
||||
{
|
||||
double tempV = 0;
|
||||
|
||||
foreach (var ve in v.VehicleEnrollments)
|
||||
{
|
||||
double tempVE = 0;
|
||||
|
||||
foreach (var r in ve.Reviews)
|
||||
{
|
||||
tempVE += r.Rating;
|
||||
result += r.Rating;
|
||||
reviewCount++;
|
||||
}
|
||||
|
||||
tempV += tempVE / ve.Reviews.Count;
|
||||
}
|
||||
|
||||
tempC += tempV / v.VehicleEnrollments.Count;
|
||||
}
|
||||
|
||||
companiesAvgRatings[i] = tempC / dbCompaniesArray[i].Vehicles.Count;
|
||||
companiesAvgRatings[i] = reviewCount != 0 ? Math.Round(result / reviewCount, 5) : 0;
|
||||
}
|
||||
|
||||
// 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
|
||||
public async Task<(bool IsSucceed, string? message, IEnumerable<ExpandoObject> stations,
|
||||
public async Task<(bool IsSucceed, IActionResult? actionResult, IEnumerable<ExpandoObject> stations,
|
||||
PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
GetPopularStations(PopularAddressesParameters parameters)
|
||||
{
|
||||
// throw new NotImplementedException();
|
||||
|
||||
var fromDateUtc =
|
||||
DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays);
|
||||
parameters.Days ??= parameters.DefaultDays;
|
||||
var fromDateUtc = DateTime.UtcNow.Date - TimeSpan.FromDays((double) parameters.Days);
|
||||
|
||||
var dbTicketGroupsArray = await _dbContext.TicketGroups
|
||||
.Include(tg => tg.Tickets)
|
||||
@ -195,22 +295,25 @@ public class StatisticsService : IStatisticsService
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
// addressIds addressCountDict and )
|
||||
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))
|
||||
.OrderBy(a => a.Id).ToArrayAsync();
|
||||
|
||||
@ -238,11 +344,12 @@ public class StatisticsService : IStatisticsService
|
||||
{
|
||||
for (int j = 0; j < n - i - 1; j++)
|
||||
{
|
||||
if (addressCountArray[j] > addressCountArray[j + 1])
|
||||
if (addressCountArray[j] > addressCountArray[j + 1])
|
||||
{
|
||||
// swap temp and arr[i]
|
||||
(addressCountArray[j], addressCountArray[j + 1]) = (addressCountArray[j + 1], addressCountArray[j]);
|
||||
(dbAddressesArray[j], dbAddressesArray[j + 1]) = (dbAddressesArray[j + 1], dbAddressesArray[j]);
|
||||
(addressCountArray[j], addressCountArray[j + 1]) =
|
||||
(addressCountArray[j + 1], addressCountArray[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 int Id { get; set; }
|
||||
public string FullName = null!;
|
||||
public string FullName { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class CreateAddressDto
|
||||
|
@ -15,10 +15,16 @@ public class TicketDto : CreateTicketDto
|
||||
public class CreateTicketDto
|
||||
{
|
||||
[Required]
|
||||
public string UserId { get; set; } = null!;
|
||||
public int TicketGroupId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int VehicleEnrollmentId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int FirstRouteAddressId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int LastRouteAddressId { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateTicketDto
|
||||
@ -26,11 +32,36 @@ public class UpdateTicketDto
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string? UserId { get; set; } = null!;
|
||||
public int? VehicleEnrollmentId { get; set; }
|
||||
[Required]
|
||||
public int TicketGroupId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int VehicleEnrollmentId { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.DateTime)]
|
||||
public DateTime PurchaseDateTimeUtc { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool IsReturned { get; set; } = false;
|
||||
|
||||
[Required]
|
||||
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;
|
||||
|
||||
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 class VehicleEnrollmentWithDetailsDto
|
||||
public class VehicleEnrollmentWithDetailsDto : VehicleEnrollmentDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
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 readonly string DefaultFields = "id,name,purchaseCount";
|
||||
public readonly string DefaultFields = "id,name,fullName,purchaseCount";
|
||||
public readonly int DefaultDays = 60;
|
||||
|
||||
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