Changelog¶
Unreleased¶
Load from DB (new widget)
Lists every dataset registered in the
datasetsmetadata table via SQLAlchemy and pulls the selected one back into Orange as anOrange.data.Tableusingpandas.read_sql+Orange.data.pandas_compat.table_from_frame.Offers a Class column combo populated from the dataset’s columns, pre-selected with the user’s persisted choice or the
class_namerecorded by Save to DB. The outputTablealready exposes the chosen column asdomain.class_var, so no Select Columns widget is needed downstream.Same dialect selector (PostgreSQL / MySQL), connection-status label and
QThreadworker pattern as Save to DB. Both the metadata listing and the actual table read happen off the GUI thread.Workflow-persisted settings:
selected_datasetandselected_classareSetting(..., schema_only=True), restored as soon as the connection comes back up on reload.
Variable Dependency Graph
New: edges now carry numeric weights equal to the largest
|argument|among the temporal calls (shift,sum,mean,count,min,max,sd) in the source expression that reference the dependency. Plain (non-temporal) references default to weight1. The weights live innetwork.edges[0].edges.dataand are consumable by any downstream Network widget.Refactor: flattened the
from_row_col/grafodecorator pattern into a single, documentedbuild_dependency_networkfunction.Performance: O(n³) → O(n²) by precomputing a
name → indexmap and using asetto dedupe dependencies.Bugfix: an
IndexErrorwas raised when the input domain had a single column. The widget now checkslen(domain) >= 2before reading the second column.Removed 65 lines of dead commented-out code.
Time Features Constructor
Fixed (critical): time-window functions used to lose context every 5 000 rows because Orange chunks tables during
transform. The widget now caches the full source perFeatureFuncand returns the right slice for each chunk, soshift(x, -20)is correct across chunk boundaries on datasets of any size.Fixed: “Variables to generate” was being cleared after every Send and on every input change, so workflow save / reload lost the definitions. Storage moved from
ContextSettingtoSetting(..., schema_only=True)(matching upstream Orange v4) and the editor is now restored from the persisted descriptors on every input. The legacy context handler stays around to migrate old workflows.Each Send re-transforms the original input table instead of the cumulative output, removing the implicit “consume on Send” semantics and the duplicate-name errors that came with it.
evalhardened:__builtins__replaced with an empty dict in the expression evaluator. Only the curated whitelist (safe builtins +math+ selectedrandom/numpyhelpers) is exposed.Refactor:
modificar_expressioncollapsed from 7 near-identical loops to a single regex-driven pass.Bugfix:
FeatureEditor.editorDatareturned the variable name as the expression.
Save to DB
New: MySQL support. The connection panel now exposes a database-type selector (PostgreSQL / MySQL). Per-dialect column types and identifier quoting (
"name"vs\`name\`) live in a_Dialectabstraction.Performance: uploads now go through
Orange.data.pandas_compat.table_to_frame+DataFrame.to_sql(method='multi', chunksize=1000)over a SQLAlchemy engine. The old row-per-INSERT loop is gone — typical speedups are 50-100×, especially over the network. Identifier quoting and per-column DDL types are emitted by the SQLAlchemy dialect (DOUBLE_PRECISIONon PostgreSQL,DOUBLEon MySQL,DATETIMEinstead ofTIMESTAMPon MySQL to dodge the 2038 cap,VARCHAR(255)/TEXTeverywhere else).UX: the upload runs in a background
QThread(_UploadWorker), so the canvas stays interactive on long writes. A status label under the connection box reports Not connected / Connected to … : … / Uploading rows N/M… / Upload completed in Xs / Upload failed: … with colour cues. Save, Connect and the form fields are disabled while the worker runs and re-enabled on success or failure; the widget aborts the thread cleanly on close.Fixed (critical): SQL injection in the metadata
INSERT. The query is now fully parametrised.Added an identifier whitelist (
^[A-Za-z_][A-Za-z0-9_]{0,62}$) and aquote_identhelper that wraps identifiers with PostgreSQL-standard double quotes (with any internal"doubled). Used in everyCREATE TABLE/INSERT INTOthat touches user-supplied names.Replaced
psycopg2withpsycopg2-binaryin the install requirements so installation works on macOS, Linux and Windows without a C compiler.setup.pynow declaresinstall_requires;pip installwas previously not pulling in any runtime dependency.
Testing¶
The widget suite grew to 92 tests, covering:
Unit tests for
modificar_expression, the time-window helpers,_sanitize_name/_expression_or_noneand_temporal_weights.A regression test for the 5 000-row chunk bug (
shift(x, -20)over a 12 000-row table).eval-safety tests asserting that__import__/openraiseNameErrorwhilesqrt/absstill resolve.Widget-level tests (via
Orange.widgets.tests.base.WidgetTest) for descriptor persistence, including an end-to-endsettingsHandler.pack_data/stored_settings=round-trip that mirrors what Orange does when saving and reopening a.owsfile.Edge-weight tests covering single calls, three-argument families, mixed temporal / non-temporal references, max across multiple calls, and per-dependency independence.