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

Feature/refactor visit page count #38

Merged
merged 8 commits into from
Nov 8, 2021
Merged
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
1 change: 0 additions & 1 deletion LinkDotNet.Blog.IntegrationTests/SqlDatabaseTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ async Task IAsyncLifetime.DisposeAsync()

public async ValueTask DisposeAsync()
{
await DbContext.Database.EnsureDeletedAsync();
await DbContext.DisposeAsync();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using LinkDotNet.Blog.Domain;
Expand Down Expand Up @@ -97,38 +96,5 @@ public async Task ShouldGetAboutMeClicks()
data.TotalAboutMeClicks.Should().Be(2);
data.AboutMeClicksLast30Days.Should().Be(1);
}

[Fact]
public async Task ShouldGetBlogPostClicks()
{
var record1 = new UserRecord
{
UrlClicked = "blogPost/1",
};
var record2 = new UserRecord
{
UrlClicked = "blogPost/2",
};
var record3 = new UserRecord
{
UrlClicked = "blogPost/1",
};
var record4 = new UserRecord
{
UrlClicked = "unrelated",
};
await Repository.StoreAsync(record1);
await Repository.StoreAsync(record2);
await Repository.StoreAsync(record3);
await Repository.StoreAsync(record4);

var data = (await sut.GetDashboardDataAsync()).BlogPostVisitCount.ToList();

data.Count.Should().Be(2);
data[0].Key.Should().Be("blogPost/1");
data[0].Value.Should().Be(2);
data[1].Key.Should().Be("blogPost/2");
data[1].Value.Should().Be(1);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Html.Dom;
using Bunit;
using FluentAssertions;
using LinkDotNet.Blog.Domain;
using LinkDotNet.Blog.Infrastructure.Persistence;
using LinkDotNet.Blog.TestUtilities;
using LinkDotNet.Blog.Web.Shared.Admin.Dashboard;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -21,12 +20,10 @@ public async Task ShouldShowCounts()
var blogPost = new BlogPostBuilder().WithTitle("I was clicked").WithLikes(2).Build();
await Repository.StoreAsync(blogPost);
using var ctx = new TestContext();
ctx.Services.AddScoped<IRepository<BlogPost>>(_ => Repository);
var visits = new List<KeyValuePair<string, int>> { new($"blogPost/{blogPost.Id}", 5) };
var pageVisitCounts = visits.OrderByDescending(s => s.Value);
ctx.Services.AddScoped(_ => DbContext);
await SaveBlogPostArticleClicked(blogPost.Id, 10);

var cut = ctx.RenderComponent<VisitCountPerPage>(p => p.Add(
s => s.PageVisitCount, pageVisitCounts));
var cut = ctx.RenderComponent<VisitCountPerPage>();

cut.WaitForState(() => cut.FindAll("td").Any());
var elements = cut.FindAll("td").ToList();
Expand All @@ -35,36 +32,54 @@ public async Task ShouldShowCounts()
titleData.Should().NotBeNull();
titleData.InnerHtml.Should().Be(blogPost.Title);
titleData.Href.Should().Contain($"blogPost/{blogPost.Id}");
elements[1].InnerHtml.Should().Be("5");
elements[1].InnerHtml.Should().Be("10");
elements[2].InnerHtml.Should().Be("2");
}

[Fact]
public void ShouldIgnoreNullForBlogPostVisits()
public async Task ShouldFilterStartDate()
{
var blogPost1 = new BlogPostBuilder().WithTitle("1").WithLikes(2).Build();
var blogPost2 = new BlogPostBuilder().WithTitle("2").WithLikes(2).Build();
await Repository.StoreAsync(blogPost1);
await Repository.StoreAsync(blogPost2);
var urlClicked1New = new UserRecord
{ UrlClicked = $"blogPost/{blogPost1.Id}", DateTimeUtcClicked = DateTime.UtcNow };
var urlClicked1Old = new UserRecord
{ UrlClicked = $"blogPost/{blogPost1.Id}", DateTimeUtcClicked = DateTime.MinValue };
var urlClicked2 = new UserRecord
{ UrlClicked = $"blogPost/{blogPost2.Id}", DateTimeUtcClicked = DateTime.MinValue };
await DbContext.UserRecords.AddRangeAsync(new[] { urlClicked1New, urlClicked1Old, urlClicked2 });
await DbContext.SaveChangesAsync();
using var ctx = new TestContext();
ctx.Services.AddScoped<IRepository<BlogPost>>(_ => Repository);
ctx.Services.AddScoped(_ => DbContext);
var cut = ctx.RenderComponent<VisitCountPerPage>();

var cut = ctx.RenderComponent<VisitCountPerPage>(p => p.Add(
s => s.PageVisitCount, null));
cut.FindComponent<DateRangeSelector>().Find("select").Change(DateTime.UtcNow.Date);

cut.WaitForState(() => cut.FindAll("td").Any());
var elements = cut.FindAll("td").ToList();
elements.Should().BeEmpty();
elements.Count.Should().Be(3);
var titleData = elements[0].ChildNodes.Single() as IHtmlAnchorElement;
titleData.Should().NotBeNull();
titleData.InnerHtml.Should().Be(blogPost1.Title);
titleData.Href.Should().Contain($"blogPost/{blogPost1.Id}");
elements[1].InnerHtml.Should().Be("1");
}

[Fact]
public void ShouldIgnoreNotBlogPosts()
private async Task SaveBlogPostArticleClicked(string blogPostId, int count)
{
using var ctx = new TestContext();
ctx.Services.AddScoped<IRepository<BlogPost>>(_ => Repository);
var visits = new List<KeyValuePair<string, int>> { new("notablogpost", 5) };
var pageVisitCounts = visits.OrderByDescending(s => s.Value);
var urlClicked = $"blogPost/{blogPostId}";
for (var i = 0; i < count; i++)
{
var data = new UserRecord
{
UrlClicked = urlClicked,
};
await DbContext.UserRecords.AddAsync(data);
}

var cut = ctx.RenderComponent<VisitCountPerPage>(p => p.Add(
s => s.PageVisitCount, pageVisitCounts));

var elements = cut.FindAll("td").ToList();
elements.Should().BeEmpty();
await DbContext.SaveChangesAsync();
}
}
}
19 changes: 18 additions & 1 deletion LinkDotNet.Blog.UnitTests/Web/Pages/Admin/DashboardTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using System.Linq;
using System.Data.Common;
using System.Linq;
using Bunit;
using Bunit.TestDoubles;
using FluentAssertions;
using LinkDotNet.Blog.Domain;
using LinkDotNet.Blog.Infrastructure.Persistence;
using LinkDotNet.Blog.Infrastructure.Persistence.Sql;
using LinkDotNet.Blog.Web;
using LinkDotNet.Blog.Web.Pages.Admin;
using LinkDotNet.Blog.Web.Shared.Admin.Dashboard;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
Expand All @@ -18,11 +22,15 @@ public class DashboardTests : TestContext
[Fact]
public void ShouldNotShowAboutMeStatisticsWhenDisabled()
{
var options = new DbContextOptionsBuilder()
.UseSqlite(CreateInMemoryConnection())
.Options;
var dashboardService = new Mock<IDashboardService>();
this.AddTestAuthorization().SetAuthorized("test");
Services.AddScoped(_ => CreateAppConfiguration(false));
Services.AddScoped(_ => dashboardService.Object);
Services.AddScoped(_ => new Mock<IRepository<BlogPost>>().Object);
Services.AddScoped(_ => new BlogDbContext(options));
dashboardService.Setup(d => d.GetDashboardDataAsync())
.ReturnsAsync(new DashboardData());

Expand All @@ -41,5 +49,14 @@ private static AppConfiguration CreateAppConfiguration(bool aboutMeEnabled)
ProfileInformation = aboutMeEnabled ? new ProfileInformation() : null,
};
}

private static DbConnection CreateInMemoryConnection()
{
var connection = new SqliteConnection("Filename=:memory:");

connection.Open();

return connection;
}
}
}
8 changes: 4 additions & 4 deletions LinkDotNet.Blog.Web/Pages/Admin/Dashboard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
}
</div>
<div class="row mt-3">
<div class="col-auto">
<VisitCountPerPage PageVisitCount="@data.BlogPostVisitCount"></VisitCountPerPage>
</div>
</div>
<div class="col-12">
<VisitCountPerPage></VisitCountPerPage>
</div>
</div>
</div>
</div>

Expand Down
7 changes: 1 addition & 6 deletions LinkDotNet.Blog.Web/Pages/Admin/DashboardData.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Linq;

namespace LinkDotNet.Blog.Web.Pages.Admin
namespace LinkDotNet.Blog.Web.Pages.Admin
{
public class DashboardData
{
Expand All @@ -13,8 +10,6 @@ public class DashboardData

public int PageClicksLast30Days { get; set; }

public IOrderedEnumerable<KeyValuePair<string, int>> BlogPostVisitCount { get; set; }

public int TotalAboutMeClicks { get; set; }

public int AboutMeClicksLast30Days { get; set; }
Expand Down
12 changes: 0 additions & 12 deletions LinkDotNet.Blog.Web/Pages/Admin/DashboardService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ public async Task<DashboardData> GetDashboardDataAsync()
var aboutMeClicks = records.Count(r => r.UrlClicked.Contains("AboutMe"));
var aboutMeClicksLast30Days = records.Count(r => r.UrlClicked.Contains("AboutMe") && r.DateTimeUtcClicked >= DateTime.UtcNow.AddDays(-30));

var visitCount = GetPageVisitCount(records);

return new DashboardData
{
TotalAmountOfUsers = users,
Expand All @@ -46,17 +44,7 @@ public async Task<DashboardData> GetDashboardDataAsync()
PageClicksLast30Days = clicks30Days,
TotalAboutMeClicks = aboutMeClicks,
AboutMeClicksLast30Days = aboutMeClicksLast30Days,
BlogPostVisitCount = visitCount,
};
}

private static IOrderedEnumerable<KeyValuePair<string, int>> GetPageVisitCount(IEnumerable<UserRecord> records)
{
return records
.Where(u => u.UrlClicked.StartsWith("blogPost/"))
.GroupBy(u => u.UrlClicked)
.ToDictionary(k => k.Key, v => v.Count())
.OrderByDescending(d => d.Value);
}
}
}
2 changes: 1 addition & 1 deletion LinkDotNet.Blog.Web/Shared/AccessControl.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<li><h6 class="dropdown-header">Others</h6></li>
<li><a class="dropdown-item" href="Sitemap">Sitemap</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="https://github.com/linkdotnet/Blog/releases">Version 2.2</a></li>
<li><a class="dropdown-item" href="https://github.com/linkdotnet/Blog/releases">Version 2.3</a></li>
</ul>
</li>
<li class="nav-item"><a class="nav-link" href="logout">Log out</a></li>
Expand Down
30 changes: 30 additions & 0 deletions LinkDotNet.Blog.Web/Shared/Admin/Dashboard/DateRangeSelector.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="col-1">
<p>Since: </p>
</div>
<div class="col-2">
<select @onchange="RaiseDateTimeSpanChanged">
@foreach (var (title, time) in options)
{
<option value="@time">@title</option>
}
</select>
</div>
@code {
[Parameter]
public EventCallback<DateTime> DateTimeSpanChanged { get; set; }

private readonly Dictionary<string, DateTime> options = new()
{
{ "Beginning of time", DateTime.MinValue },
{ "Last 90 Days", DateTime.UtcNow.AddDays(-90) },
{ "Last 30 Days", DateTime.UtcNow.AddDays(-30) },
{ "Last 7 Days", DateTime.UtcNow.AddDays(-7) },
{ "Since Today", DateTime.UtcNow.Date },
};

private async Task RaiseDateTimeSpanChanged(ChangeEventArgs args)
{
await DateTimeSpanChanged.InvokeAsync(DateTime.Parse(args.Value!.ToString()));
}

}
13 changes: 13 additions & 0 deletions LinkDotNet.Blog.Web/Shared/Admin/Dashboard/VisitCountPageData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace LinkDotNet.Blog.Web.Shared.Admin.Dashboard
{
public record VisitCountPageData
{
public string Id { get; init; }

public string Title { get; init; }

public int Likes { get; init; }

public int ClickCount { get; init; }
}
}
Loading