# progress.py from otree.api import Page from importlib import import_module import sys # Apps you don't want to count (case-insensitive): EXCLUDED_APPS = {'consent','barnum','end'} class ProgressPage(Page): def vars_for_template(self): progress_bar = self.session.config.get('progress_bar',0) app_seq = self.session.config['app_sequence'] app_seq = [a for a in app_seq if a.lower() not in {x.lower() for x in EXCLUDED_APPS}] current_app = self.__class__.__module__.split('.')[0] app_index = app_seq.index(current_app) # 0-based num_apps = len(app_seq) # Pages within the current app pages_module = import_module(f'{current_app}.pages') seq = pages_module.page_sequence pos = seq.index(self.__class__) # 0-based total = len(seq) # apps completed + fraction of current app overall_fraction = (app_index + pos / total) / num_apps return dict( apps_completed=app_index, num_apps=num_apps, app_progress_pct=int(100 * pos / total), overall_progress_pct=int(100 * overall_fraction), progress_bar = progress_bar, ) def progress_vars(page_class, player): """ Composite progress = (apps_completed + page_pos/total_pages_in_app) / num_apps. - Excludes apps in EXCLUDED_APPS from denominator. - Treats skipped pages as done (index-based within page_sequence). """ # 1) Filtered app_sequence full_seq = player.session.config['app_sequence'] excluded = {x.lower() for x in EXCLUDED_APPS} progress_seq = [a for a in full_seq if a.lower() not in excluded] # 2) Figure out current app label & pages module from the *actual* module path module_path = page_class.__module__ # e.g., "project.crt.pages" or "crt.pages" parts = module_path.split('.') # App label is the segment immediately before "pages" (fallback to last part) if len(parts) >= 2 and parts[-1] == 'pages': current_app = parts[-2] else: current_app = parts[-1] num_apps = len(progress_seq) print(page_class) # If this app is excluded (or no counted apps), return zeros if current_app not in progress_seq or num_apps == 0: return { 'apps_completed': 0, 'num_apps': num_apps, 'app_progress_pct': 0, 'overall_progress_pct': 0, } # 3) Apps completed = index of current app in filtered sequence app_index = progress_seq.index(current_app) # 4) Load the *exact* pages module using the real module path pages_module = sys.modules.get(module_path) or import_module(module_path) seq = getattr(pages_module, 'page_sequence', []) total = max(1, len(seq)) # avoid divide-by-zero try: pos = seq.index(page_class) # 0-based: pages before this one except ValueError: pos = 0 # fallback if not found # 5) Compute percentages app_fraction = pos / total overall_fraction = (app_index + app_fraction) / num_apps progress_bar = player.session.config.get('progress_bar', 0) return { 'apps_completed': app_index, 'num_apps': num_apps, 'app_progress_pct': int(round(100 * app_fraction)), 'overall_progress_pct': int(round(100 * overall_fraction)), 'progress_bar': progress_bar, }