Results site in .NET Core

This commit is contained in:
Elton Stoneman 2018-09-26 10:29:39 +01:00
parent 879e5bc477
commit 985af62bb1
14 changed files with 165 additions and 42 deletions

View File

@ -0,0 +1,42 @@
version: "3.2"
services:
vote:
build:
context: ./vote/dotnet
ports:
- "5000:80"
depends_on:
- message-queue
result:
build:
context: ./result/dotnet
ports:
- "5001:80"
environment:
- "Data:ConnectionString=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
depends_on:
- db
worker:
build:
context: ./worker/dotnet
environment:
- "Data:ConnectionString=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
depends_on:
- message-queue
- db
message-queue:
image: nats:nanoserver
db:
image: dockersamples/tidb:nanoserver
ports:
- "3306:4000"
networks:
default:
external:
name: nat

16
result/dotnet/Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM microsoft/dotnet:2.1-sdk as builder
WORKDIR /Result
COPY Result/Result.csproj .
RUN dotnet restore
COPY /Result .
RUN dotnet publish -c Release -o /out Result.csproj
# app image
FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
ENTRYPOINT ["dotnet", "Result.dll"]
COPY --from=builder /out .

View File

@ -1,14 +1,9 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Result.Models;
using Microsoft.AspNetCore.SignalR;
namespace Result.Hubs
{
public class ResultsHub : Hub
{
public async Task UpdateResults(ResultsModel results)
{
await Clients.All.SendAsync("UpdateResults", results);
}
//no public methods, only used for push from PublishRTesultsTimer
}
}

View File

@ -7,9 +7,5 @@
public int OptionB { get; set; }
public int VoteCount { get; set; }
public double OptionAPercent { get; set; }
public double OptionBPercent { get; set; }
}
}

View File

@ -1,14 +1,16 @@
@page
@model Result.Pages.IndexModel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cats vs Dogs -- Result</title>
<title>@Model.OptionA vs @Model.OptionB -- Result</title>
<base href="/index.html">
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
<meta name="keywords" content="docker-compose, docker, stack">
<meta name="author" content="Docker">
<link rel='stylesheet' href='/stylesheets/style.css' />
<link rel='stylesheet' href='~/css/site.css' />
</head>
<body>
<div id="background-stats">
@ -22,22 +24,20 @@
<div id="content-container">
<div id="content-container-center">
<div id="choice">
<div class="choice cats">
<div class="label">Cats</div>
<div class="choice resulta">
<div class="label">@Model.OptionA</div>
<div class="stat" id="optionA"></div>
</div>
<div class="divider"></div>
<div class="choice dogs">
<div class="label">Dogs</div>
<div class="choice resultb">
<div class="label">@Model.OptionB</div>
<div class="stat" id="optionB"></div>
</div>
</div>
</div>
</div>
<div id="result">
<span ng-if="total == 0">No votes yet</span>
<span ng-if="total == 1">{{total}} vote</span>
<span ng-if="total >= 2">{{total}} votes</span>
<span id="totalVotes">No votes yet</span>
</div>
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script src="~/js/results.js"></script>

View File

@ -4,14 +4,30 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
namespace Result.Pages
{
public class IndexModel : PageModel
{
private string _optionA;
private string _optionB;
protected readonly IConfiguration _configuration;
public string OptionA { get; private set; }
public string OptionB { get; private set; }
public IndexModel(IConfiguration configuration)
{
_configuration = configuration;
_optionA = _configuration.GetValue<string>("Voting:OptionA");
_optionB = _configuration.GetValue<string>("Voting:OptionB");
}
public void OnGet()
{
OptionA = _optionA;
OptionB = _optionB;
}
}
}

View File

@ -1,10 +1,10 @@
{
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56785",
"sslPort": 44369
"sslPort": 0
}
},
"profiles": {
@ -18,10 +18,10 @@
"Result": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

View File

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Result.Hubs;
using Result.Timers;
namespace Result
{
@ -26,6 +21,7 @@ namespace Result
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
services.AddSingleton<PublishResultsTimer>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
@ -45,6 +41,9 @@ namespace Result
routes.MapHub<ResultsHub>("/resultsHub");
});
app.UseMvc();
var timer = app.ApplicationServices.GetService<PublishResultsTimer>();
timer.Start();
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Timers;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Result.Hubs;
using Result.Models;
namespace Result.Timers
{
public class PublishResultsTimer
{
private readonly IHubContext<ResultsHub> _hubContext;
private readonly Timer _timer;
//TODO- temp
private static Random _Random = new Random();
public PublishResultsTimer(IHubContext<ResultsHub> hubContext, IConfiguration configuration)
{
_hubContext = hubContext;
var publishMilliseconds = configuration.GetValue<int>("ResultsTimer:PublishMilliseconds");
_timer = new Timer(publishMilliseconds)
{
Enabled = false
};
_timer.Elapsed += PublishResults;
}
public void Start()
{
if (!_timer.Enabled)
{
_timer.Start();
}
}
private void PublishResults(object sender, ElapsedEventArgs e)
{
var model = new ResultsModel
{
OptionA = _Random.Next(0, 100),
OptionB = _Random.Next(0, 100)
};
model.VoteCount = model.OptionA + model.OptionB;
_hubContext.Clients.All.SendAsync("UpdateResults", model);
}
}
}

View File

@ -1,4 +1,11 @@
{
"Voting": {
"OptionA": "Cats",
"OptionB": "Dogs"
},
"ResultsTimer": {
"PublishMilliseconds": 1500
},
"Logging": {
"LogLevel": {
"Default": "Warning"

View File

@ -95,12 +95,12 @@ body {
text-transform: uppercase;
}
#choice .choice.dogs {
#choice .choice.resultb {
color: #00cbca;
float: right;
}
#choice .choice.cats {
#choice .choice.resulta {
color: #2196f3;
float: left;
}

View File

@ -3,14 +3,23 @@
var connection = new signalR.HubConnectionBuilder().withUrl("/resultsHub").build();
connection.on("UpdateResults", function (results) {
data = JSON.parse(json);
document.body.style.opacity=1;
var a = parseInt(data.optionA || 0);
var b = parseInt(data.optionB || 0);
var a = parseInt(results.optionA || 0);
var b = parseInt(results.optionB || 0);
var percentages = getPercentages(a, b);
document.getElementById("optionA").innerText = percentages.a + "%";
document.getElementById("optionB").innerText = percentages.b + "%";
if (results.voteCount > 0) {
var totalVotes = results.voteCount + (results.voteCount > 1 ? " votes" : " vote");
document.getElementById("totalVotes").innerText = totalVotes;
}
var bg1 = document.getElementById('background-stats-1');
var bg2 = document.getElementById('background-stats-2');
bg1.style.width = percentages.a + "%";
bg2.style.width = percentages.b + "%";
});
connection.start().catch(function (err) {

View File

@ -1,4 +0,0 @@
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your Javascript code.