Releases: astra-uu-se/MPLACE
Releases · astra-uu-se/MPLACE
Version 1.3.5
Cleaning up of the codebase:
- (major) an earlier update (likely, 1.1) broke compatibility with Windows when passing the MiniZinc command to the OS. This change fixes is by ensuring that the Windows-specific command line is correct in function
_build_command()incore/minizinc_runner.py - (minor)
transform_coordinatenow returns a tuple instead of a list, preventing potential silent mutation of cached results via@lru_cache - minor code quality fixes: corrected
Optionalreturn type annotation, replacedraise ewith bareraise; fixed some docstrings, comments, indentation; replaced hardcoded plate dimension magic numbers with constants; removed a duplicated call toload_csv_file()in_load_csv_into_ui().
Version 1.3.4
- (major) on CSV load, the application now checks whether any well coordinates in the file exceed the currently configured plate dimensions; if out-of-bounds wells are found, a warning dialog reports the count and the dimensions the CSV implies versus the current settings; the check is skipped silently if no valid dimensions are set at load time (as unlikely as it is)
- (bug) fixed
_open_viz_window()inmplace.py: the main window was unlocked immediately afterVizView.show()was called due to an unconditionalfinally: unlock()wrapping the call; sinceshow()does not block, the main window became fully interactive while the visualization window was still open, allowing concurrent state mutations; thefinallyblock is removed and unlock is now delegated to a newon_window_closedcallback wired at construction time in_create_views(), mirroring the existing pattern used byDznView; as a secondary fix, the early-return guard for a missing CSV path is now placed beforelock()is called, so no lock is acquired in that branch - (bug) fixed
_parse_timeout_s()incontrollers/minizinc_controller.py: all exceptions were caught silently by a bareexcept Exception: return None, meaning a malformed or unreadable.mpcfile was indistinguishable from a file that simply has notime-limitkey; the handler is now split into specificFileNotFoundError/PermissionErrorandjson.JSONDecodeError/ValueErrorbranches, each emitting alogger.warningwith the offending path and error; a missingtime-limitkey remains silent, as that is the intended default behaviour - (bug) fixed
parse_dzn_file()incontrollers/main_controller.py: theFileNotFoundErrorhandler caught the exception and re-raised it asValueError, breaking the documented contract and causing any caller catchingFileNotFoundErrorto silently miss it; the handler now re-raises as-is with a bareraise; the same issue was present inload_csv_file()in the same file and is fixed identically - (bug) fixed
prepare_visualization()incontrollers/viz_controller.py: the catch-allexcept Exceptionat the bottom of the method re-wrapped any exception — includingFileNotFoundErrorraised by code after the initialread_csv_file()call — as a genericValueError, hiding the real cause; the handler is replaced with an explicitexcept (FileNotFoundError, ValueError): raisepass-through, so these meaningful signal types always propagate with their original type and message;VizView.show()inviews/viz_view.pyis updated accordingly to handleFileNotFoundErroras a distinct case with a dedicated "file not found" error dialog, rather than the generic "visualization failed" message shown for all other errors
Version 1.3.3
Input validation overhaul, DZN format improvements, and code quality:
- (major) added
core/model_validators.py: the validator that performs a comprehensive two-pass check over both COMPD and PLAID parameters before any model is run; checks are classified asBLOCK(hard constraint violation that will cause unsatisfiability or a crash) orWARN(recoverable issue the user should be aware of); results are aggregated and surfaced to the user as a single, structured diagnostic report rather than a silent failure or a bare MiniZinc assertion error; validator correctly distinguishes COMPD-only, PLAID-only, and shared checks, and labels each diagnostic with its originating model to avoid confusing users who run only one solver - (major) the messages produced by the validator are shown to the user; if both models are blocked, the user can not generate the dzn file; if only one model is blocked, the user is shown detailed compatibility message
- (major) added PLAID-specific unsatisfiability pre-check for
replicates_on_same_plate=true: when this flag is set with multiple plates, PLAID's internal E01/E02 assert is insufficient because it assumes even distribution across plates; the validator now independently verifies that the total compound load fits on a single plate, matching the actual constraint PLAID will encounter at solve time, and raises aBLOCKdiagnostic before MiniZinc is invoked - (major) added DZN file headers: generated
.dznfiles now include a structured metadata header comment block documenting compatibility with PLAID and COMPD; this makes saved DZN files self-documenting and easier to audit or archive - (major) resolved
dto/dzn_dataduplication: the intermediate data representation used during DZN generation was duplicated between the DTO file and the data assembly layer; the dataclass is now retained exclusively in the DTO file and imported where needed, removing the redundant definition and eliminating the risk of the two drifting out of sync - (bug) fixed missing
fprefix in_save_as_multiple_png()inviews/viz_view.py: the cancellation warning message used{i+1}and{n}as literal strings instead of interpolated values; corrected to an f-string (introduced in 1.3.2) - (bug) fixed
concentration_matrixdefault fill inconvert_pharmbio_to_plater_plate()incore/io_utils.py: empty wells were initialised to'0'instead of'', causing spurious zero-concentration entries in exported Plater files; corrected to matchdrugs_matrixwhich correctly uses'' - (minor) fixed misleading
# Remove headerinline comment inread_csv_file()incore/io_utils.py: header stripping happens insideconvert_to_pharmbio_format(), not at the return site; comment removed - (minor) removed dead import
pyplot as pltfromcontrollers/viz_controller.py: pyplot is never called directly in the controller; all figure creation is handled by the view layer - (minor) added
ArgsandReturnssections toplater_matrix_to_string()docstring incore/io_utils.py, consistent with the rest of the codebase's documentation standard - (minor) assert statements in VisualizationController.init() in controllers/viz_controller.py replaced with proper if/raise ValueError guards, as assert is a no-op under -O
- (minor) concentrations are now sorted at load time in
find_all_plates_concentrations()incore/layout_utils.py: values are sorted numerically where possible, with string values placed after numeric ones; this guarantees a consistent low-to-high order for all downstream consumers - (minor) added
ApplicationState.reset()method inmodels/application_state.pyand wired it into_set_program_state_to_default()inviews/main_view.py: previously, the UI reset cleared Tkinter variables but left the model layer (ApplicationState) holding stale file paths and plate parameters; recent files lists are intentionally preserved across resets - (minor) in create_material_scale(), replaced np.arange with a cleaner np.linspace(0.1, 0.9, num_conc)
- (minor) added a full docstring to _select_marker()
Version 1.3.2
Bug fixes, diagnostics improvements, and code quality:
- (major) rewrote
core/minizinc_runner.py: MiniZinc exit codes are now interpreted and shown to the user with actionable messages (e.g. exit code 2 "No solution exists for the current model and input data.", exit code 4 "Search finished without a conclusive result (often timeout or interrupted search).", exit code 255 "MiniZinc or the solver ran out of memory.");FileNotFoundErroris now distinguished from generic subprocess failures and produces a clear message pointing topaths.ini;process.kill()aftercommunicate()was removed (redundant no-op on a finished process); stdout/stderr decoding moved totext=TrueinPopen - (major) fixed
_open_viz_window()inmplace.py: the main window could remain permanently locked ifVizView.show()raised an exception beforewait_window; lock/unlock is now guarded withtry/finally - (minor) fixed
extract_csv_text()incore/io_utils.py:raise Exception(...)on=====UNSATISFIABLE=====replaced withraise RuntimeError(...); loop rewritten withenumerateto remove manual index tracking; he terminator-token guarde <= scould raiseTypeErrorwhenswas stillNone(terminator line appears before the CSV header) and was corrected tos is not None and e <= s - (minor) fixed
scan_csv_plater_matrices()incore/io_utils.py: all bareraise Exception(...)replaced withraise ValueError(...)for consistency with the rest of the codebase's error handling; tracking variableerenamed tocurrent_sectionfor clarity - (minor) fixed
_cleanup_canvas_widgets()inviews/viz_view.py: removed deadcanvas.get_renderer()call which had no effect and could raiseAttributeErrorif the canvas was partially torn down - (minor) fixed version header in
controllers/viz_controller.py(was1.3.0/ January 2026, out of sync with the rest of the codebase) - (minor) fixed version header in
controllers/csv_controller.pyandcontrollers/dzn_controller.py(both were behind1.3.1) - (minor) fixed
copyrightyear header incore/minizinc_runner.py(said2025) - (minor) improved
export_plater()incontrollers/csv_controller.py: if the user cancels partway through a multi-plate export, a warning dialog now informs them how many files were saved and that the already-saved files were kept; return type is now consistentlyList[str]([]for full cancel, partial list for mid-cancel) instead of mixingNoneandList[str] - (minor) improved
export_pharmbio()incontrollers/csv_controller.py: return type annotation corrected fromstrtoOptional[str] - (minor) improved
_generate_colors()incontrollers/viz_controller.py: alogger.warningis now emitted when more than 60 materials are present and colors begin to repeat, naming the first material affected - (minor) improved
DznController.validate_form_data()incontrollers/dzn_controller.py: empty-field check now returns early, preventing redundant and potentially confusing downstream validation errors;save_dzn_file()now opens files with explicitencoding='utf-8' - (minor) improved
_on_run_minizinc()inviews/main_view.py: added a one-line comment explaining why_show_startup_warningsis deferred viaroot.after(1, ...)instead of called directly, to prevent future inadvertent simplification breaking startup dialog ordering - (minor) extracted
_resolve_orientation()helper incontrollers/viz_controller.py: thenum_cols > num_rowsorientation-switching logic was duplicated independently inprepare_plate_axes()andplot_plate_wells(); both now delegate to a single helper - (minor) added Generate button visual feedback in
views/dzn_view.py: button is temporarily disabled and relabelled to "Generating…" during DZN generation, withupdate_idletasks()to force a repaint before the blocking call - (minor) fixed version and copyright headers in
core/validators.py: still atVersion: 1.0,Last Revision: October 2025,Copyright 2025; updated to match the rest of the codebase - (minor) fixed
core/minizinc_runner.py: removed unused importsList,Dict,Tuple,Union,Sequenceleft over from the previous refactor - improved
_save_as_multiple_png()inviews/viz_view.py: cancelling mid-export gave no feedback; a warning dialog now informs the user how many files were saved before cancellation, consistent with the behaviour added toexport_plater()last session - improved
_on_run_minizinc()inviews/main_view.py: when the model ran successfully but the user cancelled the CSV save dialog, no feedback was given; ashowinfodialog now confirms the model succeeded and that the save was skipped
Version 1.3.1
Major addition:
- added live elapsed-time indicator to the status label during MiniZinc execution ("Running MiniZinc: 35s / 180s" if timeout is set in the mpc file or "Running MiniZinc: 35s" if the timeout is not set); MiniZinc now runs in a background thread, so the UI remains responsive during long solves; timeout is read lazily from the solver's .mpc file at run time
Minor polish:
- fixed the visualization window event handling by removing the nested mainloop() from VizView.show(), which previously opened a second Tk event loop for a Toplevel window.
- Fixed the startup warning scheduling in mplace.py so _show_startup_warnings is passed to after() as a callback instead of being executed immediately.
- Fixed a UI lock issue in the MiniZinc workflow where cancelling layout export format selection could leave the main window locked.
- Fixed recent-file handling so opening a file from the Recent menu no longer re-adds the same file unnecessarily.
- Improved cleanup of visualization resources by removing the ineffective local del canvas pattern and relying on explicit widget and figure teardown instead.
- Improved consistency of the visualization window lifecycle by using normal Toplevel window behaviour instead of a second application loop.
- Cleaned up small documentation inconsistencies
Version 1.3.0
Bug fixing, slight refactoring, minor user experience improvements, and cleaning of dead code (in no particular order):
- fixed logic in mplace.py with respect to logging
- in extract_csv_text() from core/io_utils.py, initialized variable e as None
- differentiated FileNotFoundError and ValueError in convert_to_pharmbio_format() from core/io_utils.py
- when saving in plater format all saved csv files are listed in the recent csv files menu
- updated the message when user decides to reset the application (to be more precise)
- make sure that if loading a recent file fails, the focus stays on the application window
- de-hardcoded the visualization window position
- fixing several docstrings
- removing an extraneous dialogue windows on saving
- deleted some dead imports in the controllers/main_controller.py
- put three global variables from models/application_state.py into models/constants.py, for consistency
- (major) refactored core/io_utils.py to remove all UI concerns - write_figure(), write_figures_in_pdf(), and write_csv_file() are replaced by pure writer functions (save_figure_to_path(), save_figures_to_pdf(), save_csv_to_path()) that take a path and write to it; file dialog calls are now owned by the view/controller layer where they belong; core/io_utils.py no longer imports tkinter
- (major) fixed a bug in _on_run_minizinc() in views/main_view.py where the main window could potentially remain permanently locked if an exception was raised during CSV export after a successful model run
- (minor) fixed a bug in convert_to_pharmbio_format() in core/io_utils.py where a failed Plater format parse would show an error dialog and raise an exception, causing the user to see two error messages for the same event
- moved path_show() from core/io_utils.py to ui/ui_utils.py, as it takes a Tk widget argument and is a view concern
- csv_controller.export_pharmbio() and export_pharmbio.export_plater() now return None on user cancellation instead of -1/-2 sentinel strings; call sites updated accordingly
- removed the redundant info dialog that appeared before multiple PNG save dialogs in the visualization window; the save dialog title now indicates the plate number instead (e.g. "Save plate 2 of 4")
- fixed the DZN generation window position being computed once at construction time and never updated; it is now recalculated each time the window is opened, so it correctly follows the main window if the user moves it
- fixed startup warning dialogs appearing before the main window was fully drawn; warnings are now deferred to the first event loop iteration
- removed dead max_conc variable in create_material_scale() in controllers/viz_controller.py
- removed redundant list reconstruction [line[0]] + line[1:] in plot_plate_wells() in controllers/viz_controller.py; plate_data rows are now appended directly
- plt.get_cmap() calls in _generate_colors() replaced with matplotlib.colormaps[] (modern API); colormaps promoted to class-level constants to avoid repeated lookups
- fixed incorrect Args docstring in find_all_plates_concentrations() in core/layout_utils.py
- added missing docstring and fixed three typos in the inline comment of FigureProperties in models/constants.py
- inlined initialize_application_state() into MPlaceApplication.init() in mplace.py; module-level logger promoted accordingly
- write_figure / write_figures_in_pdf used [:-4] to strip extension instead of os.path.splitext() wich breaks for files without extension or with differently-sized extensions
- extract_csv_text silently returned an empty list when the CSV header is never found (s stays 0, e stays 0), masking the root cause
- variable e in scan_csv_plater_matrices was never initialized before the loop, as it was only assigned inside an if/elif block, making it technically unbound if execution order ever changes
- fixed silent dzn parse failure when opened from recent menu
- fixed a situation when the mainwindow stays locked if the user cancels format selection
- fixed a potential mismatch when loading CSV files could result in a partially updated UI if the file was invalid (the visualize button would be incorrectly enabled and the file path shown, even though no valid data was loaded)
- fixed misleading error messages when CSV export failed after a successful MiniZinc run (the label would incorrectly say "MiniZinc execution failed" even though the model ran fine)
- added an explicit import of messagebox in mplace.py, otherwise it could cause crashes if import order changed
- fixed file dialog titles to use proper capitalization ('Open DZN File' instead of 'open dzn file')
- removed accidental double underscore in button__set_program_state_to_default()
- fixed hardcoded "Visualize" string in menu operations (now uses MainMenu.VISUALIZE constant for consistency)
- removed redundant variable initializations in DznController.validate_form_data()
- removed dead path fields from ApplicationState that duplicated information already in AppConfig
- removed redundant in-method imports in _on_run_minizinc
- the "Run Model" button is now properly disabled during model execution to prevent concurrent runs
- deleted _refresh_recent_menus_path() method (redundant logic was inlined into _on_load_csv() with proper ordering)
- added module docstring to core/init.py to document the package's purpose and contents
- slightly improved error handling when CSV export fails after successful model execution (now displays context-specific error messages)
Version 1.2.5
Bug fixing and polishing:
- when the user selects to reset all fields, a confirmation window pops up (to avoid misclicks)
- new windows (generate dzn file, csv format selection, visualization) now disable main menu items and keyboard shortcuts to avoid potential issues
- enhanced main menu with new fields (Load files, running the model, etc)
- (major) fixed a bug when loading a csv-file in Plater format in
read_csv_file()function incore/io_utils.py, which resulted in not rendering the first replicate in a plate during the visualization - (minor) fixed
write_figure()function incore/io_utils.py, whech saved PDF pages withbbox_inches='tight', but the PNG branch omitted it. Titles, axis labels, and legends were potentially clipped in PNG output. - (minor) fixed a typo in the tooltip in the generate dzn-file window
- (minor) fixed loading the csv file redundantly just to count lines
- (minor) fixed duplicate controller creation in
views/main_view.py - slightly streamlined
path_show()function incore/io_utils.py(no change in functionality, just cleaner code) - deleted
validate_csv_lines()function fromcontroller/csv_controller.py(dead code) - printing the results of conversion to Plater format is now done as part of the logging protocol
- slightly updated logging (a couple of duplicated messages were removed)
- slightly changed the keybindings to avoid conflicts with system shortcuts
Version 1.2.4
- (minor) "Run model" button now is properly disabled when no dzn file is loaded (a bug introduced in 1.2.0)
- slightly enhanced the interface by adding keyboard shortcuts and expanding the main menu with new items (which also displays the keyboard shortcuts)
- When opening and closing file dialogues and other windows, main application will automatically retain focus
Version 1.2.3
- Updated COMPD to 1.3.4, which fixed an important bug introduced in 1.3.3 (see version history)
- Added a docstring to core/io_utils.py
- The visualization window can no longer be resized
- Visualization and create DZN file windows now disable the main window while opened.
Version 1.2.2
- Updated COMPD to 1.3.3 (see version history)
- Updated marker size logic: marker sizes now depend, within limits, on the plate dimensions (numbers of rows and columns)
- Fixed a bug when the node sizes for material scales were computed with an error due to a flawed logic of type conversions
- Fixed a number of configuration validation errors that were silently ignored instead of alerting users
- Corrected coordinate parsing logic that could fail without returning a value
- Fixed CSV format conversion when silently returned None on errors instead of raising exceptions
- Augmented file path and state management to address issues potentially affecting visualization functionality (in an unlikely scenario)
- Removed duplicate DZN loading method and streamlined file operation handling
- Improved error handling consistency and logging throughout the application