Skip to content

Commit f8ec02c

Browse files
committed
Add EMME version input to Settings
1 parent 87853a4 commit f8ec02c

File tree

5 files changed

+165
-24
lines changed

5 files changed

+165
-24
lines changed

src/renderer/components/App.jsx

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const App = ({helmetUIVersion, versions, searchEMMEPython}) => {
1515
const [isProjectRunning, setProjectRunning] = useState(false); // whether currently selected Project is running
1616
const [emmePythonPath, setEmmePythonPath] = useState(undefined); // file path to EMME python executable
1717
const [emmePythonEnvs, setEmmePythonEnvs] = useState([]); // List of all discovered python executables
18+
const [emmeVersion, setEmmeVersion] = useState(undefined);
1819
const [helmetScriptsPath, setHelmetScriptsPath] = useState(undefined); // folder path to HELMET model system scripts
1920
const [projectPath, setProjectPath] = useState(undefined); // folder path to scenario configs, default homedir
2021
const [basedataPath, setBasedataPath] = useState(undefined); // folder path to base input data (subdirectories: 2016_zonedata, 2016_basematrices)
@@ -53,6 +54,11 @@ const App = ({helmetUIVersion, versions, searchEMMEPython}) => {
5354
}
5455
}
5556

57+
const _setEMMEVersion = (newVersion) => {
58+
setEmmeVersion(newVersion);
59+
globalSettingsStore.current.set('emme_version', newVersion);
60+
}
61+
5662
const _setHelmetScriptsPath = (newPath) => {
5763
// Cannot use state variable since it'd be undefined at times
5864
const pythonPath = globalSettingsStore.current.get('emme_python_path');
@@ -145,13 +151,21 @@ const App = ({helmetUIVersion, versions, searchEMMEPython}) => {
145151
const existingBasedataPath = globalSettingsStore.current.get('basedata_path');
146152
const existingResultsPath = globalSettingsStore.current.get('resultdata_path');
147153
const existingPythonEnvs = globalSettingsStore.current.get('emme_python_envs');
154+
const existingEmmeVersion = globalSettingsStore.current.get('emme_version');
148155
setEmmePythonPath(existingEmmePythonPath);
149156
setHelmetScriptsPath(existingHelmetScriptsPath);
150157
setProjectPath(existingProjectPath);
151158
setBasedataPath(existingBasedataPath);
152159
setResultsPath(existingResultsPath);
153160
setEmmePythonEnvs(existingPythonEnvs);
154161

162+
//If Emme version is uninitialized, fetch version from config file
163+
if(existingEmmeVersion === undefined) {
164+
_setEMMEVersion(versions.emme_system);
165+
} else {
166+
_setEMMEVersion(existingEmmeVersion);
167+
}
168+
155169
// If project path is the initial (un-set), set it to homedir. Remember: state updates async so refer to existing.
156170
if (!existingProjectPath) {
157171
_setProjectPath(homedir);
@@ -201,6 +215,8 @@ const App = ({helmetUIVersion, versions, searchEMMEPython}) => {
201215
setBasedataPath={_setBasedataPath}
202216
setResultsPath={_setResultsPath}
203217
promptModelSystemDownload={_promptModelSystemDownload}
218+
emmeVersion={emmeVersion}
219+
setEmmeVersion={_setEMMEVersion}
204220
/>
205221
</div>
206222

src/renderer/components/HelmetProject/Runtime/Runtime.jsx

-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ const Runtime = ({
5151
const runningScenario = activeScenarios.filter((scenario) => scenario.id === runningScenarioID);
5252

5353
const getResultsPathFromLogfilePath = (logfilePath) => {
54-
console.log(logfilePath.replace(/\/[^\/]+$/, ''));
5554
return logfilePath.replace(/\/[^\/]+$/, '');
5655
}
5756

@@ -147,7 +146,6 @@ const Runtime = ({
147146
{scenarios.map((s) => {
148147
// Component for the tooltip showing scenario settings
149148
const tooltipContent = (scenario) => {
150-
console.log(scenario);
151149
const filteredScenarioSettings = _.pickBy(scenario, (settingValue, settingKey) => {
152150
return visibleTooltipProperties.includes(settingKey);
153151
})

src/renderer/components/Settings/Settings.css

+50-5
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,20 @@
100100
font-size: 16px;
101101
}
102102

103+
.Settings__python-env-input-btn {
104+
margin: 10px auto 0 auto;
105+
height: 40px;
106+
width: 300px;
107+
font-size: 16px;
108+
}
109+
110+
.Settings__env_option_remove {
111+
color: #c92c00;
112+
}
113+
103114
.Settings__environment_option {
104115
display: flex;
105116
width: 100%;
106-
padding: 10px 12px;
107-
margin-top: 4px;
108117
background: #ffffff;
109118
box-sizing: border-box;
110119
border-radius: 5px;
@@ -121,20 +130,56 @@
121130
flex-wrap: wrap;
122131
}
123132

124-
.Settings__env_option_btn {
133+
.Settings__env_option_text {
134+
width: 100%;
135+
}
136+
137+
.Settings__emme_version_group {
138+
display: flex;
139+
width: 100%;
140+
}
141+
142+
.Settings__emme_edit_btn {
143+
height: 2rem;
144+
margin-left: .5rem;
145+
font-size: 12px;
146+
border: none;
147+
margin-top: 6px;
148+
}
149+
150+
.Settings__emme_edit_save_btn {
125151
width: 4rem;
126152
height: 2rem;
153+
font-size: 12px;
154+
border: none;
155+
margin-top: 6px;
156+
}
157+
158+
.Settings__emme_input {
159+
width: 100%;
160+
height: 5rem;
127161
border-radius: 5px;
162+
background-color: white;
163+
}
164+
165+
.Settings__env_option_btn {
166+
width: 4rem;
167+
height: 1rem;
128168
margin-left: 1.5rem;
169+
margin-bottom: 5px;
129170
font-size: 12px;
171+
border: none;
130172
}
131173

132-
.Settings__env_btn_disabled {
174+
.Settings__btn_disabled {
175+
color: #888888;
176+
}
177+
178+
.Settings__btn_disabled:hover {
133179
color: #888888;
134180
}
135181

136182
.Settings__env_option_divider {
137183
width: 100%;
138-
height: 1rem;
139184
border-bottom: 1px solid #CCCCCC;
140185
}

src/renderer/components/Settings/Settings.jsx

+59-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import path from "path";
33
const {dialog} = require('@electron/remote');
4-
import versions from '../versions';
4+
const versions = require('../versions');
55
import { listEMMEPythonPaths } from './search_emme_pythonpath';
66
import classNames from 'classnames';
77

@@ -10,13 +10,46 @@ const EnvironmentOption = ({
1010
}) => {
1111
return (
1212
<div className="Settings__environment_option">
13-
<p>{envPath}</p>
14-
<button className={classNames('Settings__env_option_btn', { 'Settings__env_btn_disabled': isSelected})} disabled={isSelected} onClick={() => setPath(envPath)}>{isSelected ? 'Käytössä' : 'Valitse'}</button>
15-
<button className="Settings__env_option_btn" onClick={() => removePath(envPath)}>Poista</button>
13+
<p className='Settings__env_option_text'>{envPath}</p>
14+
<button className={classNames('Settings__env_option_btn', { 'Settings__btn_disabled': isSelected})} disabled={isSelected} onClick={() => setPath(envPath)}>{isSelected ? 'Käytössä' : 'Valitse'}</button>
15+
<button className={classNames('Settings__env_option_btn', 'Settings__env_option_remove')} onClick={() => removePath(envPath)}>Poista</button>
1616
</div>
1717
)
1818
}
1919

20+
const EmmeVersionEdit = ({
21+
emmeVersion, setEmmeVersion, closeEditing
22+
}) => {
23+
24+
const [savingDisabled, setSavingDisabled] = useState(true);
25+
const [newEmmeVersion, setNewEmmeVersion] = useState('');
26+
const VERSION_NUMBER_REGEX = RegExp(/^(\d+\.)?(\d+\.)?(\*|\d+)$/);
27+
const validateVersionNumber = (e) => {
28+
const regExpResults = e.target.value.match(VERSION_NUMBER_REGEX);
29+
return regExpResults !== null;
30+
}
31+
32+
return (
33+
<div className='Settings__emme_input'>
34+
<span className="Settings__pseudo-label">Syötä uusi EMME versio: </span>
35+
<input type='text' defaultValue={emmeVersion} placeholder='esim. 4.5.0' onChange={(e) => {
36+
37+
setNewEmmeVersion(e.target.value);
38+
const validationPasses = validateVersionNumber(e);
39+
setSavingDisabled(!validationPasses);
40+
}}></input>
41+
<button className={classNames('Settings__emme_edit_save_btn', { 'Settings__btn_disabled': savingDisabled})}
42+
disabled={savingDisabled}
43+
onClick={() => {
44+
closeEditing();
45+
setEmmeVersion(newEmmeVersion);
46+
}}>
47+
Tallenna
48+
</button>
49+
</div>
50+
)
51+
};
52+
2053
const PathOptionDivider = () => <div className='Settings__env_option_divider' />
2154

2255
const Settings = ({
@@ -26,8 +59,11 @@ const Settings = ({
2659
basedataPath, setBasedataPath,
2760
resultsPath, setResultsPath,
2861
closeSettings,
29-
promptModelSystemDownload,
62+
promptModelSystemDownload, emmeVersion, setEmmeVersion,
3063
}) => {
64+
65+
const [showEmmeDialog, setShowEmmeDialog] = useState(false);
66+
3167
return (
3268
<div className="Settings">
3369

@@ -38,6 +74,20 @@ const Settings = ({
3874
<div className="Settings__dialog-controls" onClick={(e) => closeSettings()}></div>
3975

4076
<div className="Settings__dialog-heading">Projektin asetukset</div>
77+
78+
<div className="Settings__emme_version_group">
79+
<span className="Settings__pseudo-label">{`EMME-versio: ${emmeVersion}`}</span>
80+
<button className='Settings__emme_edit_btn'
81+
onClick={() => {
82+
setShowEmmeDialog(!showEmmeDialog);
83+
}}
84+
>
85+
{showEmmeDialog ? 'Peruuta' : 'Muokkaa'}
86+
</button>
87+
</div>
88+
{ showEmmeDialog && <EmmeVersionEdit emmeVersion={emmeVersion}
89+
setEmmeVersion={setEmmeVersion}
90+
closeEditing={() => setShowEmmeDialog(false)} /> }
4191
<div className="Settings__dialog-input-group">
4292
<span className="Settings__pseudo-label">Käytettävät Python-ympäristöt:</span>
4393
{ emmePythonEnvs && (emmePythonEnvs.map((env, index) => { return (
@@ -47,7 +97,7 @@ const Settings = ({
4797
removePath={removeFromEMMEPythonEnvs}/>
4898
{ index < emmePythonEnvs.length && <PathOptionDivider/> }
4999
</div>)}))}
50-
<button className="Settings__input-btn"
100+
<button className="Settings__python-env-input-btn"
51101
onClick={()=>{
52102
dialog.showOpenDialog({
53103
defaultPath: emmePythonPath ? emmePythonPath : path.resolve('/'),
@@ -65,9 +115,9 @@ const Settings = ({
65115
>
66116
Lisää Python-ympäristö
67117
</button>
68-
<button className="Settings__input-btn"
118+
<button className="Settings__python-env-input-btn"
69119
onClick={(e) => {
70-
const [found, pythonPaths] = listEMMEPythonPaths();
120+
const [found, pythonPaths] = listEMMEPythonPaths(emmeVersion);
71121
if (found) {
72122
alert(`Python-ympäristöjä löytyi. Valitse listasta oikea EMME Python-ympäristö ja ota sen jälkeen käyttöön ${pythonPaths}`)
73123
setEMMEPythonEnvs(pythonPaths);

src/renderer/search_emme_pythonpath.js

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const path = require('path');
22
const fs = require('fs');
33
const versions = require('../versions');
4+
const os = require('os');
45

56
/**
67
* Check and try to set EMME's Python location on Windows, searching from common known paths.
@@ -26,7 +27,7 @@ const searchEMMEPython = () => {
2627
`\\Program Files\\${commonEmmePath}\\${pythonPathPostfix}`,
2728
`\\Program Files (x86)\\${commonEmmePath}\\${pythonPathPostfix}`,
2829
`\\${commonEmmePath}\\${pythonPathPostfix}`,
29-
`usr/bin/python2` // mainly for developers on Mac & Linux
30+
`usr/bin/python3` // mainly for developers on Mac & Linux
3031
];
3132
const allPathCombinations = drives.reduce(
3233
(accumulator, d) => {
@@ -41,11 +42,10 @@ const searchEMMEPython = () => {
4142
}
4243
};
4344

44-
const listEMMEPythonPaths = () => {
45+
const listEMMEPythonPaths = (emmeVersion) => {
4546
// Set Windows' python exe path postfix (e.g. Python27\python.exe)
4647
const p = getVersion(versions.emme_python);
4748
const pythonPathPostfix = `Python${p.major}${p.minor}\\python.exe`;
48-
4949
// Search from environment variable "EMMEPATH"
5050
const envEmmePath = process.env.EMMEPATH || '';
5151
const envEmmePythonPath = path.join(envEmmePath, pythonPathPostfix);
@@ -54,22 +54,29 @@ const listEMMEPythonPaths = () => {
5454
}
5555

5656
// Not found based on EMMEPATH, try guessing some common locations on Windows
57-
const e = getVersion(versions.emme_system);
57+
const e = getVersion(emmeVersion);
5858
const pythonVersion = getVersion(versions.emme_python);
5959
const commonEmmePath = `INRO\\Emme\\Emme ${e.major}\\Emme-${e.semver}`;
6060
const drives = ['C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:', '/'];
6161
const paths = [
62-
`\\Program Files\\${commonEmmePath}\\${pythonPathPostfix}`,
63-
`\\Program Files (x86)\\${commonEmmePath}\\${pythonPathPostfix}`,
64-
`\\${commonEmmePath}\\${pythonPathPostfix}`,
62+
`\\Program Files\\${commonEmmePath}`,
63+
`\\Program Files (x86)\\${commonEmmePath}`,
64+
`\\${commonEmmePath}`,
6565
`usr/bin/python${pythonVersion.major}`, // mainly for developers on Mac & Linux
66+
`Users/erkki/testi`
6667
];
6768
const allPathCombinations = drives.reduce(
6869
(accumulator, d) => {
6970
// Combine each (d)rive to all (p)aths, and merge results via reduce
7071
return accumulator.concat(paths.map((p) => `${d}${p}`));
7172
}, []);
72-
const pythonInstallations = allPathCombinations.filter(fs.existsSync);
73+
const pythonInstallations = []
74+
allPathCombinations.forEach((pathCombination) => {
75+
const foundPythonEnv = hasPythonEnv(pathCombination);
76+
if (foundPythonEnv !== null) {
77+
pythonInstallations.push(foundPythonEnv);
78+
}
79+
});
7380
if (pythonInstallations.length > 0) {
7481
return [true, pythonInstallations];
7582
} else {
@@ -90,6 +97,31 @@ function getVersion(semver) {
9097
}
9198
}
9299

100+
function hasPythonEnv(basePath) {
101+
const pathExists = fs.existsSync(basePath) && fs.lstatSync(basePath).isDirectory();
102+
let exePath = null;
103+
if (pathExists) {
104+
const subPaths = fs.readdirSync(basePath)
105+
subPaths.forEach(path => {
106+
if(path.startsWith("Python")) {
107+
// Go through the subdirectory and look for a python executable
108+
const subPathFiles = fs.readdirSync(`${basePath}${getDirSeparator()}${path}`);
109+
subPathFiles.forEach(fileName => {
110+
if(fileName === 'python.exe') {
111+
exePath = `${basePath}${getDirSeparator()}${path}${getDirSeparator()}${fileName}`;
112+
}
113+
})
114+
}
115+
})
116+
}
117+
return exePath;
118+
}
119+
120+
function getDirSeparator() {
121+
const isRunningOnWindows = os.platform() === 'win32';
122+
return isRunningOnWindows ? '\\' : '/'
123+
}
124+
93125
module.exports = {
94126
searchEMMEPython: searchEMMEPython,
95127
listEMMEPythonPaths: listEMMEPythonPaths,

0 commit comments

Comments
 (0)