diff --git a/APSIM.POStats.Collector/README.md b/APSIM.POStats.Collector/README.md new file mode 100644 index 0000000..f4d2e10 --- /dev/null +++ b/APSIM.POStats.Collector/README.md @@ -0,0 +1,19 @@ +# APSIM.POStats.Collector + +Console app that uploads validation data for pull request reports. + +Prerequisites +- .NET SDK 8.0+ + +Build +- `dotnet build APSIM.PerformanceTests.sln` + +Run (direct) +- `dotnet run --project APSIM.POStats.Collector/APSIM.POStats.Collector.csproj -- ` + +Run (VS Code) +- Use the `Collector` launch configuration (or the compound `Portal + Collector`). + +Notes +- The collector is intended to be run regularly (CI or scheduled) or launched from the debugger to insert PR validation data into the portal database. +- Shared utilities and DB context are in the `APSIM.POStats.Shared` project. diff --git a/APSIM.POStats.Portal/Pages/Index.cshtml b/APSIM.POStats.Portal/Pages/Index.cshtml index 30b2ab4..4553406 100644 --- a/APSIM.POStats.Portal/Pages/Index.cshtml +++ b/APSIM.POStats.Portal/Pages/Index.cshtml @@ -1,8 +1,8 @@ -@page "{pullRequestNumber:int=7867}" +@page "{pullRequestNumber:int?}" @model IndexModel @{ ViewData["Title"] = "Home"; - ViewData["ID"] = @Model.PullRequest.Id; + ViewData["ID"] = @Model.PullRequest?.Id; } @using APSIM.POStats.Shared; @using APSIM.POStats.Shared.Comparison; @@ -16,6 +16,36 @@

Predicted / Observed Stats

+ @if (Model.PullRequest == null) + { +

This site publishes validation and performance statistics for APSIM pull requests.

+

It is connected to the ApsimX GitHub repository and maintained by the APSIM Initiative to help contributors quickly review statistical changes in pull request results.

+

@Model.HomeMessage

+ +

Recent Pull Requests

+ @if (Model.RecentPullRequests.Any()) + { +
    + @foreach (var pullRequest in Model.RecentPullRequests) + { +
  • + + PR #@pullRequest.PullRequest + + - @pullRequest.DateRun.ToString("yyyy-MM-dd HH:mm") + - by @pullRequest.Author + - commit @((pullRequest.Commit?.Length ?? 0) > 7 ? pullRequest.Commit.Substring(0, 7) : pullRequest.Commit) +
  • + } +
+ } + else + { +

No pull requests were found in the stats database yet.

+ } + } + else + {

@@ -33,19 +63,19 @@ *** Very Good 0.00 ≤ RSR ≤ 0.50 - 0.75 < NSE ≤ 1.00 + 0.75 < NSE ≤ 1.00 ** Good - 0.50 < RSR ≤ 0.60 - 0.65 < NSE ≤ 0.75 + 0.50 < RSR ≤ 0.60 + 0.65 < NSE ≤ 0.75 * Satisfactory - 0.60 < RSR ≤ 0.70 - 0.50 < NSE ≤ 0.65 + 0.60 < RSR ≤ 0.70 + 0.50 < NSE ≤ 0.65   @@ -234,4 +264,5 @@ }
+ } diff --git a/APSIM.POStats.Portal/Pages/Index.cshtml.cs b/APSIM.POStats.Portal/Pages/Index.cshtml.cs index 34adfbd..a528d5a 100644 --- a/APSIM.POStats.Portal/Pages/Index.cshtml.cs +++ b/APSIM.POStats.Portal/Pages/Index.cshtml.cs @@ -33,6 +33,12 @@ public IndexModel(StatsDbContext stats) /// The pull request being analysed. public PullRequestDetails PullRequest { get; private set; } + /// Message shown on the home page when no pull request is selected. + public string HomeMessage { get; private set; } + + /// Recent pull requests sorted by date (most recent first). + public List RecentPullRequests { get; private set; } = new List(); + /// The Url for the web site. public string BaseUrl { get { return $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}"; } } @@ -40,11 +46,25 @@ public IndexModel(StatsDbContext stats) /// Invoked when page is first loaded. /// The pull request to work with. - public void OnGet(int pullRequestNumber) + public void OnGet(int? pullRequestNumber) { - PullRequest = statsDb.PullRequests.FirstOrDefault(pr => pr.PullRequest == pullRequestNumber); + RecentPullRequests = statsDb.PullRequests + .OrderByDescending(pr => pr.DateRun) + .Take(20) + .ToList(); + + if (!pullRequestNumber.HasValue) + { + HomeMessage = "Enter a pull request number in the URL to view validation stats."; + return; + } + + PullRequest = statsDb.PullRequests.FirstOrDefault(pr => pr.PullRequest == pullRequestNumber.Value); if (PullRequest == null) - throw new Exception($"Cannot find pull request #{pullRequestNumber} in stats database"); + { + HomeMessage = $"Cannot find pull request #{pullRequestNumber.Value} in stats database."; + return; + } VariableComparison.Status status = PullRequestFunctions.GetStatus(PullRequest); } diff --git a/APSIM.POStats.Portal/README.md b/APSIM.POStats.Portal/README.md new file mode 100644 index 0000000..718a47e --- /dev/null +++ b/APSIM.POStats.Portal/README.md @@ -0,0 +1,27 @@ +# APSIM.POStats.Portal + +ASP.NET Core web portal for viewing predicted vs observed statistics for pull requests. + +Prerequisites +- .NET SDK 8.0+ +- Environment variable `PORTAL_DB` (see notes below) + +Database +- For local development set `PORTAL_DB=portal.db` to use SQLite (create `portal.db` at the repo root). +- To use MySQL provide a full connection string in `PORTAL_DB` (e.g. `Server=...;Database=...;User=...;Password=...;`). + +Build & Run (local) +- `dotnet build APSIM.PerformanceTests.sln` +- `dotnet run --project APSIM.POStats.Portal/APSIM.POStats.Portal.csproj` + +Development +- Default dev URLs: `https://localhost:5001` and `http://localhost:5000`. +- Open `/` to see the landing page; open `/{pullRequestNumber}` to view a PR report. +- Use the `Portal` launch configuration or the compound `Portal + Collector` for debugging both services. + +Docker +- Build and run via `./build.sh` and `./deploy.sh`. Local service runs at `http://localhost:8081/`. + +Troubleshooting +- Error: Cannot find environment variable `PORTAL_DB` — ensure it is set in your environment or run profile. +- HTTPS cert warnings: `dotnet dev-certs https --trust`. diff --git a/APSIM.POStats.Portal/Startup.cs b/APSIM.POStats.Portal/Startup.cs index e11ce85..02530a3 100644 --- a/APSIM.POStats.Portal/Startup.cs +++ b/APSIM.POStats.Portal/Startup.cs @@ -38,7 +38,28 @@ public void ConfigureServices(IServiceCollection services) if (connectionString.Contains(".db")) { Console.WriteLine("Using SQLite database"); - services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlite(connectionString)); + + // If the connection string is just a file path (e.g. "portal.db"), + // convert it to a proper Data Source connection string and ensure + // the directory and file exist so the provider can open it. + string sqliteConnectionString = connectionString; + if (!connectionString.Contains("=")) + { + var dbPath = System.IO.Path.IsPathRooted(connectionString) + ? connectionString + : System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), connectionString); + + var dbDir = System.IO.Path.GetDirectoryName(dbPath); + if (!string.IsNullOrEmpty(dbDir) && !System.IO.Directory.Exists(dbDir)) + System.IO.Directory.CreateDirectory(dbDir); + + if (!System.IO.File.Exists(dbPath)) + System.IO.File.Create(dbPath).Dispose(); + + sqliteConnectionString = $"Data Source={dbPath}"; + } + + services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlite(sqliteConnectionString)); } else { diff --git a/APSIM.POStats.Shared/README.md b/APSIM.POStats.Shared/README.md new file mode 100644 index 0000000..972140f --- /dev/null +++ b/APSIM.POStats.Shared/README.md @@ -0,0 +1,18 @@ +# APSIM.POStats.Shared + +Shared models, utilities, and database helpers used by the Portal and Collector projects. + +Contents (high level) +- `Collector.cs`, `PullRequestTimer.cs` — collector orchestration helpers +- `StatsDbContext.cs`, `SqliteUtilities.cs` — database helpers and EF context +- `GitHub/` — GitHub API helpers and PR models +- `Comparison/` — comparison utilities used for validation reports + +Build +- This project is built as part of the solution: `dotnet build APSIM.PerformanceTests.sln`. + +Usage +- Refer to this project from `APSIM.POStats.Collector` and `APSIM.POStats.Portal` for models and utilities. + +Notes +- Keep shared, framework-agnostic code here. Avoid adding UI or app-host-specific behaviour. diff --git a/README.md b/README.md index 889c9b6..2e27649 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,118 @@ Service runs on http://localhost:8081/ ## To build, run and test docker image on the web server -Refer to apsim-web repo which has a server-wide deployment script that is used. \ No newline at end of file +Refer to apsim-web repo which has a server-wide deployment script that is used. + + +## Local development + +This repository contains: + +- [APSIM.POStats.Portal/README.md](APSIM.POStats.Portal/README.md): ASP.NET Core web portal for viewing predicted/observed stats. +- [APSIM.POStats.Collector/README.md](APSIM.POStats.Collector/README.md): console app that uploads validation data. +- [APSIM.POStats.Shared/README.md](APSIM.POStats.Shared/README.md): shared models and utilities. +- APSIM.POStats.Tests: automated tests. + +### Prerequisites + +- .NET SDK 8.0+ +- Git +- Optional: Docker Desktop (for container workflow) +- Optional: VS Code with C# Dev Kit + +### Quick start (local, no Docker) + +1. Clone and enter the repository. +2. Create or confirm a local sqlite database file at the repo root named portal.db. +3. Set environment variables (required). +4. Build and run the portal. + +#### 1) Create local environment file + +Create or update .env in the repo root with at least: + +PORTAL_DB=portal.db + +How it works: + +- If PORTAL_DB contains .db, the app uses sqlite. +- Otherwise, the app treats PORTAL_DB as a MySQL connection string. + +#### 2) Build the solution + +dotnet build APSIM.PerformanceTests.sln + +#### 3) Run the portal + +dotnet run --project APSIM.POStats.Portal/APSIM.POStats.Portal.csproj + +By default, development profile URLs are: + +- https://localhost:5001 +- http://localhost:5000 + +Home page behavior: + +- Navigate to / to see the landing page and recent pull requests. +- Navigate to /{pullRequestNumber} to open a specific PR report. + +### Run with VS Code (recommended) + +The repo includes launch and task configuration: + +- Launch config: Portal +- Launch config: Collector +- Compound launch: Portal + Collector +- Pre-launch task: Build Solution + +From Run and Debug, start Portal + Collector to launch both apps together. + +### Running the collector locally + +Collector expects command-line arguments when run directly. Example shape: + +pullRequestNumber commitId author validationPath + +If you use VS Code launch configuration, arguments can be supplied in launch settings or debugger UI. + +### Docker workflow (existing) + +Local image build and run: + +- ./build.sh +- ./deploy.sh +- Service: http://localhost:8081/ + +Server deployment: + +- Use apsim-web repository deployment process. + +### Tests + +Run all tests: + +dotnet test APSIM.PerformanceTests.sln + +### Troubleshooting + +- Error: Cannot find environment variable PORTAL_DB + - Ensure PORTAL_DB is set in your environment or injected by your run profile. + +- Portal starts but no PR data appears + - This is expected until collector/API has inserted pull request data. + +- HTTPS certificate warnings locally + - Trust the local .NET dev certificate if prompted: + - dotnet dev-certs https --trust + +- Database provider confusion + - sqlite: PORTAL_DB=portal.db + - mysql: PORTAL_DB=Server=...;Port=...;Database=...;User=...;Password=...; + +### Suggested local workflow + +1. Set PORTAL_DB=portal.db. +2. Build solution. +3. Start Portal + Collector in VS Code. +4. Open portal at http://localhost:5000. +5. Use root page to navigate recent pull requests. \ No newline at end of file