docs: def mkprof(destination, source=None, relations=None, where=None,
             in_place=False, skeleton=False, full=False, gzip=False):
      """
      Create [incr tsdb()] profiles or skeletons.
  
      Data for the testsuite may come from an existing testsuite or from
      a list of sentences. There are four main usage patterns:
  
          - `source="testsuite/"` -- read data from `testsuite/`
          - `source=None, in_place=True` -- read data from *destination*
          - `source=None, in_place=False` -- read sentences from stdin
          - `source="sents.txt"` -- read sentences from `sents.txt`
  
      For the latter two, the *relations* parameter must be specified.
  
      Args:
          destination (str): path of the new testsuite
          source (str): path to a source testsuite or a file containing
              sentences; if not given and *in_place* is `False`,
              sentences are read from stdin
          relations (str): path to a relations file to use for the
              created testsuite; if `None` and *source* is given, the
              relations file of the source testsuite is used
          where (str): TSQL condition to filter records by; ignored if
              *source* is not a testsuite
          in_place (bool): if `True` and *source* is not given, use
              *destination* as the source for data (default: `False`)
          skeleton (bool): if `True`, only write tsdb-core files
              (default: `False`)
          full (bool): if `True`, copy all data from the source
              testsuite (requires *source* to be a testsuite path;
              default: `False`)
          gzip (bool): if `True`, non-empty tables will be compressed
              with gzip
      """
      # basic validation
      if skeleton and full:
          raise ValueError("'skeleton' is incompatible with 'full'")
      elif skeleton and in_place:
          raise ValueError("'skeleton' is incompatible with 'in_place'")
      elif in_place and source is not None:
          raise ValueError("'in_place' is incompatible with 'source'")
      if in_place:
          source = destination
      if full and (source is None or not os.path.isdir(source)):
          raise ValueError("'full' must be used with a source testsuite")
      if relations is None and source is not None and os.path.isdir(source):
          relations = os.path.join(source, 'relations')
      elif relations is None or not os.path.isfile(relations):
          raise ValueError('invalid or missing relations file: {}'
                           .format(relations))
      # setup destination testsuite
      _prepare_output_directory(destination)
      dts = itsdb.TestSuite(path=destination, relations=relations)
      # input is sentences on stdin
      if source is None:
          dts.write({'item': _lines_to_rows(sys.stdin, dts.relations)},
                    gzip=gzip)
      # input is sentence file
      elif os.path.isfile(source):
          with open(source) as fh:
              dts.write({'item': _lines_to_rows(fh, dts.relations)},
                        gzip=gzip)
      # input is source testsuite
      elif os.path.isdir(source):
          sts = itsdb.TestSuite(source)
          tables = dts.relations.tables if full else itsdb.tsdb_core_files
          where = '' if where is None else 'where ' + where
          for table in tables:
              if sts.size(table) > 0:
                  # filter the data, but use all if the query fails
                  # (e.g., if the filter and table cannot be joined)
                  try:
                      rows = tsql.select(
                          '* from {} {}'.format(table, where), sts, cast=False)
                  except itsdb.ItsdbError:
                      rows = sts[table]
                  dts.write({table: rows}, gzip=gzip)
      dts.reload()
      # unless a skeleton was requested, make empty files for other tables
      if not skeleton:
          for table in dts.relations:
              if len(dts[table]) == 0:
                  dts.write({table: []})
  
      # summarize what was done
      if sys.stdout.isatty():
          _red = lambda s: '\x1b[1;31m{}\x1b[0m'.format(s)
      else:
          _red = lambda s: s
      fmt = '{:>8} bytes\t{}'
      for filename in ['relations'] + list(dts.relations.tables):
          path = os.path.join(destination, filename)
          if os.path.isfile(path):
              stat = os.stat(path)
              print(fmt.format(stat.st_size, filename))
          elif os.path.isfile(path + '.gz'):
              stat = os.stat(path + '.gz')
              print(fmt.format(stat.st_size, _red(filename + '.gz')))