From 4b488162b38193ef7f44caf5b8529d40f6d8f7ba Mon Sep 17 00:00:00 2001 From: Dan Vasilescu Date: Tue, 5 Sep 2017 16:19:24 -0400 Subject: [PATCH 1/2] Redesign of Python configuration panel. --- .../vcell/client/VCellConfigurationPanel.java | 5 +- .../PythonConfigurationPanel2.java | 328 ++++++++++++++++++ .../cbit/vcell/resource/CondaSupport.java | 18 +- 3 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java diff --git a/vcell-client/src/main/java/cbit/vcell/client/VCellConfigurationPanel.java b/vcell-client/src/main/java/cbit/vcell/client/VCellConfigurationPanel.java index cf4a3c3123..45b7e0814a 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/VCellConfigurationPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/VCellConfigurationPanel.java @@ -25,12 +25,13 @@ import cbit.vcell.client.configuration.ConfigurationOptionsTreeModel.ConfigurationOptionsTreeFolderNode; import cbit.vcell.client.configuration.GeneralConfigurationPanel; import cbit.vcell.client.configuration.PythonConfigurationPanel; +import cbit.vcell.client.configuration.PythonConfigurationPanel2; import cbit.vcell.client.configuration.VisItConfigurationPanel; public class VCellConfigurationPanel extends JPanel { private GeneralConfigurationPanel generalConfigurationPanel = null; - private PythonConfigurationPanel pythonConfigurationPanel = null; + private PythonConfigurationPanel2 pythonConfigurationPanel = null; private VisItConfigurationPanel visItConfigurationPanel = null; private ComsolConfigurationPanel comsolConfigurationPanel = null; private BioNetGenConfigurationPanel bioNetGenConfigurationPanel = null; @@ -61,7 +62,7 @@ private void initialize() { generalConfigurationPanel = new GeneralConfigurationPanel(); generalConfigurationPanel.setName("generalConfigurationPanel"); - pythonConfigurationPanel = new PythonConfigurationPanel(); + pythonConfigurationPanel = new PythonConfigurationPanel2(); pythonConfigurationPanel.setName("pythonConfigurationPanel"); visItConfigurationPanel = new VisItConfigurationPanel(); visItConfigurationPanel.setName("visItConfigurationPanel"); diff --git a/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java b/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java new file mode 100644 index 0000000000..cf3beb9488 --- /dev/null +++ b/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java @@ -0,0 +1,328 @@ +package cbit.vcell.client.configuration; + +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.MouseInputListener; + +import org.vcell.util.gui.VCFileChooser; +import org.vcell.util.gui.exporter.FileFilters; + +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.model.Model; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.model.Model.RbmModelContainer; +import cbit.vcell.resource.CondaSupport; +import cbit.vcell.resource.OperatingSystemInfo; +import cbit.vcell.resource.PropertyLoader; +import cbit.vcell.resource.ResourceUtil; +import cbit.vcell.resource.VCellConfiguration; + +// http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io + +public class PythonConfigurationPanel2 extends JPanel { + + private static String textContinuum = "Continuum web site"; + private static String urlContinuum = "https://www.continuum.io"; + + private JLabel websiteLabel = new JLabel(); + private JButton testConfigurationButton; + private JButton installPythonButton; + private JLabel testConfigurationResults = new JLabel(); + private EventHandler eventHandler = new EventHandler(); + + + + private class EventHandler implements MouseListener, FocusListener, ActionListener, PropertyChangeListener { + + @Override + public void actionPerformed(ActionEvent e) { + if(e.getSource() == getTestConfigurationButton()) { + verifyInstallation(); + } else if(e.getSource() == getInstallPythonButton()) { + installPython(); + } + + + } + @Override + public void focusGained(FocusEvent e) { + } + @Override + public void focusLost(FocusEvent e) { + } + + @Override + public void propertyChange(java.beans.PropertyChangeEvent event) { + } + + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(urlContinuum)); + } catch (URISyntaxException | IOException ex) { + System.out.println("URL problem"); + } + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + } + + public PythonConfigurationPanel2() { + super(); + initialize(); + } + + private void initialize() { + + setLayout(new BorderLayout()); + + Border margin = new EmptyBorder(5,3,1,1); + Border loweredEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + TitledBorder panelBorder = BorderFactory.createTitledBorder(loweredEtchedBorder, " Python Properties "); + panelBorder.setTitleJustification(TitledBorder.LEFT); + panelBorder.setTitlePosition(TitledBorder.TOP); + panelBorder.setTitleFont(getFont().deriveFont(Font.BOLD)); + + JPanel mainPanel = new JPanel(); + mainPanel.setBorder(new CompoundBorder(margin, panelBorder)); + mainPanel.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.weighty=0; + gbc.fill=GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.gridheight=1; + add(mainPanel, BorderLayout.CENTER); + + JPanel upper = new JPanel(); +// TitledBorder titleLeft = BorderFactory.createTitledBorder(loweredEtchedBorder, " Original Physiology "); +// titleLeft.setTitleJustification(TitledBorder.LEFT); +// titleLeft.setTitlePosition(TitledBorder.TOP); +// upper.setBorder(titleLeft); + upper.setLayout(new GridBagLayout()); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.5; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(5, 2, 2, 3); // top, left, bottom, right + mainPanel.add(upper, gbc); + + // ============================================= Populating the upper group box ================= + String managedMiniconda = new File(ResourceUtil.getVcellHome(),"Miniconda").getAbsolutePath(); + String vcellPythonText = "" + + "Python is required for parameter estimation and other analysis.
" + + "VCell manages a dedicated Miniconda python installation " + + "at " + managedMiniconda + ".
" + + ""; + String vcellPythonText2 = "" + + "For more information about Miniconda, see:" + + ""; + websiteLabel.setText(textContinuum); + websiteLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + int gridy = 0; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weightx = 1; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(4, 4, 2, 10); + upper.add(new JLabel(vcellPythonText), gbc); + + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weightx = 1; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(10, 4, 0, 10); + upper.add(new JLabel(vcellPythonText2), gbc); + + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weightx = 1; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 4, 2, 10); + upper.add(websiteLabel, gbc); + + // -------------------------------------------------- + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = gridy; + //gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(4, 4, 4, 10); // top, left, bottom, right + upper.add(getTestConfigurationButton(), gbc); + + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = gridy; + // gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(4, 4, 4, 10); + upper.add(getInstallPythonButton(), gbc); + +// Dimension size = getTestConfigurationButton().getPreferredSize(); +// getTestConfigurationButton().setPreferredSize(size); +// getTestConfigurationButton().setMaximumSize(size); +// getInstallPythonButton().setSize(size); +// getInstallPythonButton().setPreferredSize(size); +// getInstallPythonButton().setMaximumSize(size); + + // -------------------------------------------------------- + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weighty = 1; // fake cell used for filling all the vertical empty space + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(4, 4, 4, 10); + upper.add(new JLabel(""), gbc); + + gridy++; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weightx = 1; + gbc.gridwidth = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(4, 4, 2, 10); + upper.add(testConfigurationResults, gbc); + + + + getTestConfigurationButton().addActionListener(eventHandler); + getInstallPythonButton().addActionListener(eventHandler); + addFocusListener(eventHandler); + websiteLabel.addMouseListener(eventHandler); + } + + private JButton getTestConfigurationButton() { + if (testConfigurationButton == null) { + testConfigurationButton = new javax.swing.JButton(" Test Configuration "); + testConfigurationButton.setName("TestConfigurationButton"); + } + return testConfigurationButton; + } + private JButton getInstallPythonButton() { + if (installPythonButton == null) { + installPythonButton = new javax.swing.JButton(" Python Installation"); + installPythonButton.setName("InstallPythonButton"); + } + return installPythonButton; + } + + private void verifyInstallation() { + getTestConfigurationButton().setEnabled(false); + getInstallPythonButton().setEnabled(false); + try { + CondaSupport.verifyInstall(false, false, false); + } catch (Exception e) { + String ret = e.getMessage(); + testConfigurationResults.setText("" + ret + ""); + delayedDisplay(10000); + return; + } + String ret = "Python configuration is up to date, all needed packages are present"; + testConfigurationResults.setText("" + ret + ""); + delayedDisplay(5000); + } + + private void installPython() { + testConfigurationResults.setText("" + "Installing Python... This may take a while." + ""); + getTestConfigurationButton().setEnabled(false); + getInstallPythonButton().setEnabled(false); + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + //Thread.sleep(5000); // use this for faster testing of the UI + CondaSupport.verifyInstall(true, true, true); + } catch (Exception e) { + String ret = e.getMessage(); + if(ret.length() > 250) { + ret = ret.substring(0, 249) + "..."; + } + testConfigurationResults.setText("" + ret + ""); + delayedDisplay(10000); + return; + } + String ret = "Python installation was successful."; + testConfigurationResults.setText("" + ret + ""); + delayedDisplay(5000); + } + }; + Thread pythonInstallThread = new Thread(runnable,"Python Install Thread"); + pythonInstallThread.setDaemon(true); + pythonInstallThread.start(); + } + + private void delayedDisplay(int miliseconds) { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + testConfigurationResults.setText(""); + getInstallPythonButton().setEnabled(true); + getTestConfigurationButton().setEnabled(true); + } + }, miliseconds); + } + +} diff --git a/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java b/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java index f1514a7825..7561c0f9e5 100644 --- a/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java +++ b/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java @@ -90,8 +90,10 @@ public static File getPythonExe(){ private static String minicondaWeb = "https://repo.continuum.io/miniconda/"; private static String win64py27 = "Miniconda2-latest-Windows-x86_64.exe"; + private static String win32py27 = "Miniconda2-latest-Windows-x86.exe"; private static String osx64py27 = "Miniconda2-latest-MacOSX-x86_64.sh"; private static String lin64py27 = "Miniconda2-latest-Linux-x86_64.sh"; + private static String lin32py27 = "Miniconda2-latest-Linux-x86.sh"; public static void installInBackground() { @@ -169,7 +171,7 @@ public static synchronized void verifyInstall(boolean bForceDownload, boolean bF final AnacondaInstallation pythonInstallation; File providedAnacondaDir = VCellConfiguration.getFileProperty(PropertyLoader.anacondaInstallDir); File managedMinicondaInstallDir = new File(ResourceUtil.getVcellHome(),"Miniconda"); - if (providedAnacondaDir == null){ + if (providedAnacondaDir == null) { /* * download from here: https://conda.io/miniconda.html */ @@ -192,17 +194,17 @@ public static synchronized void verifyInstall(boolean bForceDownload, boolean bF if (operatingSystemInfo.isWindows()){ if (operatingSystemInfo.is64bit()){ archive = new File(downloadDir,win64py27); - installMinicondaCommand = new String[] { archive.getAbsolutePath(), "/InstallationType=JustMe", "/AddToPath=0", "/RegisterPython=0", "/S", "/D="+managedMinicondaInstallDir.getAbsolutePath() }; }else{ - throw new RuntimeException("python installation not yet supported on 32 bit Windows"); + archive = new File(downloadDir,win32py27); } + installMinicondaCommand = new String[] { archive.getAbsolutePath(), "/InstallationType=JustMe", "/AddToPath=0", "/RegisterPython=0", "/S", "/D="+managedMinicondaInstallDir.getAbsolutePath() }; }else if (operatingSystemInfo.isLinux()){ if (operatingSystemInfo.is64bit()){ archive = new File(downloadDir,lin64py27); - installMinicondaCommand = new String[] { "bash", archive.getAbsolutePath(), "-b", "-f", "-p", managedMinicondaInstallDir.getAbsolutePath() }; }else{ - throw new RuntimeException("python installation not yet supported on 32 bit Linux"); + archive = new File(downloadDir,lin32py27); } + installMinicondaCommand = new String[] { "bash", archive.getAbsolutePath(), "-b", "-f", "-p", managedMinicondaInstallDir.getAbsolutePath() }; }else if (operatingSystemInfo.isMac()){ archive = new File(downloadDir,osx64py27); installMinicondaCommand = new String[] { "bash", archive.getAbsolutePath(), "-b", "-f", "-p", managedMinicondaInstallDir.getAbsolutePath() }; @@ -235,6 +237,12 @@ public static synchronized void verifyInstall(boolean bForceDownload, boolean bF // // Step 2: download installer archive if doesn't exist // + if((!bPythonExists || !archive.exists()) && !bForceDownload) { + // + // We are just verifying. Since it's missing, we produce a good message and exit + // + throw new RuntimeException("The vCell python component is missing. To get access to full vCell features we recommend installing it"); + } URL url = new URL(minicondaWeb + archive.getName()); if (!bPythonExists || !archive.exists() || bForceDownload) { FileUtils.copyURLToFile(url, archive); // download the archive From 1d3b155039881ce93bc560169555fa01d0cadd47 Mon Sep 17 00:00:00 2001 From: Dan Vasilescu Date: Tue, 5 Sep 2017 17:28:05 -0400 Subject: [PATCH 2/2] Separate Python installation verification. --- .../PythonConfigurationPanel2.java | 27 +++++++-- .../cbit/vcell/resource/CondaSupport.java | 60 ++++++++++++++++--- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java b/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java index cf3beb9488..de8ca2a3a5 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java +++ b/vcell-client/src/main/java/cbit/vcell/client/configuration/PythonConfigurationPanel2.java @@ -200,10 +200,23 @@ private void initialize() { // -------------------------------------------------- gridy++; + + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.weightx = 1; + gbc.gridwidth = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(4, 4, 4, 10); + upper.add(new JLabel(""), gbc); + gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = gridy; - //gbc.fill = GridBagConstraints.HORIZONTAL; + //gbc.weightx = 1; + gbc.gridwidth = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.EAST; gbc.insets = new Insets(4, 4, 4, 10); // top, left, bottom, right upper.add(getTestConfigurationButton(), gbc); @@ -212,7 +225,9 @@ private void initialize() { gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = gridy; - // gbc.fill = GridBagConstraints.HORIZONTAL; + //gbc.weightx = 1; + gbc.gridwidth = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.EAST; gbc.insets = new Insets(4, 4, 4, 10); upper.add(getInstallPythonButton(), gbc); @@ -255,14 +270,14 @@ private void initialize() { private JButton getTestConfigurationButton() { if (testConfigurationButton == null) { - testConfigurationButton = new javax.swing.JButton(" Test Configuration "); + testConfigurationButton = new javax.swing.JButton("Test Configuration"); testConfigurationButton.setName("TestConfigurationButton"); } return testConfigurationButton; } private JButton getInstallPythonButton() { if (installPythonButton == null) { - installPythonButton = new javax.swing.JButton(" Python Installation"); + installPythonButton = new javax.swing.JButton("Install Python"); installPythonButton.setName("InstallPythonButton"); } return installPythonButton; @@ -272,7 +287,7 @@ private void verifyInstallation() { getTestConfigurationButton().setEnabled(false); getInstallPythonButton().setEnabled(false); try { - CondaSupport.verifyInstall(false, false, false); + CondaSupport.verifyInstallation(); } catch (Exception e) { String ret = e.getMessage(); testConfigurationResults.setText("" + ret + ""); @@ -293,7 +308,7 @@ private void installPython() { public void run() { try { //Thread.sleep(5000); // use this for faster testing of the UI - CondaSupport.verifyInstall(true, true, true); + CondaSupport.installAsNeeded(true, true, true); } catch (Exception e) { String ret = e.getMessage(); if(ret.length() > 250) { diff --git a/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java b/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java index f490d5d417..c1a86bcdb4 100644 --- a/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java +++ b/vcell-core/src/main/java/cbit/vcell/resource/CondaSupport.java @@ -105,7 +105,7 @@ public void run() { boolean bForceInstallPython = false; boolean bForceInstallPackages = false; - verifyInstall(bForceDownload, bForceInstallPython, bForceInstallPackages); + installAsNeeded(bForceDownload, bForceInstallPython, bForceInstallPackages); }catch (Throwable e){ e.printStackTrace(); } @@ -157,7 +157,55 @@ private static AnacondaInstallation getAnacondaInstallation(File anacondaInstall return new AnacondaInstallation(anacondaInstallDir, pythonExeFile, condaExeFile); } - public static synchronized void verifyInstall(boolean bForceDownload, boolean bForceInstallPython, boolean bForceInstallPackages) throws IOException { + public static synchronized void verifyInstallation() throws IOException { + + for (PythonPackage pkg : PythonPackage.values()){ + getPackageStatusMap().put(pkg, InstallStatus.INITIALIZING); + } + // if anaconda directory not specified using vcell.anaconda.installdir property, then use a managed Miniconda installation + File providedAnacondaDir = VCellConfiguration.getFileProperty(PropertyLoader.anacondaInstallDir); + File managedMinicondaInstallDir = new File(ResourceUtil.getVcellHome(),"Miniconda"); + if (providedAnacondaDir == null) { + final File downloadDir = new File(ResourceUtil.getVcellHome(),"download"); + final File archive; + + OperatingSystemInfo operatingSystemInfo = OperatingSystemInfo.getInstance(); + if (operatingSystemInfo.isWindows()) { + if (operatingSystemInfo.is64bit()) { + archive = new File(downloadDir,win64py27); + }else{ + archive = new File(downloadDir,win32py27); + } + }else if (operatingSystemInfo.isLinux()) { + if (operatingSystemInfo.is64bit()){ + archive = new File(downloadDir,lin64py27); + }else{ + archive = new File(downloadDir,lin32py27); + } + }else if (operatingSystemInfo.isMac()) { + archive = new File(downloadDir,osx64py27); + }else{ + throw new RuntimeException("python installation now supported on this platform"); + } + + // check if miniconda python installation already exists + AnacondaInstallation managedMiniconda = getAnacondaInstallation(managedMinicondaInstallDir); + boolean bPythonExists = false; + if (managedMiniconda.pythonExe.exists()) { + boolean ret = checkPython(managedMiniconda); + if (ret) { + bPythonExists = true; + } + } + + if(!bPythonExists || !archive.exists()) { + // We are just verifying. Since it's missing, we produce a good message and exit + throw new RuntimeException("The vCell python component is missing. To get access to full vCell features we recommend installing it"); + } + } + } + + public static synchronized void installAsNeeded(boolean bForceDownload, boolean bForceInstallPython, boolean bForceInstallPackages) throws IOException { isInstallingOrVerifying = true; for (PythonPackage pkg : PythonPackage.values()){ @@ -236,12 +284,6 @@ public static synchronized void verifyInstall(boolean bForceDownload, boolean bF // // Step 2: download installer archive if doesn't exist // - if((!bPythonExists || !archive.exists()) && !bForceDownload) { - // - // We are just verifying. Since it's missing, we produce a good message and exit - // - throw new RuntimeException("The vCell python component is missing. To get access to full vCell features we recommend installing it"); - } URL url = new URL(minicondaWeb + archive.getName()); if (!bPythonExists || !archive.exists() || bForceDownload) { FileUtils.copyURLToFile(url, archive); // download the archive @@ -419,7 +461,7 @@ public static void main(String[] args){ boolean bForceInstallPython = false; boolean bForceInstallPackages = false; - verifyInstall(bForceDownload, bForceInstallPython, bForceInstallPackages); + installAsNeeded(bForceDownload, bForceInstallPython, bForceInstallPackages); System.out.println("verified Python = " + getPythonExe());