diff --git a/.github/workflows/Sync-dev-with-main.yml b/.github/workflows/Sync-dev-with-main.yml new file mode 100644 index 0000000..84e717b --- /dev/null +++ b/.github/workflows/Sync-dev-with-main.yml @@ -0,0 +1,48 @@ +name: Rebase main and dev and add .dev to version + +on: + workflow_run: + workflows: ["Tag and Release"] + types: + - completed + workflow_dispatch: + +jobs: + rebase: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + token: ${{ secrets.BOT_ACCESS_TOKEN }} + + - name: Setup Git + run: | + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + - name: Fetch all branches + run: git fetch --all + + - name: Rebase dev with main + run: | + git checkout dev + git rebase origin/main + + - name: Update the version to .dev + id: update_version + run: | + version_file="genpipes/__version__.py" + version_number=$(sed -n "s/__version__ = '\([^']*\)'/\1/p" $version_file) + if [[ "$version_number" != *.dev ]]; then + echo "__version__ = '${version_number}.dev'" > $version_file + fi + + - name: Commit changes + run: | + git add genpipes/__version__.py + git commit -m "Dev Version update" + git push --force-with-lease diff --git a/.gitignore b/.gitignore index 898f0b6..ae221bb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ venv __pycache__/ .DS_Store .pdm* +pdm* dist/ # Tests diff --git a/pt_cli/cli.py b/pt_cli/cli.py index f0667ef..11d9ea1 100755 --- a/pt_cli/cli.py +++ b/pt_cli/cli.py @@ -25,7 +25,9 @@ UnDelete, Deprecate, UnDeprecate, - Curate + Curate, + GetID, + Location, ) from .__version__ import __version__ @@ -170,6 +172,9 @@ def projects(parsed_local): UnDeprecate(connection_obj=connector_session, subparser=subparser) Curate(connection_obj=connector_session, subparser=subparser) + getid_subparser = GetID(subparser).subparser + Location(connection_obj=connector_session, subparser=getid_subparser) + shtab.add_argument_to(parser, ["-s", "--print-completion"]) diff --git a/pt_cli/connect.py b/pt_cli/connect.py index f1406e6..3776cbd 100755 --- a/pt_cli/connect.py +++ b/pt_cli/connect.py @@ -10,7 +10,6 @@ import requests import bs4 -import lxml logger = logging.getLogger(__name__) @@ -128,7 +127,7 @@ def maybe_json(self, data): return loads except json.decoder.JSONDecodeError: if isinstance(data, str): - soup = bs4.BeautifulSoup(data, features="lxml") + soup = bs4.BeautifulSoup(data, features="html5lib") if soup.get_text().startswith("----------"): sys.stdout.write(soup.get_text()) elif soup.get_text().startswith("Welcome"): diff --git a/pt_cli/tools.py b/pt_cli/tools.py index 20e61a9..c01ca53 100644 --- a/pt_cli/tools.py +++ b/pt_cli/tools.py @@ -465,7 +465,7 @@ def json_to_unanalyzed(self): unanalyzed = self.unanalyzed if not self.output_file: if isinstance(unanalyzed, str): - soup = bs4.BeautifulSoup(unanalyzed, features="lxml") + soup = bs4.BeautifulSoup(unanalyzed, features="html5lib") return sys.stdout.write(soup.get_text()) # else case, not explicitely written return sys.stdout.write(json.dumps(unanalyzed)) @@ -489,6 +489,20 @@ def func(self, parsed_args): self.output_file = parsed_args.output self.json_to_unanalyzed() +class Undelivered(AddCMD): + """ + Undelivered is a sub-command of Digest subparser using base AddCMD class + """ + __tool_name__ = 'undelivered' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.parsed_input = None + self.output_file = None + + def help(self): + return "Will return undelivered Samples name/ID or Readsets name/ID" + + class Delivery(AddCMD): """ Delivery is a sub-command of Digest subparser using base AddCMD class @@ -509,10 +523,9 @@ def arguments(self): self.parser.add_argument('--specimen_id', help='Specimen ID to be selected', nargs='+') self.parser.add_argument('--sample_id', help='Sample ID to be selected', nargs='+') self.parser.add_argument('--readset_id', help='Readset ID to be selected', nargs='+') - self.parser.add_argument('--experiment_nucleic_acid_type', help="Experiment nucleic_acid_type characterizing the Samples/Readsets (RNA or DNA)", required=False) + self.parser.add_argument('--experiment_nucleic_acid_type', help="Experiment nucleic_acid_type characterizing the Samples/Readsets (RNA or DNA)", required=True) self.parser.add_argument('--endpoint', help="Endpoint in which data is located", required=True) self.parser.add_argument('--output', '-o', help="Name of output file (Default: terminal), formatted as Json file with sample/readset and endpoint") - # self.parser.add_argument('--input-json', help="Json file with all parameters") @property def delivery(self): @@ -564,14 +577,14 @@ def json_to_delivery(self): delivery = self.delivery if not self.output_file: if isinstance(delivery, str): - soup = bs4.BeautifulSoup(delivery, features="lxml") + soup = bs4.BeautifulSoup(delivery, features="html5lib") return sys.stdout.write(soup.get_text()) # else case, not explicitely written - return sys.stdout.write(json.dumps(delivery)) + return sys.stdout.write(json.dumps(delivery["DB_ACTION_OUTPUT"])) if not delivery: raise EmptyGetError with open(self.output_file, "w", encoding="utf-8") as out_pair_file: - json.dump(delivery, out_pair_file, ensure_ascii=False, indent=4) + json.dump(delivery["DB_ACTION_OUTPUT"], out_pair_file, ensure_ascii=False, indent=4) logger.info(f"Delivery file written to {self.output_file}") def func(self, parsed_args): @@ -950,3 +963,73 @@ def func(self, parsed_args): pass else: sys.stdout.write("\n".join(response["DB_ACTION_OUTPUT"])) + +class GetID: + """ + GetID is a subparser of the client in which you can query per table entries to get their IDs + """ + __tool_name__ = 'getid' + + def __init__(self, subparser=argparse.ArgumentParser().add_subparsers()): + self.subparser = subparser.add_parser(self.__tool_name__, help=self.help(), add_help=True).add_subparsers() + + def help(self): + """ + :return: the tool help string + """ + return f"All {self.__tool_name__} sub commands, those encapsulate all tables from the database to be queried and get the ID. Use 'pt_cli {self.__tool_name__} --help' to see more details." + + +class Location(AddCMD): + """ + Location is a sub-command of GetID subparser using base AddCMD class + """ + __tool_name__ = 'location' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.parsed_input = None + self.output_file = None + + def help(self): + return "Will return location ID based on location endpoint and file name" + + def arguments(self): + self.parser.add_argument('--endpoint', help='Endpoint in which data is located', required=True) + self.parser.add_argument('--file_name', help='File Name linked to the location', required=True) + + @property + def get_location(self): + ''' + Returns a list of location IDs of GenPipes of the API call for get_location + :return: + ''' + return self.post('project/get_location', data=self.parsed_input) + + def jsonify_input(self, parsed_args): + ''' + :return: jsonified input args + ''' + json = { + "location_endpoint": parsed_args.endpoint, + "file_name": parsed_args.file_name + } + + return json + + + def func(self, parsed_args): + super().func(parsed_args) + # Dev case when using --data-file + self.parsed_input = self.data() + + # When --data-file is empty + if not self.parsed_input: + self.parsed_input = json.dumps(self.jsonify_input(parsed_args), ensure_ascii=False, indent=4) + if not self.parsed_input: + raise BadArgumentError + + get_location = self.get_location + if isinstance(get_location, str): + soup = bs4.BeautifulSoup(get_location, features="html5lib") + return sys.stdout.write(soup.get_text()) + return sys.stdout.write(''.join(get_location["DB_ACTION_OUTPUT"])) diff --git a/pyproject.toml b/pyproject.toml index 3f7f7fd..9c270ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "pyyaml>=6.0", "requests>=2.28", "beautifulsoup4>=4.12", - "lxml", + "html5lib", "shtab", ]