Source code for timApp.item.scoreboard

from collections import OrderedDict
from dataclasses import dataclass
from typing import Optional

from timApp.answer.pointsumrule import PointCountMethod
from timApp.document.docinfo import DocInfo
from timApp.document.docsettings import DocSettings
from timApp.document.document import dereference_pars
from timApp.document.usercontext import UserContext
from timApp.document.viewcontext import default_view_ctx
from timApp.folder.folder import Folder
from timApp.plugin.plugin import Plugin, find_task_ids, TaskNotFoundException


[docs]@dataclass class TaskScoreInfo: taskName: str fragId: str points: float maxPoints: float
[docs]@dataclass class DocScoreInfo: doc: DocInfo total: float maxTotal: float tasks: list[TaskScoreInfo]
[docs]def get_score_infos( folder: Folder, doc_paths: list[str], user_ctx: UserContext, lang_id: str | None = None, ) -> list[DocScoreInfo]: total_table = OrderedDict() u = user_ctx.logged_user docs = folder.get_all_documents( relative_paths=doc_paths, filter_user=u, ) def doc_sorter(d: DocInfo) -> int: rel_path = folder.relative_path(d) return doc_paths.index(rel_path) docs.sort(key=doc_sorter) if lang_id: docs = [ next((t for t in d.translations if t.lang_id == lang_id), d) for d in docs ] for d in docs: d.document.insert_preamble_pars() user_context = UserContext.from_one_user(u) for d in docs: doc = d.document blocks = doc.get_paragraphs() blocks = dereference_pars(blocks, context_doc=doc, view_ctx=default_view_ctx) task_ids, _, _ = find_task_ids(blocks, default_view_ctx, user_ctx) if not task_ids: continue point_sum_rule = doc.get_settings().point_sum_rule() count_method = ( point_sum_rule.point_count_method if point_sum_rule else PointCountMethod.latest ) # cycle through all tasks in current document, resolving user's progress on each scored assignment point_dict: dict[str, TaskScoreInfo] = {} for task_id in task_ids: try: plugin, _ = Plugin.from_task_id( task_id.doc_task, user_ctx, default_view_ctx ) except TaskNotFoundException: continue max_points_str = plugin.max_points() if not max_points_str: continue try: max_points = float(max_points_str) except ValueError: # TODO: figure out something for when max_points is not convertible to float continue if max_points == 0: continue # TODO: point_sum_rule can also have max_points - maybe take it into account somehow. task = task_id.task_name if count_method == PointCountMethod.max: user_points = max( ( a.points for a in u.get_answers_for_task(task_id.doc_task) if a.points is not None ), default=0, ) elif count_method == PointCountMethod.latest: latest_answer = u.get_answers_for_task(task_id.doc_task).first() user_points = ( latest_answer.points if latest_answer and latest_answer.points is not None else 0 ) else: raise Exception(f"Unexpected count_method: {count_method}") # add current document to overall points list, using task_name as the key identifier # group tasks in the list by point_sum_rule groups # link takes to the first task in a group ("frag_id") groups = [] included_groups = [] include_groupless = True # whether to include tasks without groups if point_sum_rule is not None: groups = list(point_sum_rule.find_groups(task_id.doc_task)) included_groups = point_sum_rule.scoreboard.groups if not included_groups: included_groups = groups # only include groupless if scoreboard_groups weren't specified or * is in there elif "*" not in included_groups: include_groupless = False if groups: groups = [g for g in groups if g in included_groups] for grp in groups: tps = point_dict.get(grp) if tps: tps.maxPoints += max_points tps.points += user_points else: point_dict[grp] = TaskScoreInfo( grp, task, user_points, max_points ) elif include_groupless: point_dict[task] = TaskScoreInfo(task, task, user_points, max_points) if not point_dict: continue tasks = list(point_dict.values()) user_total = sum(t.points for t in tasks if t.points is not None) max_total = sum(t.maxPoints for t in tasks if t.maxPoints is not None) total_table[folder.relative_path(d)] = DocScoreInfo( d, user_total, max_total, tasks ) return list(total_table.values())
[docs]def get_score_infos_if_enabled( doc_info: DocInfo, doc_settings: DocSettings, user_ctx: UserContext, ) -> list[DocScoreInfo] | None: score_infos = None if user_ctx.logged_user.logged_in and doc_settings.show_scoreboard(): scoreboard_docs = doc_settings.scoreboard_docs() if not scoreboard_docs: scoreboard_docs.append(doc_info.short_name) score_infos = get_score_infos( doc_info.parent, scoreboard_docs, user_ctx, doc_info.lang_id ) return score_infos