From 3d6ffa25abdc8bbf4baa9398e3acca8cc1b49834 Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Thu, 24 Nov 2022 10:57:05 +0200 Subject: [PATCH] feat: add "Popular companies" statistic --- Server/Controllers/StatisticsController.cs | 13 +- ..._Compnay_and_VehicleEnrollment.Designer.cs | 818 +++++++++++++++++ ...rences_to_Compnay_and_VehicleEnrollment.cs | 19 + ..._references_VehicleEnrollments.Designer.cs | 820 ++++++++++++++++++ ...234_Route_references_VehicleEnrollments.cs | 19 + .../ApplicationDbContextModelSnapshot.cs | 22 +- Server/Models/Company.cs | 4 +- Server/Models/Route.cs | 1 + Server/Models/Vehicle.cs | 2 + Server/Models/VehicleEnrollment.cs | 3 + Server/Program.cs | 4 +- Server/Server.csproj | 2 - Server/Services/IStatisticsService.cs | 4 +- Server/Services/StatisticsService.cs | 100 ++- .../Statistics/PopularCompaniesParameters.cs | 11 + 15 files changed, 1817 insertions(+), 25 deletions(-) create mode 100644 Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.Designer.cs create mode 100644 Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.cs create mode 100644 Server/Migrations/20221124085234_Route_references_VehicleEnrollments.Designer.cs create mode 100644 Server/Migrations/20221124085234_Route_references_VehicleEnrollments.cs create mode 100644 SharedModels/QueryParameters/Statistics/PopularCompaniesParameters.cs diff --git a/Server/Controllers/StatisticsController.cs b/Server/Controllers/StatisticsController.cs index cf7f88a..be9ebff 100644 --- a/Server/Controllers/StatisticsController.cs +++ b/Server/Controllers/StatisticsController.cs @@ -39,9 +39,18 @@ public class StatisticsController : ControllerBase } [HttpGet("companies")] - public async Task GetPopularCompanies([FromQuery] int amount = 10) + public async Task GetPopularCompanies([FromQuery] PopularCompanyParameters parameters) { - return Ok(); + var result = await _statisticsService.GetPopularCompanies(parameters); + + if (!result.IsSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.companies); } [HttpGet("stations")] diff --git a/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.Designer.cs b/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.Designer.cs new file mode 100644 index 0000000..698579e --- /dev/null +++ b/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.Designer.cs @@ -0,0 +1,818 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.Data; + +#nullable disable + +namespace Server.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20221123180952_Add_References_to_Compnay_and_VehicleEnrollment")] + partial class Add_References_to_Compnay_and_VehicleEnrollment + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("integer"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StateId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("StateId"); + + b.ToTable("Cities"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Countries"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .HasColumnType("text"); + + b.Property("PostDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Routes"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("integer"); + + b.Property("CostToNextCity") + .HasColumnType("double precision"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("TimeSpanToNextCity") + .HasColumnType("interval"); + + b.Property("WaitTimeSpan") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("States"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsMissed") + .HasColumnType("boolean"); + + b.Property("IsReturned") + .HasColumnType("boolean"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("HasBelts") + .HasColumnType("boolean"); + + b.Property("HasClimateControl") + .HasColumnType("boolean"); + + b.Property("HasOutlet") + .HasColumnType("boolean"); + + b.Property("HasStewardess") + .HasColumnType("boolean"); + + b.Property("HasTV") + .HasColumnType("boolean"); + + b.Property("HasWC") + .HasColumnType("boolean"); + + b.Property("HasWiFi") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelationComment") + .HasColumnType("text"); + + b.Property("DelayTimeSpan") + .HasColumnType("interval"); + + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("IsCanceled") + .HasColumnType("boolean"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("VehicleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("VehicleEnrollments"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.HasOne("Server.Models.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("City"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.HasOne("Server.Models.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.HasOne("Server.Models.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany("Reviews") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Reviews") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.HasOne("Server.Models.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.HasOne("Server.Models.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany("Tickets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Tickets") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => + { + b1.Property("UserId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("CreationDateTime") + .HasColumnType("timestamp with time zone"); + + b1.Property("ExpiryDateTime") + .HasColumnType("timestamp with time zone"); + + b1.Property("Revoked") + .HasColumnType("timestamp with time zone"); + + b1.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("UserId", "Id"); + + b1.ToTable("RefreshToken"); + + b1.WithOwner() + .HasForeignKey("UserId"); + }); + + b.Navigation("RefreshTokens"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.HasOne("Server.Models.Route", "Route") + .WithMany() + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Vehicle", "Vehicle") + .WithMany("VehicleEnrollments") + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Navigation("Reviews"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Navigation("Reviews"); + + b.Navigation("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.cs b/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.cs new file mode 100644 index 0000000..4ed9850 --- /dev/null +++ b/Server/Migrations/20221123180952_Add_References_to_Compnay_and_VehicleEnrollment.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Add_References_to_Compnay_and_VehicleEnrollment : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.Designer.cs b/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.Designer.cs new file mode 100644 index 0000000..18cb9b7 --- /dev/null +++ b/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.Designer.cs @@ -0,0 +1,820 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.Data; + +#nullable disable + +namespace Server.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20221124085234_Route_references_VehicleEnrollments")] + partial class Route_references_VehicleEnrollments + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("integer"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StateId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("StateId"); + + b.ToTable("Cities"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Countries"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .HasColumnType("text"); + + b.Property("PostDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Routes"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("integer"); + + b.Property("CostToNextCity") + .HasColumnType("double precision"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("TimeSpanToNextCity") + .HasColumnType("interval"); + + b.Property("WaitTimeSpan") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("States"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsMissed") + .HasColumnType("boolean"); + + b.Property("IsReturned") + .HasColumnType("boolean"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("HasBelts") + .HasColumnType("boolean"); + + b.Property("HasClimateControl") + .HasColumnType("boolean"); + + b.Property("HasOutlet") + .HasColumnType("boolean"); + + b.Property("HasStewardess") + .HasColumnType("boolean"); + + b.Property("HasTV") + .HasColumnType("boolean"); + + b.Property("HasWC") + .HasColumnType("boolean"); + + b.Property("HasWiFi") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelationComment") + .HasColumnType("text"); + + b.Property("DelayTimeSpan") + .HasColumnType("interval"); + + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("IsCanceled") + .HasColumnType("boolean"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("VehicleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("VehicleEnrollments"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.HasOne("Server.Models.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("City"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.HasOne("Server.Models.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.HasOne("Server.Models.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany("Reviews") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Reviews") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.HasOne("Server.Models.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.HasOne("Server.Models.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany("Tickets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Tickets") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => + { + b1.Property("UserId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("CreationDateTime") + .HasColumnType("timestamp with time zone"); + + b1.Property("ExpiryDateTime") + .HasColumnType("timestamp with time zone"); + + b1.Property("Revoked") + .HasColumnType("timestamp with time zone"); + + b1.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("UserId", "Id"); + + b1.ToTable("RefreshToken"); + + b1.WithOwner() + .HasForeignKey("UserId"); + }); + + b.Navigation("RefreshTokens"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.HasOne("Server.Models.Route", "Route") + .WithMany("VehicleEnrollments") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Vehicle", "Vehicle") + .WithMany("VehicleEnrollments") + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Navigation("RouteAddresses"); + + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Navigation("Reviews"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Navigation("Reviews"); + + b.Navigation("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.cs b/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.cs new file mode 100644 index 0000000..df97d75 --- /dev/null +++ b/Server/Migrations/20221124085234_Route_references_VehicleEnrollments.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Route_references_VehicleEnrollments : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Server/Migrations/ApplicationDbContextModelSnapshot.cs b/Server/Migrations/ApplicationDbContextModelSnapshot.cs index 878f566..857f1b9 100644 --- a/Server/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Server/Migrations/ApplicationDbContextModelSnapshot.cs @@ -636,7 +636,7 @@ namespace Server.Migrations .IsRequired(); b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") - .WithMany() + .WithMany("Reviews") .HasForeignKey("VehicleEnrollmentId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -685,7 +685,7 @@ namespace Server.Migrations .IsRequired(); b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") - .WithMany() + .WithMany("Tickets") .HasForeignKey("VehicleEnrollmentId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -746,13 +746,13 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.VehicleEnrollment", b => { b.HasOne("Server.Models.Route", "Route") - .WithMany() + .WithMany("VehicleEnrollments") .HasForeignKey("RouteId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne("Server.Models.Vehicle", "Vehicle") - .WithMany() + .WithMany("VehicleEnrollments") .HasForeignKey("VehicleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -785,6 +785,8 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.Route", b => { b.Navigation("RouteAddresses"); + + b.Navigation("VehicleEnrollments"); }); modelBuilder.Entity("Server.Models.State", b => @@ -798,6 +800,18 @@ namespace Server.Migrations b.Navigation("Tickets"); }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Navigation("Reviews"); + + b.Navigation("Tickets"); + }); #pragma warning restore 612, 618 } } diff --git a/Server/Models/Company.cs b/Server/Models/Company.cs index 204731b..60bb1a4 100644 --- a/Server/Models/Company.cs +++ b/Server/Models/Company.cs @@ -13,6 +13,6 @@ public class Company [ForeignKey("OwnerId")] public string OwnerId { get; set; } = null!; public User Owner { get; set; } = null!; - - public List? Vehicles { get; set; } + + public virtual List Vehicles { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Models/Route.cs b/Server/Models/Route.cs index d8644ff..8134fcf 100644 --- a/Server/Models/Route.cs +++ b/Server/Models/Route.cs @@ -11,4 +11,5 @@ public class Route public string Type { get; set; } = null!; public virtual IList RouteAddresses { get; set; } = null!; + public virtual IList VehicleEnrollments { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Models/Vehicle.cs b/Server/Models/Vehicle.cs index e0f034b..dc96df8 100644 --- a/Server/Models/Vehicle.cs +++ b/Server/Models/Vehicle.cs @@ -23,4 +23,6 @@ public class Vehicle public bool HasTV { get; set; } public bool HasOutlet { get; set; } public bool HasBelts { get; set; } + + public IList VehicleEnrollments { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Models/VehicleEnrollment.cs b/Server/Models/VehicleEnrollment.cs index 554dca4..7271d85 100644 --- a/Server/Models/VehicleEnrollment.cs +++ b/Server/Models/VehicleEnrollment.cs @@ -22,4 +22,7 @@ public class VehicleEnrollment public bool IsCanceled { get; set; } = false; public string? CancelationComment { get; set; } = null!; + + public IList Tickets { get; set; } = null!; + public IList Reviews { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index 9646808..c2217bf 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -120,7 +120,6 @@ builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, DataShaper>(); -builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper>(); @@ -133,6 +132,9 @@ builder.Services.AddScoped, DataShaper, DataShaper>(); builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); + // Adding DB Context with PostgreSQL var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext(options => diff --git a/Server/Server.csproj b/Server/Server.csproj index 581a306..f6c6bbf 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -17,8 +17,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Server/Services/IStatisticsService.cs b/Server/Services/IStatisticsService.cs index c3c0e51..6e1b7fa 100644 --- a/Server/Services/IStatisticsService.cs +++ b/Server/Services/IStatisticsService.cs @@ -13,8 +13,8 @@ public interface IStatisticsService Task<(bool IsSucceed, string? message, IEnumerable users, PagingMetadata pagingMetadata)> GetEngagedUsers(EngagedUserParameters parameters); - Task<(bool IsSucceed, string? message, IEnumerable companies)> - GetPopularCompanies(int amount); + Task<(bool IsSucceed, string? message, IEnumerable companies, PagingMetadata pagingMetadata)> + GetPopularCompanies(PopularCompanyParameters parameters); Task<(bool IsSucceed, string? message, IEnumerable stations)> GetPopularStations(int amount); diff --git a/Server/Services/StatisticsService.cs b/Server/Services/StatisticsService.cs index 7765589..2810472 100644 --- a/Server/Services/StatisticsService.cs +++ b/Server/Services/StatisticsService.cs @@ -15,13 +15,15 @@ public class StatisticsService : IStatisticsService private readonly ApplicationDbContext _dbContext; private readonly IMapper _mapper; private readonly IDataShaper _userDataShaper; + private readonly IDataShaper _companyDataShaper; public StatisticsService(ApplicationDbContext dbContext, IMapper mapper, - IDataShaper userDataShaper) + IDataShaper userDataShaper, IDataShaper companyDataShaper) { _dbContext = dbContext; _mapper = mapper; _userDataShaper = userDataShaper; + _companyDataShaper = companyDataShaper; } // Popularity is measured in number of purchased tickets @@ -37,7 +39,7 @@ public class StatisticsService : IStatisticsService { var fromDateUtc = DateTime.UtcNow - TimeSpan.FromDays(parameters.Days ?? parameters.DefaultDays); - var resultObjectList = _dbContext.Users + var resultObjects = _dbContext.Users .Include(u => u.Tickets) .Select(u => new { @@ -48,22 +50,22 @@ public class StatisticsService : IStatisticsService .Take(parameters.Amount); - var dbUsers = resultObjectList.Select(i => i.User); + var dbUsers = resultObjects.Select(i => i.User); var pagingMetadata = ApplyPaging(ref dbUsers, parameters.PageNumber, parameters.PageSize); - var userDtos = _mapper.ProjectTo(dbUsers).ToList(); + var userDtos = _mapper.ProjectTo(dbUsers).ToArray(); var shapedData = _userDataShaper .ShapeData(userDtos, parameters.Fields ?? parameters.DefaultFields) - .ToList(); + .ToArray(); if (parameters.Fields != null && parameters.Fields.ToLower().Contains("ticketCount".ToLower())) { - var dbUsersList = await dbUsers.ToListAsync(); - for (int i = 0; i < dbUsersList.Count; i++) + var dbUsersArray = await dbUsers.ToArrayAsync(); + for (int i = 0; i < dbUsersArray.Length; i++) { - var ticketCount = dbUsersList[i].Tickets.Count; + var ticketCount = dbUsersArray[i].Tickets.Count; shapedData[i].TryAdd("TicketCount", ticketCount); } } @@ -71,11 +73,85 @@ public class StatisticsService : IStatisticsService return (true, null, shapedData, pagingMetadata); } - // Popularity is measured in number of purchased tickets & rating - public async Task<(bool IsSucceed, string? message, IEnumerable companies)> - GetPopularCompanies(int amount) + // Popularity is measured in average rating of all VehicleEnrollments of a company + public async Task<(bool IsSucceed, string? message, IEnumerable companies, PagingMetadata pagingMetadata)> + GetPopularCompanies(PopularCompanyParameters parameters) { - throw new NotImplementedException(); + var dbCompanies = _dbContext.Companies + .Include(c => c.Vehicles) + .ThenInclude(v => v.VehicleEnrollments) + .ThenInclude(ve => ve.Tickets) + .Include(c => c.Vehicles) + .ThenInclude(v => v.VehicleEnrollments) + .ThenInclude(ve => ve.Reviews); + + // Calculate average rating for each company + + var dbCompaniesArray = await dbCompanies.ToArrayAsync(); + double[] companiesAvgRatings = new double[dbCompaniesArray.Length]; + + for (int i = 0; i < dbCompaniesArray.Length; i++) + { + double tempC = 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; + } + + tempV += tempVE / ve.Reviews.Count; + } + + tempC += tempV / v.VehicleEnrollments.Count; + } + + companiesAvgRatings[i] = tempC / dbCompaniesArray[i].Vehicles.Count; + } + + // Sort companiesAvgRatings and apply the same sorting to dbCompaniesArray + + int n = companiesAvgRatings.Length; + for (int i = 0; i < n - 1; i++) + { + for (int j = 0; j < n - i - 1; j++) + { + if (companiesAvgRatings[j] > companiesAvgRatings[j + 1]) + { + // swap temp and arr[i] + (companiesAvgRatings[j], companiesAvgRatings[j + 1]) = (companiesAvgRatings[j + 1], companiesAvgRatings[j]); + (dbCompaniesArray[j], dbCompaniesArray[j + 1]) = (dbCompaniesArray[j + 1], dbCompaniesArray[j]); + } + } + } + + companiesAvgRatings = companiesAvgRatings.Skip(companiesAvgRatings.Length - parameters.Amount).Reverse().ToArray(); + var popularCompanies = dbCompaniesArray.Skip(companiesAvgRatings.Length - parameters.Amount).Reverse().AsQueryable(); + + // Apply paging, convert to DTOs and shape data + + var pagingMetadata = ApplyPaging(ref popularCompanies, parameters.PageNumber, parameters.PageSize); + var companyDtos = _mapper.ProjectTo(popularCompanies).ToArray(); + + var shapedData = _companyDataShaper.ShapeData(companyDtos, parameters.Fields ?? parameters.DefaultFields).ToArray(); + + if (parameters.Fields != null && + parameters.Fields.ToLower().Contains("rating".ToLower())) + { + for (int i = 0; i < shapedData.Length; i++) + { + shapedData[i].TryAdd("Rating", companiesAvgRatings[i]); + } + } + + return (true, null, shapedData, pagingMetadata); } // Popularity is measured in number of routes using the station diff --git a/SharedModels/QueryParameters/Statistics/PopularCompaniesParameters.cs b/SharedModels/QueryParameters/Statistics/PopularCompaniesParameters.cs new file mode 100644 index 0000000..b820746 --- /dev/null +++ b/SharedModels/QueryParameters/Statistics/PopularCompaniesParameters.cs @@ -0,0 +1,11 @@ +namespace SharedModels.QueryParameters.Statistics; + +public class PopularCompanyParameters : ParametersBase +{ + public readonly string DefaultFields = "id,ownerId,name,rating"; + + public PopularCompanyParameters() + { + Fields = DefaultFields; + } +} \ No newline at end of file