Commit fea78ed8 authored by Markus Shepherd's avatar Markus Shepherd

Merge branch 'similar' into 'master'

Resolve "add BGA similar games view"

Closes #211

See merge request !78
parents dc9404bb f6807ea8
This diff is collapsed.
1-7-3
\ No newline at end of file
1-8-0
\ No newline at end of file
......@@ -18,7 +18,8 @@ ludojApp.controller('BgaController', function BgaController(
gamesService,
toastr
) {
var users = {};
var users = {},
routeParams = filterService.getParams($routeParams);
function fetchGames(page) {
toastr.clear();
......@@ -27,12 +28,16 @@ ludojApp.controller('BgaController', function BgaController(
var append = page > 1,
url = API_URL + 'games/recommend_bga/',
userName = $routeParams.for || null,
userName = routeParams.for || null,
params = {'page': page},
promise,
bgaParams,
games;
if (routeParams.similarity) {
params.model = 'similarity';
}
if (!userName) {
promise = $q.resolve(params);
} else if (users[userName]) {
......@@ -130,16 +135,19 @@ ludojApp.controller('BgaController', function BgaController(
});
}
$scope.user = $routeParams.for;
$scope.similarity = $routeParams.similarity;
$scope.user = routeParams.for;
$scope.similarity = routeParams.similarity;
$scope.fetchGames = fetchGames;
$scope.empty = false;
$scope.total = null;
$scope.hideScore = $routeParams.for && $routeParams.similarity;
$scope.hideScore = true;
$scope.updateParams = function updateParams() {
$route.updateParams({'for': $scope.user || null});
$route.updateParams({
'for': $scope.user || null,
'similarity': $scope.similarity || null
});
};
$scope.clearField = function clearField(field, id) {
......@@ -150,7 +158,7 @@ ludojApp.controller('BgaController', function BgaController(
fetchGames(1);
gamesService.setTitle();
gamesService.setTitle(routeParams.for ? 'BGA recommendations for ' + routeParams.for : 'BGA recommendations');
gamesService.setCanonicalUrl($location.path(), filterService.getParams($routeParams));
gamesService.setImage();
gamesService.setDescription();
......
......@@ -38,6 +38,37 @@
</button>
</div>
</div>
<div class="{{ user ? 'form-exclude-enabled' : 'form-exclude-disabled text-muted' }}">
Use recommender model:
<div class="form-check form-check-inline mr-0"
data-toggle="tooltip"
data-placement="bottom"
title="Use the default or similarity based model for recommendations">
<label class="form-check-label form-label-exclude {{ user &amp;&amp; !similarity ? 'text-success' : 'text-muted' }} mr-1"
for="recommender-similarity">
default
</label>
<label class="switch mb-0">
<input type="checkbox"
id="recommender-similarity"
name="recommender-similarity"
ng-model="similarity"
ng-disabled="!user" />
<span class="slider bg-secondary"></span>
</label>
<label class="form-check-label form-label-exclude {{ user &amp;&amp; similarity ? 'text-success' : 'text-muted' }} ml-1"
for="recommender-similarity">
similarity
</label>
</div>
<a href="/#/faq#what-are-the-standard-and-similarity-models-in-the-recommendation-settings"
data-toggle="tooltip"
data-placement="right"
title="Read more about the different models"
class="text-muted">
<i class="fas fa-info-circle"></i>
</a>
</div>
</form>
<h2 ng-if="currUser">
......
......@@ -89,7 +89,19 @@ def _exclude(user=None, ids=None):
return sframe
def _parse_parts(args):
for arg in arg_to_iter(args):
if isinstance(arg, str):
for parsed in arg.split(','):
parsed = parsed.strip()
if parsed:
yield parsed
elif isinstance(arg, (list, tuple)):
yield from _parse_parts(arg)
def _parse_ints(args):
# TODO use _parse_parts()
for arg in arg_to_iter(args):
if isinstance(arg, int):
yield arg
......@@ -284,22 +296,24 @@ class GameViewSet(PermissionsModelViewSet):
def recommend_bga(self, request):
''' recommend games with Board Game Atlas data '''
user = request.query_params.get('user')
path = getattr(settings, 'BGA_RECOMMENDER_PATH', None)
recommender = load_recommender(path, 'bga')
if recommender is None:
return self.list(request)
user = request.query_params.get('user')
like = list(_parse_parts(request.query_params.getlist('like')))
recommendation = recommender.recommend(
users=(user,),
# similarity_model=take_first(params.get('model')) == 'similarity',
similarity_model=request.query_params.get('model') == 'similarity',
# exclude_known=parse_bool(take_first(params.get('exclude_known'))),
# exclude_clusters=parse_bool(take_first(params.get('exclude_clusters'))),
star_percentiles=getattr(settings, 'STAR_PERCENTILES', None),
)
) if user or not like else recommender.recommend_similar(games=like)
del path, recommender
del path, recommender, user, like
page = self.paginate_queryset(recommendation)
return (
......@@ -368,9 +382,34 @@ class GameViewSet(PermissionsModelViewSet):
# pylint: disable=unused-argument,invalid-name
@action(detail=True)
def similar_bga(self, request, pk=None):
''' find games similar to this game with BGA data '''
path = getattr(settings, 'BGA_RECOMMENDER_PATH', None)
recommender = load_recommender(path, 'bga')
if recommender is None:
raise NotFound(f'cannot find similar games to <{pk}>')
games = recommender.similar_games(pk, num_games=0)
del path, recommender
page = self.paginate_queryset(games)
return (
self.get_paginated_response(page) if page is not None
else Response(list(games[:10])))
# pylint: disable=invalid-name
@action(detail=True)
def similar(self, request, pk=None):
''' find games similar to this game '''
site = request.query_params.get('site')
if site == 'bga':
return self.similar_bga(request, pk)
path = getattr(settings, 'RECOMMENDER_PATH', None)
recommender = load_recommender(path)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment