Skip to content

Commit

Permalink
1.14.0 - Overall stats (#259)
Browse files Browse the repository at this point in the history
* first cut of the most played section

* updating the most played games and totals

* fixing incorrect logic to select history tab everytime

* fixing an issue selected sort order not being highlighted

* progress on getting setting up period for stats

* adding dockerfile for cache worker

* updating docker and readme file

* getting docker to run

* adding app settings to docker ignore

* updating android's icon to support adaptive and themed icons

* making the icon a tad smaller

* fixing a problem with ties UI not updating when scores are no longer tied

* adding basic overall stats

* Fixing stats material and font awesome icon spacings

* handling preset period change to show stats

* Hooking up selecting custom dates
  • Loading branch information
mkieres authored Apr 28, 2024
1 parent b68840c commit 81cd9b5
Show file tree
Hide file tree
Showing 62 changed files with 2,990 additions and 488 deletions.
2 changes: 2 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/appsettings.json
**/appsettings.*.json
LICENSE
README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-BGC.UpdateBoardGameCacheWorker-6fbee60d-5a99-4c79-9457-147dcaa4e3ef</UserSecretsId>
<PublishSingleFile>true</PublishSingleFile>
<OutputType>Exe</OutputType>
<UserSecretsId>dotnet-BGC.UpdateBoardGameCacheWorker-6fbee60d-5a99-4c79-9457-147dcaa4e3ef</UserSecretsId>
<DockerTargetOS>Linux</DockerTargetOS>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..</DockerfileContext>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.13.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.13" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="Polly" Version="8.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{
{
"profiles": {
"BGC.UpdateBoardGameCacheWorker": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
},
"dotnetRunMessages": true
},
"Docker": {
"commandName": "Docker"
}
}
}
}
21 changes: 21 additions & 0 deletions backend/cache/BGC.UpdateBoardGameCacheWorker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ and causes the cost of running the service to increase significantly there was a

# Deploying and running

## Docker

### Build

Open termin in the `D:\Dev\Projects\BoardGamesCompanion\backend` directory and run the below command.

`docker build -t bgc-cache-worker -f cache\BGC.UpdateBoardGameCacheWorker\Dockerfile .`

### Publish

The image should be pushed to the public docker repository https://hub.docker.com/repository/docker/mkieres/bgccacheworker/general. Do the following to upload a new image.

1. `docker login`
> NOTE: Use `CTRL + SHIFT + V` to paste in the password in the termin
2. `docker tag bgc-cache-worker mkieres/bgccacheworker:1`
3. `docker push mkieres/bgccacheworker:1`
> NOTE: Increase the tagname with each manual push
## Self-contained

This worker should be run on a machine that is always on. One of the approaches is to have it running on a ARM single-board computer (SBC) (e.g. Rasperry PI).

For more details on the build and deployment process go to https://learn.microsoft.com/en-us/dotnet/iot/deployment#deploying-a-self-contained-app
Expand Down
18 changes: 18 additions & 0 deletions backend/cache/BGC.UpdateBoardGameCacheWorker/dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build

WORKDIR /source
COPY cache/BGC.UpdateBoardGameCacheWorker/*.csproj ./cache/BGC.UpdateBoardGameCacheWorker/
COPY cache/BGC.CacheCore/*.csproj ./cache/BGC.CacheCore/
COPY BGC.Core/*.csproj ./BGC.Core/

WORKDIR /source/cache/BGC.UpdateBoardGameCacheWorker
RUN dotnet restore

# TODO Remove this, to avoid including all of the files (especially the app settings)
COPY . ./
RUN dotnet publish cache/BGC.UpdateBoardGameCacheWorker/BGC.UpdateBoardGameCacheWorker.csproj --self-contained false -o /app

FROM mcr.microsoft.com/dotnet/aspnet:7.0 as runtime
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "BGC.UpdateBoardGameCacheWorker.dll"]
2 changes: 0 additions & 2 deletions board_games_companion/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ linter:
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/primaryLight"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions board_games_companion/lib/common/app_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,37 @@ class AppText {
static const playsPageGameSpinnerPlaytimeFilterAny = 'Any';
static const playsPageGameSpinnerPlaytimeFilterInMinutesFormat = '<%s%s';

static const playsPageOverallStatsNoPlayesTitle = "You haven't played any games";
static const playsPageOverallStatsNoPlayesSubtitle =
'Start recording your plays in the app and this screen will automatically populate with statistics of your playthroughs.';

static const playsPageOverallStatsTimePeriodTitle = 'No games played in selected period';
static const playsPageOverallStatsTimePeriodSubtitle =
'Select a broader or different time period to see your logged games statistics';
static const playsPageOverallStatsTimePeriodFrom = 'From';
static const playsPageOverallStatsTimePeriodTo = 'To';
static const playsPageOverallStatsTimePeriodLastWeek = 'Last week';
static const playsPageOverallStatsTimePeriodLastMonth = 'Last month';
static const playsPageOverallStatsTimePeriodLastYear = 'Last year';
static const playsPageOverallStatsTimePeriodCustom = 'Custom';
static const playsPageOverallStatsTimePeriodDatesFormat = '%s - %s';
static const playsPageOverallStatsTimePeriodInDaysFormat = '%i days';
static const playsPageOverallStatsTimePeriodPickerHelpText = 'Pick a time period';

static const playsPageOverallStatsTimePeriodSectionTitle = 'Time period';
static const playsPageOverallStatsMostPlayedGameSectionTitle = 'Most played games';
static const playsPageOverallStatsTotalsSectionTitle = 'Totals';
static const playsPageOverallStatsGamesPlayedDistributionSctionTitle =
'Games played distribution';
static const playsPageOverallStatsTotalPlayedGamesFormat = '%i';
static const playsPageOverallStatsTotalPlayedTimeFormat = '%s%s';
static const playsPageOverallStatsTotalGamesLogged = 'logged games';
static const playsPageOverallStatsTotalPlayedGames = 'played games';
static const playsPageOverallStatsTotalPlaytime = 'playtime';
static const playsPageOverallStatsTotalSoloGames = 'solo games';
static const playsPageOverallStatsTotalDuels = 'duels';
static const playsPageOverallStatsTotalMultiplePlayerGames = 'multi-player games';

static const drawerVersionFormat = 'Version %s';
static const drawerReleaseNotes = 'Release notes';
static const drawerAppWiki = 'Wiki';
Expand Down
6 changes: 6 additions & 0 deletions board_games_companion/lib/common/dimensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Dimensions {
static const double collectionSearchResultExpansionsImageHeight = 80;
static const double collectionSearchResultExpansionsImageWidth = 80;

static const double mostPlayedGamesImageHeight = 110;
static const double mostPlayedGamesImageWidght = 110;

static const double boardGameRemoveIconSize = 40;

static const double boardGameDetailsLinkIconSize = 40;
Expand All @@ -56,6 +59,9 @@ class Dimensions {
static const double largeIconSize = 36;
static const double defaultCheckboxSize = 24;

static const double defaultStatsIconSize = 28;
static const double defaultFontAwesomeStatsIconSize = 24;

static const double floatingActionButtonBottomSpacing = 72;
static const double halfFloatingActionButtonBottomSpacing = floatingActionButtonBottomSpacing / 2;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ignore_for_file: constant_identifier_names

enum PlayStatsPresetTimePeriod {
LastWeek,
LastMonth,
LastYear,
Custom,
}
2 changes: 2 additions & 0 deletions board_games_companion/lib/common/enums/plays_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ extension ToPlaysTab on int {
case 0:
return PlaysTab.history;
case 1:
return PlaysTab.statistics;
case 2:
return PlaysTab.selectGame;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// The [weekday] may be 0 for Sunday, 1 for Monday, etc. up to 7 for Sunday.
DateTime mostRecentWeekday(DateTime date, int weekday) =>
DateTime(date.year, date.month, date.day - (date.weekday - weekday) % 7);
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ class PlaythroughDetails with _$PlaythroughDetails {
bool get hasAnyScores => scoresWithValue.isNotEmpty;

bool get finishedScoring => scoresWithValue.length == playerScores.length;

bool get isDuel => playerScores.length == 2;

bool get isMultiPlayerGame => playerScores.length > 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,52 +31,50 @@ class CollectionsFilterPanel extends StatefulWidget {

class CollectionsFilterPanelState extends State<CollectionsFilterPanel> {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
bottom: Dimensions.doubleStandardSpacing,
),
child: PageContainer(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppStyles.defaultBottomSheetCornerRadius),
topRight: Radius.circular(AppStyles.defaultBottomSheetCornerRadius),
Widget build(BuildContext context) => SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
bottom: Dimensions.doubleStandardSpacing,
),
child: Column(
children: <Widget>[
const SizedBox(height: Dimensions.oneAndHalfStandardSpacing),
const _BottomSheetHandle(),
const SizedBox(height: Dimensions.oneAndHalfStandardSpacing),
_SortBySection(gamesViewModel: widget.viewModel),
const SizedBox(height: Dimensions.standardSpacing),
_FiltersSection(gamesViewModel: widget.viewModel),
const SizedBox(height: Dimensions.standardSpacing),
Observer(
builder: (_) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: Dimensions.standardSpacing),
child: ElevatedIconButton(
icon: const Icon(Icons.clear),
title: AppText.filterGamesPanelClearFiltersButtonText,
color: AppColors.accentColor,
onPressed: widget.viewModel.anyFiltersApplied
? () => widget.viewModel.clearFilters()
: null,
child: PageContainer(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppStyles.defaultBottomSheetCornerRadius),
topRight: Radius.circular(AppStyles.defaultBottomSheetCornerRadius),
),
child: Column(
children: <Widget>[
const SizedBox(height: Dimensions.oneAndHalfStandardSpacing),
const _BottomSheetHandle(),
const SizedBox(height: Dimensions.oneAndHalfStandardSpacing),
_SortBySection(gamesViewModel: widget.viewModel),
const SizedBox(height: Dimensions.standardSpacing),
_FiltersSection(gamesViewModel: widget.viewModel),
const SizedBox(height: Dimensions.standardSpacing),
Observer(
builder: (_) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: Dimensions.standardSpacing),
child: ElevatedIconButton(
icon: const Icon(Icons.clear),
title: AppText.filterGamesPanelClearFiltersButtonText,
color: AppColors.accentColor,
onPressed: widget.viewModel.anyFiltersApplied
? () => widget.viewModel.clearFilters()
: null,
),
),
),
],
);
},
),
],
],
);
},
),
],
),
),
),
),
);
}
);
}

class _BottomSheetHandle extends StatelessWidget {
Expand All @@ -102,34 +100,32 @@ class _BottomSheetHandle extends StatelessWidget {
class _SortBySection extends StatelessWidget {
const _SortBySection({
required CollectionsViewModel gamesViewModel,
}) : _gamesViewModel = gamesViewModel;
}) : _gamesViewModel = gamesViewModel;

final CollectionsViewModel _gamesViewModel;

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SectionHeader.titleWithIcon(
title: 'Sort by',
icon: const Icon(Icons.sort),
),
const SizedBox(height: Dimensions.standardSpacing),
Wrap(
spacing: Dimensions.standardSpacing,
children: [
for (final sortByOption in _gamesViewModel.sortByOptions)
_SortByChip(
sortBy: sortByOption,
onSortByChange: (SortBy selctedSortBy) =>
_gamesViewModel.updateSortBySelection(selctedSortBy),
)
],
),
],
);
}
Widget build(BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SectionHeader.titleWithIcon(
title: 'Sort by',
icon: const Icon(Icons.sort),
),
const SizedBox(height: Dimensions.standardSpacing),
Wrap(
spacing: Dimensions.standardSpacing,
children: [
for (final sortByOption in _gamesViewModel.sortByOptions)
_SortByChip(
sortBy: sortByOption,
onSortByChange: (SortBy selctedSortBy) =>
_gamesViewModel.updateSortBySelection(selctedSortBy),
)
],
),
],
);
}

class _SortByChip extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ abstract class _EditPlaythoughViewModel with Store {
/// NOTE: Ensure this is called after player scores are assigned places
void _updatePlaceTiebreakers() {
final tiedPlayerScores = playerScores.onlyTiedScores();
if (tiedPlayerScores.isEmpty) {
if (tiedPlayerScores.isEmpty && scoreTiebreakersSet.isEmpty) {
return;
}

Expand Down
16 changes: 16 additions & 0 deletions board_games_companion/lib/pages/plays/most_played_game.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';

import '../../models/hive/board_game_details.dart';

part 'most_played_game.freezed.dart';

@freezed
class MostPlayedGame with _$MostPlayedGame {
const factory MostPlayedGame({
required BoardGameDetails boardGameDetails,
required int totalNumberOfPlays,
required int totalTimePlayedInSeconds,
}) = _MostPlayedGame;

const MostPlayedGame._();
}
Loading

0 comments on commit 81cd9b5

Please sign in to comment.