Skip to content

Commit

Permalink
Merge pull request #38 from linkdotnet/feature/refactor-visit-page-count
Browse files Browse the repository at this point in the history
Feature/refactor visit page count
  • Loading branch information
linkdotnet authored Nov 8, 2021
2 parents 1812448 + 674eddd commit fea9ba8
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 123 deletions.
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

0 comments on commit fea9ba8

Please sign in to comment.