From a0d32f10d0dbb146e563224a6e3727bbf91655ae Mon Sep 17 00:00:00 2001 From: mikibonacci Date: Tue, 3 Dec 2024 19:33:20 +0000 Subject: [PATCH] Adding DownloadYamlHdf5Widget For now, I added it at the end of the main EuphonicBaseResultsWidget. It will only allow to download phonopy.yaml and fc.hdf5. The plots can be downloaded in the corresponding tabs. I added a method to the model which produce the downloadable files, and left the actual _download_data method in the widget. I think this is a proper design as the model manipulates data, the view can provide them. Still missing the download of single tabs, for now it does not work --- .../app/widgets/euphonicmodel.py | 66 ++----------------- .../app/widgets/euphonicwidget.py | 18 +++-- 2 files changed, 18 insertions(+), 66 deletions(-) diff --git a/src/aiidalab_qe_vibroscopy/app/widgets/euphonicmodel.py b/src/aiidalab_qe_vibroscopy/app/widgets/euphonicmodel.py index d11cb89..687b838 100644 --- a/src/aiidalab_qe_vibroscopy/app/widgets/euphonicmodel.py +++ b/src/aiidalab_qe_vibroscopy/app/widgets/euphonicmodel.py @@ -1,11 +1,6 @@ import numpy as np import traitlets as tl import copy -import json -import base64 -import plotly.io as pio -from monty.json import jsanitize -from IPython.display import display from aiidalab_qe_vibroscopy.utils.euphonic.data_manipulation.intensity_maps import ( AttrDict, @@ -15,6 +10,7 @@ par_dict, par_dict_powder, export_euphonic_data, + generate_force_constant_instance, ) from aiidalab_qe_vibroscopy.utils.euphonic.tab_widgets.euphonic_q_planes_widgets import ( @@ -247,58 +243,10 @@ def curate_path_and_labels( def _clone(self): return copy.deepcopy(self) - def download_data(self, _=None): - """ - Download both the ForceConstants and the spectra json files. - """ - force_constants_dict = self.fc.to_dict() - - filename = "single_crystal.json" - my_dict = {} - for branch in range(len(self.spectra)): - my_dict[str(branch)] = self.spectra[branch].to_dict() - my_dict.update( - { - "weighting": self.weighting, - "q_spacing": self.q_spacing, - "energy_broadening": self.energy_broadening, - "ebins": self.energy_bins, - "temperature": self.temperature, - } - ) - for k in ["weighting", "q_spacing", "temperature"]: - filename += "_" + k + "_" + str(my_dict[k]) - - # FC download: - json_str = json.dumps(jsanitize(force_constants_dict)) - b64_str = base64.b64encode(json_str.encode()).decode() - self._download(payload=b64_str, filename="force_constants.json") - - # Powder data download: - json_str = json.dumps(jsanitize(my_dict)) - b64_str = base64.b64encode(json_str.encode()).decode() - self._download(payload=b64_str, filename=filename + ".json") - - # Plot download: - ## Convert the FigureWidget to an image in base64 format - image_bytes = pio.to_image( - self.map_widget.children[1], format="png", width=800, height=600 - ) - b64_str = base64.b64encode(image_bytes).decode() - self._download(payload=b64_str, filename=filename + ".png") - - @staticmethod - def _download(payload, filename): - from IPython.display import Javascript - - javas = Javascript( - """ - var link = document.createElement('a'); - link.href = 'data:text/json;charset=utf-8;base64,{payload}' - link.download = "{filename}" - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - """.format(payload=payload, filename=filename) + def produce_phonopy_files(self): + # This is used to produce the phonopy files from + # PhonopyCalculation data. The files are phonopy.yaml and force_constants.hdf5 + phonopy_yaml, fc_hdf5 = generate_force_constant_instance( + self.node.phonon_bands.creator, mode="download" ) - display(javas) + return phonopy_yaml, fc_hdf5 diff --git a/src/aiidalab_qe_vibroscopy/app/widgets/euphonicwidget.py b/src/aiidalab_qe_vibroscopy/app/widgets/euphonicwidget.py index 7fe0014..00024e8 100644 --- a/src/aiidalab_qe_vibroscopy/app/widgets/euphonicwidget.py +++ b/src/aiidalab_qe_vibroscopy/app/widgets/euphonicwidget.py @@ -190,11 +190,15 @@ def render(self): self.upload_widget.children[0].observe(self._on_upload_yaml, "_counter") self.upload_widget.children[1].observe(self._on_upload_hdf5, "_counter") + self.download_widget = DownloadYamlHdf5Widget(model=self._model) + self.download_widget.layout.display = "none" + self.children = [ self.upload_widget, self.plot_button, self.loading_widget, self.tab_widget, + self.download_widget, ] def _on_reset_uploads_button_clicked(self, change): @@ -259,10 +263,13 @@ def _on_first_plot_button_clicked(self, change=None): # basically the render. self.loading_widget.layout.display = "none" self.tab_widget.layout.display = "block" + self.download_widget.layout.display = "block" class DownloadYamlHdf5Widget(ipw.HBox): - def __init__(self, phonopy_node, **kwargs): + def __init__(self, model): + self._model = model + self.download_button = ipw.Button( description="Download phonopy data", icon="pencil", @@ -270,8 +277,7 @@ def __init__(self, phonopy_node, **kwargs): disabled=False, layout=ipw.Layout(width="auto"), ) - self.download_button.on_click(self.download_data) - self.node = phonopy_node + self.download_button.on_click(self._download_data) super().__init__( children=[ @@ -279,13 +285,11 @@ def __init__(self, phonopy_node, **kwargs): ], ) - def download_data(self, _=None): + def _download_data(self, _=None): """ Download both the phonopy.yaml and fc.hdf5 files. """ - phonopy_yaml, fc_hdf5 = generate_force_constant_instance( - self.node, mode="download" - ) + phonopy_yaml, fc_hdf5 = self._model.produce_phonopy_files() self._download(payload=phonopy_yaml, filename="phonopy" + ".yaml") self._download(payload=fc_hdf5, filename="fc" + ".hdf5")