Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a26519a
feat: DM-4756: take config_type and discovery_kind for the defaults YAML
Nov 4, 2026
5808da0
feat: DM-4756: read a document run's folders, files, totals and CSV
Nov 4, 2026
326b92f
feat: DM-4756: find a connection's last unstructured discovery run
Nov 4, 2026
2400c12
feat: DM-4756: read a run's AI Engine model and version
Nov 7, 2026
a249b99
feat: DM-4756: read per-label totals and character counts
Nov 7, 2026
e1808f4
feat: DM-4756: filter, search and sort folders and files
Nov 7, 2026
6aed8f4
docs: DM-4756: describe the AI Engine run fields as the API does
Nov 7, 2026
c67b157
feat: DM-4756: call a file the run could not read failed
Nov 7, 2026
395bc43
docs: DM-4756: say a run without an AI Engine reports None
Nov 7, 2026
c107003
feat: DM-4756: read the findings report from its new URL
Nov 9, 2026
ff721e2
docs: DM-4756: describe sampling and files without text as the server…
Nov 9, 2026
796cabe
docs: DM-4756: say a folder path search lists all its files
Nov 9, 2026
6e3e9ef
docs: DM-4756: say a label filter drops failed files
Nov 9, 2026
d471979
docs: DM-4756: list the findings report's columns
Nov 9, 2026
a4176a2
docs: DM-4756: say files_read excludes failed files
Nov 9, 2026
5993ee7
refactor: DM-4756: share the CSV-or-zip report read
Nov 9, 2026
29ef9d3
test: DM-4756: assert the keyword-only TypeError message
Nov 9, 2026
1315ed2
docs: DM-4756: call what a document run finds labels
Nov 9, 2026
52be6bb
docs: DM-4756: say no-text files have no findings
Nov 9, 2026
34e400a
feat: DM-4756: follow the split unstructured discovery API
Nov 10, 2026
1e85ed9
refactor: DM-4756: name the report method for discovery
Nov 10, 2026
8e07610
docs: DM-4756: describe the split results API in the history
Nov 10, 2026
8f8e55c
docs: DM-4756: word the sample orders as the server does
Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,33 @@ History
* ``RunInfo`` and ``UnfinishedRun`` gain a required ``project`` field:
the run's project, taken from its ruleset's project when the run starts.
* Added ``connect_timeout_seconds`` and ``read_timeout_seconds`` to the instance config models.
* Added optional keyword-only ``config_type`` and ``discovery_kind`` arguments to ``get_default_discovery_config_yaml``,
typed by ``DiscoveryConfigType`` and the new ``DiscoveryKind`` enum.
``discovery_kind=DiscoveryKind.unstructured`` with ``config_type=DiscoveryConfigType.file``
returns the template of an unstructured discovery config,
which counts the occurrences of its labels in the text of documents.
* Added methods that read an unstructured discovery run's results:
``get_unstructured_discovery_folder_page`` and ``iter_unstructured_discovery_folders``
for the folders found on the connection,
optionally narrowed by ``search``, ``labels`` and ``categories``
and sorted by ``UnstructuredDiscoveryFolderOrdering``,
``get_unstructured_discovery_file_page`` and ``iter_unstructured_discovery_files`` for the files of one folder,
optionally narrowed by ``UnstructuredDiscoveryFileStatus``, ``search``, ``labels``, ``categories``
and ``UnstructuredDiscoveryFileOutcome``,
``get_unstructured_discovery_summary`` for the run's connection, status, end time,
``UnstructuredDiscoverySettings``, ``UnstructuredDiscoveryAiEngine``, totals and how many times it found each label,
and ``get_unstructured_discovery_report`` for its findings CSV,
or a zip of CSV parts as ``bytes`` for a report of more than 1,000,000 rows.
A skip reason is a code such as ``over_sample_limit``, with its sentence in ``skip_reason_text``.
* Added ``get_last_unstructured_discovery_run_id``,
which returns the ID of a connection's newest finished unstructured discovery run that is not a dry run,
or ``None`` when it has none.
* Added ``discovery_kind`` to ``RunInfo``: ``DiscoveryKind.structured`` or ``DiscoveryKind.unstructured``
for a file discovery run, ``None`` for a run that ran no discovery task.
* Added ``ai_engine_model`` and ``ai_engine_version`` to ``RunInfo``:
the AI Engine model and engine version the run's ``ai_detect`` matchers use,
``None`` when the run recorded none, such as a run that uses no AI Engine,
or on a server that does not report them.

Requires server version 3.26.20

Expand Down
34 changes: 33 additions & 1 deletion datamasque/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@
TableConstraints,
ValueCountStatus,
)
from datamasque.client.models.discovery_config import DiscoveryConfig, DiscoveryConfigId, DiscoveryConfigType
from datamasque.client.models.discovery_config import (
DiscoveryConfig,
DiscoveryConfigId,
DiscoveryConfigType,
DiscoveryKind,
)
from datamasque.client.models.discovery_config_library import DiscoveryConfigLibrary, DiscoveryConfigLibraryId
from datamasque.client.models.dm_instance import DataMasqueInstanceConfig
from datamasque.client.models.files import (
Expand Down Expand Up @@ -172,6 +177,20 @@
TableReferenceId,
TableReferenceOptions,
)
from datamasque.client.models.unstructured_discovery import (
UnstructuredDiscoveryAiEngine,
UnstructuredDiscoveryFile,
UnstructuredDiscoveryFileOutcome,
UnstructuredDiscoveryFileStatus,
UnstructuredDiscoveryFinding,
UnstructuredDiscoveryFolder,
UnstructuredDiscoveryFolderOrdering,
UnstructuredDiscoveryLabelTotal,
UnstructuredDiscoverySampleOrder,
UnstructuredDiscoverySettings,
UnstructuredDiscoverySummary,
UnstructuredDiscoveryUnreadCount,
)
from datamasque.client.models.user import User, UserId, UserRole

__version__ = version("datamasque-python")
Expand Down Expand Up @@ -209,6 +228,7 @@
"DiscoveryConfigLibraryId",
"DiscoveryConfigNotFoundError",
"DiscoveryConfigType",
"DiscoveryKind",
"DiscoveryMatch",
"CosmosDbConnectionConfig",
"DocumentDbConnectionConfig",
Expand Down Expand Up @@ -323,6 +343,18 @@
"TemporalStatistics",
"TemporalSummaries",
"UnfinishedRun",
"UnstructuredDiscoveryFile",
"UnstructuredDiscoveryFileOutcome",
"UnstructuredDiscoveryFileStatus",
"UnstructuredDiscoveryFinding",
"UnstructuredDiscoveryFolder",
"UnstructuredDiscoveryFolderOrdering",
"UnstructuredDiscoveryLabelTotal",
"UnstructuredDiscoveryAiEngine",
"UnstructuredDiscoverySampleOrder",
"UnstructuredDiscoverySettings",
"UnstructuredDiscoverySummary",
"UnstructuredDiscoveryUnreadCount",
"UnsupportedPreview",
"UnsupportedStatistics",
"User",
Expand Down
8 changes: 8 additions & 0 deletions datamasque/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ def _delete_if_exists(self, path: str, *, params: Optional[dict] = None) -> None

self._raise_for_status(response)

@staticmethod
def _read_csv_or_zip(response: Response) -> Union[str, bytes]:
"""Returns a report's CSV text, or as `bytes` the zip of CSV parts the server splits a large report into."""

if response.headers.get("Content-Type", "").startswith("application/zip"):
return response.content
return response.text

def _iter_paginated(
self,
path: str,
Expand Down
33 changes: 29 additions & 4 deletions datamasque/client/discovery_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

from datamasque.client.base import BaseClient
from datamasque.client.exceptions import DataMasqueApiError, DataMasqueException
from datamasque.client.models.discovery_config import DiscoveryConfig, DiscoveryConfigId, DiscoveryConfigType
from datamasque.client.models.discovery_config import (
DiscoveryConfig,
DiscoveryConfigId,
DiscoveryConfigType,
DiscoveryKind,
)
from datamasque.client.models.pagination import Page

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -163,8 +168,28 @@ def delete_discovery_config_by_name_if_exists(self, name: str, config_type: Disc

self.delete_discovery_config_by_id_if_exists(config.id)

def get_default_discovery_config_yaml(self) -> str:
"""Returns the server's built-in default discovery configuration as a YAML string."""
def get_default_discovery_config_yaml(
self,
*,
config_type: Optional[DiscoveryConfigType] = None,
discovery_kind: Optional[DiscoveryKind] = None,
) -> str:
"""
Returns the server's built-in default discovery configuration as a YAML string.

`config_type` selects the database or the file default;
the server returns the database default when it is omitted.
`discovery_kind` selects what a file config looks for;
the server returns the structured default when it is omitted.
Asking for `DiscoveryKind.unstructured` without `config_type=DiscoveryConfigType.file`
raises `DataMasqueApiError` with a 400 response.
"""

params: dict[str, str] = {}
if config_type is not None:
params["config_type"] = config_type.value
if discovery_kind is not None:
params["discovery_kind"] = discovery_kind.value

response = self.make_request("GET", "/api/discovery/configs/defaults/")
response = self.make_request("GET", "/api/discovery/configs/defaults/", params=params or None)
return response.content.decode("utf-8")
2 changes: 2 additions & 0 deletions datamasque/client/dmclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from datamasque.client.runs import RunClient
from datamasque.client.settings import SettingsClient
from datamasque.client.table_references import TableReferenceClient
from datamasque.client.unstructured_discovery import UnstructuredDiscoveryClient
from datamasque.client.users import UserClient

__all__ = ["DataMasqueClient", "FileOrContent", "UploadFile"]
Expand All @@ -24,6 +25,7 @@ class DataMasqueClient(
FileClient,
RunClient,
DiscoveryClient,
UnstructuredDiscoveryClient,
DiscoveryConfigClient,
DiscoveryConfigLibraryClient,
TableReferenceClient,
Expand Down
7 changes: 7 additions & 0 deletions datamasque/client/models/discovery_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ class DiscoveryConfigType(enum.Enum):
file = "file"


class DiscoveryKind(enum.Enum):
"""What a file discovery config looks for: sensitive locators, or its labels in the text of documents."""

structured = "structured"
unstructured = "unstructured"


def unwrap_discovery_config_id(value: Any) -> Any:
"""
Coerce a `DiscoveryConfig` to its `id`; pass other values through unchanged.
Expand Down
20 changes: 20 additions & 0 deletions datamasque/client/models/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

from datamasque.client.models.connection import ConnectionConfig, ConnectionId, unwrap_connection_id
from datamasque.client.models.discovery_config import DiscoveryKind
from datamasque.client.models.ruleset import Ruleset, RulesetId, unwrap_ruleset_id
from datamasque.client.models.status import MaskingRunStatus

Expand Down Expand Up @@ -148,6 +149,25 @@ class RunInfo(BaseModel):

Taken from the ruleset's project when the run starts, and never changes after that.
"""
ai_engine_model: Optional[str] = None
"""
The AI Engine model the run's `ai_detect` matchers use:
for a Local AI Engine, the name of its model file or folder,
and for a Bedrock AI Engine, the Bedrock model set in its configuration.
`None` when the run recorded none, such as a run that uses no AI Engine,
or on a server that does not report it.
"""
ai_engine_version: Optional[str] = None
"""
The version the AI Engine reported when the run started.
`None` when the run recorded none, such as a run that uses no AI Engine,
or on a server that does not report it.
"""
discovery_kind: Optional[DiscoveryKind] = None
"""
What a file discovery run looked for: sensitive locators, or labels in the text of documents.
`None` for a run that ran no discovery task, or on a server that does not report it.
"""

@model_validator(mode="before")
@classmethod
Expand Down
Loading
Loading