Skip to content
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
87 changes: 87 additions & 0 deletions IntelliTrader.Trading.Tests/HomeControllerDownloadLogTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.IO;
using IntelliTrader.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Xunit;

namespace IntelliTrader.Trading.Tests
{
public class HomeControllerDownloadLogTests : IDisposable
{
private readonly string _logDir;

public HomeControllerDownloadLogTests()
{
_logDir = Path.Combine(Directory.GetCurrentDirectory(), "log");
}

public void Dispose()
{
// Cleanup test log files created during testing
if (Directory.Exists(_logDir))
{
var testFiles = Directory.GetFiles(_logDir, "test-*.txt");
foreach (var f in testFiles)
{
try { File.Delete(f); } catch { }
}
}
}

[Fact]
public void DownloadLog_ReturnsNotFound_WhenNoLogsExist()
{
// Arrange
var controller = new HomeController();
string nonExistentType = "nonexistent_type_" + Guid.NewGuid().ToString("N");

// Act
var result = controller.DownloadLog(nonExistentType);

// Assert
var notFoundResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal("Log file not found.", notFoundResult.Value);
}

[Fact]
public void DownloadLog_ReturnsFileStreamResult_WhenLogFileExists()
{
// Arrange
if (!Directory.Exists(_logDir))
{
Directory.CreateDirectory(_logDir);
}

string testFilePath = Path.Combine(_logDir, "test-unit-general.txt");
File.WriteAllText(testFilePath, "[INF] Test general log entry\n[INF] Second line");

var controller = new HomeController();

try
{
// Act
var result = controller.DownloadLog("general");

// Assert
var fileResult = Assert.IsType<FileStreamResult>(result);
Assert.Equal("text/plain", fileResult.ContentType);
Assert.NotNull(fileResult.FileDownloadName);
Assert.EndsWith("-general.txt", fileResult.FileDownloadName);
Assert.NotNull(fileResult.FileStream);

using (var reader = new StreamReader(fileResult.FileStream))
{
string content = reader.ReadToEnd();
Assert.Contains("Test general log entry", content);
}
}
finally
{
if (File.Exists(testFilePath))
{
File.Delete(testFilePath);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ItemGroup>
<ProjectReference Include="..\IntelliTrader.Core\IntelliTrader.Core.csproj" />
<ProjectReference Include="..\IntelliTrader.Trading\IntelliTrader.Trading.csproj" />
<ProjectReference Include="..\IntelliTrader.Web\IntelliTrader.Web.csproj" />
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions IntelliTrader.Web/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,29 @@ public IActionResult PollLogs(string type = "general", int maxLines = 100)
}
}

[HttpGet]
public IActionResult DownloadLog(string type = "general")
{
try
{
string pattern = "general".Equals(type, StringComparison.OrdinalIgnoreCase) ? "*-general.txt" : "*-trades.txt";
string filePath = GetLatestLogFilePath(pattern);

if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath))
{
return NotFound("Log file not found.");
}

string fileName = Path.GetFileName(filePath);
var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return File(stream, "text/plain", fileName);
}
catch (Exception ex)
{
return BadRequest($"Error downloading log file: {ex.Message}");
}
}

private string GetLatestLogFilePath(string pattern)
{
var logsPath = Path.Combine(Directory.GetCurrentDirectory(), "log");
Expand Down
2 changes: 2 additions & 0 deletions IntelliTrader.Web/Static/Scripts/Views/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ function setLogType(type) {
$("#logTypeGeneralBtn").removeClass("active");
}

$("#downloadMainLogBtn").attr("href", "/Home/DownloadLog?type=" + type);

$("#logTerminal").html('<div class="text-muted">Loading logs...</div>');
pollLiveLogs();
}
Expand Down
8 changes: 7 additions & 1 deletion IntelliTrader.Web/Views/Home/Dashboard.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@
<h5 class="mb-0" style="color: var(--text-light); font-size: 16px; margin: 0;"><i class="fas fa-terminal mr-2"></i>Live Log Monitor</h5>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-sm btn-outline-light active" id="logTypeGeneralBtn" style="border-color: var(--card-border); margin-right: 5px;" onclick="setLogType('general')">General Log</button>
<button type="button" class="btn btn-sm btn-outline-light" id="logTypeTradesBtn" style="border-color: var(--card-border);" onclick="setLogType('trades')">Trades Log</button>
<button type="button" class="btn btn-sm btn-outline-light" id="logTypeTradesBtn" style="border-color: var(--card-border); margin-right: 5px;" onclick="setLogType('trades')">Trades Log</button>
<a id="downloadMainLogBtn" href="/Home/DownloadLog?type=general" class="btn btn-sm btn-outline-info" style="border-color: var(--card-border); color: var(--text-color);" download><i class="fas fa-download mr-1"></i> Download Log</a>
</div>
</div>
<div class="card-body p-0">
Expand Down Expand Up @@ -566,6 +567,7 @@
<option value="trades" selected>Trade Logs</option>
<option value="general">General Logs</option>
</select>
<a id="downloadTerminalLogBtn" href="/Home/DownloadLog?type=trades" class="btn btn-sm btn-outline-info" style="border-color: var(--card-border); color: var(--text-color); margin-right: 5px;" download><i class="fas fa-download mr-1"></i> Download Log</a>
<button id="clearTerminalBtn" class="btn btn-sm btn-outline-secondary" style="border-color: var(--card-border); color: var(--text-color);">Clear</button>
</div>
</div>
Expand Down Expand Up @@ -636,6 +638,10 @@
logTypeSelect.addEventListener("change", function() {
lastLogContent = "";
logTerminal.innerHTML = '<div class="text-muted">Loading logs...</div>';
const downloadBtn = document.getElementById("downloadTerminalLogBtn");
if (downloadBtn) {
downloadBtn.href = `/Home/DownloadLog?type=${logTypeSelect.value}`;
}
fetchLogs();
});

Expand Down
18 changes: 17 additions & 1 deletion magda_agent_system/agent_tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@
},
{
"id": "web-dashboard-log-download",
"status": "todo",
"status": "done",
"area": "web",
"risk": "low",
"title": "Add log file download button on Web Dashboard",
Expand Down Expand Up @@ -505,6 +505,22 @@
"acceptance": [
"Users can click export on the Stats page to download summary statistics."
]
},
{
"id": "web-dashboard-export-stats-formatting",
"status": "todo",
"area": "web",
"risk": "low",
"title": "Enhance ExportStats CSV/JSON formatting for trade analytics",
"description": "Add formatting options or date filtering parameters to ExportStats endpoint in HomeController for improved spreadsheet readability.",
"allowed_paths": [
"IntelliTrader.Web/Controllers/**",
"IntelliTrader.Web/Views/**",
"magda_agent_system/agent_tasks.json"
],
"acceptance": [
"ExportStats allows filtering by date range and returns well-formatted CSV or JSON."
]
}
]
}