def detail(request, board_id, dummy_board_slug=None): """Return a detail view for the given board. Evidence is sorted in order of diagnosticity. Hypotheses are sorted in order of consistency. """ # NOTE: Django's page cache considers full URL including dummy_board_slug. In the future, we may want to adjust # the page key to only consider the id and the query parameters. # https://docs.djangoproject.com/en/1.10/topics/cache/#the-per-view-cache # NOTE: cannot cache page for logged in users b/c comments section contains CSRF and other protection mechanisms. view_type = ( "aggregate" if request.GET.get("view_type") is None else request.GET["view_type"] ) board = get_object_or_404(Board, pk=board_id) permissions = board.permissions.for_user(request.user) if "read_board" not in permissions: raise PermissionDenied() if view_type == "comparison" and not request.user.is_authenticated: raise PermissionDenied() collaborator_ids = board.collaborator_ids() vote_type = request.GET.get( "vote_type", default=("collab" if request.user.id in collaborator_ids else "all"), ) # calculate aggregate and disagreement for each evidence/hypothesis pair all_votes = list(board.evaluation_set.all()) agg_votes = ( [x for x in all_votes if x.user_id in collaborator_ids] if vote_type == "collab" else all_votes ) def _pair_key(evaluation): return evaluation.evidence_id, evaluation.hypothesis_id keyed = defaultdict(list) for vote in agg_votes: keyed[_pair_key(vote)].append(Eval(vote.value)) aggregate = {k: aggregate_vote(v) for k, v in keyed.items()} disagreement = {k: calc_disagreement(v) for k, v in keyed.items()} user_votes = ( {_pair_key(v): Eval(v.value) for v in all_votes if v.user_id == request.user.id} if request.user.is_authenticated else None ) # augment hypotheses and evidence with diagnosticity and consistency def _group(first, second, func, key): return [(f, func([keyed[key(f, s)] for s in second])) for f in first] hypotheses = list(board.hypothesis_set.filter(removed=False)) evidence = list(board.evidence_set.filter(removed=False)) hypothesis_consistency = _group( hypotheses, evidence, hypothesis_sort_key, key=lambda h, e: (e.id, h.id) ) evidence_diagnosticity = _group( evidence, hypotheses, evidence_sort_key, key=lambda e, h: (e.id, h.id) ) return render( request, "boards/detail.html", { "board": board, "permissions": permissions, "evidences": sorted(evidence_diagnosticity, key=lambda e: e[1]), "hypotheses": sorted(hypothesis_consistency, key=lambda h: h[1]), "view_type": view_type, "vote_type": vote_type, "votes": aggregate, "user_votes": user_votes, "disagreement": disagreement, "meta_description": board.board_desc, "allow_share": not getattr(settings, "ACCOUNT_REQUIRED", False), "debug_stats": DEBUG, }, )
def detail(request, board_id, dummy_board_slug=None): """Return a detail view for the given board. Evidence is sorted in order of diagnosticity. Hypotheses are sorted in order of consistency. """ # NOTE: Django's page cache considers full URL including dummy_board_slug. In the future, we may want to adjust # the page key to only consider the id and the query parameters. # https://docs.djangoproject.com/en/1.10/topics/cache/#the-per-view-cache # NOTE: cannot cache page for logged in users b/c comments section contains CSRF and other protection mechanisms. view_type = 'aggregate' if request.GET.get('view_type') is None else request.GET['view_type'] board = get_object_or_404(Board, pk=board_id) permissions = board.permissions.for_user(request.user) if 'read_board' not in permissions: raise PermissionDenied() if view_type == 'comparison' and not request.user.is_authenticated: raise PermissionDenied() vote_type = request.GET.get('vote_type', default=( 'collab' # rewrite to avoid unnecessary lookup if key is present? if board.permissions.collaborators.filter(pk=request.user.id).exists() else 'all' )) all_votes = list(board.evaluation_set.all()) # calculate aggregate and disagreement for each evidence/hypothesis pair agg_votes = all_votes if vote_type == 'collab': collaborators = set([c.id for c in board.permissions.collaborators.all()]) agg_votes = [v for v in all_votes if v.user_id in collaborators] def _pair_key(evaluation): return evaluation.evidence_id, evaluation.hypothesis_id keyed = defaultdict(list) for vote in agg_votes: keyed[_pair_key(vote)].append(Eval(vote.value)) aggregate = {k: aggregate_vote(v) for k, v in keyed.items()} disagreement = {k: calc_disagreement(v) for k, v in keyed.items()} user_votes = ( {_pair_key(v): Eval(v.value) for v in all_votes if v.user_id == request.user.id} if request.user.is_authenticated else None ) # augment hypotheses and evidence with diagnosticity and consistency def _group(first, second, func, key): return [(f, func([keyed[key(f, s)] for s in second])) for f in first] hypotheses = list(board.hypothesis_set.filter(removed=False)) evidence = list(board.evidence_set.filter(removed=False)) hypothesis_consistency = _group(hypotheses, evidence, hypothesis_sort_key, key=lambda h, e: (e.id, h.id)) evidence_diagnosticity = _group(evidence, hypotheses, evidence_sort_key, key=lambda e, h: (e.id, h.id)) context = { 'board': board, 'permissions': permissions, 'evidences': sorted(evidence_diagnosticity, key=lambda e: e[1]), 'hypotheses': sorted(hypothesis_consistency, key=lambda h: h[1]), 'view_type': view_type, 'vote_type': vote_type, 'votes': aggregate, 'user_votes': user_votes, 'disagreement': disagreement, 'meta_description': board.board_desc, 'allow_share': not getattr(settings, 'ACCOUNT_REQUIRED', False), 'debug_stats': DEBUG, } return render(request, 'boards/detail.html', context)
def test_extreme_votes_have_greater_disagreement(self): """Test that votes that are further from neutral result in a larger disagreement score.""" small = [Eval.consistent, Eval.inconsistent] large = [Eval.very_inconsistent, Eval.very_consistent] self.assertGreater(calc_disagreement(large), calc_disagreement(small))
def test_same_vote_has_zero_disagreement(self): """Test that calc_disagreement() returns 0.0 when there are only votes of a single type.""" for vote in Eval: self.assertEqual(calc_disagreement([vote, vote]), 0.0)
def test_single_vote_has_zero_disagreement(self): """Test that calc_disagreement() returns 0.0 when there is a single vote.""" for vote in Eval: self.assertEqual(calc_disagreement([vote]), 0.0)
def test_no_votes_returns_none(self): """Test that calc_disagreement() returns None when there is no votes""" self.assertEqual(calc_disagreement([]), None)