Tags give the ability to mark specific points in history as being important
-
1.0.2a2
Release: Release 1.0.2a2a978eaeb · ·# 1.0.2a2 - Alpha 2 Second alpha of the 1.0.2 line, additive over 1.0.2a1 apart from the TLS fix, which restores connections on Python 3.12. It adds the enaio document digest as a pair of operations: one builds the value locally from the files at hand, the other asks the server which archived documents carry it, so a file can be matched against the archive before it is uploaded. Added: - `calculate_digest()` on both DMS namespaces computes the enaio document digest locally, without contacting the server: SHA256 per file, each rendered as an uppercase hex string, concatenated in file order, then SHA256 over the ASCII bytes of that string, so a one-file document is not the plain SHA256 of its file either. It takes one file source or an iterable of them in document order and accepts bytes-like content, a `str`/`os.PathLike` path, a `JobRequestFile`, a `JobResponseFile` or an open binary stream; sources are read in chunks, so a multi-gigabyte document costs one chunk of memory. The result is uppercase like the server's and compares equal to `ECMDocumentDigest.table_digest` without normalisation; the async variant hashes in a worker thread instead of blocking the event loop (#16111) - `objects_by_digest()` on both DMS namespaces (`dms.GetObjectsByDigest`) is the server-side half of the same check: which archived documents hash to a given digest, looked up in `osdochash` and validated for existence and visibility. Each hit is a frozen `ECMDigestHit` with `id` and `type_id` plus the derived `main_type` and `cotype`, so the composite type needs no second job. Several documents can share one digest, versions are not considered, the lookup is case insensitive, a truncated server answer raises `ECMException` instead of silently reporting fewer hits, and a blank digest raises `ValueError` (#16111) Fixed: - TLS connections failed on Python 3.12 with `CERTIFICATE_VERIFY_FAILED: self-signed certificate in certificate chain`, while 3.13 and later worked. It is a stdlib default: `ssl.create_default_context()` gained `VERIFY_X509_PARTIAL_CHAIN` in 3.13 and the connect path relied on it without saying so, although `DEFAULT_CA_DATA` pins the server certificates themselves, which are leaves. The flag is now set explicitly in a shared `build_ssl_context()` (`rpc.api`) used by `sync_connect()` and `async_connect()`; `VERIFY_X509_STRICT` is left alone and `verify_mode` stays `CERT_REQUIRED`. The CI unittest job is pinned to Python 3.12 via `UV_PYTHON`, so the lowest supported version is actually tested (#16117) Both digest methods were verified against a 12.0 server, the TLS fix on Python 3.12, 3.13 and 3.14; the new behaviour is covered by offline unit tests and a server integration test. Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.2a1...1.0.2a2
-
1.0.2a1
Release: Release 1.0.2a12b3d9762 · ·# 1.0.2a1 - Alpha 1 First alpha of the 1.0.2 line, additive over 1.0.1 apart from the three behaviour changes noted below. It makes table fields work in LOL queries and under a divergent internal name, lets the query builder accept the spellings everyone expects (a list in `in_()`, a numeric string as an object ID, a subscript that behaves like an attribute), restores model fields declared by annotation on Python 3.14, and closes a set of defects around parameterised queries, the HOL streamer, workflow variables, the file cache limit and lost pool connections. Added: - `with_table_fields()` on all four query builders requests every table field the model declares, without naming each one; it is required in LOL mode, where a table field is never part of the result unless it is asked for, and useful in HOL mode to restore the table fields that `fields()` drops (#15926) Changed: - Subscript access mirrors attribute access: `model["Field"]` yields the `ECMTableList` of a declared table field instead of `None`, both the attribute name and the ECM internal name resolve, `__setitem__` converts rows the way `ECMTableField.__set__` does, `ECMTableRowModel.__getitem__` follows the same rules for columns, and both classes gained a `__contains__`. Behaviour change: a name that is neither declared nor loaded raises `KeyError` instead of returning `None`, which is also what template engines want (#15929) - `in_()` and `not_in()` accept the values as one iterable, so `in_(["A", "B"])` and `in_("A", "B")` build the same condition; the list used to be wrapped in another list and serialised into a single `<Value>`, which matched nothing on a text field and aborted with `-1060` on `system.id`. A container left among the values raises `TypeError`, and `convert_field_value_to_str` rejects containers as a second line of defence. Behaviour change: an empty value set raises `ValueError`, where the server used to drop the condition and return everything the remaining conditions allowed (#15930) - Every ID parameter of `ecm.dms` accepts a numeric string and converts it at the client edge, across `get()`, `history()`, `check_permission()`, `variants()`, `checkout()`, `checkin()`, `files()`, `insert()`, `upsert()`, `move()`, `copy()`, `delete()` and the rest, in both flavours; a string that is not a number raises `ValueError` naming the parameter before any job runs, and a `float` raises `TypeError` (#15931) - LOL table field rows are typed and carry their `row_id`: the LOL path runs through the same `DmsContentObjectRowField` and `_from_parser_row` as HOL, so a cell is converted to its declared Python type and `row.TableCol1` resolves, where the rows used to be raw display strings keyed by the column's display name (#15926) - `fields()` no longer hangs when a table field is passed as a descriptor (#15926) - `stream_lol_rows` no longer reports the nested table rows of a LOL response as hits (#15926) - Both incremental readers release the last item of a list at its boundary, so the yielded item is always the first remaining child of its container (#15926) Fixed: - `file_cache_byte_limit` now actually reaches the RPC layer: both pool clients, the notification receive loops and the deprecated `TcpClient` / `TcpPoolClient` called into the RPC layer without it. Behaviour change with default settings: the threshold at which a response file is written to disk instead of being kept in memory moves from ~3.2 MiB to the documented 32 MiB (#16019) - `stream_hol_objects` binds only to top-level `<Archive>` elements, so an `<Archive>` nested below `<Object>/<ExternalObjects>` no longer becomes the archive of every object yielded after it (#16025) - Table fields with a divergent `internal_name` keep their rows: the constructor and both parser factories keyed `_raw_table_fields_` by the Python attribute name while the descriptor reads the ECM internal name, so `obj.Positions.append(row)` landed in a throwaway list; all five places now resolve through the same attribute-or-internal-name mapping, and constructor keyword arguments accept the internal name as well (#16024) - The workflow model parser reads variables from the process level only: `schema()` scanned every `<DataFields>` element at any depth, including the activity settings each `<Activity>` carries, which would have produced duplicate or phantom variables (#16026) - Directly constructed table rows no longer share one dict across the whole process: `ECMTableRowModel` declared its field storage as mutable class attributes, so `a.user = "SVALF"; b.user = "SVKOE"` left `a.user` reading `"SVKOE"`. The class has its own `__init__` now, `InvoiceRow(ArticleNo="A-100")` works like the model classes, and the mutable class-level defaults are gone from `_ECMModelBase` as well (#15927) - Reading a column off a table row type-checks: the instance overload of `ECMField.__get__()` named only `_ECMModelBase`, so pyright rejected `row.ArticleNo` although it worked at runtime (#15927) - Model fields declared by annotation work again on Python 3.14: PEP 649 made annotations lazy, so `ECMModelMeta` read an empty dict and silently created no descriptors at all, leaving `Model.Title`, `order_by()` and `fields()` raising `AttributeError` for the declaration form the documentation teaches. No data was lost while this was broken; only the attribute surface and the mandatory-field check were missing (#16014) - `<Params>` is written before `<Archive>`, so parameterised conditions apply again: the server ignores a parameter block that follows the archive rather than rejecting it, so every `ParamValue` condition was silently dropped and the query widened to the whole object type (#16010) - The bundled XSDs under `tests/testdata/` describe what the server actually sends: LOL table field columns, `TxtNoticeCount`, `PdfAnnotationCount`, the document-only tail of `BaseParams`, three `FileProperties` attributes, optional `ExternalObjects` / `Fields`, the `Param` name attribute and five missing operators (#16011) - The async pool notices a lost connection the moment it happens: `async_connect` installs a `connection_lost` hook on every pooled connection, the pool deregisters and evicts it immediately, and the next call transparently gets a fresh connection instead of failing with `ConnectionResetError('Connection lost')`. A graceful close counts as a loss as well, which is the shape a server-side idle timeout usually takes and the only shape it takes on Windows (#15859) Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.1...1.0.2a1 -
1.0.1
Release: Release 1.0.13663b53e · ·# Release 1.0.1 — Changes since 1.0.0 ## Breaking changes - `ECMUserAttributes.last_modified` is replaced by `profile_changed: bool` — the `geaendert` attribute of `mng.GetUserAttributes` is a 0/1 profile-changed flag, not a timestamp - `ecm.dms.undo_checkout()` now sends `Flags=1` by default (undo from another station); pass `allow_other_station=False` for the previous `Flags=0` behaviour - `ecm.dms.upsert()`: emptying a table field without `.replace_table_fields(True)` now raises `ValueError` instead of silently changing nothing; value-less rows no longer clear a table ## Added - `ecm.system.delete_user_data()` and `ecm.system.get_user_data_names()` (#15865), sync + async - Connection-pool keepalive (`keepalive_interval` on `SyncPoolClient` / `AsyncPoolClient`) via `krn.CheckServerConnection`, plus explicit shutdown: `close()` / `aclose()` and context-manager support for both pool clients - `ecm.security.check_user_account()` (login check: exists / password / lockout / expiry) and `ecm.security.check_password_complexity()` (server password rule), sync + async - Document variants: `ecm.dms.set_active_variant()`, `obj.system.active_variant`, `ecm.dms.variants()` / `ecm.dms.active_variant()` (model-less), `level` on variant classes - `ecm.portfolio` namespace (search / get / create / delete / add / remove / clear, sync + async) — replaces the separate `ecmind-blue-client-portfolio` extra package - `set_server_timezone()` / `get_server_timezone()` + `ECMIND_BLUE_SERVER_TIMEZONE` env var; `tzdata` as runtime dependency on Windows - `result_as_file()` on the query builders and public streaming parsers `stream_lol_rows` / `stream_hol_objects` for very large hit lists - Result code `-1042218022` (missing system role) is translated into `ECMAccessDeniedException` ## Changed - `ecm.dms.undo_checkout()` gains keyword-only `allow_other_station: bool = True` (`Flags=1` on `dms.UndoCheckOutDocument`); verified against 12.0: foreign station fails with `-1040` without the flag, a different user may never undo (`-1039`) - Query conditions are typed against the declared field type (#15868) — `==`, `!=`, `<`, `<=`, `>`, `>=`, `in_()`, `not_in()`, `between()` are generic over the field type (pyright-checked) - Timestamps no longer depend on the client's timezone — every epoch conversion goes through the installation timezone (read **and** write, incl. `concurrency_timestamp`) - Performance: `dms.GetResultList` / `dms.GetObjectDetails` inline instead of response file (−18…−54 % measured); paging parses with `iterparse` (peak memory 222 MB → 1.2 MB per 44 MB page); `DmsContentParser` no longer builds a child-to-parent map - `lxml` evaluated and rejected (21 % slower DOM, 40 % slower streaming) ## Fixed - `and` / `or` / `not` on a query condition raise `TypeError` instead of silently dropping one condition; `where()` rejects non-condition arguments - `obj.system.rights` reported every right as granted (`bool("0")` is `True`); rights are compared now, unevaluated rights read as `None`, and `.rights()` requests the insert quotas - `ecm.dms.upsert()` can empty a table field again via `.replace_table_fields(True)` (#15842); `ecm.dms.update()` can empty a table field again (#15826) - `ecm.dms.move()` / `copy()` file into the target register instead of the folder (#15838) - A cancelled `NotificationSubscription.stop()` closes the callback channel again (#15827); orphaned hubs raise `ECMWrongStateException` instead of being silently reused - `ECMSystemRole`: eight `UNKNOWN_*` placeholders replaced with real names, missing role 54 `ACTVERSYS_START` added - Catalog enums and `time` values reach queries as their real value (#15868) - `DmsContentDocumentVariant.is_active` and `RowField.system` no longer use `bool()` on raw attribute strings; `doc_parent` is `None` for the root variant - `AsyncPoolClient` cleanup task could never fire (strong ref through bound method) — fixed - RPC layer: short reads on fixed-size fields, `sendall` instead of `send`, EOF raises `BlueProtocolException` instead of spinning forever, `readexactly` for cancelled reads, temp files deleted on every error path, `sync_connect` closes its socket on failure, `value_offset` in UTF-8 bytes, `BIGINT` parameters sendable, `%` in SQL string literals no longer misread as a placeholder ## Documentation & examples - LOL query page merged into `select` (old page redirected via page alias) - New guides: batch import (sync + async), connection-pool keepalive, server timezone; quickstart substantially expanded - New runnable examples: CSV-driven batch importer incl. sample invoices - New skills: user data, portfolio, security checks, document variants -
1.0.1a2
Release: Release 1.0.1a241758351 · ·# 1.0.1a2 - Alpha 2 Second alpha of the 1.0.1 line, additive over 1.0.1a1 apart from the two behaviour changes noted below. It keeps pooled connections alive across idle timeouts, adds the `ecm.portfolio` namespace, credential and password-rule checks in `ecm.security`, variant reading and activation in `ecm.dms`, and fixes a set of defects around object rights, table fields, register targets and cancelled notification teardowns. Added: - Keepalive for the connection pool: `keepalive_interval` on `SyncPoolClient` / `AsyncPoolClient` probes idle pooled connections with `krn.CheckServerConnection`, so NAT, firewall and load-balancer idle timeouts no longer drop connections silently; failures close the connection, are logged at DEBUG only and are counted separately in `ConnectionStats.keepalive_count` / `last_keepalive_at` (#15859) - Explicit pool shutdown: `SyncPoolClient.close()` and `AsyncPoolClient.aclose()`, plus (async) context-manager support on both pool clients; garbage collection stays a fallback rather than the documented path - `ecm.portfolio` covers enaio portfolios ("Mappen") with `search()`, `get()`, `user_favorites()`, `create()`, `delete()`, `delete_for()`, `add_objects()`, `remove_objects()` and `clear_objects()`, returning frozen `ECMPortfolio` / `ECMPortfolioObject` data classes; this replaces the former `portfolio` extra and its separate `ecmind-blue-client-portfolio` package - `ecm.security.check_user_account()` checks whether a user can log in (`krn.CheckUserAccount`) and reports existence, password match, lock state and remaining password validity as `ECMUserAccountCheck` / `ECMUserAccountStatus`; needs the `SERVER_SWITCH_JOB_CONTEXT` system role and counts failed attempts towards the account lockout like a real login - `ecm.security.check_password_complexity()` checks a password against the server's `Login\PwdComplexity` rule (`krn.VerifyPassword`); despite the job name this is not a credential check - `ecm.dms.set_active_variant()` switches the active variant of an existing W-document and resolves the previously active variant itself; activating the already-active variant performs no job at all - `obj.system.active_variant` returns the active variant of a document loaded with `variants=True` without a server call - `ecm.dms.variants()` and `ecm.dms.active_variant()` read a document's variant tree from an object ID alone, and `ECMModelDocumentVariant.walk()` flattens a node with its descendants - Document variants carry their nesting depth as `level`, on `ECMModelDocumentVariant` and `DmsContentDocumentVariant` - Result code `-1042218022` ("a system role required for this job is missing") is translated into `ECMAccessDeniedException` instead of a raw `BlueException` Fixed: - `obj.system.rights` reports the actual permissions instead of granting everything: the five right attributes were evaluated with `bool()` on the raw string, and `bool("0")` is `True`, so a rights-aware user interface could never hide or lock anything. The values are compared now, an unevaluated right reads as `None`, `DmsContentObjectRights.evaluated` tells the cases apart, and an unevaluated `<Rights>` element no longer populates the model (#15860) - `.rights()` and `rights=True` request the insert quotas (`ObjectInserts=1`) as well, without which `ECMModelRights.insert` would have been permanently `False` (#15860) - `DmsContentObjectRowField.system` shared the same `bool()` cast and reported every LOL table column as a system column - `ecm.dms.upsert()` can empty a table field with `.replace_table_fields(True)` and no longer clears one by accident: `ECMTableList` records whether the caller touched a list, a touched but empty list is emitted as an empty `<TableField/>`, and value-less rows no longer clear a table. Behaviour change: emptying a table field without deciding on `.replace_table_fields()` raises `ValueError` instead of sending a request that silently changes nothing (#15842) - `ecm.dms.update()` can empty a table field again: the emptied table was detected and `REPLACETABLEFIELDS=1` was set, but the `<TableField>` element was skipped, so the server had nothing to replace the rows with and reported success (#15826) - A cancelled `NotificationSubscription.stop()` closes the callback channel again: teardown now frees channel, receiver and hub state synchronously before the first await, the best-effort remainder is shielded from the caller's cancellation, a hub that lost its receiver raises `ECMWrongStateException` instead of being reused, `open_callback` closes its socket on any `BaseException`, and channel generations keep an outlived receiver from reading a reopened channel. The synchronous hub had the same window without any cancellation and gets the same treatment (#15827) - `ecm.dms.move()` and `ecm.dms.copy()` file into the target register again instead of the folder: the server identifies the register by the pair `register_id` + `register_type`, and the type is now taken from the argument, the instance, the class, or one `dms.GetObjectTypeByID` lookup as a last resort (#15838) - `AsyncPoolClient`'s cleanup task could never fire, because the coroutine held the pool through a bound method and the weak reference it polled never became `None`; idle connections stayed open until the event loop was torn down - `DmsContentDocumentVariant.is_active` evaluated the raw attribute with `bool()`, so inactive variants sent as `is_active="0"` reported `True` - `ECMModelDocumentVariant.doc_parent` is `None` for the root variant again: a 12.0 server sends `0` where the XSD documents `-1` - Breaking: `ECMUserAttributes.last_modified` is replaced by `profile_changed: bool`. The `geaendert` attribute is a `0`/`1` profile-changed flag, not a Unix timestamp, so it parsed as `1970-01-01 00:00:01+00:00` whenever it was set. The descriptions of `never_expire` (password expiry, not account expiry) and `login_count` (server-side count of login attempts) were corrected in the same pass Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.1a1...1.0.1a2 -
1.0.1a1
Release: Release 1.0.1a192685055 · ·# 1.0.1a1 - Alpha 1 First alpha of the 1.0.1 line. It makes every timestamp independent of the client's timezone, hardens the RPC base layer against short reads, leaked descriptors and orphaned temporary files, and adds an explicit way to declare the timezone of the enaio installation. The public API of 1.0.0 is unchanged apart from the additive capabilities below; the timezone handling is the one behaviour change, and it only takes effect once configured. Added: - set_server_timezone() / get_server_timezone() declare the timezone of the enaio installation, plus the ECMIND_BLUE_SERVER_TIMEZONE environment variable for deployments that would rather not touch application code - epoch_to_server_time(), server_time_to_epoch() and to_server_time() expose the conversions for callers that process timestamps themselves - result_as_file() on the query builders requests the hit list as a response file instead of the inline XML parameter; off by default, intended for single pages above file_cache_byte_limit - stream_lol_rows() / stream_hol_objects() in ecmind_blue_client.ecm.parser read a DMSContent response incrementally instead of building the whole DOM - tzdata as a runtime dependency on Windows: Windows ships no IANA time zone database, so zoneinfo cannot resolve a name like Europe/Zurich there Changed: - Every epoch conversion resolves through the installation timezone instead of the host's: DATETIME index fields, the Created / Modified base parameters, system.deleted_at, the concurrency_timestamp used for optimistic locking, workflow datetime variables and JobParameterTypes.DATE_TIME. Left unset the host timezone still applies, so a deployment that already runs in the installation's timezone is unaffected. Read and write move together because concurrency_timestamp is a round trip of OBJECT_MODIFYTIME - dms.GetResultList and dms.GetObjectDetails return their payload inline as the XML parameter instead of as a response file - The paging stream() / execute() methods of select() and select_lol() walk each response page incrementally; the hierarchical with_children() / with_parents() path keeps the DOM parser - Note that 1.0.0 advertised no runtime dependencies; that now holds on Linux only Fixed: - The async job path reads fixed-size response fields with StreamReader.readexactly again, so a cancelled read consumes nothing instead of leaving the connection desynchronised - Temporary files of a response are deleted on every error path of sync_call_job / async_call_job; a truncated or digest-mismatched response with a large attachment previously orphaned them permanently - sync_connect closes its socket in every failure branch; a retry loop against an unreachable server leaked one descriptor per attempt and ran towards EMFILE - value_offset in a job parameter description counts the UTF-8 bytes of the parameter name instead of its characters, so a non-ASCII name no longer points into the middle of the name - BIGINT parameters can be sent instead of raising "Bigint is currently not supported" - async_call_job defaults parameters to None like sync_call_job does, so code ported between the variants no longer raises TypeError - The fixed-size fields of a job response are read completely rather than in a single recv that may return less - A connection closed in the middle of a job response raises BlueProtocolException instead of failing later with a misleading error - _sync_send_request sends with sendall instead of send, which could transmit only part of the request and leave the server waiting - The unreachable "invalid parameter type" branch in deserialize_job_parameters reports the type instead of raising TypeError - bind_sql_params no longer interprets a percent sign inside string literals, quoted identifiers or comments as a placeholder Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0...1.0.1a1
-
1.0.0
Release: Release 1.0.00c30ade6 · ·ecmind_blue_client 1.0.0 First stable release of the typed `ecmind_blue_client.ecm` API, available in full as sync and async variants. - Namespaced API: `ecm.dms`, `ecm.security`, `ecm.system`, `ecm.workflow`, `ecm.db`, replacing the client-level methods (`lol_query`, `xml_import`, `get_object_details`, ...) and the former `manage`, `objdef` and `workflow` companion packages. - Typed model classes for folders, registers and documents, generated from a live server or an `asobjdef.xml` via `ecm-generate-models`, plus a chainable query builder for HOL and LOL with table-field conditions, full text and hierarchy queries. - Partial updates, upsert, variants, move/copy, check-in/check-out, and chunked streaming of large file attachments. - Rewritten connection pool: TLS by default, one shared session per server, AES password scheme without the 62-character limit, per-server connection checks. - Server push notifications over a dedicated callback channel. - Any server job remains directly callable through `ecm.execute()`. Requires Python 3.12. No runtime dependencies. `TcpClient` / `TcpPoolClient` and the `tcp` extra are deprecated; the `portfolio` extra has not been ported yet. Documentation and migration guide: https://ecmind-blue-client.docs.ecmind.ch
-
1.0.0rc9
Release: Release 1.0.0rc9a3f56d6a · ·# 1.0.0rc9 - Release Candidate 9 Ninth release candidate of the 1.0.0 line, additive over 1.0.0rc8. It fixes system field handling in DMSQuery result field lists, enabling sorting and selection on Model.system.* fields in the typed query builders (for example keyset pagination over the object ID). The public API of 1.0.0rc8 is unchanged. Fixed: - Result field lists emit system="1" for system field names passed as plain strings, so order_by(Model.system.id.DESC) and fields(Model.system.id) work on HOL and LOL builders (sync and async) instead of failing with server error -1031 (#15650) - DmsQueryFields.field() merges repeated calls for the same field into one Field element instead of emitting duplicates, so a field that is both selected and sorted on produces a single entry (#15650) Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc8...1.0.0rc9
-
1.0.0rc8
Release: Release 1.0.0rc81e6d172c · ·# 1.0.0rc8 - Release Candidate 8 Eighth release candidate of the 1.0.0 line, additive over 1.0.0rc7. It adds server-push notifications (a low-level callback channel and the high-level `ecm.notification` namespace), makes the connection pool keep one shared session per server, and fixes two model-generator defects. The public API of 1.0.0rc7 is unchanged apart from the additive capabilities below. Added: - ecm.notification namespace: typed server-push notifications (job_calls / messages / listen / send_message) - Notification callback channel in the low-level RPC layer (sync/async open_callback + callback_next, protocol v50 with SHA-1 digest) - ecm-callback-listen console script to inspect the server's push stream from the shell - connection_for(hostname, port) on the pool clients to borrow a connection to a specific server Changed: - Connection pool keeps one shared session per server (attach instead of repeated login) - create_os_event() / update_os_event() send the script code as UTF-8 with a BOM instead of cp1252 Fixed: - ecm-generate-models emits the stored key (not the display text) into flat-list catalog enums - ecm-generate-models sanitizes list-entry keys to valid Python identifiers Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc7...1.0.0rc8
-
1.0.0rc7
Release: Release 1.0.0rc7a44f0ae5 · ·# 1.0.0rc7 - Release Candidate 7 Seventh release candidate of the 1.0.0 line, additive over 1.0.0rc6. Added: - ecm.dms.files_streaming(): chunked attachment download without local buffering (#15486) - Modern AES login password scheme, lifting the 62-character limit (#15561) - ecm.system OsEvent management: get_os_events / create / update / delete_os_event - ecm.system.refresh_server_events() to reload the OsEvent cache (#15555) - execute_and_get() on the upsert builder Fixed: - ecm-generate-models drops flat-list display text from enum values - password_encrypt() RecursionError on passwords over 62 characters (#15561) - document_stream() no longer caps offset/length at 2 GiB (#15486) - update_and_get() overload typing without a type-ignore (#15486) Full changelog: https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc6...1.0.0rc7
-
1.0.0rc6
Release: Release 1.0.0rc69753ce99 · ·# 1.0.0rc6 - Release Candidate 6 Sixth release candidate of the `1.0.0` line. It adds document search-flag querying, archive/lock status properties on document instances, and three new system methods (`get_icons`, `jobs`, `check_connections`), and fixes the `build` CI job. The public API of `1.0.0rc5` is unchanged apart from the additive capabilities below. ## Added - **Query documents by `OBJECT_SEARCHFLAGS` state (#14491).** The `ObjectSearchFlags` enum existed but could not be used: the only condition entry point emitted `system="0"`, so the server ignored the bitmask. Document models now expose boolean search-flag properties on their `system` namespace (`Model.system.in_register`, `.signed_current`, `.without_pages`, `.archivable`, ...) usable directly in `where()`, e.g. `ecm.dms.select(Document).where(Document.system.in_register == True).execute()`. Multiple flag conditions collapse into one correct `<FieldCondition internal_name="OBJECT_SEARCHFLAGS" system="1">`. Paired flags (`archivable`, `in_register`) also accept `== False`. - **Archive-status and lock properties on loaded document instances (#14491).** `OBJECT_FLAGS` (1102) and `OBJECT_LOCKUSER` (1116) are now part of the document default system fields, so `ECMDocumentModel` instances expose decoded boolean properties without any extra query flag: `obj.system.archived`, `.archivable`, `.not_archivable`, `.page_error`, `.without_pages`, `.reference`, `.external_archived` (from the `OBJECT_FLAGS` status code) and `.locked` (from `OBJECT_LOCKUSER`), plus the raw `.flags`. The query-only search-flag properties (`multi_location`, `has_variants`, `signed_current`, `signed_former`) remain usable only inside `where(...)`. - **`ecm.system.get_icons()` fetches object/type icons by ID.** Wraps the `cnv.GetIcons` server job and returns the GIF images for the given icon IDs as a `dict[int, bytes]` (icon ID to raw GIF bytes). Icon IDs come from the object definition (`icon_id` per object type) or from `document.system.file_properties.iconid`. Multiple IDs are fetched in a single round-trip; unknown IDs are silently omitted; passing no IDs returns `{}` without contacting the server. Sync and async. - **`ecm.check_connections()` probes every configured pool server individually (#15467).** This root-level method opens a throwaway connection to each server in the pool configuration (TCP/TLS, login, `krn.GetServerInfoEx`), reports reachability, authentication state and server info per server, then closes it again. Probe connections never enter the pool and never affect pool statistics; one unreachable server does not abort the others, failures are recorded in the result's `error` field instead of being raised. Returns `list[ECMServerConnectionCheck]` (sync and async). - **`ecm.system.jobs()` lists all server jobs grouped by namespace.** Combines `krn.EnumNameSpaces` and one `krn.EnumJobs` call per engine into a single `dict[str, list[str]]`, keyed by namespace short label (e.g. `"krn"`) with the namespace's jobs as values, each including the namespace prefix. Dict keys and job lists are sorted; the async variant runs the per-engine calls concurrently (sync and async). ## Fixed - **The `build` CI job failed at `uv build` (#15468).** A newer `uv` refuses to build when its cache lives inside the build source tree, and the shared GitLab cache restored `.cache/uv` back into the project directory. The `build` job now points `UV_CACHE_DIR` outside the source and skips the shared cache, so the package builds again. The test job keeps its in-project cache for reuse between pipelines. **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc5...1.0.0rc6 -
1.0.0rc5
Release: Release 1.0.0rc5afbac8bd · ·# 1.0.0rc5 - Release Candidate 5 Fifth release candidate of the `1.0.0` line. It fixes a model-generator defect that silently dropped data-bearing fields displayed with the GROUP control. The public API of `1.0.0rc4` is unchanged. ## Fixed - **`ecm-generate-models` dropped data-bearing GROUP-control fields.** enaio uses the GROUP control (`dt='G'`) both for visual containers and for real index fields that have their own data type and database column. The generator skipped every GROUP control, so such fields (e.g. `PostDoc`'s `hash`, `group`, `user`) were silently missing from the generated model class. GROUP-control fields that carry a data type are now emitted as `ECMField` declarations; purely visual GROUP boxes (no data type, or static) stay excluded. Present since `1.0.0a1`; regenerate model files to pick up the affected fields. **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc4...1.0.0rc5
-
1.0.0rc4
Release: Release 1.0.0rc4e2f35b30 · ·# 1.0.0rc4 — Release Candidate 4 Fourth release candidate of the `1.0.0` line. It adds class-level queryable system fields and group-management write methods, and fixes license handling on enaio 12, field clearing on update and a SQL-whitespace edge case. The public API of `1.0.0rc3` is unchanged apart from the additive capabilities below. ## Added - **Class-level `Model.system.<field>` as queryable fields.** `system` is now access-aware: instance access (`obj.system.id`) still returns the loaded value, while class access (`Model.system.id`) returns an `ECMField` usable in `where()` / `order_by()`. This enables cross-type filtering on a parent via the same dotted path, e.g. all documents inside a known folder via `select(Doc).where(Folder.system.id == folder.system.id)`. Exposed for every system field with a single queryable backing column (`id`, `owner_guid`, `creator`, `creation_date`, `last_modifier`, `last_modified`, `creation_time`, `deleted_at`; register/document location fields; and the queryable subset of `system.base_params` and `system.file_properties`). Aggregate values without a backing column (`rights`, `name`, `is_modified`) remain instance-only. Verified against a live server. - **Group-management write methods on `ecm.security` (#15...).** `create_group()`, `update_group()`, `delete_group()` and `empty_group()` wrap `mng.CreateGroup`, `mng.SetGroupAttributes`, `mng.DeleteGroup` and `mng.EmptyGroup`. `create_group()` returns an `ECMGroup` with the server-assigned `id` and `guid`; `update_group()` takes a (modified) `ECMGroup`; `delete_group()` / `empty_group()` accept either an `ECMGroup` or a group name. A group can only be deleted once empty. Sync and async. ## Fixed - **License jobs no longer raise a hard error on enaio 12+.** `check_license()` and `module_info()` now pass `Flags=1` to the `LIC_CHECKLICENSE` and `LIC_LICGETMODULEINFO` server jobs; since enaio version 12 these return a hard error with the previous `Flags=0`. As a consequence `module_info()` for an unknown module now returns a result with a `?` license-type marker instead of failing; this is detected and reported as the documented `ECMNotFoundException` (sync and async). - **Fields could not be cleared on update (#15434).** Setting a loaded field to `None` was correctly detected as a change, but `_build_update_xml` skipped `None`-valued fields, so the reset was never sent and the server kept the old value. A field changed to `None` is now emitted with `field_function="NULL"` so the server actually clears it (sync and async). `insert()` still omits `None` fields. - **`ecm.db.select()` raised an opaque `IndexError` on leading whitespace (#15431).** A statement beginning with whitespace (e.g. a leading newline from a multi-line literal) made `ado.ExecuteSQL` return no result set while still reporting success, tripping `files[0]`. The bound command is now left-trimmed before being sent (sync and async). **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc3...1.0.0rc4
-
1.0.0rc3
Release: Release 1.0.0rc3c6536ad0 · ·# 1.0.0rc3 — Release Candidate 3 Third release candidate of the `1.0.0` line. It adds table-field column conditions to the query builder and fixes enum and workflow-model handling. The public API of `1.0.0rc2` is unchanged apart from the additive query capability below. ## Added - **Table-field column conditions in the query builder (#15428).** Conditions can now target columns of a sub-table / multi-field (e.g. `Invoice.Positions.ArticleNo == "A-100"`), emitting `<TableCondition>` / `<TableColumn>` instead of a flat field condition. Restrict a condition to a row via `Invoice.Positions[3].Quantity == 5` (alias `.row(3)`). Works in `select` and `select_lol` (sync and async) with all comparison and collection operators. ## Fixed - **Enum (catalog) field values were serialized as their Python repr (#15427).** Passing a generated `(str, Enum)` / `IntEnum` member to `insert` / `update` / `upsert` emitted `Class.MEMBER` instead of the catalog value, which the server rejected. Enum members are now unwrapped to their value before conversion. - **Workflow start inputs were under-detected.** Inputs were read from `INOUT` parameters across the whole model (including activities) and `IN` was ignored. They are now read from the `<WorkflowProcess>` interface only, counting both `IN` and `INOUT`; the standard ad-hoc workflow, for example, previously reported 2 of its 5 input variables. - **List-of-record workflow variables lost their type and members.** Variables typed as a list of records are now correctly typed as `list[...Record]` with their members. ## Changed - **Generated workflow-model class names derive from the stable workflow display name** (e.g. `Standard_Ad_hoc_WorkflowModel` instead of `Ad_hoc_Version_3_0_5WorkflowModel`), so they no longer change when the workflow version increases. ## Documentation - New **FastAPI integration** and **workflow-start** guides (with matching skills). German docs use "Schrank"; skill documentation links now point to the hosted Antora site. **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc2...1.0.0rc3
-
1.0.0rc2
Release: Release 1.0.0rc2498a70a8 · ·# 1.0.0rc2 — Release Candidate 2 Second release candidate of the `1.0.0` line. It adds steerable table-field replacement and fixes two object/XML handling defects, one of which affected a production environment. The public API of `1.0.0rc1` is unchanged apart from the additive parameters below. ## Added - **Steerable table-field replacement on `upsert` and `update` (#15422).** New `replace_table_fields(value=True)` fluent method on the upsert builder, and a keyword-only `replace_table_fields: bool | None` parameter on `ecm.dms.update()` and `ecm.dms.update_and_get()` (sync and async). `None` (default) keeps the automatic detection, `True` forces `REPLACETABLEFIELDS=1`, `False` suppresses it. ## Fixed - **Invalid upsert XML when a search section was used (#15424).** The `<Search>` element was placed after `<Fields>`, but the schema requires it first; the server rejected such requests (`Element 'Search' is unexpected …`). `<Search>` is now emitted first, so every `upsert(...).search(...)` call produces valid XML. - **Object definition dropped fields from unnamed PageControl tabs.** Tabs with a blank internal name collapsed onto one entry and lost their fields. Pages now fall back to their unique `page_id`, restoring the missing fields in `ecm.system.definition()`, `ecm.dms.model_by_name()` and `ecm-generate-models`. **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0rc1...1.0.0rc2
-
1.0.0rc1
Release: Release 1.0.0rc1f7efdaae · ·First release candidate of the `1.0.0` line. There are **no functional changes to the library API** since `1.0.0a9`; this candidate stabilises the release tooling and test suite. - **Automatic GitLab Release on tags.** Tag pipelines now create a GitLab Release whose description is taken from this tag's message. The built library (wheel + sdist) and the rendered documentation are uploaded to the project's generic Package Registry and linked as permanent release assets, alongside a link to the matching PyPI project page. The rendered Antora site is also bundled as `docs-<tag>.zip`. - **Workflow process-list integration test no longer assumes the creator is the inbox owner.** `ecm.workflow.process_list_by_user()` returns the processes in a user's inbox, and an inbox item may have been created by any user. The `test_process_has_creation_block` test (sync and async) now only asserts that the `creation` block is populated. No change to library behaviour. **Full changelog:** https://gitlab.ecmind.ch/open/ecmind_blue_client/-/compare/1.0.0a9...1.0.0rc1
-
-
-
-
1.0.0a6
52190a61 · ·### Added - **`ecm.workflow.process_list_by_user()`**: New sync and async method wrapping the server job `wfm.AdminGetProcessListByUser`. Returns a list of typed `ECMProcess` objects, each containing an `ECMProcessCreation` block and a tuple of `ECMProcessActivity` instances with parsed timestamps and a convenient `is_personalised` flag (equivalent to `Activity.State & 128`). The `user` argument expects the workflow-organisation user object — not the security user GUID. - **Full-text search on the typed model query builders**: New `.fulltext(term, **engine_options)` chain method on `ecm.dms.select()` (HOL) and `ecm.dms.select_lol()` (LOL), available in both sync and async variants. Adds a `<Fulltext>` condition to the queried object type's `<ConditionObject>` and accepts optional RetrievalWare engine attributes (`mode`, `expansion_level`, `fuzzy_spell_half_words`, `fuzzy_spell_threshold`, `max_fuzzy_spell`, `max_reg_expr`, `warn_max_reg_expr`, `word_expansion_limit`). Requires a configured RetrievalWare search engine on the enaio® server. The archive-wide `<FulltextQuery>` flavour (hits across multiple object types in one request) remains HOL-exclusive at the low-level `DmsQueryBuilder.fulltext_query()` API and is intentionally not exposed on the typed single-model builders.
-
1.0.0a5
3612af07 · ·## 1.0.0a5 ### Added - `ECMDocumentModelSystem.system_id` (`OBJECT_SYSTEMID`): ID of the external archive system holding the referenced file; now also loaded on `get()`. - Read-only enforcement for model fields via the new `ECMField(read_only=...)` parameter (`"always"` / `"init"` / `"arch"`), checked on `insert()`, `insert_variant()`, `update()` and `upsert()` (sync and async). Disable per call with `check_read_only=False`. ### Changed - `generate_ecm_models` now derives `read_only` from the ECM flags (`readonly` → `"always"`, `readonly_after_initialization` → `"init"`, `readonly_after_archiving` → `"arch"`) and emits it on the generated `ECMField(...)` line. - Clarified `foreign_id` documentation, including the *green arrow* special case. ### Fixed - **#15222** Model feature: `update()` failed for fields flagged "read-only after initialization". A `mandatory` + `read_only="always"` field is no longer generated as a required constructor argument.