docs: def find_data(self, subject_ids=None, visit_ids=None, **kwargs):
          """
          Find all filesets, fields and provenance records within an XNAT project
  
          Parameters
          ----------
          subject_ids : list(str)
              List of subject IDs with which to filter the tree with. If
              None all are returned
          visit_ids : list(str)
              List of visit IDs with which to filter the tree with. If
              None all are returned
  
          Returns
          -------
          filesets : list[Fileset]
              All the filesets found in the repository
          fields : list[Field]
              All the fields found in the repository
          records : list[Record]
              The provenance records found in the repository
          """
          subject_ids = self.convert_subject_ids(subject_ids)
          # Add derived visit IDs to list of visit ids to filter
          all_filesets = []
          all_fields = []
          all_records = []
          # Note we prefer the use of raw REST API calls here for performance
          # reasons over using XnatPy's data structures.
          with self:
              # Get map of internal subject IDs to subject labels in project
              subject_xids_to_labels = {
                  s['ID']: s['label'] for s in self._login.get_json(
                      '/data/projects/{}/subjects'.format(self.project_id))[
                          'ResultSet']['Result']}
              # Get list of all sessions within project
              session_xids = [
                  s['ID'] for s in self._login.get_json(
                      '/data/projects/{}/experiments'.format(self.project_id))[
                          'ResultSet']['Result']
                  if (self.session_filter is None or
                      self.session_filter.match(s['label']))]
              for session_xid in session_xids:
                  session_json = self._login.get_json(
                      '/data/projects/{}/experiments/{}'.format(
                          self.project_id, session_xid))['items'][0]
                  subject_xid = session_json['data_fields']['subject_ID']
                  subject_id = subject_xids_to_labels[subject_xid]
                  session_label = session_json['data_fields']['label']
                  session_uri = (
                      '/data/archive/projects/{}/subjects/{}/experiments/{}'
                      .format(self.project_id, subject_xid, session_xid))
                  # Get field values. We do this first so we can check for the
                  # DERIVED_FROM_FIELD to determine the correct session label and
                  # study name
                  field_values = {}
                  try:
                      fields_json = next(
                          c['items'] for c in session_json['children']
                          if c['field'] == 'fields/field')
                  except StopIteration:
                      pass
                  else:
                      for js in fields_json:
                          try:
                              value = js['data_fields']['field']
                          except KeyError:
                              pass
                          else:
                              field_values[js['data_fields']['name']] = value
                  # Extract study name and derived-from session
                  if self.DERIVED_FROM_FIELD in field_values:
                      df_sess_label = field_values.pop(self.DERIVED_FROM_FIELD)
                      from_study = session_label[len(df_sess_label) + 1:]
                      session_label = df_sess_label
                  else:
                      from_study = None
                  # Strip subject ID from session label if required
                  if session_label.startswith(subject_id + '_'):
                      visit_id = session_label[len(subject_id) + 1:]
                  else:
                      visit_id = session_label
                  # Strip project ID from subject ID if required
                  if subject_id.startswith(self.project_id + '_'):
                      subject_id = subject_id[len(self.project_id) + 1:]
                  # Check subject is summary or not and whether it is to be
                  # filtered
                  if subject_id == XnatRepo.SUMMARY_NAME:
                      subject_id = None
                  elif not (subject_ids is None or subject_id in subject_ids):
                      continue
                  # Check visit is summary or not and whether it is to be
                  # filtered
                  if visit_id == XnatRepo.SUMMARY_NAME:
                      visit_id = None
                  elif not (visit_ids is None or visit_id in visit_ids):
                      continue
                  # Determine frequency
                  if (subject_id, visit_id) == (None, None):
                      frequency = 'per_study'
                  elif visit_id is None:
                      frequency = 'per_subject'
                  elif subject_id is None:
                      frequency = 'per_visit'
                  else:
                      frequency = 'per_session'
                  # Append fields
                  for name, value in field_values.items():
                      value = value.replace('&quot;', '"')
                      all_fields.append(Field(
                          name=name, value=value, repository=self,
                          frequency=frequency,
                          subject_id=subject_id,
                          visit_id=visit_id,
                          from_study=from_study,
                          **kwargs))
                  # Extract part of JSON relating to files
                  try:
                      scans_json = next(
                          c['items'] for c in session_json['children']
                          if c['field'] == 'scans/scan')
                  except StopIteration:
                      scans_json = []
                  for scan_json in scans_json:
                      scan_id = scan_json['data_fields']['ID']
                      scan_type = scan_json['data_fields'].get('type', '')
                      scan_uri = '{}/scans/{}'.format(session_uri, scan_id)
                      try:
                          resources_json = next(
                              c['items'] for c in scan_json['children']
                              if c['field'] == 'file')
                      except StopIteration:
                          resources = {}
                      else:
                          resources = {js['data_fields']['label']:
                                       js['data_fields'].get('format', None)
                                       for js in resources_json}
                      # Remove auto-generated snapshots directory
                      resources.pop('SNAPSHOTS', None)
                      if scan_type == self.PROV_SCAN:
                          # Download provenance JSON files and parse into
                          # records
                          temp_dir = tempfile.mkdtemp()
                          try:
                              with tempfile.TemporaryFile() as temp_zip:
                                  self._login.download_stream(
                                      scan_uri + '/files', temp_zip,
                                      format='zip')
                                  with ZipFile(temp_zip) as zip_file:
                                      zip_file.extractall(temp_dir)
                              for base_dir, _, fnames in os.walk(temp_dir):
                                  for fname in fnames:
                                      if fname.endswith('.json'):
                                          pipeline_name = fname[:-len('.json')]
                                          json_path = op.join(base_dir, fname)
                                          all_records.append(
                                              Record.load(
                                                  pipeline_name, frequency,
                                                  subject_id, visit_id,
                                                  from_study, json_path))
                          finally:
                              shutil.rmtree(temp_dir, ignore_errors=True)
                      else:
                          for resource in resources:
                              all_filesets.append(Fileset(
                                  scan_type, id=scan_id, uri=scan_uri,
                                  repository=self, frequency=frequency,
                                  subject_id=subject_id, visit_id=visit_id,
                                  from_study=from_study,
                                  resource_name=resource, **kwargs))
                  logger.debug("Found node {}:{} on {}:{}".format(
                      subject_id, visit_id, self.server, self.project_id))
          return all_filesets, all_fields, all_records