Skip to content

Commit

Permalink
Version v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisgKent committed Dec 14, 2023
1 parent ad60246 commit 785a6c4
Show file tree
Hide file tree
Showing 13 changed files with 773 additions and 399 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ This is the tooling for working with the primerschemes index

The main purpose of info.json is to store vital metadata, which is parsed into the repo-manifest.

### Create

The create command has two main modes of use.
- Where the scheme directory is provided, and used to parse; --primerbed, --reference, --configpath, --algorithmversion.
- The options are provided via the CLI

If a field cannot be parsed via the scheme directory, it will require it to be spesified via the CLI.
- CLI args always override the parsed args.

Binary file modified dist/primal_page-1.0.0-py3-none-any.whl
Binary file not shown.
Binary file modified dist/primal_page-1.0.0.tar.gz
Binary file not shown.
333 changes: 324 additions & 9 deletions index.json

Large diffs are not rendered by default.

657 changes: 334 additions & 323 deletions poetry.lock

Large diffs are not rendered by default.

Binary file modified primal_page/__pycache__/build_index.cpython-311.pyc
Binary file not shown.
Binary file modified primal_page/__pycache__/main.cpython-311.pyc
Binary file not shown.
Binary file modified primal_page/__pycache__/schemas.cpython-311.pyc
Binary file not shown.
26 changes: 22 additions & 4 deletions primal_page/build_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def parse_version(
repo_url, scheme_name, length, version.name, "info.json", pclass
)

# Check the hashes in the config.json file match the generated hashes
# Check the hashes in the info.json file match the generated hashes
if version_dict["primer_bed_md5"] != info_dict["primer_bed_md5"]:
raise ValueError(
f"Hash mismatch for {version_dict['primer_bed_md5']}. Expected {version_dict['primer_bed_md5']} but got {info_dict['primer_bed_md5']}"
Expand Down Expand Up @@ -165,15 +165,33 @@ def check_consistency(existing_json, new_json):
)


def create_index(server_url, repo_url):
import json
import pathlib


def create_index(server_url, repo_url, parent_dir=pathlib.Path(".")):
"""
Create an index JSON file for the given server and repository URLs.
Args:
server_url (str): The URL of the server.
repo_url (str): The URL of the repository.
parent_dir (str, optional): The parent directory path containing the primerscheme dir. index.json will be writem to parent_dir/index.json Defaults to ".".
Returns:
bool: True if the index JSON file is created successfully, False otherwise.
"""
# For any Scheme, we can generate a JSON file with the following format:
json_dict = dict()
# Ensure the parent_dir is a pathlib.Path
if type(parent_dir) == str:
parent_dir = pathlib.Path(parent_dir)
# Parse panels and schemes
pclasses = ["primerschemes"]
for pclass in pclasses:
# Create a dict to hold all the pclass data
pclass_dict = dict()
for path in pathlib.Path(pclass).iterdir():
for path in (parent_dir / pclass).iterdir():
# Only add directories
if not path.is_dir() or path.name.startswith("."):
continue
Expand All @@ -188,7 +206,6 @@ def create_index(server_url, repo_url):
# Read in the existing index.json file
with open("index.json") as f:
existing_json_dict = json.load(f)

# Check persistence of existing files
check_consistency(existing_json_dict, json_dict)

Expand All @@ -201,4 +218,5 @@ def create_index(server_url, repo_url):
if __name__ == "__main__":
server_url = sys.argv[1]
repo_url = sys.argv[2]

create_index(server_url, repo_url)
61 changes: 38 additions & 23 deletions primal_page/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import shutil
import hashlib
import json
import github
import os
from typing import Optional
import re

from primal_page.build_index import create_index
from primal_page.schemas import PrimerClass, SchemeStatus, Info
from primal_page.schemas import (
PrimerClass,
SchemeStatus,
Info,
determine_primername_version,
PrimerNameVersion,
)

SCHEMENAME_PATTERN = r"^[a-z0-9][a-z0-9-]*[a-z0-9]$"
VERSION_PATTERN = r"^v\d+\.\d+\.\d+$"

# Create the typer app
app = typer.Typer()
Expand Down Expand Up @@ -60,7 +65,7 @@ def hashfile(fname: pathlib.Path) -> str:
return hash_md5.hexdigest()


def validate_primer_bed(primer_bed: pathlib.Path, fix: bool = False):
def validate_primer_bed(primer_bed: pathlib.Path, fix: bool = False) -> bool:
"""Validate a primer bed file"""
with open(primer_bed, "r") as f:
for lineindex, line in enumerate(f.readlines()):
Expand All @@ -73,14 +78,20 @@ def validate_primer_bed(primer_bed: pathlib.Path, fix: bool = False):
)

# Check for valid primername
primername = data[3].split("_")
if len(primername) == 4 and primername[-1].isdigit():
# Valid name
pass
else:
raise ValueError(
f"Line {lineindex} in {primer_bed} does not have a valid primername '{data[3]}'"
)
raw_primername = data[3].strip()

match determine_primername_version(raw_primername):
case PrimerNameVersion.V1:
# Valid name
pass
case PrimerNameVersion.V2:
# Valid version 2 name
pass
case PrimerNameVersion.INVALID:
raise ValueError(
f"Line {lineindex} in {primer_bed} does not have a valid primername '{raw_primername}'"
)

return True


Expand Down Expand Up @@ -219,14 +230,14 @@ def create(
"artic network",
],
primerbed: Annotated[
pathlib.Path,
Optional[pathlib.Path],
typer.Option(
help="Manually specify the primer bed file, default is *primer.bed",
readable=True,
),
] = None,
reference: Annotated[
pathlib.Path,
Optional[pathlib.Path],
typer.Option(
help="Manually specify the referance.fasta file, default is *.fasta",
readable=True,
Expand All @@ -237,19 +248,20 @@ def create(
typer.Option(help="Where to output the scheme", writable=True),
] = pathlib.Path("primerschemes"),
configpath: Annotated[
pathlib.Path,
Optional[pathlib.Path],
typer.Option(
help="Where the config.json file is located", readable=True, exists=True
),
] = None,
algorithmversion: Annotated[
str, typer.Option(help="The version of primalscheme or other")
Optional[str], typer.Option(help="The version of primalscheme or other")
] = None,
description: Annotated[
str, typer.Option(help="A description of the scheme")
Optional[str], typer.Option(help="A description of the scheme")
] = None,
derivedfrom: Annotated[
str, typer.Option(help="Which scheme has this scheme been derived from")
Optional[str],
typer.Option(help="Which scheme has this scheme been derived from"),
] = None,
primerclass: Annotated[
PrimerClass, typer.Option(help="The primer class")
Expand Down Expand Up @@ -555,16 +567,19 @@ def remove_citation(
def build_index(
gitaccount: Annotated[
str,
typer.Argument(help="The name of the github account"),
typer.Option(help="The name of the github account"),
] = "quick-lab",
gitserver: Annotated[
str,
typer.Argument(help="The name of the github server"),
typer.Option(help="The name of the github server"),
] = "https://github.com/",
parentdir: Annotated[
pathlib.Path, typer.Option(help="The parent directory")
] = pathlib.Path("."),
):
"""Build an index.json file from all schemes in the directory"""

create_index(gitserver, gitaccount)
create_index(gitserver, gitaccount, parentdir)


@app.command()
Expand Down
45 changes: 45 additions & 0 deletions primal_page/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,51 @@
SCHEMENAME_PATTERN = r"^[a-z0-9][a-z0-9-]*[a-z0-9]$"
VERSION_PATTERN = r"^v\d+\.\d+\.\d+$"

# Primername versions
V2_PRIMERNAME = r"^[a-zA-Z0-9\-]+_[0-9]+_(LEFT|RIGHT)_[0-9]+$"
V1_PRIMERNAME = r"^[a-zA-Z0-9\-]+_[0-9]+_(LEFT|RIGHT)(_ALT|_alt)*$"


class PrimerNameVersion(Enum):
V1 = "v1"
V2 = "v2"
INVALID = "invalid" # Not applicable


def determine_primername_version(primername: str) -> PrimerNameVersion:
"""
Determine the primername version
:param primername: The primername to check
:return: The primername version
"""
if re.search(V2_PRIMERNAME, primername):
return PrimerNameVersion.V2
elif re.search(V1_PRIMERNAME, primername):
return PrimerNameVersion.V1
else:
return PrimerNameVersion.INVALID


def convert_v1_primernames_to_v2(primername: str) -> str:
"""
Convert a v1 primername to a v2 primername. Cannot handle alt primers
:param primername: The v1 primername
:return: The v2 primername
"""
# Check if this is a v1 primername
if determine_primername_version(primername) != PrimerNameVersion.V1:
raise ValueError(f"{primername} is not a valid v1 primername")

# Split the primername
data = primername.split("_")
# Remove the alt
if data[-1] == "alt" or data[-1] == "ALT":
raise ValueError(f"{primername} is a v1 alt primername, cannot convert")

data.append("0")
# Join back together
return "_".join(data)


class PrimerClass(Enum):
PRIMERSCHEMES = "primerschemes"
Expand Down
40 changes: 0 additions & 40 deletions primal_page/search.py

This file was deleted.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ typer = {extras = ["all"], version = "^0.9.0"}

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
typer-cli = {path = "../typer-cli"} # https://github.com/Patarimi/typer-cli/commit/f44b547a0b9ea71e5c7b2dc7d4c1ab1212394e5e

[build-system]
requires = ["poetry-core"]
Expand Down

0 comments on commit 785a6c4

Please sign in to comment.