def process(data_dir, user_data, survey_data):
  pool = multiprocessing.Pool(processes=12)
  user_ids = [user.user_id for user in user_data]

  object_paths = [
    '/'.join([data_dir, user.object_file]) for user in user_data
  ]
  object_features = pool.map(process_objects, object_paths)
  object_timelines = [x['object_timeline'] for x in object_features]
  experiment_paths = [
    '/'.join([data_dir, user.experiment_file]) for user in user_data
  ]
  args = zip(experiment_paths, object_timelines)
  experiment_features = pool.map(process_experiment, args)
  webcam_paths = [
    '/'.join([data_dir, user.webcam_file]) for user in user_data
  ]
  args = zip(webcam_paths, object_timelines)
  webcam_features = pool.map(process_code, args)

  all_data = []
  for user_id, (exp, exp_timeline), (cam, cam_timeline), obj, survey in zip(
    user_ids, experiment_features, webcam_features, object_features,
    survey_data):
    object_stats = ObjectStats()
    object_stats.update(exp)
    object_stats.update(cam)
    data = (user_id, object_stats, exp_timeline, cam_timeline, obj, survey)
    all_data.append(data)

  html = views.generate(all_data)
  output_path = '/'.join([data_dir, 'index.html'])
  with open(output_path, 'w') as output_file:
    print(html, file=output_file)
  def object_stats(self):
    object_stats = ObjectStats()
    object_stats.update(self._left_time)
    object_stats.update(self._num_left_looks)
    object_stats.update(self._right_time)
    object_stats.update(self._num_right_looks)
    init_features = [
      'num_left_looks', 'left_time', 'mean_left', 'left_stddev',
      'num_right_looks', 'right_time', 'mean_right', 'right_stddev'
    ]
    for obj in objects.OBJECTS:
      for feature in init_features:
        object_stats.add(obj, feature, 0)
    for obj, stats in object_stats.items():
      num_left_looks = stats['num_left_looks']
      left_time = stats['left_time']
      mean_left = left_time / num_left_looks if num_left_looks != 0 else 0
      stats['mean_left'] = mean_left

      squared_left = self._squared_left.get_stats(obj)['squared_left']
      squared_mean = mean_left * mean_left
      stddev = (
        squared_left / num_left_looks - squared_mean
        if num_left_looks != 0 else 0
      )
      stats['left_stddev'] = stddev

      num_right_looks = stats['num_right_looks']
      right_time = stats['right_time']
      mean_right = right_time / num_right_looks if num_right_looks != 0 else 0
      stats['mean_right'] = mean_right

      squared_right = self._squared_right.get_stats(obj)['squared_right']
      squared_mean = mean_right * mean_right
      stddev = (
        squared_right / num_right_looks - squared_mean
        if num_right_looks != 0 else 0
      )
      stats['right_stddev'] = stddev
    return object_stats
def process_experiment((path, object_timeline)):
  """Extract features of the bag file."""
  print('Processing', path)
  bag = open_bag(path)
  if bag is None:
    return None
  object_stats = ObjectStats()
  time_taken_processor = processors.TimeTaken(object_timeline)
  camera_movement_processor = processors.CameraMovementTime(object_timeline)
  marker_movement_processor = processors.MarkerMovementTime(object_timeline)
  grasp_count_processor = processors.GraspCount(object_timeline)
  for topic, message, time in bag.read_messages(topics=EXPERIMENT_TOPICS):
    model = message_factory.model(message)
    if model is None:
      continue
    time_taken_processor.update(topic, model, time)
    camera_movement_processor.update(topic, model, time)
    marker_movement_processor.update(topic, model, time)
    grasp_count_processor.update(topic, model, time)
  bag.close()
  time_taken_processor.update_last()
  object_stats.update(time_taken_processor.object_stats())
  object_stats.update(camera_movement_processor.object_stats())
  object_stats.update(marker_movement_processor.object_stats())
  object_stats.update(grasp_count_processor.object_stats())
  for obj, stats in object_stats.items():
    stats['other_time'] = (
      stats['time_taken'] - stats['camera_movement_time'] -
      stats['marker_movement_time']
    )

  timeline = build_timeline(
    marker_movement_processor.timeline(),
    camera_movement_processor.timeline())
  
  return object_stats, timeline