# Author: Cameron F. Abrams <cfa22@drexel.edu>
"""
Subcommand to modify the pestifer package. This subcommand is only exposed if the Pestifer installation includes the entire source tree (i.e., it was installed with pip -e from a git repository).
The two main ways you can modify the package are
1. Managing examples, including adding, updating, and deleting example scripts. Updating can include name-changes, adding new auxiliary inputs, and modifying expected outputs.
2. Regenerating atomselect macros based on globals defined in :mod:`core/labels.py <pestifer.core.labels>`.
"""
from dataclasses import dataclass
import logging
import argparse as ap
from . import Subcommand
from ..core.resourcemanager import ResourceManager
logger = logging.getLogger(__name__)
[docs]
@dataclass
class ModifyPackageSubcommand(Subcommand):
name: str = 'modify-package'
short_help: str = "modify the pestifer package"
long_help: str = "Modify the pestifer package by adding, updating, or deleting examples."
[docs]
@staticmethod
def func(args: ap.Namespace, **kwargs):
"""
Modify the pestifer source package. Current modifications allowed involve example addition/update/deletion, and
updating atomselect macros based on globals defined in :mod:`core/labels.py <pestifer.core.labels>`.
.. note::
The command ``pestifer modify-package`` can only be invoked if ``pestifer`` is installed as an editable source tree, i.e. the directory containing the ``pestifer`` package. A simple pip installation of the pestifer package will not allow this command to run, as the pestifer package will not have a full source tree available. If you want to modify the package, you must clone the repository from GitHub and then execute ``pip install -e .`` from the package root. Before modifying, it would also be a good idea to create a new branch, rather than modifying in the main branch.
"""
# First, verify that the user has a full source tree by verifying the existence of the docs directory. If not, raise an error.
RM = ResourceManager()
match args.example_action:
case 'add':
example_id = args.example_id
scriptname = args.example_scriptname
author_name = args.example_author_name
author_email = args.example_author_email
title = args.example_title
db_id = args.example_db_id
auxiliary_inputs = args.example_auxiliary_inputs
outputs = args.example_outputs
if scriptname:
RM.add_example(scriptname, example_id=example_id, author_name=author_name, author_email=author_email, title=title, db_id=db_id, auxiliary_inputs=auxiliary_inputs, outputs=outputs)
else:
raise ValueError(f'Invalid parameter for add example action: scriptname={scriptname}. Must be non-empty YAML file name.')
case 'update':
example_id = args.example_id
name = args.example_name
author_name = args.example_author_name
author_email = args.example_author_email
title = args.example_title
db_id = args.example_db_id
auxiliary_inputs = args.example_auxiliary_inputs
outputs = args.example_outputs
if example_id > 0:
RM.update_example(example_id, shortname=name, author_name=author_name, author_email=author_email, title=title, db_id=db_id, auxiliary_inputs=auxiliary_inputs, outputs=outputs)
else:
raise ValueError(f'Invalid parameters for update example action: example_id={example_id}. Must be positive integer.')
case 'delete':
example_id = args.example_id
if example_id > 0:
RM.delete_example(example_id)
else:
raise ValueError(f'Invalid parameter for delete example action: example_id={example_id}. Must be positive integer.')
case 'rename':
example_id = args.example_id
new_name = args.example_name
if example_id > 0 and new_name:
RM.rename_example(example_id, new_name=new_name)
else:
raise ValueError(f'Invalid parameters for rename example action: example_id={example_id}, new_name={new_name}. Must be positive integer and non-empty YAML file name.')
case 'author':
example_id = args.example_id
author_name = args.example_author_name
author_email = args.example_author_email
if example_id > 0 and author_name and author_email:
RM.set_example_author(example_id, author_name, author_email)
else:
raise ValueError(f'Invalid parameters for set-author example action: example_id={example_id}, author_name={author_name}, author_email={author_email}. Must be positive integer and non-empty strings.')
case _:
pass
if args.update_atomselect_macros:
RM.update_atomselect_macros()
return True
[docs]
def add_subparser(self, subparsers):
super().add_subparser(subparsers)
self.parser.add_argument('--update-atomselect-macros', action='store_true', help='update the resources/tcl/macros.tcl file based on content in core/labels.py; developer use only')
self.parser.add_argument('--example-id', type=int, default=0, help='integer ID of example to modify; developer use only')
self.parser.add_argument('--example-action', type=str, default=None, choices=[None, 'add', 'update', 'delete', 'rename', 'author'], help='action to perform on the example; choices are [add|update|delete|rename|author]; developer use only')
self.parser.add_argument('--example-scriptname', type=str, default='', help='yaml file of example; developer use only')
self.parser.add_argument('--example-name', type=str, default='', help='new name for the example for action \'rename\'; default is basename of the script file (without extension); developer use only')
self.parser.add_argument('--example-author-name', type=str, default='', help='name of the author; if not given, pestifer attempts to extract it from the script header')
self.parser.add_argument('--example-author-email', type=str, default='', help='email of the author; if not given, pestifer attempts to extract it from the script header')
self.parser.add_argument('--example-title', type=str, default='', help='descriptive 1-line title of the example (default: extract from \'title\' directive in the script)')
self.parser.add_argument('--example-db-id', type=str, default='', help='database ID of the example; if not given, pestifer attempts to extract it from the script\'s "fetch" task')
self.parser.add_argument('--example-auxiliary-inputs', type=str, nargs='*', default=[], help='list of auxiliary input files for the example')
self.parser.add_argument('--example-outputs', type=str, nargs='*', default=[], help='list of output files for the example')
return self.parser