Skip to content

Releases: astra-uu-se/MPLACE

Version 1.3.5

14 Apr 08:21
e3cb9a5

Choose a tag to compare

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() in core/minizinc_runner.py
  • (minor) transform_coordinate now returns a tuple instead of a list, preventing potential silent mutation of cached results via @lru_cache
  • minor code quality fixes: corrected Optional return type annotation, replaced raise e with bare raise; fixed some docstrings, comments, indentation; replaced hardcoded plate dimension magic numbers with constants; removed a duplicated call to load_csv_file() in _load_csv_into_ui().

Version 1.3.4

30 Mar 12:22
1fb9e89

Choose a tag to compare

  • (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() in mplace.py: the main window was unlocked immediately after VizView.show() was called due to an unconditional finally: unlock() wrapping the call; since show() does not block, the main window became fully interactive while the visualization window was still open, allowing concurrent state mutations; the finally block is removed and unlock is now delegated to a new on_window_closed callback wired at construction time in _create_views(), mirroring the existing pattern used by DznView; as a secondary fix, the early-return guard for a missing CSV path is now placed before lock() is called, so no lock is acquired in that branch
  • (bug) fixed _parse_timeout_s() in controllers/minizinc_controller.py: all exceptions were caught silently by a bare except Exception: return None, meaning a malformed or unreadable .mpc file was indistinguishable from a file that simply has no time-limit key; the handler is now split into specific FileNotFoundError/PermissionError and json.JSONDecodeError/ValueError branches, each emitting a logger.warning with the offending path and error; a missing time-limit key remains silent, as that is the intended default behaviour
  • (bug) fixed parse_dzn_file() in controllers/main_controller.py: the FileNotFoundError handler caught the exception and re-raised it as ValueError, breaking the documented contract and causing any caller catching FileNotFoundError to silently miss it; the handler now re-raises as-is with a bare raise; the same issue was present in load_csv_file() in the same file and is fixed identically
  • (bug) fixed prepare_visualization() in controllers/viz_controller.py: the catch-all except Exception at the bottom of the method re-wrapped any exception — including FileNotFoundError raised by code after the initial read_csv_file() call — as a generic ValueError, hiding the real cause; the handler is replaced with an explicit except (FileNotFoundError, ValueError): raise pass-through, so these meaningful signal types always propagate with their original type and message; VizView.show() in views/viz_view.py is updated accordingly to handle FileNotFoundError as 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

24 Mar 09:55
8730c5c

Choose a tag to compare

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 as BLOCK (hard constraint violation that will cause unsatisfiability or a crash) or WARN (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 a BLOCK diagnostic before MiniZinc is invoked
  • (major) added DZN file headers: generated .dzn files 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_data duplication: 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 f prefix in _save_as_multiple_png() in views/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_matrix default fill in convert_pharmbio_to_plater_plate() in core/io_utils.py: empty wells were initialised to '0' instead of '', causing spurious zero-concentration entries in exported Plater files; corrected to match drugs_matrix which correctly uses ''
  • (minor) fixed misleading # Remove header inline comment in read_csv_file() in core/io_utils.py: header stripping happens inside convert_to_pharmbio_format(), not at the return site; comment removed
  • (minor) removed dead import pyplot as plt from controllers/viz_controller.py: pyplot is never called directly in the controller; all figure creation is handled by the view layer
  • (minor) added Args and Returns sections to plater_matrix_to_string() docstring in core/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() in core/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 in models/application_state.py and wired it into _set_program_state_to_default() in views/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

17 Mar 10:29
8f5f0f6

Choose a tag to compare

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."); FileNotFoundError is now distinguished from generic subprocess failures and produces a clear message pointing to paths.ini; process.kill() after communicate() was removed (redundant no-op on a finished process); stdout/stderr decoding moved to text=True in Popen
  • (major) fixed _open_viz_window() in mplace.py: the main window could remain permanently locked if VizView.show() raised an exception before wait_window; lock/unlock is now guarded with try/finally
  • (minor) fixed extract_csv_text() in core/io_utils.py: raise Exception(...) on =====UNSATISFIABLE===== replaced with raise RuntimeError(...); loop rewritten with enumerate to remove manual index tracking; he terminator-token guard e <= s could raise TypeError when s was still None (terminator line appears before the CSV header) and was corrected to s is not None and e <= s
  • (minor) fixed scan_csv_plater_matrices() in core/io_utils.py: all bare raise Exception(...) replaced with raise ValueError(...) for consistency with the rest of the codebase's error handling; tracking variable e renamed to current_section for clarity
  • (minor) fixed _cleanup_canvas_widgets() in views/viz_view.py: removed dead canvas.get_renderer() call which had no effect and could raise AttributeError if the canvas was partially torn down
  • (minor) fixed version header in controllers/viz_controller.py (was 1.3.0 / January 2026, out of sync with the rest of the codebase)
  • (minor) fixed version header in controllers/csv_controller.py and controllers/dzn_controller.py (both were behind 1.3.1)
  • (minor) fixed copyright year header in core/minizinc_runner.py (said 2025)
  • (minor) improved export_plater() in controllers/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 consistently List[str] ([] for full cancel, partial list for mid-cancel) instead of mixing None and List[str]
  • (minor) improved export_pharmbio() in controllers/csv_controller.py: return type annotation corrected from str to Optional[str]
  • (minor) improved _generate_colors() in controllers/viz_controller.py: a logger.warning is 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() in controllers/dzn_controller.py: empty-field check now returns early, preventing redundant and potentially confusing downstream validation errors; save_dzn_file() now opens files with explicit encoding='utf-8'
  • (minor) improved _on_run_minizinc() in views/main_view.py: added a one-line comment explaining why _show_startup_warnings is deferred via root.after(1, ...) instead of called directly, to prevent future inadvertent simplification breaking startup dialog ordering
  • (minor) extracted _resolve_orientation() helper in controllers/viz_controller.py: the num_cols > num_rows orientation-switching logic was duplicated independently in prepare_plate_axes() and plot_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, with update_idletasks() to force a repaint before the blocking call
  • (minor) fixed version and copyright headers in core/validators.py: still at Version: 1.0, Last Revision: October 2025, Copyright 2025; updated to match the rest of the codebase
  • (minor) fixed core/minizinc_runner.py: removed unused imports List, Dict, Tuple, Union, Sequence left over from the previous refactor
  • improved _save_as_multiple_png() in views/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 to export_plater() last session
  • improved _on_run_minizinc() in views/main_view.py: when the model ran successfully but the user cancelled the CSV save dialog, no feedback was given; a showinfo dialog now confirms the model succeeded and that the save was skipped

Version 1.3.1

11 Mar 12:46
140c75e

Choose a tag to compare

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

10 Mar 15:58
6a069d8

Choose a tag to compare

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

06 Mar 09:52
f4bc886

Choose a tag to compare

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 in core/io_utils.py, which resulted in not rendering the first replicate in a plate during the visualization
  • (minor) fixed write_figure() function in core/io_utils.py, whech saved PDF pages with bbox_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 in core/io_utils.py (no change in functionality, just cleaner code)
  • deleted validate_csv_lines() function from controller/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

04 Mar 16:39
4ad850a

Choose a tag to compare

  • (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

17 Feb 09:52
8dd5aa0

Choose a tag to compare

  • 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

19 Jan 14:20
a0efe9b

Choose a tag to compare

  • 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