docs: def create_database_migration_numbered_style(
          alembic_ini_file: str,
          alembic_versions_dir: str,
          message: str,
          n_sequence_chars: int = 4) -> None:
      """
      Create a new Alembic migration script.
      
      Alembic compares the **state of the database** to the **state of the
      metadata**, and generates a migration that brings the former up to the
      latter. (It does **not** compare the most recent revision to the current
      metadata, so make sure your database is up to date with the most recent
      revision before running this!)
      
      You **must check** that the autogenerated revisions are sensible.
      
      How does it know where to look for the database?
      
          1. This function changes into the directory of the Alembic ``.ini``
             file and calls the external program
          
             .. code-block:: bash
          
                  alembic -c ALEMBIC_INI_FILE revision --autogenerate -m MESSAGE --rev-id REVISION_ID
                  
          2. The Alembic ``.ini`` file points (via the ``script_location``
             variable) to a directory containing your ``env.py``. Alembic loads
             this script.
             
          3. That script typically works out the database URL and calls further
             into the Alembic code.        
             
     See http://alembic.zzzcomputing.com/en/latest/autogenerate.html.
  
      Regarding filenames: the default ``n_sequence_chars`` of 4 is like Django
      and gives files with names like
  
      .. code-block:: none
  
          0001_x.py, 0002_y.py, ...
  
      NOTE THAT TO USE A NON-STANDARD ALEMBIC VERSION TABLE, YOU MUST SPECIFY
      THAT IN YOUR ``env.py`` (see e.g. CamCOPS).
  
      Args:
          alembic_ini_file: filename of Alembic ``alembic.ini`` file
          alembic_versions_dir: directory in which you keep your Python scripts,
              one per Alembic revision
          message: message to be associated with this revision
          n_sequence_chars: number of numerical sequence characters to use in the
              filename/revision (see above).
      """  # noqa
      _, _, existing_version_filenames = next(os.walk(alembic_versions_dir),
                                              (None, None, []))
      existing_version_filenames = [
          x for x in existing_version_filenames if x != "__init__.py"]
      log.debug("Existing Alembic version script filenames: {!r}",
                existing_version_filenames)
      current_seq_strs = [x[:n_sequence_chars]
                          for x in existing_version_filenames]
      current_seq_strs.sort()
      if not current_seq_strs:
          current_seq_str = None
          new_seq_no = 1
      else:
          current_seq_str = current_seq_strs[-1]
          new_seq_no = max(int(x) for x in current_seq_strs) + 1
      new_seq_str = str(new_seq_no).zfill(n_sequence_chars)
  
      log.info(
          """
  Generating new revision with Alembic...
      Last revision was: {}
      New revision will be: {}
      [If it fails with "Can't locate revision identified by...", you might need
      to DROP the Alembic version table (by default named 'alembic_version', but
      you may have elected to change that in your env.py.]
          """,
          current_seq_str,
          new_seq_str
      )
  
      alembic_ini_dir = os.path.dirname(alembic_ini_file)
      os.chdir(alembic_ini_dir)
      cmdargs = ['alembic',
                 '-c', alembic_ini_file,
                 'revision',
                 '--autogenerate',
                 '-m', message,
                 '--rev-id', new_seq_str]
      log.info("From directory {!r}, calling: {!r}", alembic_ini_dir, cmdargs)
      subprocess.call(cmdargs)