diff --git a/Backend/Backend.csproj b/Backend/Backend.csproj index a2c7635..22f8876 100644 --- a/Backend/Backend.csproj +++ b/Backend/Backend.csproj @@ -14,12 +14,19 @@ PreserveNewest + + PreserveNewest + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Backend/Program.cs b/Backend/Program.cs index 7a2f512..0ea0988 100644 --- a/Backend/Program.cs +++ b/Backend/Program.cs @@ -18,7 +18,6 @@ static async Task Main(string[] args) // Main scope of application using (var scope = host.Services.CreateScope()) { - //TODO this is just temp, delete later var services = scope.ServiceProvider; var dataProcessingService = services.GetRequiredService(); diff --git a/Backend/Services/DataProcessingService.cs b/Backend/Services/DataProcessingService.cs index 648a262..e0ad03f 100644 --- a/Backend/Services/DataProcessingService.cs +++ b/Backend/Services/DataProcessingService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Backend.Clients; using Backend.Mappers; using HouseScout.Model; @@ -21,15 +22,39 @@ HouseScoutContext context public async Task ProcessData() { + var existingEstates = _context.Estates.ToList(); + + var fetchedEstates = new List(); + foreach (var kvp in _clientsAndMappers) { IClient client = kvp.Key; IMapper mapper = kvp.Value; var data = await client.FetchDataAsync(); - var mappedData = mapper.MapResponseToModel(data); - _context.Estates.AddRange(mappedData); - await _context.SaveChangesAsync(); + fetchedEstates.AddRange(mapper.MapResponseToModel(data)); + } + + var fetchedEstatesSet = new HashSet(fetchedEstates.Select(fe => fe.ApiId)); + var existingEstatesSet = new HashSet(existingEstates.Select(fe => fe.ApiId)); + + var estatesToAdd = fetchedEstates + .Where(fe => !existingEstatesSet.Contains(fe.ApiId)) + .ToList(); + + var estatesToRemove = existingEstates + .Where(ee => !fetchedEstatesSet.Contains(ee.ApiId)) + .ToList(); + + if (estatesToAdd.Any()) + { + await _context.Estates.AddRangeAsync(estatesToAdd); + } + + if (estatesToRemove.Any()) + { + _context.Estates.RemoveRange(estatesToRemove); } + await _context.SaveChangesAsync(); } } diff --git a/SharedDependencies/Migrations/20240901114150_InitialCreate.Designer.cs b/SharedDependencies/Migrations/20240901114150_InitialCreate.Designer.cs new file mode 100644 index 0000000..d22c1c2 --- /dev/null +++ b/SharedDependencies/Migrations/20240901114150_InitialCreate.Designer.cs @@ -0,0 +1,69 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SharedDependencies.Model; + +#nullable disable + +namespace SharedDependencies.Migrations +{ + [DbContext(typeof(HouseScoutContext))] + [Migration("20240901114150_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("HouseScout.Model.Estate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("text"); + + b.Property("ApiId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ApiType") + .HasColumnType("integer"); + + b.Property("EstateType") + .HasColumnType("integer"); + + b.Property("Link") + .IsRequired() + .HasColumnType("text"); + + b.Property("OfferType") + .HasColumnType("integer"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("Surface") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.ToTable("Estates"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SharedDependencies/Migrations/20240901114150_InitialCreate.cs b/SharedDependencies/Migrations/20240901114150_InitialCreate.cs new file mode 100644 index 0000000..0cf94ee --- /dev/null +++ b/SharedDependencies/Migrations/20240901114150_InitialCreate.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace SharedDependencies.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Estates", + columns: table => new + { + Id = table + .Column(type: "integer", nullable: false) + .Annotation( + "Npgsql:ValueGenerationStrategy", + NpgsqlValueGenerationStrategy.IdentityByDefaultColumn + ), + ApiType = table.Column(type: "integer", nullable: false), + ApiId = table.Column(type: "text", nullable: false), + Address = table.Column(type: "text", nullable: false), + Price = table.Column(type: "numeric", nullable: false), + Link = table.Column(type: "text", nullable: false), + Surface = table.Column(type: "double precision", nullable: false), + EstateType = table.Column(type: "integer", nullable: false), + OfferType = table.Column(type: "integer", nullable: false), + }, + constraints: table => + { + table.PrimaryKey("PK_Estates", x => x.Id); + } + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable(name: "Estates"); + } + } +} diff --git a/SharedDependencies/Migrations/HouseScoutContextModelSnapshot.cs b/SharedDependencies/Migrations/HouseScoutContextModelSnapshot.cs new file mode 100644 index 0000000..e100372 --- /dev/null +++ b/SharedDependencies/Migrations/HouseScoutContextModelSnapshot.cs @@ -0,0 +1,66 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SharedDependencies.Model; + +#nullable disable + +namespace SharedDependencies.Migrations +{ + [DbContext(typeof(HouseScoutContext))] + partial class HouseScoutContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("HouseScout.Model.Estate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("text"); + + b.Property("ApiId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ApiType") + .HasColumnType("integer"); + + b.Property("EstateType") + .HasColumnType("integer"); + + b.Property("Link") + .IsRequired() + .HasColumnType("text"); + + b.Property("OfferType") + .HasColumnType("integer"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("Surface") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.ToTable("Estates"); + }); +#pragma warning restore 612, 618 + } + } +}