# ============================================================ # Real Effort Task (RET) generator: Decoding Task # ============================================================ import random from random import randint from string import digits, ascii_lowercase import logging logger = logging.getLogger(__name__) # ------------------------------------------------------------ # Utility Functions # ------------------------------------------------------------ def slicelist(lst, n): """Split list into sublists of length n (if possible).""" return [lst[i:i + n] for i in range(0, len(lst), n)] def chunkify(lst, n): """Split list into n roughly equal-sized chunks.""" return [lst[i::n] for i in range(n)] def get_random_list(max_len): """Generate a random list of integers (unused for decoding, kept for flexibility).""" low_upper_bound = 50 high_upper_bound = 99 return [randint(10, randint(low_upper_bound, high_upper_bound)) for _ in range(max_len)] # ------------------------------------------------------------ # Base Task Generator Class # ------------------------------------------------------------ class TaskGenerator: """Base class for real effort tasks like Decoding or Slider.""" def __init__(self, **kwargs): # Generate body + correct answer self.body = self.get_body(**kwargs) self.correct_answer = self.get_correct_answer() logger.debug(f'Generated task: {self.body}, answer: {self.correct_answer}') def get_body(self, **kwargs): """To be implemented by each subclass.""" raise NotImplementedError("Subclasses must implement get_body()") def get_correct_answer(self): """To be implemented by each subclass.""" raise NotImplementedError("Subclasses must implement get_correct_answer()") def get_context_for_body(self): """Return the context dictionary for templates.""" return {} # ------------------------------------------------------------ # Decoding Task Generator # ------------------------------------------------------------ class Decoding(TaskGenerator): """Generate a decoding task: translate digits to letters.""" name = 'Decoding' def get_body(self, **kwargs): """Generate the random decoding dictionary and question.""" dict_length = kwargs.get('dict_length', 10) # number of pairs (e.g., 10 letters) task_len = kwargs.get('task_len', 2) # number of digits to decode # Create random digit-letter mappings digits_list = random.sample(list(digits), k=dict_length) random.shuffle(digits_list) letters_list = random.sample(ascii_lowercase, k=dict_length) self.task_dict = dict(zip(digits_list, letters_list)) self.question = random.choices(digits_list, k=task_len) return { 'question': self.question, 'task_dict': self.task_dict, } def get_correct_answer(self): """Compute the correct decoded string.""" return ''.join([self.task_dict[i] for i in self.question]) def get_context_for_body(self): """Context for rendering in oTree templates.""" return { 'question': self.question, 'task_dict': self.task_dict, }