Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Comment thread
MaitriAnv marked this conversation as resolved.
Resolved

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from html import escape
from uuid import UUID

import ipywidgets as widgets
Expand Down Expand Up @@ -124,7 +125,7 @@ def _on_filter_changed(self, change):
"""Handle filter change."""
self._update_filtered_objects()

def _format_object_name(self, obj):
def format_object_name(self, obj):
"""Format the object name by removing .json extension."""
name = obj.name
name = name.removesuffix(".json")
Expand All @@ -145,7 +146,7 @@ def _update_filtered_objects(self):
object_options = [("Select an object...", None)]
object_options.extend(
[
(f"{self._format_object_name(obj)} ({obj.schema_id.sub_classification})", obj.id)
(f"{self.format_object_name(obj)} ({obj.schema_id.sub_classification})", obj.id)
for obj in self.filtered_objects
]
)
Expand Down Expand Up @@ -178,15 +179,21 @@ def _update_info_display(self):
self.info_display.value = ""
return

object_name = escape(str(self.format_object_name(selected_obj)))
object_path = escape(str(selected_obj.path))
object_id = escape(str(selected_obj.id))
object_schema = escape(str(selected_obj.schema_id))
object_version = escape(str(selected_obj.version_id))

# Build info HTML
info_html = f"""
<div style="margin-top: 10px; padding: 10px; background-color: #f0f0f0; border-radius: 5px;">
<b>Object Details:</b><br/>
<b>Name:</b> {self._format_object_name(selected_obj)}<br/>
<b>Path:</b> {selected_obj.path}<br/>
<b>ID:</b> {selected_obj.id}<br/>
<b>Schema:</b> {selected_obj.schema_id}<br/>
<b>Version:</b> {selected_obj.version_id}<br/>
<b>Name:</b> {object_name}<br/>
<b>Path:</b> {object_path}<br/>
<b>ID:</b> {object_id}<br/>
<b>Schema:</b> {object_schema}<br/>
<b>Version:</b> {object_version}<br/>
<b>Created:</b> {selected_obj.created_at.strftime("%Y-%m-%d %H:%M:%S")}<br/>
<b>Modified:</b> {selected_obj.modified_at.strftime("%Y-%m-%d %H:%M:%S")}<br/>
</div>
Expand Down Expand Up @@ -217,12 +224,7 @@ class TargetWorkspaceSelectorWidget:
def __init__(self, manager_widget):
self.manager_widget = manager_widget
# Get the selected workspace ID
selected_id = manager_widget._workspace_selector.selected

if selected_id != manager_widget._workspace_selector.UNSELECTED[1]:
self.current_workspace_id = selected_id
else:
raise ValueError("No source workspace is currently selected.")
self.current_workspace_id = manager_widget.get_environment().workspace_id

# Create widgets
self.refresh_btn = widgets.Button(
Expand All @@ -237,7 +239,7 @@ def __init__(self, manager_widget):
self.loading_label = widgets.Label(value="")

# Operation section - label and checkbox
self.operation_label = widgets.HTML(value="<b>Copy object</b>")
self.operation_label = widgets.HTML(value="<b>Copy object/file</b>")
self.delete_original_checkbox = widgets.Checkbox(
value=False,
description="Delete original after copy",
Expand Down
117 changes: 108 additions & 9 deletions mkdocs/site/packages/evo-blockmodels/BlockModelAPIClient.html
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,8 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_block_model_columns" c
<span class="n">column_groups</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">group_missing_column_override</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">MissingColumnPolicy</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">separator</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">_QUALIFIED_TITLE_SEPARATOR</span><span class="p">,</span>
<span class="n">new_groups</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">GroupDefinition</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">column_metadata_updates</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">ColumnMetadataUpdate</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Version</span>
</code></pre></div>

Expand Down Expand Up @@ -1879,8 +1881,8 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_block_model_columns" c
<div class="doc-md-description">
<p>A dictionary assigning <strong>new</strong> columns to groups: map a new column's qualified
title (its key in <code>data</code>, e.g. <code>"Assays▸Cu"</code>) to the qualified title of the group it belongs to.
To move or ungroup an <em>existing</em> column, use :meth:<code>update_column_metadata</code> instead — a group change is
metadata-only and does not require re-uploading data.</p>
To move or ungroup an <em>existing</em> column, use <code>column_metadata_updates</code> (in the same update) or
:meth:<code>update_column_metadata</code> (on its own).</p>
</div>
</td>
<td>
Expand Down Expand Up @@ -1926,6 +1928,43 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_block_model_columns" c
<code><span title="evo.blockmodels.data.QUALIFIED_TITLE_SEPARATOR">QUALIFIED_TITLE_SEPARATOR</span></code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>new_groups</code>
</td>
<td>
<code><span title="list">list</span>[<span title="evo.blockmodels.data.GroupDefinition">GroupDefinition</span>] | None</code>
</td>
<td>
<div class="doc-md-description">
<p>Definitions of new groups to create in the same update, so a group can be created and
populated (by new columns or by moving existing ones) atomically in a single new version.</p>
</div>
</td>
<td>
<code>None</code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>column_metadata_updates</code>
</td>
<td>
<code><span title="dict">dict</span>[<span title="str">str</span>, <span title="evo.blockmodels.data.ColumnMetadataUpdate">ColumnMetadataUpdate</span>] | None</code>
</td>
<td>
<div class="doc-md-description">
<p>Metadata updates (unit, tags and/or group) for existing columns, applied in
the same update and keyed by each column's <strong>current</strong> title. Set <code>ColumnMetadataUpdate(group=...)</code> to
move a column into a group (or <code>""</code> to ungroup it). When a moved column's data is re-uploaded (it is in
<code>update_columns</code>, named by its current title), key that data by its <strong>destination</strong> title, e.g.
<code>Assays▸Cu</code> after moving <code>Cu</code> into <code>Assays</code>. A column cannot also be in <code>delete_columns</code>.</p>
</div>
</td>
<td>
<code>None</code>
</td>
</tr>
</tbody>
</table>

Expand Down Expand Up @@ -2006,6 +2045,8 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_subblocked_columns" cl
<span class="n">column_groups</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">group_missing_column_override</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">MissingColumnPolicy</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">separator</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">_QUALIFIED_TITLE_SEPARATOR</span><span class="p">,</span>
<span class="n">new_groups</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">GroupDefinition</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">column_metadata_updates</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">ColumnMetadataUpdate</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Version</span>
</code></pre></div>

Expand Down Expand Up @@ -2193,8 +2234,8 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_subblocked_columns" cl
<div class="doc-md-description">
<p>A dictionary assigning <strong>new</strong> columns to groups: map a new column's qualified
title (its key in <code>data</code>, e.g. <code>"Assays▸Cu"</code>) to the qualified title of the group it belongs to.
To move or ungroup an <em>existing</em> column, use :meth:<code>update_column_metadata</code> instead — a group change is
metadata-only and does not require re-uploading data.</p>
To move or ungroup an <em>existing</em> column, use <code>column_metadata_updates</code> (in the same update) or
:meth:<code>update_column_metadata</code> (on its own).</p>
</div>
</td>
<td>
Expand Down Expand Up @@ -2240,6 +2281,43 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_subblocked_columns" cl
<code><span title="evo.blockmodels.data.QUALIFIED_TITLE_SEPARATOR">QUALIFIED_TITLE_SEPARATOR</span></code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>new_groups</code>
</td>
<td>
<code><span title="list">list</span>[<span title="evo.blockmodels.data.GroupDefinition">GroupDefinition</span>] | None</code>
</td>
<td>
<div class="doc-md-description">
<p>Definitions of new groups to create in the same update, so a group can be created and
populated (by new columns or by moving existing ones) atomically in a single new version.</p>
</div>
</td>
<td>
<code>None</code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>column_metadata_updates</code>
</td>
<td>
<code><span title="dict">dict</span>[<span title="str">str</span>, <span title="evo.blockmodels.data.ColumnMetadataUpdate">ColumnMetadataUpdate</span>] | None</code>
</td>
<td>
<div class="doc-md-description">
<p>Metadata updates (unit, tags and/or group) for existing columns, applied in
the same update and keyed by each column's <strong>current</strong> title. Set <code>ColumnMetadataUpdate(group=...)</code> to
move a column into a group (or <code>""</code> to ungroup it). When a moved column's data is re-uploaded (it is in
<code>update_columns</code>, named by its current title), key that data by its <strong>destination</strong> title, e.g.
<code>Assays▸Cu</code> after moving <code>Cu</code> into <code>Assays</code>. A column cannot also be in <code>delete_columns</code>.</p>
</div>
</td>
<td>
<code>None</code>
</td>
</tr>
</tbody>
</table>

Expand Down Expand Up @@ -2395,19 +2473,20 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_groups" class="doc doc
<span class="n">new</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">GroupDefinition</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">update</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">GroupMetadataUpdate</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">delete</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">move_columns</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">comment</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Version</span>
</code></pre></div>

<div class="doc doc-contents ">

<p>Create, update, and/or delete column groups on a block model.</p>
<p>This method manages group definitions without requiring data upload or cache configuration. Any
combination of <code>new</code>, <code>update</code> and <code>delete</code> can be supplied in a single call.</p>
<p>Create, update, and/or delete column groups, and move existing columns between groups.</p>
<p>This method manages group definitions and group membership without requiring data upload or cache
configuration. Any combination of <code>new</code>, <code>update</code>, <code>delete</code> and <code>move_columns</code> can be supplied
in a single call, producing a single new version.</p>
<p>Groups are addressed by their qualified title (a bare title for a top-level group, or segments
joined by <code>▸</code> for a nested group). To assign a <em>new</em> column to a group, use the <code>column_groups</code>
parameter on the column methods; to move or ungroup an <em>existing</em> column, use
:meth:<code>update_column_metadata</code>. To resolve a written group back to its server-assigned UUID and
parameter on the column methods; to move or ungroup an <em>existing</em> column, use <code>move_columns</code>. To resolve a written group back to its server-assigned UUID and
resolved policy, use the helpers on the returned :class:<code>~evo.blockmodels.data.Version</code>, e.g.
:meth:<code>~evo.blockmodels.data.Version.group_by_qualified_title</code>.</p>

Expand Down Expand Up @@ -2489,6 +2568,26 @@ <h3 id="evo.blockmodels.client.BlockModelAPIClient.update_groups" class="doc doc
<code>None</code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>move_columns</code>
</td>
<td>
<code><span title="dict">dict</span>[<span title="str">str</span>, <span title="str">str</span>] | None</code>
</td>
<td>
<div class="doc-md-description">
<p>A dictionary mapping an existing column's <strong>current</strong> title (qualified
<code>group▸title</code> if it is currently grouped, plain otherwise) to the qualified title of the group to
move it into, or <code>""</code> to ungroup it. Only the column's group changes; the target group may be one
created in <code>new</code> in the same call. To change a column's unit or tags, use
:meth:<code>update_column_metadata</code>.</p>
</div>
</td>
<td>
<code>None</code>
</td>
</tr>
<tr class="doc-section-item">
<td>
<code>comment</code>
Expand Down
3 changes: 2 additions & 1 deletion packages/evo-files/src/evo/files/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from uuid import UUID

from evo.common import APIConnector, Environment, ICache
from evo.common._types import resolve_path
from evo.common.io import Download, Upload

from .data import FileMetadata
Expand Down Expand Up @@ -176,7 +177,7 @@ def _get_cache_location(self, cache: ICache) -> Path:
/ str(self.metadata.id)
/ self.metadata.version_id
)
download_dir.mkdir(parents=True, exist_ok=True)
resolve_path(download_dir).mkdir(parents=True, exist_ok=True)
return download_dir / _make_name_safe(self.metadata.name)

async def get_download_url(self) -> str:
Expand Down
29 changes: 29 additions & 0 deletions packages/evo-files/tests/test_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
# limitations under the License.

import json
import os
import unittest
from unittest import mock
from uuid import UUID

from data import load_test_data
from evo.common import ServiceUser
from evo.common._types import resolve_path
from evo.common.test_tools import TestWithConnector, TestWithDownloadHandler, TestWithUploadHandler, utc_datetime
from evo.files import FileAPIDownload, FileAPIUpload, FileMetadata

Expand Down Expand Up @@ -109,6 +113,31 @@ async def test_download_to_cache(self) -> None:
self.assertEqual(TEST_DATA, dest.read_bytes())
self.assert_download_requests(INITIAL_URL)

@unittest.skipUnless(os.name == "nt", "Windows long-path regression")
async def test_download_to_cache_long_path(self) -> None:
cache_location = self.cache.root / ("a" * 70) / ("b" * 70) / ("c" * 70)
resolved_cache_location = resolve_path(cache_location)
resolved_cache_location.mkdir(parents=True, exist_ok=True)
cache = mock.Mock()
cache.get_location.return_value = cache_location

expected = resolve_path(cache_location / str(FILE_ID) / VERSION_ID / self.metadata.name)
self.assertGreater(len(str(expected)), 259)

original_mkdir = type(resolved_cache_location).mkdir

def mkdir_with_legacy_limit(path, *args, **kwargs):
if len(str(path)) > 259 and not str(path).startswith("\\\\?\\"):
raise OSError("Path exceeds the legacy Windows limit")
return original_mkdir(path, *args, **kwargs)

with mock.patch.object(type(resolved_cache_location), "mkdir", mkdir_with_legacy_limit):
dest = await self.download.download_to_cache(cache, self.transport)

self.assertEqual(expected, resolve_path(dest))
self.assertEqual(TEST_DATA, dest.read_bytes())
self.assert_download_requests(INITIAL_URL)


class TestFileAPIUpload(TestWithConnector, TestWithUploadHandler):
def setUp(self) -> None:
Expand Down
Loading
Loading