Beispiel #1
0
def download_file(ctx, filepath, output, version):
    """
    Download project file at specified version. `project` needs to be a combination of namespace/project.
    If no version is given, the latest will be fetched.
    """
    mc = ctx.obj["client"]
    if mc is None:
        return
    mp = MerginProject(os.getcwd())
    project_path = mp.metadata["name"]
    try:
        job = download_file_async(mc, project_path, filepath, output, version)
        with click.progressbar(length=job.total_size) as bar:
            last_transferred_size = 0
            while download_project_is_running(job):
                time.sleep(1 / 10)  # 100ms
                new_transferred_size = job.transferred_size
                bar.update(
                    new_transferred_size -
                    last_transferred_size)  # the update() needs increment only
                last_transferred_size = new_transferred_size
        download_file_finalize(job)
        click.echo("Done")
    except KeyboardInterrupt:
        click.secho("Cancelling...")
        download_project_cancel(job)
    except ClientError as e:
        click.secho("Error: " + str(e), fg="red")
    except Exception as e:
        _print_unhandled_exception()
Beispiel #2
0
def show_file_changeset(ctx, path, version):
    """ Displays information about project changes."""
    mc = ctx.obj["client"]
    if mc is None:
        return
    directory = os.getcwd()
    mp = MerginProject(directory)
    project_path = mp.metadata["name"]
    info_dict = mc.project_file_changeset_info(project_path, path, version)
    # TODO: handle exception if Diff not found
    click.secho(json.dumps(info_dict, indent=2))
Beispiel #3
0
def show_file_changeset(path, version):
    """ Displays information about a single version of a project """

    c = _init_client()
    if c is None:
        return
    directory = os.getcwd()

    mp = MerginProject(directory)
    project_path = mp.metadata["name"]

    info_dict = c.project_file_changeset_info(project_path, path, version)
    print(json.dumps(info_dict, indent=2))
Beispiel #4
0
def show_version(version):
    """ Displays information about a single version of a project """

    c = _init_client()
    if c is None:
        return
    directory = os.getcwd()

    mp = MerginProject(directory)
    project_path = mp.metadata["name"]

    version_info_dict = c.project_version_info(project_path, version)[0]
    print("Project: " + version_info_dict['project']['namespace'] + "/" + version_info_dict['project']['name'])
    print("Version: " + version_info_dict['name'] + " by " + version_info_dict['author'])
    print("Time:    " + version_info_dict['created'])
    pretty_diff(version_info_dict['changes'])
Beispiel #5
0
def show_version(ctx, version):
    """ Displays information about a single version of a project. `version` is 'v1', 'v2', etc. """
    mc = ctx.obj["client"]
    if mc is None:
        return
    directory = os.getcwd()
    mp = MerginProject(directory)
    project_path = mp.metadata["name"]
    # TODO: handle exception when version not found
    version_info_dict = mc.project_version_info(project_path, version)[0]
    click.secho("Project: " + version_info_dict["project"]["namespace"] + "/" +
                version_info_dict["project"]["name"])
    click.secho("Version: " + version_info_dict["name"] + " by " +
                version_info_dict["author"])
    click.secho("Time:    " + version_info_dict["created"])
    pretty_diff(version_info_dict["changes"])
Beispiel #6
0
def show_file_history(ctx, path):
    """ Displays information about a single version of a project """
    mc = ctx.obj["client"]
    if mc is None:
        return
    directory = os.getcwd()
    mp = MerginProject(directory)
    project_path = mp.metadata["name"]
    info_dict = mc.project_file_history_info(project_path, path)
    # TODO: handle exception if history not found
    history_dict = info_dict["history"]
    click.secho("File history: " + info_dict["path"])
    click.secho("-----")
    for version, version_data in history_dict.items():
        diff_info = ""
        if "diff" in version_data:
            diff_info = "diff ({} bytes)".format(version_data["diff"]["size"])
        click.secho(" {:5} {:10} {}".format(version, version_data["change"],
                                            diff_info))
Beispiel #7
0
def show_file_history(path):
    """ Displays information about a single version of a project """

    c = _init_client()
    if c is None:
        return
    directory = os.getcwd()

    mp = MerginProject(directory)
    project_path = mp.metadata["name"]

    info_dict = c.project_file_history_info(project_path, path)
    history_dict = info_dict['history']

    print("File history: " + info_dict['path'])
    print("-----")
    for version, version_data in history_dict.items():
        diff_info = ''
        if 'diff' in version_data:
            diff_info = "diff ({} bytes)".format(version_data['diff']['size'])
        print(" {:5} {:10} {}".format(version, version_data['change'], diff_info))
Beispiel #8
0
def dbsync_push(mc):
    """ Take changes in the 'modified' schema in the database and push them to Mergin """

    tmp_dir = tempfile.gettempdir()
    tmp_changeset_file = os.path.join(tmp_dir, 'dbsync-push-base2our')
    if os.path.exists(tmp_changeset_file):
        os.remove(tmp_changeset_file)

    _check_has_working_dir()
    _check_has_sync_file()

    mp = MerginProject(config.project_working_dir)
    if mp.geodiff is None:
        raise DbSyncError(
            "Mergin client installation problem: geodiff not available")
    project_path = mp.metadata["name"]
    local_version = mp.metadata["version"]

    try:
        projects = mc.get_projects_by_names([project_path])
        server_version = projects[project_path]["version"]
    except ClientError as e:
        # this could be e.g. DNS error
        raise DbSyncError("Mergin client error: " + str(e))

    status_push = mp.get_push_changes()
    if status_push['added'] or status_push['updated'] or status_push['removed']:
        raise DbSyncError(
            "There are pending changes in the local directory - that should never happen! "
            + str(status_push))

    # check there are no pending changes on server
    if server_version != local_version:
        raise DbSyncError(
            "There are pending changes on server - need to pull them first.")

    conn = psycopg2.connect(config.db_conn_info)

    if not _check_schema_exists(conn, config.db_schema_base):
        raise DbSyncError("The base schema does not exist: " +
                          config.db_schema_base)
    if not _check_schema_exists(conn, config.db_schema_modified):
        raise DbSyncError("The 'modified' schema does not exist: " +
                          config.db_schema_modified)

    # get changes in the DB
    _geodiff_create_changeset(config.db_driver, config.db_conn_info,
                              config.db_schema_base, config.db_schema_modified,
                              tmp_changeset_file)

    if os.path.getsize(tmp_changeset_file) == 0:
        print("No changes in the database.")
        return

    # summarize changes
    summary = _geodiff_list_changes_summary(tmp_changeset_file)
    _print_changes_summary(summary)

    # write changes to the local geopackage
    print("Writing DB changes to working dir...")
    gpkg_full_path = os.path.join(config.project_working_dir,
                                  config.mergin_sync_file)
    _geodiff_apply_changeset("sqlite", "", gpkg_full_path, tmp_changeset_file)

    # write to the server
    try:
        mc.push_project(config.project_working_dir)
    except ClientError as e:
        # TODO: should we do some cleanup here? (undo changes in the local geopackage?)
        raise DbSyncError("Mergin client error on push: " + str(e))

    version = _get_project_version()
    print("Pushed new version to Mergin: " + version)

    # update base schema in the DB
    print("Updating DB base schema...")
    _geodiff_apply_changeset(config.db_driver, config.db_conn_info,
                             config.db_schema_base, tmp_changeset_file)
    _set_db_project_comment(conn, config.db_schema_base,
                            config.mergin_project_name, version)

    print("Push done!")
Beispiel #9
0
def dbsync_status(mc):
    """ Figure out if there are any pending changes in the database or in Mergin """

    _check_has_working_dir()
    _check_has_sync_file()

    # get basic information
    mp = MerginProject(config.project_working_dir)
    if mp.geodiff is None:
        raise DbSyncError(
            "Mergin client installation problem: geodiff not available")
    status_push = mp.get_push_changes()
    if status_push['added'] or status_push['updated'] or status_push['removed']:
        raise DbSyncError(
            "Pending changes in the local directory - that should never happen! "
            + str(status_push))

    project_path = mp.metadata["name"]
    local_version = mp.metadata["version"]
    print("Working directory " + config.project_working_dir)
    print("Mergin project " + project_path + " at local version " +
          local_version)
    print("")
    print("Checking status...")

    # check if there are any pending changes on server
    try:
        server_info = mc.project_info(project_path, since=local_version)
    except ClientError as e:
        raise DbSyncError("Mergin client error: " + str(e))

    print("Server is at version " + server_info["version"])

    status_pull = mp.get_pull_changes(server_info["files"])
    if status_pull['added'] or status_pull['updated'] or status_pull['removed']:
        print("There are pending changes on server:")
        _print_mergin_changes(status_pull)
    else:
        print("No pending changes on server.")

    print("")
    conn = psycopg2.connect(config.db_conn_info)

    if not _check_schema_exists(conn, config.db_schema_base):
        raise DbSyncError("The base schema does not exist: " +
                          config.db_schema_base)
    if not _check_schema_exists(conn, config.db_schema_modified):
        raise DbSyncError("The 'modified' schema does not exist: " +
                          config.db_schema_modified)

    # get changes in the DB
    tmp_dir = tempfile.gettempdir()
    tmp_changeset_file = os.path.join(tmp_dir, 'dbsync-status-base2our')
    if os.path.exists(tmp_changeset_file):
        os.remove(tmp_changeset_file)
    _geodiff_create_changeset(config.db_driver, config.db_conn_info,
                              config.db_schema_base, config.db_schema_modified,
                              tmp_changeset_file)

    if os.path.getsize(tmp_changeset_file) == 0:
        print("No changes in the database.")
    else:
        print("There are changes in DB")
        # summarize changes
        summary = _geodiff_list_changes_summary(tmp_changeset_file)
        _print_changes_summary(summary)
Beispiel #10
0
def dbsync_pull(mc):
    """ Downloads any changes from Mergin and applies them to the database """

    _check_has_working_dir()
    _check_has_sync_file()

    mp = MerginProject(config.project_working_dir)
    if mp.geodiff is None:
        raise DbSyncError(
            "Mergin client installation problem: geodiff not available")
    project_path = mp.metadata["name"]
    local_version = mp.metadata["version"]

    try:
        projects = mc.get_projects_by_names([project_path])
        server_version = projects[project_path]["version"]
    except ClientError as e:
        # this could be e.g. DNS error
        raise DbSyncError("Mergin client error: " + str(e))

    status_push = mp.get_push_changes()
    if status_push['added'] or status_push['updated'] or status_push['removed']:
        raise DbSyncError(
            "There are pending changes in the local directory - that should never happen! "
            + str(status_push))

    if server_version == local_version:
        print("No changes on Mergin.")
        return

    gpkg_basefile = os.path.join(config.project_working_dir, '.mergin',
                                 config.mergin_sync_file)
    gpkg_basefile_old = gpkg_basefile + "-old"

    # make a copy of the basefile in the current version (base) - because after pull it will be set to "their"
    shutil.copy(gpkg_basefile, gpkg_basefile_old)

    tmp_dir = tempfile.gettempdir()
    tmp_base2our = os.path.join(tmp_dir, 'dbsync-pull-base2our')
    tmp_base2their = os.path.join(tmp_dir, 'dbsync-pull-base2their')

    # find out our local changes in the database (base2our)
    _geodiff_create_changeset(config.db_driver, config.db_conn_info,
                              config.db_schema_base, config.db_schema_modified,
                              tmp_base2our)

    needs_rebase = False
    if os.path.getsize(tmp_base2our) != 0:
        needs_rebase = True
        summary = _geodiff_list_changes_summary(tmp_base2our)
        _print_changes_summary(summary, "DB Changes:")

    try:
        mc.pull_project(config.project_working_dir)  # will do rebase as needed
    except ClientError as e:
        # TODO: do we need some cleanup here?
        raise DbSyncError("Mergin client error on pull: " + str(e))

    print("Pulled new version from Mergin: " + _get_project_version())

    # simple case when there are no pending local changes - just apply whatever changes are coming
    _geodiff_create_changeset("sqlite", "", gpkg_basefile_old, gpkg_basefile,
                              tmp_base2their)

    # summarize changes
    summary = _geodiff_list_changes_summary(tmp_base2their)
    _print_changes_summary(summary, "Mergin Changes:")

    if not needs_rebase:
        print("Applying new version [no rebase]")
        _geodiff_apply_changeset(config.db_driver, config.db_conn_info,
                                 config.db_schema_base, tmp_base2their)
        _geodiff_apply_changeset(config.db_driver, config.db_conn_info,
                                 config.db_schema_modified, tmp_base2their)
    else:
        print("Applying new version [WITH rebase]")
        tmp_conflicts = os.path.join(tmp_dir, 'dbsync-pull-conflicts')
        _geodiff_rebase(config.db_driver, config.db_conn_info,
                        config.db_schema_base, config.db_schema_modified,
                        tmp_base2their, tmp_conflicts)
        _geodiff_apply_changeset(config.db_driver, config.db_conn_info,
                                 config.db_schema_base, tmp_base2their)

    os.remove(gpkg_basefile_old)
    conn = psycopg2.connect(config.db_conn_info)
    version = _get_project_version()
    _set_db_project_comment(conn, config.db_schema_base,
                            config.mergin_project_name, version)
    print("Pull done!")
Beispiel #11
0
def _get_project_version():
    """ Returns the current version of the project """
    mp = MerginProject(config.project_working_dir)
    return mp.metadata["version"]