diff --git a/CHANGELOG.md b/CHANGELOG.md index efd361248..a2cea24a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - Added data extraction API. - Added support to extract coordinates from a borehole attachment. - Show ChangedAt and ChangedBy information in borehole detail header. -- Add JSON export for single and multiple boreholes. +- Add JSON and CSV export for single and multiple boreholes. - The workgroup name is now displayed in the borehole location tab. - Added new API endpoint to retrieve all boreholes. - Add JSON import for boreholes. diff --git a/src/api/BdmsContext.cs b/src/api/BdmsContext.cs index 4faa93f36..d4f1b5200 100644 --- a/src/api/BdmsContext.cs +++ b/src/api/BdmsContext.cs @@ -159,16 +159,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(bf => bf.BoreholeId), j => j.HasKey(bf => new { bf.BoreholeId, bf.CodelistId })); - modelBuilder.Entity().HasOne(l => l.Chronostratigraphy).WithMany().HasForeignKey(l => l.ChronostratigraphyId); + modelBuilder.Entity().HasOne(l => l.ChronostratigraphyTopBedrock).WithMany().HasForeignKey(l => l.ChronostratigraphyTopBedrockId); modelBuilder.Entity().HasOne(l => l.Type).WithMany().HasForeignKey(l => l.TypeId); modelBuilder.Entity().HasOne(l => l.Hrs).WithMany().HasForeignKey(l => l.HrsId); modelBuilder.Entity().HasOne(l => l.LithologyTopBedrock).WithMany().HasForeignKey(l => l.LithologyTopBedrockId); - modelBuilder.Entity().HasOne(l => l.Lithostratigraphy).WithMany().HasForeignKey(l => l.LithostratigraphyId); + modelBuilder.Entity().HasOne(l => l.LithostratigraphyTopBedrock).WithMany().HasForeignKey(l => l.LithostratigraphyTopBedrockId); modelBuilder.Entity().HasOne(l => l.Purpose).WithMany().HasForeignKey(l => l.PurposeId); - modelBuilder.Entity().HasOne(l => l.QtDepth).WithMany().HasForeignKey(l => l.QtDepthId); + modelBuilder.Entity().HasOne(l => l.DepthPrecision).WithMany().HasForeignKey(l => l.DepthPrecisionId); modelBuilder.Entity().HasOne(l => l.ElevationPrecision).WithMany().HasForeignKey(l => l.ElevationPrecisionId); modelBuilder.Entity().HasOne(l => l.LocationPrecision).WithMany().HasForeignKey(l => l.LocationPrecisionId); - modelBuilder.Entity().HasOne(l => l.QtReferenceElevation).WithMany().HasForeignKey(l => l.QtReferenceElevationId); + modelBuilder.Entity().HasOne(l => l.ReferenceElevationPrecision).WithMany().HasForeignKey(l => l.ReferenceElevationPrecisionId); modelBuilder.Entity().HasOne(l => l.ReferenceElevationType).WithMany().HasForeignKey(l => l.ReferenceElevationTypeId); modelBuilder.Entity().HasOne(l => l.Restriction).WithMany().HasForeignKey(l => l.RestrictionId); modelBuilder.Entity().HasOne(l => l.Status).WithMany().HasForeignKey(l => l.StatusId); diff --git a/src/api/BdmsContextExtensions.cs b/src/api/BdmsContextExtensions.cs index 6a6f7d77d..8afbf51d7 100644 --- a/src/api/BdmsContextExtensions.cs +++ b/src/api/BdmsContextExtensions.cs @@ -159,7 +159,7 @@ public static void SeedData(this BdmsContext context) .RuleFor(o => o.Restriction, _ => default!) .RuleFor(o => o.RestrictionUntil, f => f.Date.Future().ToUniversalTime().OrNull(f, .9f)) .RuleFor(o => o.OriginalName, f => f.Name.FullName()) - .RuleFor(o => o.AlternateName, f => "") + .RuleFor(o => o.Name, f => "") .RuleFor(o => o.LocationPrecisionId, f => f.PickRandom(locationPrecisionIds).OrNull(f, .1f)) .RuleFor(o => o.LocationPrecision, _ => default!) .RuleFor(o => o.ElevationPrecisionId, f => f.PickRandom(elevationPrecisionIds).OrNull(f, .1f)) @@ -172,21 +172,21 @@ public static void SeedData(this BdmsContext context) .RuleFor(o => o.Purpose, _ => default!) .RuleFor(o => o.StatusId, f => f.PickRandom(statusIds).OrNull(f, .05f)) .RuleFor(o => o.Status, _ => default!) - .RuleFor(o => o.QtDepthId, f => f.PickRandom(qtDepthIds).OrNull(f, .05f)) - .RuleFor(o => o.QtDepth, _ => default!) + .RuleFor(o => o.DepthPrecisionId, f => f.PickRandom(qtDepthIds).OrNull(f, .05f)) + .RuleFor(o => o.DepthPrecision, _ => default!) .RuleFor(o => o.TopBedrockFreshMd, f => f.Random.Double(0, 1000).OrNull(f, .05f)) .RuleFor(o => o.TopBedrockWeatheredMd, f => f.Random.Double(0, 2).OrNull(f, .05f)) .RuleFor(o => o.HasGroundwater, f => f.Random.Bool().OrNull(f, .2f)) .RuleFor(o => o.Remarks, f => f.Rant.Review().OrNull(f, .05f)) .RuleFor(o => o.LithologyTopBedrockId, f => f.PickRandom(lithologyTopBedrockIds).OrNull(f, .05f)) .RuleFor(o => o.LithologyTopBedrock, _ => default!) - .RuleFor(o => o.LithostratigraphyId, f => f.PickRandom(lithostratigraphyTopBedrockIds).OrNull(f, .05f)) - .RuleFor(o => o.Lithostratigraphy, _ => default!) - .RuleFor(o => o.ChronostratigraphyId, f => f.PickRandom(chronostratigraphyTopBedrockIds).OrNull(f, .05f)) - .RuleFor(o => o.Chronostratigraphy, _ => default!) + .RuleFor(o => o.LithostratigraphyTopBedrockId, f => f.PickRandom(lithostratigraphyTopBedrockIds).OrNull(f, .05f)) + .RuleFor(o => o.LithostratigraphyTopBedrock, _ => default!) + .RuleFor(o => o.ChronostratigraphyTopBedrockId, f => f.PickRandom(chronostratigraphyTopBedrockIds).OrNull(f, .05f)) + .RuleFor(o => o.ChronostratigraphyTopBedrock, _ => default!) .RuleFor(o => o.ReferenceElevation, f => f.Random.Double(0, 4500).OrNull(f, .05f)) - .RuleFor(o => o.QtReferenceElevationId, f => f.PickRandom(elevationPrecisionIds).OrNull(f, .05f)) - .RuleFor(o => o.QtReferenceElevation, _ => default!) + .RuleFor(o => o.ReferenceElevationPrecisionId, f => f.PickRandom(elevationPrecisionIds).OrNull(f, .05f)) + .RuleFor(o => o.ReferenceElevationPrecision, _ => default!) .RuleFor(o => o.ReferenceElevationTypeId, f => f.PickRandom(referenceElevationTypeIds).OrNull(f, .05f)) .RuleFor(o => o.ReferenceElevationType, _ => default!) .RuleFor(o => o.BoreholeCodelists, _ => new Collection()) @@ -203,7 +203,7 @@ public static void SeedData(this BdmsContext context) .RuleFor(o => o.PrecisionLocationXLV03, f => f.PickRandom(Enumerable.Range(0, 10))) .RuleFor(o => o.PrecisionLocationYLV03, f => f.PickRandom(Enumerable.Range(0, 10))) .RuleFor(o => o.Observations, _ => new Collection()) - .FinishWith((f, o) => { o.AlternateName = o.OriginalName; }); + .FinishWith((f, o) => { o.Name = o.OriginalName; }); Borehole SeededBoreholes(int seed) => fakeBoreholes.UseSeed(seed).Generate(); context.BulkInsert(boreholeRange.Select(SeededBoreholes).ToList(), bulkConfig); diff --git a/src/api/Controllers/BoreholeController.cs b/src/api/Controllers/BoreholeController.cs index 6dd349f2b..d25d2ab0c 100644 --- a/src/api/Controllers/BoreholeController.cs +++ b/src/api/Controllers/BoreholeController.cs @@ -1,10 +1,13 @@ using BDMS.Authentication; using BDMS.Models; +using CsvHelper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using NetTopologySuite.Geometries; using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Text; namespace BDMS.Controllers; @@ -77,7 +80,7 @@ public async override Task> EditAsync(Borehole entity) /// The page size for pagination. [HttpGet] [Authorize(Policy = PolicyNames.Viewer)] - public async Task> GetAllAsync([FromQuery][MaxLength(MaxPageSize)] IEnumerable? ids = null, [FromQuery][Range(1, int.MaxValue)] int pageNumber = 1, [FromQuery] [Range(1, MaxPageSize)] int pageSize = 100) + public async Task> GetAllAsync([FromQuery][MaxLength(MaxPageSize)] IEnumerable? ids = null, [FromQuery][Range(1, int.MaxValue)] int pageNumber = 1, [FromQuery][Range(1, MaxPageSize)] int pageSize = 100) { pageSize = Math.Min(MaxPageSize, Math.Max(1, pageSize)); @@ -117,6 +120,63 @@ public async Task> GetByIdAsync(int id) return Ok(borehole); } + /// + /// Exports the details of up to boreholes as a CSV file. Filters the boreholes based on the provided list of IDs. + /// + /// The list of IDs for the boreholes to be exported. + /// A CSV file containing the details specified boreholes. + [HttpGet("export-csv")] + [Authorize(Policy = PolicyNames.Viewer)] + public async Task DownloadCsvAsync([FromQuery][MaxLength(MaxPageSize)] IEnumerable ids) + { + ids = ids.Take(MaxPageSize).ToList(); + if (!ids.Any()) return BadRequest("The list of IDs must not be empty."); + + var boreholes = await Context.Boreholes + .Where(borehole => ids.Contains(borehole.Id)) + .Select(b => new + { + b.Id, + b.OriginalName, + b.ProjectName, + b.Name, + b.RestrictionId, + b.RestrictionUntil, + b.NationalInterest, + b.LocationX, + b.LocationY, + b.LocationPrecisionId, + b.ElevationZ, + b.ElevationPrecisionId, + b.ReferenceElevation, + b.ReferenceElevationTypeId, + b.ReferenceElevationPrecisionId, + b.HrsId, + b.TypeId, + b.PurposeId, + b.StatusId, + b.Remarks, + b.TotalDepth, + b.DepthPrecisionId, + b.TopBedrockFreshMd, + b.TopBedrockWeatheredMd, + b.HasGroundwater, + b.LithologyTopBedrockId, + b.ChronostratigraphyTopBedrockId, + b.LithostratigraphyTopBedrockId, + }) + .ToListAsync() + .ConfigureAwait(false); + + if (boreholes.Count == 0) return NotFound("No borehole(s) found for the provided id(s)."); + + using var stringWriter = new StringWriter(); + using var csvWriter = new CsvWriter(stringWriter, CultureInfo.InvariantCulture); + await csvWriter.WriteRecordsAsync(boreholes).ConfigureAwait(false); + + return File(Encoding.UTF8.GetBytes(stringWriter.ToString()), "text/csv", "boreholes_export.csv"); + } + /// /// Asynchronously copies a . /// @@ -238,7 +298,7 @@ public async Task> CopyAsync([Required] int id, [Required] int borehole.Workflows.Add(new Workflow { Borehole = borehole, Role = Role.Editor, UserId = user.Id }); borehole.OriginalName += " (Copy)"; - borehole.AlternateName += " (Copy)"; + borehole.Name += " (Copy)"; var entityEntry = await Context.AddAsync(borehole).ConfigureAwait(false); await Context.SaveChangesAsync().ConfigureAwait(false); diff --git a/src/api/Controllers/StratigraphyController.cs b/src/api/Controllers/StratigraphyController.cs index 7ed184652..4690d960c 100644 --- a/src/api/Controllers/StratigraphyController.cs +++ b/src/api/Controllers/StratigraphyController.cs @@ -230,7 +230,7 @@ public async Task> AddBedrockLayerAsync([Required] int id) StratigraphyId = stratigraphy.Id, FromDepth = borehole.TopBedrockFreshMd.Value, LithologyTopBedrockId = borehole.LithologyTopBedrockId, - LithostratigraphyId = borehole.LithostratigraphyId, + LithostratigraphyId = borehole.LithostratigraphyTopBedrockId, IsLast = false, }; diff --git a/src/api/Controllers/UploadController.cs b/src/api/Controllers/UploadController.cs index 64a020959..70501d2e0 100644 --- a/src/api/Controllers/UploadController.cs +++ b/src/api/Controllers/UploadController.cs @@ -416,22 +416,22 @@ public CsvImportBoreholeMap() Map(m => m.RestrictionId).Optional(); Map(m => m.RestrictionUntil).Optional(); Map(m => m.NationalInterest).Optional(); - Map(m => m.AlternateName).Optional(); + Map(m => m.Name).Optional(); Map(m => m.LocationPrecisionId).Optional(); Map(m => m.ElevationPrecisionId).Optional(); Map(m => m.ProjectName).Optional(); Map(m => m.PurposeId).Optional(); Map(m => m.StatusId).Optional(); - Map(m => m.QtDepthId).Optional(); + Map(m => m.DepthPrecisionId).Optional(); Map(m => m.TopBedrockFreshMd).Optional(); Map(m => m.TopBedrockWeatheredMd).Optional(); Map(m => m.HasGroundwater).Optional(); Map(m => m.Remarks).Optional(); Map(m => m.LithologyTopBedrockId).Optional(); - Map(m => m.LithostratigraphyId).Optional(); - Map(m => m.ChronostratigraphyId).Optional(); + Map(m => m.LithostratigraphyTopBedrockId).Optional(); + Map(m => m.ChronostratigraphyTopBedrockId).Optional(); Map(m => m.ReferenceElevation).Optional(); - Map(m => m.QtReferenceElevationId).Optional(); + Map(m => m.ReferenceElevationPrecisionId).Optional(); Map(m => m.ReferenceElevationTypeId).Optional(); Map(m => m.LocationX).Optional(); Map(m => m.LocationY).Optional(); diff --git a/src/api/Models/Borehole.cs b/src/api/Models/Borehole.cs index d46953d7b..bc7017a41 100644 --- a/src/api/Models/Borehole.cs +++ b/src/api/Models/Borehole.cs @@ -185,7 +185,7 @@ public class Borehole : IChangeTracking, IIdentifyable /// Gets or sets the 's alternate name. /// [Column("alternate_name_bho")] - public string? AlternateName { get; set; } + public string? Name { get; set; } /// /// Gets or sets the 's location precision. @@ -253,15 +253,15 @@ public class Borehole : IChangeTracking, IIdentifyable public Codelist? Status { get; set; } /// - /// Gets or sets the 's QtDepth id. + /// Gets or sets the 's Depth presicion id. /// [Column("qt_depth_id_cli")] - public int? QtDepthId { get; set; } + public int? DepthPrecisionId { get; set; } /// - /// Gets or sets the 's QtDepth. + /// Gets or sets the 's Depth presicion. /// - public Codelist? QtDepth { get; set; } + public Codelist? DepthPrecision { get; set; } /// /// Gets or sets the 's top bedrock. @@ -306,26 +306,26 @@ public class Borehole : IChangeTracking, IIdentifyable public Codelist? LithologyTopBedrock { get; set; } /// - /// Gets or sets the 's lithostratigraphy id. + /// Gets or sets the 's lithostratigraphy top bedrock id. /// [Column("lithostrat_id_cli")] - public int? LithostratigraphyId { get; set; } + public int? LithostratigraphyTopBedrockId { get; set; } /// - /// Gets or sets the 's lithostratigraphy. + /// Gets or sets the 's lithostratigraphy top bedrock. /// - public Codelist? Lithostratigraphy { get; set; } + public Codelist? LithostratigraphyTopBedrock { get; set; } /// - /// Gets or sets the 's chronostratigraphy id. + /// Gets or sets the 's chronostratigraphy top bedrock id. /// [Column("chronostrat_id_cli")] - public int? ChronostratigraphyId { get; set; } + public int? ChronostratigraphyTopBedrockId { get; set; } /// - /// Gets or sets the 's chronostratigraphy. + /// Gets or sets the 's chronostratigraphy top bedrock. /// - public Codelist? Chronostratigraphy { get; set; } + public Codelist? ChronostratigraphyTopBedrock { get; set; } /// /// Gets or sets the 's reference elevation. @@ -334,15 +334,15 @@ public class Borehole : IChangeTracking, IIdentifyable public double? ReferenceElevation { get; set; } /// - /// Gets or sets the 's Qt reference elevation id. + /// Gets or sets the 's reference elevation precision id. /// [Column("qt_reference_elevation_id_cli")] - public int? QtReferenceElevationId { get; set; } + public int? ReferenceElevationPrecisionId { get; set; } /// - /// Gets or sets the 's Qt reference elevation. + /// Gets or sets the 's reference elevation precision. /// - public Codelist? QtReferenceElevation { get; set; } + public Codelist? ReferenceElevationPrecision { get; set; } /// /// Gets or sets the 's reference elevation type id. diff --git a/src/client/cypress/e2e/detailPage/boreholeform.cy.js b/src/client/cypress/e2e/detailPage/boreholeform.cy.js index de0d52eb5..0c9ef7cba 100644 --- a/src/client/cypress/e2e/detailPage/boreholeform.cy.js +++ b/src/client/cypress/e2e/detailPage/boreholeform.cy.js @@ -1,4 +1,4 @@ -import { exportItem, saveWithSaveBar } from "../helpers/buttonHelpers"; +import { exportCSVItem, exportJsonItem, saveWithSaveBar } from "../helpers/buttonHelpers"; import { clickOnRowWithText, showTableAndWaitForData, sortBy } from "../helpers/dataGridHelpers"; import { evaluateInput, evaluateSelect, isDisabled, setInput, setSelect } from "../helpers/formHelpers"; import { @@ -36,7 +36,7 @@ describe("Test for the borehole form.", () => { setSelect("originalReferenceSystem", 0); setSelect("locationPrecisionId", 2); setSelect("elevationPrecisionId", 2); - setSelect("qtReferenceElevationId", 2); + setSelect("referenceElevationPrecisionId", 2); setSelect("referenceElevationTypeId", 4); evaluateSelect("restrictionId", "20111003"); @@ -44,7 +44,7 @@ describe("Test for the borehole form.", () => { evaluateSelect("originalReferenceSystem", "20104001"); evaluateSelect("locationPrecisionId", "20113002"); evaluateSelect("elevationPrecisionId", "20114002"); - evaluateSelect("qtReferenceElevationId", "20114002"); + evaluateSelect("referenceElevationPrecisionId", "20114002"); evaluateSelect("referenceElevationTypeId", "20117004"); saveWithSaveBar(); @@ -56,19 +56,19 @@ describe("Test for the borehole form.", () => { evaluateSelect("originalReferenceSystem", "20104001"); evaluateSelect("locationPrecisionId", "20113002"); evaluateSelect("elevationPrecisionId", "20114002"); - evaluateSelect("qtReferenceElevationId", "20114002"); + evaluateSelect("referenceElevationPrecisionId", "20114002"); evaluateSelect("referenceElevationTypeId", "20117004"); // fill all dropdowns on borehole tab cy.get('[data-cy="borehole-menu-item"]').click(); setSelect("purposeId", 1); setSelect("typeId", 1); - setSelect("qtDepthId", 1); + setSelect("depthPrecisionId", 1); setSelect("statusId", 1); evaluateSelect("purposeId", "22103001"); evaluateSelect("typeId", "20101001"); - evaluateSelect("qtDepthId", "22108001"); + evaluateSelect("depthPrecisionId", "22108001"); evaluateSelect("statusId", "22104001"); saveWithSaveBar(); @@ -79,7 +79,7 @@ describe("Test for the borehole form.", () => { evaluateSelect("purposeId", "22103001"); evaluateSelect("typeId", "20101001"); - evaluateSelect("qtDepthId", "22108001"); + evaluateSelect("depthPrecisionId", "22108001"); evaluateSelect("statusId", "22104001"); }); @@ -91,7 +91,7 @@ describe("Test for the borehole form.", () => { setSelect("purposeId", 1); setSelect("typeId", 1); - setSelect("qtDepthId", 1); + setSelect("depthPrecisionId", 1); setSelect("statusId", 1); setSelect("lithologyTopBedrockId", 1); setSelect("lithostratigraphyId", 1); @@ -156,7 +156,7 @@ describe("Test for the borehole form.", () => { clickOnRowWithText("Zena Rath"); evaluateInput("originalName", "Zena Rath"); - evaluateInput("alternateName", "Zena Rath"); + evaluateInput("name", "Zena Rath"); evaluateInput("projectName", "Reactive asymmetric alliance"); evaluateSelect("restrictionId", ""); cy.get(`[data-cy="nationalInterest-formSelect"] input`).should("have.attr", "value", "0"); @@ -166,13 +166,13 @@ describe("Test for the borehole form.", () => { evaluateInput("elevationZ", "3'519.948980314633"); evaluateInput("referenceElevation", "3'554.9389396584306"); evaluateSelect("elevationPrecisionId", ""); - evaluateSelect("qtReferenceElevationId", "20114007"); // not specified + evaluateSelect("referenceElevationPrecisionId", "20114007"); // not specified evaluateSelect("referenceElevationTypeId", "30000013"); // kelly bushing returnToOverview(); clickOnRowWithText("Zena Mraz"); evaluateInput("originalName", "Zena Mraz"); - evaluateInput("alternateName", "Zena Mraz"); + evaluateInput("name", "Zena Mraz"); evaluateInput("projectName", "Ergonomic heuristic installation"); evaluateSelect("restrictionId", ""); evaluateSelect("nationalInterest", "1"); // Yes @@ -182,7 +182,7 @@ describe("Test for the borehole form.", () => { evaluateInput("elevationZ", "3'062.9991330499756"); evaluateInput("referenceElevation", "3'478.1368118609007"); evaluateSelect("elevationPrecisionId", "20114003"); // 1 - evaluateSelect("qtReferenceElevationId", "20114005"); //0.1 + evaluateSelect("referenceElevationPrecisionId", "20114005"); //0.1 evaluateSelect("referenceElevationTypeId", "30000013"); // kelly bushing }); @@ -235,21 +235,24 @@ describe("Test for the borehole form.", () => { }); }); - it("Exports a borehole", () => { - const boreholeAlternateName = "AAA_HIPPOPOTHAMUS"; + it("Exports a borehole as csv and json", () => { + const boreholeName = "AAA_HIPPOPOTHAMUS"; createBorehole({ - "extended.original_name": boreholeAlternateName, - "custom.alternate_name": boreholeAlternateName, + "extended.original_name": boreholeName, + "custom.alternate_name": boreholeName, }).as("borehole_id"); - deleteDownloadedFile(`${boreholeAlternateName}.json`); + deleteDownloadedFile(`${boreholeName}.json`); + deleteDownloadedFile(`${boreholeName}.csv`); cy.get("@borehole_id").then(id => { goToRouteAndAcceptTerms(`/${id}`); ensureEditingDisabled(); - exportItem(); + exportJsonItem(); + exportCSVItem(); }); - readDownloadedFile(`${boreholeAlternateName}.json`); + readDownloadedFile(`${boreholeName}.json`); + readDownloadedFile(`${boreholeName}.csv`); }); }); diff --git a/src/client/cypress/e2e/detailPage/location.cy.js b/src/client/cypress/e2e/detailPage/location.cy.js index e488c1ba1..369b40c59 100644 --- a/src/client/cypress/e2e/detailPage/location.cy.js +++ b/src/client/cypress/e2e/detailPage/location.cy.js @@ -68,28 +68,28 @@ describe("Tests for 'Location' edit page.", () => { cy.get('[data-cy="originalName-formInput"]').within(() => { cy.get("input").as("originalNameInput"); }); - cy.get('[data-cy="alternateName-formInput"]').within(() => { - cy.get("input").as("alternateNameInput"); + cy.get('[data-cy="name-formInput"]').within(() => { + cy.get("input").as("nameInput"); }); cy.get("@originalNameInput").should("have.value", "PHOTOSQUIRREL"); - cy.get("@alternateNameInput").should("have.value", "PHOTOSQUIRREL"); + cy.get("@nameInput").should("have.value", "PHOTOSQUIRREL"); startBoreholeEditing(); // changing original name should also change alternate name cy.get("@originalNameInput").clear().type("PHOTOCAT"); cy.get("@originalNameInput").should("have.value", "PHOTOCAT"); - cy.get("@alternateNameInput").should("have.value", "PHOTOCAT"); + cy.get("@nameInput").should("have.value", "PHOTOCAT"); - cy.get("@alternateNameInput").clear().type("PHOTOMOUSE"); + cy.get("@nameInput").clear().type("PHOTOMOUSE"); cy.get("@originalNameInput").should("have.value", "PHOTOCAT"); - cy.get("@alternateNameInput").should("have.value", "PHOTOMOUSE"); + cy.get("@nameInput").should("have.value", "PHOTOMOUSE"); - cy.get("@alternateNameInput").clear(); + cy.get("@nameInput").clear(); saveWithSaveBar(); // should be reset to original name if alternate name is empty cy.get("@originalNameInput").should("have.value", "PHOTOCAT"); - cy.get("@alternateNameInput").should("have.value", "PHOTOCAT"); + cy.get("@nameInput").should("have.value", "PHOTOCAT"); }); }); diff --git a/src/client/cypress/e2e/helpers/buttonHelpers.js b/src/client/cypress/e2e/helpers/buttonHelpers.js index bb8fdc5f2..28e4b1247 100644 --- a/src/client/cypress/e2e/helpers/buttonHelpers.js +++ b/src/client/cypress/e2e/helpers/buttonHelpers.js @@ -60,10 +60,19 @@ export const deleteItem = parent => { }; /** - * Clicks on the export button. + * Clicks on the JSON-export button. */ -export const exportItem = () => { - const selector = '[data-cy="export-button"]'; +export const exportJsonItem = () => { + const selector = '[data-cy="exportjson-button"]'; + cy.get(selector).should("not.be.disabled"); + cy.get(selector).click({ force: true }); +}; + +/** + * Clicks on the CSV-export button. + */ +export const exportCSVItem = () => { + const selector = '[data-cy="exportcsv-button"]'; cy.get(selector).should("not.be.disabled"); cy.get(selector).click({ force: true }); }; diff --git a/src/client/cypress/e2e/helpers/testHelpers.js b/src/client/cypress/e2e/helpers/testHelpers.js index 9636a49f1..d0a8c34dd 100644 --- a/src/client/cypress/e2e/helpers/testHelpers.js +++ b/src/client/cypress/e2e/helpers/testHelpers.js @@ -33,6 +33,7 @@ export const interceptApiCalls = () => { cy.intercept("PUT", "/api/v2/layer").as("update-layer"); cy.intercept("/api/v2/location/identify**").as("location"); cy.intercept("/api/v2/borehole/copy*").as("borehole_copy"); + cy.intercept("/api/v2/borehole/export-csv**").as("borehole_export_csv"); cy.intercept("/api/v2/borehole/**").as("borehole_by_id"); cy.intercept("PUT", "/api/v2/borehole").as("update-borehole"); @@ -354,8 +355,7 @@ export const deleteDownloadedFile = fileName => { }); }; -// Read the downloaded file in Cypress' downloads folder -export const readDownloadedFile = fileName => { +export const prepareDownloadPath = fileName => { // Get the path to the downloaded file you want to read let filePath = "cypress/downloads/" + fileName; @@ -363,6 +363,12 @@ export const readDownloadedFile = fileName => { if (Cypress.platform === "win32") { filePath = "cypress\\downloads\\" + fileName; } + return filePath; +}; + +// Read the downloaded file in Cypress' downloads folder +export const readDownloadedFile = fileName => { + let filePath = prepareDownloadPath(fileName); cy.readFile(filePath); }; diff --git a/src/client/cypress/e2e/mainPage/export.cy.js b/src/client/cypress/e2e/mainPage/export.cy.js index dd51ab18c..5be5dcddb 100644 --- a/src/client/cypress/e2e/mainPage/export.cy.js +++ b/src/client/cypress/e2e/mainPage/export.cy.js @@ -1,9 +1,19 @@ -import { exportItem } from "../helpers/buttonHelpers"; -import { checkRowWithText, showTableAndWaitForData } from "../helpers/dataGridHelpers.js"; -import { createBorehole, deleteDownloadedFile, loginAsAdmin, readDownloadedFile } from "../helpers/testHelpers"; +import { exportCSVItem, exportJsonItem } from "../helpers/buttonHelpers"; +import { checkAllVisibleRows, checkRowWithText, showTableAndWaitForData } from "../helpers/dataGridHelpers.js"; +import { + createBorehole, + deleteDownloadedFile, + handlePrompt, + loginAsAdmin, + prepareDownloadPath, + readDownloadedFile, +} from "../helpers/testHelpers"; + +const jsonFileName = `bulkexport_${new Date().toISOString().split("T")[0]}.json`; +const csvFileName = `bulkexport_${new Date().toISOString().split("T")[0]}.csv`; describe("Test for exporting boreholes.", () => { - it("exports a borehole", () => { + it("bulk exports boreholes to json and csv", () => { createBorehole({ "extended.original_name": "AAA_NINTIC", "custom.alternate_name": "AAA_NINTIC" }).as( "borehole_id_1", ); @@ -17,12 +27,38 @@ describe("Test for exporting boreholes.", () => { checkRowWithText("AAA_LOMONE"); }); - const filename = `bulkexport_${new Date().toISOString().split("T")[0]}.json`; + deleteDownloadedFile(jsonFileName); + deleteDownloadedFile(csvFileName); + exportJsonItem(); + exportCSVItem(); + readDownloadedFile(jsonFileName); + readDownloadedFile(csvFileName); + }); + + it("downloads a maximum of 100 boreholes", () => { + loginAsAdmin(); + showTableAndWaitForData(); + checkAllVisibleRows(); + deleteDownloadedFile(csvFileName); + exportCSVItem(); - deleteDownloadedFile(filename); + const moreThan100SelectedPrompt = + "You have selected more than 100 boreholes and a maximum of 100 boreholes can be exported. Do you want to continue?"; + handlePrompt(moreThan100SelectedPrompt, "Cancel"); + cy.get("@borehole_export_csv").should("not.exist"); + exportCSVItem(); + handlePrompt(moreThan100SelectedPrompt, "Export 100 boreholes"); + cy.wait("@borehole_export_csv").its("response.statusCode").should("eq", 200); + readDownloadedFile(csvFileName); - exportItem(); + // Verify file length + cy.readFile(prepareDownloadPath(csvFileName)).then(fileContent => { + const lines = fileContent.split("\n"); + expect(lines.length).to.equal(102); + }); - readDownloadedFile(filename); + deleteDownloadedFile(jsonFileName); + exportJsonItem(); + handlePrompt(moreThan100SelectedPrompt, "Cancel"); }); }); diff --git a/src/client/docs/import.md b/src/client/docs/import.md index d1096c46d..ad5ee3ed4 100644 --- a/src/client/docs/import.md +++ b/src/client/docs/import.md @@ -63,7 +63,7 @@ Die zu importierenden Daten müssen gemäss obigen Anforderungen im CSV-Format v | id_kernlager | Zahl | Nein | ID Kernlager | | original_name | Text | Ja | Originalname | | project_name | Text | Nein | Projektname | -| alternate_name | Text | Nein | Alternativer Name | +| name | Text | Nein | Name | | restriction_id | ID (Codeliste) | Nein | Beschränkung | | restriction_until | Datum | Nein | Ablaufdatum der Beschränkung | | national_interest | True/False | Nein | Nationales Interesse | diff --git a/src/client/public/locale/de/common.json b/src/client/public/locale/de/common.json index 94a346cdb..5fdf5edf9 100644 --- a/src/client/public/locale/de/common.json +++ b/src/client/public/locale/de/common.json @@ -48,6 +48,7 @@ "boreholesImported": "Bohrungen wurden importiert", "boreholestatus": "Bohrungsstatus", "bulkEditing": "Mehrfachbearbeitung", + "bulkExportMoreThan100": "Sie haben mehr als 100 Bohrungen selektiert und es können maximal 100 Bohrungen exportiert werden. Wollen Sie fortfahren?", "by": "durch", "cancel": "Abbrechen", "canton": "Kanton", @@ -189,6 +190,9 @@ "existingFiles": "Vorhandene Dokumente", "expand": "Aufklappen", "export": "Exportieren", + "export100Boreholes": "100 Bohrungen exportieren", + "exportCsv": "CSV exportieren", + "exportJson": "JSON exportieren", "extendlower": "Untere Schicht nach oben ausdehnen", "extendupper": "Obere Schicht nach unten ausdehnen", "facies_description": "Fazielle Beschreibung", diff --git a/src/client/public/locale/en/common.json b/src/client/public/locale/en/common.json index 4d0da9319..3185533fa 100644 --- a/src/client/public/locale/en/common.json +++ b/src/client/public/locale/en/common.json @@ -48,6 +48,7 @@ "boreholesImported": "boreholes were imported", "boreholestatus": "Borehole status", "bulkEditing": "Bulk editing", + "bulkExportMoreThan100": "You have selected more than 100 boreholes and a maximum of 100 boreholes can be exported. Do you want to continue?", "by": "by", "cancel": "Cancel", "canton": "Canton", @@ -189,6 +190,9 @@ "existingFiles": "Existing documents", "expand": "Expand", "export": "Export", + "export100Boreholes": "Export 100 boreholes", + "exportCsv": "Export CSV", + "exportJson": "Export JSON", "extendlower": "Extend lower layer to top", "extendupper": "Extend upper layer to bottom", "facies_description": "Facies description", diff --git a/src/client/public/locale/fr/common.json b/src/client/public/locale/fr/common.json index b50e33fbb..e00675dcf 100644 --- a/src/client/public/locale/fr/common.json +++ b/src/client/public/locale/fr/common.json @@ -48,6 +48,7 @@ "boreholesImported": "forages ont été importés", "boreholestatus": "Statut du forage", "bulkEditing": "Édition en bloc", + "bulkExportMoreThan100": "Vous avez sélectionné plus de 100 forages et un maximum de 100 forages peuvent être exportés. Voulez-vous continuer?", "by": "par", "cancel": "Annuler", "canton": "Canton", @@ -189,6 +190,9 @@ "existingFiles": "Documents existants", "expand": "Étendre", "export": "Exporter", + "export100Boreholes": "Exporter 100 forages", + "exportCsv": "Exporter CSV", + "exportJson": "Exporter JSON", "extendlower": "Étendre la couche inférieure jusqu'au sommet", "extendupper": "Étendre la couche supérieure à la couche inférieure", "facies_description": "Description du faciès géologique", diff --git a/src/client/public/locale/it/common.json b/src/client/public/locale/it/common.json index 786a1f17a..72e596fed 100644 --- a/src/client/public/locale/it/common.json +++ b/src/client/public/locale/it/common.json @@ -48,6 +48,7 @@ "boreholesImported": "perforazioni Tre pozzi sono stati importati", "boreholestatus": "Stato della perforazione", "bulkEditing": "Modifica in blocco", + "bulkExportMoreThan100": "Sono stati selezionati più di 100 perforazioni e si possono esportare un massimo di 100 perforazioni. Vuoi continuare?", "by": "da", "cancel": "Annulla", "canton": "Cantone", @@ -189,6 +190,9 @@ "existingFiles": "Documenti esistenti", "expand": "Espandere", "export": "Esportare", + "export100Boreholes": "Esportare 100 perforazioni", + "exportCsv": "Esportare CSV", + "exportJson": "Esportare JSON", "extendlower": "Estendi lo strato inferiore fino alla superficie topografica", "extendupper": "Estendere lo strato superiore verso il basso", "facies_description": "Descrizione del facies geologico", diff --git a/src/client/src/api/borehole.ts b/src/client/src/api/borehole.ts index ab14bc2ed..56109a80e 100644 --- a/src/client/src/api/borehole.ts +++ b/src/client/src/api/borehole.ts @@ -23,7 +23,7 @@ export interface BoreholeV2 { hasGroundwater: boolean | null; topBedrockWeatheredMd: number; topBedrockFreshMd: number; - qtDepthId: number; + depthPrecisionId: number; totalDepth: number; purposeId: number; typeId: number; @@ -47,7 +47,7 @@ export interface BoreholeV2 { municipality: string; country: string; canton: string; - alternateName: string; + name: string; originalName: string; projectName: number; restrictionId: number; @@ -56,7 +56,7 @@ export interface BoreholeV2 { elevationZ: number | string; // Number with thousands separator then parsed to number elevationPrecisionId: number; referenceElevation: number | string; // Number with thousands separator then parsed to number - qtReferenceElevationId: number; + referenceElevationPrecisionId: number; referenceElevationTypeId: number; locationPrecisionId: number | null; hrsId: number; @@ -84,3 +84,8 @@ export const getAllBoreholes = async (ids: number[] | GridRowSelectionModel, pag const idsQuery = ids.map(id => `ids=${id}`).join("&"); return await fetchApiV2(`borehole?${idsQuery}&pageNumber=${pageNumber}&pageSize=${pageSize}`, "GET"); }; + +export const exportCSVBorehole = async (boreholeIds: GridRowSelectionModel) => { + const idsQuery = boreholeIds.map(id => `ids=${id}`).join("&"); + return await fetchApiV2(`borehole/export-csv?${idsQuery}`, "GET"); +}; diff --git a/src/client/src/components/buttons/buttons.tsx b/src/client/src/components/buttons/buttons.tsx index 7d2eaf770..b8eb31728 100644 --- a/src/client/src/components/buttons/buttons.tsx +++ b/src/client/src/components/buttons/buttons.tsx @@ -124,7 +124,7 @@ export const ExportButton = forwardRef((props, r } diff --git a/src/client/src/components/legacyComponents/formUtils.ts b/src/client/src/components/legacyComponents/formUtils.ts index 5cca47e54..a67752d15 100644 --- a/src/client/src/components/legacyComponents/formUtils.ts +++ b/src/client/src/components/legacyComponents/formUtils.ts @@ -71,8 +71,8 @@ export const prepareLocationDataForSubmit = (formInputs: LocationFormInputs) => data.referenceElevationTypeId = data.referenceElevationTypeId ?? null; data.elevationPrecisionId = data.elevationPrecisionId ?? null; data.locationPrecisionId = data.locationPrecisionId ?? null; - data.qtReferenceElevationId = data.qtReferenceElevationId ?? null; - data.alternateName = data?.alternateName ?? data.originalName; + data.referenceElevationPrecisionId = data.referenceElevationPrecisionId ?? null; + data.name = data?.name ?? data.originalName; data.precisionLocationX = data?.locationX ? getPrecisionFromString(formInputs.locationX) : null; data.precisionLocationY = data?.locationY ? getPrecisionFromString(formInputs.locationY) : null; data.precisionLocationXLV03 = data?.locationXLV03 ? getPrecisionFromString(formInputs.locationXLV03) : null; diff --git a/src/client/src/pages/detail/detailHeader.tsx b/src/client/src/pages/detail/detailHeader.tsx index 5f62f452d..37bb99850 100644 --- a/src/client/src/pages/detail/detailHeader.tsx +++ b/src/client/src/pages/detail/detailHeader.tsx @@ -5,7 +5,7 @@ import { useHistory } from "react-router-dom"; import { Chip, Stack, Typography } from "@mui/material"; import { Check, Trash2, X } from "lucide-react"; import { deleteBorehole, lockBorehole, unlockBorehole } from "../../api-lib"; -import { BoreholeV2 } from "../../api/borehole.ts"; +import { BoreholeV2, exportCSVBorehole } from "../../api/borehole.ts"; import { useAuth } from "../../auth/useBdmsAuth.tsx"; import { DeleteButton, @@ -17,6 +17,7 @@ import { import DateText from "../../components/legacyComponents/dateText"; import { PromptContext } from "../../components/prompt/promptContext.tsx"; import { DetailHeaderStack } from "../../components/styledComponents.ts"; +import { downloadData } from "../../utils.ts"; import { useFormDirty } from "./useFormDirty.tsx"; interface DetailHeaderProps { @@ -84,18 +85,17 @@ const DetailHeader = ({ history.push("/"); }; - const handleExport = () => { + const getFileName = (name: string) => { + return name.replace(/\s/g, "_"); + }; + const handleJsonExport = () => { const jsonString = JSON.stringify([borehole], null, 2); - const blob = new Blob([jsonString], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - const name_without_spaces = borehole.alternateName.replace(/\s/g, "_"); - link.download = `${name_without_spaces}.json`; - document.body.appendChild(link); - link.click(); - link.remove(); - URL.revokeObjectURL(url); + downloadData(jsonString, getFileName(borehole.name), "application/json"); + }; + + const handleCSVExport = async () => { + const csvData = await exportCSVBorehole([borehole.id]); + downloadData(csvData, getFileName(borehole.name), "text/csv"); }; return ( @@ -110,7 +110,7 @@ const DetailHeader = ({ }} /> - {borehole?.alternateName} + {borehole?.name} {!auth.anonymousModeEnabled && ( {t("lastUpdated")}: {t("by")} {borehole?.updatedBy?.name} @@ -150,7 +150,8 @@ const DetailHeader = ({ ) : ( <> - + + ))} diff --git a/src/client/src/pages/detail/form/borehole/boreholeForm.tsx b/src/client/src/pages/detail/form/borehole/boreholeForm.tsx index 919c4e862..1455abf55 100644 --- a/src/client/src/pages/detail/form/borehole/boreholeForm.tsx +++ b/src/client/src/pages/detail/form/borehole/boreholeForm.tsx @@ -29,7 +29,7 @@ export const BoreholeForm = forwardRef(({ borehole, editingEnabled, onSubmit }: purposeId: borehole.purposeId, statusId: borehole.statusId, totalDepth: borehole.totalDepth, - qtDepthId: borehole.qtDepthId, + depthPrecisionId: borehole.depthPrecisionId, topBedrockFreshMd: borehole.topBedrockFreshMd, topBedrockWeatheredMd: borehole.topBedrockWeatheredMd, lithologyTopBedrockId: borehole.lithologyTopBedrockId, @@ -129,11 +129,11 @@ export const BoreholeForm = forwardRef(({ borehole, editingEnabled, onSubmit }: readonly={!editingEnabled} /> diff --git a/src/client/src/pages/detail/form/borehole/boreholePanelInterfaces.ts b/src/client/src/pages/detail/form/borehole/boreholePanelInterfaces.ts index 5b0d971c4..7a3384295 100644 --- a/src/client/src/pages/detail/form/borehole/boreholePanelInterfaces.ts +++ b/src/client/src/pages/detail/form/borehole/boreholePanelInterfaces.ts @@ -16,7 +16,7 @@ export interface BoreholePanelProps extends BoreholeGeneralProps { export interface BoreholeFormInputs { totalDepth: number | null; - qtDepthId: number; + depthPrecisionId: number; typeId: number; purposeId: number; statusId: number; diff --git a/src/client/src/pages/detail/form/location/elevationSegment.tsx b/src/client/src/pages/detail/form/location/elevationSegment.tsx index a9408d0ca..7991800df 100644 --- a/src/client/src/pages/detail/form/location/elevationSegment.tsx +++ b/src/client/src/pages/detail/form/location/elevationSegment.tsx @@ -44,11 +44,11 @@ const ElevationSegment: FC = ({ borehole, editingEnabled, readonly={!editingEnabled} /> diff --git a/src/client/src/pages/detail/form/location/locationPanel.tsx b/src/client/src/pages/detail/form/location/locationPanel.tsx index f67723e56..b02abcbe9 100644 --- a/src/client/src/pages/detail/form/location/locationPanel.tsx +++ b/src/client/src/pages/detail/form/location/locationPanel.tsx @@ -15,7 +15,7 @@ export const LocationPanel = forwardRef( const formMethods = useForm({ mode: "onChange", defaultValues: { - alternateName: borehole.alternateName, + name: borehole.name, originalName: borehole.originalName, projectName: borehole.projectName, restrictionId: borehole.restrictionId, @@ -24,7 +24,7 @@ export const LocationPanel = forwardRef( elevationZ: borehole.elevationZ, elevationPrecisionId: borehole.elevationPrecisionId, referenceElevation: borehole.referenceElevation, - qtReferenceElevationId: borehole.qtReferenceElevationId, + referenceElevationPrecisionId: borehole.referenceElevationPrecisionId, referenceElevationTypeId: borehole.referenceElevationTypeId, hrsId: borehole.hrsId, country: borehole.country, diff --git a/src/client/src/pages/detail/form/location/locationPanelInterfaces.tsx b/src/client/src/pages/detail/form/location/locationPanelInterfaces.tsx index 34b1895c9..f69b2cf3a 100644 --- a/src/client/src/pages/detail/form/location/locationPanelInterfaces.tsx +++ b/src/client/src/pages/detail/form/location/locationPanelInterfaces.tsx @@ -14,7 +14,7 @@ export interface LocationPanelProps extends LocationBaseProps { } interface LocationFormBaseInputs { - alternateName: string; + name: string; originalName: string; projectName: number; restrictionId: number | null; @@ -23,7 +23,7 @@ interface LocationFormBaseInputs { elevationZ: number | string | null; // Number with thousands separator then parsed to number elevationPrecisionId: number | null; referenceElevation: number | string | null; // Number with thousands separator then parsed to number - qtReferenceElevationId: number | null; + referenceElevationPrecisionId: number | null; referenceElevationTypeId: number | null; originalReferenceSystem: number | null; hrsId?: number; diff --git a/src/client/src/pages/detail/form/location/nameSegment.tsx b/src/client/src/pages/detail/form/location/nameSegment.tsx index 7059020f3..a068b0015 100644 --- a/src/client/src/pages/detail/form/location/nameSegment.tsx +++ b/src/client/src/pages/detail/form/location/nameSegment.tsx @@ -16,10 +16,10 @@ const NameSegment = ({ borehole, editingEnabled, formMethods }: NameSegmentProps const { dirtyFields, isDirty } = formMethods.formState; useEffect(() => { - if (dirtyFields.originalName || (!isDirty && formMethods.getValues("alternateName") === "")) { - formMethods.setValue("alternateName", originalName); + if (dirtyFields.originalName || (!isDirty && formMethods.getValues("name") === "")) { + formMethods.setValue("name", originalName); } - }, [borehole?.alternateName, dirtyFields.originalName, formMethods, formMethods.setValue, isDirty, originalName]); + }, [borehole?.name, dirtyFields.originalName, formMethods, formMethods.setValue, isDirty, originalName]); return ( @@ -45,10 +45,10 @@ const NameSegment = ({ borehole, editingEnabled, formMethods }: NameSegmentProps )} void; onCopyBorehole: () => void; - onExportMultiple: () => void; + onExportMultipleJson: () => void; + onExportMultipleCsv: () => void; workgroup: string; setWorkgroup: React.Dispatch>; } @@ -31,7 +32,8 @@ const BottomBar = ({ onDeleteMultiple, search, onCopyBorehole, - onExportMultiple, + onExportMultipleJson, + onExportMultipleCsv, boreholes, workgroup, setWorkgroup, @@ -115,7 +117,8 @@ const BottomBar = ({ showCopyPromptForSelectedWorkgroup()} /> )} - + + {t("selectedCount", { count: selectionModel.length })} ) : ( diff --git a/src/client/src/pages/overview/boreholeTable/bottomBarContainer.tsx b/src/client/src/pages/overview/boreholeTable/bottomBarContainer.tsx index 46298fc25..b6c5989bd 100644 --- a/src/client/src/pages/overview/boreholeTable/bottomBarContainer.tsx +++ b/src/client/src/pages/overview/boreholeTable/bottomBarContainer.tsx @@ -1,10 +1,14 @@ import React, { useCallback, useContext, useLayoutEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; import { GridRowSelectionModel, GridSortDirection, GridSortModel } from "@mui/x-data-grid"; +import { ArrowDownToLine } from "lucide-react"; import { deleteBoreholes } from "../../../api-lib"; import { Boreholes, ReduxRootState, User } from "../../../api-lib/ReduxStateInterfaces.ts"; -import { copyBorehole, getAllBoreholes } from "../../../api/borehole.ts"; +import { copyBorehole, exportCSVBorehole, getAllBoreholes } from "../../../api/borehole.ts"; +import { PromptContext } from "../../../components/prompt/promptContext.tsx"; +import { downloadData } from "../../../utils.ts"; import { OverViewContext } from "../overViewContext.tsx"; import { FilterContext } from "../sidePanelContent/filter/filterContext.tsx"; import { BoreholeTable } from "./boreholeTable.tsx"; @@ -41,8 +45,10 @@ const BottomBarContainer = ({ }: BottomBarContainerProps) => { const user: User = useSelector((state: ReduxRootState) => state.core_user); const history = useHistory(); + const { t } = useTranslation(); const { featureIds } = useContext(FilterContext); const { bottomDrawerOpen } = useContext(OverViewContext); + const { showPrompt } = useContext(PromptContext); const [workgroupId, setWorkgroupId] = useState(user.data.workgroups[0]?.id); const [isBusy, setIsBusy] = useState(false); const [paginationModel, setPaginationModel] = useState({ @@ -88,18 +94,41 @@ const BottomBarContainer = ({ setIsBusy(false); }; - const onExportMultiple = async () => { + const getBulkExportFilename = (suffix: string) => { + return `bulkexport_${new Date().toISOString().split("T")[0]}.${suffix}`; + }; + + const handleExportMultipleJson = async () => { const paginatedResponse = await getAllBoreholes(selectionModel, 1, selectionModel.length); const jsonString = JSON.stringify(paginatedResponse.boreholes, null, 2); - const blob = new Blob([jsonString], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `bulkexport_${new Date().toISOString().split("T")[0]}.json`; - document.body.appendChild(link); - link.click(); - link.remove(); - URL.revokeObjectURL(url); + downloadData(jsonString, getBulkExportFilename("json"), "application/json"); + }; + + const handleExportMultipleCsv = async () => { + const csvData = await exportCSVBorehole(selectionModel.slice(0, 100)); + downloadData(csvData, getBulkExportFilename("csv"), "text/csv"); + }; + + const showPromptExportMoreThan100 = (callback: () => void) => { + showPrompt(t("bulkExportMoreThan100"), [ + { + label: t("cancel"), + }, + { + label: t("export100Boreholes"), + icon: , + variant: "contained", + action: callback, + }, + ]); + }; + + const onExportMultiple = async (callback: () => Promise) => { + if (selectionModel.length > 100) { + showPromptExportMoreThan100(callback); + } else { + await callback(); + } }; return ( @@ -109,7 +138,8 @@ const BottomBarContainer = ({ multipleSelected={multipleSelected} onCopyBorehole={onCopyBorehole} onDeleteMultiple={onDeleteMultiple} - onExportMultiple={onExportMultiple} + onExportMultipleJson={() => onExportMultiple(handleExportMultipleJson)} + onExportMultipleCsv={() => onExportMultiple(handleExportMultipleCsv)} search={search} boreholes={boreholes} workgroup={workgroupId} diff --git a/src/client/src/pages/overview/sidePanelContent/importer/importModalContent.tsx b/src/client/src/pages/overview/sidePanelContent/importer/importModalContent.tsx index 261316aa2..c66651ce4 100644 --- a/src/client/src/pages/overview/sidePanelContent/importer/importModalContent.tsx +++ b/src/client/src/pages/overview/sidePanelContent/importer/importModalContent.tsx @@ -68,7 +68,7 @@ const ImportModalContent = ({ setSelectedBoreholeAttachments, setSelectedFile, s {ExampleHeadings( "import_id;id_geodin_shortname;id_info_geol;id_original;" + "id_canton;id_geo_quat;id_geo_mol;id_geo_therm;id_top_fels;" + - "id_geodin;id_kernlager;original_name;project_name;alternate_name;" + + "id_geodin;id_kernlager;original_name;project_name;name;" + "restriction_id;restriction_until;national_interest;location_x;location_y;" + "location_precision;elevation_z;elevation_precision_id;" + "reference_elevation;reference_elevation_type_id;" + diff --git a/src/client/src/utils.ts b/src/client/src/utils.ts index e32c4a5c9..7da05c1e6 100644 --- a/src/client/src/utils.ts +++ b/src/client/src/utils.ts @@ -2,3 +2,15 @@ export function capitalizeFirstLetter(text: string) { if (!text) return ""; return text.charAt(0).toUpperCase() + text.slice(1); } + +export const downloadData = (dataString: string, fileName: string, type: string) => { + const blob = new Blob([dataString], { type: type }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = fileName; + document.body.appendChild(link); + link.click(); + link.remove(); + URL.revokeObjectURL(url); +}; diff --git a/tests/api/Controllers/BoreholeControllerTest.cs b/tests/api/Controllers/BoreholeControllerTest.cs index ed2a89315..ebe4887f3 100644 --- a/tests/api/Controllers/BoreholeControllerTest.cs +++ b/tests/api/Controllers/BoreholeControllerTest.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using System.Text; using static BDMS.Helpers; namespace BDMS.Controllers; @@ -131,7 +132,7 @@ public async Task EditBoreholeWithCompleteBorehole() Assert.AreEqual(newBorehole.RestrictionUntil.ToString(), updatedBorehole.RestrictionUntil.ToString()); Assert.AreEqual(newBorehole.NationalInterest, updatedBorehole.NationalInterest); Assert.AreEqual(newBorehole.OriginalName, updatedBorehole.OriginalName); - Assert.AreEqual(newBorehole.AlternateName, updatedBorehole.AlternateName); + Assert.AreEqual(newBorehole.Name, updatedBorehole.Name); Assert.AreEqual(newBorehole.LocationPrecisionId, updatedBorehole.LocationPrecisionId); Assert.AreEqual(newBorehole.ElevationPrecisionId, updatedBorehole.ElevationPrecisionId); Assert.AreEqual(newBorehole.ProjectName, updatedBorehole.ProjectName); @@ -140,16 +141,16 @@ public async Task EditBoreholeWithCompleteBorehole() Assert.AreEqual(newBorehole.Municipality, updatedBorehole.Municipality); Assert.AreEqual(newBorehole.PurposeId, updatedBorehole.PurposeId); Assert.AreEqual(newBorehole.StatusId, updatedBorehole.StatusId); - Assert.AreEqual(newBorehole.QtDepthId, updatedBorehole.QtDepthId); + Assert.AreEqual(newBorehole.DepthPrecisionId, updatedBorehole.DepthPrecisionId); Assert.AreEqual(newBorehole.TopBedrockFreshMd, updatedBorehole.TopBedrockFreshMd); Assert.AreEqual(newBorehole.TopBedrockWeatheredMd, updatedBorehole.TopBedrockWeatheredMd); Assert.AreEqual(newBorehole.HasGroundwater, updatedBorehole.HasGroundwater); Assert.AreEqual(newBorehole.Remarks, updatedBorehole.Remarks); Assert.AreEqual(newBorehole.LithologyTopBedrockId, updatedBorehole.LithologyTopBedrockId); - Assert.AreEqual(newBorehole.LithostratigraphyId, updatedBorehole.LithostratigraphyId); - Assert.AreEqual(newBorehole.ChronostratigraphyId, updatedBorehole.ChronostratigraphyId); + Assert.AreEqual(newBorehole.LithostratigraphyTopBedrockId, updatedBorehole.LithostratigraphyTopBedrockId); + Assert.AreEqual(newBorehole.ChronostratigraphyTopBedrockId, updatedBorehole.ChronostratigraphyTopBedrockId); Assert.AreEqual(newBorehole.ReferenceElevation, updatedBorehole.ReferenceElevation); - Assert.AreEqual(newBorehole.QtReferenceElevationId, updatedBorehole.QtReferenceElevationId); + Assert.AreEqual(newBorehole.ReferenceElevationPrecisionId, updatedBorehole.ReferenceElevationPrecisionId); Assert.AreEqual(newBorehole.ReferenceElevationTypeId, updatedBorehole.ReferenceElevationTypeId); // Stratigraphies and workflows remain unchanged @@ -404,7 +405,7 @@ public async Task Copy() var copiedBorehole = GetBorehole((int)copiedBoreholeId); Assert.AreEqual($"{originalBorehole.OriginalName} (Copy)", copiedBorehole.OriginalName); - Assert.AreEqual($"{originalBorehole.AlternateName} (Copy)", copiedBorehole.AlternateName); + Assert.AreEqual($"{originalBorehole.Name} (Copy)", copiedBorehole.Name); Assert.AreEqual(originalBorehole.CreatedBy.SubjectId, copiedBorehole.CreatedBy.SubjectId); Assert.AreEqual(originalBorehole.UpdatedBy.SubjectId, copiedBorehole.UpdatedBy.SubjectId); Assert.AreEqual(DefaultWorkgroupId, copiedBorehole.Workgroup.Id); @@ -553,7 +554,7 @@ private Borehole GetBoreholeToAdd() RestrictionUntil = DateTime.UtcNow.AddYears(1), NationalInterest = false, OriginalName = "BH-257", - AlternateName = "Borehole 257", + Name = "Borehole 257", LocationPrecisionId = 20113002, ElevationPrecisionId = null, ProjectName = "Project Alpha", @@ -562,17 +563,17 @@ private Borehole GetBoreholeToAdd() Municipality = "Zurich", PurposeId = 22103002, StatusId = 22104001, - QtDepthId = 22108005, + DepthPrecisionId = 22108005, TopBedrockFreshMd = 10.5, TopBedrockWeatheredMd = 8.0, HasGroundwater = true, Geometry = null, Remarks = "Test borehole for project", LithologyTopBedrockId = 15104934, - LithostratigraphyId = 15300259, - ChronostratigraphyId = 15001141, + LithostratigraphyTopBedrockId = 15300259, + ChronostratigraphyTopBedrockId = 15001141, ReferenceElevation = 500.0, - QtReferenceElevationId = 20114002, + ReferenceElevationPrecisionId = 20114002, ReferenceElevationTypeId = 20117003, }; } @@ -686,4 +687,59 @@ public async Task CopyWithNonAdminUser() Assert.IsNotNull(copiedBoreholeId); Assert.IsInstanceOfType(copiedBoreholeId, typeof(int)); } + + [TestMethod] + public async Task DownloadCsvWithValidIdsReturnsFileResultWithMax100Boreholes() + { + var ids = Enumerable.Range(testBoreholeId, 120).ToList(); + + var result = await controller.DownloadCsvAsync(ids) as FileContentResult; + + Assert.IsNotNull(result); + Assert.AreEqual("text/csv", result.ContentType); + Assert.AreEqual("boreholes_export.csv", result.FileDownloadName); + var csvData = Encoding.UTF8.GetString(result.FileContents); + var fileLength = csvData.Split('\n').Length; + var recordCount = fileLength - 2; // Remove header and last line break + Assert.IsTrue(recordCount <= 100); + } + + [TestMethod] + public async Task DownloadCsvWithInvalidIdsReturnsNotFound() + { + var ids = new List { 8, 2, 11, 87 }; + + var result = await controller.DownloadCsvAsync(ids) as NotFoundObjectResult; + + Assert.IsNotNull(result); + Assert.AreEqual("No borehole(s) found for the provided id(s).", result.Value); + } + + [TestMethod] + public async Task DownloadCsvWithPartiallyValidIdsReturnsFileForPartillyValidIds() + { + var ids = new List { 9, 8, 0, testBoreholeId }; + + var result = await controller.DownloadCsvAsync(ids) as FileContentResult; + + Assert.IsNotNull(result); + Assert.IsNotNull(result); + Assert.AreEqual("text/csv", result.ContentType); + Assert.AreEqual("boreholes_export.csv", result.FileDownloadName); + var csvData = Encoding.UTF8.GetString(result.FileContents); + var fileLength = csvData.Split('\n').Length; + var recordCount = fileLength - 2; + Assert.AreEqual(recordCount, 1); + } + + [TestMethod] + public async Task DownloadCsvEmptyIdsReturnsBadRequest() + { + var ids = new List(); + + var result = await controller.DownloadCsvAsync(ids) as BadRequestObjectResult; + + Assert.IsNotNull(result); + Assert.AreEqual("The list of IDs must not be empty.", result.Value); + } } diff --git a/tests/api/Controllers/CoordinateControllerTest.cs b/tests/api/Controllers/CoordinateControllerTest.cs index e9cd12835..2e2a1aa6e 100644 --- a/tests/api/Controllers/CoordinateControllerTest.cs +++ b/tests/api/Controllers/CoordinateControllerTest.cs @@ -141,7 +141,7 @@ private Borehole CreateLV95BoreholeWithAllCoordinatesSet() LocationXLV03 = 765875.1615463407, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Laurence.Padberg3", + Name = "Laurence.Padberg3", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -159,7 +159,7 @@ private Borehole CreateLV03BoreholeWithAllCoordinatesSet() LocationXLV03 = 655269, LocationYLV03 = 297874, OriginalReferenceSystem = ReferenceSystem.LV03, - AlternateName = "Floyd29", + Name = "Floyd29", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -177,7 +177,7 @@ private Borehole CreateLV03BoreholeWithMissingSourceCoordinates() LocationXLV03 = null, LocationYLV03 = 224735.18581408318, OriginalReferenceSystem = ReferenceSystem.LV03, - AlternateName = "Brendan.Trantow38", + Name = "Brendan.Trantow38", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -195,7 +195,7 @@ private Borehole CreateLV95BoreholeWithMissingDestCoordinates() LocationXLV03 = 523204.9377711746, LocationYLV03 = null, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Sherri.Goodwin99", + Name = "Sherri.Goodwin99", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -206,7 +206,7 @@ private Borehole CreateLV95BoreholeWithMissingDestCoordinates() private void AssertUnchanged(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual(initialBohrung.AlternateName, editierteBohrung.AlternateName); + Assert.AreEqual(initialBohrung.Name, editierteBohrung.Name); Assert.AreEqual(initialBohrung.OriginalReferenceSystem, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual(initialBohrung.LocationX, editierteBohrung.LocationX); Assert.AreEqual(initialBohrung.LocationY, editierteBohrung.LocationY); @@ -227,7 +227,7 @@ private void AssertUnchanged(Borehole initialBohrung) private void AssertLV95BoreholeWithAllCoordinatesSet(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual("Laurence.Padberg3", editierteBohrung.AlternateName); + Assert.AreEqual("Laurence.Padberg3", editierteBohrung.Name); Assert.AreEqual(ReferenceSystem.LV95, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual(2626103.1988343936, editierteBohrung.LocationX); Assert.AreEqual(1125366.3469565178, editierteBohrung.LocationY); @@ -239,7 +239,7 @@ private void AssertLV95BoreholeWithAllCoordinatesSet(Borehole initialBohrung) private void AssertLV03BoreholeWithAllCoordinatesSet(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual("Floyd29", editierteBohrung.AlternateName); + Assert.AreEqual("Floyd29", editierteBohrung.Name); Assert.AreEqual(ReferenceSystem.LV03, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual(9877, editierteBohrung.LocationX); Assert.AreEqual(1235, editierteBohrung.LocationY); @@ -251,7 +251,7 @@ private void AssertLV03BoreholeWithAllCoordinatesSet(Borehole initialBohrung) private void AssertLV95BoreholeWithMissingDestCoordinates(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual("Sherri.Goodwin99", editierteBohrung.AlternateName); + Assert.AreEqual("Sherri.Goodwin99", editierteBohrung.Name); Assert.AreEqual(ReferenceSystem.LV95, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual(2582431.203588229, editierteBohrung.LocationX); Assert.AreEqual(1189604.098138797, editierteBohrung.LocationY); diff --git a/tests/api/Controllers/LocationControllerTest.cs b/tests/api/Controllers/LocationControllerTest.cs index 0e7b57bf1..f222f0c9b 100644 --- a/tests/api/Controllers/LocationControllerTest.cs +++ b/tests/api/Controllers/LocationControllerTest.cs @@ -135,7 +135,7 @@ private Borehole CreateBoreholeWithAllLocationAttributes() LocationXLV03 = null, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Byron_West", + Name = "Byron_West", Country = "Northern Mariana Islands", Canton = "South Dakota", Municipality = "Lake Chayamouth", @@ -156,7 +156,7 @@ private Borehole CreateBoreholeWithMissingLocationAttributes() LocationXLV03 = 741929.5530394556, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Andy.Lang", + Name = "Andy.Lang", Country = null, Canton = "South Dakota", Municipality = "Lake Chayamouth", @@ -177,7 +177,7 @@ private Borehole CreateBoreholeWithMissingSourceCoordinates() LocationXLV03 = 741929.5530394556, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Tasha.Walsh", + Name = "Tasha.Walsh", Country = "British Indian Ocean Territory (Chagos Archipelago)", Canton = "South Dakota", Municipality = "Lake Chayamouth", @@ -191,7 +191,7 @@ private Borehole CreateBoreholeWithMissingSourceCoordinates() private void AssertUnchanged(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual(initialBohrung.AlternateName, editierteBohrung.AlternateName); + Assert.AreEqual(initialBohrung.Name, editierteBohrung.Name); Assert.AreEqual(initialBohrung.OriginalReferenceSystem, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual(initialBohrung.LocationX, editierteBohrung.LocationX); Assert.AreEqual(initialBohrung.LocationY, editierteBohrung.LocationY); @@ -204,7 +204,7 @@ private void AssertUnchanged(Borehole initialBohrung) private void AssertBoreholeWithAllLocationAttributes(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual("Byron_West", editierteBohrung.AlternateName); + Assert.AreEqual("Byron_West", editierteBohrung.Name); Assert.AreEqual(ReferenceSystem.LV95, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual("RAGETRINITY", editierteBohrung.Country); Assert.AreEqual("SLEEPYMONKEY", editierteBohrung.Canton); @@ -214,7 +214,7 @@ private void AssertBoreholeWithAllLocationAttributes(Borehole initialBohrung) private void AssertBoreholeWithMissingLocationAttributes(Borehole initialBohrung) { var editierteBohrung = context.Boreholes.Single(b => b.Id == initialBohrung.Id); - Assert.AreEqual("Andy.Lang", editierteBohrung.AlternateName); + Assert.AreEqual("Andy.Lang", editierteBohrung.Name); Assert.AreEqual(ReferenceSystem.LV95, editierteBohrung.OriginalReferenceSystem); Assert.AreEqual("RAGETRINITY", editierteBohrung.Country); Assert.AreEqual("SLEEPYMONKEY", editierteBohrung.Canton); diff --git a/tests/api/Controllers/StratigraphyControllerTest.cs b/tests/api/Controllers/StratigraphyControllerTest.cs index 28be77d92..4da57fe04 100644 --- a/tests/api/Controllers/StratigraphyControllerTest.cs +++ b/tests/api/Controllers/StratigraphyControllerTest.cs @@ -343,7 +343,7 @@ public async Task AddBedrockLayer() Assert.AreEqual(stratigraphyWithoutBedrockLayer.Id, bedrockLayer.StratigraphyId); Assert.AreEqual(boreholeWithBedrock.TopBedrockFreshMd.Value, bedrockLayer.FromDepth); Assert.AreEqual(boreholeWithBedrock.LithologyTopBedrockId, bedrockLayer.LithologyTopBedrockId); - Assert.AreEqual(boreholeWithBedrock.LithostratigraphyId, bedrockLayer.LithostratigraphyId); + Assert.AreEqual(boreholeWithBedrock.LithostratigraphyTopBedrockId, bedrockLayer.LithostratigraphyId); Assert.AreEqual(false, bedrockLayer.IsLast); } diff --git a/tests/api/Controllers/UploadControllerTest.cs b/tests/api/Controllers/UploadControllerTest.cs index b699ac8c7..964513097 100644 --- a/tests/api/Controllers/UploadControllerTest.cs +++ b/tests/api/Controllers/UploadControllerTest.cs @@ -134,7 +134,7 @@ public async Task UploadJsonWithValidJsonShouldSaveData() Assert.IsNull(borehole.RestrictionUntil, nameof(Borehole.RestrictionUntil).ShouldBeNullMessage()); Assert.IsFalse(borehole.NationalInterest, nameof(Borehole.NationalInterest)); Assert.AreEqual("PURPLETOLL", borehole.OriginalName, nameof(Borehole.OriginalName)); - Assert.AreEqual("GREYGOAT", borehole.AlternateName, nameof(Borehole.AlternateName)); + Assert.AreEqual("GREYGOAT", borehole.Name, nameof(Borehole.Name)); Assert.IsNull(borehole.LocationPrecisionId, nameof(Borehole.LocationPrecisionId).ShouldBeNullMessage()); Assert.IsNull(borehole.LocationPrecision, nameof(Borehole.LocationPrecision).ShouldBeNullMessage()); Assert.AreEqual(20114002, borehole.ElevationPrecisionId, nameof(Borehole.ElevationPrecisionId)); @@ -147,21 +147,21 @@ public async Task UploadJsonWithValidJsonShouldSaveData() Assert.IsNull(borehole.Purpose, nameof(Borehole.Purpose).ShouldBeNullMessage()); Assert.AreEqual(22104006, borehole.StatusId, nameof(Borehole.StatusId)); Assert.IsNull(borehole.Status, nameof(Borehole.Status).ShouldBeNullMessage()); - Assert.AreEqual(22108003, borehole.QtDepthId, nameof(Borehole.QtDepthId)); - Assert.IsNull(borehole.QtDepth, nameof(Borehole.QtDepth).ShouldBeNullMessage()); + Assert.AreEqual(22108003, borehole.DepthPrecisionId, nameof(Borehole.DepthPrecisionId)); + Assert.IsNull(borehole.DepthPrecision, nameof(Borehole.DepthPrecision).ShouldBeNullMessage()); Assert.AreEqual(759.5479580385368, borehole.TopBedrockFreshMd, nameof(Borehole.TopBedrockFreshMd)); Assert.AreEqual(1.338392690447342, borehole.TopBedrockWeatheredMd, nameof(Borehole.TopBedrockWeatheredMd)); Assert.IsFalse(borehole.HasGroundwater, nameof(Borehole.HasGroundwater)); Assert.AreEqual("This product works too well.", borehole.Remarks, nameof(Borehole.Remarks)); Assert.AreEqual(15104543, borehole.LithologyTopBedrockId, nameof(Borehole.LithologyTopBedrockId)); Assert.IsNull(borehole.LithologyTopBedrock, nameof(Borehole.LithologyTopBedrock).ShouldBeNullMessage()); - Assert.AreEqual(15302037, borehole.LithostratigraphyId, nameof(Borehole.LithostratigraphyId)); - Assert.IsNull(borehole.Lithostratigraphy, nameof(Borehole.Lithostratigraphy).ShouldBeNullMessage()); - Assert.AreEqual(15001060, borehole.ChronostratigraphyId, nameof(Borehole.ChronostratigraphyId)); - Assert.IsNull(borehole.Chronostratigraphy, nameof(Borehole.Chronostratigraphy).ShouldBeNullMessage()); + Assert.AreEqual(15302037, borehole.LithostratigraphyTopBedrockId, nameof(Borehole.LithostratigraphyTopBedrockId)); + Assert.IsNull(borehole.LithostratigraphyTopBedrock, nameof(Borehole.LithostratigraphyTopBedrock).ShouldBeNullMessage()); + Assert.AreEqual(15001060, borehole.ChronostratigraphyTopBedrockId, nameof(Borehole.ChronostratigraphyTopBedrockId)); + Assert.IsNull(borehole.ChronostratigraphyTopBedrock, nameof(Borehole.ChronostratigraphyTopBedrock).ShouldBeNullMessage()); Assert.AreEqual(899.1648284248844, borehole.ReferenceElevation, nameof(Borehole.ReferenceElevation)); - Assert.AreEqual(20114006, borehole.QtReferenceElevationId, nameof(Borehole.QtReferenceElevationId)); - Assert.IsNull(borehole.QtReferenceElevation, nameof(Borehole.QtReferenceElevation).ShouldBeNullMessage()); + Assert.AreEqual(20114006, borehole.ReferenceElevationPrecisionId, nameof(Borehole.ReferenceElevationPrecisionId)); + Assert.IsNull(borehole.ReferenceElevationPrecision, nameof(Borehole.ReferenceElevationPrecision).ShouldBeNullMessage()); Assert.AreEqual(20117005, borehole.ReferenceElevationTypeId, nameof(Borehole.ReferenceElevationTypeId)); Assert.IsNull(borehole.ReferenceElevationType, nameof(Borehole.ReferenceElevationType).ShouldBeNullMessage()); @@ -498,7 +498,7 @@ public async Task UploadShouldSaveDataToDatabaseAsync() // Assert imported values var borehole = GetBoreholesWithIncludes(context.Boreholes).ToList().Find(b => b.OriginalName == "Unit_Test_6"); Assert.AreEqual(1, borehole.WorkgroupId); - Assert.AreEqual("Unit_Test_6_a", borehole.AlternateName); + Assert.AreEqual("Unit_Test_6_a", borehole.Name); Assert.AreEqual(null, borehole.IsPublic); Assert.AreEqual(new DateTime(2024, 06, 15), borehole.RestrictionUntil); Assert.AreEqual(2474.472693, borehole.TotalDepth); @@ -545,7 +545,7 @@ public async Task UploadShouldSaveMinimalDatasetAsync() // Assert imported values var borehole = GetBoreholesWithIncludes(context.Boreholes).ToList().Find(b => b.OriginalName == "Unit_Test_2"); Assert.AreEqual(1, borehole.WorkgroupId); - Assert.AreEqual(null, borehole.AlternateName); + Assert.AreEqual(null, borehole.Name); Assert.AreEqual(null, borehole.IsPublic); Assert.AreEqual(null, borehole.RestrictionUntil); Assert.AreEqual(null, borehole.TotalDepth); diff --git a/tests/api/CoordinateServiceTest.cs b/tests/api/CoordinateServiceTest.cs index 01bcf37ae..60fc7e5d1 100644 --- a/tests/api/CoordinateServiceTest.cs +++ b/tests/api/CoordinateServiceTest.cs @@ -57,7 +57,7 @@ public async Task MigrateCoordinatesOfLV95BoreholeWithAllCoordinatesSet() LocationXLV03 = 765875.1615463407, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Laurence.Padberg3", + Name = "Laurence.Padberg3", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -86,7 +86,7 @@ public async Task DoesNotMigrateCoordinatesOfLV95BoreholeWithAllCoordinatesSet() LocationXLV03 = 765875.1615463407, LocationYLV03 = 78390.10392298926, OriginalReferenceSystem = ReferenceSystem.LV95, - AlternateName = "Laurence.Padberg3", + Name = "Laurence.Padberg3", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -116,7 +116,7 @@ public async Task MigrateCoordinatesOfLV03BoreholeWithMissingDestCoordinates() LocationXLV03 = 655269, LocationYLV03 = 297874, OriginalReferenceSystem = ReferenceSystem.LV03, - AlternateName = "Floyd29", + Name = "Floyd29", }; context.Boreholes.Add(borehole); context.SaveChanges(); @@ -145,7 +145,7 @@ public async Task DoesNotMigrateCoordinatesOfLV03BoreholeWithMissingSourceCoordi LocationXLV03 = null, LocationYLV03 = 224735.18581408318, OriginalReferenceSystem = ReferenceSystem.LV03, - AlternateName = "Brendan.Trantow38", + Name = "Brendan.Trantow38", }; context.Boreholes.Add(borehole); context.SaveChanges(); diff --git a/tests/api/TestData/json_import_duplicated_by_location.json b/tests/api/TestData/json_import_duplicated_by_location.json index d8a59b952..dc6f47dcf 100644 --- a/tests/api/TestData/json_import_duplicated_by_location.json +++ b/tests/api/TestData/json_import_duplicated_by_location.json @@ -33,7 +33,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "PURPLETOLL", - "alternateName": "GREYGOAT", + "name": "GREYGOAT", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -46,21 +46,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": null, "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ @@ -1191,7 +1191,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "JETMONSTER", - "alternateName": "SKYSHOP", + "name": "SKYSHOP", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -1204,21 +1204,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": null, "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ diff --git a/tests/api/TestData/json_import_duplicates_existing.json b/tests/api/TestData/json_import_duplicates_existing.json index bdcc9d15e..1d5aab43b 100644 --- a/tests/api/TestData/json_import_duplicates_existing.json +++ b/tests/api/TestData/json_import_duplicates_existing.json @@ -33,7 +33,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "PURPLETOLL", - "alternateName": "GREYGOAT", + "name": "GREYGOAT", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -46,21 +46,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": null, "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ @@ -1191,7 +1191,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "JETMONSTER", - "alternateName": "SKYSHOP", + "name": "SKYSHOP", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -1204,21 +1204,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": null, "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ diff --git a/tests/api/TestData/json_import_single.json b/tests/api/TestData/json_import_single.json index 41c8917c2..933066895 100644 --- a/tests/api/TestData/json_import_single.json +++ b/tests/api/TestData/json_import_single.json @@ -32,7 +32,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "PURPLETOLL", - "alternateName": "GREYGOAT", + "name": "GREYGOAT", "locationPrecisionId": 20113007, "locationPrecision": null, "elevationPrecisionId": 20114005, @@ -45,21 +45,21 @@ "purpose": null, "statusId": 22104002, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 543.0975381951301, "topBedrockWeatheredMd": 0.701670643268931, "hasGroundwater": true, "remarks": "This product works excessively well. It speedily improves my baseball by a lot.", "lithologyTopBedrockId": 15104790, "lithologyTopBedrock": null, - "lithostratigraphyId": 15304098, - "lithostratigraphy": null, - "chronostratigraphyId": 15001003, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15304098, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001003, + "chronostratigraphyTopBedrock": null, "referenceElevation": 1434.1510240613254, - "qtReferenceElevationId": 20114005, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114005, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117002, "referenceElevationType": null, "stratigraphies": [ diff --git a/tests/api/TestData/json_import_valid.json b/tests/api/TestData/json_import_valid.json index 4a91889a6..5f243b7a9 100644 --- a/tests/api/TestData/json_import_valid.json +++ b/tests/api/TestData/json_import_valid.json @@ -33,7 +33,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "PURPLETOLL", - "alternateName": "GREYGOAT", + "name": "GREYGOAT", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -46,21 +46,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": "This product works too well.", "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ @@ -1205,7 +1205,7 @@ "restrictionUntil": null, "nationalInterest": false, "originalName": "JETMONSTER", - "alternateName": "SKYSHOP", + "name": "SKYSHOP", "locationPrecisionId": null, "locationPrecision": null, "elevationPrecisionId": 20114002, @@ -1218,21 +1218,21 @@ "purpose": null, "statusId": 22104006, "status": null, - "qtDepthId": 22108003, - "qtDepth": null, + "depthPrecisionId": 22108003, + "depthPrecision": null, "topBedrockFreshMd": 759.5479580385368, "topBedrockWeatheredMd": 1.338392690447342, "hasGroundwater": false, "remarks": null, "lithologyTopBedrockId": 15104543, "lithologyTopBedrock": null, - "lithostratigraphyId": 15302037, - "lithostratigraphy": null, - "chronostratigraphyId": 15001060, - "chronostratigraphy": null, + "lithostratigraphyTopBedrockId": 15302037, + "lithostratigraphyTopBedrock": null, + "chronostratigraphyTopBedrockId": 15001060, + "chronostratigraphyTopBedrock": null, "referenceElevation": 899.1648284248844, - "qtReferenceElevationId": 20114006, - "qtReferenceElevation": null, + "referenceElevationPrecisionId": 20114006, + "referenceElevationPrecision": null, "referenceElevationTypeId": 20117005, "referenceElevationType": null, "stratigraphies": [ diff --git a/tests/api/TestData/missing_required_headers_testdata.csv b/tests/api/TestData/missing_required_headers_testdata.csv index 7e6fac12b..f1dd9a7a5 100644 --- a/tests/api/TestData/missing_required_headers_testdata.csv +++ b/tests/api/TestData/missing_required_headers_testdata.csv @@ -1,3 +1,3 @@ -alternate_name; +name; Unit_Test_ Row 1; Unit_Test_ Row 2; diff --git a/tests/api/TestData/testdata.csv b/tests/api/TestData/testdata.csv index 87f066536..7e2dff36a 100644 --- a/tests/api/TestData/testdata.csv +++ b/tests/api/TestData/testdata.csv @@ -1,4 +1,4 @@ -import_id;id_geodin_shortname;id_info_geol;id_original;id_canton;id_geo_quat;id_geo_mol;id_geo_therm;id_top_fels;id_geodin;id_kernlager;original_name;project_name;alternate_name;date;restriction_id;restriction_until;original_reference_system;location_x;location_y;location_x_lv_03;location_y_lv_03;location_precision_id;elevation_z;elevation_precision_id;reference_elevation;reference_elevation_type_id;qt_reference_elevation_id;hrs_id;type_id;purpose_id;status_id;remarks;total_depth;qt_depth_id;top_bedrock_fresh_md;top_bedrock_weathered_md;has_groundwater;lithology_top_bedrock_id;chronostratigraphy_id;lithostratigraphy_id +import_id;id_geodin_shortname;id_info_geol;id_original;id_canton;id_geo_quat;id_geo_mol;id_geo_therm;id_top_fels;id_geodin;id_kernlager;original_name;project_name;name;date;restriction_id;restriction_until;original_reference_system;location_x;location_y;location_x_lv_03;location_y_lv_03;location_precision_id;elevation_z;elevation_precision_id;reference_elevation;reference_elevation_type_id;qt_reference_elevation_id;hrs_id;type_id;purpose_id;status_id;remarks;total_depth;qt_depth_id;top_bedrock_fresh_md;top_bedrock_weathered_md;has_groundwater;lithology_top_bedrock_id;chronostratigraphy_id;lithostratigraphy_id 545;Id_1;Id_2;;;;;Id_3;;;kernlager AETHERMAGIC;Unit_Test_1;Projekt 1 ;Unit_Test_1_a;2021-08-06 00:36:21.991827+00;20111002;;20104001;2618962;1144995;;;20113005;640.7726659;20114001;317.9010264;20117002;20114004;20106001;20101001;22103001;22104003;this product is top-notch.;4232.711946;22108003;398.8529283;656.2476436;TRUE;15104669;15001073;15300261 8787;Id_4;;Id_5;Id_6;;;;;;;Unit_Test_2;Projekt 2;Unit_Test_2_a;2021-03-31 12:20:10.341393+00;;;20104001;2631690;1170516;;;20113002;3430.769638;20114005;2016.314814;20117005;20114004;20106001;20101001;22103001;22104008;This product works certainly well. It perfectly improves my tennis by a lot.;794.1547194;22108005;958.2378855;549.9801019;;15104670;15001009;15302009 5454;;;Id_7;Id_8;;;;Id_9;;;Unit_Test_3;Projekt 3;Unit_Test_3_a;;20111002;01.12.2023;20104001;2614834;1178661;;;20113005;1720.766609;20114003;1829.812475;20117005;20114002;20106001;20101001;;22104002;This is a really good product.;2429.747725;22108002;759.7574008;827.8441205;TRUE;15104671;15001007;15302339