Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Added rename project option #1013

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"email": "[email protected]"
},
"date": {
"metadataLastUpdated": "2022-09-28"
"metadataLastUpdated": "2022-09-30"
}
},
{
Expand Down
4 changes: 3 additions & 1 deletion doc_source/contents/developer/changelog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog


## 1.2.1 / 2022-09-28
## 1.2.1 / 2022-09-30

- Data fetcher bugfix.
- Improvement to Windows install instructions.
Expand All @@ -11,6 +11,8 @@
- Add check_stream config section with any_trace_failures key.
- Modify StationID column in metric tables to include full channel code when available.
- Move C code to esi-core repository.
- Added rename project flag for gmrecords proj subcommand
- Switched from os.path to Pathlib in projects.py and gmrecords.py

## 1.2.0 / 2022-08-15

Expand Down
63 changes: 30 additions & 33 deletions src/gmprocess/apps/gmrecords.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import shutil
import importlib.metadata

from pathlib import Path
from ..subcommands.lazy_loader import LazyLoader

VERSION = importlib.metadata.version("gmprocess")
Expand Down Expand Up @@ -57,17 +58,17 @@ def __init__(self):
# Try not to let tests interfere with actual system:
if os.getenv("CALLED_FROM_PYTEST") is None:
# Not called from pytest
local_proj = os.path.join(os.getcwd(), const.PROJ_CONF_DIR)
local_proj_conf = os.path.join(local_proj, "projects.conf")
if os.path.isdir(local_proj) and os.path.isfile(local_proj_conf):
local_proj = Path.cwd() / const.PROJ_CONF_DIR
local_proj_conf = local_proj / "projects.conf"
if local_proj.is_dir() and local_proj_conf.is_file():
PROJECTS_PATH = local_proj
else:
PROJECTS_PATH = const.PROJECTS_PATH
else:
PROJECTS_PATH = const.CONFIG_PATH_TEST

self.PROJECTS_PATH = PROJECTS_PATH
self.PROJECTS_FILE = os.path.join(PROJECTS_PATH, "projects.conf")
self.PROJECTS_FILE = str(Path(Path(PROJECTS_PATH) / "projects.conf"))
self.gmprocess_version = VERSION

def main(self, **kwargs):
Expand Down Expand Up @@ -144,7 +145,7 @@ def _initialize(self):
logging.info(f"PROJECTS_PATH: {self.PROJECTS_PATH}")

def _load_config(self):
if not os.path.isfile(self.PROJECTS_FILE):
if not Path(self.PROJECTS_FILE).is_file():
# If projects.conf file doesn't exist then we need to run the
# initial setup.
print("No project config file detected.")
Expand Down Expand Up @@ -178,29 +179,25 @@ def _load_config(self):
print("Exiting.")
sys.exit()

self.conf_path = os.path.abspath(
os.path.join(
self.PROJECTS_FILE, os.pardir, self.current_project["conf_path"]
)
)
self.data_path = os.path.abspath(
os.path.join(
self.PROJECTS_FILE, os.pardir, self.current_project["data_path"]
)
)
self.conf_path = Path(
Path(self.PROJECTS_FILE).parent / self.current_project["conf_path"]
).resolve()
self.data_path = Path(
Path(self.PROJECTS_FILE).parent / self.current_project["data_path"]
).resolve()

if os.getenv("CALLED_FROM_PYTEST") is not None:
self.conf_path = const.CONFIG_PATH_TEST # ~/gmptest
test_conf_file = os.path.normpath(
os.path.join(const.DATA_DIR, const.CONFIG_FILE_TEST) # config_test.yml
)
if not os.path.exists(self.conf_path):
os.mkdir(self.conf_path)
test_conf_file = Path(
Path(const.DATA_DIR) / const.CONFIG_FILE_TEST
).resolve()
if not Path(self.conf_path).exists():
Path(self.conf_path).mkdir()
shutil.copyfile(
test_conf_file, os.path.join(self.conf_path, const.CONFIG_FILE_TEST)
test_conf_file, Path(Path(self.conf_path) / const.CONFIG_FILE_TEST)
)

if (not os.path.exists(self.conf_path)) or (not os.path.exists(self.data_path)):
if (not Path(self.conf_path).exists()) or (not Path(self.data_path).exists()):
print(
"Config and/or data directory does not exist for project: "
+ self.project
Expand All @@ -209,14 +206,14 @@ def _load_config(self):
project = self.project

conf_path = config["projects"][project]["conf_path"]
if os.path.exists(conf_path):
if Path(conf_path).exists():
question = f"Okay to delete everything in: {conf_path}?\n"
if not prompt.query_yes_no(question, default="yes"):
shutil.rmtree(conf_path, ignore_errors=True)
print(f"\tDeleted conf directory {conf_path}:")

data_path = config["projects"][project]["data_path"]
if os.path.exists(data_path):
if Path(data_path).exists():
question = f"Okay to delete everything in: {data_path}?\n"
if not prompt.query_yes_no(question, default="yes"):
shutil.rmtree(data_path, ignore_errors=True)
Expand Down Expand Up @@ -260,13 +257,13 @@ def _validate_projects_config(self):
if ("conf_path" not in proj) or ("data_path" not in proj):
bad_projs.append(proj_name)
continue
conf_path = os.path.abspath(
os.path.join(self.PROJECTS_FILE, os.pardir, proj["conf_path"])
)
data_path = os.path.abspath(
os.path.join(self.PROJECTS_FILE, os.pardir, proj["data_path"])
)
if not (os.path.isdir(conf_path) and os.path.isdir(data_path)):
conf_path = Path(
Path(self.PROJECTS_FILE).parent / proj["conf_path"]
).resolve()
data_path = Path(
Path(self.PROJECTS_FILE).parent / proj["data_path"]
).resolve()
if not (conf_path.is_dir() and data_path.is_dir()):
bad_projs.append(proj_name)
for bad in bad_projs:
print(f'Problem encountered in "{bad}" project. Deleting.')
Expand All @@ -292,8 +289,8 @@ def _initial_setup(self):
Initial setup of ~/.gmprogress/projects.conf; essentially invoke
# gmrecords projects -c
"""
if not os.path.isdir(self.PROJECTS_PATH):
os.mkdir(self.PROJECTS_PATH)
if not Path(self.PROJECTS_PATH).is_dir():
Path(self.PROJECTS_PATH).mkdir()
empty_conf = configobj.ConfigObj(encoding="utf-8")
empty_conf.filename = self.PROJECTS_FILE
projmod.create(empty_conf)
Expand Down
110 changes: 69 additions & 41 deletions src/gmprocess/subcommands/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import logging
import shutil

from pathlib import Path
from gmprocess.subcommands.lazy_loader import LazyLoader

ryaml = LazyLoader("yaml", globals(), "ruamel.yaml")
Expand Down Expand Up @@ -59,6 +60,14 @@ class ProjectsModule(base.SubcommandModule):
"metavar": "PROJECT",
"default": None,
},
{
"long_flag": "--rename",
"help": "Rename project SOURCE to TARGET.",
"type": str,
"nargs": 2,
"metavar": ("SOURCE", "TARGET"),
"default": None,
},
]

def main(self, gmrecords):
Expand Down Expand Up @@ -120,17 +129,11 @@ def main(self, gmrecords):
print(msg % (project, configfile, self.command_name))
sys.exit(1)

conf_path = os.path.normpath(
os.path.join(
os.path.abspath(os.path.join(config.filename, os.pardir)),
config["projects"][project]["conf_path"],
)
conf_path = Path(
Path(config.filename).parent / config["projects"][project]["conf_path"]
)
data_path = os.path.normpath(
os.path.join(
os.path.abspath(os.path.join(config.filename, os.pardir)),
config["projects"][project]["data_path"],
)
data_path = Path(
Path(config.filename).parent / config["projects"][project]["data_path"]
)

question = (
Expand All @@ -140,8 +143,7 @@ def main(self, gmrecords):
if not prompt.query_yes_no(question, default="yes"):
sys.exit(0)

shutil.rmtree(conf_path, ignore_errors=True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emthompson-usgs are things at data_path no longer being removed here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose not. Perhaps we need to fix that, but I think it is a separate issue. I'm note sure the "delete" argument ever gets used by anyone.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gferragu I modified the shutil to delete the parent of config_path so that it would also delete the project folder. See ) I don't know if this is the right approach (probably not) so if the alternative way of deleting the config_path and data_path folders separately is preferred I can change this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no guarantee that the the data and conf directories share a common parent directory (although that is the default), which is why it done separately before.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emthompson-usgs OK I didn't think about this because I am using the default. I can revert it back, but I think I'll wait until the Windows relative_path issue is resolved.

shutil.rmtree(data_path, ignore_errors=True)
shutil.rmtree(conf_path.parent, ignore_errors=True)

del config["projects"][project]

Expand All @@ -166,6 +168,38 @@ def main(self, gmrecords):
print(newproject)
sys.exit(0)

if args.rename:
source, target = args.rename
if source not in config["projects"]:
msg = "Project %s not in %s. Run %s -l to see available projects."
print(msg % (source, configfile, self.command_name))
sys.exit(1)

source_conf_path = Path(
Path(config.filename).parent / config["projects"][source]["conf_path"]
)
source_data_path = Path(
Path(config.filename).parent / config["projects"][source]["data_path"]
)
target_conf_path = Path(str(source_conf_path).replace(source, target))
target_dath_path = Path(str(source_data_path).replace(source, target))

shutil.move(
str(source_conf_path.parent),
str(target_conf_path.parent),
)

config["projects"][target] = {
"conf_path": target_conf_path,
"data_path": target_dath_path,
}
config["project"] = target
del config["projects"][source]
config.write()

print(f"\nRenamed {source} to {target}")
sys.exit(0)

project = config["project"]
projects = config["projects"]
if project not in projects:
Expand Down Expand Up @@ -205,13 +239,12 @@ def __init__(self, name, indict, filename, is_current=False):

def __repr__(self):
fmt = "Project: %s %s\n\tConf Path: %s\n\tData Path: %s"
base_dir = os.path.join(os.path.abspath(os.path.join(self.filename, os.pardir)))
if platform.system() != "Windows":
if platform != "Windows":
tpl = (
self.name,
self.current_marker,
os.path.normpath(os.path.join(base_dir, self.conf_path)),
os.path.normpath(os.path.join(base_dir, self.data_path)),
Path(Path(self.filename).parent / self.conf_path).resolve(),
Path(Path(self.filename).parent / self.data_path).resolve(),
)
else:
tpl = (self.name, self.current_marker, self.conf_path, self.data_path)
Expand All @@ -237,22 +270,17 @@ def check_project_config(config):
sys.exit(1)
# Check that the paths for each project exist
for project in config["projects"].keys():
data_exists = os.path.isdir(
os.path.join(
os.path.abspath(os.path.join(config.filename, os.pardir)),
config["projects"][project]["data_path"],
)
)

data_exists = Path(
Path(config.filename).parent / config["projects"][project]["data_path"]
).is_dir()
delete_project = False
if not data_exists:
logging.warn(f"Data path for project {project} does not exist.")
delete_project = True
conf_exists = os.path.isdir(
os.path.join(
os.path.abspath(os.path.join(config.filename, os.pardir)),
config["projects"][project]["conf_path"],
)
)
conf_exists = Path(
Path(config.filename).parent / config["projects"][project]["conf_path"]
).is_dir()
if not conf_exists:
logging.warn(f"Install path for project {project} does not exist.")
delete_project = True
Expand Down Expand Up @@ -292,13 +320,13 @@ def create(config, cwd=False):
default_conf, default_data
)
else:
cwd = os.getcwd()
proj_dir = os.path.join(cwd, ".gmprocess")
new_conf_path = os.path.join(cwd, "conf")
new_data_path = os.path.join(cwd, "data")
cwd = Path.cwd()
proj_dir = Path(cwd / ".gmprocess")
new_conf_path = Path(cwd / "conf")
new_data_path = Path(cwd / "data")
for p in [new_conf_path, new_data_path]:
if not os.path.isdir(p):
os.mkdir(p)
if not p.is_dir():
p.mkdir()

print("Please enter your name and email. This information will be added")
print("to the config file and reported in the provenance of the data")
Expand All @@ -315,12 +343,12 @@ def create(config, cwd=False):

# Apparently, relpath doesn't work for Windows, at least with the Azure
# CI builds
if platform.system() != "Windows":
new_conf_path = os.path.relpath(
new_conf_path, os.path.join(config.filename, os.pardir)
if platform != "Windows":
new_conf_path = "../" + str(
Path(new_conf_path).relative_to(Path(config.filename).parents[1])
)
new_data_path = os.path.relpath(
new_data_path, os.path.join(config.filename, os.pardir)
new_data_path = "../" + str(
Path(new_data_path).relative_to(Path(config.filename).parents[1])
)

if "projects" not in config:
Expand All @@ -342,9 +370,9 @@ def create(config, cwd=False):
user_conf = {}
user_conf["user"] = user_info
if os.getenv("CALLED_FROM_PYTEST") is None:
proj_conf_file = os.path.join(proj_dir, new_conf_path, "user.yml")
proj_conf_file = Path(Path(proj_dir) / new_conf_path / "user.yml")
else:
proj_dir = constants.CONFIG_PATH_TEST
proj_conf_file = os.path.join(proj_dir, "user.yml")
proj_conf_file = Path(proj_dir, "user.yml")
with open(proj_conf_file, "w", encoding="utf-8") as yf:
yaml.dump(user_conf, yf)
3 changes: 3 additions & 0 deletions tests/gmprocess/subcommands/projects_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ def test_projects(script_runner):
ret = script_runner.run("gmrecords", "projects", "-s", "test")
assert ret.success

ret = script_runner.run("gmrecords", "projects", "--rename", "test", "tested")
assert ret.success

setup_inputs = io.StringIO("y\n")
ret = script_runner.run(
"gmrecords", "projects", "-d", "test2", stdin=setup_inputs
Expand Down