Skip to content

Commit

Permalink
Stats site
Browse files Browse the repository at this point in the history
  • Loading branch information
89netraM committed Aug 17, 2024
1 parent af4c606 commit 156d4ae
Show file tree
Hide file tree
Showing 15 changed files with 549 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,40 @@ jobs:
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

build-and-push-stats-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-stats
tags: latest

- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: Dockerfile-stats
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
23 changes: 23 additions & 0 deletions Dockerfile-stats
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src/rpc
COPY ./rpc ./
WORKDIR /src/common
COPY ./common ./
WORKDIR /src/stats
COPY ./stats/TimeKeep.Stats.csproj .
RUN dotnet restore
COPY ./stats .
RUN dotnet build -c Release -o /app/build

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY ./stats/appsettings.json .
COPY --from=publish /app/publish .
ENTRYPOINT dotnet TimeKeep.Stats.dll
14 changes: 14 additions & 0 deletions TimeKeep.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TimeKeep.App", "TimeKeep.Ap
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeKeep.Common", "common\TimeKeep.Common.csproj", "{6CED5B1B-65DC-4BBA-9779-C3402F32A863}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeKeep.Stats", "stats\TimeKeep.Stats.csproj", "{9B8CA094-2D26-4C97-8CF0-265F3712A280}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -111,6 +113,18 @@ Global
{6CED5B1B-65DC-4BBA-9779-C3402F32A863}.Release|ARM32.Build.0 = Release|Any CPU
{6CED5B1B-65DC-4BBA-9779-C3402F32A863}.Release|ARM64.ActiveCfg = Release|Any CPU
{6CED5B1B-65DC-4BBA-9779-C3402F32A863}.Release|ARM64.Build.0 = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|ARM32.ActiveCfg = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|ARM32.Build.0 = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Debug|ARM64.Build.0 = Debug|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|Any CPU.Build.0 = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|ARM32.ActiveCfg = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|ARM32.Build.0 = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|ARM64.ActiveCfg = Release|Any CPU
{9B8CA094-2D26-4C97-8CF0-265F3712A280}.Release|ARM64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
7 changes: 7 additions & 0 deletions stats/Options/TimeKeepOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TimeKeep.Stats.Options;

public class TimeKeepOptions
{
public required string Server { init; get; }
public required string Token { init; get; }
}
97 changes: 97 additions & 0 deletions stats/Pages/Components/WeekView/Default.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
@model (int, Dictionary<DateOnly, Week>)
@using System.Globalization
@using Humanizer
@using Humanizer.Localisation
@using TimeKeep.Stats.Services

@{
var (hue, weeks) = Model;
if (weeks.Count is 0)
{
return;
}
var maxTimeSpent = weeks.Max(p => p.Value.Max);
}

<table style="--table-color: hsl(@hue 64% 59%)">
<thead>
<tr>
<th></th>
@{
int? prevMonth = null;
}
@foreach (var (date, _) in weeks)
{
var weekEndDate = date.AddDays(6);
var month = weekEndDate.Month;
if (month != prevMonth)
{
<th>
@if (month == 1 || prevMonth is null)
{
@(weekEndDate.ToString("yyyy"))
}
@(weekEndDate.ToString("MMM"))
</th>
}
else
{
<th></th>
}

prevMonth = month;
}
</tr>
</thead>
<tbody>
<tr>
<th>Mon</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Monday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Monday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Tue</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Tuesday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Tuesday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Wed</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Wednesday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Wednesday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Thu</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Thursday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Thursday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Fri</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Friday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Friday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Sun</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Saturday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Saturday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
<tr>
<th>Sat</th>
@foreach (var (_, week) in weeks)
{
<td style="--time-spent: @((week.Sunday / maxTimeSpent).ToString("0.00000", NumberFormatInfo.InvariantInfo))" title="@(week.Sunday.Humanize(precision: 2, minUnit: TimeUnit.Minute))"></td>
}
</tr>
</tbody>
</table>
36 changes: 36 additions & 0 deletions stats/Pages/Weeks.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@page "/weeks"
@namespace TimeKeep.Stats.Pages
@using Grpc.Core
@using TimeKeep.RPC.Categories
@inject CategoriesService.CategoriesServiceClient categoriesClient

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weeks</title>
<link href="./style.css" rel="stylesheet">
</head>
<body>
@if (Request.Query.TryGetValue("categories", out var categories))
{
@await Component.InvokeAsync("WeekView", new { categories = categories.ToArray(), cancellationToken = HttpContext.RequestAborted })
}
<br />
<form>
<input name="categories" type="search" list="validCategories" required placeholder="Categories" value="@(categories)">
<datalist id="validCategories">
@{
var response = categoriesClient.List(new(), cancellationToken: HttpContext.RequestAborted);
var validCategories = await response.ResponseStream.ReadAllAsync().ToArrayAsync();
}
@foreach (var category in validCategories)
{
<option value="@(category.Name)"></option>
}
</datalist>
<input type="submit" value="Show">
</form>
</body>
</html>
27 changes: 27 additions & 0 deletions stats/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TimeKeep.Stats;
using TimeKeep.Stats.Options;
using TimeKeep.Stats.Services;

var builder = WebApplication.CreateBuilder(args);

var timeKeepOptions =
builder.Configuration.GetSection("TimeKeep").Get<TimeKeepOptions>()
?? throw new Exception("Missing \"TimeKeep\" options.");

builder.Services.AddRpcClients(timeKeepOptions);

builder.Services.AddTransient<WeekService>();

builder.Services.AddRazorPages();

var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();

app.Run();
29 changes: 29 additions & 0 deletions stats/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:11608",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5143",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
46 changes: 46 additions & 0 deletions stats/RpcServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.DependencyInjection;
using TimeKeep.RPC.Categories;
using TimeKeep.RPC.Entries;
using TimeKeep.Stats.Options;

namespace TimeKeep.Stats;

public static class RpcServiceCollectionExtensions
{
public static IServiceCollection AddRpcClients(
this IServiceCollection services,
TimeKeepOptions options
)
{
var base64Token = GetBase64Token(options.Token);
var callCredentials = CallCredentials.FromInterceptor(
(_, metadata) =>
{
metadata.Add("Authorization", $"Bearer {base64Token}");
return Task.CompletedTask;
}
);
services
.AddGrpcClient<EntriesService.EntriesServiceClient>(
(sp, o) => o.Address = new(options.Server)
)
.AddCallCredentials(callCredentials);
services
.AddGrpcClient<CategoriesService.CategoriesServiceClient>(
(sp, o) => o.Address = new(options.Server)
)
.AddCallCredentials(callCredentials);
return services;
}

private static string GetBase64Token(string token)
{
var utf8ValidatingEncoding = new UTF8Encoding(false, true);
var tokenBytes = utf8ValidatingEncoding.GetBytes(token);
return Convert.ToBase64String(tokenBytes);
}
}
Loading

0 comments on commit 156d4ae

Please sign in to comment.