Skip to content
Merged
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
2,205 changes: 1,091 additions & 1,114 deletions pixi.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ plotly = ">=6.0.0"
scikit-learn = ">=1.6.1"
trame-plotly = ">=3.1.0"
trame-datagrid = ">=0.2.2"
selenium = ">=4.38.0,<5"
setuptools = ">=80.9.0,<81"
cryptography = ">=46.0.3,<47"

Expand Down
78 changes: 1 addition & 77 deletions src/exphub/app/models/css_status.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,17 @@
"""Model for CSS status."""

import io
import time
from typing import Dict, List

import plotly.graph_objects as go
from PIL import Image
from plotly.data import iris
from pydantic import BaseModel, Field
from selenium.webdriver import Firefox, FirefoxOptions

IRIS_DATA = iris()


bl12cssstatus_urlsrc = "https://status.sns.ornl.gov/dbwr/view.jsp?display=https%3A//webopi.sns.gov/bl12/files/bl12/opi/BL12_ADnED_2D_4x4.bob&macros=%7B%26quot%3BDET1%26quot%3B%3A%26quot%3BMain%20Detector%26quot%3B%2C%26quot%3BDET2%26quot%3B%3A%26quot%3BMain%20d-Space%26quot%3B%2C%26quot%3BDET3%26quot%3B%3A%26quot%3BMain%20q-Space%26quot%3B%2C%26quot%3BDET4%26quot%3B%3A%26quot%3BMain%204x4%20and%20ROI%20d-Space%26quot%3B%2C%26quot%3BDET5%26quot%3B%3A%26quot%3BMain%20ROI%20q-Space%26quot%3B%2C%26quot%3BIOCSTATS%26quot%3B%3A%26quot%3BBL12%3ACS%3AADnED%3A%26quot%3B%2C%26quot%3BP%26quot%3B%3A%26quot%3BBL12%3ADet%3A%26quot%3B%2C%26quot%3BR%26quot%3B%3A%26quot%3BN1%3A%26quot%3B%2C%26quot%3BTAB%26quot%3B%3A%26quot%3BMain%20Detector%26quot%3B%2C%26quot%3BTOPR%26quot%3B%3A%26quot%3BN1%3A%26quot%3B%2C%26quot%3BBL%26quot%3B%3A%26quot%3BBL12%26quot%3B%2C%26quot%3BDID%26quot%3B%3A%26quot%3BDID305%26quot%3B%2C%26quot%3BS%26quot%3B%3A%26quot%3BBL12%26quot%3B%7D"


def save_webpage_as_image(url: str, output_file: str = "webpage.png") -> bytes:
# Configure headless Firefox browser
options = FirefoxOptions()
options.add_argument("-headless")
options.add_argument("--height=10000")

# Start browser and load page
browser = Firefox(options=options)
browser.get(url)

# Take screenshot and save it
time.sleep(1.4)
screenshot = browser.get_screenshot_as_png()
# with open(output_file, "wb") as file:
# file.write(screenshot)
# print(f"Screenshot saved to {output_file}")

# Close browser

return screenshot


# save_webpage_as_image(bl12cssstatus_urlsrc)


class CSSStatusModel(BaseModel):
"""Pydantic class for CSS status."""

Expand Down Expand Up @@ -71,6 +42,7 @@ class CSSStatusModel(BaseModel):
# init_image: bytes = save_webpage_as_image(bl12cssstatus_urlsrc)
# plot_type_options: list[str] = ["Detector", "D-space", "Q-space", "4x4 and ROI D-space", "ROI Q-space", "IOCSTATS", "Det", "N1", "Main Detector", "N1", "BL12", "DID", "S"] # noqa
# fig: go.Figure = Field(default=go.Figure(), title="Figure")
screenshot: str = Field(default="")

# @computed_field # type: ignore
# @property
Expand All @@ -84,54 +56,6 @@ def is_not_heatmap(self) -> bool:
case _:
return True

def get_figure(self) -> go.Figure:
screenshot = save_webpage_as_image(bl12cssstatus_urlsrc)
image = Image.open(io.BytesIO(screenshot))
width, height = image.size
# timestamp = time.time()
# self.timestamp = timestamp
print("genratged fig md5sum:" + str(hash(image.tobytes())))
match self.plot_type:
case "Detector":
cropscreen = image.crop((0, 0, int(width * 0.65), int(height * 0.457)))
case "D-space":
cropscreen = image.crop((0, int(height * 0.457), int(width * 0.65), int(height * 1.000)))
screenshot_obj = io.BytesIO()
cropscreen.save(screenshot_obj, format="PNG")
screenshot = screenshot_obj.getvalue()
image = Image.open(io.BytesIO(screenshot))
print("genratged fig md5sum:" + str(hash(image.tobytes())))

fig = go.Figure()
fig.update_layout(
plot_bgcolor="white", # Background color of the plot area
paper_bgcolor="white", # Background color of the entire figure
)
fig.add_layout_image(
{
"source": image,
"xref": "paper",
"yref": "paper",
"x": 0.5,
"y": 0.5,
"sizex": 1,
"sizey": 1,
"xanchor": "center",
"yanchor": "middle",
"opacity": 1,
# plot_bgcolor='white', # Background color of the plot area
# paper_bgcolor='white' # Background color of the entire figure
}
)
fig.update_layout(
# title="CSS Status: "+str(time.time()),
xaxis={"visible": False},
yaxis={"visible": False},
)
fig.update_layout(margin={"l": 0, "r": 0, "t": 0, "b": 0})
print("get plotly figure done")
return fig

def get_figure_0(self) -> go.Figure:
match self.plot_type:
case "heatmap":
Expand Down
12 changes: 10 additions & 2 deletions src/exphub/app/view_models/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,26 @@
# from ..models.temporal_analysis import TemporalAnalysisModel
import plotly.graph_objects as go
from nova.mvvm.interface import BindingInterface
from pydantic import BaseModel, Field

# from ..models.plotly import PlotlyConfig
# from pyvista import Plotter # just for typing
# from ..models.pyvista import PyVistaConfig
from ..models.main_model import MainModel


class ViewState(BaseModel):
"""View state for the application."""

active_tab: str = Field(default="0")


class MainViewModel:
"""Viewmodel class, used to create data<->view binding and react on changes from GUI."""

def __init__(self, model: MainModel, binding: BindingInterface):
self.model = model
self.view_state = ViewState()
# Guard to prevent recursive / re-entrant updates for temporalanalysis
self._temporalanalysis_updating: bool = False
# Debounce: avoid repeated updates in a short interval (seconds)
Expand All @@ -39,6 +47,7 @@ def __init__(self, model: MainModel, binding: BindingInterface):
# but one also can provide a callback function if they want to react to those events
# and/or process errors.
self.model_bind = binding.new_bind(self.model, callback_after_update=self.change_callback)
self.view_state_bind = binding.new_bind(self.view_state)

# self.experimentinfo_bind = binding.new_bind(self.model.experimentinfo, callback_after_update=self.change_callback)#noqa
self.experimentinfo_bind = binding.new_bind(
Expand All @@ -53,7 +62,6 @@ def __init__(self, model: MainModel, binding: BindingInterface):

# self.cssstatus_bind = binding.new_bind(self.model.cssstatus, callback_after_update=self.change_callback)
self.cssstatus_bind = binding.new_bind(self.model.cssstatus, callback_after_update=self.update_cssstatus_figure)
self.cssstatus_updatefig_bind = binding.new_bind()
self.temporalanalysis_updatefigure_uncertainty_bind = binding.new_bind()
self.temporalanalysis_updatefigure_intensity_bind = binding.new_bind()
######################################################################################################################################################
Expand Down Expand Up @@ -145,8 +153,8 @@ def call_load_token(self) -> None:
#

def update_cssstatus_figure(self, _: Any = None) -> None:
# self.model.cssstatus.update_figure()
self.cssstatus_bind.update_in_view(self.model.cssstatus)
self.cssstatus_updatefig_bind.update_in_view(self.model.cssstatus.get_figure())
# time.sleep(7)

async def auto_update_cssstatus_figure(self) -> None:
Expand Down
54 changes: 17 additions & 37 deletions src/exphub/app/views/angle_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import plotly.graph_objects as go
import trame
from nova.trame.view.components import InputField, RemoteFileInput
from nova.trame.view.layouts import GridLayout, HBoxLayout
from nova.trame.view.layouts import GridLayout, HBoxLayout, VBoxLayout
from trame.widgets import html, plotly
from trame.widgets import vuetify3 as vuetify

Expand Down Expand Up @@ -101,10 +101,11 @@ def save_run() -> None:
# self.view_model.save_run()
self.view_model.save_run()

with GridLayout(columns=2):
with GridLayout(columns=2, gap="0.5em"):
InputField(v_model="model_angleplan.plan_name") # , type="button", label="Upload")
InputField(v_model="model_angleplan.plan_type", type="select", items="model_angleplan.plan_type_list")
with GridLayout(columns=2):

with HBoxLayout(gap="0.5em"):
RemoteFileInput(
v_model="model_angleplan.plan_file",
base_paths=[
Expand All @@ -118,31 +119,13 @@ def save_run() -> None:

# vuetify.VCardTitle("CrystalPlan Table")

with vuetify.VSheet(classes="pa-4"):
vuetify.VCardTitle("Experiment Run Strategy")
with VBoxLayout(classes="border-lg border-primary mb-1", stretch=True):
with vuetify.VDataTable(
classes="flex-1-1",
headers=("model_angleplan.angle_list_headers", []), # TODO trame syntax,
items=("model_angleplan.angle_list", []),
):
with vuetify.Template(v_slot_top=True):
# with vuetify.Template(v_slot_bottom=True):
with vuetify.VToolbar(flat=True):
vuetify.VToolbarTitle("Experiment Run Strategy")
# vuetify.VSpacer()
# vuetify.VBtn(
# "Reset Strategy",
# #prepend_icon="mdi-",
# click=self.view_model.reset_run,
# #click="trigger('add_run')",
# )

# vuetify.VSpacer()
# vuetify.VBtn(
# "Add a Run",
# prepend_icon="mdi-plus",
# click=self.view_model.add_run,
# #click="trigger('add_run')",
# )

with vuetify.Template(
raw_attrs=['v-slot:item.actions="{ item }"']
): # TODO 'item' predefined by vuetify
Expand Down Expand Up @@ -252,7 +235,7 @@ def show_coverage() -> None:
self.figure_coverage.state.flush()
self.view_model.show_coverage()

with GridLayout(columns=3):
with HBoxLayout(gap="0.5em", halign="center"):
vuetify.VBtn("Initialize Strategy", click=self.view_model.reset_run, style="align-self: center;")
vuetify.VBtn(
"Add a Run",
Expand All @@ -263,14 +246,18 @@ def show_coverage() -> None:
vuetify.VBtn("Show Coverage", click="trigger('show_coverage')", style="align-self: center;")
# vuetify.VBtn("Show Coverage", click="trigger('show_coverage',[coverage_fig,])", style="align-self: center;")#noqa

with GridLayout(columns=2):
with HBoxLayout(gap="0.5em", valign="center"):
RemoteFileInput(v_model="model_eiccontrol.token_file", base_paths=["/HFIR", "/SNS"])
vuetify.VBtn("Authenticate", click=self.view_model.call_load_token, style="align-self: center;")
with GridLayout(columns=2):
vuetify.VBtn("Authenticate", click=self.view_model.call_load_token)
InputField(v_model="model_eiccontrol.is_simulation", type="checkbox")
# vuetify.VBtn("Update Strategy", click=self.view_model.update_view, style="align-self: center;")
# html.Div(style="height: 20px;")
vuetify.VBtn("Submit through EIC", click=self.view_model.submit_angle_plan, style="align-self: center;")
vuetify.VBtn("Submit through EIC", click=self.view_model.submit_angle_plan)
html.P(
"Submission Successful.",
v_if="model_eiccontrol.eic_submission_success",
# text="Submission Successful. Scan ID: {{model_eiccontrol.eic_submission_scan_id}}, Message: {{model_eiccontrol.eic_submission_message}}",#noqa
)

with vuetify.VDialog(
v_model="model_angleplan.is_showing_coverage", max_width="500px"
Expand Down Expand Up @@ -344,14 +331,7 @@ def show_coverage() -> None:
# InputField(v_model="model_eiccontrol.IPTS_number")
# InputField(v_model="model_eiccontrol.instrument_name")

with GridLayout(columns=1):
vuetify.VBanner(
v_if="model_eiccontrol.eic_submission_success",
text="Submission Successful.",
# text="Submission Successful. Scan ID: {{model_eiccontrol.eic_submission_scan_id}}, Message: {{model_eiccontrol.eic_submission_message}}",#noqa
color="success",
)
with GridLayout(columns=4):
with GridLayout(columns=4, gap="0.5em"):
InputField(
v_model="model_eiccontrol.eic_auto_stop_strategy",
type="select",
Expand Down
Loading
Loading