Merge pull request #15 from OriolBonjoch/master
Removed notes, fixed footer gap and hide Twitter if not configured
This commit is contained in:
Коммит
dec38c3d8f
|
@ -1,19 +0,0 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-dotnet.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-dotnet.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-dotnet.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-dotnet.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
|
@ -1,19 +1,6 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.inf.ingress.game.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-game.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-game.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-game.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-game.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-java.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-java.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-java.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-java.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
|
@ -11,5 +11,6 @@ metadata:
|
|||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
data:
|
||||
APPLICATION_INSIGHTS_IKEY: {{ .Values.inf.appinsights.id }}
|
||||
APPLICATION_INSIGHTS_IKEY: {{ .Values.inf.appinsights.id }}
|
||||
APPLICATION_INSIGHTS_ENABLED: {{ if .Values.inf.appinsights.id }}'true'{{ else }}'false'{{ end }}
|
||||
PREDICTOR_URL: {{ .Values.inf.apiurls.predictor }}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-node.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-node.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-node.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-node.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
|
@ -1,19 +0,0 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-php.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-php.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-php.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-php.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
|
@ -1,19 +0,0 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-python.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-python.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-python.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-python.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:80 to use your application"
|
||||
kubectl port-forward $POD_NAME 80:80
|
||||
{{- end }}
|
|
@ -1,19 +1,9 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.inf.ingress.web.path }}
|
||||
{{- if $.Values.uploaderEnabled }}
|
||||
http{{ if $.Values.ingress.tlsEnabled }}s{{ end }}://{{ . }}{{ $.Values.inf.ingress.web.uploaderPath }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "rpsls-web.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "rpsls-web.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "rpsls-web.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "rpsls-web.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
||||
|
|
|
@ -6,16 +6,6 @@ inf:
|
|||
googleanalytics:
|
||||
id: {{googleanalyticsid}}
|
||||
ingress:
|
||||
dotnet:
|
||||
path: /dotnet-player
|
||||
node:
|
||||
path: /node-player
|
||||
python:
|
||||
path: /python-player
|
||||
php:
|
||||
path: /php-player
|
||||
java:
|
||||
path: /java-player
|
||||
game:
|
||||
path: /game-manager
|
||||
web:
|
||||
|
|
|
@ -6,16 +6,6 @@ inf:
|
|||
googleanalytics:
|
||||
id: {{googleanalyticsid}}
|
||||
ingress:
|
||||
dotnet:
|
||||
path: /dotnet-player
|
||||
node:
|
||||
path: /node-player
|
||||
python:
|
||||
path: /python-player
|
||||
php:
|
||||
path: /php-player
|
||||
java:
|
||||
path: /java-player
|
||||
game:
|
||||
path: /game-manager
|
||||
web:
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.8.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.4.10" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -7,9 +7,6 @@ using RPSLS.DotNetPlayer.Api.Services;
|
|||
using RPSLS.DotNetPlayer.Api.Settings;
|
||||
using RPSLS.DotNetPlayer.API.Services;
|
||||
using RPSLS.DotNetPlayer.API.Settings;
|
||||
using RPSLS.DotNetPlayer.API.Strategies;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace RPSLS.DotNetPlayer.API
|
||||
{
|
||||
|
@ -27,6 +24,7 @@ namespace RPSLS.DotNetPlayer.API
|
|||
{
|
||||
services.Configure<StrategySettings>(Configuration);
|
||||
services.Configure<PredictorSettings>(Configuration);
|
||||
services.AddApplicationInsightsTelemetry();
|
||||
services.AddControllers();
|
||||
services.AddHealthChecks();
|
||||
services.AddHttpClient("Predictor");
|
||||
|
|
|
@ -39,7 +39,12 @@
|
|||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>applicationinsights-spring-boot-starter</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
rpsls.player.pick.strategy=${PICK_STRATEGY:random}
|
||||
rpsls.predictor.pick.url=${PREDICTOR_URL:}
|
||||
rpsls.predictor.pick.url=${PREDICTOR_URL:}
|
||||
azure.application-insights.instrumentation-key=${APPLICATION_INSIGHTS_IKEY:00000000-0000-0000-0000-000000000000}
|
||||
azure.application-insights.enabled=${APPLICATION_INSIGHTS_ENABLED:false}
|
|
@ -1,7 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
require('dotenv').config();
|
||||
const appInsights = require("applicationinsights");
|
||||
|
||||
const routes = require('./api/routes/index.route');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
// Start application insights
|
||||
const applicationInsightsIK = process.env.APPLICATION_INSIGHTS_IKEY;
|
||||
if (applicationInsightsIK) {
|
||||
appInsights.setup(applicationInsightsIK).start();
|
||||
}
|
||||
|
||||
const app = express();
|
||||
|
||||
// Application-Level Middleware
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -11,6 +11,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"applicationinsights": "^1.6.0",
|
||||
"axios": "^0.19.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^8.1.0",
|
||||
|
|
|
@ -7,25 +7,29 @@ use Illuminate\Support\Facades\Log;
|
|||
use Illuminate\Support\Facades\Request;
|
||||
use App\Services\PickStrategyFactory;
|
||||
use App\Services\PredictorProxy;
|
||||
use App\Services\AppInsightsService;
|
||||
|
||||
class PickController extends BaseController
|
||||
{
|
||||
private $_context;
|
||||
private $_strategy;
|
||||
private $_predictor;
|
||||
private $_appinsights;
|
||||
|
||||
public function __construct(PickStrategyFactory $strategyFactory, PredictorProxy $predictorProxy)
|
||||
public function __construct(PickStrategyFactory $strategyFactory, PredictorProxy $predictorProxy, AppInsightsService $appinsights)
|
||||
{
|
||||
$this->_strategy = config('strategies.PICK_STRATEGY');
|
||||
$strategyFactory->setDefaultStrategy($this->_strategy);
|
||||
Log::info('Configured pick strategy with '.$this->_strategy);
|
||||
$this->_context = $strategyFactory->getStrategy();
|
||||
$this->_predictor = $predictorProxy;
|
||||
$this->_appinsights = $appinsights;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$username = Request::get('username', '');
|
||||
$this->_appinsights->trackRequest('PickController:index()', 'GET /pick', time());
|
||||
if($username != '') {
|
||||
try {
|
||||
$predicted = $this->_predictor->getPickPredicted($username);
|
||||
|
@ -33,6 +37,7 @@ class PickController extends BaseController
|
|||
return response()->json($predicted);
|
||||
} catch(\Throwable $e) {
|
||||
Log::error('Predictor had a problem');
|
||||
$this->_appinsights->trackException($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,5 +20,9 @@ class AppServiceProvider extends ServiceProvider
|
|||
$this->app->singleton(PredictorProxy::class, function ($app) {
|
||||
return new PredictorProxy();
|
||||
});
|
||||
|
||||
$this->app->singleton(AppInsightsService::class, function($app) {
|
||||
return new AppInsightsService();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class AppInsightsService
|
||||
{
|
||||
private $_telemetryClient;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$key = config('appinsights.APPLICATION_INSIGHTS_IKEY');
|
||||
$this->_telemetryClient = new \ApplicationInsights\Telemetry_Client();
|
||||
$context = $this->_telemetryClient->getContext();
|
||||
$context->setInstrumentationKey($key);
|
||||
}
|
||||
|
||||
public function trackRequest($name, $url, $startTime)
|
||||
{
|
||||
$this->_telemetryClient->trackRequest($name, $url, $startTime);
|
||||
$this->_telemetryClient->flush();
|
||||
}
|
||||
|
||||
public function trackException($ex)
|
||||
{
|
||||
$telemetryClient->trackException($ex);
|
||||
$telemetryClient->flush();
|
||||
}
|
||||
}
|
|
@ -93,6 +93,7 @@ $app->register(App\Providers\AppServiceProvider::class);
|
|||
|
||||
$app->configure('strategies');
|
||||
$app->configure('predictor');
|
||||
$app->configure('appinsights');
|
||||
|
||||
$app->router->group([
|
||||
'namespace' => 'App\Http\Controllers',
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
"type": "project",
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"guzzlehttp/guzzle": "^6.4",
|
||||
"laravel/lumen-framework": "^6.0"
|
||||
"guzzlehttp/guzzle": "~5.0",
|
||||
"laravel/lumen-framework": "^6.0",
|
||||
"microsoft/application-insights": "~0.2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "^1.4",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'APPLICATION_INSIGHTS_IKEY' => getenv('APPLICATION_INSIGHTS_IKEY') ?: env('APPLICATION_INSIGHTS_IKEY', ''),
|
||||
];
|
|
@ -1,17 +1,24 @@
|
|||
from flask import Flask, request
|
||||
from healthcheck import HealthCheck
|
||||
from applicationinsights.flask.ext import AppInsights
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from .pick import Picker
|
||||
|
||||
app = Flask(__name__)
|
||||
appinsightskey = os.getenv('APPLICATION_INSIGHTS_IKEY', '')
|
||||
if appinsightskey:
|
||||
app.config['APPINSIGHTS_INSTRUMENTATIONKEY'] = appinsightskey
|
||||
# log requests, traces and exceptions to the Application Insights service
|
||||
appinsights = AppInsights(app)
|
||||
|
||||
health = HealthCheck()
|
||||
|
||||
app.add_url_rule("/healthcheck", "healthcheck", view_func=lambda: health.run())
|
||||
app.add_url_rule('/pick', 'pick', view_func=Picker.as_view('picker'))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(threaded=True)
|
||||
else:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import random
|
||||
import socket
|
||||
from flask import jsonify
|
||||
|
||||
from .rpsls import RPSLS
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
gunicorn
|
||||
flask
|
||||
py-healthcheck
|
||||
py-healthcheck
|
||||
applicationinsights
|
|
@ -1,174 +1,180 @@
|
|||
@layout ConsoleLayout
|
||||
@page "/battle"
|
||||
@attribute [Authorize]
|
||||
|
||||
@inject IGameService Game
|
||||
@inject BattleHelper battleHelper
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
|
||||
<div class="console-screen">
|
||||
<p class="console-text">@Username</p>
|
||||
<div class="user-picks">
|
||||
<div class="user-picks-container">
|
||||
@if (battleToBeStarted)
|
||||
{
|
||||
<div class="pick active toplay">
|
||||
<div class="pick-images @(shakeHand ? "shakingHand" : string.Empty)">
|
||||
<img class="@(battleHelper.GetAnimatedClass(shakeHand)) placeholder" src="/assets/images/png/hands/user/rock.png">
|
||||
<img class="pick @(battleHelper.GetAnimatedClass(shakeHand))" src="/assets/images/png/hands/user/@(battleHelper.GetHandIcon(userHandToShow)).png">
|
||||
<img class="base" src="/assets/images/png/hands/user/base.png">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var pick in picks)
|
||||
{
|
||||
var active = pick.Value == Game.Pick;
|
||||
<div class="pick selectable @(active ? "active" : string.Empty)" @onclick="() => SelectPick(pick)">
|
||||
<div class="pick-images"><img src='@("/assets/images/png/hands/user/"+pick.Image+".png")'></div>
|
||||
<p class="pick-text">@pick.Text</p>
|
||||
</div>
|
||||
@if (pick.BreakAfter)
|
||||
{
|
||||
<div class="break"></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!battleToBeStarted)
|
||||
{
|
||||
<div class="cam-content">
|
||||
<div class="cam-button"><a class="cam-link" href="/cambattle">use webcam</a></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Game.Pick == -1 || battleToBeStarted)
|
||||
{
|
||||
<div class="console-vs">
|
||||
<div class="vs-circle">
|
||||
<p class="vs-text">VS</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="console-vs">
|
||||
<div class="start-battle-circle" @onclick="async () => await StartBattle()">
|
||||
<p class="start-battle-text">start</p>
|
||||
<p class="start-battle-text">battle</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="console-screen">
|
||||
<p class="console-text">@Game.Challenger?.DisplayName</p>
|
||||
<div class="challenger-picks">
|
||||
<div class="challenger-player challenger-picks-container">
|
||||
@if (battleToBeStarted)
|
||||
{
|
||||
<div style="transform: scaleX(-1)" class="pick active toplay">
|
||||
<div class="pick-images @(shakeHand ? "shakingHand" : string.Empty)">
|
||||
<img class="@(battleHelper.GetAnimatedClass(shakeHand)) placeholder" src="/assets/images/png/hands/@Game.Challenger.Name/0.png">
|
||||
@if (Game.GameResult != null)
|
||||
{
|
||||
<img class="pick @(battleHelper.GetAnimatedClass(shakeHand))" src="/assets/images/png/hands/@Game.Challenger.Name/@(battleHelper.GetHandIcon(Game.GameResult.ChallengerPick)).png">
|
||||
}
|
||||
<img class="base bot" src="/assets/images/png/hands/@Game.Challenger.Name/base.png">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (Game.Challenger != null)
|
||||
{
|
||||
<div class="challenger-image">
|
||||
<div class="challenger-spiral">
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<div class="challenger-circle challenger-circle-big"></div>
|
||||
<div class="challenger-circle challenger-circle-small"></div>
|
||||
</div>
|
||||
<img src='@("/assets/images/png/"+Game.Challenger.Name+".png")'>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter] ConsoleLayout Layout { get; set; }
|
||||
|
||||
string Username = "-";
|
||||
bool battleToBeStarted = false;
|
||||
string userHandToShow = "rock";
|
||||
bool shakeHand = false;
|
||||
RPSLSViewModel pickedToPlay;
|
||||
RPSLSViewModel[] picks;
|
||||
bool isTwitterUser = false;
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Layout.Initialize(title: "the battle", withHelp: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
if (Game.Challenger == null)
|
||||
{
|
||||
NavigationManager.NavigateTo("/challenger");
|
||||
}
|
||||
|
||||
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
Username = state.User.Identity.Name;
|
||||
isTwitterUser = state.User.Identity.AuthenticationType == "Twitter";
|
||||
|
||||
Game.Pick = -1;
|
||||
picks = new RPSLSViewModel[]
|
||||
{
|
||||
new RPSLSViewModel { Text = "Rock", Value = 0, Image = "rock" },
|
||||
new RPSLSViewModel { Text = "Paper", Value = 1, Image = "paper", BreakAfter = true },
|
||||
new RPSLSViewModel { Text = "Scissors", Value = 2, Image = "scissors" },
|
||||
new RPSLSViewModel { Text = "Lizard", Value = 3, Image = "lizard" },
|
||||
new RPSLSViewModel { Text = "Spock", Value = 4, Image = "spock", BreakAfter = true }
|
||||
};
|
||||
}
|
||||
|
||||
void SelectPick(RPSLSViewModel pick)
|
||||
{
|
||||
pickedToPlay = pick;
|
||||
Game.Pick = pick.Value;
|
||||
}
|
||||
|
||||
private async Task ShakeHand()
|
||||
{
|
||||
battleToBeStarted = true;
|
||||
shakeHand = true;
|
||||
|
||||
await Game.Play(Username, isTwitterUser);
|
||||
await Task.Delay(2000);
|
||||
shakeHand = false;
|
||||
userHandToShow = pickedToPlay.Image;
|
||||
StateHasChanged();
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
|
||||
async Task StartBattle()
|
||||
{
|
||||
await ShakeHand();
|
||||
NavigationManager.NavigateTo("/result");
|
||||
}
|
||||
}
|
||||
@layout ConsoleLayout
|
||||
@page "/battle"
|
||||
@attribute [Authorize]
|
||||
|
||||
@inject IGameService Game
|
||||
@inject BattleHelper battleHelper
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
|
||||
<div class="console-screen">
|
||||
<p class="console-text">@Username</p>
|
||||
<div class="user-picks">
|
||||
<div class="user-picks-container">
|
||||
@if (battleToBeStarted)
|
||||
{
|
||||
<div class="pick active toplay">
|
||||
<div class="pick-images @(shakeHand ? "shakingHand" : string.Empty)">
|
||||
<img class="@(battleHelper.GetAnimatedClass(shakeHand)) placeholder" src="/assets/images/png/hands/user/rock.png">
|
||||
<img class="pick @(battleHelper.GetAnimatedClass(shakeHand))" src="/assets/images/png/hands/user/@(battleHelper.GetHandIcon(userHandToShow)).png">
|
||||
<img class="base" src="/assets/images/png/hands/user/base.png">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var pick in picks)
|
||||
{
|
||||
var active = pick.Value == Game.Pick;
|
||||
<div class="pick selectable @(active ? "active" : string.Empty)" @onclick="() => SelectPick(pick)">
|
||||
<div class="pick-images"><img src='@("/assets/images/png/hands/user/"+pick.Image+".png")'></div>
|
||||
<p class="pick-text">@pick.Text</p>
|
||||
</div>
|
||||
@if (pick.BreakAfter)
|
||||
{
|
||||
<div class="break"></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!battleToBeStarted)
|
||||
{
|
||||
<div class="cam-content">
|
||||
<div class="cam-button"><a class="cam-link" href="/cambattle">use webcam</a></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Game.Pick == -1 || battleToBeStarted)
|
||||
{
|
||||
<div class="console-vs">
|
||||
<div class="vs-circle">
|
||||
<p class="vs-text">VS</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="console-vs">
|
||||
<div class="start-battle-circle" @onclick="async () => await StartBattle()">
|
||||
<p class="start-battle-text">start</p>
|
||||
<p class="start-battle-text">battle</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="console-screen">
|
||||
<p class="console-text">@Game.Challenger?.DisplayName</p>
|
||||
<div class="challenger-picks">
|
||||
<div class="challenger-player challenger-picks-container">
|
||||
@if (battleToBeStarted)
|
||||
{
|
||||
<div style="transform: scaleX(-1)" class="pick active toplay">
|
||||
<div class="pick-images @(shakeHand ? "shakingHand" : string.Empty)">
|
||||
<img class="@(battleHelper.GetAnimatedClass(shakeHand)) placeholder" src="/assets/images/png/hands/@Game.Challenger.Name/0.png">
|
||||
@if (Game.GameResult != null)
|
||||
{
|
||||
<img class="pick @(battleHelper.GetAnimatedClass(shakeHand))" src="/assets/images/png/hands/@Game.Challenger.Name/@(battleHelper.GetHandIcon(Game.GameResult.ChallengerPick)).png">
|
||||
}
|
||||
<img class="base bot" src="/assets/images/png/hands/@Game.Challenger.Name/base.png">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (Game.Challenger != null)
|
||||
{
|
||||
<div class="challenger-image">
|
||||
<div class="challenger-spiral">
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<div class="challenger-circle challenger-circle-big"></div>
|
||||
<div class="challenger-circle challenger-circle-small"></div>
|
||||
</div>
|
||||
<img src='@("/assets/images/png/"+Game.Challenger.Name+".png")'>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter] ConsoleLayout Layout { get; set; }
|
||||
|
||||
string Username = "-";
|
||||
bool battleToBeStarted = false;
|
||||
string userHandToShow = "rock";
|
||||
bool shakeHand = false;
|
||||
RPSLSViewModel pickedToPlay;
|
||||
RPSLSViewModel[] picks;
|
||||
bool isTwitterUser = false;
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Layout.Initialize(title: "the battle", withHelp: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
if (Game.Challenger == null)
|
||||
{
|
||||
NavigationManager.NavigateTo("/challenger");
|
||||
}
|
||||
|
||||
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
Username = state.User.Identity.Name;
|
||||
isTwitterUser = state.User.Identity.AuthenticationType == "Twitter";
|
||||
|
||||
Game.Pick = -1;
|
||||
picks = new RPSLSViewModel[]
|
||||
{
|
||||
new RPSLSViewModel { Text = "Rock", Value = 0, Image = "rock" },
|
||||
new RPSLSViewModel { Text = "Paper", Value = 1, Image = "paper", BreakAfter = true },
|
||||
new RPSLSViewModel { Text = "Scissors", Value = 2, Image = "scissors" },
|
||||
new RPSLSViewModel { Text = "Lizard", Value = 3, Image = "lizard" },
|
||||
new RPSLSViewModel { Text = "Spock", Value = 4, Image = "spock", BreakAfter = true }
|
||||
};
|
||||
}
|
||||
|
||||
void SelectPick(RPSLSViewModel pick)
|
||||
{
|
||||
pickedToPlay = pick;
|
||||
Game.Pick = pick.Value;
|
||||
}
|
||||
|
||||
private async Task ShakeHand()
|
||||
{
|
||||
battleToBeStarted = true;
|
||||
shakeHand = true;
|
||||
|
||||
await Game.Play(Username, isTwitterUser);
|
||||
if (!Game.GameResult.IsValid)
|
||||
{
|
||||
shakeHand = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
shakeHand = false;
|
||||
userHandToShow = pickedToPlay.Image;
|
||||
StateHasChanged();
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
|
||||
async Task StartBattle()
|
||||
{
|
||||
await ShakeHand();
|
||||
NavigationManager.NavigateTo("/result");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
@layout MainLayout
|
||||
@page "/"
|
||||
|
||||
<div class="index-content">
|
||||
<div class="login">
|
||||
<div class="login-section">
|
||||
<form action="/api/account/login" method="get" autocomplete="off">
|
||||
<img src='@("/assets/images/png/logo.png")'>
|
||||
<div class="subtitle-container">
|
||||
<h2 class="subtitle">Play the geek version of rock-paper-scissors.</h2>
|
||||
<h2 class="subtitle">Can you defeat our bot?</h2>
|
||||
</div>
|
||||
<input name="username" class="user" type="text" placeholder="Type Username" required />
|
||||
<div class="sign_twitter">or <a href="/api/account/login/twitter">Sign in with Twitter</a></div>
|
||||
<div class="custom-button">
|
||||
<button type="submit" class="custom-button-link">Enter Game</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="github">
|
||||
<a class="github-text" target="_blank" href="https://github.com/microsoft/RockPaperScissorsLizardSpock">Get the code from GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@layout MainLayout
|
||||
@inject IOptions<TwitterOptions> TwitterOptions
|
||||
@page "/"
|
||||
|
||||
<div class="index-content">
|
||||
<div class="login">
|
||||
<div class="login-section">
|
||||
<form action="/api/account/login" method="get" autocomplete="off">
|
||||
<img src='@("/assets/images/png/logo.png")'>
|
||||
<div class="subtitle-container">
|
||||
<h2 class="subtitle">Play the geek version of rock-paper-scissors.</h2>
|
||||
<h2 class="subtitle">Can you defeat our bot?</h2>
|
||||
</div>
|
||||
<input name="username" class="user" type="text" placeholder="Type Username" required />
|
||||
|
||||
<div class="sign_twitter">
|
||||
@if (!string.IsNullOrWhiteSpace(TwitterOptions?.Value?.ConsumerKey) && !string.IsNullOrWhiteSpace(TwitterOptions?.Value?.ConsumerSecret))
|
||||
{
|
||||
<span>or <a href="/api/account/login/twitter"> Sign in with Twitter</a></span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="custom-button">
|
||||
<button type="submit" class="custom-button-link">Enter Game</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github">
|
||||
<a class="github-text" target="_blank" href="https://github.com/microsoft/RockPaperScissorsLizardSpock">Get the code from GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CookiesBanner />
|
|
@ -1,172 +1,175 @@
|
|||
@layout ConsoleLayout
|
||||
@page "/result"
|
||||
@attribute [Authorize]
|
||||
|
||||
@inject IGameService Game
|
||||
@inject SvgHelper svgHelper
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<div class="result console-screen @resultStyle">
|
||||
<p class="console-text">@result.User</p>
|
||||
<div class="user-picks">
|
||||
<div class="user-picks-container">
|
||||
<div class="user-picks-items">
|
||||
<div class="user-hand">
|
||||
<img src='@("/assets/images/png/hands/user/"+MapPick(result.UserPick)+".png")'>
|
||||
</div>
|
||||
<p class="pick-text">@MapPick(result.UserPick)</p>
|
||||
|
||||
<p class="pick-text text-result">@resultMessage</p>
|
||||
@if (userIsWinner || userIsLoser)
|
||||
{
|
||||
<p class="pick-text text-message">@CreateMessage(result.UserPick, result.ChallengerPick)</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-button-container">
|
||||
<div class="custom-button">
|
||||
<a class="custom-button-link" href="/battle">play again</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="console-vs">
|
||||
<div class="vs-circle">
|
||||
<p class="vs-text">VS</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result console-screen">
|
||||
<p class="console-text">@Game.Challenger?.DisplayName</p>
|
||||
<div class="challenger-picks">
|
||||
<div class="challenger-result challenger-picks-container">
|
||||
@if (result.IsValid)
|
||||
{
|
||||
<div class="challenger-picks-items">
|
||||
<div class="opponent-hand">
|
||||
<img src='@("/assets/images/png/hands/" + result.Challenger + "/" + result.ChallengerPick + ".png")' />
|
||||
</div>
|
||||
<p class="pick-text">@MapPick(result.ChallengerPick)</p>
|
||||
</div>
|
||||
}
|
||||
else if (error != null)
|
||||
{
|
||||
<div class="opponent-hand-error">
|
||||
<img src='@("/assets/images/png/" + error.Image)'>
|
||||
<h2>@error.Text</h2>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-button-container">
|
||||
<div class="custom-button">
|
||||
<a class="custom-button-link challenger" href="/challenger">choose opponent</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
ResultDto result = new ResultDto();
|
||||
string resultMessage = null;
|
||||
bool userIsWinner = false;
|
||||
bool userIsLoser = false;
|
||||
string resultStyle = "";
|
||||
ChallengerErrorViewModel error = null;
|
||||
ChallengerErrorViewModel[] errors = new ChallengerErrorViewModel[]
|
||||
{
|
||||
new ChallengerErrorViewModel() { Name = "python", Text = "how did I get here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "node", Text = "oooops, I have to update the rocket", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "dotnet", Text = "how did I end here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "java", Text = "how did I end here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "php", Text = "how did I end here?", Image = "node-error.png" }
|
||||
};
|
||||
|
||||
[CascadingParameter] ConsoleLayout Layout { get; set; }
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Layout.Initialize(title: "results", withHelp: true, hasToShowGithub: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Game.GameResult == null)
|
||||
{
|
||||
NavigationManager.NavigateTo("/challenger");
|
||||
return;
|
||||
}
|
||||
result.UserPick = Game.Pick;
|
||||
result = Game.GameResult;
|
||||
|
||||
resultMessage = CreateResult(result.Result);
|
||||
|
||||
userIsWinner = result.Result == 1;
|
||||
userIsLoser = result.Result == 2;
|
||||
|
||||
resultStyle = GetResultClass(result.Result);
|
||||
error = errors.FirstOrDefault(c => c.Name == result.Challenger);
|
||||
}
|
||||
|
||||
private string GetResultClass(int result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
1 => "winner",
|
||||
2 => "loser",
|
||||
_ => ""
|
||||
};
|
||||
}
|
||||
|
||||
private string CreateResult(int result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
1 => "you win!!",
|
||||
2 => "you lose!!",
|
||||
_ => "tie!!"
|
||||
};
|
||||
}
|
||||
|
||||
private static string MapPick(int pick)
|
||||
{
|
||||
return pick switch
|
||||
{
|
||||
0 => "rock",
|
||||
1 => "paper",
|
||||
2 => "scissors",
|
||||
3 => "lizard",
|
||||
4 => "spock",
|
||||
_ => "-"
|
||||
};
|
||||
}
|
||||
|
||||
private static string MapAction(int winner, int loser)
|
||||
{
|
||||
return winner switch
|
||||
{
|
||||
0 => "crushes",
|
||||
1 => loser == 0 ? "covers" : "disproves",
|
||||
2 => loser == 1 ? "cuts" : "decapitates",
|
||||
3 => loser == 1 ? "eats" : "poisons",
|
||||
4 => loser == 0 ? "vaporizes" : "smashes",
|
||||
_ => "ties"
|
||||
};
|
||||
}
|
||||
|
||||
private string CreateMessage(int userPick, int challengerPick)
|
||||
{
|
||||
var userPickText = MapPick(userPick);
|
||||
var challengerPickText = MapPick(challengerPick);
|
||||
|
||||
if(userIsWinner)
|
||||
{
|
||||
return $"{userPickText} {MapAction(result.UserPick, result.ChallengerPick)} {challengerPickText}";
|
||||
}
|
||||
|
||||
return $"{challengerPickText} {MapAction(result.ChallengerPick, result.UserPick)} {userPickText}";
|
||||
}
|
||||
}
|
||||
@layout ConsoleLayout
|
||||
@page "/result"
|
||||
@attribute [Authorize]
|
||||
|
||||
@inject IGameService Game
|
||||
@inject SvgHelper svgHelper
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<div class="result console-screen @resultStyle">
|
||||
<p class="console-text">@result.User</p>
|
||||
<div class="user-picks">
|
||||
<div class="user-picks-container">
|
||||
<div class="user-picks-items">
|
||||
<div class="user-hand">
|
||||
<img src='@("/assets/images/png/hands/user/"+MapPick(result.UserPick)+".png")'>
|
||||
</div>
|
||||
<p class="pick-text">@MapPick(result.UserPick)</p>
|
||||
|
||||
<p class="pick-text text-result">@resultMessage</p>
|
||||
@if (userIsWinner || userIsLoser)
|
||||
{
|
||||
<p class="pick-text text-message">@CreateMessage(result.UserPick, result.ChallengerPick)</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-button-container">
|
||||
<div class="custom-button">
|
||||
<a class="custom-button-link" href="/battle">play again</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="console-vs">
|
||||
<div class="vs-circle">
|
||||
<p class="vs-text">VS</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result console-screen">
|
||||
<p class="console-text">@Game.Challenger?.DisplayName</p>
|
||||
<div class="challenger-picks">
|
||||
<div class="challenger-result challenger-picks-container">
|
||||
@if (result.IsValid)
|
||||
{
|
||||
<div class="challenger-picks-items">
|
||||
<div class="opponent-hand">
|
||||
<img src='@("/assets/images/png/hands/" + result.Challenger + "/" + result.ChallengerPick + ".png")' />
|
||||
</div>
|
||||
<p class="pick-text">@MapPick(result.ChallengerPick)</p>
|
||||
</div>
|
||||
}
|
||||
else if (error != null)
|
||||
{
|
||||
<div class="opponent-hand-error">
|
||||
<img src='@("/assets/images/png/" + error.Image)'>
|
||||
<h2>@error.Text</h2>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-button-container">
|
||||
<div class="custom-button">
|
||||
<a class="custom-button-link challenger" href="/challenger">choose opponent</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
ResultDto result = new ResultDto();
|
||||
string resultMessage = string.Empty;
|
||||
bool userIsWinner = false;
|
||||
bool userIsLoser = false;
|
||||
string resultStyle = "";
|
||||
ChallengerErrorViewModel error = null;
|
||||
ChallengerErrorViewModel[] errors = new ChallengerErrorViewModel[]
|
||||
{
|
||||
new ChallengerErrorViewModel() { Name = "python", Text = "how did I get here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "node", Text = "oooops, I have to update the rocket", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "dotnet", Text = "how did I end here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "java", Text = "how did I end here?", Image = "node-error.png" },
|
||||
new ChallengerErrorViewModel() { Name = "php", Text = "how did I end here?", Image = "node-error.png" }
|
||||
};
|
||||
|
||||
[CascadingParameter] ConsoleLayout Layout { get; set; }
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
Layout.Initialize(title: "results", withHelp: true, hasToShowGithub: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Game.GameResult == null)
|
||||
{
|
||||
NavigationManager.NavigateTo("/challenger");
|
||||
return;
|
||||
}
|
||||
|
||||
result.UserPick = Game.Pick;
|
||||
result = Game.GameResult;
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
resultMessage = CreateResult(result.Result);
|
||||
userIsWinner = result.Result == 1;
|
||||
userIsLoser = result.Result == 2;
|
||||
}
|
||||
|
||||
resultStyle = GetResultClass(result.Result);
|
||||
error = errors.FirstOrDefault(c => c.Name == result.Challenger);
|
||||
}
|
||||
|
||||
private string GetResultClass(int result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
1 => "winner",
|
||||
2 => "loser",
|
||||
_ => ""
|
||||
};
|
||||
}
|
||||
|
||||
private string CreateResult(int result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
1 => "you win!!",
|
||||
2 => "you lose!!",
|
||||
_ => "tie!!"
|
||||
};
|
||||
}
|
||||
|
||||
private static string MapPick(int pick)
|
||||
{
|
||||
return pick switch
|
||||
{
|
||||
0 => "rock",
|
||||
1 => "paper",
|
||||
2 => "scissors",
|
||||
3 => "lizard",
|
||||
4 => "spock",
|
||||
_ => "-"
|
||||
};
|
||||
}
|
||||
|
||||
private static string MapAction(int winner, int loser)
|
||||
{
|
||||
return winner switch
|
||||
{
|
||||
0 => "crushes",
|
||||
1 => loser == 0 ? "covers" : "disproves",
|
||||
2 => loser == 1 ? "cuts" : "decapitates",
|
||||
3 => loser == 1 ? "eats" : "poisons",
|
||||
4 => loser == 0 ? "vaporizes" : "smashes",
|
||||
_ => "ties"
|
||||
};
|
||||
}
|
||||
|
||||
private string CreateMessage(int userPick, int challengerPick)
|
||||
{
|
||||
var userPickText = MapPick(userPick);
|
||||
var challengerPickText = MapPick(challengerPick);
|
||||
|
||||
if (userIsWinner)
|
||||
{
|
||||
return $"{userPickText} {MapAction(result.UserPick, result.ChallengerPick)} {challengerPickText}";
|
||||
}
|
||||
|
||||
return $"{challengerPickText} {MapAction(result.ChallengerPick, result.UserPick)} {userPickText}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Microsoft.AspNetCore.Authentication.Twitter;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
@ -37,6 +38,7 @@ namespace RPSLS.Web
|
|||
services.AddOptions();
|
||||
services.Configure<RecognitionSettings>(Configuration);
|
||||
services.Configure<GoogleAnalyticsSettings>(Configuration);
|
||||
services.Configure<TwitterOptions>(Configuration.GetSection("Authentication:Twitter"));
|
||||
services.Configure<GameManagerSettings>(Configuration.GetSection("GameManager"));
|
||||
if (Configuration.GetValue<bool>("GameManager:Grpc:GrpcOverHttp", false))
|
||||
{
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
@using System.Net.Http
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.JSInterop
|
||||
@using RPSLS.Web
|
||||
@using RPSLS.Web.Shared
|
||||
@using RPSLS.Web.Services
|
||||
@using RPSLS.Web.Models
|
||||
@using RPSLS.Web.Helpers
|
||||
@using System.Net.Http
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Authentication.Twitter
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.Extensions.Options
|
||||
@using Microsoft.JSInterop
|
||||
@using RPSLS.Web
|
||||
@using RPSLS.Web.Shared
|
||||
@using RPSLS.Web.Services
|
||||
@using RPSLS.Web.Models
|
||||
@using RPSLS.Web.Helpers
|
||||
@using RPSLS.Web.Clients
|
|
@ -15,7 +15,7 @@
|
|||
flex-direction: column;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 250px);
|
||||
min-height: calc(100vh - 199px);
|
||||
background-image: url(../assets/images/svg/screens_panel.svg),
|
||||
url(../assets/images/svg/platform_opponents.svg),
|
||||
linear-gradient(
|
||||
|
|
|
@ -57,7 +57,7 @@ body {
|
|||
|
||||
/* Footer */
|
||||
.footer {
|
||||
height: 3.3rem;
|
||||
height: 47px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
|
@ -1,142 +1,142 @@
|
|||
/* Title */
|
||||
.game-content-title {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding-top: 2.8rem;
|
||||
margin-bottom: 2.7rem;
|
||||
height: 75px;
|
||||
font-family: "Voyager";
|
||||
font-weight: 300;
|
||||
font-size: 3.75rem;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.9);
|
||||
line-height: 3.75rem;
|
||||
color: #ffaa44;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding-top: 40px;
|
||||
margin-bottom: 37px;
|
||||
height: 75px;
|
||||
font-family: "Voyager";
|
||||
font-weight: 300;
|
||||
font-size: 75px;
|
||||
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.9);
|
||||
line-height: 75px;
|
||||
color: #ffaa44;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
height: 75px;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
height: 75px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Modal Layout */
|
||||
.modal {
|
||||
position: fixed;
|
||||
display: block;
|
||||
z-index: 1;
|
||||
padding-top: 9rem;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
position: fixed;
|
||||
display: block;
|
||||
z-index: 1;
|
||||
padding-top: 9rem;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.close:before,
|
||||
.close:after {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
content: " ";
|
||||
height: 33px;
|
||||
width: 2px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
.close:before,
|
||||
.close:after {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
content: " ";
|
||||
height: 33px;
|
||||
width: 2px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.close:before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.close:before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.close:after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
.close:after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
background-color: #372369;
|
||||
margin: auto;
|
||||
max-width: 630px;
|
||||
position: relative;
|
||||
background-color: #372369;
|
||||
margin: auto;
|
||||
max-width: 630px;
|
||||
}
|
||||
|
||||
.modal-content > div {
|
||||
margin: 1.25rem;
|
||||
}
|
||||
.modal-content > div {
|
||||
margin: 1.25rem;
|
||||
}
|
||||
|
||||
.relationship-diagram {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.relationship-diagram h1 {
|
||||
margin-bottom: 1.25rem;
|
||||
color: #ffffff;
|
||||
font-family: Voyager;
|
||||
font-size: 32px;
|
||||
margin: 2rem 0;
|
||||
font-weight: 300;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.relationship-diagram img {
|
||||
max-width: 430px;
|
||||
margin-bottom: 100px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.info-icon {
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.game-content-title {
|
||||
font-size: 2rem;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.relationship-diagram h1 {
|
||||
margin-bottom: 1.25rem;
|
||||
color: #ffffff;
|
||||
font-family: Voyager;
|
||||
font-size: 32px;
|
||||
margin: 2rem 0;
|
||||
font-weight: 300;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.relationship-diagram img {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
}
|
||||
.relationship-diagram img {
|
||||
max-width: 430px;
|
||||
margin-bottom: 100px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.info-icon {
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.game-content-title {
|
||||
font-size: 2rem;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.relationship-diagram img {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.mainTitle p.header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 3.1rem;
|
||||
text-align: center;
|
||||
margin: 0 2rem;
|
||||
}
|
||||
.mainTitle p.header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 3.1rem;
|
||||
text-align: center;
|
||||
margin: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,37 +5,44 @@ services:
|
|||
ports:
|
||||
- "5003:5000"
|
||||
environment:
|
||||
- PICK_STRATEGY=scissors
|
||||
- APPLICATION_INSIGHTS_IKEY=
|
||||
- PICK_STRATEGY=random
|
||||
- PREDICTOR_URL=
|
||||
node.player:
|
||||
ports:
|
||||
- "5002:3000"
|
||||
environment:
|
||||
- PICK_STRATEGY=paper
|
||||
- APPLICATION_INSIGHTS_IKEY=
|
||||
- PICK_STRATEGY=random
|
||||
- PREDICTOR_URL=
|
||||
dotnet.player:
|
||||
ports:
|
||||
- "5001:80"
|
||||
environment:
|
||||
- PICK_STRATEGY=rock
|
||||
- APPINSIGHTS_INSTRUMENTATIONKEY=
|
||||
- PICK_STRATEGY=random
|
||||
- PREDICTOR_Url=
|
||||
php.player:
|
||||
ports:
|
||||
- "5004:80"
|
||||
environment:
|
||||
- PICK_STRATEGY=lizard
|
||||
- APPLICATION_INSIGHTS_IKEY=
|
||||
- PICK_STRATEGY=random
|
||||
- PREDICTOR_URL=
|
||||
java.player:
|
||||
ports:
|
||||
- "5005:8080"
|
||||
environment:
|
||||
- PICK_STRATEGY=spock
|
||||
- APPLICATION_INSIGHTS_IKEY=
|
||||
- APPLICATION_INSIGHTS_ENABLED=false
|
||||
- PICK_STRATEGY=random
|
||||
- PREDICTOR_URL=
|
||||
game.api:
|
||||
ports:
|
||||
- "5101:80"
|
||||
- "5102:81"
|
||||
environment:
|
||||
- APPINSIGHTS_INSTRUMENTATIONKEY=
|
||||
- GRPC_PORT=81
|
||||
- Challengers__0__Url=http://dotnet.player/
|
||||
- Challengers__1__Url=http://node.player:3000/
|
||||
|
@ -46,6 +53,7 @@ services:
|
|||
ports:
|
||||
- "5100:80"
|
||||
environment:
|
||||
- APPINSIGHTS_INSTRUMENTATIONKEY=
|
||||
- GameManager__Url=http://game.api:81/
|
||||
- GameManager__Grpc__GrpcOverHttp=true
|
||||
- Authentication__Twitter__ConsumerKey=
|
||||
|
@ -56,6 +64,7 @@ services:
|
|||
ports:
|
||||
- "5110:80"
|
||||
environment:
|
||||
- APPINSIGHTS_INSTRUMENTATIONKEY=
|
||||
- UploadPath=model
|
||||
volumes:
|
||||
- model-data:/app/model
|
||||
|
|
Загрузка…
Ссылка в новой задаче