Skip to content

subset_options

Subset options menu component, contains everything under the subset header.

CloneSubsetButton(key) #

Clone subset button

Note

Calls SubsetState.clone_subset

Parameters:

Name Type Description Default
key str

subset key

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
@sl.component()
def CloneSubsetButton(key: str) -> ValueElement:
    """Clone subset button

    Note:
        Calls `SubsetState.clone_subset`

    Args:
        key: subset key

    """
    with sl.Tooltip("Clone this subset") as main:
        sl.Button(
            label="",
            icon_name="mdi-content-duplicate",
            icon=True,
            text=True,
            on_click=lambda: SubsetState.clone_subset(key),
        )
    return main

DeleteSubsetDialog(deleter) #

Delete subset dialog

Parameters:

Name Type Description Default
deleter Callable

deletion function to remove from SubsetState

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
@sl.component()
def DeleteSubsetDialog(deleter: Callable) -> ValueElement:
    """Delete subset dialog

    Args:
        deleter: deletion function to remove from SubsetState

    """
    # confirmation dialog for deletion
    delete = sl.use_reactive(False)
    with sl.Tooltip("Delete this subset") as main:
        sl.Button(
            label="",
            icon_name="mdi-delete-outline",
            icon=True,
            text=True,
            disabled=True if len(SubsetState.subsets.value) <= 1 else False,
            color="red",
            on_click=lambda: delete.set(True),
        )
        ConfirmationDialog(
            delete,
            title="Are you sure you want to delete this subset?",
            ok="yes",
            cancel="no",
            on_ok=deleter,
        )
    return main

DownloadMenu(key) #

Download menu and button

Note

THIS CONTAINS ALL FUNCTIONALITY FOR QUERYING sdss_explorer.server

Parameters:

Name Type Description Default
key str

subset key

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
@sl.component()
def DownloadMenu(key: str) -> ValueElement:
    """Download menu and button

    Note:
        **THIS CONTAINS ALL FUNCTIONALITY FOR QUERYING `sdss_explorer.server`**

    Args:
        key: subset key
    """

    router = sl.use_router()
    subset = SubsetState.subsets.value[key]
    response, set_response = sl.use_state({"status": "not_run"})

    def reset_status():
        """On change and not pending a result, reset"""
        logger.debug(f"Resetting download button on {subset.name}")
        default = {"status": "not_run"}
        if response["status"] != "in_progress":
            set_response(default)

    sl.use_effect(reset_status, dependencies=[subset])

    def query_job_status(uid: str) -> dict[str, str]:
        """Ping command to query job state and return result

        Args:
            uid: job ID

        Returns:
            data: Dictionary of response data
            response: previous response data (when failing)
        """
        try:
            resp = requests.get(urljoin(settings.api_url, f"status/{uid}"))
            if resp.status_code == 200:
                data = json.loads(resp.text)
                return data
        except Exception as e:
            logger.debug(f"failed to connect: {e}")
            Alert.update("Failed to connect to download sever", color="error")
        return response

    # @sl.lab.task()
    async def query_task():
        """Runs a GET query continously for the given job of the subset"""
        logger.debug(f"query task start on {subset.name}")

        # get our local response
        local_response = response.copy()
        status = local_response.get("status", "in_progress")

        # early exit if something else triggered update (reset, completion, etc)
        if (status == "complete") or (status == "failed") or (status
                                                              == "not_run"):
            return

        # loop until received, spawning in thread
        while True:
            local_response = await asyncio.to_thread(query_job_status,
                                                     local_response["uid"])
            logger.debug(f"received response {local_response}")

            if local_response["status"] == "complete":
                logger.debug(f"job returned as complete! {local_response}")
                Alert.update(
                    message=
                    f"Your file for Subset {subset.name} is ready! Hit the download button",
                    color="success",
                )

                # apply to everything and get out
                set_response(local_response)
                break
            elif local_response["status"] == "failed":
                logger.debug(f"job returned as failed! {local_response}")
                Alert.update(
                    message="File render failed! Please try again, "
                    "and inform system adminstrator if it keeps failing.",
                    color="error",
                )
                set_response(local_response)
                break

            # if not run or in_progress, continues
            await asyncio.sleep(2)

        # flag our exit
        logger.debug(f"query task shutdown on {subset.name}")
        return

    sl.lab.use_task(query_task, dependencies=[response])

    def send_job():
        """Exports subset data to JSON and sends to FastAPI DL sever."""
        from ...dataclass import State
        from dataclasses import asdict

        # serialize & remove columns/df from req
        data = asdict(subset)
        data.pop("columns")
        data.pop("df")
        dataset = data["dataset"]
        jsonData = json.dumps(data)
        logger.debug("requesting" + str(jsonData))
        try:
            # TODO: no idea how requests works
            resp = requests.post(
                urljoin(
                    settings.api_url,
                    f"filter_subset/{State.release}/{State.datatype}/{dataset}",
                ),
                params=data,
                data=jsonData,
            )
            if resp.status_code == 202:
                # ready! push update to call query loop task
                logger.debug("Successfully called for download for" +
                             subset.name)
                Alert.update("Creating file for download! Please wait.")
                set_response(json.loads(resp.text))
        # on timeout raise, inform user
        except Exception as e:
            logger.debug(f"failed to connect to call for download: {e}")
            Alert.update("Failed to connect to download sever", color="error")
        return

    def click_handler():
        """Handles click events"""
        if (response.get("status") == "not_run") or (response.get("status")
                                                     == "failed"):
            send_job()
        elif response.get("status") == "complete":
            logger.debug(
                f"sending user to {settings.download_url}{response.get('filepath', 'foobar')}"
            )

    # color logic
    if response["status"] == "complete":
        color = "green"
    elif response["status"] == "failed":
        color = "red"
    else:
        color = "white"
    button = sl.Button(
        label="",
        icon_name="mdi-download",
        color=color,
        disabled=True if response["status"] == "in_progress" else False,
        icon=True,
        text=True,
        href=
        f"{urljoin(settings.download_url, response.get('filepath', 'foobar'))}"
        if response["status"] == "complete" else None,
        target="_blank",
        on_click=click_handler,
    )

    tooltipped = rv.Tooltip(
        bottom=True,
        disabled=True if response["status"] == "complete" else False,
        v_slots=[{
            "name": "activator",
            "variable": "tooltip",
            "children": [button]
        }],
        color=None,  # pyright: ignore[]
        children=["Download subset"],
    )

    def set_v_on():
        widget = sl.get_widget(button)
        widget.v_on = "tooltip.on"  # pyright: ignore

    sl.use_effect(set_v_on, button)

    return sl.Column(children=[tooltipped])

InvertButton(invert) #

Invert subset button

Parameters:

Name Type Description Default
invert Reactive[bool]

invert setting

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@sl.component()
def InvertButton(invert) -> ValueElement:
    """Invert subset button

    Args:
        invert (solara.Reactive[bool]): invert setting

    """
    with sl.Tooltip("Invert the filters of this subset") as main:
        sl.Button(
            label="",
            icon_name="mdi-invert-colors-off"
            if invert.value else "mdi-invert-colors",
            color="red" if invert.value else None,
            icon=True,
            text=True,
            on_click=lambda: invert.set(not invert.value),
        )
    return main

RenameSubsetButton(key) #

Rename subset button

Note

Calls SubsetState.rename_subset

Parameters:

Name Type Description Default
key str

subset key

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
@sl.component()
def RenameSubsetButton(key: str) -> ValueElement:
    """Rename subset button

    Note:
        Calls `SubsetState.rename_subset`

    Args:
        key: subset key

    """
    newname, set_newname = sl.use_state("")  # rename dialog state
    rename = sl.use_reactive(False)

    def rename_handler():
        if SubsetState.rename_subset(key, newname=newname):
            rename.set(False)
            set_newname("")

    with sl.Tooltip("Rename this subset"):
        sl.Button(
            label="",
            icon_name="mdi-rename-box",
            icon=True,
            text=True,
            on_click=lambda: rename.set(True),
        )
        with Dialog(
                rename,
                title="Enter a new name for this subset",
                close_on_ok=False,
                on_ok=rename_handler,
                on_cancel=lambda: set_newname(""),
        ):
            sl.InputText(
                label="Subset name",
                value=newname,
                on_value=set_newname,
            )

SubsetOptions(key, deleter) #

Contains all subset configuration threads and state variables, including expression, cartonmapper, clone and delete.

Grouped to single namespace to add clone functionality.

Parameters:

Name Type Description Default
key str

key for subset

required
deleter Callable

deletion functions

required
Source code in src/sdss_explorer/dashboard/components/sidebar/subset_options.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@sl.component()
def SubsetOptions(key: str, deleter: Callable):
    """
    Contains all subset configuration threads and state variables,
    including expression, cartonmapper, clone and delete.

    Grouped to single namespace to add clone functionality.

    Args:
        key: key for subset
        deleter: deletion functions
    """
    # filter settings/subfilters
    subset = SubsetState.subsets.value[key]
    invert = sl.use_reactive(False)
    open, set_open = sl.use_state([])
    dataset, set_dataset = (
        subset.dataset,
        lambda arg: SubsetState.update_subset(key, dataset=arg),
    )

    # flag data

    # User facing
    with sl.Column() as main:
        if subset.df is not None:
            ExprEditor(key, invert)
            DatasetSelect(key, dataset, set_dataset)
            # complex option panels
            with rv.ExpansionPanels(flat=True,
                                    multiple=True,
                                    v_model=open,
                                    on_v_model=set_open):
                TargetingFiltersPanel(key, invert)
                CrossmatchPanel(key)

            # bottom card actions
            with rv.Row(style_="width: 100%; height: 100%"):
                DownloadMenu(key)  # download button
                InvertButton(invert)  # invert button
                rv.Spacer()  # spacer
                RenameSubsetButton(key)  # rename button
                CloneSubsetButton(key)  # clone button
                DeleteSubsetDialog(deleter)  # delete button

    return main