From 7c2374de629ce6bba7d77c0138839efa604b361f Mon Sep 17 00:00:00 2001 From: mromanie Date: Mon, 28 Apr 2025 21:32:30 +0200 Subject: [PATCH] Add files via upload --- eso_edps/Dockerfile | 43 +++++++++ eso_edps/Dockerfile.fedora | 43 +++++++++ eso_edps/Dockerfile.ubuntu | 39 ++++++++ eso_edps/README.md | 12 +++ eso_edps/application.properties | 161 ++++++++++++++++++++++++++++++++ eso_edps/edps-gui.py | 104 +++++++++++++++++++++ eso_edps/eso-logo.jpg | Bin 0 -> 17558 bytes eso_edps/logging.yaml | 26 ++++++ eso_edps/pdf_handler.py | 16 ++++ eso_edps/requirements.txt | 4 + 10 files changed, 448 insertions(+) create mode 100644 eso_edps/Dockerfile create mode 100644 eso_edps/Dockerfile.fedora create mode 100644 eso_edps/Dockerfile.ubuntu create mode 100644 eso_edps/README.md create mode 100644 eso_edps/application.properties create mode 100644 eso_edps/edps-gui.py create mode 100644 eso_edps/eso-logo.jpg create mode 100644 eso_edps/logging.yaml create mode 100644 eso_edps/pdf_handler.py create mode 100644 eso_edps/requirements.txt diff --git a/eso_edps/Dockerfile b/eso_edps/Dockerfile new file mode 100644 index 00000000..fe089ab3 --- /dev/null +++ b/eso_edps/Dockerfile @@ -0,0 +1,43 @@ +FROM fedora:40 + +ARG PIPE=fors + +RUN dnf install -y dnf-plugins-core && \ + dnf update -y && \ + dnf config-manager -y --add-repo=https://ftp.eso.org/pub/dfs/pipelines/repositories/stable/fedora/esorepo.repo && \ + dnf clean all + +# skip networkx extra-dependencies to reduce image size +RUN dnf install -y --setopt=install_weak_deps=False python3-networkx which gzip curl graphviz && \ + dnf clean all + +# ESO pipeline (incl. all dependencies like ADARI, EDPS, Python3, ...) +RUN dnf install -y esopipe-${PIPE}-wkf esopipe-${PIPE}-datastatic && \ + dnf clean all + +ENV XDG_RUNTIME_DIR=/var/run/adari +RUN mkdir $XDG_RUNTIME_DIR && chmod 777 $XDG_RUNTIME_DIR + +# Create user +RUN useradd -m -u 1000 user +USER user + +WORKDIR /home/user +RUN mkdir .edps EDPS_data + +COPY --chown=user ./requirements.txt requirements.txt +COPY --chown=user ./edps-gui.py edps-gui.py +COPY --chown=user ./pdf_handler.py pdf_handler.py +COPY --chown=user ./eso-logo.jpg eso-logo.jpg +COPY --chown=user ./application.properties .edps/application.properties +COPY --chown=user ./logging.yaml .edps/logging.yaml + +RUN python3 -m venv venv && . venv/bin/activate && \ + pip install --no-cache-dir --upgrade -r requirements.txt + +RUN curl https://ftp.eso.org/pub/dfs/pipelines/instruments/fors/fors-demo-reflex-1.0.tar.gz | tar -zxf - + +ENV VIRTUAL_ENV=/home/user/venv +ENV PATH=$VIRTUAL_ENV/bin:$PATH +ENV EDPSGUI_INPUT_DIR=/home/user/${PIPE} +CMD ["panel", "serve", "edps-gui.py", "--plugins", "pdf_handler", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*"] diff --git a/eso_edps/Dockerfile.fedora b/eso_edps/Dockerfile.fedora new file mode 100644 index 00000000..fe089ab3 --- /dev/null +++ b/eso_edps/Dockerfile.fedora @@ -0,0 +1,43 @@ +FROM fedora:40 + +ARG PIPE=fors + +RUN dnf install -y dnf-plugins-core && \ + dnf update -y && \ + dnf config-manager -y --add-repo=https://ftp.eso.org/pub/dfs/pipelines/repositories/stable/fedora/esorepo.repo && \ + dnf clean all + +# skip networkx extra-dependencies to reduce image size +RUN dnf install -y --setopt=install_weak_deps=False python3-networkx which gzip curl graphviz && \ + dnf clean all + +# ESO pipeline (incl. all dependencies like ADARI, EDPS, Python3, ...) +RUN dnf install -y esopipe-${PIPE}-wkf esopipe-${PIPE}-datastatic && \ + dnf clean all + +ENV XDG_RUNTIME_DIR=/var/run/adari +RUN mkdir $XDG_RUNTIME_DIR && chmod 777 $XDG_RUNTIME_DIR + +# Create user +RUN useradd -m -u 1000 user +USER user + +WORKDIR /home/user +RUN mkdir .edps EDPS_data + +COPY --chown=user ./requirements.txt requirements.txt +COPY --chown=user ./edps-gui.py edps-gui.py +COPY --chown=user ./pdf_handler.py pdf_handler.py +COPY --chown=user ./eso-logo.jpg eso-logo.jpg +COPY --chown=user ./application.properties .edps/application.properties +COPY --chown=user ./logging.yaml .edps/logging.yaml + +RUN python3 -m venv venv && . venv/bin/activate && \ + pip install --no-cache-dir --upgrade -r requirements.txt + +RUN curl https://ftp.eso.org/pub/dfs/pipelines/instruments/fors/fors-demo-reflex-1.0.tar.gz | tar -zxf - + +ENV VIRTUAL_ENV=/home/user/venv +ENV PATH=$VIRTUAL_ENV/bin:$PATH +ENV EDPSGUI_INPUT_DIR=/home/user/${PIPE} +CMD ["panel", "serve", "edps-gui.py", "--plugins", "pdf_handler", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*"] diff --git a/eso_edps/Dockerfile.ubuntu b/eso_edps/Dockerfile.ubuntu new file mode 100644 index 00000000..1fa96303 --- /dev/null +++ b/eso_edps/Dockerfile.ubuntu @@ -0,0 +1,39 @@ +FROM ubuntu:latest + +RUN apt-get update && \ + apt-get install -y build-essential procps curl file git && \ + apt-get clean + +ENV XDG_RUNTIME_DIR=/var/run/adari +RUN mkdir $XDG_RUNTIME_DIR && chmod 777 $XDG_RUNTIME_DIR + +# Create user +RUN useradd -m -d /home/linuxbrew -s /bin/bash linuxbrew +USER linuxbrew +ENV PATH="/home/linuxbrew/.local/bin:$PATH" + +# Install Homebrew +RUN curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash +ENV PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}" + +WORKDIR /home/linuxbrew +RUN mkdir .edps EDPS_data + +COPY --chown=linuxbrew ./requirements.txt requirements.txt +COPY --chown=linuxbrew ./edps-gui.py edps-gui.py +COPY --chown=linuxbrew ./pdf_handler.py pdf_handler.py +COPY --chown=linuxbrew ./eso-logo.jpg eso-logo.jpg +COPY --chown=linuxbrew ./application.properties .edps/application.properties +COPY --chown=linuxbrew ./logging.yaml .edps/logging.yaml + +RUN brew install python@3.11 +RUN brew tap eso/pipelines +RUN brew install esopipe-fors + +RUN python3.11 -m venv venv && . venv/bin/activate && \ + pip install --no-cache-dir --upgrade -r requirements.txt + +ENV VIRTUAL_ENV=/home/linuxbrew/venv +ENV PATH=$VIRTUAL_ENV/bin:$PATH + +CMD ["panel", "serve", "edps-gui.py", "--plugins", "pdf_handler", "--address", "0.0.0.0", "--port", "7860", "--allow-websocket-origin", "*"] \ No newline at end of file diff --git a/eso_edps/README.md b/eso_edps/README.md new file mode 100644 index 00000000..68784d77 --- /dev/null +++ b/eso_edps/README.md @@ -0,0 +1,12 @@ +--- +title: Edps +emoji: 🔥 +colorFrom: red +colorTo: gray +sdk: docker +pinned: false +license: bsd-3-clause +short_description: ESO Data Processing System +--- + +Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference diff --git a/eso_edps/application.properties b/eso_edps/application.properties new file mode 100644 index 00000000..5173f553 --- /dev/null +++ b/eso_edps/application.properties @@ -0,0 +1,161 @@ +[server] +# Host or IP address to which EDPS server binds, e.g. localhost or 0.0.0.0 +host=0.0.0.0 + +# EDPS port number, e.g. 5000 +port=5000 + +[application] +# Comma-separated list of directories where workflows are installed. +# If not specified, EDPS will search for workflows in the pipeline installation tree. +# The naming convention for workflows is: /_wkf.py, e.g. espresso/espresso_wkf.py +workflow_dir= + +[executor] +# esorex is the command to execute pipeline recipes and it is installed with the pipeline. +# Please make sure that the path provided here can be located using the "which" command. +esorex_path=esorex + +# Path where pipeline plugins are installed. +# This configuration is used for ESO internal operations and can be left empty. +pipeline_path= + +# genreport is the command to execute quality control plots and it is installed with the Adari package. +genreport_path=genreport + +# EDPS data directory where recipe products, logs and quality control plots are saved. +# The files are organised in a directory structure under the base directory, defined as: +# /// +# Example: ESPRESSO/bias/fbf31155-a731-47f5-abf2-6445adce6c4b/master_bias.fits +# Please make sure that this directory has enough disk space available for storing the pipeline products, +# and consider enabling automatic data cleaning in the [cleanup] section. +base_dir=EDPS_data + +# If true, a dummy command is executed instead of esorex +dummy=False + +# If true, EDPS will attempt to execute a data reduction step even if the previous step has failed. +continue_on_error=False + +# Number of concurrent data reductions processes. +# Running concurrent data reductions will increase performance if sufficient resources are available, +# but can also lead to pipeline crashes if not enough memory is available to execute parallel reductions. +processes=1 + +# Number of CPUs (cores) available for data reduction. EDPS will not exceed the number of cores when scheduling +# data reduction tasks. +cores=1 + +# Pipeline recipes are parallelized using OpenMP. EDPS uses this parameter to set the number of threads when +# running a recipe, up to the available cores: OMP_NUM_THREADS=min(default_omp_threads, cores) +default_omp_threads=1 + +# Execution ordering. All orderings follow topological order so parent tasks are always placed before their children. +# Options: dfs, bfs, type, dynamic +# dfs - depth-first, give preference to reaching final reduction target quicker +# bfs - breadth-first, give preference to following reduction cascade level by level +# type - same as bfs, but make sure to process same type of data together (eg. first all BIASes) +# dynamic - immediately run whichever job is ready (has all needed inputs), no stalling but order is unpredictable +ordering=dfs + +# If provided, the recipe products will be renamed according to the following scheme: +# ..YYYY-MM-DDThh:mm.ss.mss.fits (Example: QC1.ESPRESSO.2023-02-09T17:30:14.326.fits), +# where timestamp is taken from the moment of renaming the file. +# Note that the renaming occurs in the base directory, not in the package (output) directory. +output_prefix= + +# In case EDPS was stopped while some jobs were waiting to be executed, should we execute them after restart. +resume_on_startup=False + +# EDPS will automatically re-execute a job if it's failed but needed as association, but only within this time window. +reexecution_window_minutes=60 + +[generator] +# Path to yaml file defining locations of static calibrations for each of the workflows. +# This configuration is used for ESO internal operations and can be left empty. +# EDPS will automatically load static calibrations delivered with the pipeline. +calibrations_config_file= + +# Path to yaml file defining locations of recipe and workflow parameters for each of the workflows. +# This configuration is used for ESO internal operations and can be left empty. +# EDPS will automatically load recipe and workflow parameters delivered with the pipeline. +parameters_config_file= + +# In case multiple matching associated inputs (e.g. calibrations) are available, which ones should be used. +# Options: raw, master, raw_per_quality_level, master_per_quality_level +# raw - use reduced raw data results even if master calibrations closer in time are available +# master - use master calibrations even if results of reduced raw data closer in time are available +# raw_per_quality_level - use calibrations closest in time but prefer reduced raw data results +# master_per_quality_level - use calibrations closest in time but prefer master calibrations +association_preference=raw_per_quality_level + +# URL to ESO-provided list of calibration breakpoints. +breakpoints_url= + +# Comma-separated list of workflows which should be combined together into one. +# This allows to submit data from different instruments to a single workflow "edps.workflow.meta_wkf" +meta_workflow= + +[repository] +# Clear the EDPS bookkeeping database on startup. +# This will cause all tasks to be re-executed even if they have been executed before on the same data. +truncate=False + +# Should we use local database for bookkeeping (currently always True). +local=True + +# Path where the bookkeeping database should be stored. +path=db.json + +# Type of bookkeeping database to use. +# Options: tiny, memory, caching +# tiny - directly use TinyDB json-file-based database +# memory - use fast in-memory non-persistent database +# caching - use in-memory cache on top of persistent TinyDB database for higher performance +type=caching + +# How many changes are needed to trigger TinyDB flushing data to disk. +flush_size=10 + +# How often automatically data should be flushed, regardless of changes. +flush_timeout=60 + +# Minimum amount of available disk space (in MB) required to flush data to disk. +min_disk_space_mb=100 + +[cleanup] +# Should automatic cleanup of reduced data be enabled. +enabled=False + +# How much time needs to pass since data got reduced to consider them for removal. +cleanup_older_than_seconds=1209600 + +# How often should we check if there are data to be removed. +cleanup_check_period_seconds=3600 + +[packager] +# Location where selected products should be placed. +package_base_dir= + +# Method to place files in the package directory. Options: link, symlink, copy. +# link - create hardlinks +# symlink - create symbolic links +# copy - copy the files +mode=symlink + +# Directory and filename pattern to use when placing files in the package directory. +# The pattern can contain any string, header keywords enclosed in $ (e.g. $pro.catg$), +# and the following predefined special variables: +# $NIGHT - year-month-day of when the data was taken +# $FILENAME - original name of the file +# $EXT - original extension of the file name +# $TASK - name of EDPS task which produced the file +# $TIMESTAMP - timestamp when data were submitted for reduction +# $DATASET - dataset name, derived from the first raw input file +# Example: $DATASET/$TIMESTAMP/$object$_$pro.catg$.$EXT +pattern=$DATASET/$TIMESTAMP/$object$_$pro.catg$.$EXT + +# Comma-separated list of product categories to place in the package directory. +# Empty means all products matching reduction target. +categories= + diff --git a/eso_edps/edps-gui.py b/eso_edps/edps-gui.py new file mode 100644 index 00000000..a38aee76 --- /dev/null +++ b/eso_edps/edps-gui.py @@ -0,0 +1,104 @@ +from pathlib import Path + +import panel as pn + +from edpsgui.gui.classifier import Classifier +from edpsgui.gui.dataset_creator import DatasetCreator +from edpsgui.gui.edps_ctl import EDPSControl +from edpsgui.gui.input_selector import InputSelector +from edpsgui.gui.job_viewer import JobViewer +from edpsgui.gui.reduction_history import ReductionHistory +from edpsgui.gui.reduction_queue import ReductionQueue +from edpsgui.gui.target_selector import TargetSelector +from edpsgui.gui.workflow import Workflow +from edpsgui.gui.workflow_selector import WorkflowSelector + +pn.extension('tabulator', 'tree', 'ipywidgets', 'terminal', 'codeeditor', 'modal', + css_files=["https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"], + disconnect_notification='Connection lost, try reloading the page!', + ready_notification='Application fully loaded.', + notifications=True) + +pn.state.notifications.position = 'top-center' + +input_selector = InputSelector() +edps_ctl = EDPSControl() +workflow_selector = WorkflowSelector(edps_status=edps_ctl.param.edps_status) +workflow_graph = Workflow(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow) +target_selector = TargetSelector(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow) +dataset_creator = DatasetCreator(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow, + inputs=input_selector.param.inputs, + targets=target_selector.param.targets) +classifier = Classifier(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow, + inputs=input_selector.param.inputs) +reduction_queue = ReductionQueue(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow) +reduction_history = ReductionHistory(edps_status=edps_ctl.param.edps_status, + workflow=workflow_selector.param.workflow) + +data_explorer = pn.Column( + f'### 1. Select input data', + input_selector, + pn.layout.Divider(), + f'### 2. Classify and inspect input data (optional)', + classifier, + pn.layout.Divider(), + f'### 3. Select reduction target', + target_selector, + pn.layout.Divider(), + f'### 4. Configure association (optional)', + dataset_creator, +) + +edps_aa_article = 'https://www.aanda.org/articles/aa/full_html/2024/01/aa47651-23/aa47651-23.html' +about_edps = pn.pane.HTML(f""" +If you make use of EDPS or its workflows for your research, we request to acknowledge +Freudling, Zampieri, Coccato et al 2024, A&A 681, A93. +""") + +reduction_tabs = pn.Tabs( + ('Processing', reduction_queue), + ('Processed', reduction_history), + dynamic=True, +) + + +def update_tabs(event): + if event == 2: + reduction_queue.update_table() + elif event == 3: + reduction_history.update_table() + + +tabs = pn.Tabs( + ('Workflow', workflow_graph), + ('Raw Data', data_explorer), + ('Processing Queue', reduction_queue), + ('Processed Data', reduction_history), + ('About EDPS', about_edps), + dynamic=True, +) + +pn.bind(update_tabs, tabs.param.active, watch=True) + +layout = pn.Column( + workflow_selector, + pn.layout.Divider(), + tabs, + sizing_mode='scale_width' +) + +logo_path = Path(__file__).resolve().parent / 'eso-logo.jpg' +template = pn.template.FastListTemplate(title='EDPS Dashboard', sidebar_width=250, logo=str(logo_path)) +template.main.append(layout) +template.header.append(edps_ctl) + +job_id = pn.state.location.query_params.get('job_id') +if job_id: + JobViewer(job_id).servable() +else: + template.servable() diff --git a/eso_edps/eso-logo.jpg b/eso_edps/eso-logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ceebf800fb366487177f0c412a147263b1c0cd3e GIT binary patch literal 17558 zcmb7r2UwHK(s1ZSK)O<;Lud+wUZp40gkD5yAfZVWP!JK2&dG>CdyXFtD)y z{NcZSIOzs3P!g38X^;>x0EiigNEnDt`T$v{WD^sU5D}dc`tL$XMNUCNN=8IX^n309 z{76I(0FV)rkWi3Qlao=CQk+&25ud`6GcZ!{GEquhhVnH9$vgQ;Gh@!*PSb=<@UvK0 zo-1i|_D7fYM9G*5Jl49CUe-qtlx2NBsh!c>YaWPg5n}kQ^MBEBG6$d`IVFsNgaM!q zNN(VHT4|g@T*NgLfUC_-Vk;eOXRXu5c4@PzuYa%oezy9HQlz?wtow&>t62ik(d8QT zJ6jK*7M~rawfTC%;xLH%0*1Z$vs%sDE&9I$8~XNFFu>R1siK~PtL~<^|G-wFwRgwz z7R6L}fyQsxgb)7G=`S!%idW=+(DuK;iqHPhDImfW`RwI!eRHMh&rW}~jQ+{OFEEn5 zTYsg4`6pLAKe3VgA~*f`U(EmF>SqUkBews{0m=CS;vluaI2Oux+64@~NJDo6(RtfY zSm`EF`o;+$LGD)Azz((erGMMffBqs1-KI#8zZ*8V13*yxt0|zN`)}H?R~Q8$EuK}+ z?9}jj96&h6N>iT;pj@-J6eejQ7xmlM(~VWKBj^5CQ?X~(DmM3(r}aEY^i^z{zf7xp zkj4HD{;jEMKZJQzt0HoUm4t36^}L)N+e2ac%Rr5S4uwpZ&Idp@q2juRwsnZ5FvI^Qs_Wn+UCI zb9TedV#v?~^chZkTR}B+pPq8l&#wlOe7pwrMi?s;R%e)pcOW$%ANaN51oC4v8GXdRE6)mYS3` z*e|b+6)iP>(fAvipeV9#m)M#Y=tpKTmJf%smR}-7XXQu=YLgglEz?ZX%sW(Bv{o&< z*k8)_H+SuWq3#lfjc&v=l*tWdY9qj9Ky-sD*804TC;3cT$P&oFM`^6AdJw`LqyYDh zX6WSYOM5HJixnB{(q;;tAE)Il21P$V!i(5{kYKHiRp-h5R56jON=hT_}c)eUC{ zd5rSLjq%2Eh;txX(DdjHfeK%VPE@9TfCDK>^oia}z&YxB+->Q{Xf zYP=!NeaN|h`7Pg%ftBg&$v%#*Iz@zkQ zC&_P7bS2quON>?a;{|vrFFWIadbb%EScTxkl3N3;6NE1x3zP-qdf^^nYpGE-m4k=J zHDcnO!C^iOYwe_&{pS%|ZYed#dHg$aj^^(;U%aX|=K>A%%26Bm4#PO4O~&+uHE5=V zDRLY3wO5NuwV-<~AMX-M_w%3&CYwY@IX#E9m!9M4pzxY6ZLTUAx7wUqzNemlo6OnT zmi?x-bT$w(w0d{NAhC6A0@Jpscnk<;d-yrIkwgA9xV|SkMv(dnTn6pppT@|z-lBj> z+Y4gW5OAkEp0Or`w1#B7NL!mikUt*Uh;C`wzla`832s$Dz|3dt@V8%!Q%3sm{2@1% zH#Oh?HP7(;G?d@wU5Y%1PVen7cK8g^4*_LuLYy59rQtPZV@FZR(<;=E=DfTDP+{@p zPOr%PQ1$5!h@x!QHc^gS{MeyldlG8ON!P3Y@EE+8iJ2GEIEAECp+th9bAc;#JRx zU+WCGpy^0B`)z?D3%2@3-&x?P^p-5s#dLOdwEGUVW2D=$OYNa$nLH3Ae8D-84}zU6 zZ!^+2@Kj-3VKiT5O_%PiF6)JRNr`JI0f-nCs;1?ugRE-V#S<~PVF^1&v^8Hsf!kqi zf_rSUMYtO@UH7ymRwnO;x#{MyA3We=E_Y+RQVMl27S9uIm3HMhRJ<>NFFx)JF7)ch z$jMds@w-|}TWH_mvglfO9_$K~6)fj})s1Q|l7`t?UJwLAGdc9z02BM4M+h1E&^{d0 zSx=_FZOJezpKn7peN5m45E*%WgA=jn{pbXs*1ox)EyJhYTo5ceVZEcmtte10C-{cw zKIluFgzSSwZ3VL_08D5Q$fzy3yg>E>pJ}rdn4adi#v}!X8?69qUP+O? z%YT1jgt>~YcpO`qf=NvApiJqppVZm6j|xe905&L3-%JstJOR7`WNZ9k82@>G8mO-! zo_^?oEM4$ZMGA#)i$XB-Nh)9vZhAVMUywoM$dCUGKM(zTyN~2PdBAv_8l*b7rTsc( z>J|*!6v;2d+eVfXo`Y&+9+$tMGlaZ`UQFpq8k&c))!ea{|NGMR5; zzScCSzZ_1gGaOw7G!Y^%Rr3PqEV4}?j?;q_O@wC{Q^P;pJ`k}{W^If;!1do zEVOm=wdb7gfSc6SzD~6+wasU*i7o`GM~mVV?;_X&-uk)q#NeLbDm4Hm4Fd5O6%S$v74g?aM;K&$vaA4mvi@Xg8F?WV}BEu z4a_T@F@uQv>u9}NS9mYCdtncde3Op=J!i$`q;~>D4)SPvdrD16|y4wp)KGQ%7_ZlIlKfCd)~a@X6wW?-LD~FYYM3(0!^v+uY@n2a43m<<)VJ z^8q30f`ZZJNH??uu>~(ANY{F|f}g#5?A|l$L#k=Zh5MRb#F_C_$Lxb>)uRo_DYxD_qE)Nh-BY8eEGbsSXF2Qul6-F6{#ay zX;go+bUsbmP6$PQ6E%8AJk(sLg}iO0@$BQ7TxOtn9jN@_^CTgeDv-itlJ<3F&&!!W zn0X>iBIy=Uc}QzXCv1Y^RT-0#_(m(4K-}n`(v5eJ&>TkC-SIihEl?t9{mkN}08?>2r*4SxBcrQz&@o-in;-$#HdHRyP9cl4d!)L@qDmgK zW}9wj&CJvEo1OMU7^`$EAAa+p4T~|xcgu2$=EHGMo@yHiEnikZLm3LG`79>}Fcdd` zc&`7)fIt7Rs!T0=apQ;piH}f7rl({oZ;BT@0l1IIx@ob5`RA*@IEZF^gz2{aKt0-8 zD&*#n!l08Q?XK%Og3V9GoB;IC$ea>PR42Ug066A~-EyVku8N#uZ$h_FfY$YopFoM!lJj8toHUN4wV7J6y0iX=sj`EQdgU>F)N9qR-I`dpQRqK2u)$rPkv<)Pg+_XS{aqlxCiwVh;K-3ao}! z3#<}u1gm0i&@>Wl{A)aq-*mMKH#0$CEEk3k97^x_Ju2cBWnTR*Y*ftsi8^DV+G9~k zs&e{(TRS&kSPj$p(CZ<2(PhAVHvsT|Y@t8YKWU$q?VI_88H(HZSXLWprOGym43e5q zq3vXt)=TD2K&hFsBH`M)@DK+7Z0fnmJ8ts$a(~v$wG3yX`>UfUpdP+V8sk2yO+2Il zh`10Q_g0Zl3*{CItKbgpXEco#XoO+Py5r&!m}H2~%5J0B@jbv}mNdl|a$n7)(=3v4 zpsxc=!$!4Dc$&12-dEMeA_X(5M9uFxq=-5fP*6iX0(?Wq*nN-I3doakc}!8NJR-Q@MNvcv}R& zU9wZIJt%YXAQ~H&IeYGikY(&_+n!-ma!BDDoeg+Rw2T^Zj%9iTu$g%d}T80q`m(GWPFxEx$y)jGEChhY%W zg*@n5vuGvH(~RG8@L7(lw;pL1sEQYqXx>XVlDkC*P+g*V_o8u2o(w0r=RKQOog|gv zC0Nyx=Z_M)Sv_)31=mh2^O{Ih0|2lpYzS!%xn7$*PM-3)+5J;(#P}JSADsaB`Nj2j zC2-Byw(~z;&nm5Z-td`Xf3>6?uB;f`Qc7eCok{E1zKCvn<}1`sAN6$EGpsks8L=cV zncZo?*vp{fYIKJ$M@UDg@_By&RsTR1+fsGv8WL2QcsX}c_Pu4*wKO`eWwEJo8&eY; zO6+v#`(E(wf4d*AaCOGW8uP~XLzTNquym>Ks?Y?QXc^Z%<83I7Yvk*+t>`Kh&XD@_ z+}PMm5FX^iZqu)VwKKoj2PfeqB&cpnZdKbp8_|AKRheu&>`gw$+`cAvjK<_tez<%O zlX!G(fOwtA*<4qi*_y`Rr%XCgJ*bI?Yy5OMZno-(XbmEhG|yFU@R&I#~IzF}gHWmc{kmub4R08NEwV)E6)dJZ6rzz5leccc&r9EzLTSk;-I z-t}0o?z%(%prPe%wH=+vQysP`HlKZNQGd~ATob!sTSj00pWOuBspG1yUQIo@X! zDE<~GHARR}jBP&($#BEi#c4$mdPF z3B1Jq^g-S6gR9;~qg}4eTzV*36QLDWioJjV10Rt`p}IUh4`CQM(sq>4S~*I5eyrbUs2hLHG}$_Gg{7n@gB6c;EoC9im-3aCZkwS{iqZ)p0v$S`Sj1 zv#fmYwj_t-lhNE`pUhFY8L4kAvb z`K2E8()l+CL~lAL%$2yo;sjuFQ7WQ|$fH>H%1B)<8Efq~xlT)zP@rs{N!sHyDG}RX zcJTCzAab|CP>Ti%on+n)pOn~DJX^Y?A*K`c4XRHO%4y;O7C^~M!|eOF%tpQ~kV8F? zPx6<}a^CQNz#3TE_sxt&hG$4H9+T-Hv;Z^6A5s9sEa;&}Y$V?6t4dz^WV=tb48b$%Ob~Uzy!i+=1gEX5-6GNeW3?`%2B)P``qkc4 zvbW?utk;wGw9NH`E%$;t5E748VQ4x;INo7|x5w+tsbKdP-PH3~k>w^=isX z+R?)2q~-bY9M|!DkgQI~B6-C}Z)C(U12Hb^wpWxWEeO)|%2LWtm(hw7XY}W1?|L$u zvjblBkm(*h6xrzQD0*(q0udkCi{Z?vH?v!|nDdHDm*jUlNET^hJ;1PfEtI3BRl+(* zuTOAH>l9QHAoCY}O1h4DE&ODLJ7AWWdwF#Bd7LIY0+H6%06HqdXQnk#@y}xhiI0r# zj%RUL6^QZk6V=yAaPpOme3u8FteM>o z_os6`XQMrA-Jun_{iKC8THFyjuj&n6v;!11G?@$a+=BUHFIhHeAKDJiG8`@2$TTv;LU2Nx*)g z_6qDKhsaB^mRk&^5-MNX5?=vzUYiGIJti@_mUhKR*^;xoqgiM>@XZVFhb|FH>AXS_ z@bNcKh!Lml0iR0+1qP338ZNN}x+f{G?f_a`#Pvtgx&4W(YDMzmAkrWGzJ*ZA;@q-EHhY%=_H+Gop-Zlp$SEziT6uWpoKK`LFZ-`%%`6nvWMTQO(p5; z{0yB^j$q$r?qm?7yWI0p*4S*!gx_o5-H95^h{>1YgY2^%3&2UB_Md2-*NPOJecHu+uujAR+a{|YxvyLw9+SB2FZTPnTd?(3)Y={kTm zAPQ~x$I%SDSy(e{=TrDG<38}SyyQ(s)pv9v4pkMcN+U_H!s=l*a&MlcuuyqlU@G6B z{R{w{Mqp1v!G9AIrbImvT&AnI{v4m3%yd`NZI)?_x)1$zG>08}a~(A8E8UIwrrt$O zc(@_Yo}}F`=i-^^AwGQI_RT&D98kE4q_=Ldfx7j>?e)_5#G?hLFCetr7Q9^{ka6rP z1QNH)&#U(%U_gD(yH%3Wj&B>NjlKx12e8HwH#uqmEhq`!%3(r$Y81nv%cn6m{WEf> zJ`1<(n^<|D+}t_?krybd+?DV~laNn_YMv@|W=KAj%HDk>>6z+-574Rlqf^Fhxga3gLWMUsFkx-=L)EEX!=3 zZp)evRHUuS@ms+SdDTz&Xx~vh%kyCXX&il5p@8Bzb*Af_N5nhNSm~Y50(Jma#5G&g^?* zdfNd9$b7_FFsWOiK))1aEMkvoEb|0#Ryk63D+%J~BLT6i6PaF1kbz-v+@dhd z4a3o+z6$`?!{mxBiNfjCx%#0yObCqNdK#Nh*=U);S=^Ixq|6+G?-GYv%oCx~$1nyP zoLTkT(=p|FgZ3bFT6bUPgPh2V(>~aOX<9*35m{<++p*g1X#E^6vI$(9N8kyd`g!EC zpP*hAOO9Wrv0iF)*GSADRvZO}x=iSjV94C<7{dLV^--~E&1%XD5nrP#jx}uJs9&{M zFCi`Hltw(WNG%Btp+o#cF8`x=QrD1|H}hyn)mCk3efp<8WGCjX^mTO#8{KLq!q_ z9OW}uSBhu&ju%;RtQF@{m*e(*i&KgTvz3iR%S5lOeDaXXF4JNe)|fWKM#!s&^8)pY zPZ6ULnnYZ!De+zQ@%Gi^MG^x=m>5~16`19SH?L0A0}C;MjtWHsMKoBYYHav&eMs>0 z&@6lpYdb92qq)jBy~Yl;H5OjhFU~LJSU#k4P%(7Z3bV_zU~jpVA0|@sqWLC{g)gn6g1c080Ba(5fz~JH*$LLJK+wV;A zD=$OPNnAWa)<1^Ik3zjabpl}Zr8`KQ13W){^7?*|GDqYTDK%1NEUsL`Vh=iRaV|+9 z#U==I+#}G*m^GO}$jZ@>EeMF_I*luIId3uRFa&WV3EzBv0g?{9So@tUmDbPi10q<< z$=7a%)V3O}Tbdx1p>^#;-w>s(`7y2D7aN075-p2O7Nmrw1z#h>Ad9}i;&9xqV~fZj zMz7fk2bPAJ+cE3v+L3a}R-)5kDZ~=xBmvg<#gGysRPjkhhY}r(j0WC}u!?eaW^Dv? zz*xJ2QC-J}u}dG?QfbdN%YcrV5es6k8B#INyl(me8puv{QE^v+#MLhM95!`LOCG zy1p2cH>d{59ny)m#tCn_+{RM0K6aCHVK)u3N$C{Q=`qWJ**z1PUQHxq55ov`jaA4* zq>O4s+}Zn6W>C0a6X*2+Vzo|JKcL)i`jjs#3Z-a1OLl zYsvG(aIit}4H)99W(B6hVlcq-6%!JjopFiX5u!?Z)h)o;t1VljL+-w{mz{(gO$~>S>#TT=3J@$?-f5 z?}zvw0dDa8uW-_m4`lh|oV5Yy}qc4`0M<;*}-PSymM0IVl#^VIr zIiV+Zfw{Hq6)+r{Cflz{RNkniCS6A8emwttGq;ww)u4Nkm6-XeKF;*3FRCQN)PI#7PSaPnQD*hXclHyFh-8L6kXhDQ9ZFz$V z$}IW3x3A0ix%x~OM<6-#04 z%a-du$85UEl4G0}L}fUDXjT_ZIuLmjO-_4tO>PIBk2w>}1!_yXr}#$bBAuI{1jZHn z^(YdVmtuAR`I4IJ25DBRtkS#ZlSHK4w%E5T5Y}Q|5f;uCH%dM@Daj-mKao=uD!y~9 z+s`6f2C2B8F0P-k$s45-Vd=KdZh#?eXv5!#cv04imO!3AxHuu1Dg)< zh9!<3jZ#$pj|Kkt_(xZbR=GchrrK6n+J+$yW8;UU#dex(t6sUqC?N73!0vlzj$w=t zR^a4~et|VJqP#kqCeeqZ_|P(sC3g9NH~y)X}F=hDQDlwVasX_JQJe~%*q1Y1O&~^L~u57 zOeU+DIYwSz+dWg_VM17FEvA+T!Q?j7)0KlVsC+k6!x%HrgJVX zt3>B~&3pk)3#BBBi3E&?jr9uD%)N`)ESYAj7|ET~FJLq?uw`8n5t>-Fzr31n2Z13* z#*&paJc|OnA!8>1+cLM!F|i8?{v?FkJ#OKTPXO7AX=S1OT6QM@+ zE?=x2tF&A&IZVV8o+Q_AT>=L_nvR^$e30g0+Zb-Uh}FThxq|(ma}fJ$3F#s450fus zATM#h_@Rz}mw#GOPk&bNe7Y=_{2Xs;1in1QGZ=T5*&5Bd579jLY@>3p8yUg$@m(ik zP8QMnnQpRr3eicY{3_#_RcygL5@v#MUM-N`G^Un_)sT+^0BHScGU-Yk1X)?`T|WWL zoBM0V-!PPZWF6Y88tdD(=DGN4Hnk~B(raEiw$L}qX4ClUm!TF0e@4WUTXp(pXF63* z08c7U8=8Cw38xh`0o3nEHAaeGd zS_J$t7%ZWT56mj$-Vhf@t}Fus?VYKHdq3BY-wFn{S3L37j)0j(J1QdD4-(8na>^G1 zJD-U?M}l6w7x_A*NpMs>4e9-<{K~x`!gBv(j#to@g=o0+nFH#?%#xnObbsxv>CAae zg8~RNjRBCDoArbduvku zy`^6H(p%`@jU8_R;{)LH-rg5=Zam_lSxKe^P0(mpKky?ke^TD56Oc=(AXS)r^Yx{| z7ANbAbUwzN?aTmbH9c%O==HNvBO%}k;1Ug8y|drhGNbAeoof9oUtK|^)_f^Gj?O2OIph!?kJg<;C|Z|Rt9_VdYi3Er5uhX z;$uIJlxyK7BtffS?~Xfyf?%lgslP>&7#R`yyVQOQ5T}OxDM0)Sis;P(%dzSMogCZR zVQnQ>^08TI&UJ`^69a=Y5|%gwgcz`}r7^H&Q_9xl_6BTagl7BAh3C?JlQ7+R-DsAg z)avRzfKI$#-{Bqwvqnn|7nI{f1h^ACSV&AZV}c!oxwg~FleOyAuwi%!tgsS_8oK@z zEp8ujbg4&yRj7D6x3&LDSSxT#C#rQ)66|)_I#Zw`4aF!mkY)^*3{0gfnC+d$BTDT%HkdLb#m#ez zigf%o_GUdE2zKxSS%H=&hEvD?~rNApnrSo8!HM6DW*Q{W|a zZw^BXWRAXybxXZ|@FljgIXgYrx?!jqO}SV#Zf!C(4E8o_%)6+wDrq^{_|*B?W^C)Y z4^M^lgiYy;gTGrkuQfxVq_QzA#S_oqjFU+@OgblwO>P5P8-+BL86=KY*7IrGP#V;# zO`yCx=0>C~V}u!Dr(P@@VEva?{$W4)8^Di8Qpwc-%lvziF>@c=AAR?U+{C@&=M*&2 z1^U5nZnz^EUX9ioCMl5nJ$6XvWq2f2L$k)G8|*!8sdM19+YcpJh9cKi#*Q9^ zP{Lw6*JFMNm>l8nRN-qU#m+I1PGZRcb7hPMkb+k4>aETNmDlA8_#>C_UEZVdQG7n2 zHGV6ovUJM!QC-4e9P1Vly$3l@Kyy$S z@+H3j&;&reHfA!?tlNr)i(2@r7#1uQn`v4V$m8>dVLo**NSKidPshLkjrs=hX zQwlbphAcvYC70Gsf3z}ajnkMZfCdQ3=@z+ zAL~DJ&58eLZeL(&dpYaY~S99&*#r>n!nTNJXJ`%#CSYEVAj-zTF z$j4>T&a%cpoYQX(`{ZaaSE4UqpRMD49|`pe-?EW`MSiuy9k~tjPlX7#L6afrWVE`n zK_Kb#)9#6gg1#>sow0srJ%*j+_S~qFcHestM7mw>pc!hw!+0V3OpXN?C`~)a)z@yn zzzus2sGI(sw^{vTLSVw62B?U+?XgJiJyAR02=^nFToj`LDQ@D4a{nt?Ab#~x2>H;< z#~ZNCPb`&tBVWbF2qgleW4b7^uA2!ZB6c3?N|9j4U&8or7DTJIrl}dLsgZ}Cg=kUZ zUj9vOPQJ1u8zP|&4Q#%uNH8+N+p}CvsW9Zmb zM!a4{bn?hqk(p2G$n8`YPNWG5m>22Qw6)fp0ABDg{)j}7YOD$6dTp`LVRfQ}V99Dv zgLG3{*}j^j(hZQaxWM;2Jzi}*t!M^)-Q`}q^#Jhc^At9>#5M?%N!o`pU9{vdiJ0TO z%yuMCO@#k!RZ~}lXZ}1Skr86Xs}P|rzkQmf2+04%_wSp+e;lxQyJX1vLPn0k$K^_c zAnT9#gPb^_0U8kj54VOZ!0h_f{L_t`eyQm8`kl1ePcC;`hRwX!){)9aS+1m!FZ%_L z+^wyYYMSJZsKsGC93Ou0Xt(AD1O{skb`6V`8XkS%i+3fVU*X;Nw3?1O0T5trauLo! zS?YsCZ6RY|?(7MRr@3!kF2;(ZiMRtLHP^Peu&av3h}PzkY0KH~F@T2O82T;m=O0}g z%duHnQ=hr7EUeThv)soC#q+5xCjgBo#D|BQiY0B&oo(_C`a}}4ClyZs8_YV%JTAv= zt&2%)j_$m7Z_Vf+p6x~YF45S7H(T9m!#3*F$|R%AtzSfx5k1m4r9`Jrj#IP3MuOpB z-pB}fk?( z%r^mIbq`LhEIex-*>H~WqYqMC111f`zxyJ;opD_F{fy%`F%Sc``H}?(j9d54|i%EZ26%n06 zZ*|zy3Nzq_4X`U*kh~>pC|K*A$lwW}GWz^k_oz9!!$*PBg!GFRN$h7Jy-x3^+wJ+( zg;p+{r<-BVz=&E@Xn8;FqPE|Z2xNFS=XRA%iRA2x=-tCue8FX~>LP0apQe4UlhRSS zlGz)OLi1;U(GQ9LyZkq**YT0@mTfj`e@_m(QAJywCTm9pw52t9vY$I-oOPg2ar-d~ zA=jbIxLDpYM@u@7Xe&(cWDw6OU>2!03*2=`4OG$|k(~ zzUej?9BN5od9Bf(FR5nNxhq8KO58UULRk;+T0`pdBZAlZ#U$Wv0S${Xy*7IMo1 zrt_eZ(yv89R*WGfhFV}#3r@%&N_)Q3&c#>B zd<1M0@u5_9_iV7xtEsKYor@|;#xuq!qW@(|drXb^7>O-p<@F_6LSAftkx3S#1f z!(taeUkN#YsOh8{%X>by-mS(m_Qy4SF%N4k(kSAW!$We|(me#l4_71n$P&gqFvl#6 zWUTp1@5p3hSD#!i6f$Z-YX)3UN};WI>YV)fm(2fe0Y42z{{r}*C;oFRxpI5cuS>`I zbH^wlKGt|vA@Aa*QY6eS4AmpKvZtRB+h0j8*vr}^g_veRTDdX>FFGxJz=@9tfNQ$D zhL0rFZ!;9}S=@fh4~=|r({v+0eoZ*s&hJxewl|~LB|8%bbNER0-ldO)ZpxQl!Q@y8 zX(F;kw7JXN!U;O$u(on$oa1S58k2jSSn%tlcyLgp5xIlgpu)fC&sZD+RC#)|;>`eEenb0xwbIVE3N^~MH=e1Ontjh7rnOntjuq`O+uSf6a0RIn-VA_ z%zy>hB!RMV#_qAPdF#WE3wn)e!f{qcDANR!N)w5_PjD01>niJSaPilRz^g!Sbli6z z7g@j*X2c|ZOi{CVJg1V6h#gg4QY~vNc|IpM&uQ#%TdGhk%8+76$)K+dXW}+rjO9}@ zM|yAF+i7IWaJpSZ|DtcAOfsGd@cM+%_;g^QgqCA- z4p6^e)&8j%g0**;M9R11Qoe#RztA^D)jFr`v|%jMM{wf_(#3CZ?f5Ihq8Jz zPE`=wbdzu@&A0?&vvIiNMAq$_`JV$L53*NyS=(C%d*-Qvea#+hRqE5Sr5lC zQ8Hs0nYK_}Z}OW%*BXwB#<@9apDkZ1wdDyH%!ef<=3P-onY`nG^Y; zyF`V5=I^I}P(t#D8AAWp)bZygf4u!#4Kyas{_6mY`JZlA{n6&%-~YAv0RYe&hTP{? zlOY&VsI}YC0gz7VNX;f~D~*bLJA@$(XPj5XXC=khc|_tpiDW>6Hd-kYmUOa5{Q2wO z$Zdf=)MJE5e)su)5b%$2<-bq~ptR_3^_Eq&RYID>7g_WB+&R>Y-rh0K5V^n874;dQ z|CDok81FIFwja+Q!9iWog+5Zlu4a3sjC5(9T_I}H_zOO(!urpDpEmesK%y(`=~tA# zmo6Mc1%wf*F;R(c41+VhrM?eQ(d*Zp@ywg|c~#7<(49{puzj7yyLR7?t}MSrS*jcP zXFPy<)vNzXa4GqDCG_u;>p@8 zTZ>rgQ5?4I?EpzPzpO8c_G+Ck{k_zedI4xv#qu)tTR{`xM_*rFg5V%k)~PU0rdQ_c zSw~0poxHdDRx!?WT`){06(8Ro;21!X1$cBD9BX$508MeD zag~hgmnNHd@Y<~ntK*Q8&hEsIuh^22#rKl_!7dRxj)x_R0TLhwq)Ojq|;hG;bUBA?zCuWzif*PyBzu*U!q<8 zI~k|Xf1i%>7oZ=nq>QV^(ShOcIybMp6m#vc1hUBo9;sUo&&^v?u{`#sJiYk9=(K8V zSq>>|DeUEYwl(-_Q`;FD+U;}cFW7$D%lwC1*r$zjd_X%{-^bg0^c$zQuyO~ypXk#q z&j>O-w-Z-1zyIFRL4#50N%5`uHaEIx*Zhxz469v;b+aT8rUPH3=n5y9|4YaJHLnyL zyJh+Uf`_TQaxGo6r*|m?`?y`j8`n=yXD(c#)A$Qoz%PbKxJxZXL6A}QG18dfAD~i7MULL<;EQfNITTWhqj y!f+a}PaqH6@z6}5!iiGJ@i)z@ozANO5lVns0P(}9KkEOd@?S4RqX3eV+5ZQDZ)D>D literal 0 HcmV?d00001 diff --git a/eso_edps/logging.yaml b/eso_edps/logging.yaml new file mode 100644 index 00000000..669bee97 --- /dev/null +++ b/eso_edps/logging.yaml @@ -0,0 +1,26 @@ +version: 1 +disable_existing_loggers: false + +formatters: + standard: + format: "%(asctime)s.%(msecs)03dZ %(levelname)5s %(process)d --- [%(threadName)s] %(name)s : %(message)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + +handlers: + console: + class: logging.StreamHandler + level: DEBUG + formatter: standard + stream: ext://sys.stdout + + file: + class: logging.handlers.RotatingFileHandler + formatter: standard + filename: edps.log + maxBytes: 10485760 # 10MB + backupCount: 20 + encoding: utf8 + +root: + level: DEBUG + handlers: [ console,file ] diff --git a/eso_edps/pdf_handler.py b/eso_edps/pdf_handler.py new file mode 100644 index 00000000..329e6ad8 --- /dev/null +++ b/eso_edps/pdf_handler.py @@ -0,0 +1,16 @@ +import os + +from tornado.web import RequestHandler + + +class PdfHandler(RequestHandler): + PDF_DIR = os.environ.get('EDPSGUI_PDF_DIR') or '.' + + def get(self): + filename = os.path.join(self.PDF_DIR, self.get_argument('file')) + self.set_header('Content-Type', 'application/pdf') + with open(filename, 'rb') as f: + self.write(f.read()) + + +ROUTES = [('/pdf', PdfHandler, {})] diff --git a/eso_edps/requirements.txt b/eso_edps/requirements.txt new file mode 100644 index 00000000..ab11b961 --- /dev/null +++ b/eso_edps/requirements.txt @@ -0,0 +1,4 @@ +https://www.eso.org/~szampier/edps/adari_core-1.0.0.tar.gz +https://www.eso.org/~szampier/edps/edps-latest.tar.gz +https://www.eso.org/~szampier/edps/edpsgui-0.0.1.tar.gz +https://www.eso.org/~szampier/edps/replots-0.0.1.tar.gz \ No newline at end of file