Add Versionning for bundle backtest request
This commit is contained in:
@@ -698,7 +698,7 @@ public class BacktestController : BaseController
|
|||||||
/// <returns>The requested bundle backtest request with current status and results.</returns>
|
/// <returns>The requested bundle backtest request with current status and results.</returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Bundle/{id}")]
|
[Route("Bundle/{id}")]
|
||||||
public async Task<ActionResult<BundleBacktestRequest>> GetBundleBacktestRequest(string id)
|
public async Task<ActionResult<BundleBacktestRequestViewModel>> GetBundleBacktestRequest(string id)
|
||||||
{
|
{
|
||||||
if (!Guid.TryParse(id, out var requestId))
|
if (!Guid.TryParse(id, out var requestId))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ public class BundleBacktestRequestViewModel
|
|||||||
[Required]
|
[Required]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Version number for the bundle backtest request (auto-incremented for same names)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int Version { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The universal configuration that applies to all backtests
|
/// The universal configuration that applies to all backtests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -126,6 +132,7 @@ public class BundleBacktestRequestViewModel
|
|||||||
CompletedAt = request.CompletedAt,
|
CompletedAt = request.CompletedAt,
|
||||||
Status = request.Status,
|
Status = request.Status,
|
||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
|
Version = request.Version,
|
||||||
Results = request.Results,
|
Results = request.Results,
|
||||||
TotalBacktests = request.TotalBacktests,
|
TotalBacktests = request.TotalBacktests,
|
||||||
CompletedBacktests = request.CompletedBacktests,
|
CompletedBacktests = request.CompletedBacktests,
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ public class BundleBacktestRequest
|
|||||||
[Required]
|
[Required]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Version number for the bundle backtest request (auto-incremented for same names)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int Version { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The universal configuration that applies to all backtests (serialized as JSON)
|
/// The universal configuration that applies to all backtests (serialized as JSON)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Managing.Infrastructure.Databases.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddVersionToBundleBacktestRequests : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Version",
|
||||||
|
table: "BundleBacktestRequests",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 1);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_BundleBacktestRequests_UserId_Name_Version",
|
||||||
|
table: "BundleBacktestRequests",
|
||||||
|
columns: new[] { "UserId", "Name", "Version" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_BundleBacktestRequests_UserId_Name_Version",
|
||||||
|
table: "BundleBacktestRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Version",
|
||||||
|
table: "BundleBacktestRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -448,6 +448,11 @@ namespace Managing.Infrastructure.Databases.Migrations
|
|||||||
b.Property<int?>("UserId")
|
b.Property<int?>("UserId")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Version")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("RequestId")
|
b.HasIndex("RequestId")
|
||||||
@@ -459,6 +464,8 @@ namespace Managing.Infrastructure.Databases.Migrations
|
|||||||
|
|
||||||
b.HasIndex("UserId", "CreatedAt");
|
b.HasIndex("UserId", "CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "Name", "Version");
|
||||||
|
|
||||||
b.ToTable("BundleBacktestRequests");
|
b.ToTable("BundleBacktestRequests");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ public class BundleBacktestRequestEntity
|
|||||||
[MaxLength(255)]
|
[MaxLength(255)]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int Version { get; set; } = 1;
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[Column(TypeName = "jsonb")]
|
[Column(TypeName = "jsonb")]
|
||||||
public string ResultsJson { get; set; } = "[]";
|
public string ResultsJson { get; set; } = "[]";
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ public class ManagingDbContext : DbContext
|
|||||||
entity.Property(e => e.RequestId).IsRequired().HasMaxLength(255);
|
entity.Property(e => e.RequestId).IsRequired().HasMaxLength(255);
|
||||||
entity.Property(e => e.UserId);
|
entity.Property(e => e.UserId);
|
||||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||||
|
entity.Property(e => e.Version).IsRequired().HasDefaultValue(1);
|
||||||
entity.Property(e => e.Status)
|
entity.Property(e => e.Status)
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasConversion<string>(); // Store enum as string
|
.HasConversion<string>(); // Store enum as string
|
||||||
@@ -224,6 +225,9 @@ public class ManagingDbContext : DbContext
|
|||||||
|
|
||||||
// Composite index for user queries ordered by creation date
|
// Composite index for user queries ordered by creation date
|
||||||
entity.HasIndex(e => new { e.UserId, e.CreatedAt });
|
entity.HasIndex(e => new { e.UserId, e.CreatedAt });
|
||||||
|
|
||||||
|
// Composite index for user queries by name and version
|
||||||
|
entity.HasIndex(e => new { e.UserId, e.Name, e.Version });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure Scenario entity
|
// Configure Scenario entity
|
||||||
|
|||||||
@@ -709,11 +709,22 @@ public class PostgreSqlBacktestRepository : IBacktestRepository
|
|||||||
public void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest)
|
public void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest)
|
||||||
{
|
{
|
||||||
bundleRequest.User = user;
|
bundleRequest.User = user;
|
||||||
var entity = PostgreSqlMappers.Map(bundleRequest);
|
|
||||||
|
|
||||||
// Set the UserId by finding the user entity
|
// Set the UserId by finding the user entity
|
||||||
var userEntity = _context.Users.FirstOrDefault(u => u.Name == user.Name);
|
var userEntity = _context.Users.FirstOrDefault(u => u.Name == user.Name);
|
||||||
if (userEntity != null)
|
if (userEntity != null)
|
||||||
|
{
|
||||||
|
// Check for existing bundle requests with the same name for this user
|
||||||
|
var maxVersion = _context.BundleBacktestRequests
|
||||||
|
.Where(b => b.UserId == userEntity.Id && b.Name == bundleRequest.Name)
|
||||||
|
.Max(b => (int?)b.Version);
|
||||||
|
|
||||||
|
// Increment version if a bundle with the same name exists
|
||||||
|
bundleRequest.Version = (maxVersion ?? 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = PostgreSqlMappers.Map(bundleRequest);
|
||||||
|
if (userEntity != null)
|
||||||
{
|
{
|
||||||
entity.UserId = userEntity.Id;
|
entity.UserId = userEntity.Id;
|
||||||
}
|
}
|
||||||
@@ -725,11 +736,22 @@ public class PostgreSqlBacktestRepository : IBacktestRepository
|
|||||||
public async Task InsertBundleBacktestRequestForUserAsync(User user, BundleBacktestRequest bundleRequest)
|
public async Task InsertBundleBacktestRequestForUserAsync(User user, BundleBacktestRequest bundleRequest)
|
||||||
{
|
{
|
||||||
bundleRequest.User = user;
|
bundleRequest.User = user;
|
||||||
var entity = PostgreSqlMappers.Map(bundleRequest);
|
|
||||||
|
|
||||||
// Set the UserId by finding the user entity
|
// Set the UserId by finding the user entity
|
||||||
var userEntity = await _context.Users.FirstOrDefaultAsync(u => u.Name == user.Name);
|
var userEntity = await _context.Users.FirstOrDefaultAsync(u => u.Name == user.Name);
|
||||||
if (userEntity != null)
|
if (userEntity != null)
|
||||||
|
{
|
||||||
|
// Check for existing bundle requests with the same name for this user
|
||||||
|
var maxVersion = await _context.BundleBacktestRequests
|
||||||
|
.Where(b => b.UserId == userEntity.Id && b.Name == bundleRequest.Name)
|
||||||
|
.MaxAsync(b => (int?)b.Version);
|
||||||
|
|
||||||
|
// Increment version if a bundle with the same name exists
|
||||||
|
bundleRequest.Version = (maxVersion ?? 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = PostgreSqlMappers.Map(bundleRequest);
|
||||||
|
if (userEntity != null)
|
||||||
{
|
{
|
||||||
entity.UserId = userEntity.Id;
|
entity.UserId = userEntity.Id;
|
||||||
}
|
}
|
||||||
@@ -801,6 +823,7 @@ public class PostgreSqlBacktestRepository : IBacktestRepository
|
|||||||
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
||||||
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
||||||
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
||||||
|
entity.Version = bundleRequest.Version; // Preserve the version
|
||||||
entity.UpdatedAt = DateTime.UtcNow;
|
entity.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
// Serialize Results to JSON
|
// Serialize Results to JSON
|
||||||
@@ -842,6 +865,7 @@ public class PostgreSqlBacktestRepository : IBacktestRepository
|
|||||||
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
||||||
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
||||||
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
||||||
|
entity.Version = bundleRequest.Version; // Preserve the version
|
||||||
entity.UpdatedAt = DateTime.UtcNow;
|
entity.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
// Serialize Results to JSON
|
// Serialize Results to JSON
|
||||||
|
|||||||
@@ -391,7 +391,8 @@ public static class PostgreSqlMappers
|
|||||||
ProgressInfo = entity.ProgressInfo,
|
ProgressInfo = entity.ProgressInfo,
|
||||||
CurrentBacktest = entity.CurrentBacktest,
|
CurrentBacktest = entity.CurrentBacktest,
|
||||||
EstimatedTimeRemainingSeconds = entity.EstimatedTimeRemainingSeconds,
|
EstimatedTimeRemainingSeconds = entity.EstimatedTimeRemainingSeconds,
|
||||||
Name = entity.Name
|
Name = entity.Name,
|
||||||
|
Version = entity.Version
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize Results from JSON
|
// Deserialize Results from JSON
|
||||||
@@ -434,6 +435,7 @@ public static class PostgreSqlMappers
|
|||||||
CurrentBacktest = bundleRequest.CurrentBacktest,
|
CurrentBacktest = bundleRequest.CurrentBacktest,
|
||||||
EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds,
|
EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds,
|
||||||
Name = bundleRequest.Name,
|
Name = bundleRequest.Name,
|
||||||
|
Version = bundleRequest.Version,
|
||||||
UpdatedAt = DateTime.UtcNow
|
UpdatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -872,7 +872,7 @@ export class BacktestClient extends AuthorizedApiBase {
|
|||||||
return Promise.resolve<BundleBacktestRequest>(null as any);
|
return Promise.resolve<BundleBacktestRequest>(null as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
backtest_GetBundleBacktestRequests(): Promise<BundleBacktestRequestViewModel[]> {
|
backtest_GetBundleBacktestRequests(): Promise<BundleBacktestRequest[]> {
|
||||||
let url_ = this.baseUrl + "/Backtest/Bundle";
|
let url_ = this.baseUrl + "/Backtest/Bundle";
|
||||||
url_ = url_.replace(/[?&]$/, "");
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
@@ -890,13 +890,13 @@ export class BacktestClient extends AuthorizedApiBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected processBacktest_GetBundleBacktestRequests(response: Response): Promise<BundleBacktestRequestViewModel[]> {
|
protected processBacktest_GetBundleBacktestRequests(response: Response): Promise<BundleBacktestRequest[]> {
|
||||||
const status = response.status;
|
const status = response.status;
|
||||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
return response.text().then((_responseText) => {
|
return response.text().then((_responseText) => {
|
||||||
let result200: any = null;
|
let result200: any = null;
|
||||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BundleBacktestRequestViewModel[];
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BundleBacktestRequest[];
|
||||||
return result200;
|
return result200;
|
||||||
});
|
});
|
||||||
} else if (status !== 200 && status !== 204) {
|
} else if (status !== 200 && status !== 204) {
|
||||||
@@ -904,7 +904,7 @@ export class BacktestClient extends AuthorizedApiBase {
|
|||||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.resolve<BundleBacktestRequestViewModel[]>(null as any);
|
return Promise.resolve<BundleBacktestRequest[]>(null as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
backtest_GetBundleBacktestRequest(id: string): Promise<BundleBacktestRequestViewModel> {
|
backtest_GetBundleBacktestRequest(id: string): Promise<BundleBacktestRequestViewModel> {
|
||||||
@@ -4472,6 +4472,7 @@ export interface BundleBacktestRequest {
|
|||||||
completedAt?: Date | null;
|
completedAt?: Date | null;
|
||||||
status: BundleBacktestRequestStatus;
|
status: BundleBacktestRequestStatus;
|
||||||
name: string;
|
name: string;
|
||||||
|
version: number;
|
||||||
universalConfigJson: string;
|
universalConfigJson: string;
|
||||||
dateTimeRangesJson: string;
|
dateTimeRangesJson: string;
|
||||||
moneyManagementVariantsJson: string;
|
moneyManagementVariantsJson: string;
|
||||||
@@ -4542,6 +4543,7 @@ export interface BundleBacktestRequestViewModel {
|
|||||||
completedAt?: Date | null;
|
completedAt?: Date | null;
|
||||||
status: BundleBacktestRequestStatus;
|
status: BundleBacktestRequestStatus;
|
||||||
name: string;
|
name: string;
|
||||||
|
version: number;
|
||||||
universalConfig: BundleBacktestUniversalConfig;
|
universalConfig: BundleBacktestUniversalConfig;
|
||||||
dateTimeRanges: DateTimeRange[];
|
dateTimeRanges: DateTimeRange[];
|
||||||
moneyManagementVariants: MoneyManagementVariant[];
|
moneyManagementVariants: MoneyManagementVariant[];
|
||||||
|
|||||||
@@ -647,6 +647,7 @@ export interface BundleBacktestRequest {
|
|||||||
completedAt?: Date | null;
|
completedAt?: Date | null;
|
||||||
status: BundleBacktestRequestStatus;
|
status: BundleBacktestRequestStatus;
|
||||||
name: string;
|
name: string;
|
||||||
|
version: number;
|
||||||
universalConfigJson: string;
|
universalConfigJson: string;
|
||||||
dateTimeRangesJson: string;
|
dateTimeRangesJson: string;
|
||||||
moneyManagementVariantsJson: string;
|
moneyManagementVariantsJson: string;
|
||||||
@@ -717,6 +718,7 @@ export interface BundleBacktestRequestViewModel {
|
|||||||
completedAt?: Date | null;
|
completedAt?: Date | null;
|
||||||
status: BundleBacktestRequestStatus;
|
status: BundleBacktestRequestStatus;
|
||||||
name: string;
|
name: string;
|
||||||
|
version: number;
|
||||||
universalConfig: BundleBacktestUniversalConfig;
|
universalConfig: BundleBacktestUniversalConfig;
|
||||||
dateTimeRanges: DateTimeRange[];
|
dateTimeRanges: DateTimeRange[];
|
||||||
moneyManagementVariants: MoneyManagementVariant[];
|
moneyManagementVariants: MoneyManagementVariant[];
|
||||||
|
|||||||
@@ -75,6 +75,13 @@ const BundleRequestsTable = () => {
|
|||||||
Header: 'Name',
|
Header: 'Name',
|
||||||
accessor: 'name',
|
accessor: 'name',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Header: 'Version',
|
||||||
|
accessor: 'version',
|
||||||
|
Cell: ({ value }: { value: number }) => (
|
||||||
|
<span className="badge badge-sm badge-outline">{`v${value}`}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Header: 'Progress & Status',
|
Header: 'Progress & Status',
|
||||||
accessor: 'completedBacktests',
|
accessor: 'completedBacktests',
|
||||||
|
|||||||
Reference in New Issue
Block a user