From d563b1477e21fd8eae0e4d4c3b1dbf69ba54b6ab Mon Sep 17 00:00:00 2001 From: Pavlos Stephanos Bekiaris <36934614+Paulocracy@users.noreply.github.com> Date: Fri, 27 Jan 2023 14:37:38 +0100 Subject: [PATCH] Fix concentration setting (#453) * Fix setting of concentrations * Bump to 1.1.6 * minor GUI improvements * Fix freezing with empty search string * variable rename * GUI improvements * cleanup * Add thermodynamic FBA * Clean-up * Fix clean-up Co-authored-by: axelvonkamp --- README.md | 4 +- cnapy/appdata.py | 2 +- cnapy/core.py | 4 +- cnapy/gui_elements/central_widget.py | 3 + cnapy/gui_elements/flux_feasibility_dialog.py | 44 ++++++++-- cnapy/gui_elements/main_window.py | 30 ++++++- cnapy/gui_elements/scenario_tab.py | 8 +- cnapy/gui_elements/thermodynamics_dialog.py | 83 ++++++++++++++----- cnapy/utils.py | 5 +- constructor/linux/construct.yaml | 2 +- constructor/win/README.md | 4 +- constructor/win/construct.yaml | 4 +- constructor/win/post_install.bat | 2 +- docs/index.md | 4 +- recipes/linux/meta.yaml | 2 +- recipes/noarch/meta.yaml | 2 +- recipes/win/meta.yaml | 2 +- setup.py | 2 +- 18 files changed, 156 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index fe3882c..f37f0ce 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,13 @@ Everyone is welcome to contribute to CNApy's development. [See our contribution 3. Create a conda environment with all dependencies ```sh - conda create -n cnapy-1.1.5 -c conda-forge -c cnapy cnapy=1.1.5 + conda create -n cnapy-1.1.6 -c conda-forge -c cnapy cnapy=1.1.6 ``` 4. Activate the cnapy conda environment ```sh - conda activate cnapy-1.1.5 + conda activate cnapy-1.1.6 ``` 5. Run CNApy within you activated conda environment diff --git a/cnapy/appdata.py b/cnapy/appdata.py index 08e6f1e..537c478 100644 --- a/cnapy/appdata.py +++ b/cnapy/appdata.py @@ -24,7 +24,7 @@ class AppData: ''' The application data ''' def __init__(self): - self.version = "cnapy-1.1.5" + self.version = "cnapy-1.1.6" self.format_version = 2 self.unsaved = False self.project = ProjectData() diff --git a/cnapy/core.py b/cnapy/core.py index cfdde71..750a950 100644 --- a/cnapy/core.py +++ b/cnapy/core.py @@ -229,7 +229,7 @@ def make_scenario_feasible(cobra_model: cobra.Model, scen_values: Dict[str, Tupl if v != 0: print(s.name, format_string.format(v, v/abs(coeff))) bm_mod[m] = v - if gam_mets_param is not None: + if len(gam_mets) > 0: gam_adjust = gam_max_change * model.solver.variables["gam_slack"].primal else: for m,_,coeff,(s_p,s_n) in bm_coeff_var: @@ -241,7 +241,7 @@ def make_scenario_feasible(cobra_model: cobra.Model, scen_values: Dict[str, Tupl if v != 0: print(s_n.name, format_string.format(v, v/abs(coeff))) bm_mod[m] = -v - if gam_mets_param is not None: + if len(gam_mets) > 0: gam_adjust = gam_max_change * \ (model.solver.variables["gam_slack_pos"].primal - model.solver.variables["gam_slack_neg"].primal) if len(gam_mets) > 0: diff --git a/cnapy/gui_elements/central_widget.py b/cnapy/gui_elements/central_widget.py index 4527e0b..e91311d 100644 --- a/cnapy/gui_elements/central_widget.py +++ b/cnapy/gui_elements/central_widget.py @@ -309,6 +309,9 @@ def delete_map(self, idx: int): def update_selected(self): string = self.searchbar.text() + if string == "": + return + idx = self.tabs.currentIndex() with_annotations = self.search_annotations.isChecked() and self.search_annotations.isEnabled() if idx == ModelTabIndex.Reactions: diff --git a/cnapy/gui_elements/flux_feasibility_dialog.py b/cnapy/gui_elements/flux_feasibility_dialog.py index 5a1cc49..1f080e8 100644 --- a/cnapy/gui_elements/flux_feasibility_dialog.py +++ b/cnapy/gui_elements/flux_feasibility_dialog.py @@ -1,8 +1,10 @@ from math import copysign from qtpy.QtCore import Qt, Slot, QSignalBlocker from qtpy.QtWidgets import (QDialog, QGroupBox, QHBoxLayout, QTableWidget, QCheckBox, QMainWindow, - QLabel, QLineEdit, QMessageBox, QPushButton, QAbstractItemView, - QRadioButton, QVBoxLayout, QTableWidgetItem, QButtonGroup, QStyledItemDelegate) + QLabel, QLineEdit, QMessageBox, QPushButton, QAbstractItemView, QAction, + QRadioButton, QVBoxLayout, QTableWidgetItem, QButtonGroup, + QStyledItemDelegate, QTableWidgetSelectionRange) +from qtpy.QtGui import QGuiApplication from cnapy.utils import QComplReceivLineEdit from cnapy.core import make_scenario_feasible, QPnotSupportedException @@ -93,11 +95,16 @@ def __init__(self, main_window: QMainWindow): self.bm_constituents.setSortingEnabled(True) self.bm_constituents.verticalHeader().setVisible(False) self.bm_constituents.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.bm_constituents.setSelectionMode(QAbstractItemView.ContiguousSelection) self.bm_constituents.resizeColumnsToContents() coefficient_delegate = CoefficientDelegate() self.bm_constituents.setItemDelegateForColumn(3, coefficient_delegate) self.bm_constituents.setItemDelegateForColumn(4, coefficient_delegate) self.bm_constituents.setItemDelegateForColumn(5, coefficient_delegate) + copy_action = QAction("Copy", self.bm_constituents) + copy_action.setShortcut("Ctrl+C") + copy_action.triggered.connect(self.copy_table_selection) + self.bm_constituents.addAction(copy_action) vbox_bm_coeff.addWidget(self.bm_constituents) hbox = QHBoxLayout() hbox.addWidget(QLabel("Maximal relative coefficient change [%]:")) @@ -182,6 +189,12 @@ def compute(self): abs_flux_weights: bool = False weights_key: str = None + # clear previous results + self.gam_adjustment.setText("") + for i in range(self.bm_constituents.rowCount()): + self.bm_constituents.setItem(i, 4, None) + self.bm_constituents.setItem(i, 5, None) + if self.flux_group.isChecked(): if self.abs_flux_weights_button.isChecked(): abs_flux_weights = True @@ -212,8 +225,6 @@ def compute(self): checkbox = self.bm_constituents.cellWidget(i, 0) if checkbox is not None and checkbox.isChecked(): variable_constituents.append(self.bm_constituents.item(i, 1).data(Qt.UserRole)) - self.bm_constituents.setItem(i, 4, None) - self.bm_constituents.setItem(i, 5, None) try: max_coeff_change = float(self.max_coeff_change.text()) except ValueError: @@ -333,7 +344,7 @@ def compute(self): self.appdata.project.scen_values.reactions[self.bm_mod_reac_id] = \ [{met.id: coeff for met, coeff in bm_reac_mod.metabolites.items()}, fixed_growth_rate, fixed_growth_rate] self.main_window.centralWidget().tabs.widget(ModelTabIndex.Scenario).recreate_scenario_items_needed = True - if self.adjust_gam.isChecked(): + if self.bm_group.isChecked() and self.adjust_gam.isChecked(): self.gam_adjustment.setText("Calculated GAM adjustment: "+coefficient_format.format(gam_adjust)) self.main_window.centralWidget().update() else: @@ -409,3 +420,26 @@ def enable_biomass_equation_modifications(self, enable: bool): self.bm_group.setChecked(enable) self.adjust_bm_coeff.setEnabled(enable) self.adjust_gam.setEnabled(enable) + + @Slot() + def copy_table_selection(self): + selection_range: QTableWidgetSelectionRange = self.bm_constituents.selectedRanges()[0] + table = [] + r = selection_range.topRow() + while r <= selection_range.bottomRow(): + row = [] + c = selection_range.leftColumn() + while c <= selection_range.rightColumn(): + if c == 0: + row.append(str(self.bm_constituents.cellWidget(r, c).isChecked())) + else: + item: QTableWidgetItem = self.bm_constituents.item(r, c) + if item is None: + row.append("") + else: + row.append(str(item.data(Qt.DisplayRole))) + c += 1 + table.append('\t'.join(row)) + r += 1 + clipboard = QGuiApplication.clipboard() + clipboard.setText('\r'.join(table)) diff --git a/cnapy/gui_elements/main_window.py b/cnapy/gui_elements/main_window.py index 3b4acf2..5eddc1f 100644 --- a/cnapy/gui_elements/main_window.py +++ b/cnapy/gui_elements/main_window.py @@ -45,7 +45,7 @@ from cnapy.gui_elements.flux_optimization_dialog import FluxOptimizationDialog from cnapy.gui_elements.configuration_cplex import CplexConfigurationDialog from cnapy.gui_elements.configuration_gurobi import GurobiConfigurationDialog -from cnapy.gui_elements.thermodynamics_dialog import ThermodynamicDialog +from cnapy.gui_elements.thermodynamics_dialog import ThermodynamicAnalysisTypes, ThermodynamicDialog import cnapy.utils as utils @@ -398,6 +398,9 @@ def __init__(self, appdata: AppData): optmdf_action.triggered.connect(self.perform_optmdfpathway) self.analysis_menu.addAction(optmdf_action) + tfba_action = QAction("Thermodynamic FBA...", self) + tfba_action.triggered.connect(self.perform_thermodynamic_fba) + self.analysis_menu.addAction(tfba_action) bottleneck_action = QAction("Thermodynamic bottleneck analysis...", self) bottleneck_action.triggered.connect(self.perform_bottleneck_analysis) @@ -1878,14 +1881,32 @@ def set_status_unknown(self): @Slot() def perform_optmdfpathway(self): + # Has to be in self to keep computation thread self.optmdfpathway_dialog = ThermodynamicDialog( - self.appdata, self.centralWidget(), bottleneck_analysis=False) + self.appdata, + self.centralWidget(), + analysis_type=ThermodynamicAnalysisTypes.OPTMDFPATHWAY + ) self.optmdfpathway_dialog.exec_() + @Slot() + def perform_thermodynamic_fba(self): + # Has to be in self to keep computation thread + self.thermodynamic_fba_dialog = ThermodynamicDialog( + self.appdata, + self.centralWidget(), + analysis_type=ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA + ) + self.thermodynamic_fba_dialog.exec_() + @Slot() def perform_bottleneck_analysis(self): + # Has to be in self to keep computation thread self.bottleneck_dialog = ThermodynamicDialog( - self.appdata, self.centralWidget(), bottleneck_analysis=True) + self.appdata, + self.centralWidget(), + analysis_type=ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS + ) self.bottleneck_dialog.exec_() def _load_json(self) -> Dict[Any, Any]: @@ -1933,6 +1954,9 @@ def _set_concentrations(self, concentrations): else: lb = concentrations[metabolite.id]["min"] ub = concentrations[metabolite.id]["max"] + + metabolite.annotation["Cmin"] = lb + metabolite.annotation["Cmax"] = ub self.centralWidget().update() self.unsaved_changes() diff --git a/cnapy/gui_elements/scenario_tab.py b/cnapy/gui_elements/scenario_tab.py index 41ba1d7..0b09d70 100644 --- a/cnapy/gui_elements/scenario_tab.py +++ b/cnapy/gui_elements/scenario_tab.py @@ -42,7 +42,7 @@ def __init__(self, central_widget): self.use_scenario_objective.setEnabled(True) self.use_scenario_objective.stateChanged.connect(self.use_scenario_objective_changed) self.objective_group_layout.addWidget(self.use_scenario_objective) - self.scenario_objective = QComplReceivLineEdit(self, []) + self.scenario_objective = QComplReceivLineEdit(self, [], reject_empty_string=False) self.scenario_objective.set_wordlist(self.reaction_ids, replace_completer_model=False) self.scenario_objective.set_completer_model(self.reaction_ids_model) self.objective_group_layout.addWidget(self.scenario_objective) @@ -55,6 +55,7 @@ def __init__(self, central_widget): layout.addWidget(group) label = QLabel("Scenario reactions") + label.setToolTip("The IDs of the scenario reactions must be distinct from those in the network.\nYou may introduce new metabolites in scenario reactions.") hbox = QHBoxLayout() hbox.addWidget(label) self.add_reaction = QPushButton("+") @@ -76,6 +77,7 @@ def __init__(self, central_widget): layout.addWidget(self.equation) label = QLabel("Scenario constraints") + label.setToolTip('Formulated as linear inequality constraints over the reactions,\ne.g. "R1 + R2 >= 10"\nmeans that the sum of fluxes through R1 and R2 must be at least 10.') hbox = QHBoxLayout() hbox.addWidget(label) self.add_constraint_button = QPushButton("+") @@ -407,13 +409,13 @@ def change_scenario_objective_coefficients(self, text_correct: bool): self.objectiveSetupChanged.emit() self.use_scenario_objective.setEnabled(True) else: - self.appdata.project.scen_values.objective_coefficients.clear() self.use_scenario_objective.setEnabled(False) @Slot() def validate_objective(self): if not self.scenario_objective.is_valid and self.appdata.project.scen_values.use_scenario_objective: - self.use_scenario_objective.setChecked(False) + self.appdata.project.scen_values.objective_coefficients.clear() + self.use_scenario_objective.setChecked(False) # triggers use_scenario_objective_changed @Slot(int) def use_scenario_objective_changed(self, state: int): diff --git a/cnapy/gui_elements/thermodynamics_dialog.py b/cnapy/gui_elements/thermodynamics_dialog.py index 16a6ce9..6bbad06 100644 --- a/cnapy/gui_elements/thermodynamics_dialog.py +++ b/cnapy/gui_elements/thermodynamics_dialog.py @@ -36,6 +36,13 @@ import re from cnapy.gui_elements.solver_buttons import get_solver_buttons from straindesign.names import CPLEX, GLPK, GUROBI, SCIP +from enum import Enum + + +class ThermodynamicAnalysisTypes(Enum): + OPTMDFPATHWAY = 1 + THERMODYNAMIC_FBA = 2 + BOTTLENECK_ANALYSIS = 3 def make_model_irreversible( @@ -136,11 +143,10 @@ def cancel(self): class ComputationThread(QThread): - def __init__(self, linear_program: LinearProgram, bottleneck_analysis: bool): + def __init__(self, linear_program: LinearProgram): # super().__init__() QThread.__init__(self) self.linear_program: LinearProgram = linear_program - self.bottleneck_analysis: bool = bottleneck_analysis def run(self): try: @@ -166,20 +172,23 @@ class ThermodynamicDialog(QDialog): """A dialog to perform several thermodynamic methods.""" def __init__( - self, appdata: AppData, central_widget: CentralWidget, bottleneck_analysis: bool + self, appdata: AppData, central_widget: CentralWidget, analysis_type: ThermodynamicAnalysisTypes ) -> None: self.FWDID = "_FWDCNAPY" self.REVID = "_REVCNAPY" QDialog.__init__(self) - window_title = "Perform OptMDFpathway" - if bottleneck_analysis: - window_title += " bottleneck analysis" + if analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY: + window_title = "Perform OptMDFpathway" + elif analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS: + window_title = "Perform OptMDFpathway bottleneck analysis" + elif analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA: + window_title = "Perform thermodynamic FBA" self.setWindowTitle(window_title) self.appdata = appdata self.central_widget = central_widget - self.bottleneck_analysis = bottleneck_analysis + self.analysis_type = analysis_type self.reac_ids = self.appdata.project.cobra_py_model.reactions.list_attr("id") self.metabolite_ids = self.appdata.project.cobra_py_model.metabolites.list_attr( @@ -187,20 +196,30 @@ def __init__( ) self.layout = QVBoxLayout() - if not self.bottleneck_analysis: + if analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY: l = QLabel( "Perform OptMDFpathway. dG'° values and metabolite concentration " "ranges have to be given in relevant annotations." ) self.layout.addWidget(l) - else: + elif analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS: l = QLabel( "Perform OptMDFpathway bottleneck analysis. dG'° values and metabolite concentration " "ranges have to be given in relevant annotations.\nThe minimal amount of bottlenecks and their IDs " "to reach the given minimal MDF will be shown in the console afterwards." ) self.layout.addWidget(l) + elif analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA: + l = QLabel( + "Perform thermodynamic Flux Balance Analysis. Based on OptMDFpathway, you can perform an FBA at an OptMDF greater than the given value. " + "\nE.g., if the OptMDF is greater than 0 kJ/mol, you perform an FBA with enforced thermodynamic feasibility." + "\nFor this analysis, dG'° values and metabolite concentration " + "ranges have to be given in relevant annotations.\nThe minimal amount of bottlenecks and their IDs " + "to reach the given minimal MDF will be shown in the console afterwards." + ) + self.layout.addWidget(l) + if (analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS) or (analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA): lineedit_text = QLabel("MDF to reach [ín kJ/mol]:") min_mdf_layout = QHBoxLayout() @@ -217,6 +236,9 @@ def __init__( self.at_objective.setChecked(True) self.layout.addWidget(self.at_objective) + if analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA: + self.at_objective.hide() + solver_group = QGroupBox("Solver:") solver_buttons_layout, self.solver_buttons = get_solver_buttons(appdata) solver_group.setLayout(solver_buttons_layout) @@ -245,7 +267,7 @@ def get_solution_from_thread(self, solution) -> None: ) elif solution.status == Status.INFEASIBLE: QMessageBox.warning( - self, "Infeasible", "No solution exists, the problem is infeasible" + self, "Infeasible", "No solution exists, the problem is either stoichiometrically or thermodynamically (e.g., the minimal MDF is too high) infeasible" ) elif solution.status == Status.TIME_LIMIT: QMessageBox.warning( @@ -260,19 +282,19 @@ def get_solution_from_thread(self, solution) -> None: "The solution is unbounded (inf) so that no optimization solution can be shown.", ) elif solution.status == Status.OPTIMAL: - self.set_boxes(solution=solution.values) + self.set_boxes(solution=solution.values, objective_value=solution.objective_value) self.setCursor(Qt.ArrowCursor) self.accept() @Slot() def compute_in_thread( - self, linear_program: LinearProgram, bottleneck_analysis: bool + self, linear_program: LinearProgram ) -> None: # launch progress viewer and computation thread self.computation_viewer = ComputationViewer() # connect signals to update progress - self.computation_thread = ComputationThread(linear_program, bottleneck_analysis) + self.computation_thread = ComputationThread(linear_program) self.computation_thread.finished_computation.connect( self.computation_viewer.close_window, Qt.QueuedConnection ) @@ -457,7 +479,7 @@ def compute_optmdf(self): elif solver_name == GLPK: solver = Solver.GLPK - if not self.bottleneck_analysis: + if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY: minimal_optmdf = -float("inf") else: minimal_optmdf_str = self.min_mdf.text() @@ -482,29 +504,44 @@ def compute_optmdf(self): ratio_constraints=[], R=R, T=T, - add_bottleneck_constraints=self.bottleneck_analysis, + add_bottleneck_constraints=self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS, minimal_optmdf=minimal_optmdf, ) - if not self.bottleneck_analysis: + if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY: optmdfpathway_lp.set_objective( {"var_B": 1}, direction=ObjectiveDirection.MAX, ) - else: + elif self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS: optmdfpathway_lp.set_objective( {"bottleneck_z_sum": 1}, direction=ObjectiveDirection.MIN, ) + elif self.analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA: + objective_dict = {} + for ( + reaction, + coefficient, + ) in cobra.util.solver.linear_reaction_coefficients(model).items(): + if reaction.reversibility: + objective_dict[reaction.id + self.FWDID] = coefficient + objective_dict[reaction.id + self.REVID] = -coefficient + else: + objective_dict[reaction.id] = coefficient + + optmdfpathway_lp.set_objective( + objective_dict, + direction=ObjectiveDirection.MIN, + ) optmdfpathway_lp.construct_solver_object( solver=solver, ) # solution = optmdfpathway_lp.run_solve() self.compute_in_thread( linear_program=optmdfpathway_lp, - bottleneck_analysis=self.bottleneck_analysis, ) - def set_boxes(self, solution: Dict[str, float]): + def set_boxes(self, solution: Dict[str, float], objective_value: float): # Combine FWD and REV flux solutions combined_solution = {} for var_id in solution.keys(): @@ -547,10 +584,14 @@ def set_boxes(self, solution: Dict[str, float]): # Show OptMDF console_text = "print('\\n" - if not self.bottleneck_analysis: + if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY: optmdf = combined_solution["var_B"] console_text += f"OptMDF: {optmdf} kJ/mol" - else: + elif self.analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA: + optmdf = combined_solution["var_B"] + console_text += f"Reached objective value: {objective_value}" + console_text += f"\\nReached MDF @ optimum of objective: {optmdf} kJ/mol" + elif self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS: bottleneck_z_sum = combined_solution["bottleneck_z_sum"] console_text += f"Number of deactivated bottlenecks to reach minimal MDF: {bottleneck_z_sum}" diff --git a/cnapy/utils.py b/cnapy/utils.py index ea4bb21..291ee53 100644 --- a/cnapy/utils.py +++ b/cnapy/utils.py @@ -121,7 +121,7 @@ def finish(self): class QComplReceivLineEdit(QLineEdit): '''# does new completion after SPACE''' - def __init__(self, sd_dialog, wordlist, check=True, is_constr=False): + def __init__(self, sd_dialog, wordlist, check=True, is_constr=False, reject_empty_string=True): super().__init__("") self.sd_dialog = sd_dialog self.completer: QCompleter = QCompleter() @@ -134,6 +134,7 @@ def __init__(self, sd_dialog, wordlist, check=True, is_constr=False): self.setObjectName("EditField") self.check = check self.is_constr = is_constr + self.reject_empty_string = reject_empty_string self.is_valid = None def set_wordlist(self, wordlist, replace_completer_model=True): @@ -175,7 +176,7 @@ def focusOutEvent(self, event): def check_text(self, final): if self.check: - if self.text().strip() == "": + if self.reject_empty_string and self.text().strip() == "": self.setStyleSheet(BACKGROUND_COLOR( "#ffffff", self.objectName())) self.textCorrect.emit(False) diff --git a/constructor/linux/construct.yaml b/constructor/linux/construct.yaml index 5a6fc0b..32a4d72 100644 --- a/constructor/linux/construct.yaml +++ b/constructor/linux/construct.yaml @@ -1,5 +1,5 @@ name: CNApy -version: 1.1.5 +version: 1.1.6 ignore_duplicate_files: True diff --git a/constructor/win/README.md b/constructor/win/README.md index f137750..809f647 100644 --- a/constructor/win/README.md +++ b/constructor/win/README.md @@ -24,12 +24,12 @@ We use conda as package manager to install CNApy. You can use [miniconda](https: 1. Create a conda environment with all dependencies ```sh - conda create -n cnapy-1.1.5 -c conda-forge -c cnapy cnapy=1.1.5 + conda create -n cnapy-1.1.6 -c conda-forge -c cnapy cnapy=1.1.6 ``` 2. Activate the cnapy conda environment ``` - conda activate cnapy-1.1.5 + conda activate cnapy-1.1.6 ``` 3. Run CNApy diff --git a/constructor/win/construct.yaml b/constructor/win/construct.yaml index e2a8930..4524556 100644 --- a/constructor/win/construct.yaml +++ b/constructor/win/construct.yaml @@ -1,5 +1,5 @@ name: CNApy -version: 1.1.5 +version: 1.1.6 ignore_duplicate_files: True @@ -10,7 +10,7 @@ channels: specs: - conda - # - cnapy=1.1.5 + # - cnapy=1.1.6 - mamba - pip - python=3.8 diff --git a/constructor/win/post_install.bat b/constructor/win/post_install.bat index f8401ef..ebcc1a8 100644 --- a/constructor/win/post_install.bat +++ b/constructor/win/post_install.bat @@ -1,5 +1,5 @@ call %~dp0..\Scripts\activate.bat -call mamba install cnapy=1.1.5 -c cnapy -c conda-forge -v --yes +call mamba install cnapy=1.1.6 -c cnapy -c conda-forge -v --yes call pause call %~dp0..\Scripts\conda clean --all --yes diff --git a/docs/index.md b/docs/index.md index 0b31085..5b27933 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,13 +61,13 @@ Everyone is welcome to contribute to CNApy's development. [See our contribution 3. Create a conda environment with all dependencies ```sh - conda create -n cnapy-1.1.5 -c conda-forge -c cnapy cnapy=1.1.5 + conda create -n cnapy-1.1.6 -c conda-forge -c cnapy cnapy=1.1.6 ``` 4. Activate the cnapy conda environment ```sh - conda activate cnapy-1.1.5 + conda activate cnapy-1.1.6 ``` 5. Run CNApy within you activated conda environment diff --git a/recipes/linux/meta.yaml b/recipes/linux/meta.yaml index 03ac3e8..2a11c01 100644 --- a/recipes/linux/meta.yaml +++ b/recipes/linux/meta.yaml @@ -1,4 +1,4 @@ -{% set version = '1.1.5' %} +{% set version = '1.1.6' %} package: name: cnapy diff --git a/recipes/noarch/meta.yaml b/recipes/noarch/meta.yaml index 055ceba..3c49bb2 100644 --- a/recipes/noarch/meta.yaml +++ b/recipes/noarch/meta.yaml @@ -1,4 +1,4 @@ -{% set version = '1.1.5' %} +{% set version = '1.1.6' %} package: name: cnapy diff --git a/recipes/win/meta.yaml b/recipes/win/meta.yaml index de21167..3e712cf 100644 --- a/recipes/win/meta.yaml +++ b/recipes/win/meta.yaml @@ -1,4 +1,4 @@ -{% set version = '1.1.5' %} +{% set version = '1.1.6' %} package: name: cnapy diff --git a/setup.py b/setup.py index 985cb7a..9002dd8 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ setup( name='cnapy', - version='1.1.5', + version='1.1.6', url='https://github.com/cnapy-org/CNApy/', license='GPLv3+', description='An integrated environment for metabolic network analysis.',