Skip to content

Commit

Permalink
1.13.0 - Resolving ties (#233)
Browse files Browse the repository at this point in the history
* disabling rbac on the kv as an access policy

* trying out creating a KV with access policies only

* retrying creating the kv entries

* fixing unit tests and editorconfig

* adding code to retrieve key vault config in the search api

* fixing integration tests

* fixing a typo

* fixing queue's name kv value

* fixing insufficient premissions for KV

* creating new cache queue function

* Setting up bare bones for the cache queue worker function

* adding console logs

* progress on processing the board games to cache

* parsing board game XML details

* Moving re-usable models into the Core project and using XML serialization attributes for the search results

* adding mongo service to the update cache function

* adding mongo upsert

* mapping bgg details to domain model

* adding logging

* progress on getting game prices

* adding prices to the board game models

* updating log level on errors logge and ensuring the prices doesn't throw exceptions

* returning prices from the search API endpoint

* refactoring test projects

* adding board game oracle unit tests

* renaming and moving consts

* adding update board game cache function tests

* updating function infra and cache function build and deploy pipeline

* updating build pipeline

* update build pipeline reference

* fixing project name

* updating directory name for functions project

* fixing yaml

* updating func app name

* adding download step

* fixing syntax

* another try at fixing pipeline

* another try

* final fix

* now?

* path fix

* app settings fix

* adding unit tests for updating cache

* adding test step

* excluding models from the code coverage

* adding more tests

* fixing incorrect path

* excluding all UnitTests

* adding more exclusions to test coverage

* fixing broken docker image

* trying out different path

* another pipeliune test

* fixing condition

* small changes

* adding prices model to the app

* progress on showing prices in the app

* progress on showing prices in the search results

* adding azure arch uml file

* adding arch diagrams

* updating sequence diagram

* updating prices display styles on the search games screen

* adding mocks for the home view model

* adding first home view model test

* begnning of reordering of scores

* TODO

* adding sorting of the player scores

* attempt at moving scores around correcty

* Adding shimmer load to the hot board games instead of a spinner

* completing the logic to reorder player scores

* adding tiebreaker models and test

* introducing visual states and refactoring of the code

* adding readme file about purging application insights

* updating readme

* fixing an issue with player's missing name when importing plays

* fixing an issue with importing collection removing existing settings and prices from the saved board game

* fixing imports

* trying out docker hub as image registry

* adding login step to be more explicit

* updating repo

* updating image registry to docker hub

* fixing incorrect script execution

* updating prod step to use docker hub

* removing quotes

* using the correct service connection for prod

* removing acr from the shared infra

* disabling app performance counters for application insights

* detecting tied scores

* tie breaker instruction and showing place

* adding animations package and handling tie states

* ensuring the places are reflected when reording

* fixing ordering and refreshing on the screen

* updating tiebrekears logic

* fixing host.json

* showing all of the historical playthroughs, even if a game is no longer in the collection

* removing line of code that causes high storate transaction numbers and increases cost

* adding a comment

* downgrading storage to v1

* disabling AzureWebJobsDashboard

* using runFromPackage deployment

* updating function packages to latest

* excluding date time service from coverage

* adding test to bump up the coverage

* adding missed attribution

* limiting the amount of analytics captured by AI

* tie breaker work continuation

* adding a TODO

* handling shared place logic

* updating last winner logic

* fixing issue with scores not updating after state change

* showing multiple winners on the plays history page

* fixing top 5 scores

* removing unnecessary code

* fixing logging a game in the past
  • Loading branch information
mkieres authored Oct 22, 2023
1 parent 9b9695c commit 499d0d2
Show file tree
Hide file tree
Showing 82 changed files with 3,271 additions and 576 deletions.
5 changes: 4 additions & 1 deletion backend/BGC.Core/Services/DateTimeService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using BGC.Core.Services.Interfaces;
using System.Diagnostics.CodeAnalysis;

using BGC.Core.Services.Interfaces;

namespace BGC.Core.Services
{
[ExcludeFromCodeCoverage(Justification = "There's not really much to test here")]
public class DateTimeService : IDateTimeService
{
public DateTimeOffset UtcOffsetNow => DateTimeOffset.UtcNow;
Expand Down
12 changes: 6 additions & 6 deletions backend/BGC.Functions/BGC.Functions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.19.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.13.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.14.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.12.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.14.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.15.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.13.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.11" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="7.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.21.0" />
<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="MongoDB.Driver" Version="2.22.0" />
<PackageReference Include="Polly" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BGC.Core\BGC.Core.csproj" />
Expand Down
8 changes: 8 additions & 0 deletions backend/BGC.Functions/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@
var host = new HostBuilder()
.ConfigureAppConfiguration((hostingContext, configBuilder) =>
{
// Added conditional code because there seems to be an issue with Azure Functions / Azure Storage
// where a lot of sotrage transactions is being made for no good reason, which increases the cost of subscription.
// The below are links with some information / explanation of the problem

// https://stackoverflow.com/questions/60114152/inexplicable-storage-transactions-from-azure-functions
// https://stackoverflow.com/a/60484059/510627
#if DEBUG
configBuilder.AddJsonFile("local.settings.json", optional: true, reloadOnChange: false);
#endif
})
.ConfigureFunctionsWorkerDefaults(builder => { }, options =>
{
Expand Down
3 changes: 2 additions & 1 deletion backend/BGC.Functions/host.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
"Host.Aggregator": "Information"
},
"applicationInsights": {
"enablePerformanceCountersCollection": false,
"samplingSettings": {
"isEnabled": true,
"maxTelemetryItemsPerSecond": 10
"maxTelemetryItemsPerSecond": "10"
}
},
"aggregator": {
Expand Down
8 changes: 6 additions & 2 deletions backend/tests/BGC.Core.UnitTests/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[*.cs]
[*.{cs,vb}]

# StyleCop

Expand All @@ -7,4 +7,8 @@ dotnet_diagnostic.SA1600.severity = none

# No need for a file header
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md
dotnet_diagnostic.SA1633.severity = none
dotnet_diagnostic.SA1633.severity = none

# We do want to use underscores in the names of the test cases
# https://learn.microsoft.com/en-nz/dotnet/fundamentals/code-analysis/quality-rules/ca1707
dotnet_diagnostic.CA1707.severity = none
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using BGC.Core.Extensions;
using BGC.Core.Models.Dtos.BoardGameOracle;

namespace BGC.Core.UnitTests.Extensions
{
public class RegionDtoExtensionsTests
{
[Theory]
[InlineData(RegionDto.Australia, "au")]
[InlineData(RegionDto.Canada, "ca")]
[InlineData(RegionDto.UnitedStates, "us")]
[InlineData(RegionDto.UnitedKingdom, "gb")]
[InlineData(RegionDto.NewZealand, "nz")]
public void ToAbbreviation_RegionDto_ConvertsToExpectedValue(RegionDto regionDto, string expectedAbbreviation)
{
var abbreviation = regionDto.ToAbbreviation();

abbreviation.Should().Be(expectedAbbreviation);
}
}
}
7 changes: 7 additions & 0 deletions board_games_companion/lib/common/app_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ class AppText {
static const editPlaythroughPageUnsavedChangesDialogContent =
'Are you sure you want to navigate away?';
static const editPlaythroughPageUnsavedChangesActionButtonText = 'Navigate away';
static const editPlaythroughPageCannotReorderNotTiedScore =
"Sorry, you can't reorder not tied score";
static const editPlaythroughPageCannotReorderSharedPlaceScore =
"Sorry, you can't reorder shared place score";
static const editPlaythroughPageTieBreakerInstruction =
'Resolve ties by dragging up or down tied player score(s) '
'or check the box if players share the place';

static const playthroughNotePageTitle = 'Game Note';
static const playthroughNotePageAddNoteButtonText = 'Add note';
Expand Down
1 change: 1 addition & 0 deletions board_games_companion/lib/common/dimensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Dimensions {

static const double smallButtonIconSize = 16;
static const double defaultButtonIconSize = 20;
static const double largeIconSize = 36;
static const double defaultCheckboxSize = 24;

static const double floatingActionButtonBottomSpacing = 72;
Expand Down
2 changes: 2 additions & 0 deletions board_games_companion/lib/common/hive_boxes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ class HiveBoxes {
static const noScoreGameResultTypeId = 21;
static const cooperativeGameResultTypeId = 22;
static const boardGamePrices = 23;
static const scoreGameResultTypeId = 24;
static const scoreTiebreakerTypeId = 25;
}
24 changes: 21 additions & 3 deletions board_games_companion/lib/extensions/player_score_extensions.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import 'package:board_games_companion/models/hive/score.dart';
import 'package:board_games_companion/models/hive/score_game_results.dart';

import '../common/constants.dart';
import '../common/enums/game_family.dart';
import '../extensions/scores_extensions.dart';
import '../models/player_score.dart';

extension PlayerScoresExtesions on List<PlayerScore> {
List<PlayerScore> sortByScore(GameFamily gameFamily) {
/// Orders [PlayerScore] based on place or score.
///
/// The sorting logic checks if the [ScoreGameResult] is present and if it is then it compares [ScoreGameResult.place].
/// If [ScoreGameResult.place] hasn't been yet defined for a score then it compares the [ScoreGameResult.points].
///
/// For older records, that don't have [ScoreGameResult] the comparison is done based on the [Score.value]
///
/// Use [ignorePlaces] parameter to disregard current placement in [ScoreGameResult.place] and force the reorder based on the scores.
List<PlayerScore> sortByScore(
GameFamily gameFamily, {
bool ignorePlaces = false,
}) {
return this
..sort((PlayerScore playerScore, PlayerScore otherPlayerScore) {
return compareScores(playerScore.score, otherPlayerScore.score, gameFamily);
return compareScores(
playerScore.score,
otherPlayerScore.score,
gameFamily,
ignorePlaces: ignorePlaces,
);
});
}

Expand Down
9 changes: 9 additions & 0 deletions board_games_companion/lib/extensions/route_extensions.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:board_games_companion/pages/enter_score/enter_score_dialog.dart';
import 'package:flutter/widgets.dart';

import '../pages/about/about_page.dart';
Expand All @@ -7,6 +8,8 @@ import '../pages/edit_playthrough/edit_playthrough_page.dart';
import '../pages/edit_playthrough/playthrough_note_page.dart';
import '../pages/home/home_page.dart';
import '../pages/player/player_page.dart';
import '../pages/plays/game_spinner_game_selected_dialog.dart';
import '../pages/playthroughs/bgg_plays_import_report_dialog.dart';
import '../pages/playthroughs/playthrough_migration_page.dart';
import '../pages/playthroughs/playthrough_players_selection_page.dart';
import '../pages/playthroughs/playthroughs_page.dart';
Expand Down Expand Up @@ -37,6 +40,12 @@ extension RouteExtensions on Route {
return 'Playthrough Migration';
case PlahtyroughPlayersSelectionPage.pageRoute:
return 'Playthrough Player Selection';
case EnterScoreDialog.pageRoute:
return 'Enter Score';
case GameSpinnerGameSelectedDialog.pageRoute:
return 'Game Spinner Selected Game';
case BggPlaysImportReportDialog.pageRoute:
return 'BGG Plays Import';
default:
return settings.name ?? 'Undefined';
}
Expand Down
61 changes: 27 additions & 34 deletions board_games_companion/lib/extensions/scores_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,26 @@ extension ScoreExtesions on Score {
}
}

extension ScoresExtesions on List<Score>? {
List<Score> onlyScoresWithValue() {
return this
?.where((s) => (s.value?.isNotEmpty ?? false) && num.tryParse(s.value!) != null)
.toList() ??
<Score>[];
}
extension ScoresExtesions on Iterable<Score>? {
List<Score> onlyScoresWithValue() => this?.where((s) => s.hasScore).toList() ?? <Score>[];

List<Score> winners() =>
this?.onlyScoresWithValue().where((s) => s.isWinner).toList() ?? <Score>[];

List<Score> onlyCooperativeGames() {
return this?.where((s) => s.noScoreGameResult?.cooperativeGameResult != null).toList() ??
<Score>[];
}

List<Score>? sortByScore(GameFamily gameFamily) {
return this
List<Score>? sortByScore(GameFamily gameFamily, {bool ignorePlaces = false}) {
return this?.toList()
?..sort((Score score, Score otherScore) {
return compareScores(score, otherScore, gameFamily);
return compareScores(score, otherScore, gameFamily, ignorePlaces: ignorePlaces);
});
}

num? toBestScore(GameFamily gameFamily) {
final scores = this
?.where((Score score) => score.value != null && num.tryParse(score.value!) != null)
.map((Score score) => num.parse(score.value!)) ??
[];
final scores = this?.onlyScoresWithValue().map((Score score) => score.score!) ?? [];
switch (gameFamily) {
case GameFamily.HighestScore:
return scores.reduce(max);
Expand All @@ -49,10 +44,7 @@ extension ScoresExtesions on List<Score>? {
}

double toAverageScore() {
final scores = this
?.where((Score score) => score.value != null && num.tryParse(score.value!) != null)
.map((Score score) => num.parse(score.value!)) ??
[];
final scores = this?.onlyScoresWithValue().map((Score score) => score.score!) ?? [];

return scores.reduce((a, b) => a + b) / scores.length;
}
Expand All @@ -72,7 +64,12 @@ extension ScoresExtesions on List<Score>? {
0;
}

int compareScores(Score score, Score otherScore, GameFamily gameFamily) {
int compareScores(
Score score,
Score otherScore,
GameFamily gameFamily, {
bool ignorePlaces = false,
}) {
switch (gameFamily) {
case GameFamily.LowestScore:
// MK Swap scores around
Expand All @@ -87,31 +84,27 @@ int compareScores(Score score, Score otherScore, GameFamily gameFamily) {
break;
}

if (score.value == null && otherScore.value == null) {
return Constants.leaveAsIs;
}

if (score.value == null) {
return Constants.moveBelow;
}
return _compareScores(score, otherScore, ignorePlaces);
}

if (otherScore.value == null) {
return Constants.moveAbove;
int _compareScores(Score score, Score otherScore, [bool ignorePlaces = false]) {
if (!ignorePlaces &&
score.scoreGameResult?.place != null &&
otherScore.scoreGameResult?.place != null) {
return score.scoreGameResult!.place!.compareTo(otherScore.scoreGameResult!.place!);
}

final num? aNumber = num.tryParse(score.value!);
final num? bNumber = num.tryParse(otherScore.value!);
if (aNumber == null && bNumber == null) {
if (!score.hasScore && !otherScore.hasScore) {
return Constants.leaveAsIs;
}

if (aNumber == null) {
if (!score.hasScore) {
return Constants.moveBelow;
}

if (bNumber == null) {
if (!otherScore.hasScore) {
return Constants.moveAbove;
}

return bNumber.compareTo(aNumber);
return otherScore.score!.compareTo(score.score!);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ extension XmlElementExtensions on XmlElement? {
return null;
}

return firstOrDefaultAttributeWhere(
(attr) {
return attr.name.local == attributeName;
},
)?.value;
return firstOrDefaultAttributeWhere((attr) => attr.name.local == attributeName)?.value;
}
}
3 changes: 3 additions & 0 deletions board_games_companion/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import 'models/hive/player.dart';
import 'models/hive/playthrough.dart';
import 'models/hive/playthrough_note.dart';
import 'models/hive/score.dart';
import 'models/hive/score_game_results.dart';
import 'models/hive/search_history_entry.dart';
import 'models/hive/user.dart';
import 'models/sort_by.dart';
Expand Down Expand Up @@ -71,6 +72,8 @@ Future<void> main() async {
..registerAdapter(PlaythroughNoteAdapter())
..registerAdapter(SearchHistoryEntryAdapter())
..registerAdapter(NoScoreGameResultAdapter())
..registerAdapter(ScoreGameResultAdapter())
..registerAdapter(ScoreTiebreakerTypeAdapter())
..registerAdapter(CooperativeGameResultAdapter())
..registerAdapter(BoardGamePricesAdapter());

Expand Down
1 change: 1 addition & 0 deletions board_games_companion/lib/mixins/enter_score_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mixin EnterScoreDialogMixin {
Future<void> showEnterScoreDialog(BuildContext context, EnterScoreViewModel viewModel) async {
await showGeneralDialog<void>(
context: context,
routeSettings: const RouteSettings(name: EnterScoreDialog.pageRoute),
pageBuilder: (_, __, ___) {
return EnterScoreDialog(viewModel: viewModel);
},
Expand Down
4 changes: 2 additions & 2 deletions board_games_companion/lib/models/board_game_statistics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ class ScoreBoardGameStatistics with _$ScoreBoardGameStatistics {
required int totalPlaytimeInSeconds,
required int averagePlaytimeInSeconds,
required int averageScorePrecision,
PlayerScore? lastWinner,
List<PlayerScore>? lastGameWinners,
num? bestScore,
double? averageScore,
List<Tuple2<Player, String>>? topScoreres,
List<Tuple2<Player, double>>? topScoreres,
@Default(<PlayerStatistics>[]) List<PlayerStatistics> playersStatistics,
@Default(<PlayerCountStatistics>[]) List<PlayerCountStatistics> playerCountPercentage,
@Default(<PlayerWinsStatistics>[]) List<PlayerWinsStatistics> playerWinsPercentage,
Expand Down
Loading

0 comments on commit 499d0d2

Please sign in to comment.