From 2307f3ba7c282bf4cadc9cd669843f856a3d82e5 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 8 Jul 2024 14:38:47 -0700 Subject: [PATCH 01/19] draft: copy over old code --- cellpack/autopack/Analysis.py | 167 +++++++++++--------------- cellpack/autopack/writers/__init__.py | 93 ++++++++++++++ 2 files changed, 162 insertions(+), 98 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 5a219734..b99bff10 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -12,15 +12,15 @@ import matplotlib import numpy +import pandas as pd from matplotlib import pyplot as plt from matplotlib.patches import Circle -from mdutils.mdutils import MdUtils import cellpack.autopack as autopack from cellpack.autopack.ldSequence import halton from cellpack.autopack.plotly_result import PlotlyAnalysis from cellpack.autopack.utils import check_paired_key, get_paired_key, get_seed_list -from cellpack.autopack.writers import Writer +from cellpack.autopack.writers import Writer, MarkdownWriter from cellpack.autopack.writers.ImageWriter import ImageWriter @@ -271,7 +271,7 @@ def read_dict_from_glob_file( def run_distance_analysis( self, - report_md, + md_object: MarkdownWriter, recipe_data, pairwise_distance_dict, figure_path, @@ -293,19 +293,21 @@ def run_distance_analysis( pairwise_distance_dict ) - report_md.new_header(level=1, title="Distance analysis") - report_md.new_line( + md_object.add_header(level=1, header="Distance analysis") + # TODO: add new method to add new line in markdown writer + md_object.report_md.new_line( f"Expected minimum distance: {expected_minimum_distance:.2f}" ) - report_md.new_line( + md_object.report_md.new_line( f"Actual minimum distance: {packed_minimum_distance:.2f}\n" ) if expected_minimum_distance > packed_minimum_distance: - report_md.new_header( - level=2, title="Possible errors", add_table_of_contents="n" + # TODO: add new method to add new list in markdown writer, check `add_table_of_contents` + md_object.add_header( + level=2, header="Possible errors", add_table_of_contents="n" ) - report_md.new_list( + md_object.report_md.new_list( [ f"Packed minimum distance {packed_minimum_distance:.2f}" " is less than the " @@ -313,7 +315,9 @@ def run_distance_analysis( ] ) - num_keys = len(all_pairwise_distances.keys()) + df = pd.DataFrame() + df["Ingredient key"] = [] + df["Pairwise distance distribution"] = [] img_list = [] for ingr_key in all_pairwise_distances: ingr_distance_histo_path = figure_path.glob( @@ -321,24 +325,16 @@ def run_distance_analysis( ) for img_path in ingr_distance_histo_path: img_list.append( - report_md.new_inline_image( + md_object.report_md.new_inline_image( text=f"Distance distribution {ingr_key}", path=f"{output_image_location}/{img_path.name}", ) ) - text_list = [ - "Ingredient key", - "Pairwise distance distribution", - *[ - val - for pair in zip(all_pairwise_distances.keys(), img_list) - for val in pair - ], - ] - - report_md.new_table( - columns=2, rows=(num_keys + 1), text=text_list, text_align="center" - ) + + df = pd.DataFrame() + df["Ingredient key"] = all_pairwise_distances.keys() + df["Pairwise distance distribution"] = img_list + md_object.add_table(header="", table=df) def get_ingredient_key_from_object_or_comp_name( self, search_name, ingredient_key_dict @@ -398,7 +394,7 @@ def get_partner_pair_dict( def run_partner_analysis( self, - report_md, + md_object: MarkdownWriter, recipe_data, combined_pairwise_distance_dict, ingredient_radii, @@ -414,9 +410,12 @@ def run_partner_analysis( avg_num_packed, ) if len(partner_pair_dict): - report_md.new_header(level=1, title="Partner Analysis") + md_object.add_header(header="Partner Analysis") - val_list = [] + paired_keys = [] + touching_radii = [] + binding_probabilities = [] + close_fractions = [] for paired_key, partner_values in partner_pair_dict.items(): pairwise_distances = numpy.array( combined_pairwise_distance_dict[paired_key] @@ -426,28 +425,18 @@ def run_partner_analysis( numpy.count_nonzero(pairwise_distances < padded_radius) / partner_values["num_packed"] ) - val_list.extend( - [ - paired_key, - partner_values["touching_radius"], - partner_values["binding_probability"], - close_fraction, - ] - ) + paired_keys.append(paired_key) + touching_radii.append(partner_values["touching_radius"]) + binding_probabilities.append(partner_values["binding_probability"]) + close_fractions.append(close_fraction) - text_list = [ - "Partner pair", - "Touching radius", - "Binding probability", - "Close packed fraction", - *val_list, - ] - report_md.new_table( - columns=4, - rows=(len(partner_pair_dict) + 1), - text=text_list, - text_align="center", - ) + df = pd.DataFrame() + df["Paired keys"] = paired_keys + df["Touching radii"] = touching_radii + df["Binding probabilities"] = binding_probabilities + df["Close packing fractions"] = close_fractions + + md_object.add_table(header="", table=df) def create_report( self, @@ -474,24 +463,7 @@ def create_report( run_*_analysis: bool whether to run specific analysis """ - if report_output_path is None: - report_output_path = self.output_path - report_output_path = Path(report_output_path) - - report_md = MdUtils( - file_name=f"{report_output_path}/analysis_report", - title="Packing analysis report", - ) - report_md.new_header( - level=2, - title=f"Analysis for packing results located at {self.packing_results_path}", - add_table_of_contents="n", - ) - - if not hasattr(self, "ingredient_key_dict"): - self.ingredient_key_dict = self.read_dict_from_glob_file( - "ingredient_keys_*" - ) + self.ingredient_key_dict = self.read_dict_from_glob_file("ingredient_key_*") if ingredient_keys is None: ingredient_keys = list(self.ingredient_key_dict.keys()) @@ -500,34 +472,38 @@ def create_report( ingredient_keys=ingredient_keys ) ingredient_radii = self.get_ingredient_radii(recipe_data=recipe_data) - + pairwise_distance_dict = self.read_dict_from_glob_file( + "pairwise_distances_*.json" + ) + combined_pairwise_distance_dict = self.combine_results_from_seeds( + pairwise_distance_dict + ) if not hasattr(self, "pairwise_distance_dict"): self.pairwise_distance_dict = self.read_dict_from_glob_file( "pairwise_distances_*.json" ) - combined_pairwise_distance_dict = self.combine_results_from_seeds( - self.pairwise_distance_dict + df = pd.DataFrame() + df["Ingredient name"] = list(ingredient_keys) + df["Encapsulating radius"] = list(ingredient_radii.values()) + df["Average number packed"] = list(avg_num_packed.values()) + + # path to save report and other outputs + if output_image_location is None: + output_image_location = self.output_path + + md_object = MarkdownWriter( + title="Packing analysis report", + output_path=self.output_path, + output_image_location=output_image_location, + report_name="analysis_report", ) - val_list = [] - for key, radius, num_packed in zip( - ingredient_keys, ingredient_radii.values(), avg_num_packed.values() - ): - val_list.extend([key, radius, num_packed]) - text_list = [ - "Ingredient name", - "Encapsulating radius", - "Average number packed", - *val_list, - ] - report_md.new_table( - columns=3, - rows=(len(ingredient_keys) + 1), - text=text_list, - text_align="center", + md_object.add_header( + header=f"Analysis for packing results located at {self.packing_results_path}" ) + # TODO: check if this is needed # path to save report and other outputs if output_image_location is None: output_image_location = self.output_path @@ -536,21 +512,16 @@ def create_report( packing_results_path = self.packing_results_path figure_path = packing_results_path / "figures" - report_md.new_header(level=1, title="Packing image") - glob_to_packing_image = figure_path.glob("packing_image_*.png") - for img_path in glob_to_packing_image: - report_md.new_line( - report_md.new_inline_image( - text="Packing image", - path=f"{output_image_location}/{img_path.name}", - ) - ) - report_md.new_line("") + md_object.add_images( + header="Packing image", + image_text=["Packing image"], + filepaths=list(figure_path.glob("packing_image_*.png")), + ) if run_distance_analysis: # TODO: take packing distance dict as direct input for live mode self.run_distance_analysis( - report_md, + md_object, recipe_data, self.pairwise_distance_dict, figure_path, @@ -559,14 +530,14 @@ def create_report( if run_partner_analysis: self.run_partner_analysis( - report_md, + md_object, recipe_data, combined_pairwise_distance_dict, ingredient_radii, avg_num_packed, ) - report_md.create_md_file() + md_object.write_file() def run_analysis_workflow( self, diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 5b439ecb..db69aafd 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -5,9 +5,13 @@ import json import os +from pathlib import Path import numpy from collections import OrderedDict +from mdutils.mdutils import MdUtils +import pandas as pd + from cellpack import autopack from cellpack.autopack.ingredient.grow import ActinIngredient, GrowIngredient import cellpack.autopack.transformation as tr @@ -431,3 +435,92 @@ def save( self.save_as_simularium(env, seed_to_results_map) else: print("format output " + output_format + " not recognized (json,python)") + + +class MarkdownWriter(object): + def __init__( + self, + title: str, + output_path: Path, + output_image_location: Path, + report_name: str, + ): + self.title = title + self.output_path = output_path + self.output_image_location = output_image_location + self.report_md = MdUtils( + file_name=str(self.output_path / report_name), + title=title, + ) + + # level is the header style, can only be 1 or 2 + def add_header(self, header, level: int = 2): + self.report_md.new_header(level=level, title=header, add_table_of_contents="n") + + def add_table(self, header, table, text_align="center"): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + + text_list = [] + for row in table.values.tolist(): + for item in row: + text_list.append(item) + + self.report_md.new_table( + columns=table.shape[1], + rows=table.shape[0], + text=self.text_list, + text_align=text_align, + ) + + def add_table_from_csv(self, header, filepath, text_align="center"): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + + table = pd.read_csv(filepath) + + text_list = [] + for row in table.values.tolist(): + for item in row: + text_list.append(item) + + self.report_md.new_table( + columns=table.shape[1], + rows=table.shape[0], + text=self.text_list, + text_align=text_align, + ) + + def write_file(self): + self.report_md.create_md_file() + + # Image text must be a list, if list is not same length as list of filepaths, only 1st item in image_text is used + def add_images(self, header, image_text, filepaths): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + if len(image_text) == len(filepaths): + for i in range(len(filepaths)): + self.report_md.new_line( + self.report_md.new_inline_image( + text=image_text[i], + path=str(self.output_image_location / filepaths[i]), + ) + ) + else: + for i in range(len(filepaths)): + self.report_md.new_line( + self.report_md.new_inline_image( + text=image_text[0], + path=str(self.output_image_location / filepaths[i]), + ) + ) + self.report_md.new_line() From c709c7fa5d43d5d26254a9e98cf23807c7f0b9eb Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 22 Jul 2024 14:41:14 -0700 Subject: [PATCH 02/19] fix text list --- cellpack/autopack/writers/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index db69aafd..c1f42345 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -472,7 +472,7 @@ def add_table(self, header, table, text_align="center"): self.report_md.new_table( columns=table.shape[1], rows=table.shape[0], - text=self.text_list, + text=text_list, text_align=text_align, ) @@ -493,7 +493,7 @@ def add_table_from_csv(self, header, filepath, text_align="center"): self.report_md.new_table( columns=table.shape[1], rows=table.shape[0], - text=self.text_list, + text=text_list, text_align=text_align, ) From 47d9c6f4bf988fda14146a1202004ced0b8d12b8 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 22 Jul 2024 14:41:50 -0700 Subject: [PATCH 03/19] unit tests --- cellpack/tests/test_markdown_writer.py | 98 ++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 cellpack/tests/test_markdown_writer.py diff --git a/cellpack/tests/test_markdown_writer.py b/cellpack/tests/test_markdown_writer.py new file mode 100644 index 00000000..9b00c3be --- /dev/null +++ b/cellpack/tests/test_markdown_writer.py @@ -0,0 +1,98 @@ +import pytest +import pandas as pd +from cellpack.autopack.writers import MarkdownWriter + + +@pytest.fixture +def setup_md_writer(tmp_path): + title = "Test Report" + output_path = tmp_path / "output" + output_image_location = tmp_path / "images" + report_name = "test_report.md" + + output_path.mkdir(parents=True, exist_ok=True) + output_image_location.mkdir(parents=True, exist_ok=True) + + writer = MarkdownWriter(title, output_path, output_image_location, report_name) + return writer, output_path / report_name + + +def test_add_header(setup_md_writer): + writer, report_path = setup_md_writer + writer.add_header("Header Level 2", level=2) + writer.write_file() + + with open(report_path, "r") as f: + report = f.read() + assert "# Header Level 2" in report + + +def test_add_table(setup_md_writer): + writer, report_path = setup_md_writer + header = "Test Table" + data = { + "col1": [1, 2, 3], + "col2": [4, 5, 6], + } + df = pd.DataFrame(data) + writer.add_table(header, df) + writer.write_file() + + with open(report_path, "r") as f: + report = f.read() + assert "Test Table" in report + assert "|1|4|" in report + + +def test_add_table_from_csv(setup_md_writer, tmp_path): + writer, report_path = setup_md_writer + header = "Test Table" + data = { + "col1": [5, 6], + "col2": [7, 8], + } + df = pd.DataFrame(data) + csv_path = tmp_path / "test_table.csv" + df.to_csv(csv_path, index=False) + + writer.add_table_from_csv(header, csv_path) + writer.write_file() + + with open(report_path, "r") as f: + report = f.read() + assert "Test Table" in report + assert "|5|7|" in report + + +def test_write_file(setup_md_writer): + writer, report_path = setup_md_writer + writer.add_header("Header Level 2", level=2) + writer.add_header("Header Level 3", level=3) + writer.add_header("Header Level 4", level=4) + writer.write_file() + + with open(report_path, "r") as f: + report = f.read() + assert "# Header Level 2" in report + assert "## Header Level 3" in report + assert "### Header Level 4" in report + + +def test_add_image(setup_md_writer, tmp_path): + writer, report_path = setup_md_writer + header = "Test Image" + image_text = ["Image 1", "Image 2"] + filepaths = ["image1.png", "image2.png"] + + for image in filepaths: + (tmp_path / image).touch() + + writer.add_images(header, image_text, filepaths) + writer.write_file() + + with open(report_path, "r") as f: + report = f.read() + assert "Test Image" in report + assert "![Image 1]" in report + assert "![Image 2]" in report + assert "images/image1.png" in report From bcab5b4b8063824ba4c4ab283e66a471ac9f5ee2 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 22 Jul 2024 15:07:10 -0700 Subject: [PATCH 04/19] move mdutils methods to md writer --- cellpack/autopack/Analysis.py | 13 +++++-------- cellpack/autopack/writers/__init__.py | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 26deb490..d0f09ad6 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -20,8 +20,7 @@ from cellpack.autopack.ldSequence import halton from cellpack.autopack.plotly_result import PlotlyAnalysis from cellpack.autopack.utils import check_paired_key, get_paired_key, get_seed_list -from cellpack.autopack.writers import Writer, MarkdownWriter -from cellpack.autopack.writers.ImageWriter import ImageWriter +from cellpack.autopack.writers import Writer, MarkdownWriter, ImageWriter class Analysis: @@ -294,20 +293,18 @@ def run_distance_analysis( ) md_object.add_header(level=1, header="Distance analysis") - # TODO: add new method to add new line in markdown writer - md_object.report_md.new_line( + md_object.add_line( f"Expected minimum distance: {expected_minimum_distance:.2f}" ) - md_object.report_md.new_line( + md_object.add_line( f"Actual minimum distance: {packed_minimum_distance:.2f}\n" ) if expected_minimum_distance > packed_minimum_distance: - # TODO: add new method to add new list in markdown writer, check `add_table_of_contents` md_object.add_header( level=2, header="Possible errors", add_table_of_contents="n" ) - md_object.report_md.new_list( + md_object.add_list( [ f"Packed minimum distance {packed_minimum_distance:.2f}" " is less than the " @@ -325,7 +322,7 @@ def run_distance_analysis( ) for img_path in ingr_distance_histo_path: img_list.append( - md_object.report_md.new_inline_image( + md_object.add_inline_image( text=f"Distance distribution {ingr_key}", path=f"{output_image_location}/{img_path.name}", ) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index c1f42345..f1eb45ca 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -497,9 +497,6 @@ def add_table_from_csv(self, header, filepath, text_align="center"): text_align=text_align, ) - def write_file(self): - self.report_md.create_md_file() - # Image text must be a list, if list is not same length as list of filepaths, only 1st item in image_text is used def add_images(self, header, image_text, filepaths): self.report_md.new_header( @@ -524,3 +521,15 @@ def add_images(self, header, image_text, filepaths): ) ) self.report_md.new_line() + + def add_line(self, line): + self.report_md.new_line(line) + + def add_list(self, list_items): + self.report_md.new_list(list_items) + + def add_inline_image(self, text, filepath): + return self.report_md.new_inline_image(text=text, path=str(filepath)) + + def write_file(self): + self.report_md.create_md_file() From 94c63acc338a78688e09ddde37ce50f17cdf3975 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 22 Jul 2024 19:39:35 -0700 Subject: [PATCH 05/19] correct arg name, unittest assertion --- cellpack/autopack/Analysis.py | 2 +- cellpack/tests/test_markdown_writer.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index d0f09ad6..717c8fbf 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -324,7 +324,7 @@ def run_distance_analysis( img_list.append( md_object.add_inline_image( text=f"Distance distribution {ingr_key}", - path=f"{output_image_location}/{img_path.name}", + filepath=f"{output_image_location}/{img_path.name}", ) ) diff --git a/cellpack/tests/test_markdown_writer.py b/cellpack/tests/test_markdown_writer.py index 9b00c3be..276e1eb3 100644 --- a/cellpack/tests/test_markdown_writer.py +++ b/cellpack/tests/test_markdown_writer.py @@ -95,4 +95,3 @@ def test_add_image(setup_md_writer, tmp_path): assert "Test Image" in report assert "![Image 1]" in report assert "![Image 2]" in report - assert "images/image1.png" in report From 801660eb7589b54a67147134d728c7a0809ea73b Mon Sep 17 00:00:00 2001 From: meganrm Date: Wed, 24 Jul 2024 14:50:19 -0700 Subject: [PATCH 06/19] fix typo --- cellpack/autopack/Analysis.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 717c8fbf..97a96cbe 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -460,7 +460,7 @@ def create_report( run_*_analysis: bool whether to run specific analysis """ - self.ingredient_key_dict = self.read_dict_from_glob_file("ingredient_key_*") + self.ingredient_key_dict = self.read_dict_from_glob_file("ingredient_keys_*") if ingredient_keys is None: ingredient_keys = list(self.ingredient_key_dict.keys()) @@ -751,7 +751,10 @@ def add_ingredient_positions_to_plot( ) # plot the sphere if ingr.use_rbsphere: - (ext_recipe, pts,) = ingr.getInterpolatedSphere( + ( + ext_recipe, + pts, + ) = ingr.getInterpolatedSphere( seed_ingredient_positions[-i - 1], seed_ingredient_positions[-i], ) @@ -853,9 +856,9 @@ def update_pairwise_distances( ingr.name, ingr2.name, ): - pairwise_distance_dict[seed_index][ - f"{ingr.name}_{ingr2.name}" - ] = self.env.calc_pairwise_distances(ingr.name, ingr2.name).tolist() + pairwise_distance_dict[seed_index][f"{ingr.name}_{ingr2.name}"] = ( + self.env.calc_pairwise_distances(ingr.name, ingr2.name).tolist() + ) return pairwise_distance_dict From 0f0bfd5fdcf5dbac974ebe86e9bef38838d2738a Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Fri, 26 Jul 2024 13:29:54 -0700 Subject: [PATCH 07/19] edit output path --- cellpack/autopack/Analysis.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 97a96cbe..b7d53f60 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -460,6 +460,10 @@ def create_report( run_*_analysis: bool whether to run specific analysis """ + if report_output_path is None: + report_output_path = self.output_path + report_output_path = Path(report_output_path) + self.ingredient_key_dict = self.read_dict_from_glob_file("ingredient_keys_*") if ingredient_keys is None: @@ -491,7 +495,7 @@ def create_report( md_object = MarkdownWriter( title="Packing analysis report", - output_path=self.output_path, + output_path=report_output_path, output_image_location=output_image_location, report_name="analysis_report", ) @@ -751,10 +755,7 @@ def add_ingredient_positions_to_plot( ) # plot the sphere if ingr.use_rbsphere: - ( - ext_recipe, - pts, - ) = ingr.getInterpolatedSphere( + (ext_recipe, pts,) = ingr.getInterpolatedSphere( seed_ingredient_positions[-i - 1], seed_ingredient_positions[-i], ) @@ -856,9 +857,9 @@ def update_pairwise_distances( ingr.name, ingr2.name, ): - pairwise_distance_dict[seed_index][f"{ingr.name}_{ingr2.name}"] = ( - self.env.calc_pairwise_distances(ingr.name, ingr2.name).tolist() - ) + pairwise_distance_dict[seed_index][ + f"{ingr.name}_{ingr2.name}" + ] = self.env.calc_pairwise_distances(ingr.name, ingr2.name).tolist() return pairwise_distance_dict From 6e4f404c117bf57a94852240abef1cee50d85be3 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 10:46:35 -0700 Subject: [PATCH 08/19] 1st attempt adjusting the table --- cellpack/autopack/writers/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index f1eb45ca..07d45c3b 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -471,8 +471,8 @@ def add_table(self, header, table, text_align="center"): self.report_md.new_table( columns=table.shape[1], - rows=table.shape[0], - text=text_list, + rows=table.shape[0] + 1, + text=[header] + text_list, text_align=text_align, ) @@ -492,8 +492,8 @@ def add_table_from_csv(self, header, filepath, text_align="center"): self.report_md.new_table( columns=table.shape[1], - rows=table.shape[0], - text=text_list, + rows=table.shape[0] + 1, + text=[header] + text_list, text_align=text_align, ) From 205bbd950ea4a9e510601a78927382488087525f Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 12:21:43 -0700 Subject: [PATCH 09/19] debugging --- cellpack/autopack/Analysis.py | 29 ++++++++++++++------------- cellpack/autopack/writers/__init__.py | 11 ++++++---- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index b7d53f60..885710ea 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -312,9 +312,6 @@ def run_distance_analysis( ] ) - df = pd.DataFrame() - df["Ingredient key"] = [] - df["Pairwise distance distribution"] = [] img_list = [] for ingr_key in all_pairwise_distances: ingr_distance_histo_path = figure_path.glob( @@ -328,9 +325,13 @@ def run_distance_analysis( ) ) - df = pd.DataFrame() - df["Ingredient key"] = all_pairwise_distances.keys() - df["Pairwise distance distribution"] = img_list + df = pd.DataFrame( + { + "Ingredient key": list(all_pairwise_distances.keys()), + "Pairwise distance distribution": img_list, + } + ) + md_object.add_table(header="", table=df) def get_ingredient_key_from_object_or_comp_name( @@ -484,10 +485,13 @@ def create_report( "pairwise_distances_*.json" ) - df = pd.DataFrame() - df["Ingredient name"] = list(ingredient_keys) - df["Encapsulating radius"] = list(ingredient_radii.values()) - df["Average number packed"] = list(avg_num_packed.values()) + df = pd.DataFrame( + { + "Ingredient name": list(ingredient_keys), + "Encapsulating radius": list(ingredient_radii.values()), + "Average number packed": list(avg_num_packed.values()), + } + ) # path to save report and other outputs if output_image_location is None: @@ -504,10 +508,7 @@ def create_report( header=f"Analysis for packing results located at {self.packing_results_path}" ) - # TODO: check if this is needed - # path to save report and other outputs - if output_image_location is None: - output_image_location = self.output_path + md_object.add_table(header="", table=df) # path where packing results are stored packing_results_path = self.packing_results_path diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 07d45c3b..25d5f52a 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -471,8 +471,8 @@ def add_table(self, header, table, text_align="center"): self.report_md.new_table( columns=table.shape[1], - rows=table.shape[0] + 1, - text=[header] + text_list, + rows=table.shape[0], + text=text_list, text_align=text_align, ) @@ -492,8 +492,8 @@ def add_table_from_csv(self, header, filepath, text_align="center"): self.report_md.new_table( columns=table.shape[1], - rows=table.shape[0] + 1, - text=[header] + text_list, + rows=table.shape[0], + text=text_list, text_align=text_align, ) @@ -504,8 +504,10 @@ def add_images(self, header, image_text, filepaths): title=header, add_table_of_contents="n", ) + print(f"Output image location: {self.output_image_location}") if len(image_text) == len(filepaths): for i in range(len(filepaths)): + print(f"Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], @@ -514,6 +516,7 @@ def add_images(self, header, image_text, filepaths): ) else: for i in range(len(filepaths)): + print(f"Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], From a6a7f3d3499cc4ae52e78769a1c758130040562c Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 12:25:32 -0700 Subject: [PATCH 10/19] fix print statements --- cellpack/autopack/writers/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 25d5f52a..9fc345b7 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -504,10 +504,10 @@ def add_images(self, header, image_text, filepaths): title=header, add_table_of_contents="n", ) - print(f"Output image location: {self.output_image_location}") + print("Output image location: {self.output_image_location}") if len(image_text) == len(filepaths): for i in range(len(filepaths)): - print(f"Adding image:", str(self.output_image_location / filepaths[i])) + print("Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], @@ -516,7 +516,7 @@ def add_images(self, header, image_text, filepaths): ) else: for i in range(len(filepaths)): - print(f"Adding image:", str(self.output_image_location / filepaths[i])) + print("Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], From 72a3f8c1e7ad8aad798cff94fe153c5a8ec67572 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 12:54:54 -0700 Subject: [PATCH 11/19] 2nd attempt --- cellpack/autopack/writers/__init__.py | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 9fc345b7..7a6162b7 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -464,14 +464,17 @@ def add_table(self, header, table, text_align="center"): add_table_of_contents="n", ) - text_list = [] - for row in table.values.tolist(): - for item in row: - text_list.append(item) + header_row = table.columns.tolist() + text_list = header_row + [ + item for sublist in table.values.tolist() for item in sublist + ] + + total_rows = table.shape[0] + 1 # Adding 1 for the header row + total_columns = table.shape[1] self.report_md.new_table( - columns=table.shape[1], - rows=table.shape[0], + columns=total_columns, + rows=total_rows, text=text_list, text_align=text_align, ) @@ -485,14 +488,16 @@ def add_table_from_csv(self, header, filepath, text_align="center"): table = pd.read_csv(filepath) - text_list = [] - for row in table.values.tolist(): - for item in row: - text_list.append(item) + header_row = table.columns.tolist() + text_list = header_row + [ + item for sublist in table.values.tolist() for item in sublist + ] + total_rows = table.shape[0] + 1 # Adding 1 for the header row + total_columns = table.shape[1] self.report_md.new_table( - columns=table.shape[1], - rows=table.shape[0], + columns=total_columns, + rows=total_rows, text=text_list, text_align=text_align, ) @@ -504,10 +509,8 @@ def add_images(self, header, image_text, filepaths): title=header, add_table_of_contents="n", ) - print("Output image location: {self.output_image_location}") if len(image_text) == len(filepaths): for i in range(len(filepaths)): - print("Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], @@ -516,14 +519,13 @@ def add_images(self, header, image_text, filepaths): ) else: for i in range(len(filepaths)): - print("Adding image:", str(self.output_image_location / filepaths[i])) self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], path=str(self.output_image_location / filepaths[i]), ) ) - self.report_md.new_line() + self.report_md.new_line("") def add_line(self, line): self.report_md.new_line(line) From 98b6f8d5cd47fe902218bfe883feadf05dbd24a2 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 13:39:36 -0700 Subject: [PATCH 12/19] correct output image path --- cellpack/autopack/writers/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 7a6162b7..c0da4c13 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -514,7 +514,7 @@ def add_images(self, header, image_text, filepaths): self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], - path=str(self.output_image_location / filepaths[i]), + path=str(self.output_image_location / filepaths[i].name), ) ) else: @@ -522,7 +522,7 @@ def add_images(self, header, image_text, filepaths): self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], - path=str(self.output_image_location / filepaths[i]), + path=str(self.output_image_location / filepaths[i].name), ) ) self.report_md.new_line("") From 863633a31a80ccf198f2c81211f4717a33bba7cf Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 14:06:23 -0700 Subject: [PATCH 13/19] fix path --- cellpack/autopack/writers/__init__.py | 6 ++++-- cellpack/tests/test_markdown_writer.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index c0da4c13..db0d61f5 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -511,18 +511,20 @@ def add_images(self, header, image_text, filepaths): ) if len(image_text) == len(filepaths): for i in range(len(filepaths)): + img_path = f"{self.output_image_location} / {filepaths[i].name}" self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], - path=str(self.output_image_location / filepaths[i].name), + path=img_path, ) ) else: for i in range(len(filepaths)): + img_path = f"{self.output_image_location} / {filepaths[i].name}" self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], - path=str(self.output_image_location / filepaths[i].name), + path=img_path, ) ) self.report_md.new_line("") diff --git a/cellpack/tests/test_markdown_writer.py b/cellpack/tests/test_markdown_writer.py index 276e1eb3..f2acee42 100644 --- a/cellpack/tests/test_markdown_writer.py +++ b/cellpack/tests/test_markdown_writer.py @@ -82,10 +82,10 @@ def test_add_image(setup_md_writer, tmp_path): writer, report_path = setup_md_writer header = "Test Image" image_text = ["Image 1", "Image 2"] - filepaths = ["image1.png", "image2.png"] + filepaths = [tmp_path / "image1.png", tmp_path / "image2.png"] for image in filepaths: - (tmp_path / image).touch() + image.touch() writer.add_images(header, image_text, filepaths) writer.write_file() From a61d748237ecec5af071d379f94e49b839c80e13 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 14:22:09 -0700 Subject: [PATCH 14/19] remove spaces --- cellpack/autopack/writers/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index db0d61f5..0eef1f50 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -511,7 +511,7 @@ def add_images(self, header, image_text, filepaths): ) if len(image_text) == len(filepaths): for i in range(len(filepaths)): - img_path = f"{self.output_image_location} / {filepaths[i].name}" + img_path = f"{self.output_image_location}/{filepaths[i].name}" self.report_md.new_line( self.report_md.new_inline_image( text=image_text[i], @@ -520,7 +520,7 @@ def add_images(self, header, image_text, filepaths): ) else: for i in range(len(filepaths)): - img_path = f"{self.output_image_location} / {filepaths[i].name}" + img_path = f"{self.output_image_location}/{filepaths[i].name}" self.report_md.new_line( self.report_md.new_inline_image( text=image_text[0], From b557cc95477e4d07ea3eb2858e09d8c9dea1bfc3 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Mon, 29 Jul 2024 14:41:56 -0700 Subject: [PATCH 15/19] code refactor --- cellpack/autopack/Analysis.py | 39 ++++++++++++++++------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 885710ea..ae0c2c4a 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -409,11 +409,7 @@ def run_partner_analysis( ) if len(partner_pair_dict): md_object.add_header(header="Partner Analysis") - - paired_keys = [] - touching_radii = [] - binding_probabilities = [] - close_fractions = [] + partner_data = [] for paired_key, partner_values in partner_pair_dict.items(): pairwise_distances = numpy.array( combined_pairwise_distance_dict[paired_key] @@ -423,16 +419,16 @@ def run_partner_analysis( numpy.count_nonzero(pairwise_distances < padded_radius) / partner_values["num_packed"] ) - paired_keys.append(paired_key) - touching_radii.append(partner_values["touching_radius"]) - binding_probabilities.append(partner_values["binding_probability"]) - close_fractions.append(close_fraction) + partner_data.append( + { + "Ingredient pair": paired_key, + "Touching radius": partner_values["touching_radius"], + "Binding probability": partner_values["binding_probability"], + "Close packed fraction": close_fraction, + } + ) - df = pd.DataFrame() - df["Paired keys"] = paired_keys - df["Touching radii"] = touching_radii - df["Binding probabilities"] = binding_probabilities - df["Close packing fractions"] = close_fractions + df = pd.DataFrame(partner_data) md_object.add_table(header="", table=df) @@ -465,7 +461,10 @@ def create_report( report_output_path = self.output_path report_output_path = Path(report_output_path) - self.ingredient_key_dict = self.read_dict_from_glob_file("ingredient_keys_*") + if not hasattr(self, "ingredient_key_dict"): + self.ingredient_key_dict = self.read_dict_from_glob_file( + "ingredient_keys_*" + ) if ingredient_keys is None: ingredient_keys = list(self.ingredient_key_dict.keys()) @@ -474,16 +473,14 @@ def create_report( ingredient_keys=ingredient_keys ) ingredient_radii = self.get_ingredient_radii(recipe_data=recipe_data) - pairwise_distance_dict = self.read_dict_from_glob_file( - "pairwise_distances_*.json" - ) - combined_pairwise_distance_dict = self.combine_results_from_seeds( - pairwise_distance_dict - ) + if not hasattr(self, "pairwise_distance_dict"): self.pairwise_distance_dict = self.read_dict_from_glob_file( "pairwise_distances_*.json" ) + combined_pairwise_distance_dict = self.combine_results_from_seeds( + self.pairwise_distance_dict + ) df = pd.DataFrame( { From dd245a944b2fa0734cdc762026b84f7f3425cff9 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Thu, 1 Aug 2024 18:42:11 -0700 Subject: [PATCH 16/19] make markdown writer a separate file in writers folder --- cellpack/autopack/writers/MarkdownWriter.py | 113 ++++++++++++++++++++ cellpack/autopack/writers/__init__.py | 109 ------------------- 2 files changed, 113 insertions(+), 109 deletions(-) create mode 100644 cellpack/autopack/writers/MarkdownWriter.py diff --git a/cellpack/autopack/writers/MarkdownWriter.py b/cellpack/autopack/writers/MarkdownWriter.py new file mode 100644 index 00000000..08abf89f --- /dev/null +++ b/cellpack/autopack/writers/MarkdownWriter.py @@ -0,0 +1,113 @@ +from pathlib import Path + +from mdutils.mdutils import MdUtils +import pandas as pd + +""" +MarkdownWriter provides a class to write markdown files +""" + + +class MarkdownWriter(object): + def __init__( + self, + title: str, + output_path: Path, + output_image_location: Path, + report_name: str, + ): + self.title = title + self.output_path = output_path + self.output_image_location = output_image_location + self.report_md = MdUtils( + file_name=str(self.output_path / report_name), + title=title, + ) + + # level is the header style, can only be 1 or 2 + def add_header(self, header, level: int = 2): + self.report_md.new_header(level=level, title=header, add_table_of_contents="n") + + def add_table(self, header, table, text_align="center"): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + + header_row = table.columns.tolist() + text_list = header_row + [ + item for sublist in table.values.tolist() for item in sublist + ] + + total_rows = table.shape[0] + 1 # Adding 1 for the header row + total_columns = table.shape[1] + + self.report_md.new_table( + columns=total_columns, + rows=total_rows, + text=text_list, + text_align=text_align, + ) + + def add_table_from_csv(self, header, filepath, text_align="center"): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + + table = pd.read_csv(filepath) + + header_row = table.columns.tolist() + text_list = header_row + [ + item for sublist in table.values.tolist() for item in sublist + ] + total_rows = table.shape[0] + 1 # Adding 1 for the header row + total_columns = table.shape[1] + + self.report_md.new_table( + columns=total_columns, + rows=total_rows, + text=text_list, + text_align=text_align, + ) + + # Image text must be a list, if list is not same length as list of filepaths, only 1st item in image_text is used + def add_images(self, header, image_text, filepaths): + self.report_md.new_header( + level=1, + title=header, + add_table_of_contents="n", + ) + if len(image_text) == len(filepaths): + for i in range(len(filepaths)): + img_path = f"{self.output_image_location}/{filepaths[i].name}" + self.report_md.new_line( + self.report_md.new_inline_image( + text=image_text[i], + path=img_path, + ) + ) + else: + for i in range(len(filepaths)): + img_path = f"{self.output_image_location}/{filepaths[i].name}" + self.report_md.new_line( + self.report_md.new_inline_image( + text=image_text[0], + path=img_path, + ) + ) + self.report_md.new_line("") + + def add_line(self, line): + self.report_md.new_line(line) + + def add_list(self, list_items): + self.report_md.new_list(list_items) + + def add_inline_image(self, text, filepath): + return self.report_md.new_inline_image(text=text, path=str(filepath)) + + def write_file(self): + self.report_md.create_md_file() diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 0eef1f50..5b439ecb 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -5,13 +5,9 @@ import json import os -from pathlib import Path import numpy from collections import OrderedDict -from mdutils.mdutils import MdUtils -import pandas as pd - from cellpack import autopack from cellpack.autopack.ingredient.grow import ActinIngredient, GrowIngredient import cellpack.autopack.transformation as tr @@ -435,108 +431,3 @@ def save( self.save_as_simularium(env, seed_to_results_map) else: print("format output " + output_format + " not recognized (json,python)") - - -class MarkdownWriter(object): - def __init__( - self, - title: str, - output_path: Path, - output_image_location: Path, - report_name: str, - ): - self.title = title - self.output_path = output_path - self.output_image_location = output_image_location - self.report_md = MdUtils( - file_name=str(self.output_path / report_name), - title=title, - ) - - # level is the header style, can only be 1 or 2 - def add_header(self, header, level: int = 2): - self.report_md.new_header(level=level, title=header, add_table_of_contents="n") - - def add_table(self, header, table, text_align="center"): - self.report_md.new_header( - level=1, - title=header, - add_table_of_contents="n", - ) - - header_row = table.columns.tolist() - text_list = header_row + [ - item for sublist in table.values.tolist() for item in sublist - ] - - total_rows = table.shape[0] + 1 # Adding 1 for the header row - total_columns = table.shape[1] - - self.report_md.new_table( - columns=total_columns, - rows=total_rows, - text=text_list, - text_align=text_align, - ) - - def add_table_from_csv(self, header, filepath, text_align="center"): - self.report_md.new_header( - level=1, - title=header, - add_table_of_contents="n", - ) - - table = pd.read_csv(filepath) - - header_row = table.columns.tolist() - text_list = header_row + [ - item for sublist in table.values.tolist() for item in sublist - ] - total_rows = table.shape[0] + 1 # Adding 1 for the header row - total_columns = table.shape[1] - - self.report_md.new_table( - columns=total_columns, - rows=total_rows, - text=text_list, - text_align=text_align, - ) - - # Image text must be a list, if list is not same length as list of filepaths, only 1st item in image_text is used - def add_images(self, header, image_text, filepaths): - self.report_md.new_header( - level=1, - title=header, - add_table_of_contents="n", - ) - if len(image_text) == len(filepaths): - for i in range(len(filepaths)): - img_path = f"{self.output_image_location}/{filepaths[i].name}" - self.report_md.new_line( - self.report_md.new_inline_image( - text=image_text[i], - path=img_path, - ) - ) - else: - for i in range(len(filepaths)): - img_path = f"{self.output_image_location}/{filepaths[i].name}" - self.report_md.new_line( - self.report_md.new_inline_image( - text=image_text[0], - path=img_path, - ) - ) - self.report_md.new_line("") - - def add_line(self, line): - self.report_md.new_line(line) - - def add_list(self, list_items): - self.report_md.new_list(list_items) - - def add_inline_image(self, text, filepath): - return self.report_md.new_inline_image(text=text, path=str(filepath)) - - def write_file(self): - self.report_md.create_md_file() From 17302c3df8c9c446f89e0f83efbaa95ac57c9723 Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Thu, 1 Aug 2024 18:51:48 -0700 Subject: [PATCH 17/19] fix import --- cellpack/autopack/Analysis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index ae0c2c4a..27b99486 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -20,7 +20,8 @@ from cellpack.autopack.ldSequence import halton from cellpack.autopack.plotly_result import PlotlyAnalysis from cellpack.autopack.utils import check_paired_key, get_paired_key, get_seed_list -from cellpack.autopack.writers import Writer, MarkdownWriter, ImageWriter +from cellpack.autopack.writers import Writer, ImageWriter +from cellpack.autopack.writers.MarkdownWriter import MarkdownWriter class Analysis: From 45bacb4b3c00f693ba47b911cacb406ce3a13a1e Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Thu, 1 Aug 2024 18:55:08 -0700 Subject: [PATCH 18/19] fix import for tests --- cellpack/tests/test_markdown_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cellpack/tests/test_markdown_writer.py b/cellpack/tests/test_markdown_writer.py index f2acee42..9280498b 100644 --- a/cellpack/tests/test_markdown_writer.py +++ b/cellpack/tests/test_markdown_writer.py @@ -1,6 +1,6 @@ import pytest import pandas as pd -from cellpack.autopack.writers import MarkdownWriter +from cellpack.autopack.writers.MarkdownWriter import MarkdownWriter @pytest.fixture From 0adf24ffb8646297fe4601b9bfd4257b550854eb Mon Sep 17 00:00:00 2001 From: Ruge Li Date: Fri, 2 Aug 2024 09:45:52 -0700 Subject: [PATCH 19/19] correct image writer import --- cellpack/autopack/Analysis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 27b99486..5b88b335 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -20,7 +20,8 @@ from cellpack.autopack.ldSequence import halton from cellpack.autopack.plotly_result import PlotlyAnalysis from cellpack.autopack.utils import check_paired_key, get_paired_key, get_seed_list -from cellpack.autopack.writers import Writer, ImageWriter +from cellpack.autopack.writers import Writer +from cellpack.autopack.writers.ImageWriter import ImageWriter from cellpack.autopack.writers.MarkdownWriter import MarkdownWriter