text: import logging
  import os.path as op
  import sys
  
  from . import __version__, __packagename__
  from .bids import populate_bids_templates, tuneup_bids_json_files
  from .convert import prep_conversion
  from .parser import get_study_sessions
  from .queue import queue_conversion
  from .utils import anonymize_sid, load_heuristic, treat_infofile, SeqInfo
  
  lgr = logging.getLogger(__name__)
  
  
  INIT_MSG = "Running {packname} version {version} latest {latest}".format
  
  
  def is_interactive():
      """Return True if all in/outs are tty"""
      # TODO: check on windows if hasattr check would work correctly and add value:
      return sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty()
  
  
  def setup_exceptionhook():
      """
      Overloads default sys.excepthook with our exceptionhook handler.
  
      If interactive, our exceptionhook handler will invoke pdb.post_mortem;
      if not interactive, then invokes default handler.
      """
      def _pdb_excepthook(type, value, tb):
          if is_interactive():
              import traceback
              import pdb
              traceback.print_exception(type, value, tb)
              # print()
              pdb.post_mortem(tb)
          else:
              lgr.warning(
                "We cannot setup exception hook since not in interactive mode")
  
      sys.excepthook = _pdb_excepthook
  
  
  def process_extra_commands(outdir, command, files, dicom_dir_template,
                             heuristic, session, subjs, grouping):
      """
      Perform custom command instead of regular operations. Supported commands:
      ['treat-json', 'ls', 'populate-templates']
  
      Parameters
      ----------
      outdir : str
          Output directory
      command : {'treat-json', 'ls', 'populate-templates'}
          Heudiconv command to run
      files : list of str
          List of files
      dicom_dir_template : str
          Location of dicomdir that can be indexed with subject id
          {subject} and session {session}. Tarballs (can be compressed)
          are supported in addition to directory. All matching tarballs
          for a subject are extracted and their content processed in a
          single pass. If multiple tarballs are found, each is assumed to
          be a separate session and the 'session' argument is ignored.
      heuristic : str
          Path to heuristic file or name of builtin heuristic.
      session : str
          Session identifier
      subjs : list of str
          List of subject identifiers
      grouping : {'studyUID', 'accession_number', 'all', 'custom'}
          How to group dicoms.
      """
      if command == 'treat-jsons':
          for f in files:
              treat_infofile(f)
      elif command == 'ls':
          ensure_heuristic_arg(heuristic)
          heuristic = load_heuristic(heuristic)
          heuristic_ls = getattr(heuristic, 'ls', None)
          for f in files:
              study_sessions = get_study_sessions(
                  dicom_dir_template, [f], heuristic, outdir,
                  session, subjs, grouping=grouping)
              print(f)
              for study_session, sequences in study_sessions.items():
                  suf = ''
                  if heuristic_ls:
                      suf += heuristic_ls(study_session, sequences)
                  print(
                      "\t%s %d sequences%s"
                      % (str(study_session), len(sequences), suf)
                  )
      elif command == 'populate-templates':
          ensure_heuristic_arg(heuristic)
          heuristic = load_heuristic(heuristic)
          for f in files:
              populate_bids_templates(f, getattr(heuristic, 'DEFAULT_FIELDS', {}))
      elif command == 'sanitize-jsons':
          tuneup_bids_json_files(files)
      elif command == 'heuristics':
          from .utils import get_known_heuristics_with_descriptions
          for name_desc in get_known_heuristics_with_descriptions().items():
              print("- %s: %s" % name_desc)
      elif command == 'heuristic-info':
          ensure_heuristic_arg(heuristic)
          from .utils import get_heuristic_description
          print(get_heuristic_description(heuristic, full=True))
      else:
          raise ValueError("Unknown command %s" % command)
      return
  
  
  def ensure_heuristic_arg(heuristic=None):
      """
      Check that the heuristic argument was provided.
      """
      from .utils import get_known_heuristic_names
      if not heuristic:
          raise ValueError("Specify heuristic using -f. Known are: %s"
                           % ', '.join(get_known_heuristic_names()))
  
  
  def workflow(*, dicom_dir_template=None, files=None, subjs=None,
               converter='dcm2niix', outdir='.', locator=None, conv_outdir=None,
               anon_cmd=None, heuristic=None, with_prov=False, session=None,
               bids_options=None, overwrite=False, datalad=False, debug=False,
               command=None, grouping='studyUID', minmeta=False,
               random_seed=None, dcmconfig=None, queue=None, queue_args=None):
      """Run the HeuDiConv conversion workflow.
  
      Parameters
      ----------
      dicom_dir_template : str or None, optional
          Location of dicomdir that can be indexed with subject id
          {subject} and session {session}. Tarballs (can be compressed)
          are supported in addition to directory. All matching tarballs
          for a subject are extracted and their content processed in a
          single pass. If multiple tarballs are found, each is assumed to
          be a separate session and the 'session' argument is ignored.
          Mutually exclusive with 'files'. Default is None.
      files : list or None, optional
          Files (tarballs, dicoms) or directories containing files to
          process. Mutually exclusive with 'dicom_dir_template'. Default is None.
      subjs : list or None, optional
          List of subjects - required for dicom template. If not
          provided, DICOMS would first be "sorted" and subject IDs
          deduced by the heuristic. Default is None.
      converter : {'dcm2niix', None}, optional
          Tool to use for DICOM conversion. Setting to None disables
          the actual conversion step -- useful for testing heuristics.
          Default is None.
      outdir : str, optional
          Output directory for conversion setup (for further
          customization and future reference. This directory will refer
          to non-anonymized subject IDs.
          Default is '.' (current working directory).
      locator : str or 'unknown' or None, optional
          Study path under outdir. If provided, it overloads the value
          provided by the heuristic. If 'datalad=True', every
          directory within locator becomes a super-dataset thus
          establishing a hierarchy. Setting to "unknown" will skip that
          dataset. Default is None.
      conv_outdir : str or None, optional
          Output directory for converted files. By default this is
          identical to --outdir. This option is most useful in
          combination with 'anon_cmd'. Default is None.
      anon_cmd : str or None, optional
          Command to run to convert subject IDs used for DICOMs to
          anonymized IDs. Such command must take a single argument and
          return a single anonymized ID. Also see 'conv_outdir'. Default is None.
      heuristic : str or None, optional
          Name of a known heuristic or path to the Python script containing
          heuristic. Default is None.
      with_prov : bool, optional
          Store additional provenance information. Requires python-rdflib.
          Default is False.
      session : str or None, optional
          Session for longitudinal study_sessions. Default is None.
      bids_options : str or None, optional
          Flag for output into BIDS structure. Can also take BIDS-
          specific options, e.g., --bids notop. The only currently
          supported options is "notop", which skips creation of
          top-level BIDS files. This is useful when running in batch
          mode to prevent possible race conditions. Default is None.
      overwrite : bool, optional
          Overwrite existing converted files. Default is False.
      datalad : bool, optional
          Store the entire collection as DataLad dataset(s). Small files
          will be committed directly to git, while large to annex. New
          version (6) of annex repositories will be used in a "thin"
          mode so it would look to mortals as just any other regular
          directory (i.e. no symlinks to under .git/annex). For now just
          for BIDS mode. Default is False.
      debug : bool, optional
          Do not catch exceptions and show exception traceback. Default is False.
      command : {'heuristics', 'heuristic-info', 'ls', 'populate-templates',
                 'sanitize-jsons', 'treat-jsons', None}, optional
          Custom action to be performed on provided files instead of regular
          operation. Default is None.
      grouping : {'studyUID', 'accession_number', 'all', 'custom'}, optional
          How to group dicoms. Default is 'studyUID'.
      minmeta : bool, optional
          Exclude dcmstack meta information in sidecar jsons. Default is False.
      random_seed : int or None, optional
          Random seed to initialize RNG. Default is None.
      dcmconfig : str or None, optional
          JSON file for additional dcm2niix configuration. Default is None.
      queue : {'SLURM', None}, optional
          Batch system to submit jobs in parallel. Default is None.
          If set, will cause scheduling of conversion and return without performing
          any further action.
      queue_args : str or None, optional
          Additional queue arguments passed as single string of space-separated
          Argument=Value pairs. Default is None.
  
      Notes
      -----
      All parameters in this function must be called as keyword arguments.
      """
  
      # To be done asap so anything random is deterministic
      if random_seed is not None:
          import random
          random.seed(random_seed)
          import numpy
          numpy.random.seed(random_seed)
      # Ensure only supported bids options are passed
      if debug:
          lgr.setLevel(logging.DEBUG)
      # Should be possible but only with a single subject -- will be used to
      # override subject deduced from the DICOMs
      if files and subjs and len(subjs) > 1:
          raise ValueError(
              "Unable to processes multiple `--subjects` with files"
          )
  
      if debug:
          setup_exceptionhook()
  
      # Deal with provided files or templates
      # pre-process provided list of files and possibly sort into groups/sessions
      # Group files per each study/sid/session
  
      outdir = op.abspath(outdir)
  
      try:
          import etelemetry
          latest = etelemetry.get_project("nipy/heudiconv")
      except Exception as e:
          lgr.warning("Could not check for version updates: %s", str(e))
          latest = {"version": 'Unknown'}
  
      lgr.info(INIT_MSG(packname=__packagename__,
                        version=__version__,
                        latest=latest["version"]))
  
      if command:
          process_extra_commands(outdir, command, files, dicom_dir_template,
                                 heuristic, session, subjs, grouping)
          return
      #
      # Load heuristic -- better do it asap to make sure it loads correctly
      #
      if not heuristic:
          raise RuntimeError("No heuristic specified - add to arguments and rerun")
  
      if queue:
          lgr.info("Queuing %s conversion", queue)
          iterarg, iterables = ("files", len(files)) if files else \
                               ("subjects", len(subjs))
          queue_conversion(queue, iterarg, iterables, queue_args)
          return
  
      heuristic = load_heuristic(heuristic)
  
      study_sessions = get_study_sessions(dicom_dir_template, files,
                                          heuristic, outdir, session,
                                          subjs, grouping=grouping)
  
      # extract tarballs, and replace their entries with expanded lists of files
      # TODO: we might need to sort so sessions are ordered???
      lgr.info("Need to process %d study sessions", len(study_sessions))
  
      # processed_studydirs = set()
  
      locator_manual, session_manual = locator, session
      for (locator, session, sid), files_or_seqinfo in study_sessions.items():
  
          # Allow for session to be overloaded from command line
          if session_manual is not None:
              session = session_manual
          if locator_manual is not None:
              locator = locator_manual
          if not len(files_or_seqinfo):
              raise ValueError("nothing to process?")
          # that is how life is ATM :-/ since we don't do sorting if subj
          # template is provided
          if isinstance(files_or_seqinfo, dict):
              assert(isinstance(list(files_or_seqinfo.keys())[0], SeqInfo))
              dicoms = None
              seqinfo = files_or_seqinfo
          else:
              dicoms = files_or_seqinfo
              seqinfo = None
  
          if locator == 'unknown':
              lgr.warning("Skipping unknown locator dataset")
              continue
  
          anon_sid = anonymize_sid(sid, anon_cmd) if anon_cmd else None
          if anon_cmd:
              lgr.info('Anonymized {} to {}'.format(sid, anon_sid))
  
          study_outdir = op.join(outdir, locator or '')
          anon_outdir = conv_outdir or outdir
          anon_study_outdir = op.join(anon_outdir, locator or '')
  
          if datalad:
              from .external.dlad import prepare_datalad
              dlad_sid = sid if not anon_sid else anon_sid
              dl_msg = prepare_datalad(anon_study_outdir, anon_outdir, dlad_sid,
                                       session, seqinfo, dicoms,
                                       bids_options)
  
          lgr.info("PROCESSING STARTS: {0}".format(
              str(dict(subject=sid, outdir=study_outdir, session=session))))
  
          prep_conversion(sid,
                          dicoms,
                          study_outdir,
                          heuristic,
                          converter=converter,
                          anon_sid=anon_sid,
                          anon_outdir=anon_study_outdir,
                          with_prov=with_prov,
                          ses=session,
                          bids_options=bids_options,
                          seqinfo=seqinfo,
                          min_meta=minmeta,
                          overwrite=overwrite,
                          dcmconfig=dcmconfig,
                          grouping=grouping,)
  
          lgr.info("PROCESSING DONE: {0}".format(
              str(dict(subject=sid, outdir=study_outdir, session=session))))
  
          if datalad:
              from .external.dlad import add_to_datalad
              msg = "Converted subject %s" % dl_msg
              # TODO:  whenever propagate to supers work -- do just
              # ds.save(msg=msg)
              #  also in batch mode might fail since we have no locking ATM
              #  and theoretically no need actually to save entire study
              #  we just need that
              add_to_datalad(outdir, study_outdir, msg, bids_options)
  
      # if bids:
      #     # Let's populate BIDS templates for folks to take care about
      #     for study_outdir in processed_studydirs:
      #         populate_bids_templates(study_outdir)
      #
      # TODO: record_collection of the sid/session although that information
      # is pretty much present in .heudiconv/SUBJECT/info so we could just poke there
  