From be1eec9cbfafaf6f71c5f18fd1c53c9e67571ae5 Mon Sep 17 00:00:00 2001 From: tcezard Date: Fri, 9 Jun 2017 23:06:06 +0100 Subject: [PATCH 1/4] Change name of module --- docs/conf.py | 2 +- examples/attach_delivery_report.py | 31 -------- examples/epp_script.py | 64 --------------- examples/get_application.py | 26 ------- examples/get_artifacts.py | 49 ------------ examples/get_containers.py | 54 ------------- examples/get_labs.py | 39 ---------- examples/get_processes.py | 29 ------- examples/get_projects.py | 51 ------------ examples/get_samples.py | 62 --------------- examples/get_samples2.py | 27 ------- examples/set_project_queued.py | 30 ------- examples/set_sample_name.py | 31 -------- genologics/__init__.py | 1 + genologics/constants.py | 55 ------------- {genologics => pyclarity_lims}/config.py | 16 ++-- pyclarity_lims/constants.py | 55 +++++++++++++ {genologics => pyclarity_lims}/descriptors.py | 10 +-- {genologics => pyclarity_lims}/entities.py | 6 +- {genologics => pyclarity_lims}/epp.py | 12 +-- {genologics => pyclarity_lims}/lims.py | 2 +- {genologics => pyclarity_lims}/lims_utils.py | 6 +- {genologics => pyclarity_lims}/version.py | 0 setup.cfg | 4 +- setup.py | 78 ++++++++++--------- tests/test_descriptors.py | 24 +++--- tests/test_entities.py | 26 +++---- tests/test_lims.py | 12 +-- tests/to_rewrite_test_logging.py | 2 +- 29 files changed, 157 insertions(+), 647 deletions(-) delete mode 100644 examples/attach_delivery_report.py delete mode 100644 examples/epp_script.py delete mode 100644 examples/get_application.py delete mode 100644 examples/get_artifacts.py delete mode 100644 examples/get_containers.py delete mode 100644 examples/get_labs.py delete mode 100644 examples/get_processes.py delete mode 100644 examples/get_projects.py delete mode 100644 examples/get_samples.py delete mode 100644 examples/get_samples2.py delete mode 100644 examples/set_project_queued.py delete mode 100644 examples/set_sample_name.py delete mode 100644 genologics/constants.py rename {genologics => pyclarity_lims}/config.py (72%) create mode 100644 pyclarity_lims/constants.py rename {genologics => pyclarity_lims}/descriptors.py (98%) rename {genologics => pyclarity_lims}/entities.py (99%) rename {genologics => pyclarity_lims}/epp.py (97%) rename {genologics => pyclarity_lims}/lims.py (99%) rename {genologics => pyclarity_lims}/lims_utils.py (95%) rename {genologics => pyclarity_lims}/version.py (100%) diff --git a/docs/conf.py b/docs/conf.py index 1ebd72bc..0f159283 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -218,7 +218,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'genologics', u'Genologics Documentation', + ('index', 'pyclarity_lims', u'Genologics Documentation', [u'Per Kraulis, Johannes Alneberg'], 1) ] diff --git a/examples/attach_delivery_report.py b/examples/attach_delivery_report.py deleted file mode 100644 index 1daf671a..00000000 --- a/examples/attach_delivery_report.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Attach customer delivery report to LIMS - - - -Roman Valls Guimera, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs -from pprint import pprint -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -project = Project(lims, id="P193") - -print 'UDFs:' -pprint(project.udf.items()) - -print 'files:' -for file in project.files: - print file.content_location - -project.udf['Delivery Report'] = "http://example.com/delivery_note.pdf" -project.put() diff --git a/examples/epp_script.py b/examples/epp_script.py deleted file mode 100644 index c413c3c9..00000000 --- a/examples/epp_script.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -"""EPP example script for Clarity LIMS, written in Python - -Usage example: Trigger from Clarity with command -bash -c "PATH/TO/INSTALLED/SCRIPT ---log {compoundOutputFileLuidN} ---pid {processLuid} ---file PATH_TO_FILE_TO_UPLOAD -" - - - -Johannes Alneberg, Science for Life Laboratory, Stockholm, Sweden. -""" -from argparse import ArgumentParser -from genologics.lims import Lims -from genologics.entities import Process -from genologics.config import BASEURI,USERNAME,PASSWORD -from genologics.epp import EppLogger, attach_file -import sys - -def main(lims,pid,file): - """Uploads a given file to the first output artifact of the process - - lims: The LIMS instance - pid: Process Lims id - file: File to be attached - """ - p=Process(lims,id=pid) - - # Fetch all input-output artifact pairs - io = p.input_output_maps - - # Filter them so that only PerInput output artifacts remains - io_filtered = filter(lambda (x,y): y['output-generation-type']=='PerInput',io) - - # Fetch the first input-output artifact pair - (input,output) = io_filtered[0] - - # Instantiate the output artifact - output_artifact = Artifact(output['limsid']) - - # Attach the file - attach_file(args.file,output_artifact) - - -if __name__ == "__main__": - parser = ArgumentParser() - # Arguments that are useful in all EPP scripts - parser.add_argument("--log",default=sys.stdout, - help="Log file") - - # Arguments specific for this scripts task - parser.add_argument("--pid", help="Process id") - parser.add_argument("--file", help="File to upload") - - args = parser.parse_args() - - # Log everything to log argument - with EppLogger(args.log): - lims = Lims(BASEURI,USERNAME,PASSWORD) - lims.check_version() - - main(lims,args.pid,args.file) diff --git a/examples/get_application.py b/examples/get_application.py deleted file mode 100644 index ad8e71a8..00000000 --- a/examples/get_application.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Attach customer delivery report to LIMS - - - -Roman Valls Guimera, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs -from pprint import pprint -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -project = Project(lims, id="P193") - -print 'UDFs:' -pprint(project.udf.items()) - -print project.udf['Application'] diff --git a/examples/get_artifacts.py b/examples/get_artifacts.py deleted file mode 100644 index 3c640cdc..00000000 --- a/examples/get_artifacts.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Get artifacts and artifact info. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs - -from genologics.lims import Lims - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all artifacts. -## artifacts = lims.get_artifacts() -## print len(artifacts), 'total artifacts' - -# Get lists of artifacts with different QC flags -## artifacts = lims.get_artifacts(qc_flag='UNKNOWN') -## print len(artifacts), 'QC UNKNOWN artifacts' -## artifacts = lims.get_artifacts(qc_flag='PASSED') -## print len(artifacts), 'QC PASSED artifacts' -## artifacts = lims.get_artifacts(qc_flag='FAILED') -## print len(artifacts), 'QC FAILED artifacts' - -## artifacts = lims.get_artifacts(working_flag=True) -## print len(artifacts), 'Working-flag True artifacts' - -name = 'jgr33' -artifacts = lims.get_artifacts(sample_name=name) -print len(artifacts), 'artifacts for sample name', name - -artifacts = lims.get_batch(artifacts) -for artifact in artifacts: - print artifact, artifact.name, artifact.state - -print -artifacts = lims.get_artifacts(qc_flag='PASSED') -print len(artifacts), 'QC PASSED artifacts' -artifacts = lims.get_batch(artifacts) -for artifact in artifacts: - print artifact, artifact.name, artifact.state diff --git a/examples/get_containers.py b/examples/get_containers.py deleted file mode 100644 index 99a5e117..00000000 --- a/examples/get_containers.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Get some containers. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all containers. -## containers = lims.get_containers() -## print len(containers), 'containers in total' - -## for state in ['Empty', 'Reagent-Only', 'Discarded', 'Populated']: -## containers = lims.get_containers(state=state) -## print len(containers), state, 'containers' - -containers = lims.get_containers(type='96 well plate') -print len(containers) - -container = containers[2] -print container, container.occupied_wells - -placements = container.get_placements() -for location, artifact in sorted(placements.iteritems()): - print location, artifact.name, id(artifact), repr(artifact), artifact.root - -containertype = container.type -print containertype, containertype.name, containertype.x_dimension, containertype.y_dimension - - - -containers = lims.get_containers(type='Illumina Flow Cell',state='Populated') -for container in containers: - print container.name - print container.id - print container.placements.keys() - arts=lims.get_artifacts(containername=container.name) - for art in arts: - print art.name - print art.type - print art.udf.items() - print art.parent_process.type.name diff --git a/examples/get_labs.py b/examples/get_labs.py deleted file mode 100644 index 6a737910..00000000 --- a/examples/get_labs.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Get labs and lab info. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all projects. -labs = lims.get_labs(name='SciLifeLab') -print len(labs), 'labs in total' -for lab in labs: - print lab, id(lab), lab.name, lab.uri, lab.id - print lab.shipping_address.items() - for key, value in lab.udf.items(): - if isinstance(value, unicode): - value = codecs.encode(value, 'UTF-8') - print ' ', key, '=', value - udt = lab.udt - if udt: - print 'UDT:', udt.udt - for key, value in udt.items(): - if isinstance(value, unicode): - value = codecs.encode(value, 'UTF-8') - print ' ', key, '=', value - -lab = Lab(lims, id='2') -print lab, id(lab), lab.name, lab.uri, lab.id diff --git a/examples/get_processes.py b/examples/get_processes.py deleted file mode 100644 index 6e2d59eb..00000000 --- a/examples/get_processes.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Get some processes. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all processes. -processes = lims.get_processes() -print len(processes), 'processes in total' - -process = Process(lims, id='QCF-PJK-120703-24-1140') -print process, process.id, process.type, process.type.name -for input, output in process.input_output_maps: - if input: - print 'input:', input.items() - if output: - print 'output:', output.items() diff --git a/examples/get_projects.py b/examples/get_projects.py deleted file mode 100644 index 47c102e7..00000000 --- a/examples/get_projects.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage example: Get some projects. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -import codecs - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all projects. -projects = lims.get_projects() -print len(projects), 'projects in total' - -# Get the list of all projects opened since May 30th 2012. -day = '2012-05-30' -projects = lims.get_projects(open_date=day) -print len(projects), 'projects opened since', day - -# Get the project with the specified LIMS id, and print some info. -project = Project(lims, id='P193') -print project, project.name, project.open_date, project.close_date - -print ' UDFs:' -for key, value in project.udf.items(): - if isinstance(value, unicode): - value = codecs.encode(value, 'UTF-8') - print ' ', key, '=', value - -udt = project.udt -print ' UDT:', udt.udt -for key, value in udt.items(): - if isinstance(value, unicode): - value = codecs.encode(value, 'UTF-8') - print ' ', key, '=', value - -print ' files:' -for file in project.files: - print file.id - print file.content_location - print file.original_location diff --git a/examples/get_samples.py b/examples/get_samples.py deleted file mode 100644 index e5ad890d..00000000 --- a/examples/get_samples.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage examples: Get some samples, and sample info. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. - -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the list of all samples. -samples = lims.get_samples() -print len(samples), 'samples in total' - -# Get the list of samples in the project with the LIMS id KLL60. -project = Project(lims, id='KRA61') -samples = lims.get_samples(projectlimsid=project.id) -print len(samples), 'samples in', project - -print -# Get the sample with the LIMS id KRA61A1 -sample = Sample(lims, id='KRA61A1') -print sample.id, sample.name, sample.date_received, sample.uri, -for key, value in sample.udf.items(): - print ' ', key, '=', value -for note in sample.notes: - print 'Note', note.uri, note.content -for file in sample.files: - print 'File', file.content_location - -# Get the sample with the name 'spruce_a'. -# Check that it is the sample as the previously obtained sample; -# not just equal, but exactly the same instance, courtesy of the Lims cache. -samples = lims.get_samples(name='spruce_a') -print samples[0].name, samples[0] is sample - -## # Get the samples having a UDF Color with values Blue or Orange. -samples = lims.get_samples(udf={'Color': ['Blue', 'Orange']}) -print len(samples) -for sample in samples: - print sample, sample.name, sample.udf['Color'] - -sample = samples[0] - -print -# Print the submitter (researcher) for the sample. -submitter = sample.submitter -print submitter, submitter.email, submitter.initials, submitter.lab - -print -# Print the artifact of the sample. -artifact = sample.artifact -print artifact, artifact.state, artifact.type, artifact.qc_flag diff --git a/examples/get_samples2.py b/examples/get_samples2.py deleted file mode 100644 index f66db09c..00000000 --- a/examples/get_samples2.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Usage examples: Get some samples, and sample info. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -from genologics.lims import * - -from genologics.config import BASEURI, USERNAME, PASSWORD -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -project = Project(lims, id='KRA61') -samples = lims.get_samples(projectlimsid=project.id) -print len(samples), 'samples in', project - -for sample in samples: - print sample, sample.name, sample.date_received, sample.artifact - -name = 'spruce_a' -artifacts = lims.get_artifacts(sample_name=name) -print len(artifacts), 'artifacts for sample', name -for artifact in artifacts: - print artifact, artifact.name, artifact.qc_flag diff --git a/examples/set_project_queued.py b/examples/set_project_queued.py deleted file mode 100644 index 2e407b1b..00000000 --- a/examples/set_project_queued.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Example usage: Set the UDF 'Queued' of a project. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -import datetime - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the project with the LIMS id KLL60, and print some info. -project = Project(lims, id='KLL60') -print project, project.name, project.open_date -print project.udf.items() - -d = datetime.date(2012,1,2) -print d - -project.udf['Queued'] = d -project.put() diff --git a/examples/set_sample_name.py b/examples/set_sample_name.py deleted file mode 100644 index a147f149..00000000 --- a/examples/set_sample_name.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Example usage: Set the name and a UDF of a sample. - - - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -""" - -from genologics.lims import * - -# Login parameters for connecting to a LIMS instance. -from genologics.config import BASEURI, USERNAME, PASSWORD - -# Create the LIMS interface instance, and check the connection and version. -lims = Lims(BASEURI, USERNAME, PASSWORD) -lims.check_version() - -# Get the sample with the given LIMS identifier, and output its current name. -sample = Sample(lims, id='JGR58A21') -print sample, sample.name - -sample.name = 'Joels extra-proper sample-20' - -# Set the value of one of the UDFs -sample.udf['Emmas field 2'] = 5 -for key, value in sample.udf.items(): - print ' ', key, '=', value - -sample.put() -print 'Updated sample', sample diff --git a/genologics/__init__.py b/genologics/__init__.py index e69de29b..fa60fe4d 100644 --- a/genologics/__init__.py +++ b/genologics/__init__.py @@ -0,0 +1 @@ +from pyclarity_lims import * \ No newline at end of file diff --git a/genologics/constants.py b/genologics/constants.py deleted file mode 100644 index 42f9bb4f..00000000 --- a/genologics/constants.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Python interface to GenoLogics LIMS via its REST API. - -Entities and their descriptors for the LIMS interface. - -Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. -Copyright (C) 2012 Per Kraulis -""" - -import re -from xml.etree import ElementTree - -_NSMAP = dict( - art='http://genologics.com/ri/artifact', - artgr='http://genologics.com/ri/artifactgroup', - cnf='http://genologics.com/ri/configuration', - con='http://genologics.com/ri/container', - ctp='http://genologics.com/ri/containertype', - exc='http://genologics.com/ri/exception', - file='http://genologics.com/ri/file', - inst='http://genologics.com/ri/instrument', - lab='http://genologics.com/ri/lab', - prc='http://genologics.com/ri/process', - prj='http://genologics.com/ri/project', - prop='http://genologics.com/ri/property', - protcnf='http://genologics.com/ri/protocolconfiguration', - protstepcnf='http://genologics.com/ri/stepconfiguration', - prx='http://genologics.com/ri/processexecution', - ptm='http://genologics.com/ri/processtemplate', - ptp='http://genologics.com/ri/processtype', - res='http://genologics.com/ri/researcher', - ri='http://genologics.com/ri', - rt='http://genologics.com/ri/routing', - rtp='http://genologics.com/ri/reagenttype', - kit='http://genologics.com/ri/reagentkit', - lot='http://genologics.com/ri/reagentlot', - smp='http://genologics.com/ri/sample', - stg='http://genologics.com/ri/stage', - stp='http://genologics.com/ri/step', - udf='http://genologics.com/ri/userdefined', - ver='http://genologics.com/ri/version', - wkfcnf='http://genologics.com/ri/workflowconfiguration' -) - -for prefix, uri in _NSMAP.items(): - ElementTree._namespace_map[uri] = prefix - -_NSPATTERN = re.compile(r'(\{)(.+?)(\})') - - -def nsmap(tag): - "Convert from normal XML-ish namespace tag to ElementTree variant." - parts = tag.split(':') - if len(parts) != 2: - raise ValueError("no namespace specifier in tag") - return "{%s}%s" % (_NSMAP[parts[0]], parts[1]) diff --git a/genologics/config.py b/pyclarity_lims/config.py similarity index 72% rename from genologics/config.py rename to pyclarity_lims/config.py index 11e03440..b4aed331 100644 --- a/genologics/config.py +++ b/pyclarity_lims/config.py @@ -6,10 +6,10 @@ ''' Usage: -from genologics.config import BASEURI, USERNAME, PASSWORD +from pyclarity_lims.config import BASEURI, USERNAME, PASSWORD Alternate Usage: -from genologics import config +from pyclarity_lims import config BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG = config.load_config(specified_config = ) ''' @@ -20,12 +20,12 @@ def get_config_info(config_file): config.readfp(open(config_file)) - BASEURI = config.get('genologics', 'BASEURI').rstrip() - USERNAME = config.get('genologics', 'USERNAME').rstrip() - PASSWORD = config.get('genologics', 'PASSWORD').rstrip() + BASEURI = config.get('pyclarity_lims', 'BASEURI').rstrip() + USERNAME = config.get('pyclarity_lims', 'USERNAME').rstrip() + PASSWORD = config.get('pyclarity_lims', 'PASSWORD').rstrip() - if config.has_section('genologics') and config.has_option('genologics','VERSION'): - VERSION = config.get('genologics', 'VERSION').rstrip() + if config.has_section('pyclarity_lims') and config.has_option('pyclarity_lims','VERSION'): + VERSION = config.get('pyclarity_lims', 'VERSION').rstrip() else: VERSION = 'v2' @@ -43,7 +43,7 @@ def load_config(specified_config = None): config = ConfigParser.SafeConfigParser() try: conf_file = config.read([os.path.expanduser('~/.genologicsrc'), '.genologicsrc', - 'genologics.conf', 'genologics.cfg', '/etc/genologics.conf']) + 'pyclarity_lims.conf', 'pyclarity_lims.cfg', '/etc/pyclarity_lims.conf']) # First config file found wins config_file = conf_file[0] diff --git a/pyclarity_lims/constants.py b/pyclarity_lims/constants.py new file mode 100644 index 00000000..7d53cdaa --- /dev/null +++ b/pyclarity_lims/constants.py @@ -0,0 +1,55 @@ +"""Python interface to GenoLogics LIMS via its REST API. + +Entities and their descriptors for the LIMS interface. + +Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. +Copyright (C) 2012 Per Kraulis +""" + +import re +from xml.etree import ElementTree + +_NSMAP = dict( + art='http://pyclarity_lims.com/ri/artifact', + artgr='http://pyclarity_lims.com/ri/artifactgroup', + cnf='http://pyclarity_lims.com/ri/configuration', + con='http://pyclarity_lims.com/ri/container', + ctp='http://pyclarity_lims.com/ri/containertype', + exc='http://pyclarity_lims.com/ri/exception', + file='http://pyclarity_lims.com/ri/file', + inst='http://pyclarity_lims.com/ri/instrument', + lab='http://pyclarity_lims.com/ri/lab', + prc='http://pyclarity_lims.com/ri/process', + prj='http://pyclarity_lims.com/ri/project', + prop='http://pyclarity_lims.com/ri/property', + protcnf='http://pyclarity_lims.com/ri/protocolconfiguration', + protstepcnf='http://pyclarity_lims.com/ri/stepconfiguration', + prx='http://pyclarity_lims.com/ri/processexecution', + ptm='http://pyclarity_lims.com/ri/processtemplate', + ptp='http://pyclarity_lims.com/ri/processtype', + res='http://pyclarity_lims.com/ri/researcher', + ri='http://pyclarity_lims.com/ri', + rt='http://pyclarity_lims.com/ri/routing', + rtp='http://pyclarity_lims.com/ri/reagenttype', + kit='http://pyclarity_lims.com/ri/reagentkit', + lot='http://pyclarity_lims.com/ri/reagentlot', + smp='http://pyclarity_lims.com/ri/sample', + stg='http://pyclarity_lims.com/ri/stage', + stp='http://pyclarity_lims.com/ri/step', + udf='http://pyclarity_lims.com/ri/userdefined', + ver='http://pyclarity_lims.com/ri/version', + wkfcnf='http://pyclarity_lims.com/ri/workflowconfiguration' +) + +for prefix, uri in _NSMAP.items(): + ElementTree._namespace_map[uri] = prefix + +_NSPATTERN = re.compile(r'(\{)(.+?)(\})') + + +def nsmap(tag): + "Convert from normal XML-ish namespace tag to ElementTree variant." + parts = tag.split(':') + if len(parts) != 2: + raise ValueError("no namespace specifier in tag") + return "{%s}%s" % (_NSMAP[parts[0]], parts[1]) diff --git a/genologics/descriptors.py b/pyclarity_lims/descriptors.py similarity index 98% rename from genologics/descriptors.py rename to pyclarity_lims/descriptors.py index 04c0e84f..e1f078d7 100644 --- a/genologics/descriptors.py +++ b/pyclarity_lims/descriptors.py @@ -6,7 +6,7 @@ Copyright (C) 2012 Per Kraulis """ -from genologics.constants import nsmap +from pyclarity_lims.constants import nsmap try: from urllib.parse import urlsplit, urlparse, parse_qs, urlunparse @@ -236,7 +236,7 @@ def _update_elems(self): self._elems = self.rootnode(self.instance).findall('placement') def _parse_element(self, element, **kwargs): - from genologics.entities import Artifact + from pyclarity_lims.entities import Artifact key = element.find('value').text dict.__setitem__(self, key, Artifact(self.instance.lims, uri=element.attrib['uri'])) @@ -457,7 +457,7 @@ def _parse_element(self, element, lims, **kwargs): list.append(self, (input, output)) def _get_dict(self, lims, node): - from genologics.entities import Artifact, Process + from pyclarity_lims.entities import Artifact, Process if node is None: return None result = dict() for key in ['limsid', 'output-type', 'output-generation-type']: @@ -500,7 +500,7 @@ def _create_new_node(self, value): return node def _parse_element(self, element, lims, **kwargs): - from genologics.entities import Artifact, Container + from pyclarity_lims.entities import Artifact, Container input = Artifact(lims, uri=element.attrib['uri']) loc = element.find('location') location = (None, None) @@ -667,7 +667,7 @@ class LocationDescriptor(TagDescriptor): """ def __get__(self, instance, cls): - from genologics.entities import Container + from pyclarity_lims.entities import Container instance.get() node = self.rootnode(instance).find(self.tag) uri = node.find('container').attrib['uri'] diff --git a/genologics/entities.py b/pyclarity_lims/entities.py similarity index 99% rename from genologics/entities.py rename to pyclarity_lims/entities.py index b293e9fa..8a468a60 100644 --- a/genologics/entities.py +++ b/pyclarity_lims/entities.py @@ -5,8 +5,8 @@ Per Kraulis, Science for Life Laboratory, Stockholm, Sweden. Copyright (C) 2012 Per Kraulis """ -from genologics.constants import nsmap -from genologics.descriptors import StringDescriptor, UdfDictionaryDescriptor, \ +from pyclarity_lims.constants import nsmap +from pyclarity_lims.descriptors import StringDescriptor, UdfDictionaryDescriptor, \ UdtDictionaryDescriptor, ExternalidListDescriptor, EntityDescriptor, BooleanDescriptor, \ DimensionDescriptor, IntegerDescriptor, \ InputOutputMapList, LocationDescriptor, IntegerAttributeDescriptor, \ @@ -805,7 +805,7 @@ class StepDetails(Entity): class Step(Entity): - "Step, as defined by the genologics API." + "Step, as defined by the pyclarity_lims API." _URI = 'steps' _PREFIX = 'stp' diff --git a/genologics/epp.py b/pyclarity_lims/epp.py similarity index 97% rename from genologics/epp.py rename to pyclarity_lims/epp.py index 08e18a19..3b475db1 100644 --- a/genologics/epp.py +++ b/pyclarity_lims/epp.py @@ -14,8 +14,8 @@ from pkg_resources import DistributionNotFound from shutil import copy from requests import HTTPError -from genologics.entities import Artifact -from genologics.config import MAIN_LOG +from pyclarity_lims.entities import Artifact +from pyclarity_lims.config import MAIN_LOG from logging.handlers import RotatingFileHandler from time import strftime, localtime import csv @@ -59,17 +59,17 @@ class EppLogger(object): This context manager (CM) automatically logs what script that is executed, with what parameters it was executed and what version (including) commit - hash of the genologics package used. Since EPP scripts are often ran - automatically by the genologics LIMS client, the stdout and stderr is + hash of the pyclarity_lims package used. Since EPP scripts are often ran + automatically by the pyclarity_lims LIMS client, the stdout and stderr is captured and logged within this CM. Stderr is duplicated so that the last line can be shown in the GUI. In order to track multiple runs - of the same process from the genologics LIMS GUI, the previous log + of the same process from the pyclarity_lims LIMS GUI, the previous log files can be prepended. Also a main log file can be used that is supposed to be common for all scripts executed on the server. """ - PACKAGE = 'genologics' + PACKAGE = 'pyclarity_lims' def __enter__(self): logging.info('Executing file: {0}'.format(sys.argv[0])) logging.info('with parameters: {0}'.format(sys.argv[1:])) diff --git a/genologics/lims.py b/pyclarity_lims/lims.py similarity index 99% rename from genologics/lims.py rename to pyclarity_lims/lims.py index 0dd05df5..4f64117a 100644 --- a/genologics/lims.py +++ b/pyclarity_lims/lims.py @@ -52,7 +52,7 @@ class Lims(object): def __init__(self, baseuri, username, password, version=VERSION): """baseuri: Base URI for the GenoLogics server, excluding the 'api' or version parts! - For example: https://genologics.scilifelab.se:8443/ + For example: https://pyclarity_lims.scilifelab.se:8443/ username: The account name of the user to login as. password: The password for the user account to login as. version: The optional LIMS API version, by default 'v2' diff --git a/genologics/lims_utils.py b/pyclarity_lims/lims_utils.py similarity index 95% rename from genologics/lims_utils.py rename to pyclarity_lims/lims_utils.py index 4c8eff29..d42a4364 100644 --- a/genologics/lims_utils.py +++ b/pyclarity_lims/lims_utils.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -from genologics.epp import EppLogger +from pyclarity_lims.epp import EppLogger import logging import sys import os -from genologics.lims import * -from genologics.config import BASEURI, USERNAME, PASSWORD +from pyclarity_lims.lims import * +from pyclarity_lims.config import BASEURI, USERNAME, PASSWORD lims = Lims(BASEURI, USERNAME, PASSWORD) diff --git a/genologics/version.py b/pyclarity_lims/version.py similarity index 100% rename from genologics/version.py rename to pyclarity_lims/version.py diff --git a/setup.cfg b/setup.cfg index 49a81075..41b4263e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ -[egg_info] -tag_svn_revision = true +[zest.releaser] +tag-format = v%%(version)s \ No newline at end of file diff --git a/setup.py b/setup.py index 05fd4e67..3ab1e1c6 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,12 @@ from setuptools import setup, find_packages -from genologics.version import __version__ -import sys, os +from pyclarity_lims.version import __version__ import subprocess -import glob # Fetch version from git tags. # if git is not available (PyPi package), use stored version.py. try: - version = subprocess.Popen(["git", "describe", "--abbrev=0"],stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].rstrip() + version = subprocess.Popen(["git", "describe", "--abbrev=0"], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].rstrip() version = version.decode("utf-8") except: version = __version__ @@ -19,37 +17,41 @@ except: requires=["requests"] -setup(name='genologics', - version=version, - description="Python interface to the GenoLogics LIMS (Laboratory Information Management System) server via its REST API.", - long_description="""A basic module for interacting with the GenoLogics LIMS server via its REST API. - The goal is to provide simple access to the most common entities and their attributes in a reasonably Pythonic fashion.""", - classifiers=[ - "Development Status :: 4 - Beta", - "Environment :: Console", - "Intended Audience :: Developers", - "Intended Audience :: Healthcare Industry", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python", - "Topic :: Scientific/Engineering :: Medical Science Apps." - ], - keywords='genologics api rest', - author='Per Kraulis', - author_email='per.kraulis@scilifelab.se', - maintainer='Denis Moreno', - maintainer_email='denis.moreno@scilifelab.se', - url='https://github.com/scilifelab/genologics', - license='GPLv3', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - scripts=glob.glob("scripts/*.py"), - include_package_data=True, - zip_safe=False, - install_requires=[ - "requests" - ], - entry_points=""" - # -*- Entry points: -*- - """, - ) +setup(name='pyclarity_lims', + version=version, + description="Python interface to the Basespace-Clarity LIMS (Laboratory Information Management System) " + "server via its REST API.", + long_description="""A basic module for interacting with the Basespace-Clarity LIMS server via its REST API. + The goal is to provide simple access to the most common entities and their attributes in + a reasonably Pythonic fashion.""", + classifiers=[ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Healthcare Industry", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.6", + "Topic :: Scientific/Engineering :: Medical Science Apps." + ], + keywords='clarity api rest', + author='Per Kraulis', + author_email='per.kraulis@scilifelab.se', + maintainer='Timothee Cezard', + maintainer_email='timothee.cezard@ed.ac.uk', + url='https://github.com/EdinburghGenomics/pyclarity-lims', + license='GPLv3', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=False, + install_requires=[ + "requests" + ], + +) diff --git a/tests/test_descriptors.py b/tests/test_descriptors.py index 0791bfdc..63579d4e 100644 --- a/tests/test_descriptors.py +++ b/tests/test_descriptors.py @@ -5,12 +5,12 @@ import pytest -from genologics.constants import nsmap -from genologics.descriptors import StringDescriptor, StringAttributeDescriptor, StringListDescriptor, \ +from pyclarity_lims.constants import nsmap +from pyclarity_lims.descriptors import StringDescriptor, StringAttributeDescriptor, StringListDescriptor, \ StringDictionaryDescriptor, IntegerDescriptor, BooleanDescriptor, UdfDictionary, EntityDescriptor, \ InputOutputMapList, EntityListDescriptor, PlacementDictionary, EntityList, SubTagDictionary, ExternalidList -from genologics.entities import Artifact -from genologics.lims import Lims +from pyclarity_lims.entities import Artifact +from pyclarity_lims.lims import Lims if version_info[0] == 2: from mock import Mock @@ -253,14 +253,14 @@ def test__set__(self): class TestUdfDictionary(TestCase): def setUp(self): et = ElementTree.fromstring(""" - + stuff 42 true """) self.instance1 = Mock(root=et) et = ElementTree.fromstring(""" - + stuff 42 @@ -273,7 +273,7 @@ def setUp(self): self.dict_fail = UdfDictionary(self.instance2) self.empty_et = ElementTree.fromstring(""" - + """) def _get_udf_value(self, udf_dict, key): @@ -375,7 +375,7 @@ class TestPlacementDictionary(TestCase): def setUp(self): et = ElementTree.fromstring(""" - + A:1 @@ -400,7 +400,7 @@ class TestSubTagDictionary(TestCase): def setUp(self): et = ElementTree.fromstring(""" - + value1 @@ -488,7 +488,7 @@ def test_set(self): class TestInputOutputMapList(TestCase): def setUp(self): et = ElementTree.fromstring(""" - + @@ -513,8 +513,8 @@ class TestExternalidList(TestCase): def setUp(self): et = ElementTree.fromstring(""" - - + + """) self.lims = Lims('http://testgenologics.com:4040', username='test', password='password') diff --git a/tests/test_entities.py b/tests/test_entities.py index adaca7c9..72c0dc84 100644 --- a/tests/test_entities.py +++ b/tests/test_entities.py @@ -2,9 +2,9 @@ from unittest import TestCase from xml.etree import ElementTree -from genologics.entities import StepActions, Researcher, Artifact, \ +from pyclarity_lims.entities import StepActions, Researcher, Artifact, \ Step, StepPlacements, Container, Stage, ReagentKit, ReagentLot, Sample, Project -from genologics.lims import Lims +from pyclarity_lims.lims import Lims if version_info[0] == 2: from mock import patch, Mock @@ -16,7 +16,7 @@ ######## # Entities in XML generic_artifact_xml = """ - + test_sample1 Analyte Analyte @@ -36,7 +36,7 @@ """ generic_step_placements_xml = """ - + Step name @@ -59,7 +59,7 @@ """ generic_reagentkit_xml = """ - + regaentkitname reagentProvider www.reagentprovider.com @@ -67,7 +67,7 @@ """ generic_reagentlot_xml = """ - + kitname 100 @@ -80,7 +80,7 @@ 1 """ -generic_step_actions_xml = """ +generic_step_actions_xml = """ ... @@ -116,7 +116,7 @@ """ -generic_step_actions_no_escalation_xml = """ +generic_step_actions_no_escalation_xml = """ ... @@ -127,7 +127,7 @@ """ generic_sample_creation_xml = """ - + @@ -279,7 +279,7 @@ def test_parse_entity(self): assert r.archived == False def test_create_entity(self): - with patch('genologics.lims.requests.post', return_value=Mock(content=self.reagentkit_xml, status_code=201)): + with patch('pyclarity_lims.lims.requests.post', return_value=Mock(content=self.reagentkit_xml, status_code=201)): r = ReagentKit.create(self.lims, name='regaentkitname', supplier='reagentProvider', website='www.reagentprovider.com', archived=False) self.assertRaises(TypeError, ReagentKit.create, self.lims, error='test') @@ -300,7 +300,7 @@ def test_parse_entity(self): def test_create_entity(self): with patch('requests.Session.get', return_value=Mock(content=self.reagentkit_xml, status_code=200)): r = ReagentKit(uri=self.lims.get_uri('reagentkits', 'r1'), lims=self.lims) - with patch('genologics.lims.requests.post', + with patch('pyclarity_lims.lims.requests.post', return_value=Mock(content=self.reagentlot_xml, status_code=201)) as patch_post: l = ReagentLot.create( self.lims, @@ -319,7 +319,7 @@ class TestSample(TestEntities): sample_creation = generic_sample_creation_xml.format(url=url) def test_create_entity(self): - with patch('genologics.lims.requests.post', + with patch('pyclarity_lims.lims.requests.post', return_value=Mock(content=self.sample_creation, status_code=201)) as patch_post: l = Sample.create( self.lims, @@ -329,7 +329,7 @@ def test_create_entity(self): name='s1', ) data = ''' - + s1 diff --git a/tests/test_lims.py b/tests/test_lims.py index 5a01b675..ffa503bb 100644 --- a/tests/test_lims.py +++ b/tests/test_lims.py @@ -3,7 +3,7 @@ from requests.exceptions import HTTPError -from genologics.lims import Lims +from pyclarity_lims.lims import Lims try: callable(1) except NameError: # callable() doesn't exist in Python 3.0 and 3.1 @@ -24,16 +24,16 @@ class TestLims(TestCase): username = 'test' password = 'password' sample_xml = """ - + """.format(url=url) error_xml = """ - + Generic error message """ error_no_msg_xml = """ - + """ @@ -94,8 +94,8 @@ def test_post(self): def test_upload_new_file(self, mocked_open, mocked_isfile): lims = Lims(self.url, username=self.username, password=self.password) xml_intro = """""" - file_start = """""" - file_start2 = """""" + file_start = """""" + file_start2 = """""" attached = """ {url}/api/v2/samples/test_sample""" upload = """ filename_to_upload""" content_loc = """ sftp://{url}/opt/gls/clarity/users/glsftp/clarity/samples/test_sample/test""" diff --git a/tests/to_rewrite_test_logging.py b/tests/to_rewrite_test_logging.py index f9973294..dcc78a28 100644 --- a/tests/to_rewrite_test_logging.py +++ b/tests/to_rewrite_test_logging.py @@ -5,7 +5,7 @@ import os import sys from unittest import TestCase -from genologics.epp import EppLogger +from pyclarity_lims.epp import EppLogger file_path = os.path.realpath(__file__) test_dir_path = os.path.dirname(file_path) From b6416c2a8a6db6db19580c07f7ba3f24c6d6968b Mon Sep 17 00:00:00 2001 From: tcezard Date: Mon, 12 Jun 2017 10:39:15 +0100 Subject: [PATCH 2/4] Change more reference to genologics to pyclarity_lims --- .gitignore | 6 +- Changelogs.md | 5 + MANIFEST.in | 11 + README.md | 15 +- genologics/__init__.py | 3 +- genologics/entities.py | 1 + genologics/lims.py | 1 + pyclarity_lims/__init__.py | 6 + pyclarity_lims/config.py | 60 ----- pyclarity_lims/epp.py | 391 ------------------------------- pyclarity_lims/lims_utils.py | 79 ------- pyclarity_lims/version.py | 1 - requirements.txt | 2 - setup.py | 20 +- tests/to_rewrite_test_logging.py | 77 ------ 15 files changed, 39 insertions(+), 639 deletions(-) create mode 100644 Changelogs.md create mode 100644 MANIFEST.in create mode 100644 genologics/entities.py create mode 100644 genologics/lims.py create mode 100644 pyclarity_lims/__init__.py delete mode 100644 pyclarity_lims/config.py delete mode 100644 pyclarity_lims/epp.py delete mode 100644 pyclarity_lims/lims_utils.py delete mode 100644 pyclarity_lims/version.py delete mode 100644 tests/to_rewrite_test_logging.py diff --git a/.gitignore b/.gitignore index 99dd0c82..289c0eba 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,5 @@ docs/_sources/ docs/.doctrees/ docs/_templates/ docs/*.html -.fabfilerc -scripts/*.csv -scripts/*.log -scripts/*.out + + diff --git a/Changelogs.md b/Changelogs.md new file mode 100644 index 00000000..45bec614 --- /dev/null +++ b/Changelogs.md @@ -0,0 +1,5 @@ +Changelog for Pyclarity-Lims +============================ + +0.4 (unreleased) +---------------- diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..258fcfae --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,11 @@ +include *.md +include *.txt +include *.yml +include CHANGELOG +recursive-include etc *.html +recursive-include etc *.yaml +recursive-include tests *.gz +recursive-include tests *.log +recursive-include tests *.py +recursive-include tests *.sh +recursive-include tests *.txt diff --git a/README.md b/README.md index 04064486..0a87c267 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -## Python interface to the GenoLogics LIMS server via its REST API. +## Python interface to the BaseSpace Clarity LIMS server via its REST API. [![PyPI version](https://badge.fury.io/py/genologics.svg)](http://badge.fury.io/py/genologics) -A basic module for interacting with the GenoLogics LIMS server via +A basic module for interacting with the BaseSpace LIMS server via its REST API. The goal is to provide simple access to the most common entities and their attributes in a reasonably Pythonic fashion. Supported python versions : -2.6 -2.7 (recommended) +2.7 3.4 3.5 +3.6 (recommended) ### Design @@ -32,14 +32,9 @@ instance to the database. ### Installation ``` -pip install genologics +pip install pyclarity-lims ``` -or for the cutting edge version: - -``` -pip install https://github.com/SciLifeLab/genologics/tarball/master -``` ### Usage diff --git a/genologics/__init__.py b/genologics/__init__.py index fa60fe4d..9a6b88e5 100644 --- a/genologics/__init__.py +++ b/genologics/__init__.py @@ -1 +1,2 @@ -from pyclarity_lims import * \ No newline at end of file +from pyclarity_lims import lims +from pyclarity_lims import entities \ No newline at end of file diff --git a/genologics/entities.py b/genologics/entities.py new file mode 100644 index 00000000..fab2713e --- /dev/null +++ b/genologics/entities.py @@ -0,0 +1 @@ +from pyclarity_lims.entities import * \ No newline at end of file diff --git a/genologics/lims.py b/genologics/lims.py new file mode 100644 index 00000000..e92fa050 --- /dev/null +++ b/genologics/lims.py @@ -0,0 +1 @@ +from pyclarity_lims.lims import * \ No newline at end of file diff --git a/pyclarity_lims/__init__.py b/pyclarity_lims/__init__.py new file mode 100644 index 00000000..86019fda --- /dev/null +++ b/pyclarity_lims/__init__.py @@ -0,0 +1,6 @@ +import pkg_resources + +try: + __version__ = pkg_resources.get_distribution("pyclarity_lims").version +except pkg_resources.DistributionNotFound: + __version__ = "" diff --git a/pyclarity_lims/config.py b/pyclarity_lims/config.py deleted file mode 100644 index b4aed331..00000000 --- a/pyclarity_lims/config.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import sys -import warnings - -import ConfigParser - -''' -Usage: -from pyclarity_lims.config import BASEURI, USERNAME, PASSWORD - -Alternate Usage: -from pyclarity_lims import config -BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG = config.load_config(specified_config = ) -''' - -spec_config = None - -def get_config_info(config_file): - config = ConfigParser.SafeConfigParser() - config.readfp(open(config_file)) - - - BASEURI = config.get('pyclarity_lims', 'BASEURI').rstrip() - USERNAME = config.get('pyclarity_lims', 'USERNAME').rstrip() - PASSWORD = config.get('pyclarity_lims', 'PASSWORD').rstrip() - - if config.has_section('pyclarity_lims') and config.has_option('pyclarity_lims','VERSION'): - VERSION = config.get('pyclarity_lims', 'VERSION').rstrip() - else: - VERSION = 'v2' - - if config.has_section('logging') and config.has_option('logging','MAIN_LOG'): - MAIN_LOG = config.get('logging', 'MAIN_LOG').rstrip() - else: - MAIN_LOG = None - return BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG - - -def load_config(specified_config = None): - if specified_config != None: - config_file = specified_config - else: - config = ConfigParser.SafeConfigParser() - try: - conf_file = config.read([os.path.expanduser('~/.genologicsrc'), '.genologicsrc', - 'pyclarity_lims.conf', 'pyclarity_lims.cfg', '/etc/pyclarity_lims.conf']) - - # First config file found wins - config_file = conf_file[0] - - except: - warnings.warn("Please make sure you've created or indicated your own Genologics configuration file (i.e: ~/.genologicsrc) as stated in README.md") - sys.exit(-1) - - BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG = get_config_info(config_file) - - return BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG - - -BASEURI, USERNAME, PASSWORD, VERSION, MAIN_LOG = load_config(specified_config = spec_config) diff --git a/pyclarity_lims/epp.py b/pyclarity_lims/epp.py deleted file mode 100644 index 3b475db1..00000000 --- a/pyclarity_lims/epp.py +++ /dev/null @@ -1,391 +0,0 @@ -from __future__ import print_function -"""Contains useful and reusable code for EPP scripts. - -Classes, methods and exceptions. - -Johannes Alneberg, Science for Life Laboratory, Stockholm, Sweden. -Copyright (C) 2013 Johannes Alneberg -""" - -import logging -import sys -import os -import pkg_resources -from pkg_resources import DistributionNotFound -from shutil import copy -from requests import HTTPError -from pyclarity_lims.entities import Artifact -from pyclarity_lims.config import MAIN_LOG -from logging.handlers import RotatingFileHandler -from time import strftime, localtime -import csv - -def attach_file(src,resource): - """Attach file at src to given resource - - Copies the file to the current directory, EPP node will upload this file - automatically if the process output is properly set up""" - original_name = os.path.basename(src) - new_name = resource.id + '_' + original_name - dir = os.getcwd() - location = os.path.join(dir,new_name) - copy(src,location) - return location - -class EmptyError(ValueError): - "Raised if an iterator is unexpectedly empty." - pass - -class NotUniqueError(ValueError): - "Raised if there are unexpectedly more than 1 item in an iterator" - pass - -def unique_check(l,msg): - "Check that l is of length 1, otherwise raise error, with msg appended" - if len(l)==0: - raise EmptyError("No item found for {0}".format(msg)) - elif len(l)!=1: - raise NotUniqueError("Multiple items found for {0}".format(msg)) - -def set_field(element): - try: - element.put() - except (TypeError, HTTPError) as e: - logging.warning("Error while updating element: {0}".format(e)) - -class EppLogger(object): - - """Context manager for logging module useful for EPP script execution. - - This context manager (CM) automatically logs what script that is executed, - with what parameters it was executed and what version (including) commit - hash of the pyclarity_lims package used. Since EPP scripts are often ran - automatically by the pyclarity_lims LIMS client, the stdout and stderr is - captured and logged within this CM. Stderr is duplicated so that the - last line can be shown in the GUI. In order to track multiple runs - of the same process from the pyclarity_lims LIMS GUI, the previous log - files can be prepended. Also a main log file can be used that is - supposed to be common for all scripts executed on the server. - - """ - - PACKAGE = 'pyclarity_lims' - def __enter__(self): - logging.info('Executing file: {0}'.format(sys.argv[0])) - logging.info('with parameters: {0}'.format(sys.argv[1:])) - try: - logging.info('Version of {0}: '.format(self.PACKAGE) + - pkg_resources.require(self.PACKAGE)[0].version) - except DistributionNotFound as e: - logging.error(e) - logging.error(('Make sure you have the {0} ' - 'package installed').format(self.PACKAGE)) - sys.exit(-1) - return self - - def __exit__(self,exc_type,exc_val,exc_tb): - # If no exception has occured in block, turn off logging. - if not exc_type: - logging.shutdown() - sys.stderr = self.saved_stderr - sys.stdout = self.saved_stdout - # Do not repress possible exception - return False - - def __init__(self,log_file=None,level=logging.INFO,lims=None,prepend=False): - """ Initialize the logger with custom settings. - - Arguments: - log_file -- file to write individual log to - - Keyword Arguments: - level -- Logging level, default logging.INFO - lims -- Lims instance, needed for prepend to work - prepend -- If True, prepend old log file to new, requires lims - """ - self.lims = lims - self.log_file = log_file - self.level = level - self.prepend = prepend - - if prepend and self.log_file: - self.prepend_old_log() - - # Loggers that will capture stdout and stderr respectively - stdout_logger = logging.getLogger('STDOUT') - self.slo = self.StreamToLogger(stdout_logger, logging.INFO) - self.saved_stdout = sys.stdout - sys.stdout = self.slo - - stderr_logger = logging.getLogger('STDERR') - self.saved_stderr = sys.stderr - # Duplicate stderr stream to log - self.sle = self.StreamToLogger(stderr_logger, logging.INFO, - self.saved_stderr) - sys.stderr = self.sle - - # Root logger with filehandler(s) - self.logger = logging.getLogger() - self.logger.setLevel(self.level) - formatter = logging.Formatter( - '%(asctime)s:%(levelname)s:%(name)s:%(message)s') - if self.log_file: - individual_fh = logging.FileHandler(self.log_file,mode='a') - individual_fh.setFormatter(formatter) - self.logger.addHandler(individual_fh) - - if MAIN_LOG: - # Rotating file handler, that will create up to 10 backup logs, - # each no bigger than 100MB. - main_fh = RotatingFileHandler(MAIN_LOG,mode='a', - maxBytes=1e8,backupCount=10) - main_fh.setFormatter(formatter) - self.logger.addHandler(main_fh) - else: - self.logger.warning('No main log file found.') - - def prepend_old_log(self, external_log_file = None): - """Prepend the old log to the new log. - - The location of the old log file is retrieved through the REST api. - In order to work, the script should be executed on the LIMS server - since the location on the disk is parsed out from the sftp string - and then used for local copy of file. - - This method does not use logging since that could mess up the - logging settings, instead warnings are printed to stderr.""" - if external_log_file: - log_file_name = external_log_file - else: - log_file_name = self.log_file - - local_log_path = os.path.join(os.getcwd(), log_file_name) - if not os.path.isfile(local_log_path): - try: - log_artifact = Artifact(self.lims,id = log_file_name) - log_artifact.get() - if log_artifact.files: - log_path = log_artifact.files[0].content_location.split( - self.lims.baseuri.split(':')[1])[1] - copy(log_path, local_log_path) - with open(local_log_path,'a') as f: - f.write('='*80+'\n') - except HTTPError: # Probably no artifact found, skip prepending - print(('No log file artifact found ' - 'for id: {0}').format(log_file_name), file=sys.stderr) - except IOError as e: # Probably some path was wrong in copy - print(('Log could not be prepended, ' - 'make sure {0} and {1} are ' - 'proper paths.').format(log_path, - log_file_name), file=sys.stderr) - raise e - - class StreamToLogger(object): - """Fake file-like stream object that redirects writes to a logger - instance. - - source: - http://www.electricmonk.nl/log/2011/08/14/ - redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - def __init__(self, logger, log_level=logging.INFO, stream=None): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - self.stream = stream - - def write(self, buf): - if self.stream: - self.stream.write(buf) - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) - -class ReadResultFiles(): - """Class to read pars different kinds of result files from a process. - The class stores the parsed content of all shared result files in a - dictionary 'shared_files'. The data is parsed as lists of lists. """ - - def __init__(self, process): - self.process = process - self.shared_files = self._pars_file('SharedResultFile') - self.perinput_files = self._pars_file('ResultFile') - - def get_file_path(self, artifact): - if len(artifact.files) > 0: - file = artifact.files[0] - file_path = file.content_location.split('scilifelab.se')[1] - if len(file_path.split('.')) > 1: - return file_path - return None - - def _pars_file(self, output_type): - """Reads a csv or txt into a list of lists, where sub lists are lines - of the csv.""" - outs = self.process.all_outputs() - outarts = [a for a in outs if a.output_type == output_type] - parsed_files = {} - for outart in outarts: - file_path = self.get_file_path(outart) - if file_path: - of = open(file_path ,'r') - file_ext = file_path.split('.')[-1] - if file_ext == 'csv': - pf = [row for row in csv.reader(of.read().splitlines())] - parsed_files[outart.name] = pf - elif file_ext == 'txt': - pf = [row.strip().strip('\\').split('\t') for row in of.readlines()] - parsed_files[outart.name] = pf - of.close() - return parsed_files - - def format_file(self, parsed_file, name = '', first_header = None, - header_row = None, root_key_col = 0, find_keys = []): - """Function to format a parsed csv or txt file. - - Arguments and Output: - parsed_file A list of lists where sublists are rows of the csv. - name Name of parsed file. - first_header First column of the heather section in the file. - default value is 'None' - root_key_col If you want the root keys to be given by some other - column than the first one, set root_key_col to the - column number. - header_row Instead of specifying first_header you can choose - from what line to reed by setting header_row to the - row number where you want to start reading. - find_keys List of row names to look for. Will exclude all - others. - file_info Dict of dicts. Keys of root dict are the first - column in the csv starting from the line after the - heather line. Keys of sub dicts are the columns of - the heather line.""" - file_info = {} - keys = [] - error_message = '' - duplicated_lines = [] - exeptions = ['Sample','Fail', ''] - if type(first_header) is not list: - if first_header: - first_header=[first_header] - else: - first_header=[] - for row, line in enumerate(parsed_file): - if keys and len(line)==len(keys): - root_key = line[root_key_col] - cond1 = find_keys == [] and root_key not in exeptions - cond2 = root_key in find_keys - if root_key in file_info: - duplicated_lines.append(root_key) - elif (cond1 or cond2): - file_info[root_key] = {} - if not duplicated_lines: - for col in range(len(keys)): - if keys[col] != '': - file_info[root_key][keys[col]] = line[col] - elif keys[col-1] != '': - tupl = (file_info[root_key][keys[col-1]], line[col]) - file_info[root_key][keys[col-1]] = tupl - - head = line[root_key_col] if len(line) > root_key_col else None - if first_header and head in first_header: - keys = line - elif header_row and row == header_row: - keys = line - if duplicated_lines: - error_message = ("Row names {0} occurs more than once in file {1}. " - "Fix the file to continue. ").format(','.join(duplicated_lines), name) - if not file_info: - error_message = error_message + "Could not format parsed file {0}.".format(name) - if error_message: - print(error_message, file=sys.stderr) - sys.exit(-1) - return file_info - - -class CopyField(object): - """Class to copy any filed (or udf) from any lims element to any - udf on any other lims element - - arguments: - - s_elt source element - instance of a type - d_elt destination element - instance of a type - s_field_name name of source field (or udf) to be copied - d_udf_name name of destination udf name. If not specified - s_field_name will be used. - - The copy_udf() function takes a log file as optional argument. - If this is given the changes will be logged there. - - Written by Maya Brandi and Johannes Alnberg - """ - def __init__(self, s_elt, d_elt, s_field_name, d_udf_name = None): - if not d_udf_name: - d_udf_name = s_field_name - self.s_elt = s_elt - self.s_field_name = s_field_name - self.s_field = self._get_field(s_elt, s_field_name) - self.d_elt = d_elt - self.d_type = d_elt._URI - self.d_udf_name = d_udf_name - self.old_dest_udf = self._get_field(d_elt, d_udf_name) - - def _current_time(self): - return strftime("%Y-%m-%d %H:%M:%S", localtime()) - - def _get_field(self, elt, field): - if field in elt.udf: - return elt.udf[field] - else: - try: - return elt.field - except: - return None - - def _set_udf(self, elt, udf_name, val): - try: - elt.udf[udf_name] = val - elt.put() - return True - except (TypeError, HTTPError) as e: - print("Error while updating element: {0}".format(e), file=sys.stderr) - sys.exit(-1) - return False - - def _log_before_change(self, changelog_f=None): - if changelog_f: - d = {'ct' : self._current_time(), - 's_udf' : self.s_field_name, - 'sn' : self.d_elt.name, - 'si' : self.d_elt.id, - 'su' : self.old_dest_udf, - 'nv' : self.s_field, - 'd_elt_type': self.d_type} - - changelog_f.write(("{ct}: udf: '{s_udf}' on {d_elt_type}: '{sn}' (" - "id: {si}) is changed from '{su}' to '{nv}'.\n").format(**d)) - - logging.info(("Copying from element with id: {0} to element with " - " id: {1}").format(self.s_elt.id, self.d_elt.id)) - - def _log_after_change(self): - d = {'s_udf': self.s_field_name, - 'd_udf': self.d_udf_name, - 'su': self.old_dest_udf, - 'nv': self.s_field, - 'd_elt_type': self.d_type} - - logging.info("Updated {d_elt_type} udf: {d_udf}, from {su} to " - "{nv}.".format(**d)) - - def copy_udf(self, changelog_f = None): - if self.s_field != self.old_dest_udf: - self._log_before_change(changelog_f) - log = self._set_udf(self.d_elt, self.d_udf_name, self.s_field) - self._log_after_change() - return log - else: - return False - - - diff --git a/pyclarity_lims/lims_utils.py b/pyclarity_lims/lims_utils.py deleted file mode 100644 index d42a4364..00000000 --- a/pyclarity_lims/lims_utils.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python - -from pyclarity_lims.epp import EppLogger - -import logging -import sys -import os - -from pyclarity_lims.lims import * -from pyclarity_lims.config import BASEURI, USERNAME, PASSWORD - -lims = Lims(BASEURI, USERNAME, PASSWORD) - -def get_run_info(fc): - fc_summary={} - for iom in fc.input_output_maps: - art = iom[0]['uri'] - lane = art.location[1].split(':')[0] - if lane not in fc_summary: - fc_summary[lane]= dict(list(art.udf.items())) #"%.2f" % val ----round?? - return fc_summary - -def procHistory(proc, samplename): - """Quick wat to get the ids of parent processes from the given process, - while staying in a sample scope""" - hist=[] - artifacts = lims.get_artifacts(sample_name = samplename, type = 'Analyte') - not_done=True - starting_art=proc.input_per_sample(samplename)[0].id - while not_done: - not_done=False - for o in artifacts: - if o.id == starting_art: - if o.parent_process is None: - #flow control : if there is no parent process, we can stop iterating, we're done. - not_done=False - break #breaks the for artifacts, we are done anyway. - else: - not_done=True #keep the loop running - hist.append(o.parent_process.id) - for i in o.parent_process.all_inputs(): - if i in artifacts: - # while increment - starting_art=i.id - - break #break the for allinputs, if we found the right one - break # breaks the for artifacts if we matched the current one - return hist - -def get_sequencing_info(fc): - """Input: a process object 'fc', of type 'Illumina Sequencing (Illumina SBS) 4.0', - Output: A dictionary where keys are lanes 1,2,...,8, and values are lane artifact udfs""" - fc_summary={} - for iom in fc.input_output_maps: - art = Artifact(lims,id = iom[0]['limsid']) - lane = art.location[1].split(':')[0] - if lane not in fc_summary: - fc_summary[lane]= dict(list(art.udf.items())) #"%.2f" % val ----round?? - fc_summary[lane]['qc'] = art.qc_flag - return fc_summary - -def make_sample_artifact_maps(sample_name): - """outin: connects each out_art for a specific sample to its - corresponding in_art and process. one-one relation""" - outin = {} - artifacts = lims.get_artifacts(sample_name = sample_name, type = 'Analyte') - for outart in artifacts: - try: - pro = outart.parent_process - inarts = outart.input_artifact_list() - for inart in inarts: - for samp in inart.samples: - if samp.name == sample_name: - outin[outart.id] = (pro, inart.id) - except: - pass - return outin - - diff --git a/pyclarity_lims/version.py b/pyclarity_lims/version.py deleted file mode 100644 index c0024b40..00000000 --- a/pyclarity_lims/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__="0.3.12" diff --git a/requirements.txt b/requirements.txt index a59fba0e..f2293605 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1 @@ requests -pytest -mock diff --git a/setup.py b/setup.py index 3ab1e1c6..25f21868 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,13 @@ +from os.path import join, abspath, dirname from setuptools import setup, find_packages -from pyclarity_lims.version import __version__ -import subprocess -# Fetch version from git tags. -# if git is not available (PyPi package), use stored version.py. -try: - version = subprocess.Popen(["git", "describe", "--abbrev=0"], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].rstrip() - version = version.decode("utf-8") -except: - version = __version__ -try: - with open("requirements.txt") as rq: - requires=rq.readlines() -except: - requires=["requests"] +requirements_txt = join(abspath(dirname(__file__)), 'requirements.txt') +requirements = [l.strip() for l in open(requirements_txt) if l and not l.startswith('#')] + +version = '0.3.12' + setup(name='pyclarity_lims', version=version, diff --git a/tests/to_rewrite_test_logging.py b/tests/to_rewrite_test_logging.py deleted file mode 100644 index dcc78a28..00000000 --- a/tests/to_rewrite_test_logging.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -from os.path import isdir -import os -import sys -from unittest import TestCase -from pyclarity_lims.epp import EppLogger - -file_path = os.path.realpath(__file__) -test_dir_path = os.path.dirname(file_path) -tmp_dir_path = test_dir_path + '/nose_tmp_output' -CWD = os.getcwd() - -class TestLog(TestCase): - def setUp(self): - """Create temporary dir if necessary, - otherwise clear contents of it""" - if not isdir(tmp_dir_path): - os.mkdir(tmp_dir_path) - self.tearDown() - os.chdir(test_dir_path) - - def tearDown(self): - """remove temporary output files""" - for d in os.listdir(tmp_dir_path): - d_path = os.path.join(tmp_dir_path,d) - try: - os.remove(d_path) - except: - for f in os.listdir(d_path): - f_path = os.path.join(d_path,f) - os.remove(f_path) - os.rmdir(d_path) - - assert os.listdir(tmp_dir_path) == [] - - - def test_stderr(self): - """ Stderr should be printed to stderr and logged""" - tmp_file = os.path.join(tmp_dir_path,'tmp_log') - saved_stderr = sys.stderr - tmp_stderr = os.path.join(tmp_dir_path,'tmp_stderr') - with open(tmp_stderr,'w') as sys.stderr: - with EppLogger(tmp_file, prepend=False) as epp_logger: - print('stderr nosetest', file=sys.stderr) - sys.stderr = saved_stderr - with open(tmp_stderr,'r') as stderr: - stream_lines = stderr.readlines() - assert 'stderr nosetest' in stream_lines[-1] - - with open(tmp_file,'r') as log_file: - log_lines = log_file.readlines() - assert 'stderr nosetest' in log_lines[-1] - - def test_stdout(self): - """ Stdout should be logged but not printed""" - tmp_file = os.path.join(tmp_dir_path,'tmp_log') - saved_stdout = sys.stdout - tmp_stdout = os.path.join(tmp_dir_path,'tmp_stdout') - with open(tmp_stdout,'w') as sys.stdout: - with EppLogger(tmp_file, prepend=False) as epp_logger: - print('stdout nosetest', file=sys.stdout) - sys.stdout = saved_stdout - with open(tmp_stdout,'r') as stdout: - stream_lines = stdout.readlines() - assert not stream_lines - - with open(tmp_file,'r') as log_file: - log_lines = log_file.readlines() - assert 'stdout nosetest' in log_lines[-1] - - def test_exception(self): - """ Exceptions should be printed and logged""" - # Hard to test, if exceptions are caught in a try statement, - # they will not be printed... - pass From da3da9cf85ebf56e6146177b14dbe8e842366466 Mon Sep 17 00:00:00 2001 From: tcezard Date: Mon, 12 Jun 2017 11:16:37 +0100 Subject: [PATCH 3/4] Remove setup.cfg cause it broke python 2.7 build --- setup.cfg | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 41b4263e..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[zest.releaser] -tag-format = v%%(version)s \ No newline at end of file From eca3a7cff449baab3cd30e002a2f067dc14fde73 Mon Sep 17 00:00:00 2001 From: tcezard Date: Mon, 12 Jun 2017 11:35:24 +0100 Subject: [PATCH 4/4] Add coverall commands in travis CI --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6b4aa8a7..429bf09c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,15 @@ language: python python: - "2.7" - - "3.3" - "3.4" - - "3.5" -install: "pip install -r requirements.txt" -script: PYTHONPATH=. py.test + - "3.6" +install: + - "pip install -r requirements.txt" + - "pip install python-coveralls pytest-cov" + - "python setup.py install" +script: PYTHONPATH=. py.test tests/ --doctest-modules -v --cov pyclarity_lims --cov-report term-missing +after_success: + - coveralls notifications: email: on_success: never