code changes for release 3.1.0: SciLog, DA30-PShell data files
This commit is contained in:
46
.gitea/workflows/deploy-pages.yaml
Normal file
46
.gitea/workflows/deploy-pages.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: build and deploy documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- distro
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: gitea.psi.ch/pearl/docs
|
||||
credentials:
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.package_token }}
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
working-directory: /app
|
||||
run: |
|
||||
git clone --branch master --single-branch https://${{ secrets.REPO_TOKEN }}@gitea.psi.ch/${{ github.repository }}.git
|
||||
|
||||
- name: build
|
||||
working-directory: /app/igor-procs/doc
|
||||
run: |
|
||||
REVISION=$(git describe --always --tags --dirty --long || date +"%F %T %z")
|
||||
export REVISION
|
||||
doxygen config.dox
|
||||
mv html/ /app/
|
||||
|
||||
- name: configure git
|
||||
working-directory: /app/igor-procs
|
||||
run: |
|
||||
git config --global user.name "Gitea Actions"
|
||||
git config --global user.email "actions@gitea.local"
|
||||
|
||||
- name: push to gitea-pages
|
||||
working-directory: /app/igor-procs
|
||||
run: |
|
||||
git checkout --orphan gitea-pages
|
||||
git reset --hard
|
||||
cp -r /app/html/* .
|
||||
git add .
|
||||
git commit -m "Deploy documentation to gitea"
|
||||
git push -f https://${{ secrets.REPO_TOKEN }}@gitea.psi.ch/${{ github.repository }}.git gitea-pages
|
||||
|
||||
@@ -36,12 +36,18 @@ Matthias Muntwiler, <mailto:matthias.muntwiler@psi.ch>
|
||||
Copyright
|
||||
---------
|
||||
|
||||
Copyright 2009-2022 by [Paul Scherrer Institut](http://www.psi.ch)
|
||||
Copyright 2009-2025 by [Paul Scherrer Institut](http://www.psi.ch)
|
||||
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
## rev-distro-3.1.0
|
||||
|
||||
- Ingestor to SciLog electronic logbook
|
||||
- Support for PShell files from DA30/DFS30 analyser
|
||||
- Fix Gizmo window in Igor 8 and higher
|
||||
|
||||
## rev-distro-3.0.0
|
||||
|
||||
- New panel and procedure interface for PShell data file import.
|
||||
|
||||
@@ -1581,7 +1581,7 @@ EXTRA_SEARCH_MAPPINGS =
|
||||
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_LATEX = YES
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma TextEncoding = "UTF-8"
|
||||
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
||||
#pragma IgorVersion = 6.2
|
||||
#pragma IgorVersion = 7.0
|
||||
#pragma ModuleName = PearlAreaDisplay
|
||||
#pragma version = 1.04
|
||||
#pragma version = 1.05
|
||||
#include "pearl-compat"
|
||||
#include "pearl-area-profiles"
|
||||
|
||||
@@ -1161,7 +1161,7 @@ function /s ad_display_brick(data)
|
||||
string /g gizmo_graphname = graphname_from_dfref(datadf, "giz_")
|
||||
svar graphname = gizmo_graphname
|
||||
|
||||
if ((strlen(graphname) > 0) && (wintype(graphname) == 13))
|
||||
if ((strlen(graphname) > 0) && (wintype(graphname) == 17))
|
||||
setdatafolder savedf
|
||||
return graphname // gizmo window exists
|
||||
endif
|
||||
@@ -1380,7 +1380,7 @@ function ad_brick_slicer(data)
|
||||
z_slice_pos = dimoffset(data, 2) + dimsize(data, 2) * dimdelta(data, 2) / 2
|
||||
|
||||
svar /z /sdfr=viewdf gizmo_graphname
|
||||
if (svar_exists(gizmo_graphname) && (strlen(gizmo_graphname) > 0) && (wintype(gizmo_graphname) == 13))
|
||||
if (svar_exists(gizmo_graphname) && (strlen(gizmo_graphname) > 0) && (wintype(gizmo_graphname) == 17))
|
||||
ad_gizmo_set_plane(data, 0, x_slice_pos)
|
||||
ad_gizmo_set_plane(data, 1, y_slice_pos)
|
||||
ad_gizmo_set_plane(data, 2, z_slice_pos)
|
||||
@@ -1495,7 +1495,7 @@ function ad_gizmo_set_plane(brick, dim, value)
|
||||
return -1 // requested value out of range
|
||||
endif
|
||||
|
||||
if (svar_exists(graphname) && (strlen(graphname) > 0) && (wintype(graphname) == 13))
|
||||
if (svar_exists(graphname) && (strlen(graphname) > 0) && (wintype(graphname) == 17))
|
||||
string axes = "xyz"
|
||||
string obj = "surface_" + axes[dim] + "mid"
|
||||
string cmd
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
||||
#pragma IgorVersion = 6.1
|
||||
#pragma ModuleName = PearlArpes
|
||||
#pragma version = 1.05
|
||||
#pragma version = 1.06
|
||||
#include "pearl-area-display" // 2D and 3D data visualization
|
||||
#include "pearl-area-profiles" // data processing for multi-dimensional datasets
|
||||
#include "pearl-area-import" // import data files generated by area detector software
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "pearl-anglescan-tracker" // live preview of hemispherical angle scan
|
||||
#include "pearl-scienta-preprocess" // pre-processing functions for Scienta detector images
|
||||
#include "pearl-elog"
|
||||
#include "pearl-scilog"
|
||||
#if exists("pvOpen")
|
||||
#include "pearl-area-live" // live view of area detector
|
||||
#include "pearl-epics" // EPICS access under Igor
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
||||
#pragma IgorVersion = 6.36
|
||||
#pragma ModuleName = PearlDataExplorer
|
||||
#pragma version = 2.1
|
||||
#pragma version = 2.2
|
||||
#include <HierarchicalListWidget>, version >= 1.14
|
||||
#include "pearl-area-import"
|
||||
#include "pearl-area-profiles"
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "pearl-compat"
|
||||
#include "pearl-pshell-import"
|
||||
|
||||
// copyright (c) 2013-22 Paul Scherrer Institut
|
||||
// copyright (c) 2013-25 Paul Scherrer Institut
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -543,87 +543,6 @@ static function notebook_add_attributes(notebook_name, attr_filter, attr_names,
|
||||
endfor
|
||||
end
|
||||
|
||||
/// send general metadata to ELOG panel - if available
|
||||
///
|
||||
/// the following metatdata are sent.
|
||||
/// they must be present as strings in the specified data folder:
|
||||
///
|
||||
/// | ELOG parameter | global string | function argument |
|
||||
/// | --- | --- | --- |
|
||||
/// | file | s_filepath | filename |
|
||||
/// | graph attachment | | graphname |
|
||||
/// | author | authors | |
|
||||
/// | p-group | pgroup | |
|
||||
/// | project | proposal | |
|
||||
/// | sample | sample | |
|
||||
///
|
||||
/// @param file_df data folder that contains the metadata.
|
||||
///
|
||||
/// @param filename override file path read from s_filepath global string variable.
|
||||
/// if neither is declared, the file name is reset to empty field.
|
||||
///
|
||||
/// @param graphname select this graph window for attaching.
|
||||
/// default: do not change the selection.
|
||||
///
|
||||
static function set_elog_attributes(file_df, [filename, graphname])
|
||||
dfref file_df
|
||||
string filename
|
||||
string graphname
|
||||
|
||||
if (ParamIsDefault(filename))
|
||||
svar /sdfr=file_df /z loaded_file=s_filepath
|
||||
if (svar_Exists(loaded_file))
|
||||
filename = loaded_file
|
||||
else
|
||||
filename = ""
|
||||
endif
|
||||
endif
|
||||
|
||||
if (ParamIsDefault(graphname))
|
||||
graphname = ""
|
||||
endif
|
||||
|
||||
string cmd
|
||||
|
||||
if (exists("PearlElog#set_panel_attributes") == 6)
|
||||
sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"File=%s\")", ParseFilePath(0, filename, ":", 1, 0)
|
||||
execute /Q/Z cmd
|
||||
if ((strlen(graphname) > 0) && (WinType(graphname) == 1))
|
||||
sprintf cmd, "PearlElog#set_panel_graphs(\"\", \"%s\")", graphname
|
||||
execute /Q/Z cmd
|
||||
endif
|
||||
svar /sdfr=file_df /z authors
|
||||
if (svar_Exists(authors))
|
||||
if (strlen(authors)>=1)
|
||||
sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"author=%s\")", authors
|
||||
execute /Q/Z cmd
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z pgroup
|
||||
if (svar_Exists(pgroup))
|
||||
if (strlen(pgroup)>=1)
|
||||
sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"p-group=%s\")", pgroup
|
||||
execute /Q/Z cmd
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z proposal
|
||||
if (svar_Exists(proposal))
|
||||
if (strlen(proposal)>=1)
|
||||
sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"project=%s\")", proposal
|
||||
execute /Q/Z cmd
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z proposer
|
||||
svar /sdfr=file_df /z sample
|
||||
if (svar_Exists(sample))
|
||||
if (strlen(sample)>=1)
|
||||
sprintf cmd, "PearlElog#set_panel_attributes(\"\", \"sample=%s\")", sample
|
||||
execute /Q/Z cmd
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
end
|
||||
|
||||
// ====== preview ======
|
||||
|
||||
static function preview_file(filename)
|
||||
@@ -1475,6 +1394,12 @@ end
|
||||
|
||||
// ====== panel ======
|
||||
|
||||
/// macro to create the data explorer panel
|
||||
///
|
||||
/// note: after editing the panel using the graphical editor,
|
||||
/// remove all automatically generated assignments from the lb_contents listbox.
|
||||
/// only the pos, size and keySelectCol properties should remain.
|
||||
///
|
||||
Window PearlDataExplorer() : Panel
|
||||
PauseUpdate; Silent 1 // building window...
|
||||
NewPanel /K=1 /W=(510,45,1190,539) as "PEARL Data Explorer"
|
||||
@@ -1486,6 +1411,7 @@ Window PearlDataExplorer() : Panel
|
||||
TitleBox tb_filepath,variable= root:packages:pearl_explorer:s_short_filepath,fixedSize=1
|
||||
Button b_browse_filepath,pos={303.00,24.00},size={20.00,20.00},proc=PearlDataExplorer#bp_browse_filepath,title="..."
|
||||
Button b_browse_filepath,fColor=(65280,48896,32768)
|
||||
Button b_browse_filepath,help={"select the file system folder that contains the data files"}
|
||||
GroupBox gb_prefs,pos={8.00,351.00},size={65.00,131.00},title="prefs"
|
||||
GroupBox gb_prefs,help={"explorer package preferences"}
|
||||
Button b_save_prefs,pos={21.00,394.00},size={38.00,17.00},proc=PearlDataExplorer#bp_save_prefs,title="save"
|
||||
@@ -1500,18 +1426,27 @@ Window PearlDataExplorer() : Panel
|
||||
ListBox lb_files,selWave=root:packages:pearl_explorer:wSelectedFiles,mode= 4
|
||||
Button b_update_filelist,pos={246.00,315.00},size={76.00,22.00},proc=PearlDataExplorer#bp_update_filelist,title="update list"
|
||||
Button b_update_filelist,fColor=(65280,48896,32768)
|
||||
CheckBox cb_file_preview,pos={78.00,318.00},size={59.00,14.00},title="preview"
|
||||
Button b_update_filelist,help={"reload the file list"}
|
||||
CheckBox cb_file_preview,pos={94.00,375.00},size={59.00,14.00},title="preview"
|
||||
CheckBox cb_file_preview,help={"enable/disable automatic preview window when selecting a data file"}
|
||||
CheckBox cb_file_preview,value= 0
|
||||
CheckBox cb_file_elog,pos={94.00,395.00},size={82.00,14.00},title="ELOG"
|
||||
CheckBox cb_file_elog,help={"enable/disable sending metadata to ELOG panel when selecting a data file (does not submit to ELOG)"}
|
||||
CheckBox cb_file_elog,value= 0
|
||||
Button b_attr_notebook,pos={94.00,415.00},size={64.00,22.00},disable=2,proc=PearlDataExplorer#bp_attr_notebook,title="notebook"
|
||||
Button b_attr_notebook,help={"show a summary of attributes in a notebook window"}
|
||||
Button b_attr_notebook,fColor=(65280,48896,32768)
|
||||
Button b_file_prev,pos={20.00,314.00},size={22.00,22.00},proc=PearlDataExplorer#bp_file_prev,title="\\W646"
|
||||
Button b_file_prev,help={"previous file"},fColor=(65280,48896,32768)
|
||||
Button b_file_prev,fColor=(65280,48896,32768)
|
||||
Button b_file_prev,help={"select previous file from list"}
|
||||
Button b_file_next,pos={44.00,314.00},size={22.00,22.00},proc=PearlDataExplorer#bp_file_next,title="\\W649"
|
||||
Button b_file_next,help={"next file"},fColor=(65280,48896,32768)
|
||||
Button b_file_next,fColor=(65280,48896,32768)
|
||||
Button b_file_next,help={"select next file from list"}
|
||||
Button b_goto_dataset,pos={355.00,315.00},size={64.00,22.00},disable=2,proc=PearlDataExplorer#bp_goto_dataset,title="goto DF"
|
||||
Button b_goto_dataset,help={"change the current data folder ot where the selected dataset could be located"}
|
||||
Button b_goto_dataset,help={"change the current data folder to find the selected dataset (if loaded)"}
|
||||
Button b_goto_dataset,fColor=(65280,48896,32768)
|
||||
Button b_display_dataset,pos={423.00,315.00},size={64.00,22.00},disable=2,proc=PearlDataExplorer#bp_display_dataset,title="display"
|
||||
Button b_display_dataset,help={"display the selected dataset in its own window"}
|
||||
Button b_display_dataset,help={"display the selected dataset"}
|
||||
Button b_display_dataset,fColor=(65280,48896,32768)
|
||||
Button b_load_complete,pos={355.00,451.00},size={92.00,22.00},disable=2,proc=PearlDataExplorer#bp_load_options,title="all data"
|
||||
Button b_load_complete,help={"load all datasets of the selected file."}
|
||||
@@ -1520,35 +1455,29 @@ Window PearlDataExplorer() : Panel
|
||||
TitleBox tb_selected_file,pos={360.00,28.00},size={309.00,22.00},frame=0
|
||||
TitleBox tb_selected_file,variable= root:packages:pearl_explorer:s_selected_file,fixedSize=1
|
||||
GroupBox gb_contents,pos={346.00,55.00},size={327.00,294.00},title="datasets"
|
||||
Button b_attr_notebook,pos={97.00,375.00},size={64.00,22.00},disable=2,proc=PearlDataExplorer#bp_attr_notebook,title="notebook"
|
||||
Button b_attr_notebook,help={"show a summary of attributes in a notebook window"}
|
||||
Button b_attr_notebook,fColor=(65280,48896,32768)
|
||||
ListBox lb_contents,pos={355.00,84.00},size={305.00,222.00}
|
||||
ListBox lb_contents,keySelectCol= 1
|
||||
GroupBox gb_selected_file,pos={346.00,4.00},size={328.00,48.00},title="selected file"
|
||||
Button b_load_region,pos={355.00,426.00},size={92.00,22.00},disable=2,proc=PearlDataExplorer#bp_load_options,title="region"
|
||||
Button b_load_region,help={"load the selected region"}
|
||||
Button b_load_region,help={"load significant datasets and metadata from the selected region"}
|
||||
Button b_load_region,userdata= "mode:load_region;",fColor=(65280,48896,32768)
|
||||
PopupMenu popup_reduction,pos={366.00,391.00},size={200.00,17.00},bodyWidth=200,proc=PearlDataExplorer#pmp_reduction_func
|
||||
PopupMenu popup_reduction,help={"data reduction of 3d ScientaImage. note: the list may contain unsuitable functions. check the code or documentation!"}
|
||||
PopupMenu popup_reduction,help={"data reduction function for 3d ScientaImage. note: the list may contain unsuitable functions. check the code or documentation!"}
|
||||
PopupMenu popup_reduction,mode=1,popvalue="None",value= #"PearlDataExplorer#pm_reduction_values()"
|
||||
GroupBox group_import,pos={346.00,351.00},size={326.00,131.00},title="import"
|
||||
Button b_load_scan,pos={450.00,426.00},size={94.00,22.00},disable=2,proc=PearlDataExplorer#bp_load_options,title="scan"
|
||||
Button b_load_scan,help={"load the selected scan"},userdata= "mode:load_scan;"
|
||||
Button b_load_scan,help={"load significant datasets and metadata from the selected scan"},userdata= "mode:load_scan;"
|
||||
Button b_load_scan,fColor=(65280,48896,32768)
|
||||
Button b_load_diags,pos={450.00,451.00},size={94.00,22.00},disable=2,proc=PearlDataExplorer#bp_load_options,title="diagnostics"
|
||||
Button b_load_diags,help={"load diagnostics of selected scans"},userdata= "mode:load_diags;"
|
||||
Button b_load_diags,fColor=(65280,48896,32768)
|
||||
Button b_load_diags,help={"load diagnostic datasets of the selected scans"}
|
||||
Button b_load_diags,userdata= "mode:load_diags;",fColor=(65280,48896,32768)
|
||||
Button b_load_dataset,pos={547.00,426.00},size={101.00,22.00},disable=2,proc=PearlDataExplorer#bp_load_options,title="dataset"
|
||||
Button b_load_dataset,help={"load the selected datasets"}
|
||||
Button b_load_dataset,help={"load the selected dataset(s) and significant metadata"}
|
||||
Button b_load_dataset,userdata= "mode:load_dataset;",fColor=(65280,48896,32768)
|
||||
Button b_reduction_params,pos={571.00,390.00},size={71.00,19.00},disable=2,proc=PearlDataExplorer#bp_reduction_params,title="set params"
|
||||
Button b_reduction_params,help={"set data reduction parameters"}
|
||||
Button b_reduction_params,fColor=(65280,48896,32768)
|
||||
GroupBox g_fileinfo,pos={85.00,351.00},size={251.00,131.00},title="file info"
|
||||
Button b_elog,pos={97.00,401.00},size={64.00,22.00},disable=2,proc=PearlDataExplorer#bp_elog,title="ELOG"
|
||||
Button b_elog,help={"send file metadata to ELOG panel (does not submit to ELOG)"}
|
||||
Button b_elog,fColor=(65280,48896,32768)
|
||||
ToolsGrid grid=(0,28.35,5)
|
||||
EndMacro
|
||||
|
||||
@@ -1591,8 +1520,6 @@ static function update_controls()
|
||||
dis = file_selected && scan_selected ? 0 : 2
|
||||
Button b_attr_notebook win=PearlDataExplorer,disable=dis
|
||||
|
||||
dis = file_selected && (strlen(WinList("*ElogPanel*", ";", "WIN:64")) > 1) ? 0 : 2
|
||||
Button b_elog win=PearlDataExplorer,disable=dis
|
||||
dis = scan_selected ? 0 : 2
|
||||
Button b_load_scan win=PearlDataExplorer,disable=dis
|
||||
dis = region_selected ? 0 : 2
|
||||
@@ -1782,24 +1709,34 @@ End
|
||||
///
|
||||
/// - load metadata
|
||||
/// - load preview if requested
|
||||
/// - send to elog panel if requested
|
||||
///
|
||||
/// @param file name of selected file
|
||||
///
|
||||
/// @param do_preview enable/disable loading of preview data
|
||||
/// non-zero: load preview,
|
||||
/// zero: don't load preview
|
||||
///
|
||||
static function selected_file(file, do_preview)
|
||||
/// @param do_elog enable/disable sending metadata to elog panel
|
||||
/// non-zero: send,
|
||||
/// zero: don't send
|
||||
///
|
||||
static function selected_file(file, do_preview, do_elog)
|
||||
string file
|
||||
variable do_preview
|
||||
variable do_elog
|
||||
|
||||
dfref save_df = GetDataFolderDFR()
|
||||
setdatafolder $package_path
|
||||
svar s_selected_file
|
||||
s_selected_file = file
|
||||
get_file_info(file)
|
||||
if (do_preview)
|
||||
variable fi = get_file_info(file)
|
||||
if (fi == 0 && do_preview != 0)
|
||||
preview_file(file)
|
||||
endif
|
||||
if (fi == 0 && do_elog != 0)
|
||||
send_to_elog()
|
||||
endif
|
||||
update_controls()
|
||||
|
||||
setdatafolder save_df
|
||||
@@ -1825,7 +1762,10 @@ static function bp_file_next(ba) : ButtonControl
|
||||
if (v_value >= 0)
|
||||
variable ifile = v_value
|
||||
ControlInfo /W=PearlDataExplorer cb_file_preview
|
||||
selected_file(wtFiles[ifile], v_value)
|
||||
variable do_preview = v_value
|
||||
ControlInfo /W=PearlDataExplorer cb_file_elog
|
||||
variable do_elog = v_value
|
||||
selected_file(wtFiles[ifile], do_preview, do_elog)
|
||||
endif
|
||||
update_controls()
|
||||
break
|
||||
@@ -1856,7 +1796,10 @@ static function bp_file_prev(ba) : ButtonControl
|
||||
if (v_value >= 0)
|
||||
variable ifile = v_value
|
||||
ControlInfo /W=PearlDataExplorer cb_file_preview
|
||||
selected_file(wtFiles[ifile], v_value)
|
||||
variable do_preview = v_value
|
||||
ControlInfo /W=PearlDataExplorer cb_file_elog
|
||||
variable do_elog = v_value
|
||||
selected_file(wtFiles[ifile], do_preview, do_elog)
|
||||
endif
|
||||
update_controls()
|
||||
break
|
||||
@@ -1887,9 +1830,12 @@ static function lbp_filelist(lba) : ListBoxControl
|
||||
if (selWave[row])
|
||||
if (sum(wSelectedFiles) == 1)
|
||||
ControlInfo /W=PearlDataExplorer cb_file_preview
|
||||
selected_file(listWave[row], v_value)
|
||||
variable do_preview = v_value
|
||||
ControlInfo /W=PearlDataExplorer cb_file_elog
|
||||
variable do_elog = v_value
|
||||
selected_file(listWave[row], do_preview, do_elog)
|
||||
else
|
||||
selected_file(listWave[row], 0)
|
||||
selected_file(listWave[row], 0, 0)
|
||||
endif
|
||||
endif
|
||||
update_controls()
|
||||
@@ -2285,6 +2231,24 @@ static function bp_display_dataset(ba) : ButtonControl
|
||||
return 0
|
||||
End
|
||||
|
||||
|
||||
/// *******
|
||||
static function /s get_default_elog_module()
|
||||
string modules = "PearlSciLog;PearlElog"
|
||||
|
||||
variable imod
|
||||
variable nmod = ItemsInList(modules, ";")
|
||||
string smod
|
||||
for (imod = 0; imod < nmod; imod += 1)
|
||||
smod = StringFromList(imod, modules, ";")
|
||||
if (exists(smod + "#set_panel_attributes") == 6)
|
||||
return smod
|
||||
endif
|
||||
endfor
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
/// send file metadata to the ELOG panel
|
||||
///
|
||||
/// metadate is looked up in the following locations:
|
||||
@@ -2292,6 +2256,9 @@ End
|
||||
/// 2. file info folder inside package folder
|
||||
/// 3. package folder if it contains preview data from the selected file (???)
|
||||
///
|
||||
/// the data is sent to the first ElogPanel in the window list.
|
||||
/// call open_pearl_elog() to ensure a new default panel.
|
||||
///
|
||||
static function send_to_elog()
|
||||
dfref save_df = GetDataFolderDFR()
|
||||
|
||||
@@ -2338,27 +2305,123 @@ static function send_to_elog()
|
||||
graphname = ""
|
||||
endif
|
||||
|
||||
string module = get_default_elog_module()
|
||||
funcref PearlDataExplorer_proto_get_panel_name f_get_panel_name = $(module + "#get_default_panel_name")
|
||||
string panel = f_get_panel_name()
|
||||
|
||||
if (result == 0)
|
||||
set_elog_attributes(data_df, filename=s_selected_file, graphname=graphname)
|
||||
string windowname
|
||||
windowname = StringFromList(0, WinList("*ElogPanel*", ";", "WIN:64"), ";")
|
||||
DoWindow /F $windowname
|
||||
set_elog_attributes(module, panel, data_df, filename=s_selected_file, graphname=graphname)
|
||||
endif
|
||||
|
||||
setdatafolder save_df
|
||||
end
|
||||
|
||||
static function bp_elog(ba) : ButtonControl
|
||||
STRUCT WMButtonAction &ba
|
||||
function /s PearlDataExplorer_proto_get_panel_name()
|
||||
return ""
|
||||
end
|
||||
|
||||
switch( ba.eventCode )
|
||||
case 2: // mouse up
|
||||
send_to_elog()
|
||||
break
|
||||
case -1: // control being killed
|
||||
break
|
||||
endswitch
|
||||
function /s PearlDataExplorer_proto_set_panel_attributes(windowname, attributes, [clear])
|
||||
string windowname
|
||||
string attributes
|
||||
variable clear
|
||||
return ""
|
||||
end
|
||||
|
||||
return 0
|
||||
End
|
||||
function /s PearlDataExplorer_proto_set_panel_graphs(windowname, graphs)
|
||||
string windowname
|
||||
string graphs
|
||||
return ""
|
||||
end
|
||||
|
||||
/// send general metadata to ELOG panel - if available
|
||||
///
|
||||
/// the function works with any electronic logbook that has the same interfaces as pearl-elog.ipf.
|
||||
/// the set_panel_attributes and set_panel_graphs functions are required.
|
||||
///
|
||||
/// the following metatdata are sent.
|
||||
/// they must be present as strings in the specified data folder:
|
||||
///
|
||||
/// | ELOG parameter | global string | function argument |
|
||||
/// | --- | --- | --- |
|
||||
/// | file | s_filepath | filename |
|
||||
/// | graph attachment | | graphname |
|
||||
/// | author | authors | |
|
||||
/// | p-group | pgroup | |
|
||||
/// | project | proposal | |
|
||||
/// | sample | sample | |
|
||||
///
|
||||
/// @param elog_module Igor module name of the electronic logbook, PearlElog or PearlSciLog.
|
||||
///
|
||||
/// @param panel_name Window name of the logbook panel
|
||||
///
|
||||
/// @param file_df data folder that contains the metadata.
|
||||
///
|
||||
/// @param filename override file path read from s_filepath global string variable.
|
||||
/// if neither is declared, the file name is reset to empty field.
|
||||
///
|
||||
/// @param graphname select this graph window for attaching.
|
||||
/// default: do not change the selection.
|
||||
///
|
||||
static function set_elog_attributes(elog_module, panel_name, file_df, [filename, graphname])
|
||||
string elog_module
|
||||
string panel_name
|
||||
dfref file_df
|
||||
string filename
|
||||
string graphname
|
||||
|
||||
if (ParamIsDefault(filename))
|
||||
svar /sdfr=file_df /z loaded_file=s_filepath
|
||||
if (svar_Exists(loaded_file))
|
||||
filename = loaded_file
|
||||
else
|
||||
filename = ""
|
||||
endif
|
||||
endif
|
||||
|
||||
if (ParamIsDefault(graphname))
|
||||
graphname = ""
|
||||
endif
|
||||
|
||||
string cmd
|
||||
string attrib = ""
|
||||
|
||||
if (exists(elog_module + "#set_panel_attributes") == 6)
|
||||
funcref PearlDataExplorer_proto_set_panel_attributes f_set_attributes = $(elog_module + "#set_panel_attributes")
|
||||
funcref PearlDataExplorer_proto_set_panel_graphs f_set_graphs = $(elog_module + "#set_panel_graphs")
|
||||
|
||||
attrib = ReplaceStringByKey("file", attrib, ParseFilePath(0, filename, ":", 1, 0), ":", ";")
|
||||
svar /sdfr=file_df /z authors
|
||||
if (svar_Exists(authors))
|
||||
if (strlen(authors)>=1)
|
||||
attrib = ReplaceStringByKey("author", attrib, authors, ":", ";")
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z pgroup
|
||||
if (svar_Exists(pgroup))
|
||||
if (strlen(pgroup)>=1)
|
||||
attrib = ReplaceStringByKey("p-group", attrib, pgroup, ":", ";")
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z proposal
|
||||
if (svar_Exists(proposal))
|
||||
if (strlen(proposal)>=1)
|
||||
attrib = ReplaceStringByKey("project", attrib, proposal, ":", ";")
|
||||
endif
|
||||
endif
|
||||
svar /sdfr=file_df /z proposer
|
||||
svar /sdfr=file_df /z sample
|
||||
if (svar_Exists(sample))
|
||||
if (strlen(sample)>=1)
|
||||
attrib = ReplaceStringByKey("sample", attrib, sample, ":", ";")
|
||||
endif
|
||||
endif
|
||||
|
||||
if (strlen(attrib)>=3)
|
||||
f_set_attributes(panel_name, attrib)
|
||||
endif
|
||||
|
||||
if ((strlen(graphname) > 0) && (WinType(graphname) == 1))
|
||||
f_set_graphs(panel_name, graphname)
|
||||
endif
|
||||
endif
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma TextEncoding = "UTF-8"
|
||||
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
||||
#pragma version = 1.50
|
||||
#pragma version = 2.2
|
||||
#pragma IgorVersion = 6.36
|
||||
#pragma ModuleName = PearlElog
|
||||
|
||||
// author: matthias.muntwiler@psi.ch
|
||||
// Copyright (c) 2013-20 Paul Scherrer Institut
|
||||
// Copyright (c) 2013-25 Paul Scherrer Institut
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -145,8 +145,7 @@ static function AfterFileOpenHook(refNum,file,pathName,type,creator,kind)
|
||||
Variable refNum,kind
|
||||
String file,pathName,type,creator
|
||||
if( (kind >= 1) && (kind <= 2))
|
||||
init_package(clean=1)
|
||||
load_prefs()
|
||||
clear_package_data()
|
||||
endif
|
||||
return 0
|
||||
end
|
||||
@@ -206,6 +205,30 @@ static function /df get_elog_df(name, category)
|
||||
endif
|
||||
end
|
||||
|
||||
/// delete all package data
|
||||
///
|
||||
/// also kills any ELOG panels
|
||||
///
|
||||
static function clear_package_data()
|
||||
dfref savedf = getdatafolderdfr()
|
||||
dfref df_root = get_elog_df("", kdfRoot)
|
||||
|
||||
if (DataFolderRefStatus(df_root) == 1)
|
||||
string wins = WinList("*ElogPanel*", ";", "WIN:64")
|
||||
variable iwin
|
||||
variable nwin = ItemsInList(wins, ";")
|
||||
string swin
|
||||
for (iwin = 0; iwin < nwin; iwin += 1)
|
||||
swin = StringFromList(iwin, wins, ";")
|
||||
KillWindow /Z $swin
|
||||
endfor
|
||||
KillDataFolder /Z df_root
|
||||
endif
|
||||
|
||||
setdatafolder savedf
|
||||
return 0
|
||||
end
|
||||
|
||||
/// initialize the package data folder.
|
||||
///
|
||||
/// the data folder is initialized with a default, local configuration without any logbooks.
|
||||
@@ -1406,6 +1429,7 @@ static function elog_panel_hook(s)
|
||||
svar /sdfr=df_volatile url
|
||||
url = format_url(logbook)
|
||||
update_attach_items(logbook)
|
||||
update_buttons(s.winName, logbook)
|
||||
endif
|
||||
break
|
||||
case 6: // resize
|
||||
@@ -1427,6 +1451,32 @@ static function elog_panel_hook(s)
|
||||
return hookResult // 0 if nothing done, else 1
|
||||
end
|
||||
|
||||
static function update_buttons(win_name, logbook)
|
||||
string win_name
|
||||
string logbook
|
||||
dfref df = get_elog_df(logbook, kdfVolatile)
|
||||
//string win_name = StringFromList(0, WinList(logbook + "ElogPanel*", ";", "WIN:64"), ";")
|
||||
|
||||
variable logged = 0
|
||||
if (strlen(logbook) > 0)
|
||||
svar /z /sdfr=df g_username=username
|
||||
svar /z /sdfr=df g_password=password
|
||||
if (svar_exists(g_username) && svar_exists(g_password))
|
||||
logged = strlen(g_username) > 0 && strlen(g_password) > 0
|
||||
endif
|
||||
endif
|
||||
|
||||
if (strlen(win_name) > 0)
|
||||
if (logged)
|
||||
Button b_login, win=$win_name, disable=3
|
||||
Button b_logout, win=$win_name, disable=0
|
||||
else
|
||||
Button b_login, win=$win_name, disable=0
|
||||
Button b_logout, win=$win_name, disable=3
|
||||
endif
|
||||
endif
|
||||
end
|
||||
|
||||
static constant kAttachColSel = 0
|
||||
static constant kAttachColTitle = 1
|
||||
static constant kAttachColName = 2
|
||||
@@ -1756,7 +1806,7 @@ end
|
||||
/// @param windowname window name of the ELOG panel
|
||||
/// if empty, use default name "PearlElogPanel"
|
||||
///
|
||||
/// @return list of attributes to in the format <code>"key1=value1;key2=value2"</code>.
|
||||
/// @return list of attributes to in the format `"key1:value1;key2:value2"`.
|
||||
///
|
||||
static function /s get_panel_attributes(windowname)
|
||||
string windowname
|
||||
@@ -1781,11 +1831,11 @@ static function /s get_panel_attributes(windowname)
|
||||
ControlInfo /w=$windowname $control
|
||||
switch(v_flag)
|
||||
case 2: // checkbox
|
||||
attributes = ReplaceNumberByKey(attribute, attributes, v_value, "=", ";")
|
||||
attributes = ReplaceNumberByKey(attribute, attributes, v_value, ":", ";")
|
||||
break
|
||||
case 3: // popupmenu
|
||||
case 5: // setvariable
|
||||
attributes = ReplaceStringByKey(attribute, attributes, s_value, "=", ";")
|
||||
attributes = ReplaceStringByKey(attribute, attributes, s_value, ":", ";")
|
||||
break
|
||||
endswitch
|
||||
endif
|
||||
@@ -1799,7 +1849,7 @@ end
|
||||
/// @param windowname window name of the ELOG panel
|
||||
/// if empty, use default name "PearlElogPanel"
|
||||
///
|
||||
/// @param attributes list of attributes to set (format "key1=value1;key2=value2")
|
||||
/// @param attributes list of attributes to set (format "key1:value1;key2:value2")
|
||||
///
|
||||
/// @param clear what to do if a key is missing in attributes?
|
||||
/// @arg 0 (default) leave the field unchanged
|
||||
@@ -1839,12 +1889,12 @@ static function /s set_panel_attributes(windowname, attributes, [clear])
|
||||
control = StringFromList(ico, controls, ";")
|
||||
attribute = GetUserData(windowname, control, "attribute")
|
||||
if (strlen(attribute))
|
||||
value = StringByKey(attribute, attributes, "=", ";")
|
||||
value = StringByKey(attribute, attributes, ":", ";")
|
||||
if (strlen(value) || clear)
|
||||
ControlInfo /w=$windowname $control
|
||||
switch(v_flag)
|
||||
case 2: // checkbox
|
||||
numval = NumberByKey(attribute, attributes, "=", ";")
|
||||
numval = NumberByKey(attribute, attributes, ":", ";")
|
||||
if ((numtype(numval) != 0) && clear)
|
||||
numval = 0
|
||||
endif
|
||||
@@ -1853,7 +1903,7 @@ static function /s set_panel_attributes(windowname, attributes, [clear])
|
||||
endif
|
||||
break
|
||||
case 3: // popupmenu
|
||||
options_path = persistent_path + StringByKey(attribute, options, "=", ";")
|
||||
options_path = persistent_path + StringByKey(attribute, options, ":", ";")
|
||||
svar values = $options_path
|
||||
numval = WhichListItem(value, values, ";") + 1
|
||||
if (numval >= 1)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#pragma TextEncoding = "UTF-8"
|
||||
#pragma rtGlobals=1 // Use modern global access method.
|
||||
#pragma ModuleName = PearlMenu
|
||||
#pragma version = 1.02
|
||||
#pragma version = 1.03
|
||||
|
||||
// main menu for PEARL data acquisition and analysis packages
|
||||
|
||||
// $Id$
|
||||
// author: matthias.muntwiler@psi.ch
|
||||
// Copyright (c) 2013-14 Paul Scherrer Institut
|
||||
// Copyright (c) 2013-25 Paul Scherrer Institut
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -65,6 +65,8 @@ menu "PEARL"
|
||||
submenu "Services"
|
||||
PearlMenuEnableFunc("pearl_elog") + "Open ELOG Panel", /Q, pearl_elog("")
|
||||
help = {"Open an ELOG panel to send entries to an ELOG logbook"}
|
||||
PearlMenuEnableFunc("pearl_scilog") + "Open SciLog Panel", /Q, pearl_scilog("")
|
||||
help = {"Open a panel to send entries to a SciLog logbook"}
|
||||
end
|
||||
|
||||
submenu "Sample Preparation"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma IgorVersion = 6.2
|
||||
#pragma ModuleName = PearlPmscoImport
|
||||
|
||||
// copyright (c) 2018 Paul Scherrer Institut
|
||||
// copyright (c) 2018-25 Paul Scherrer Institut
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -326,3 +326,30 @@ function /s pmsco_load_xyz(pathname, filename)
|
||||
wave /t at
|
||||
at = unpadstring(at, 32)
|
||||
end
|
||||
|
||||
function pmsco_load_alm(pathname, filename, wname)
|
||||
string pathname
|
||||
string filename
|
||||
string wname
|
||||
|
||||
variable fid
|
||||
if (strlen(pathname) > 0)
|
||||
HDF5OpenFile /P=pathname /R /Z fid as filename
|
||||
else
|
||||
HDF5OpenFile /R /Z fid as filename
|
||||
endif
|
||||
|
||||
if (v_flag == 0)
|
||||
HDF5LoadData /N=$wname /O /Q /Z fid, "alm"
|
||||
HDF5CloseFile fid
|
||||
|
||||
wave alm_r = $(wname + "_r")
|
||||
wave alm_i = $(wname + "_i")
|
||||
make /c /n=(dimsize(alm_r, 0), dimsize(alm_r, 1)) /o $wname
|
||||
wave /c alm = $wname
|
||||
alm = cmplx(alm_r, alm_i)
|
||||
variable lmax = (dimsize(alm, 0) - 1) * 2
|
||||
setscale /i x 0, lmax, "l", alm, alm_r, alm_i
|
||||
setscale /i y -lmax, lmax, "m", alm, alm_r, alm_i
|
||||
endif
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
||||
#pragma IgorVersion = 8.00
|
||||
#pragma ModuleName = PearlPShellImport
|
||||
#pragma version = 2.1
|
||||
#pragma version = 2.2
|
||||
#if IgorVersion() < 9.00
|
||||
#include <HDF5 Browser>
|
||||
#endif
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "pearl-gui-tools"
|
||||
#include "pearl-area-import"
|
||||
|
||||
// copyright (c) 2013-22 Paul Scherrer Institut
|
||||
// copyright (c) 2013-25 Paul Scherrer Institut
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -63,7 +63,7 @@ strconstant kDataDimLabel = "data"
|
||||
strconstant kPreviewDatasets = "ImageEnergyDistribution;ScientaSpectrum;ScientaImage;Counts;SampleCurrent;"
|
||||
|
||||
/// List of datasets that must be loaded to determine the axis scaling of a Scienta image
|
||||
strconstant kScientaScalingDatasets = "LensMode;ScientaChannelBegin;ScientaChannelEnd;ScientaSliceBegin;ScientaSliceEnd;Eph;"
|
||||
strconstant kScientaScalingDatasets = "LensMode;ScientaChannelBegin;ScientaChannelEnd;ScientaSliceBegin;ScientaSliceEnd;Eph;ScientaHighEnergy;ScientaHighThetaX;ScientaLowEnergy;ScientaLowThetaX;"
|
||||
|
||||
/// List of diagnostic datasets that are normally loaded with a scan
|
||||
strconstant kEssentialDiagnostics = "ManipulatorX;ManipulatorY;ManipulatorZ;ManipulatorTheta;ManipulatorTilt;ManipulatorPhi;MonoEnergy;"
|
||||
@@ -2202,6 +2202,10 @@ end
|
||||
/// where the first folder in the list takes precedence.
|
||||
/// it may fall back to more or less reasonable default values if no data is not found.
|
||||
/// @arg `LensMode`
|
||||
/// @arg `ScientaLowEnergy`
|
||||
/// @arg `ScientaHighEnergy`
|
||||
/// @arg `ScientaLowThetaX`
|
||||
/// @arg `ScientaHighThetaX`
|
||||
/// @arg `ScientaChannelBegin`
|
||||
/// @arg `ScientaChannelEnd`
|
||||
/// @arg `ScientaSliceBegin`
|
||||
@@ -2260,6 +2264,10 @@ function ps_detect_scale(data_df, ax, lo, hi, un)
|
||||
ax[%$kDataDimLabel] = "value"
|
||||
|
||||
wave /T /Z LensMode = ps_find_scale_wave("LensMode", data_df, scan_df, attr_df)
|
||||
wave /Z LowEnergy = ps_find_scale_wave("ScientaLowEnergy", data_df, scan_df, attr_df)
|
||||
wave /Z HighEnergy = ps_find_scale_wave("ScientaHighEnergy", data_df, scan_df, attr_df)
|
||||
wave /Z LowThetaX = ps_find_scale_wave("ScientaLowThetaX", data_df, scan_df, attr_df)
|
||||
wave /Z HighThetaX = ps_find_scale_wave("ScientaHighThetaX", data_df, scan_df, attr_df)
|
||||
wave /Z ChannelBegin = ps_find_scale_wave("ScientaChannelBegin", data_df, scan_df, attr_df)
|
||||
wave /Z ChannelEnd = ps_find_scale_wave("ScientaChannelEnd", data_df, scan_df, attr_df)
|
||||
wave /Z SliceBegin = ps_find_scale_wave("ScientaSliceBegin", data_df, scan_df, attr_df)
|
||||
@@ -2268,35 +2276,30 @@ function ps_detect_scale(data_df, ax, lo, hi, un)
|
||||
|
||||
// lens mode can give more detail
|
||||
if (waveexists(LensMode) && (numpnts(LensMode) >= 1))
|
||||
strswitch(LensMode[0])
|
||||
case "Angular45":
|
||||
lo[%$kAngleDimLabel] = -45/2
|
||||
hi[%$kAngleDimLabel] = +45/2
|
||||
un[%$kAngleDimLabel] = "°"
|
||||
ax[%$kAngleDimLabel] = "angle"
|
||||
break
|
||||
case "Angular60":
|
||||
lo[%$kAngleDimLabel] = -60/2
|
||||
hi[%$kAngleDimLabel] = +60/2
|
||||
un[%$kAngleDimLabel] = "°"
|
||||
ax[%$kAngleDimLabel] = "angle"
|
||||
break
|
||||
case "Transmission":
|
||||
un[%$kAngleDimLabel] = "arb."
|
||||
ax[%$kAngleDimLabel] = "offset"
|
||||
break
|
||||
endswitch
|
||||
if (stringmatch(LensMode[0], "*Transmission*"))
|
||||
un[%$kAngleDimLabel] = "mm"
|
||||
ax[%$kAngleDimLabel] = "position"
|
||||
else
|
||||
un[%$kAngleDimLabel] = "°"
|
||||
ax[%$kAngleDimLabel] = "angle"
|
||||
endif
|
||||
endif
|
||||
|
||||
// best option if scales are explicit in separate waves
|
||||
if (waveexists(ChannelBegin) && waveexists(ChannelEnd) && (numpnts(ChannelBegin) >= 1) && (numpnts(ChannelEnd) >= 1))
|
||||
if (waveexists(LowEnergy) && waveexists(HighEnergy) && (numpnts(LowEnergy) >= 1) && (numpnts(HighEnergy) >= 1))
|
||||
lo[%$kEnergyDimLabel] = LowEnergy[0]
|
||||
hi[%$kEnergyDimLabel] = HighEnergy[0]
|
||||
elseif (waveexists(ChannelBegin) && waveexists(ChannelEnd) && (numpnts(ChannelBegin) >= 1) && (numpnts(ChannelEnd) >= 1))
|
||||
lo[%$kEnergyDimLabel] = ChannelBegin[0]
|
||||
hi[%$kEnergyDimLabel] = ChannelEnd[0]
|
||||
elseif (waveexists(ScientaChannels) && (numpnts(ScientaChannels) >= 1))
|
||||
lo[%$kEnergyDimLabel] = ScientaChannels[0]
|
||||
hi[%$kEnergyDimLabel] = ScientaChannels[numpnts(ScientaChannels)-1]
|
||||
endif
|
||||
if (waveexists(SliceBegin) && waveexists(SliceEnd) && (numpnts(SliceBegin) >= 1) && (numpnts(SliceEnd) >= 1))
|
||||
if (waveexists(LowThetaX) && waveexists(HighThetaX) && (numpnts(LowThetaX) >= 1) && (numpnts(HighThetaX) >= 1))
|
||||
lo[%$kAngleDimLabel] = LowThetaX[0]
|
||||
hi[%$kAngleDimLabel] = HighThetaX[0]
|
||||
elseif (waveexists(SliceBegin) && waveexists(SliceEnd) && (numpnts(SliceBegin) >= 1) && (numpnts(SliceEnd) >= 1))
|
||||
lo[%$kAngleDimLabel] = SliceBegin[0]
|
||||
hi[%$kAngleDimLabel] = SliceEnd[0]
|
||||
endif
|
||||
|
||||
1739
pearl/pearl-scilog.ipf
Normal file
1739
pearl/pearl-scilog.ipf
Normal file
File diff suppressed because it is too large
Load Diff
331
pearl/scilog-ingest.py
Normal file
331
pearl/scilog-ingest.py
Normal file
@@ -0,0 +1,331 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
#
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = [
|
||||
# "pillow",
|
||||
# "scilog",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
|
||||
from scilog import SciLog, LogbookMessage
|
||||
|
||||
|
||||
def get_image_size(file_path):
|
||||
"""
|
||||
|
||||
Args:
|
||||
file_path:
|
||||
|
||||
Returns:
|
||||
|
||||
Raises:
|
||||
OSError: If the file does not exist.
|
||||
PIL.Image.UnidentifiedImageError: If the image is not recognized.
|
||||
"""
|
||||
|
||||
with Image.open(file_path) as img:
|
||||
return img.size
|
||||
|
||||
|
||||
re_single_nbsp = re.compile(r"(?<!;) (?!&)")
|
||||
re_rep_pre_spc = re.compile(r" {2,}")
|
||||
|
||||
|
||||
def replace_pre_spaces(s: str) -> str:
|
||||
"""
|
||||
Replace multiple spaces with the corresponding number of entities.
|
||||
|
||||
Args:
|
||||
s: string
|
||||
|
||||
Returns: string
|
||||
|
||||
"""
|
||||
def nbsp(mo):
|
||||
return " " * len(mo.group())
|
||||
|
||||
return re_rep_pre_spc.sub(nbsp, s)
|
||||
|
||||
|
||||
class ScilogIngestor:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._author: str = ""
|
||||
self._pgroup: str = ""
|
||||
self._logbook: str = ""
|
||||
self._message: str = ""
|
||||
self._tags: List[str] = []
|
||||
self._attachments: List[str] = []
|
||||
self._url: str = ""
|
||||
self._user: str = ""
|
||||
self._password: str = ""
|
||||
self._location: str = ""
|
||||
self._scilog: Optional[SciLog] = None
|
||||
|
||||
def prepare(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _adjust_image_size(file_info):
|
||||
try:
|
||||
filepath = file_info['filepath']
|
||||
except KeyError:
|
||||
return
|
||||
try:
|
||||
w, h = get_image_size(filepath)
|
||||
except (OSError, UnidentifiedImageError):
|
||||
return
|
||||
else:
|
||||
# limit image size
|
||||
while w > 400:
|
||||
w //= 2
|
||||
file_info['style'] = {'width': f'{w}px', 'height': ''}
|
||||
|
||||
def convert_plain(self, mesg: str) -> str:
|
||||
"""
|
||||
convert plain-text entry to HTML
|
||||
|
||||
- Convert plain text message to HTML paragraph with <tt> formatting.
|
||||
- Replace line feeds by <br> tags.
|
||||
- Replace tabs and spaces.
|
||||
|
||||
Args:
|
||||
mesg:
|
||||
|
||||
Returns: string
|
||||
"""
|
||||
|
||||
mesg = mesg.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
mesg = mesg.replace("\n", "<br>")
|
||||
mesg = mesg.expandtabs(8)
|
||||
mesg = replace_pre_spaces(mesg)
|
||||
mesg = "<p><tt>" + mesg + "</tt></p>"
|
||||
|
||||
return mesg
|
||||
|
||||
@staticmethod
|
||||
def is_pgroup(value: str) -> str:
|
||||
value = value.lower()
|
||||
if len(value) == 6 and value[0] in {"e", "p"} and int(value[1:]):
|
||||
return "p" + value[1:]
|
||||
else:
|
||||
return ""
|
||||
|
||||
@property
|
||||
def scilog(self) -> SciLog:
|
||||
"""
|
||||
Return a SciLog instance.
|
||||
|
||||
Returns: SciLog
|
||||
"""
|
||||
|
||||
if self._scilog is None:
|
||||
options = {"username": self._user,
|
||||
"password": self._password}
|
||||
url = self._url
|
||||
self._scilog = SciLog(url, options=options)
|
||||
|
||||
return self._scilog
|
||||
|
||||
def ingest_message(self):
|
||||
"""
|
||||
ingest message into scilog
|
||||
|
||||
this method ingests the current message into scilog.
|
||||
|
||||
"""
|
||||
|
||||
log = self.scilog
|
||||
|
||||
try:
|
||||
logbook_name = self._logbook
|
||||
except KeyError:
|
||||
lb_filter = {"ownerGroup": self._pgroup, "deleted": False}
|
||||
else:
|
||||
lb_filter = {"ownerGroup": self._pgroup, "name": logbook_name, "deleted": False}
|
||||
|
||||
logbooks = log.get_logbooks(where=lb_filter, limit=10)
|
||||
try:
|
||||
logbook = logbooks[0]
|
||||
except IndexError:
|
||||
raise ValueError(f"no logbook found for {lb_filter}")
|
||||
else:
|
||||
if len(logbooks) > 1:
|
||||
raise ValueError(f"multiple logbooks found for {lb_filter}")
|
||||
else:
|
||||
log.select_logbook(logbook)
|
||||
|
||||
msg = LogbookMessage()
|
||||
if self._message[0] == '<':
|
||||
msg.add_text(self._message)
|
||||
else:
|
||||
for line in self._message.split('\n'):
|
||||
msg.add_text(line)
|
||||
for att in self._attachments:
|
||||
msg.add_file(att)
|
||||
file_info = msg._content.files[-1]
|
||||
self._adjust_image_size(file_info)
|
||||
if self._tags:
|
||||
msg.add_tag(self._tags)
|
||||
log.send_logbook_message(msg)
|
||||
|
||||
def load_attributes_from_file(self, attributes_file):
|
||||
"""
|
||||
Load attributes, tags and attachment paths from a file.
|
||||
|
||||
Each line of the file declares a key:value pair.
|
||||
The keys are:
|
||||
- author: (required) Value is the e-mail address of the author.
|
||||
- pgroup: (required) Value is the p-group of the logbook.
|
||||
- logbook: (required) Value is the name of the logbook.
|
||||
- location: (not used) Value is the name of the location. Currently not used.
|
||||
- tag: (optional) Value is a tag to be added to the snippet. Can occur multiple times.
|
||||
Duplicates are ignored. Spaces and commas are removed.
|
||||
- attachment: (optional) Value is the path of an attachment. Can occur multiple times.
|
||||
Duplicates are ignored.
|
||||
|
||||
:param attributes_file:
|
||||
:return:
|
||||
"""
|
||||
|
||||
unique_tags = set()
|
||||
unique_attachments = set()
|
||||
|
||||
with open(attributes_file, "rt", encoding="utf8") as f:
|
||||
for line in f.readlines():
|
||||
try:
|
||||
key, val = line.split(":", 1)
|
||||
key = key.strip()
|
||||
val = val.strip()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if key == "tag":
|
||||
val = val.replace(" ", "")
|
||||
val = val.replace(",", "")
|
||||
if val not in unique_tags:
|
||||
self._tags.append(val)
|
||||
unique_tags.add(val)
|
||||
elif key == "attachment":
|
||||
if val not in unique_attachments:
|
||||
self._attachments.append(val)
|
||||
unique_attachments.add(val)
|
||||
elif key == "author":
|
||||
self._author = val
|
||||
elif key == "pgroup":
|
||||
self._pgroup = val
|
||||
elif key == "location":
|
||||
self._location = val
|
||||
elif key == "logbook":
|
||||
self._logbook = val
|
||||
|
||||
def load_message_from_file(self, message_file):
|
||||
"""
|
||||
Load message from file
|
||||
|
||||
If the file starts with a `<`, the content is assumed to be HTML.
|
||||
Otherwise, it is assumed to be plain text and will be tagged and escaped.
|
||||
|
||||
:param message_file:
|
||||
:return:
|
||||
"""
|
||||
|
||||
with open(message_file, "rt", encoding="utf8") as f:
|
||||
self._message = "\n".join(f.readlines())
|
||||
|
||||
def load_credentials_from_file(self, credentials_file):
|
||||
"""
|
||||
Load the credentials from a file.
|
||||
|
||||
The file must contain three lines, each in the form key:value.
|
||||
The keys are `url`, `user`, and `password`.
|
||||
The URL must start with `https://` and end with `/api/v1`.
|
||||
|
||||
:param credentials_file: path of the credentials file.
|
||||
If None, it is read from `.scilog.cred` the home directory.
|
||||
Make sure to protect the credentials from unauthorized access!
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if credentials_file is None:
|
||||
credentials_file = Path.home() / ".scilog.cred"
|
||||
if not Path(credentials_file).exists():
|
||||
raise ValueError("Missing credentials")
|
||||
with open(credentials_file, "rt", encoding="utf8") as f:
|
||||
for line in f.readlines():
|
||||
try:
|
||||
key, val = line.split(":", 1)
|
||||
key = key.strip()
|
||||
val = val.strip()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if key == "user":
|
||||
self._user = val
|
||||
elif key == "password":
|
||||
self._password = val
|
||||
elif key == "url":
|
||||
self._url = val
|
||||
|
||||
def validate(self):
|
||||
"""
|
||||
Perform a number of basic validity checks on the attributes.
|
||||
|
||||
:return:
|
||||
:raise ValueError if a problem is found.
|
||||
"""
|
||||
|
||||
if not re.match(r"[^@]+@[^@]+\.[^@]+", self._author):
|
||||
raise ValueError("Invalid email address.")
|
||||
if not self._user or not self._password:
|
||||
raise ValueError("Invalid credentials.")
|
||||
if not self.is_pgroup(self._pgroup):
|
||||
raise ValueError("Invalid pgroup.")
|
||||
if not self._logbook:
|
||||
raise ValueError("Empty logbook name.")
|
||||
if not self._message:
|
||||
raise ValueError("Empty message.")
|
||||
if not re.match(r"https://[a-zA-Z0-9]+\.[a-zA-Z0-9.]+(:[0-9]+)?/api/v1", self._url):
|
||||
raise ValueError("Invalid URL.")
|
||||
|
||||
def run(self):
|
||||
self.validate()
|
||||
self.ingest_message()
|
||||
|
||||
|
||||
def main(attributes_file, message_file, credentials_file):
|
||||
ingestor = ScilogIngestor()
|
||||
ingestor.load_attributes_from_file(attributes_file)
|
||||
ingestor.load_message_from_file(message_file)
|
||||
ingestor.load_credentials_from_file(credentials_file)
|
||||
ingestor.run()
|
||||
print("Message successfully transmitted")
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Simple file interface to ingest an entry into a SciLog logbook",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument("-c", "--credentials", default=None, help="credentials file, UTF-8 encoded")
|
||||
parser.add_argument("attributes", help="attributes file, UTF-8 encoded")
|
||||
parser.add_argument("message", help="message file, UTF-8 encoded")
|
||||
|
||||
clargs = parser.parse_args()
|
||||
|
||||
return clargs
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
clargs = parse_args()
|
||||
main(clargs.attributes, clargs.message, clargs.credentials)
|
||||
Reference in New Issue
Block a user