From 70afc565d0f9e44e14ba3c32064111a4c537d82c Mon Sep 17 00:00:00 2001 From: Scott Small Date: Fri, 19 Jul 2024 10:43:40 -0700 Subject: [PATCH 1/5] Updated version.py and changelog for 1.6.0 release --- CHANGELOG.md | 8 ++++++++ s4/clarity/version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd5ca7..cfc4f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ Release History =============== +1.6.0 +- +- (#60) Added support for Python 3.12. +- (#55) Step placements can now be updated without having to clear existing placements first. +- (#53) Fix an issue that prevented `StepRunner.add_default_reagents()` from working correctly on Clarity 6.1+ if the step being run contains an archived reagent kit. +- (#51) Fix an issue that could cause log messages to become duplicated, or disappear, when `s4-clarity-lib` is being used in tandem with an LLTK/LITK on the same step in Clarity 6.0+. +- (#51) Added `lims.versions` and `lims.current_minor_version` properties. + 1.5.0 - - (#45) Added the `permitted_containers` property to `StepConfiguration`. diff --git a/s4/clarity/version.py b/s4/clarity/version.py index ed01537..d975afc 100644 --- a/s4/clarity/version.py +++ b/s4/clarity/version.py @@ -1,4 +1,4 @@ # Copyright 2019 Semaphore Solutions, Inc. # --------------------------------------------------------------------------- -__version__ = '1.5.0' +__version__ = '1.6.0' From ee002deb569c8a1fc49b3ca1aacf3114900450c2 Mon Sep 17 00:00:00 2001 From: Scott Small Date: Fri, 19 Jul 2024 10:45:09 -0700 Subject: [PATCH 2/5] Declare support for Python 3.12 in setup.py (#60) --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b6703b6..7b943bc 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,7 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: PyPy' ], install_requires=( From 2fef2349d84bb66010c51cdcdcb3b541c10e3520 Mon Sep 17 00:00:00 2001 From: Scott Small Date: Fri, 19 Jul 2024 10:46:02 -0700 Subject: [PATCH 3/5] Fix EPP log message duplication and missing log entries in Clarity 6+ when combining Python automations with LLTK/LITKs (#51) * Fix EPP log message duplication and missing log entries in Clarity 6+ when combining Python automations with LLTK/LITKs * Fix current_minor_version failure on Python 2 --- s4/clarity/lims.py | 31 +++++++++++++++++++++++++++++++ s4/clarity/scripts/stepepp.py | 17 +++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/s4/clarity/lims.py b/s4/clarity/lims.py index f4fb65e..3113807 100644 --- a/s4/clarity/lims.py +++ b/s4/clarity/lims.py @@ -3,6 +3,12 @@ import logging import re import time + +try: + import urllib.parse as urlparse # Python 3 +except ImportError: + import urlparse # Python 2 + from xml.etree import cElementTree as ETree import requests @@ -247,6 +253,31 @@ def properties(self): properties = root.findall("property") return dict( (property.get("name"), property.get("value")) for property in properties ) + @lazy_property + def versions(self): + """ + :type: list[dict] + """ + root = self.request("get", self.root_uri + "/..") + versions = root.findall("version") + return list({ + "uri": version.get("uri"), + "major": version.get("major"), + "minor": version.get("minor") + } for version in versions) + + @lazy_property + def current_minor_version(self): + """ + :type: str + """ + path = urlparse.urlparse(self.root_uri).path + current_major_version = [x for x in path.split("/") if x][-1] + root = self.request("get", self.root_uri + "/..") + xpath = "version[@major='%s']" % current_major_version + version = root.findall(xpath)[0] + return version.get("minor") + def raw_request(self, method, uri, **kwargs): """ :type method: str diff --git a/s4/clarity/scripts/stepepp.py b/s4/clarity/scripts/stepepp.py index 0a99bfd..15d2da1 100644 --- a/s4/clarity/scripts/stepepp.py +++ b/s4/clarity/scripts/stepepp.py @@ -53,11 +53,24 @@ def open_log_output_stream(self): Use step's logfile. """ if self.options.logfile: + + # Log files that are shared by Python and LLTK/LITKs on a step must + # use the same file name in order to prevent bugs. In Clarity 5 and + # earlier, the file name used by LLTK/LITKs is simply the limsid of + # the ResultFile. But in Clarity 6 and later it has "LogFile" + # appended to the name, and so we need to check the active Clarity + # version and adjust our behaviour accordingly. + + filename = self.options.logfile + revision = int(self.lims.current_minor_version[1:]) + if revision >= 31: # Clarity 6.0 has an API minor version of 31 + filename = "%s-LogFile" % filename + if self.options.log_type == 'html': - filename = "%s.html" % self.options.logfile + filename = "%s.html" % filename content_type = 'text/html' elif self.options.log_type == 'text': - filename = "%s.log" % self.options.logfile + filename = "%s.log" % filename content_type = 'text/plain' else: raise Exception("Unrecognized log type %s", self.options.log_type) From f47c5ca81ea49f4a5f046409357e0fb2bb544e50 Mon Sep 17 00:00:00 2001 From: Scott Small Date: Fri, 19 Jul 2024 10:46:30 -0700 Subject: [PATCH 4/5] StepRunner: ignore archived reagent kits when adding default reagents to a step (#53) * Fix EPP log message duplication and missing log entries in Clarity 6+ when combining Python automations with LLTK/LITKs * Fix current_minor_version failure on Python 2 * StepRunner: ignore archived reagent kits when adding default reagents to a step --- s4/clarity/steputils/step_runner.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/s4/clarity/steputils/step_runner.py b/s4/clarity/steputils/step_runner.py index c2937a1..be43465 100644 --- a/s4/clarity/steputils/step_runner.py +++ b/s4/clarity/steputils/step_runner.py @@ -340,10 +340,15 @@ def add_default_reagents(self): """ For every required reagent kit in the step, the first active lot will be selected. If there are no active lots it will be omitted. + + Archived kits will be ignored on Clarity 6.1 (api revision 32) and later. """ + revision = int(self.lims.current_minor_version[1:]) log.info("Adding default reagent lots.") lots = [] for kit in self.step.configuration.required_reagent_kits: + if revision >= 32 and kit.archived: + continue for lot in kit.related_reagent_lots: if lot.status == "ACTIVE": lots.append(lot) From 1c88ace305da7ca857e0b6b3304acaf5c82a21b1 Mon Sep 17 00:00:00 2001 From: Joe Rickwalder Date: Fri, 19 Jul 2024 10:47:42 -0700 Subject: [PATCH 5/5] check for placement node that matches uri before creating one (#55) * check for placement node that matches uri before creating one * add missing quotes, fix for py2 * remove f * Fix coveralls on Python 3.6 (#52) * Enable automated unit testing with Python 3.12 (#56) * Fix coveralls on Python 3.6 * Enable automated unit testing with Python 3.12 * Only trigger a production PyPi build on tags using semantic versioning (#57) * Use latest versions of GitHub workflow actions, build with Python 3.12 (#58) * Enable automated unit testing for Python 3.5 (#59) * Fix coveralls parallel builds (#61) --------- Co-authored-by: Scott Small --- s4/clarity/step.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/s4/clarity/step.py b/s4/clarity/step.py index 2878ba5..f8fb746 100644 --- a/s4/clarity/step.py +++ b/s4/clarity/step.py @@ -534,7 +534,9 @@ def create_placement(self, artifact, container, well_string): :param well_string: The location on the plate to place the artifact """ placement_root = self.xml_root.find("./output-placements") - placement_node = ETree.SubElement(placement_root, "output-placement", {"uri": artifact.uri}) + placement_node = self.xml_root.find("./output-placements/output-placement[@uri='" + artifact.uri + "']") + if not placement_node: + placement_node = ETree.SubElement(placement_root, "output-placement", {"uri": artifact.uri}) location_subnode = ETree.SubElement(placement_node, "location") ETree.SubElement(location_subnode, "container", {"uri": container.uri}) ETree.SubElement(location_subnode, "value").text = well_string