def METHOD_NAME(self):
    """Return our best estimate of the remaining duration, or None
    if we have no bases for guessing."""
    if self.end_times is None:
        return 2 * self.elapsed_time()  # We have not started the first module yet
    else:
        module_index = self.current_module.module_num - 1
        index = self.image_set_index * self.num_modules + module_index
        durations = (self.end_times[1:] - self.end_times[:-1]).reshape(
            self.num_image_sets, self.num_modules
        )
        per_module_estimates = numpy.zeros(self.num_modules)
        per_module_estimates[:module_index] = numpy.median(
            durations[: self.image_set_index + 1, :module_index], 0
        )
        current_module_so_far = self.elapsed_time() - self.end_times[1 + index - 1]
        if self.image_set_index > 0:
            per_module_estimates[module_index:] = numpy.median(
                durations[: self.image_set_index, module_index:], 0
            )
            per_module_estimates[module_index] = max(
                per_module_estimates[module_index], current_module_so_far
            )
        else:
            # Guess that the modules that haven't finished yet are
            # as slow as the slowest one we've seen so far.
            per_module_estimates[module_index] = current_module_so_far
            per_module_estimates[module_index:] = per_module_estimates[
                : module_index + 1
            ].max()
        per_module_estimates[:module_index] *= (
            self.num_image_sets - self.image_set_index - 1
        )
        per_module_estimates[module_index:] *= (
            self.num_image_sets - self.image_set_index
        )
        per_module_estimates[module_index] -= current_module_so_far
        return per_module_estimates.sum()