From cf75180fcffdb4e28a816174077e534c6cc719b3 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 27 Sep 2024 11:14:17 +0000 Subject: [PATCH 1/5] fix: smaller widgets --- example.ipynb | 195 +++++++++++++++++++++++++++++++++++++++++------- ipygee/asset.py | 23 +++--- 2 files changed, 181 insertions(+), 37 deletions(-) diff --git a/example.ipynb b/example.ipynb index 645327a..5fc6aee 100644 --- a/example.ipynb +++ b/example.ipynb @@ -2,9 +2,20 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import ee \n", "\n", @@ -13,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -22,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -33,9 +44,44 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "am = AssetManager()\n", "am.to_sidecar()" @@ -43,33 +89,134 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "tm = TaskManager()\n", - "tm.to_sidecar()" + "#tm = TaskManager()\n", + "#tm.to_sidecar()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "m = Map()\n", - "m.to_sidecar()" + "#m = Map()\n", + "#m.to_sidecar()" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "jupyter": { - "source_hidden": true + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" } - }, - "outputs": [], + ], "source": [ "dataset = (\n", " ee.ImageCollection('ECMWF/ERA5_LAND/HOURLY')\n", @@ -87,9 +234,7 @@ " ]\n", "}\n", "\n", - "m.setCenter(22.2, 21.2, 0)\n", - "\n", - "m.addLayer(dataset, visualization, 'Air temperature [K] at 2m height')" + "#m.addLayer(dataset, visualization, 'Air temperature [K] at 2m height')" ] }, { @@ -109,7 +254,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -123,7 +268,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.12.6" } }, "nbformat": 4, diff --git a/ipygee/asset.py b/ipygee/asset.py index fde425a..1b0d330 100644 --- a/ipygee/asset.py +++ b/ipygee/asset.py @@ -80,19 +80,18 @@ def __init__(self): # fmt: off # add a line of buttons to reload and add new projects - self.w_new = v.Btn(color="error", children="NEW", elevation=2, class_="ma-1", disabled=True) - self.w_reload = v.Btn(children=[v.Icon(color="primary", children="mdi-reload")], elevation=2, class_="ma-1") - self.w_search = v.Btn(children=[v.Icon(color="primary", children="mdi-magnify")], elevation=2, class_="ma-1", disabled=True) + self.w_new = v.Btn(color="error", children="NEW", elevation=2, class_="ml-1", disabled=True, small=True) + self.w_reload = v.Btn(children=[v.Icon(color="primary", children="mdi-reload", small=True)], elevation=2, class_="ma-1", small=True) + self.w_search = v.Btn(children=[v.Icon(color="primary", children="mdi-magnify", small=True)], elevation=2, class_="mr-1", disabled=True, small=True) w_main_line = v.Flex(children=[self.w_new, self.w_reload, self.w_search]) # generate the asset selector and the CRUD buttons - self.w_selected = v.TextField(readonly=True, placeholder="Selected item", v_model="", clearable=True, outlined=True, class_="ma-1") - self.w_view = v.Btn(children=[v.Icon(color="primary", children="mdi-eye")], disabled=True) - self.w_copy = v.Btn(children=[v.Icon(color="primary", children="mdi-content-copy")], disabled=True) - self.w_move = v.Btn(children=[v.Icon(color="primary", children="mdi-file-move")], disabled=True) - self.w_delete = v.Btn(children=[v.Icon(color="primary", children="mdi-trash-can")], disabled=True) + self.w_selected = v.TextField(readonly=True, label="Selected item", v_model="", clearable=True, outlined=True, class_="mt-1") + self.w_view = v.Btn(children=[v.Icon(color="primary", children="mdi-eye", small=True)], disabled=True, small=True) + self.w_copy = v.Btn(children=[v.Icon(color="primary", children="mdi-content-copy", small=True)], disabled=True, small=True) + self.w_move = v.Btn(children=[v.Icon(color="primary", children="mdi-file-move", small=True)], disabled=True, small=True) + self.w_delete = v.Btn(children=[v.Icon(color="primary", children="mdi-trash-can", small=True)], disabled=True, small=True) w_btn_list = v.ItemGroup(class_="ma-1 v-btn-toggle",children=[self.w_view, self.w_copy, self.w_move, self.w_delete]) - w_selected_line = v.Layout(row=True, children=[w_btn_list, self.w_selected], class_="ma-1") # generate the initial list w_group = v.ListItemGroup(children=self.get_items(), v_model="") @@ -107,7 +106,7 @@ def __init__(self): super().__init__(children=[ self.w_delete_dialog, self.w_move_dialog, self.w_asset_dialog, self.w_create_dialog, - w_main_line, w_selected_line, self.w_card + w_main_line, w_btn_list, self.w_selected, self.w_card ], v_model="", class_="ma-1") # fmt: on @@ -190,7 +189,7 @@ def get_items(self) -> List[v.ListItem]: icon = ICON_STYLE[type]["icon"] color = ICON_STYLE[type]["color"] - action = v.ListItemAction(children=[v.Icon(color=color, children=[icon])], class_="mr-1") + action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") content = v.ListItemContent(children=[v.ListItemTitle(children=[i["name"]])]) dst_list = folder_list if type in ["FOLDER", "PROJECT"] else file_list dst_list.append(v.ListItem(value=i["id"], children=[action, content])) @@ -210,7 +209,7 @@ def get_items(self) -> List[v.ListItem]: name = parent.parts[1] if parent.is_project() else parent.name name = name or "." # special case for the root - action = v.ListItemAction(children=[v.Icon(color=color, children=[icon])], class_="mr-1") + action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") content = v.ListItemContent(children=[v.ListItemTitle(children=[name])]) item = v.ListItem(value=str(parent), children=[action, content]) From e2184840e60ad65d5a6e363b42d99b1504cfe45f Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 27 Sep 2024 11:20:32 +0000 Subject: [PATCH 2/5] fix: smaller widgets --- .pre-commit-config.yaml | 11 +-- example.ipynb | 175 +++------------------------------------- ipygee/asset.py | 8 +- 3 files changed, 24 insertions(+), 170 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bdcf075..f4972aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,11 +32,12 @@ repos: - id: ruff stages: [commit] - - repo: https://github.com/PyCQA/doc8 - rev: "v1.1.1" - hooks: - - id: doc8 - stages: [commit] + # crash on windows + #- repo: https://github.com/PyCQA/doc8 + # rev: "v1.1.1" + # hooks: + # - id: doc8 + # stages: [commit] # crash on ipyvuetify #- repo: https://github.com/FHPythonUtils/LicenseCheck diff --git a/example.ipynb b/example.ipynb index 5fc6aee..bc0a269 100644 --- a/example.ipynb +++ b/example.ipynb @@ -2,20 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import ee \n", "\n", @@ -24,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -33,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -44,44 +33,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "am = AssetManager()\n", "am.to_sidecar()" @@ -89,44 +43,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#tm = TaskManager()\n", "#tm.to_sidecar()" @@ -134,44 +53,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#m = Map()\n", "#m.to_sidecar()" @@ -179,44 +63,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "dataset = (\n", " ee.ImageCollection('ECMWF/ERA5_LAND/HOURLY')\n", diff --git a/ipygee/asset.py b/ipygee/asset.py index 1b0d330..b53fc5a 100644 --- a/ipygee/asset.py +++ b/ipygee/asset.py @@ -189,7 +189,9 @@ def get_items(self) -> List[v.ListItem]: icon = ICON_STYLE[type]["icon"] color = ICON_STYLE[type]["color"] - action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") + action = v.ListItemAction( + children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" + ) content = v.ListItemContent(children=[v.ListItemTitle(children=[i["name"]])]) dst_list = folder_list if type in ["FOLDER", "PROJECT"] else file_list dst_list.append(v.ListItem(value=i["id"], children=[action, content])) @@ -209,7 +211,9 @@ def get_items(self) -> List[v.ListItem]: name = parent.parts[1] if parent.is_project() else parent.name name = name or "." # special case for the root - action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") + action = v.ListItemAction( + children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" + ) content = v.ListItemContent(children=[v.ListItemTitle(children=[name])]) item = v.ListItem(value=str(parent), children=[action, content]) From 216261228b1fda7fe6685844c0f4f83562ef4a5f Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 27 Sep 2024 11:54:14 +0000 Subject: [PATCH 3/5] fix: reload upon file deletion --- example.ipynb | 175 ++++++++++++++++++++++++++++++++++++++++++++---- ipygee/asset.py | 16 ++--- 2 files changed, 171 insertions(+), 20 deletions(-) diff --git a/example.ipynb b/example.ipynb index bc0a269..5fc6aee 100644 --- a/example.ipynb +++ b/example.ipynb @@ -2,9 +2,20 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import ee \n", "\n", @@ -13,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -22,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -33,9 +44,44 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "am = AssetManager()\n", "am.to_sidecar()" @@ -43,9 +89,44 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#tm = TaskManager()\n", "#tm.to_sidecar()" @@ -53,9 +134,44 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#m = Map()\n", "#m.to_sidecar()" @@ -63,9 +179,44 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "dataset = (\n", " ee.ImageCollection('ECMWF/ERA5_LAND/HOURLY')\n", diff --git a/ipygee/asset.py b/ipygee/asset.py index b53fc5a..e113891 100644 --- a/ipygee/asset.py +++ b/ipygee/asset.py @@ -120,6 +120,7 @@ def __init__(self): t.link((self, "selected_item"), (self, "v_model")) self.w_list.children[0].observe(self.on_item_select, "v_model") self.w_reload.on_event("click", self.on_reload) + self.w_delete_dialog.observe(self.on_reload, "value") self.w_copy.on_event("click", self.on_copy) self.w_delete.on_event("click", self.on_delete) self.w_selected.observe(self.activate_buttons, "v_model") @@ -189,9 +190,7 @@ def get_items(self) -> List[v.ListItem]: icon = ICON_STYLE[type]["icon"] color = ICON_STYLE[type]["color"] - action = v.ListItemAction( - children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" - ) + action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") content = v.ListItemContent(children=[v.ListItemTitle(children=[i["name"]])]) dst_list = folder_list if type in ["FOLDER", "PROJECT"] else file_list dst_list.append(v.ListItem(value=i["id"], children=[action, content])) @@ -211,9 +210,7 @@ def get_items(self) -> List[v.ListItem]: name = parent.parts[1] if parent.is_project() else parent.name name = name or "." # special case for the root - action = v.ListItemAction( - children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" - ) + action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") content = v.ListItemContent(children=[v.ListItemTitle(children=[name])]) item = v.ListItem(value=str(parent), children=[action, content]) @@ -244,8 +241,11 @@ def on_item_select(self, change: dict): def on_reload(self, *args): """Reload the current folder.""" - self.on_item_select(change={"new": self.folder}) - + try: + self.on_item_select(change={"new": self.folder}) + except ValueError: + self.on_item_select(change={"new": ee.Asset(self.folder).parent.as_posix()}) + def on_copy(self, *args): """Copy the selected item to clipboard.""" self.send({"method": "clip", "args": [self.w_selected.v_model]}) From bfd86040061ae97771a144b3c11c6348f4d0f56a Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 27 Sep 2024 12:03:21 +0000 Subject: [PATCH 4/5] fix: only use the core version of geemap --- example.ipynb | 6 +++--- ipygee/map.py | 4 ++-- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example.ipynb b/example.ipynb index 5fc6aee..11f0498 100644 --- a/example.ipynb +++ b/example.ipynb @@ -173,8 +173,8 @@ } ], "source": [ - "#m = Map()\n", - "#m.to_sidecar()" + "m = Map()\n", + "m.to_sidecar()" ] }, { @@ -234,7 +234,7 @@ " ]\n", "}\n", "\n", - "#m.addLayer(dataset, visualization, 'Air temperature [K] at 2m height')" + "m.addLayer(dataset, visualization, 'Air temperature [K] at 2m height')" ] }, { diff --git a/ipygee/map.py b/ipygee/map.py index 6e7e583..f3248a5 100644 --- a/ipygee/map.py +++ b/ipygee/map.py @@ -1,12 +1,12 @@ """All the map related widgets and functions are here.""" from __future__ import annotations -import geemap +from geemap import core from .sidecar import HasSideCar -class Map(geemap.Map, HasSideCar): +class Map(core.Map, HasSideCar): """A subclass of geemap.Map with a sidecar method.""" sidecar_title = "Map" diff --git a/pyproject.toml b/pyproject.toml index fb2070e..1f7bcef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ipyvuetify", "natsort", "sidecar", - "geemap", + "geemap[core]", ] [[project.authors]] From a47a45829d4882d60b9988ae59b045182ca27d37 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 27 Sep 2024 12:05:58 +0000 Subject: [PATCH 5/5] fix: lint --- example.ipynb | 175 ++++-------------------------------------------- ipygee/asset.py | 12 ++-- 2 files changed, 20 insertions(+), 167 deletions(-) diff --git a/example.ipynb b/example.ipynb index 11f0498..da905ce 100644 --- a/example.ipynb +++ b/example.ipynb @@ -2,20 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import ee \n", "\n", @@ -24,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -33,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -44,44 +33,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "am = AssetManager()\n", "am.to_sidecar()" @@ -89,44 +43,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#tm = TaskManager()\n", "#tm.to_sidecar()" @@ -134,44 +53,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "m = Map()\n", "m.to_sidecar()" @@ -179,44 +63,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "dataset = (\n", " ee.ImageCollection('ECMWF/ERA5_LAND/HOURLY')\n", diff --git a/ipygee/asset.py b/ipygee/asset.py index e113891..2d21902 100644 --- a/ipygee/asset.py +++ b/ipygee/asset.py @@ -190,7 +190,9 @@ def get_items(self) -> List[v.ListItem]: icon = ICON_STYLE[type]["icon"] color = ICON_STYLE[type]["color"] - action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") + action = v.ListItemAction( + children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" + ) content = v.ListItemContent(children=[v.ListItemTitle(children=[i["name"]])]) dst_list = folder_list if type in ["FOLDER", "PROJECT"] else file_list dst_list.append(v.ListItem(value=i["id"], children=[action, content])) @@ -210,7 +212,9 @@ def get_items(self) -> List[v.ListItem]: name = parent.parts[1] if parent.is_project() else parent.name name = name or "." # special case for the root - action = v.ListItemAction(children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1") + action = v.ListItemAction( + children=[v.Icon(color=color, small=True, children=[icon])], class_="mr-1" + ) content = v.ListItemContent(children=[v.ListItemTitle(children=[name])]) item = v.ListItem(value=str(parent), children=[action, content]) @@ -243,9 +247,9 @@ def on_reload(self, *args): """Reload the current folder.""" try: self.on_item_select(change={"new": self.folder}) - except ValueError: + except ValueError: self.on_item_select(change={"new": ee.Asset(self.folder).parent.as_posix()}) - + def on_copy(self, *args): """Copy the selected item to clipboard.""" self.send({"method": "clip", "args": [self.w_selected.v_model]})