Skip to content

Commit

Permalink
Merge pull request #7 from m-d-grunnill/master
Browse files Browse the repository at this point in the history
1.3.0 relsease
  • Loading branch information
m-d-grunnill authored Nov 20, 2024
2 parents ea7b137 + 56829a3 commit 6d6da6f
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 36 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
release:
types: [published]

permissions:
contents: read

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
3 changes: 0 additions & 3 deletions beast2xml/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from beast2xml.beast2 import BEAST2XML

# Note that the version string must have the following format, otherwise it
# will not be found by the version() function in ../setup.py
__version__ = "1.1.0"

__all__ = ["BEAST2XML"]
116 changes: 105 additions & 11 deletions beast2xml/beast2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from beast2xml.date_utilities import date_to_decimal
import xml.etree.ElementTree as ET
import xml
import ete3

import warnings

from importlib.resources import files

Expand Down Expand Up @@ -110,16 +113,17 @@ def __init__(
self._age_by_full_id = {}
self._age_by_short_id = {}
self._date_unit = date_unit
self._initial_phylo_tree = None

@staticmethod
def find_elements(tree):
"""
Check that an XML tree has the required structure and return the found
Check that an XML file_path has the required structure and return the found
elements.
Parameters
----------
tree : xml.etree.ElementTree.Element
file_path : xml.etree.ElementTree.Element
Returns
Expand Down Expand Up @@ -321,7 +325,7 @@ def _to_xml_tree(
Specifying how often to write to the trace log file. If None, the value in the
template will be retained.
tree_log_every : int, default=None
Specifying how often to write to the tree log file. If None, the value in the
Specifying how often to write to the file_path log file. If None, the value in the
template will be retained.
screen_log_every : int, default=None
Specifying how often to write to the terminal (screen) log. If None, the
Expand All @@ -338,7 +342,7 @@ def _to_xml_tree(
Returns
-------
tree: xml.etree.ElementTree
file_path: xml.etree.ElementTree
ElementTree for running on BEAST
"""
if mimic_beauti:
Expand All @@ -352,18 +356,71 @@ def _to_xml_tree(
data = elements["data"]
data_id = data.get("id")
tree_logger_key = "./run/logger[@id='treelog.t:" + data_id + "']"
trait = elements["./run/state/tree/trait"]

# Delete any existing children of the data node.
delete_child_nodes(data)

trait = elements["./run/state/tree/trait"]

if not isinstance(default_age, (float, int)):
raise TypeError("The default age must be an integer or float.")

sequences = self._sequences
age_by_short_id = deepcopy(self._age_by_short_id)
if self._initial_phylo_tree is not None:
tip_set_diffs = self.set_diffs_initial_tree_and_sequences()
if tip_set_diffs["in initial tree"]:
raise ValueError(
"Initial tree has additional sequences to the ones you have added."
)
if tip_set_diffs["in sequences"]:
warnings.warn(
"\n".join(
[
"One or more you have added sequences are not represented in the initial tree you gave.",
"These sequences will not be added to the xml being generated.",
"Use method set_diffs_initial_tree_and_sequences to view these.",
]
)
)
sequences = Reads(
[
sequence
for sequence in self._sequences
if sequence.id not in tip_set_diffs["in sequences"]
]
)
age_by_short_id = {
key: age
for key, age in self._age_by_short_id.items()
if key not in tip_set_diffs["in sequences"]
}

initial_tree_nodes = self._tree.findall("./run/init")
if len(initial_tree_nodes) == 0:
raise ValueError("Template has no initial tree.")
if len(initial_tree_nodes) > 1:
raise ValueError(
"More than one intial tree is in the template xml BEAST2-xml only supports template xmls with one initial tree."
)
elements["run"].remove(initial_tree_nodes[0])
newick_tree = self._initial_phylo_tree.write(
format=self._initial_phylo_tree_format
)
replacement = ET.SubElement(
elements["run"],
"init",
spec="beast.util.TreeParser",
id="NewickTree.t:" + data_id,
initial="@Tree.t:" + data_id,
taxa="@" + data_id,
IsLabelledNewick="true",
adjustTipHeights="false",
newick=newick_tree,
)

# Add in all sequences.
for sequence in sorted(
self._sequences
sequences
): # Sorting adds the sequences alphabetically like in BEAUti.
seq_id = sequence.id
short_id = seq_id.split()[0]
Expand All @@ -381,7 +438,7 @@ def _to_xml_tree(
)

trait_order = [
sequence.id.split()[0] for sequence in self._sequences
sequence.id.split()[0] for sequence in sequences
] # ensures order is the same as BEAUti's.
trait_text = [
short_id + "=" + str(age_by_short_id[short_id]) for short_id in trait_order
Expand Down Expand Up @@ -478,7 +535,7 @@ def to_string(
Specifying how often to write to the trace log file. If None, the value in the
template will be retained.
tree_log_every: int, default=None
Specifying how often to write to the tree log file. If None, the value in the
Specifying how often to write to the file_path log file. If None, the value in the
template will be retained.
screen_log_every: int, default=None
Specifying how often to write to the terminal (screen) log. If None, the
Expand All @@ -495,7 +552,7 @@ def to_string(
Returns
-------
tree: str
file_path: str
String representation of xml.etree.ElementTree for running on BEAST
"""
tree = self._to_xml_tree(
Expand Down Expand Up @@ -555,7 +612,7 @@ def to_xml(
Specifying how often to write to the trace log file. If None, the value in the
template will be retained.
tree_log_every: int, default=None
Specifying how often to write to the tree log file. If None, the value in the
Specifying how often to write to the file_path log file. If None, the value in the
template will be retained.
screen_log_every: int, default=None
Specifying how often to write to the terminal (screen) log. If None, the
Expand Down Expand Up @@ -859,3 +916,40 @@ def add_rate_change_times(self, parameter, times):
self.change_parameter_state_node(
self._rate_change_to_param_dict[parameter], dimension=dimensions
)

def add_initial_tree(self, file_path, format=1):
"""
Add initial newick tree.
Parameters
----------
file_path: str
Path to the newick tree file.
format: int, default 1
Format of the newick tree file:
0 flexible with support values
1 flexible with internal node names
2 all branches + leaf names + internal supports
3 all branches + all names
4 leaf branches + leaf names
5 internal and leaf branches + leaf names
6 internal branches + leaf names
7 leaf branches + all names
8 all names
9 leaf names
100 topology only
Returns
-------
None
"""
self._initial_phylo_tree = ete3.Tree(file_path, format=format)
self._initial_phylo_tree_format = format

def set_diffs_initial_tree_and_sequences(self):
tree_tips = set(self._initial_phylo_tree.get_leaf_names())
sequence_tips = set([sequence.id for sequence in self._sequences])
return {
"in initial tree": tree_tips - sequence_tips,
"in sequences": sequence_tips - tree_tips,
}
31 changes: 9 additions & 22 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,14 @@
from setuptools import setup


# Modified from http://stackoverflow.com/questions/2058802/
# how-can-i-get-the-version-defined-in-setup-py-setuptools-in-my-package
def version():
import os
import re

init = os.path.join("beast2xml", "__init__.py")
with open(init) as fp:
initData = fp.read()
match = re.search(r"^__version__ = ['\"]([^'\"]+)['\"]", initData, re.M)
if match:
return match.group(1)
else:
raise RuntimeError("Unable to find version string in %r." % init)


setup(
name="beast2-xml",
version=version(),
version="1.3.0",
packages=["beast2xml"],
package_data={"beast2xml": ["templates/*.xml"]},
url="https://github.com/acorg/beast2-xml",
download_url="https://github.com/acorg/beast2-xml",
author="Terry Jones",
author="Terry Jonesl",
author_email="[email protected]",
keywords=["BEAST2", "XML"],
classifiers=[
Expand All @@ -44,8 +28,11 @@ def version():
long_description=("Please see https://github.com/acorg/beast2-xml for details."),
license="MIT",
scripts=["bin/beast2-xml.py", "bin/beast2-xml-version.py"],
install_requires=["dark-matter>=1.1.28",
'pandas>=2.2.2',
'python=3.10',
'six'],
install_requires=[
"dark-matter>=1.1.28",
"pandas>=2.2.2",
"python=3.10",
"ete3",
"six",
],
)

0 comments on commit 6d6da6f

Please sign in to comment.