Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tvd to export #1753

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 55 additions & 36 deletions src/api/Controllers/BoreholeController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using BDMS.Authentication;
using BDMS.BoreholeGeometry;
using BDMS.Models;
using CsvHelper;
using CsvHelper.Configuration;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -149,56 +151,73 @@ private IQueryable<Borehole> GetBoreholesWithIncludes()
/// Exports the details of up to <see cref="MaxPageSize"></see> boreholes as a CSV file. Filters the boreholes based on the provided list of IDs.
/// </summary>
/// <param name="ids">The list of IDs for the boreholes to be exported.</param>
/// <returns>A CSV file containing the details specified boreholes.</returns>
/// <returns>A CSV file containing the details of the specified boreholes.</returns>
[HttpGet("export-csv")]
[Authorize(Policy = PolicyNames.Viewer)]
public async Task<IActionResult> DownloadCsvAsync([FromQuery][MaxLength(MaxPageSize)] IEnumerable<int> ids)
{
ids = ids.Take(MaxPageSize).ToList();
if (!ids.Any()) return BadRequest("The list of IDs must not be empty.");
List<int> idList = ids.Take(MaxPageSize).ToList();
if (idList.Count < 1) return BadRequest("The list of IDs must not be empty.");

var boreholes = await Context.Boreholes
.Where(borehole => ids.Contains(borehole.Id))
.Select(b => new
List<BoreholeExport> boreholes = await Context.Boreholes
.Where(borehole => idList.Contains(borehole.Id))
.OrderBy(b => idList.IndexOf(b.Id))
.Select(b => new BoreholeExport
{
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,
Id = b.Id,
OriginalName = b.OriginalName,
ProjectName = b.ProjectName,
Name = b.Name,
RestrictionId = b.RestrictionId,
RestrictionUntil = b.RestrictionUntil,
NationalInterest = b.NationalInterest,
LocationX = b.LocationX,
LocationY = b.LocationY,
LocationPrecisionId = b.LocationPrecisionId,
ElevationZ = b.ElevationZ,
ElevationPrecisionId = b.ElevationPrecisionId,
ReferenceElevation = b.ReferenceElevation,
ReferenceElevationTypeId = b.ReferenceElevationTypeId,
ReferenceElevationPrecisionId = b.ReferenceElevationPrecisionId,
HrsId = b.HrsId,
TypeId = b.TypeId,
PurposeId = b.PurposeId,
StatusId = b.StatusId,
Remarks = b.Remarks,
TotalDepth = b.TotalDepth,
DepthPresicionId = b.DepthPrecisionId,
TopBedrockFreshMd = b.TopBedrockFreshMd,
TopBedrockWeatheredMd = b.TopBedrockWeatheredMd,
HasGroundwater = b.HasGroundwater,
LithologyTopBedrockId = b.LithologyTopBedrockId,
ChronostratigraphyTopBedrockId = b.ChronostratigraphyTopBedrockId,
LithostratigraphyTopBedrockId = b.LithostratigraphyTopBedrockId,
})
.ToListAsync()
.ConfigureAwait(false);

if (boreholes.Count == 0) return NotFound("No borehole(s) found for the provided id(s).");

foreach (var b in boreholes)
{
var boreholeGeometry = await Context.BoreholeGeometry
.AsNoTracking()
.Where(g => g.BoreholeId == b.Id)
.ToListAsync()
.ConfigureAwait(false);
Comment on lines +203 to +207
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PP: Einrückung korrigieren.


b.TotalDepthTvd = BoreholeGeometryController.GetTVDIfGeometryExists(b.TotalDepth, boreholeGeometry);
b.TopBedrockFreshTvd = BoreholeGeometryController.GetTVDIfGeometryExists(b.TopBedrockFreshMd, boreholeGeometry);
b.TopBedrockWeatheredTvd = BoreholeGeometryController.GetTVDIfGeometryExists(b.TopBedrockWeatheredMd, boreholeGeometry);
Comment on lines +209 to +211
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q/REQ: Lässt sich die Methode in die Helper-Klasse, wo auch die Methode GetDepthTVD() liegt auslagern? So rufen sich die Controller nicht gegenseitig auf. GetTVDIfGeometryExists kann zum Beispiel als Extension von List<BoreholeGeometryElement> definiert werden.

Wenn man gerade dabei ist und ich den Namen Helper nicht gut finde: Im Helper hat es einige Extensions auf List<BoreholeGeometryElement> oder BoreholeGeometryElement, welche in eine eigene Klasse BoreholeGeometryElementExtensions ausgelagert werden können. Das macht das Ganze klarer und man packt nicht alles automatisch in diese Helper-Klasse 😁. Man muss dann schauen, was noch übrig bleibt und ob man diese Methoden ebenfalls weg bringt oder den Klassennamen treffender umformulieren kann.

}

using var stringWriter = new StringWriter();
using var csvWriter = new CsvWriter(stringWriter, CultureInfo.InvariantCulture);
var csvConfig = new CsvConfiguration(new CultureInfo("de-CH"))
{
Delimiter = ";",
};
using var csvWriter = new CsvWriter(stringWriter, csvConfig);
await csvWriter.WriteRecordsAsync(boreholes).ConfigureAwait(false);

return File(Encoding.UTF8.GetBytes(stringWriter.ToString()), "text/csv", "boreholes_export.csv");
}

Expand Down
22 changes: 15 additions & 7 deletions src/api/Controllers/BoreholeGeometryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,28 +118,36 @@ public async Task<IActionResult> GetDepthTVD([FromQuery] int boreholeId, [FromQu
{
var geometry = await GetBoreholeGeometry(boreholeId).ConfigureAwait(false);

var tvd = GetTVDIfGeometryExists(depthMD, geometry);
if (tvd != null) return Ok(tvd);

logger?.LogInformation($"Invalid input, could not calculate true vertical depth from measured depth of {depthMD}");
return Ok();
Comment on lines +122 to +125
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PP: Die if-Anweisung umkehren, das macht es einfacher lesbar/verständlich.

if (tvd == null)
{
    logger?.LogInformation($"Invalid input, could not calculate true vertical depth from measured depth of {depthMD}");
    return Ok();
}

return Ok(tvd);

}

internal static double? GetTVDIfGeometryExists(double? depthMD, List<BoreholeGeometryElement> geometry)
{
if (geometry.Count < 2)
{
if (depthMD >= 0)
if (depthMD != null && depthMD >= 0)
{
// Return the depthMD unchanged as if the borehole is perfectly vertical and infinitely long.
return Ok(depthMD);
return depthMD;
}
}
else
else if (depthMD != null)
{
try
{
return Ok(geometry.GetDepthTVD(depthMD));
return geometry.GetDepthTVD(depthMD.Value);
}
catch (ArgumentOutOfRangeException)
{
// Exception is ignored so that the action returns an empty response in case the input was invalid.
// Exception is ignored so that the method returns null in case the input was invalid.
}
}

logger?.LogInformation($"Invalid input, could not calculate true vertical depth from measured depth of {depthMD}");
return Ok();
return null;
}

private async Task<List<BoreholeGeometryElement>> GetBoreholeGeometry(int boreholeId)
Expand Down
162 changes: 162 additions & 0 deletions src/api/Models/BoreholeExport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
namespace BDMS.Models;

/// <summary>
/// Represents a borehole from the csv export.
/// </summary>
public class BoreholeExport
Copy link
Contributor

@danjov danjov Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Kann hier etwas von Borehole geerbt werden? So spart man sich Doppelspurigkeit und muss bei Änderungen an den Borehole-Feldern nicht an diese Klasse denken. Siehe z.B. BoreholeImport-Klasse.

{
/// <summary>
/// Gets or sets the ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int Id { get; set; }

/// <summary>
/// Gets or sets the original name of the <see cref="BoreholeExport"/>.
/// </summary>
public string? OriginalName { get; set; }

/// <summary>
/// Gets or sets the project name associated with the <see cref="BoreholeExport"/>.
/// </summary>
public string? ProjectName { get; set; }

/// <summary>
/// Gets or sets the name of the <see cref="BoreholeExport"/>.
/// </summary>
public string? Name { get; set; }

/// <summary>
/// Gets or sets the restriction ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? RestrictionId { get; set; }

/// <summary>
/// Gets or sets the date until which the <see cref="BoreholeExport"/> is restricted.
/// </summary>
public DateTime? RestrictionUntil { get; set; }

/// <summary>
/// Gets or sets whether the <see cref="BoreholeExport"/> has national interest.
/// </summary>
public bool? NationalInterest { get; set; }

/// <summary>
/// Gets or sets the X-coordinate of the <see cref="BoreholeExport"/>'s location.
/// </summary>
public double? LocationX { get; set; }

/// <summary>
/// Gets or sets the Y-coordinate of the <see cref="BoreholeExport"/>'s location.
/// </summary>
public double? LocationY { get; set; }

/// <summary>
/// Gets or sets the location precision ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? LocationPrecisionId { get; set; }

/// <summary>
/// Gets or sets the elevation (Z) of the <see cref="BoreholeExport"/>.
/// </summary>
public double? ElevationZ { get; set; }

/// <summary>
/// Gets or sets the elevation precision ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? ElevationPrecisionId { get; set; }

/// <summary>
/// Gets or sets the reference elevation of the <see cref="BoreholeExport"/>.
/// </summary>
public double? ReferenceElevation { get; set; }

/// <summary>
/// Gets or sets the reference elevation type ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? ReferenceElevationTypeId { get; set; }

/// <summary>
/// Gets or sets the precision ID for the reference elevation of the <see cref="BoreholeExport"/>.
/// </summary>
public int? ReferenceElevationPrecisionId { get; set; }

/// <summary>
/// Gets or sets the HRS ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? HrsId { get; set; }

/// <summary>
/// Gets or sets the type ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? TypeId { get; set; }

/// <summary>
/// Gets or sets the purpose ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? PurposeId { get; set; }

/// <summary>
/// Gets or sets the status ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? StatusId { get; set; }

/// <summary>
/// Gets or sets any remarks associated with the <see cref="BoreholeExport"/>.
/// </summary>
public string? Remarks { get; set; }

/// <summary>
/// Gets or sets the total depth of the <see cref="BoreholeExport"/>.
/// </summary>
public double? TotalDepth { get; set; }

/// <summary>
/// Gets or sets the depth precision ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? DepthPresicionId { get; set; }

/// <summary>
/// Gets or sets the top fresh bedrock depth of the <see cref="BoreholeExport"/>.
/// </summary>
public double? TopBedrockFreshMd { get; set; }

/// <summary>
/// Gets or sets the top weathered bedrock depth of the <see cref="BoreholeExport"/>.
/// </summary>
public double? TopBedrockWeatheredMd { get; set; }

/// <summary>
/// Gets or sets whether the <see cref="BoreholeExport"/> has groundwater.
/// </summary>
public bool? HasGroundwater { get; set; }

/// <summary>
/// Gets or sets the lithology top bedrock ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? LithologyTopBedrockId { get; set; }

/// <summary>
/// Gets or sets the chronostratigraphy top bedrock ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? ChronostratigraphyTopBedrockId { get; set; }

/// <summary>
/// Gets or sets the lithostratigraphy top bedrock ID of the <see cref="BoreholeExport"/>.
/// </summary>
public int? LithostratigraphyTopBedrockId { get; set; }

/// <summary>
/// Gets or sets the <see cref="BoreholeExport"/>'s true vertical total depth.
/// </summary>
public double? TotalDepthTvd { get; set; }

/// <summary>
/// Gets or sets the <see cref="BoreholeExport"/>'s true vertical top fresh bedrock depth.
/// </summary>
public double? TopBedrockFreshTvd { get; set; }

/// <summary>
/// Gets or sets the <see cref="BoreholeExport"/>'s true vertical top weathered bedrock depth.
/// </summary>
public double? TopBedrockWeatheredTvd { get; set; }
}
4 changes: 4 additions & 0 deletions src/client/cypress/e2e/helpers/testHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ export const returnToOverview = () => {
cy.wait(["@edit_list", "@borehole"]);
};

export const getElementByDataCy = attribute => {
return cy.get(`[data-cy=${attribute}]`);
};

export const deleteBorehole = id => {
cy.get("@id_token").then(token => {
cy.request({
Expand Down
Loading
Loading