PyELSSA 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyelssa-0.1.0/LICENSE ADDED
@@ -0,0 +1,88 @@
1
+ Copyright (c) 2026 [Jieming Ye, Engineering Services Design]
2
+
3
+ Open Source Non-Commercial License (OSNCL) v2.0
4
+
5
+ Preamble
6
+ The purpose of this license is to ensure that the software remains free for non-commercial use, while also ensuring
7
+ that any modifications to the software are made publicly available. Commercial use of this software is not allowed
8
+ without prior written permission from the original author(s).
9
+
10
+ The precise terms and conditions for copying, distribution, and modification follow.
11
+
12
+ TERMS AND CONDITIONS
13
+ Section 0: Definitions
14
+
15
+ “This License” refers to version 2.0 of the Open Source Non-Commercial License.
16
+ “The Software” refers to the Python package and all associated files, including but not limited to source code,
17
+ documentation, and configuration files, distributed under this License.
18
+ “Modification” means any work derived from or based on the Software, such as bug fixes, enhancements, or other
19
+ alterations to the original work.
20
+ "E&P Team" refers to the E&P team under Engineering Services, Route Services, Network Rail Infrastructure Ltd,
21
+ structured at September 2024.
22
+ “You” refers to the individual or entity using, modifying, or distributing the Software.
23
+
24
+ “Public Distribution” refers to the act of making any modifications of the Software available to the general
25
+ public.
26
+
27
+ Section 1: Source Code and Modifications
28
+
29
+ 1.1 You may modify the Software and distribute your modified version, provided that:
30
+ You make the source code of the modified version available to the public under the same terms as this License.
31
+ The modified version must be clearly marked to indicate that changes have been made, including the date of changes.
32
+
33
+ 1.2 All modifications must be publicly available, in a manner that makes them easily accessible, such as posting
34
+ on a public repository (e.g., GitHub, GitLab).
35
+
36
+ Section 2: Non-Commercial Use
37
+
38
+ 2.1 You may use, distribute, and modify the Software for non-commercial purposes only, including:
39
+ Personal projects.
40
+ Educational purposes.
41
+ Research purposes.
42
+
43
+ 2.2 Commercial use of the Software is prohibited unless you have received explicit, prior written permission
44
+ from the original author(s).
45
+
46
+ 2.3 If you wish to make commercial use of the Software, including any modifications, you must contact the original
47
+ author(s) to seek permission and negotiate terms for such use.
48
+
49
+ Section 3: Conveying Modified Source Code
50
+
51
+ 3.1 If you modify the Software, you must:
52
+ Ensure that the modifications are licensed under this License.
53
+ Make the modified source code publicly available.
54
+ Provide information in any modified version about the changes made, including a notice stating the date of the
55
+ modification and who made the changes.
56
+
57
+ Section 4: Conveying Non-Source Forms
58
+
59
+ 4.1 You may convey non-source forms of the Software, such as compiled binaries, provided that:
60
+ You also provide access to the corresponding source code.
61
+ The source code is made available under the same terms as this License.
62
+
63
+ Section 5: Commercial Use Exception
64
+
65
+ 5.1 Commercial use of the Software or any modified versions is strictly prohibited unless permission is granted
66
+ in writing by the original author(s). For commercial inquiries, please contact [Traction.Power@networkrail.co.uk].
67
+
68
+ 5.2 Commercial use of the Software or any modified versions is allowed by [E&P Team] without any restriction.
69
+
70
+ Section 6: No Warranty
71
+
72
+ 6.1 There is no warranty for the Software, to the extent permitted by applicable law. The Software is provided
73
+ “as is” without warranty of any kind, either expressed or implied, including but not limited to the implied
74
+ warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and
75
+ performance of the Software is with you.
76
+
77
+ 6.2 In no event, unless required by law or agreed to in writing, shall the author(s) be liable for any damages,
78
+ including any general, special, incidental, or consequential damages arising from the use or inability to use the
79
+ Software.
80
+
81
+ Section 7: Termination
82
+
83
+ 7.1 You may not propagate or modify the Software except as expressly provided under this License. Any attempt
84
+ otherwise to propagate or modify the Software is void and will automatically terminate your rights under this
85
+ License.
86
+
87
+ 7.2 However, parties who have received copies, or rights, from you under this License will not have their
88
+ licenses terminated so long as they remain in full compliance.
pyelssa-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyELSSA
3
+ Version: 0.1.0
4
+ Summary: This is 'ELSSA in Python' package owned by Network Rail Infrastructure Limited.
5
+ License: Open Source Non-Commercial License (OSNCL) v2.0
6
+ License-File: LICENSE
7
+ Author: Jieming Ye
8
+ Author-email: Jieming.Ye@networkrail.co.uk
9
+ Requires-Python: >=3.10
10
+ Classifier: License :: Other/Proprietary License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Dist: matplotlib
19
+ Requires-Dist: numpy
20
+ Requires-Dist: pandas
21
+ Requires-Dist: psutil
22
+ Requires-Dist: scipy
23
+ Requires-Dist: tk
24
+ Project-URL: Homepage, https://pypi.org/project/PyELSSA/
25
+ Description-Content-Type: text/markdown
26
+
27
+ # ELSSA_python
28
+ ELSSA (Electrification System Simulation Analysis) was written in Matlab. The last supporting Matlab version is 2014a which is out of official support for a fairly long time.
29
+ Some of the source file is p coded (encrypted) which makes the support extremly difficult.
30
+
31
+ This repository looks to mordenlize the software suite to a new era, giving the technology involvement in the last decade.
32
+
33
+ The code will be rewritten from scratch in Python aiming for better user interface and program maintenance.
34
+
35
+ ## ELSSA Python Migration Plan
36
+ - Phase 1: Reverse engineering the p coded section.
37
+ - Phase 2: Replicate the ELSSA Matlab functionality.
38
+ - Phase 3: Enhance the tool package and user interface.
39
+
40
+
41
+ ## ELLSA Matlab
42
+ Please refer to: https://github.com/NR-ESTractionPower/ELSSA_matlab
43
+
44
+
@@ -0,0 +1,17 @@
1
+ # ELSSA_python
2
+ ELSSA (Electrification System Simulation Analysis) was written in Matlab. The last supporting Matlab version is 2014a which is out of official support for a fairly long time.
3
+ Some of the source file is p coded (encrypted) which makes the support extremly difficult.
4
+
5
+ This repository looks to mordenlize the software suite to a new era, giving the technology involvement in the last decade.
6
+
7
+ The code will be rewritten from scratch in Python aiming for better user interface and program maintenance.
8
+
9
+ ## ELSSA Python Migration Plan
10
+ - Phase 1: Reverse engineering the p coded section.
11
+ - Phase 2: Replicate the ELSSA Matlab functionality.
12
+ - Phase 3: Enhance the tool package and user interface.
13
+
14
+
15
+ ## ELLSA Matlab
16
+ Please refer to: https://github.com/NR-ESTractionPower/ELSSA_matlab
17
+
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["poetry-core>=1.0.0"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+
6
+ [tool.poetry]
7
+ name = "PyELSSA"
8
+ version = "0.1.0"
9
+ description = "This is 'ELSSA in Python' package owned by Network Rail Infrastructure Limited."
10
+ authors = ["Jieming Ye <Jieming.Ye@networkrail.co.uk>"]
11
+ readme = "README.md"
12
+ include = ["src"]
13
+ exclude = ["src/PyELSSA/core/*"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: Other/Proprietary License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ license = "Open Source Non-Commercial License (OSNCL) v2.0"
20
+ urls = { Homepage = "https://pypi.org/project/PyELSSA/" }
21
+
22
+
23
+ # Add this line to reference the requirements.txt file
24
+ [tool.poetry.dependencies]
25
+ python = ">=3.10"
26
+ tk = "*"
27
+ numpy = "*"
28
+ pandas = "*"
29
+ matplotlib = "*"
30
+ scipy = "*"
31
+ psutil = "*"
32
+
33
+ [tool.poetry.group.dev.dependencies]
34
+ flake8 = "*"
35
+ pytest = "*"
@@ -0,0 +1,28 @@
1
+ #
2
+ # -*- coding: utf-8 -*-
3
+ #=================================================================
4
+ # Created by: Jieming Ye
5
+ # Created on: Sep 2026
6
+ # Last Modified: XX/XX/2026
7
+ #=================================================================
8
+ # Copyright (c) 2026 [Jieming Ye]
9
+ #
10
+ # This Python source code is licensed under the
11
+ # Open Source Non-Commercial License (OSNCL) v1.0
12
+ # See LICENSE for details.
13
+ #=================================================================
14
+ """
15
+ Python on ELSSA: PyELSSA Package
16
+
17
+ """
18
+ #=================================================================
19
+ # VERSION CONTROL
20
+ # V1.0.0 (Jieming Ye) - Initial Version
21
+ #=================================================================
22
+ # Set Information Variable
23
+ __author__ = "Jieming Ye"
24
+ __copyright__ = "Copyright 2026, Engineering Services"
25
+ __credits__ = "Jieming Ye"
26
+ __version__ = "0.1.0"
27
+ __email__ = "jieming.ye@networkrail.co.uk"
28
+ __status__ = "Beta Version"
@@ -0,0 +1,325 @@
1
+ #
2
+ # -*- coding: utf-8 -*-
3
+ #=================================================================
4
+ # Created by: Jieming Ye
5
+ # Created on: July 2026
6
+ # Last Modified: July 2026
7
+ #=================================================================
8
+ # Copyright (c) 2026 [Jieming Ye]
9
+ #
10
+ # This Python source code is licensed under the
11
+ # Open Source Non-Commercial License (OSNCL) v1.0
12
+ # See LICENSE for details.
13
+ #=================================================================
14
+ """
15
+ This scripts contains the functions that control the configuration file settings
16
+
17
+ """
18
+ #=================================================================
19
+ # VERSION CONTROL
20
+ # V1.0 (Jieming Ye) - Initial Version
21
+ #
22
+ #=================================================================
23
+ # Set Information Variable
24
+ # N/A
25
+ #=================================================================
26
+
27
+ import os
28
+ import json
29
+ import importlib
30
+ import requests
31
+ import shutil
32
+
33
+ from PyELSSA.shared_contents import SharedVariables, SharedMethods, DataFilenames, InternetValidation
34
+ from PyELSSA.shared_msazure_api import AzureWorkflow, AzureCloudID
35
+
36
+ CONFIG_FILE = SharedVariables.config_file
37
+
38
+ # initial database system check
39
+ def initial_missing_user_database_check():
40
+ '''
41
+ This function doing application database entering checking and returing:
42
+ FALSE: if the initial database check passed i.e. no missing files.
43
+ Filenamelist: if the initial database check failed.
44
+ '''
45
+ filenamelist = []
46
+ # check if the database path exist.
47
+ if not os.path.exists(SharedVariables.database_path_user):
48
+ for filename in DataFilenames.private_file.values():
49
+ filenamelist.append(filename)
50
+ for filename in DataFilenames.public_file.values():
51
+ filenamelist.append(filename)
52
+
53
+ # loop the database folder and add the missing files into the filename list
54
+ else:
55
+ for filename in DataFilenames.private_file.values():
56
+ file_in_package = os.path.join(SharedVariables.database_path_user, filename)
57
+ if not os.path.isfile(file_in_package):
58
+ filenamelist.append(filename)
59
+ for filename in DataFilenames.public_file.values():
60
+ file_in_package = os.path.join(SharedVariables.database_path_user, filename)
61
+ if not os.path.isfile(file_in_package):
62
+ filenamelist.append(filename)
63
+
64
+ # if not empty filename list return filenamelist otherwise False
65
+ if filenamelist:
66
+ return filenamelist
67
+ else:
68
+ return False
69
+
70
+ # update the customised database library
71
+ @staticmethod
72
+ def update_database_library(filenamelist: list=None, library: int = 0):
73
+ '''
74
+ This function updates the customised database library from GitHub Repo online (if public)
75
+ or data folder (if private).
76
+ This will overwrite the existing database file blindly.
77
+
78
+ if no input is not provided, this will restore all default database library
79
+
80
+ library: (int)
81
+ default to 0: default library
82
+ 1: user library
83
+ '''
84
+ if library == 0:
85
+ controlled_data_path = SharedVariables.database_path_default
86
+ else:
87
+ controlled_data_path = SharedVariables.database_path_user
88
+
89
+ print(f"Config Path: '{controlled_data_path}'")
90
+
91
+ os.makedirs(controlled_data_path, exist_ok=True)
92
+
93
+ private_file_list = []
94
+ public_file_list = []
95
+
96
+ if filenamelist:
97
+ for filename in filenamelist:
98
+ # check if the filename belongs public or private dataset, and update accordingly
99
+ if filename in DataFilenames.private_file.values():
100
+ private_file_list.append(filename)
101
+ elif filename in DataFilenames.public_file.values():
102
+ public_file_list.append(filename)
103
+ else:
104
+ SharedMethods.print_message(f"ERROR: File '{filename}' is not recognised as a valid database file. Please contact support using this error message...", "31")
105
+ return False
106
+ else: # default to updating all database files
107
+ for file in DataFilenames.private_file.values():
108
+ private_file_list.append(file)
109
+ for file in DataFilenames.public_file.values():
110
+ public_file_list.append(file)
111
+
112
+ # copy the private data from the packaged data folder
113
+ distribution = importlib.resources.files(SharedVariables.package_name)
114
+ package_path = os.path.join(str(distribution), SharedVariables.private_data_name)
115
+
116
+ # Get the absolute path of the file in the package location
117
+ for file in private_file_list:
118
+ file_in_package = os.path.join(package_path, file)
119
+ target_file = os.path.join(controlled_data_path, file)
120
+ try:
121
+ shutil.copy(file_in_package, target_file)
122
+ SharedMethods.print_message(f"ATTENTION: '{file}' database updated to default dataset.","33")
123
+ except Exception as e:
124
+ SharedMethods.print_message(f"ERROR: Error copying file {file} to {target_file}: {e}. Please contact support using this error message...", "31")
125
+ return False
126
+
127
+ # check online resources and download the public data to the same folder
128
+ # Check internet connection
129
+ if not InternetValidation.internet_check(SharedVariables.public_data_path):
130
+ SharedMethods.print_message(f"WARNING: Online databse updated cannot proceed due to not allowed network. Skipping...","33")
131
+ return False
132
+
133
+ for file in public_file_list:
134
+ file_url = SharedVariables.raw_data_url_begin + file
135
+ # download the csv file to the target folder
136
+ target_file = os.path.join(controlled_data_path, file)
137
+ try:
138
+ response = requests.get(file_url)
139
+ response.raise_for_status() # Raise an error for bad responses
140
+ with open(target_file, 'wb') as f:
141
+ f.write(response.content)
142
+ SharedMethods.print_message(f"ATTENTION: '{file}' database updated to default dataset.","33")
143
+ except Exception as e:
144
+ SharedMethods.print_message(f"ERROR: Error downloading file {file} from {file_url}: {e}. Please contact support using this error message...", "31")
145
+ return False
146
+
147
+ return True
148
+
149
+ # reset configuraiton file varile to default
150
+ def reset_configuration_file_varible(key: str = "DEFAULT_ALL"):
151
+ """
152
+ This function reset configuration varible to default
153
+ return True of False
154
+ """
155
+ try:
156
+ if not os.path.exists(SharedVariables.config_file):
157
+ SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
158
+ return False
159
+
160
+ config_data = DataFilenames.default_config
161
+
162
+ if key == "DEFAULT_ALL":
163
+ with open(SharedVariables.config_file, "w") as file:
164
+ json.dump(config_data, file, indent=4)
165
+ return True
166
+ else:
167
+ return set_configuration_file_variable(key,config_data[key])
168
+
169
+ except Exception as e:
170
+ SharedMethods.print_message(f"ERROR: Error resetting configuration: {e}","31")
171
+ return False
172
+
173
+
174
+ # set configuration file
175
+ def set_configuration_file_variable(key:str, value):
176
+ """
177
+ This function handle the configuration file value update
178
+ return True or False
179
+ """
180
+ try:
181
+ if not os.path.exists(CONFIG_FILE):
182
+ SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
183
+ return False
184
+
185
+ with open(CONFIG_FILE, "r") as file:
186
+ config_data = json.load(file)
187
+
188
+ if config_data.get(key) != value:
189
+ config_data[key] = value
190
+ with open(CONFIG_FILE, "w") as file:
191
+ json.dump(config_data, file, indent=4)
192
+ return True
193
+
194
+ except Exception as e:
195
+ SharedMethods.print_message(f"ERROR: Error setting configuration: {e}","31")
196
+ return False
197
+
198
+ # read configuration file varible
199
+ def read_configuration_file_variable(key:str, update_required: bool = False):
200
+ """
201
+ This function read the configuration file and return value
202
+ return Value or False / None
203
+ """
204
+ try:
205
+ if not os.path.exists(CONFIG_FILE):
206
+ SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
207
+ return False
208
+
209
+ config_data = {}
210
+ with open(CONFIG_FILE, "r") as file:
211
+ config_data = json.load(file)
212
+
213
+ if key not in config_data:
214
+ SharedMethods.print_message(f"WARNING: Configuration does not contain setting of {key}. ","33")
215
+ if update_required:
216
+ SharedMethods.print_message(f"WARNING: {key} will be set to default value. ","33")
217
+ return False
218
+
219
+ return config_data[key]
220
+
221
+ except Exception as e:
222
+ SharedMethods.print_message(f"ERROR: Error reading configuration: {e}","31")
223
+ return False
224
+
225
+ def version_updated():
226
+ try:
227
+ if os.path.exists(SharedVariables.config_file):
228
+ with open(SharedVariables.config_file, 'r') as file:
229
+ config_data = json.load(file)
230
+ if not config_data["package_version_number"] == SharedVariables.installed_version:
231
+ return True
232
+ return False
233
+ except Exception as e:
234
+ SharedMethods.print_message(f"ERROR: Error reading configuration: {e}","31")
235
+ return False
236
+
237
+ def update_elssa_core():
238
+ try:
239
+ # ensure the installation folder location exist
240
+ if os.path.exists(SharedVariables.package_path):
241
+ # validate if configuration path folder exist if not, create it
242
+ os.makedirs(SharedVariables.elssa_core_path, exist_ok=True)
243
+ # download the latest
244
+ if not AzureWorkflow.download_tdns_sharepoint_folder(AzureCloudID.DOC_LIBRARY,AzureCloudID.ELSSA_CORE_PATH,SharedVariables.elssa_core_path,True):
245
+ return False
246
+ return True
247
+
248
+ else:
249
+ SharedMethods.print_message(f"ERROR: {SharedVariables.package_name} has not been installed yet...","31")
250
+ return False
251
+ except Exception as e:
252
+ SharedMethods.print_message(f"ERROR: Error updating the PyELSSA core: {e}","31")
253
+ SharedMethods.print_message(f"ERROR: Please check the message above. Try again with proper internet or contact support...","31")
254
+ return False
255
+
256
+ # processing of checking configuraiton file and set up approprite action
257
+ def configuration_main():
258
+ # disable in debug mode (bypass the process in debug mode):
259
+ if SharedMethods.is_debug_mode():
260
+ return True
261
+
262
+ # validate if configuration path folder exist if not, create it
263
+ os.makedirs(SharedVariables.configuration_path, exist_ok=True)
264
+
265
+ # update default database if configuration file does not exist (1st run only)
266
+ if not os.path.exists(CONFIG_FILE):
267
+ # create empty configuration file.
268
+ try:
269
+ with open(CONFIG_FILE, "w") as file:
270
+ json.dump({}, file)
271
+ except Exception as e:
272
+ SharedMethods.print_message(f"ERROR: Error creating configuration: {e}","31")
273
+ return False
274
+
275
+ # version update steps
276
+ try:
277
+ if version_updated():
278
+ # after the version udpate, elssa core is forced to be update, set to false
279
+ set_configuration_file_variable(key = 'elssa_core_updated',value = False)
280
+
281
+ # download the ELSSA core from the SharePoint
282
+ if not read_configuration_file_variable(key = 'elssa_core_updated'):
283
+ # trying the update the default library if success or not.
284
+ if not update_elssa_core():
285
+ # update not successful, force exit until update completed to ensure consistent version
286
+ return False
287
+ else:
288
+ # write the status to json configuraiton file
289
+ set_configuration_file_variable(key = 'elssa_core_updated',value = True)
290
+ SharedMethods.print_message(f"INFO: ELSSA Core Package Updating Succesfully. Consider relauch the application for proper reloading.","32")
291
+
292
+ if not read_configuration_file_variable(key = 'default_database_updated', update_required = True):
293
+ # trying the update the default library if success or not.
294
+ if not update_database_library():
295
+ # update not successful
296
+ database_library = False
297
+ else:
298
+ # update succesfully
299
+ database_library = True
300
+ # write the status to json configuraiton file
301
+ set_configuration_file_variable(key = 'default_database_updated',value = database_library)
302
+
303
+ set_configuration_file_variable(key = 'package_version_number', value = SharedVariables.installed_version)
304
+ return True
305
+
306
+ except Exception as e:
307
+ SharedMethods.print_message(f"ERROR: Error reading / writing configuration: {e}","31")
308
+ return False
309
+
310
+
311
+ def main():
312
+ if not configuration_main():
313
+ SharedMethods.print_message(f"ERROR: Configuration environment is not set properly. Close and try it again...","31")
314
+ SharedMethods.print_message(f"ERROR: Contact support {SharedVariables.contacts} if the issue persist...","31")
315
+ return False
316
+ else:
317
+ # database folder check: This is on customised user database only
318
+ filenamelist = initial_missing_user_database_check()
319
+ if filenamelist:
320
+ update_database_library(filenamelist=filenamelist,library=1)
321
+ return True
322
+
323
+ # Programme running
324
+ if __name__ == '__main__':
325
+ main()
File without changes
File without changes
File without changes