├── docs
├── modes.rst
├── examples
│ ├── first.png
│ └── imc.png
├── tabs
│ ├── api.rst
│ ├── index.rst
│ ├── websites.rst
│ └── modes.rst
├── images
│ └── screenshot.png
├── api
│ └── index.rst
├── plots.rst
├── states.rst
├── _templates
│ ├── autosummary
│ │ ├── base.rst
│ │ ├── module.rst
│ │ └── class.rst
│ ├── layout.html
│ └── autoclass
│ │ └── class.rst
├── automation.rst
├── environment.yml
├── configuration
│ ├── index.rst
│ ├── format.rst
│ └── tabs.rst
├── cli.rst
├── overview.rst
├── index.rst
├── _static
│ └── css
│ │ └── custom.css
├── Makefile
└── make.bat
├── .gitattributes
├── MANIFEST.in
├── .flake8
├── .codecov.yml
├── .gitignore
├── .codeclimate.yml
├── gwsumm
├── config
│ ├── matplotlib.ini
│ └── defaults.ini
├── tests
│ ├── __init__.py
│ ├── common.py
│ ├── test_mode.py
│ ├── test_utils.py
│ ├── test_channels.py
│ ├── test_tabs.py
│ ├── test_batch.py
│ └── test_archive.py
├── html
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_static.py
│ │ ├── test_bootstrap.py
│ │ └── test_html5.py
│ ├── __init__.py
│ ├── static.py
│ └── bootstrap.py
├── plot
│ ├── guardian
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ └── test_main.py
│ │ ├── __init__.py
│ │ └── __main__.py
│ ├── triggers
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ └── test_main.py
│ │ └── __init__.py
│ ├── registry.py
│ ├── __init__.py
│ ├── utils.py
│ └── sei.py
├── units.py
├── __init__.py
├── globalv.py
├── tabs
│ ├── __init__.py
│ ├── registry.py
│ ├── misc.py
│ └── stamp.py
├── state
│ ├── __init__.py
│ ├── all.py
│ └── registry.py
├── io.py
├── data
│ ├── __init__.py
│ ├── utils.py
│ └── range.py
├── mode.py
└── utils.py
├── .readthedocs.yaml
├── .github
└── workflows
│ ├── lint.yml
│ └── build.yml
├── README.rst
├── CONTRIBUTING.md
└── pyproject.toml
/docs/modes.rst:
--------------------------------------------------------------------------------
1 | .. automodapi:: gwsumm.mode
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | gwsumm/_version.py export-subst
2 |
--------------------------------------------------------------------------------
/docs/examples/first.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpy/gwsumm/HEAD/docs/examples/first.png
--------------------------------------------------------------------------------
/docs/examples/imc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpy/gwsumm/HEAD/docs/examples/imc.png
--------------------------------------------------------------------------------
/docs/tabs/api.rst:
--------------------------------------------------------------------------------
1 | #######
2 | Tab API
3 | #######
4 |
5 | .. automodapi:: gwsumm.tabs
6 |
--------------------------------------------------------------------------------
/docs/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpy/gwsumm/HEAD/docs/images/screenshot.png
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE README.rst MANIFEST.in
2 | include gwsumm/_version.py
3 | include gwsumm/config/*.ini
4 |
--------------------------------------------------------------------------------
/docs/api/index.rst:
--------------------------------------------------------------------------------
1 | Full API
2 | --------
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 | :glob:
7 |
8 | *
9 |
--------------------------------------------------------------------------------
/docs/plots.rst:
--------------------------------------------------------------------------------
1 | #####
2 | Plots
3 | #####
4 |
5 | .. currentmodule:: gwsumm.plot
6 |
7 | .. automodule:: gwsumm.plot
8 |
--------------------------------------------------------------------------------
/docs/states.rst:
--------------------------------------------------------------------------------
1 | ######
2 | States
3 | ######
4 |
5 | .. currentmodule:: gwsumm.state
6 |
7 | .. automodule:: gwsumm.state
8 | :no-members:
9 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude =
3 | __pycache__,
4 | .eggs/,
5 | .git/,
6 | build/,
7 | docs/,
8 | gwsumm/_version.py,
9 | per-file-ignores =
10 | __init__.py:F401,
11 |
--------------------------------------------------------------------------------
/.codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | precision: 2
3 | range: 50..100
4 | round: nearest
5 | status:
6 | project:
7 | default:
8 | target: auto
9 | threshold: 1
10 | patch:
11 | default:
12 | target: 90
13 |
--------------------------------------------------------------------------------
/docs/_templates/autosummary/base.rst:
--------------------------------------------------------------------------------
1 | {% if referencefile %}
2 | .. include:: {{ referencefile }}
3 | {% endif %}
4 |
5 | {{ objname }}
6 | {{ underline }}
7 |
8 | .. currentmodule:: {{ module }}
9 |
10 | .. auto{{ objtype }}:: {{ objname }}
11 |
--------------------------------------------------------------------------------
/docs/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 | {% set script_files = ['//gwpy.github.io/docs/stable/_static/gwpy_https.js'] + script_files + ['//gwpy.github.io/docs/stable/_static/copybutton.js']%}
3 | {% set bootswatch_css_custom = ['//gwpy.github.io/css/gwpy.css', '//gwpy.github.io/docs/stable/_static/gwpy-sphinx.css']%}
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /dist
3 | /.idea
4 | /gwsumm.egg-info
5 | .DS_store
6 | *.pyc
7 | /gwsumm/_version.py
8 | /docs/_build
9 | /docs/_generated
10 | /docs/api/modules.rst
11 | /docs/api/gwsumm.*
12 | /docs/api/api
13 | /*.patch
14 | .nfs*
15 | *.swp
16 | /.cache/
17 | /.eggs/
18 | /*.egg
19 | /share/css/
20 | .sass-cache
21 | /gwsumm/html/static/
22 | /.coverage
23 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | duplication:
3 | enabled: true
4 | config:
5 | languages:
6 | - python
7 | fixme:
8 | enabled: true
9 | config:
10 | strings:
11 | - FIXME
12 | - XXX
13 | pep8:
14 | enabled: true
15 | radon:
16 | enabled: true
17 |
18 | ratings:
19 | paths:
20 | - "**.py"
21 |
22 | exclude_paths:
23 | - gwsumm/_version.py
24 | - docs/*
25 | - gwsumm/tests/*
26 |
--------------------------------------------------------------------------------
/gwsumm/config/matplotlib.ini:
--------------------------------------------------------------------------------
1 | [tab-plots]
2 | type = plot
3 | name = My plots
4 | 1 = http://matplotlib.org/mpl_examples/pylab_examples/simple_plot.png
5 | 2 = http://matplotlib.org/mpl_examples/subplots_axes_and_figures/subplot_demo.png
6 | 3 = http://matplotlib.org/mpl_examples/statistics/histogram_demo_features.png
7 | layout = 1,2
8 | afterword = All images were taken from matplotlib.org.
9 |
--------------------------------------------------------------------------------
/docs/automation.rst:
--------------------------------------------------------------------------------
1 | #############################
2 | Automatic generation for LIGO
3 | #############################
4 |
5 | The LIGO Detector Characterization group use the `gwsumm` package to generate
6 | daily, weekly, and monthly summaries of the performance of the LIGO detectors.
7 | These data generation runs are automatically completed using the HTCondor
8 | high-throughput job scheduler.
9 |
10 | Members of the LIGO Scientific Collaboration or the Virgo Collaboration
11 | can view more details on the HTCondor configuration
12 | `here `_.
13 |
--------------------------------------------------------------------------------
/docs/environment.yml:
--------------------------------------------------------------------------------
1 | name: gwsumm
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | # build
6 | - pip
7 | - setuptools
8 | - setuptools-scm
9 | - wheel
10 | # install
11 | - astropy >=3.0.0
12 | - gwdatafind >=1.1.1
13 | - gwdetchar >=2.3.2
14 | - gwpy >=3.0.9
15 | - gwtrigfind
16 | - lalsuite
17 | - lscsoft-glue >=1.60.0
18 | - lxml
19 | - markdown
20 | - MarkupPy
21 | - matplotlib >=3.5
22 | - numpy >=1.16
23 | - pygments >=2.7.0
24 | - python-dateutil
25 | - igwn-ligolw
26 | - scipy >=1.2.0
27 | # docs
28 | - numpydoc
29 | - sphinx
30 | - sphinx-automodapi !=0.17.0
31 | - sphinx_bootstrap_theme
32 | - sphinxcontrib-programoutput
33 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Set the OS, Python version and other tools you might need
9 | build:
10 | os: "ubuntu-24.04"
11 | tools:
12 | python: "miniconda-latest"
13 |
14 | conda:
15 | environment: docs/environment.yml
16 |
17 | # Build documentation in the docs/ directory with Sphinx
18 | sphinx:
19 | configuration: docs/conf.py
20 |
21 | # Build docs in additional formats
22 | formats: all
23 |
24 | # Set the version of Python and requirements required to build your docs
25 | python:
26 | install:
27 | - method: pip
28 | path: .
29 | extra_requirements:
30 | - doc
31 |
--------------------------------------------------------------------------------
/gwsumm/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Test suite
20 |
21 | """
22 |
23 | __author__ = 'Duncan Macleod '
24 |
--------------------------------------------------------------------------------
/gwsumm/html/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2019)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Unit tests for gwsumm.html
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 |
--------------------------------------------------------------------------------
/docs/_templates/autosummary/module.rst:
--------------------------------------------------------------------------------
1 | {% if referencefile %}
2 | .. include:: {{ referencefile }}
3 | {% endif %}
4 |
5 | {{ objname }}
6 | {{ underline }}
7 |
8 | .. automodule:: {{ fullname }}
9 |
10 | {% block functions %}
11 | {% if functions %}
12 | .. rubric:: Functions
13 |
14 | .. autosummary::
15 | {% for item in functions %}
16 | {{ item }}
17 | {%- endfor %}
18 | {% endif %}
19 | {% endblock %}
20 |
21 | {% block classes %}
22 | {% if classes %}
23 | .. rubric:: Classes
24 |
25 | .. autosummary::
26 | {% for item in classes %}
27 | {{ item }}
28 | {%- endfor %}
29 | {% endif %}
30 | {% endblock %}
31 |
32 | {% block exceptions %}
33 | {% if exceptions %}
34 | .. rubric:: Exceptions
35 |
36 | .. autosummary::
37 | {% for item in exceptions %}
38 | {{ item }}
39 | {%- endfor %}
40 | {% endif %}
41 | {% endblock %}
42 |
--------------------------------------------------------------------------------
/gwsumm/plot/guardian/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Unit tests for gwsumm.plot.guardian
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 |
--------------------------------------------------------------------------------
/gwsumm/plot/triggers/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Unit tests for gwsumm.plot.triggers
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | # ---------------------------
2 | #
3 | # Check the source files for quality issues
4 | #
5 | # ---------------------------
6 |
7 | name: Lint
8 |
9 | on:
10 | push:
11 | branches:
12 | - main
13 | - master
14 | - release/**
15 | pull_request:
16 | branches:
17 | - main
18 | - master
19 | - release/**
20 |
21 | concurrency:
22 | group: ${{ github.workflow }}-${{ github.ref }}
23 | cancel-in-progress: true
24 |
25 | jobs:
26 | flake8:
27 | name: Flake8
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v5
31 | - name: Set up Python
32 | uses: actions/setup-python@v6
33 | with:
34 | python-version: '3.x'
35 | - name: Install dependencies
36 | run: |
37 | python -m pip install --upgrade pip
38 | python -m pip install "flake8>=3.7.0"
39 | - name: Lint with flake8
40 | run: python -m flake8 .
41 |
--------------------------------------------------------------------------------
/docs/configuration/index.rst:
--------------------------------------------------------------------------------
1 | ########################################
2 | How to write your own configuration file
3 | ########################################
4 |
5 | GWSumm makes use of INI-format configuration files to customise which data
6 | should be read, how it should be manipulated, and how it should be displayed.
7 | Through the `gw_summary` executable, all input from the user, excepting a
8 | small number of command-line options,
9 | are provided through one or more INI-format configuration files.
10 | These files contain sets of ``key = value`` pairs grouped into named
11 | ``[sections]`` to define everything from the required data inputs for a given
12 | page of output, to whether or not to display the gravitational-wave amplitude
13 | spectrum with a logarithmic frequency scale.
14 |
15 | Each of the following pages will explain how to write an INI configuration
16 | file to setup pretty much any data you could want:
17 |
18 | .. toctree::
19 | :maxdepth: 2
20 |
21 | format
22 | tabs
23 | data
24 |
--------------------------------------------------------------------------------
/gwsumm/plot/guardian/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Submodule for plots of Guardian data
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 | __credits__ = 'Duncan Macleod '
24 |
25 | from .core import (
26 | GuardianStatePlot,
27 | )
28 |
--------------------------------------------------------------------------------
/gwsumm/plot/triggers/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Submodule for plots of event triggers
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 | __credits__ = 'Duncan Macleod '
24 |
25 | from .core import (
26 | TriggerPlotMixin,
27 | TriggerDataPlot,
28 | TriggerTimeSeriesDataPlot,
29 | TriggerHistogramPlot,
30 | TriggerRateDataPlot,
31 | )
32 |
--------------------------------------------------------------------------------
/gwsumm/units.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Extra units for GW data processing
20 | """
21 |
22 | __author__ = 'Duncan Macleod '
23 |
24 | from astropy import units
25 | import gwpy.detector.units # noqa: F401
26 |
27 | _ns = {} # collector for new units
28 |
29 | # Virgo units
30 | units.def_unit('state', namespace=_ns)
31 |
32 | # -- register new units -----
33 | units.add_enabled_units(_ns)
34 |
--------------------------------------------------------------------------------
/docs/_templates/autoclass/class.rst:
--------------------------------------------------------------------------------
1 | {% if '__init__' in methods %}
2 | {{ methods.remove('__init__') }}
3 | {% endif %}
4 |
5 | .. code-block:: python
6 |
7 | >>> from {{ module }} import {{ name }}
8 |
9 | {{ docstring }}
10 |
11 | {% block attributes_summary %}
12 | {% if attributes %}
13 |
14 | .. rubric:: Attributes Summary
15 |
16 | .. autosummary::
17 | {% for item in attributes %}
18 | ~{{ name }}.{{ item }}
19 | {%- endfor %}
20 |
21 | {% endif %}
22 | {% endblock %}
23 |
24 | {% block methods_summary %}
25 | {% if methods %}
26 |
27 | .. rubric:: Methods Summary
28 |
29 | .. autosummary::
30 | {% for item in methods %}
31 | ~{{ name }}.{{ item }}
32 | {%- endfor %}
33 |
34 | {% endif %}
35 | {% endblock %}
36 |
37 | {% block attributes_documentation %}
38 | {% if attributes %}
39 |
40 | .. rubric:: Attributes Documentation
41 |
42 | {% for item in attributes %}
43 | .. autoattribute:: {{ item }}
44 | {%- endfor %}
45 |
46 | {% endif %}
47 | {% endblock %}
48 |
49 | {% block methods_documentation %}
50 | {% if methods %}
51 |
52 | .. rubric:: Methods Documentation
53 |
54 | {% for item in methods %}
55 | .. automethod:: {{ item }}
56 | {%- endfor %}
57 |
58 | {% endif %}
59 | {% endblock %}
60 |
--------------------------------------------------------------------------------
/gwsumm/tests/common.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWpy.
5 | #
6 | # GWpy is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWpy is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWpy. If not, see .
18 |
19 | """Compatibility module
20 | """
21 |
22 | from functools import wraps
23 |
24 | from .. import globalv
25 |
26 |
27 | # -- test decorators ----------------------------------------------------------
28 |
29 | def empty_globalv_CHANNELS(f):
30 | @wraps(f)
31 | def wrapped_f(*args, **kwargs):
32 | _channels = globalv.CHANNELS
33 | globalv.CHANNELS = type(globalv.CHANNELS)()
34 | try:
35 | return f(*args, **kwargs)
36 | finally:
37 | globalv.CHANNELS = _channels
38 | return wrapped_f
39 |
--------------------------------------------------------------------------------
/gwsumm/html/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """HTML helpers
20 |
21 | HTML output is built upon the
22 | `markup.py module `_ and primarily
23 | formatted to fit the
24 | `twitter bootstrap library `_.
25 | """
26 |
27 | from .static import (
28 | get_css,
29 | get_js,
30 | )
31 | from .html5 import (
32 | _expand_path,
33 | load_state,
34 | load,
35 | comments_box,
36 | ldvw_qscan,
37 | dialog_box,
38 | overlay_canvas,
39 | )
40 | from .bootstrap import (
41 | banner,
42 | calendar,
43 | wrap_content,
44 | state_switcher,
45 | base_map_dropdown,
46 | )
47 |
48 | __author__ = 'Duncan Macleod '
49 |
--------------------------------------------------------------------------------
/docs/_templates/autosummary/class.rst:
--------------------------------------------------------------------------------
1 | {% if referencefile %}
2 | .. include:: {{ referencefile }}
3 | {% endif %}
4 |
5 | {{ objname }}
6 | {{ underline }}
7 |
8 | .. currentmodule:: {{ module }}
9 |
10 | .. autoclass:: {{ objname }}
11 | :show-inheritance:
12 | :no-inherited-members:
13 |
14 | {% if '__init__' in methods %}
15 | {{ methods.remove('__init__') }}
16 | {% endif %}
17 |
18 | {% block attributes_summary %}
19 | {% if attributes %}
20 |
21 | .. rubric:: Attributes Summary
22 |
23 | .. autosummary::
24 | {% for item in attributes %}
25 | ~{{ name }}.{{ item }}
26 | {%- endfor %}
27 |
28 | {% endif %}
29 | {% endblock %}
30 |
31 | {% block methods_summary %}
32 | {% if methods %}
33 |
34 | .. rubric:: Methods Summary
35 |
36 | .. autosummary::
37 | {% for item in methods %}
38 | ~{{ name }}.{{ item }}
39 | {%- endfor %}
40 |
41 | {% endif %}
42 | {% endblock %}
43 |
44 | {% block attributes_documentation %}
45 | {% if attributes %}
46 |
47 | .. rubric:: Attributes Documentation
48 |
49 | {% for item in attributes %}
50 | .. autoattribute:: {{ item }}
51 | {%- endfor %}
52 |
53 | {% endif %}
54 | {% endblock %}
55 |
56 | {% block methods_documentation %}
57 | {% if methods %}
58 |
59 | .. rubric:: Methods Documentation
60 |
61 | {% for item in methods %}
62 | .. automethod:: {{ item }}
63 | {%- endfor %}
64 |
65 | {% endif %}
66 | {% endblock %}
67 |
--------------------------------------------------------------------------------
/gwsumm/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Gravitational-wave interferometer summary information system
20 |
21 | """
22 |
23 | from . import (
24 | globalv, # creates global variables
25 | units, # registers custom units
26 | )
27 |
28 | try:
29 | from ._version import version as __version__
30 | except ModuleNotFoundError:
31 | try:
32 | import setuptools_scm
33 | __version__ = setuptools_scm.get_version(fallback_version='?.?.?')
34 | except (ModuleNotFoundError, TypeError, LookupError):
35 | __version__ = '?.?.?'
36 |
37 | __author__ = 'Duncan Macleod '
38 | __credits__ = ('Alex Urban , ',
39 | 'Evan Goetz ')
40 |
--------------------------------------------------------------------------------
/gwsumm/globalv.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 |
20 | """Set of global memory variables for GWSumm package
21 | """
22 |
23 | import time
24 |
25 | from gwpy.time import to_gps
26 | from gwpy.segments import DataQualityDict
27 | from gwpy.detector import ChannelList
28 |
29 | CHANNELS = ChannelList()
30 | STATES = {}
31 |
32 | DATA = {}
33 | SPECTROGRAMS = {}
34 | SPECTRUM = {}
35 | COHERENCE_COMPONENTS = {}
36 | COHERENCE_SPECTRUM = {}
37 | SEGMENTS = DataQualityDict()
38 | TRIGGERS = {}
39 |
40 | VERBOSE = False
41 | PROFILE = False
42 | START = time.time()
43 |
44 | # run time variables
45 | MODE = 0
46 | WRITTEN_PLOTS = []
47 | NOW = int(to_gps('now'))
48 | HTMLONLY = False
49 |
50 | # comments
51 | IFO = None
52 | HTML_COMMENTS_NAME = None
53 |
--------------------------------------------------------------------------------
/docs/cli.rst:
--------------------------------------------------------------------------------
1 | .. _cli-page:
2 |
3 | ######################
4 | Command-line interface
5 | ######################
6 |
7 | GW Summary
8 | ==========
9 |
10 | The primary interface for GWSumm is a command-line utility called `gw_summary`.
11 | For a full explanation of the available command-line arguments and options, you
12 | can run
13 |
14 | .. command-output:: python ../bin/gw_summary --help
15 |
16 | This tool can be run in four modes: daily, weekly, and monthly analyses, and
17 | a specific range of GPS times.
18 |
19 | Day mode
20 | --------
21 |
22 | To run in daily summary mode, the following command-line options are available:
23 |
24 | .. command-output:: python gw_summary day --help
25 |
26 | Week mode
27 | ---------
28 |
29 | The arguments in weekly mode are as follows:
30 |
31 | .. command-output:: python gw_summary week --help
32 |
33 | Month mode
34 | ----------
35 |
36 | In monthly mode:
37 |
38 | .. command-output:: python gw_summary month --help
39 |
40 | GPS mode
41 | --------
42 |
43 | To run within a specific (but arbitrary) range of GPS seconds:
44 |
45 | .. command-output:: python gw_summary gps --help
46 |
47 | Batch mode
48 | ==========
49 |
50 | To stage a batch of analyses with a large collection of configuration files,
51 | as is done in embarrassingly parallel fashion when the summary pages run
52 | online, you can use the `gw_summary_pipe` command-line utility. This tool
53 | uses `HT Condor `_ to schedule
54 | and run jobs in parallel.
55 |
56 | To see all the available arguments and options for this tool, you can run
57 | with `--help` as usual:
58 |
59 | .. command-output:: python gw_summary_pipe --help
60 |
--------------------------------------------------------------------------------
/docs/tabs/index.rst:
--------------------------------------------------------------------------------
1 | .. _tabs:
2 |
3 | .. currentmodule:: gwsumm.tabs
4 |
5 | ####################
6 | Introduction to Tabs
7 | ####################
8 |
9 | GWsumm can be used either from the command line as described in
10 | :ref:`CLI interface ` or as a package to progromatically
11 | generate pages called "tabs".
12 |
13 | A `Tab` is a single, configurable page of output, containing some data.
14 | Each `Tab` is written in its own HTML page, and can be written to contain
15 | any set of data, with any format.
16 |
17 | The basic object provided by :mod:`gwsumm.tabs` is the `Tab`, which allows
18 | embedding of arbitrary HTML text into a standard surrounding HTML framework.
19 | The `Tab` also defines the API for other tabs.
20 |
21 | ----------------
22 | Simple `Tab` use
23 | ----------------
24 |
25 | A simple `Tab` can be created in only two steps
26 |
27 | .. code-block:: python
28 |
29 | from gwsumm.tabs import Tab
30 | mytab = Tab('My first tab')
31 | mytab.write_html("This tab doesn't do very much")
32 |
33 | This will create a directory under the current one,
34 |
35 | - ``my_first_tab/`` containing the HTML for the new tab
36 |
37 | The output webpage looks like:
38 |
39 | .. image:: examples/first.png
40 | :width: 80%
41 | :align: center
42 | :alt: First GWSumm example screenshot
43 |
44 | The content given to `Tab.write_html` is passed along untouched, so can contain any HTML you like.
45 |
46 | -------------------
47 | Generating websites
48 | -------------------
49 |
50 | The :ref:`next page ` will guide you through created groups of tabs
51 | and combining them to generate a fully-fledged website complete with
52 | navigation.
53 |
--------------------------------------------------------------------------------
/gwsumm/tabs/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013-2016)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """This module defines the `Tab` API, and all of the built-in tab objects
20 | """
21 |
22 | # core
23 | from .registry import (
24 | register_tab,
25 | get_tab,
26 | )
27 | from .core import (
28 | _MetaTab,
29 | BaseTab,
30 | StaticTab,
31 | GpsTab,
32 | IntervalTab,
33 | EventTab,
34 | Tab,
35 | ParentTab,
36 | TabList,
37 | )
38 | from .builtin import (
39 | ExternalTab,
40 | PlotTab,
41 | StateTab,
42 | UrlTab,
43 | )
44 | from .misc import (
45 | AboutTab,
46 | Error404Tab,
47 | )
48 |
49 | # data
50 | from .data import (ProcessedTab, DataTab)
51 |
52 | # application-specific extras
53 | from .sei import SEIWatchDogTab
54 | from .guardian import GuardianTab
55 | from .stamp import StampPEMTab
56 | from .management import AccountingTab
57 | from .etg import EventTriggerTab
58 | from .fscan import FscanTab
59 | from .gracedb import GraceDbTab
60 |
61 | __author__ = 'Duncan Macleod '
62 |
--------------------------------------------------------------------------------
/docs/configuration/format.rst:
--------------------------------------------------------------------------------
1 | #####################################
2 | A whistle-stop tour of the INI format
3 | #####################################
4 |
5 | The INI file syntax is an intertionally-recognised method of provided basic
6 | configuration options to any program, and is used extensively in, amongst
7 | other projects, the red-hat linux distrobution.
8 | Any `INI` file can be as simple as one line containing a `key` -- the name
9 | of a given variable, perhaps -- and a corresponding `value`:
10 |
11 | .. code-block:: ini
12 |
13 | name = John Doe
14 |
15 | Most parsers would then return a variable ``name`` containg the string value
16 | ``John Doe``, or at least something very similar.
17 |
18 | Moving beyond a simple set of variables and their values, the INI format
19 | supports grouping the key-value pairs into named sections.
20 | Any text enclosed in square brackets (``[]``) is interpreted as a section
21 | name:
22 |
23 | .. code-block:: ini
24 |
25 | [haggis]
26 | name = Haggis, Neeps, and Tatties
27 | ingredients = haggis, neeps, tatties
28 | time = 2.0
29 |
30 | [marsbar]
31 | name = Deep-fried Mars bar
32 | ingredients = Mars bar
33 | utensils = Deep-fat frier
34 | time = 0.01
35 |
36 | The above example, an extract from the author's recipe book, contains two
37 | sections with unique names, and a matchine set of keys describing the basic
38 | information about each dish.
39 | In this case, a standard file-parser would return a structured object with
40 | two sub-structures, each representing one section of the file.
41 |
42 | For more details and a good selections of examples, please see the
43 | documentation of the python :mod:`ConfigParser` module.
44 |
45 | .. warning::
46 |
47 | By default, the python :mod:`ConfigParser` module allows separation of
48 | keys and values using either the colon (``:``) or the equals sign (``=``),
49 | however, in order to increase functionality and user-friendliness,
50 | `gwsumm` configuration files must use only the equals sign (``=``) for
51 | this purpose. Do not use colons to separate key-value pairs.
--------------------------------------------------------------------------------
/gwsumm/tests/test_mode.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Test suite for the mode module
20 |
21 | """
22 |
23 | import datetime
24 |
25 | import pytest
26 |
27 | from .. import (globalv, mode)
28 |
29 | __author__ = 'Duncan Macleod '
30 |
31 | DEFAULT_MODE = mode.get_mode()
32 |
33 |
34 | def teardown_module():
35 | """Undo any set_mode() operations from this module
36 | """
37 | mode.set_mode(DEFAULT_MODE)
38 |
39 |
40 | def test_get_mode():
41 | assert mode.get_mode().value == globalv.MODE
42 | assert mode.get_mode(10) == mode.Mode.day
43 | assert mode.get_mode('week') == mode.Mode.week
44 | with pytest.raises(ValueError):
45 | mode.get_mode('invalid mode')
46 |
47 |
48 | def test_set_mode():
49 | mode.set_mode(0)
50 | assert globalv.MODE == mode.Mode(0).value
51 |
52 | mode.set_mode('GPS')
53 | assert globalv.MODE == mode.Mode.gps.value
54 |
55 | with pytest.raises(ValueError):
56 | mode.set_mode('invalid mode')
57 |
58 |
59 | @pytest.mark.parametrize('m, basestr', [
60 | ('day', 'day/20150914'),
61 | ('week', 'week/20150914'),
62 | ('month', 'month/201509'),
63 | ('year', 'year/2015'),
64 | ])
65 | def test_get_base(m, basestr):
66 | date = datetime.date(2015, 9, 14)
67 | mode.set_mode(m)
68 | assert mode.get_base(date) == basestr
69 |
--------------------------------------------------------------------------------
/gwsumm/html/tests/test_static.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2019)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Unit tests for gwsumm.html.static
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 |
24 | from collections import OrderedDict
25 |
26 | from .. import static
27 |
28 |
29 | # test simple utils
30 |
31 | def test_get_css():
32 | css = static.get_css()
33 | assert isinstance(css, OrderedDict)
34 | # test dict keys
35 | assert list(css.keys()) == [
36 | 'font-awesome',
37 | 'font-awesome-solid',
38 | 'gwbootstrap',
39 | ]
40 | # test list of files
41 | css_files = list(x.split('/')[-1] for x in css.values())
42 | assert css_files == [
43 | 'fontawesome.min.css',
44 | 'solid.min.css',
45 | 'gwbootstrap.min.css',
46 | ]
47 |
48 |
49 | def test_get_js():
50 | js = static.get_js()
51 | assert isinstance(js, OrderedDict)
52 | # test dict keys
53 | assert list(js.keys()) == [
54 | 'jquery',
55 | 'jquery-ui',
56 | 'moment',
57 | 'bootstrap',
58 | 'fancybox',
59 | 'datepicker',
60 | 'gwbootstrap',
61 | ]
62 | # test list of files
63 | js_files = list(x.split('/')[-1] for x in js.values())
64 | assert js_files == [
65 | 'jquery-3.7.1.min.js',
66 | 'jquery-ui.min.js',
67 | 'moment.min.js',
68 | 'bootstrap.bundle.min.js',
69 | 'fancybox.umd.js',
70 | 'bootstrap-datepicker.min.js',
71 | 'gwbootstrap-extra.min.js',
72 | ]
73 |
--------------------------------------------------------------------------------
/gwsumm/state/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """A `SummaryState` defines a sub-set of time over which a `~gwsumm.tabs.Tab`
20 | should be processed.
21 | Each `SummaryState` is normally tied to one or more data-quality flags marking
22 | times during which each of the LIGO instruments was operating in a certain
23 | configuration, or was subject to a known noise interference.
24 |
25 | ==================
26 | The state registry
27 | ==================
28 |
29 | GWSumm defines a state 'registry', simply a record of all `SummaryState`
30 | objects that have been defined (and registered) so far in a given program.
31 | The registry just makes remembering states in complicated programs a little
32 | easier.
33 |
34 | Any `SummaryState` can be registered with an arbitrary name as follows::
35 |
36 | >>> from gwsumm.state.registry import register_state
37 | >>> register_state(mystate, 'my state')
38 |
39 | and can be recovered later::
40 |
41 | >>> from gwsumm.state.registry import get_state
42 | >>> mystate = get_state('my state')
43 |
44 | =============
45 | API reference
46 | =============
47 |
48 | .. autosummary::
49 | :toctree: api
50 |
51 | SummaryState
52 | get_state
53 | get_states
54 | register_state
55 |
56 | """
57 |
58 | from .core import SummaryState
59 | from .registry import (get_state, get_states, register_state)
60 | from .all import (ALLSTATE, generate_all_state)
61 |
62 | __all__ = ['ALLSTATE', 'SummaryState', 'get_state', 'get_states',
63 | 'register_state', 'generate_all_state']
64 |
--------------------------------------------------------------------------------
/gwsumm/state/all.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Definition of the 'All' state.
20 |
21 | This is a special `SummaryState` that has valid and active segments spanning
22 | the full analysis interval.
23 | """
24 |
25 | from gwpy.segments import (Segment, SegmentList)
26 |
27 | from ..globalv import NOW
28 | from .core import SummaryState
29 | from .registry import register_state
30 |
31 | __author__ = 'Duncan Macleod '
32 |
33 | ALLSTATE = 'all'
34 |
35 |
36 | def generate_all_state(start, end, register=True, **kwargs):
37 | """Build a new `SummaryState` for the given [start, end) interval.
38 |
39 | Parameters
40 | ----------
41 | start : `~gwpy.time.LIGOTimeGPS`, float
42 | the GPS start time of the current analysis
43 | end : `~gwpy.time.LIGOTimeGPS`, float
44 | the GPS end time of the current analysis
45 | register : `bool`, optional
46 | should the new `SummaryState` be registered, default `True`
47 | **kwargs
48 | other keyword arguments passed to the `SummaryState` constructor
49 |
50 | Returns
51 | -------
52 | allstate : `SummaryState`
53 | the newly created 'All' `SummaryState`
54 | """
55 | now = min(end, NOW)
56 | all_ = SummaryState(ALLSTATE,
57 | known=SegmentList([Segment(start, end)]),
58 | active=SegmentList([Segment(start, now)]),
59 | **kwargs)
60 | all_.ready = True
61 | if register:
62 | register_state(all_)
63 | return all_
64 |
--------------------------------------------------------------------------------
/gwsumm/plot/registry.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Registry for GWSumm output plot types
20 |
21 | All plot types should be registered for easy identification from the
22 | configuration INI files
23 | """
24 |
25 | import re
26 |
27 | __author__ = 'Duncan Macleod '
28 |
29 | _PLOTS = {}
30 |
31 |
32 | def register_plot(plot, name=None, force=False):
33 | """Register a new summary `Plot` to the given ``name``
34 |
35 | Parameters
36 | ----------
37 | name : `str`
38 | unique descriptive name for this type of plot, must not
39 | contain any spaces, e.g. 'timeseries'
40 | plotclass : `type`
41 | defining Class for this plot type
42 | force : `bool`
43 | overwrite existing registration for this type
44 |
45 | Raises
46 | ------
47 | ValueError
48 | if name is already registered and ``force`` not given as `True`
49 | """
50 | if name is None:
51 | name = plot.type
52 | if name not in _PLOTS or force:
53 | _PLOTS[name] = plot
54 | else:
55 | raise ValueError("Plot '%s' has already been registered to the %s "
56 | "class" % (name, plot.__name__))
57 |
58 |
59 | def get_plot(name):
60 | """Query the registry for the plot class registered to the given
61 | name
62 | """
63 | if isinstance(name, str):
64 | name = re.sub(r'[\'\"]', '', name)
65 | try:
66 | return _PLOTS[name]
67 | except KeyError:
68 | raise ValueError("No TabPlot registered with name '%s'" % name)
69 |
--------------------------------------------------------------------------------
/gwsumm/io.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2017)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Input/output utilities
20 | """
21 |
22 | import os.path
23 | import re
24 |
25 | from astropy.io.registry import IORegistryError
26 |
27 | from gwpy.frequencyseries import FrequencySeries
28 |
29 | __author__ = 'Duncan Macleod '
30 |
31 | HDF5_FILENAME = re.compile(r'(?P(.hdf5|.hdf|.h5))\/')
32 |
33 |
34 | def read_frequencyseries(filename):
35 | """Read a `~gwpy.frequencyseries.FrequencySeries` from a file
36 |
37 | IF using HDF5, the filename can be given as a combined filename/path, i.e.
38 | ``test.hdf5/path/to/dataset``.
39 |
40 | Parameters
41 | ----------
42 | filename : `str`
43 | path of file to read
44 |
45 | Returns
46 | -------
47 | series : `~gwpy.frequencyseries.FrequencySeries`
48 | the data as read
49 |
50 | Raises
51 | ------
52 | astropy.io.registry.IORegistryError
53 | if the input format cannot be identified or is not registered
54 | """
55 | # try and parse path in HDF5 file if given
56 | try:
57 | ext = HDF5_FILENAME.search(filename).groupdict()['ext']
58 | except AttributeError: # no match
59 | kwargs = {}
60 | else:
61 | kwargs = {'path': filename.rsplit(ext, 1)[1]}
62 | # read file
63 | try:
64 | return FrequencySeries.read(filename, **kwargs)
65 | except IORegistryError:
66 | if filename.endswith('.gz'):
67 | fmt = os.path.splitext(filename[:-3])[-1]
68 | else:
69 | fmt = os.path.splitext(filename)[-1]
70 | return FrequencySeries.read(filename, format=fmt.lstrip('.'), **kwargs)
71 |
--------------------------------------------------------------------------------
/gwsumm/tabs/registry.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Registry for GWSumm data tabs.
20 |
21 | All Tabs should be registered for easy identification from the
22 | configuration INI files
23 | """
24 |
25 | from ..utils import re_quote
26 |
27 | __author__ = 'Duncan Macleod '
28 |
29 | __all__ = ['register_tab', 'get_tab']
30 |
31 | _TABS = {}
32 |
33 |
34 | def register_tab(tab, name=None, force=False):
35 | """Register a new summary `Tab` to the given ``name``
36 |
37 | Parameters
38 | ----------
39 | tab : `type`
40 | defining Class for this tab type.
41 | name : `str`, optional
42 | unique descriptive name for this type of tab, must not
43 | contain any spaces, e.g. 'hveto'. If ``name=None``, the `Tab.type`
44 | class attribute of the given tab will be used.
45 | force : `bool`
46 | overwrite existing registration for this type
47 |
48 | Raises
49 | ------
50 | ValueError
51 | if name is already registered and ``force`` not given as `True`
52 | """
53 | if name is None:
54 | name = tab.type
55 | if name not in _TABS or force:
56 | _TABS[name] = tab
57 | else:
58 | raise ValueError("Tab '%s' has already been registered to the %s "
59 | "class" % (name, _TABS[name].__name__))
60 |
61 |
62 | def get_tab(name):
63 | """Query the registry for the tab class registered to the given
64 | name
65 | """
66 | name = re_quote.sub('', name)
67 | try:
68 | return _TABS[name]
69 | except KeyError:
70 | raise ValueError("No Tab registered with name '%s'" % name)
71 |
--------------------------------------------------------------------------------
/gwsumm/data/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013-2016)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Methods and classes for loading and pre-processing data
20 |
21 | Each of the sub-modules are designed to read or create the data requested
22 | only once, with the containers from the `globalv` module used as a storage
23 | buffer for each unique data type
24 | """
25 |
26 | # read TimeSeries data
27 | from .timeseries import (
28 | _urlpath,
29 | _get_timeseries_dict,
30 | sieve_cache,
31 | find_frames,
32 | find_best_frames,
33 | find_frame_type,
34 | frame_trend_type,
35 | get_channel_type,
36 | exclude_short_trend_segments,
37 | all_adc,
38 | get_timeseries_dict,
39 | locate_data,
40 | get_timeseries,
41 | add_timeseries,
42 | resample_timeseries_dict,
43 | filter_timeseries,
44 | )
45 |
46 | # generate Spectrograms and FrequencySeries
47 | from .spectral import (
48 | _get_spectrum,
49 | _get_spectrogram,
50 | get_spectrogram,
51 | add_spectrogram,
52 | get_spectrograms,
53 | size_for_spectrogram,
54 | apply_transfer_function_series,
55 | get_spectrum,
56 | )
57 |
58 | # generate coherence Spectrograms and Spectra
59 | from .coherence import (
60 | _get_from_list,
61 | _get_coherence_spectrogram,
62 | get_coherence_spectrogram,
63 | get_coherence_spectrum,
64 | add_coherence_component_spectrogram,
65 | get_coherence_spectrograms,
66 | complex_percentile,
67 | )
68 |
69 | # generate TimeSeries of sensitive distance (range)
70 | from .range import (
71 | _metadata,
72 | _segments_diff,
73 | get_range_channel,
74 | get_range,
75 | get_range_spectrogram,
76 | get_range_spectrum,
77 | )
78 |
--------------------------------------------------------------------------------
/docs/tabs/websites.rst:
--------------------------------------------------------------------------------
1 | .. _websites:
2 |
3 | .. currentmodule:: gwsumm.tabs
4 |
5 | Generating Websites
6 | ===================
7 |
8 | :ref:`As we have seen `, generating standalone pages is trivial using GWSumm. What would be more useful would be to generate linked sets of pages, aka a website.
9 |
10 | Navigation
11 | ----------
12 |
13 | The key difference between standalone pages and a website is the ability to navigate between them. The `Tab.write_html` method will take care of that for you if you pass it all of the tabs:
14 |
15 | .. code-block:: python
16 |
17 | from gwsumm.tabs import Tab
18 | tab1 = Tab('Tab 1')
19 | tab2 = Tab('Tab 2')
20 | tabs = [tab1, tab2]
21 | tab1.write_html('This is tab 1', tabs=tabs)
22 | tab2.write_html('This is tab 2', tabs=tabs)
23 |
24 | This will write each tab into its own directory, as before, but the HTML will now contain an `` block above the banner to allow navigation between the pages.
25 |
26 | Tab parents
27 | -----------
28 |
29 | In the above example, each tab is included as a link in the navigation bar. However, in larger websites with many pages, the navigation can quickly become cluttered and will start to overflow the width of the page.
30 | This can be avoided by declaring `~Tab.parent` for sets of tabs:
31 |
32 | .. code-block:: python
33 |
34 | tab1 = Tab('Tab 1')
35 | tab2a = Tab('A', parent='Tab 2')
36 | tab2b = Tab('B', parent=tab2a.parent)
37 | tabs = [tab1, tab2a, tab2b]
38 | tab1.write_html('This is tab 1', tabs=tabs)
39 | tab2a.write_html('This is tab 2A', tabs=tabs)
40 | tab2b.write_html('This is tab 2B', tabs=tabs)
41 |
42 | Here we have set a `parent` tab for 2A, and used the same for 2B, which creates a dropdown menu in the navigation bar linking to these tabs. 'Tab 2' is never created, but is used only for navigation.
43 |
44 | Tab groups
45 | ----------
46 |
47 | For even larger websites, sets of tabs under a single parent can be
48 | further separated into `groups `. For example, to put 2A
49 | into group `1` and 2B into group `2`, we can write:
50 |
51 | .. code-block:: python
52 |
53 | tab1 = Tab('Tab 1')
54 | tab2a = Tab('A', parent='Tab 2', group='1')
55 | tab2b = Tab('B', parent=tab2a.parent, group='2')
56 | tabs = [tab1, tab2a, tab2b]
57 | tab1.write_html('This is tab 1', tabs=tabs)
58 | tab2a.write_html('This is tab 2A', tabs=tabs)
59 | tab2b.write_html('This is tab 2B', tabs=tabs)
60 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ================================================
2 | Gravitational-wave Summary Information Generator
3 | ================================================
4 |
5 | GWSumm is a python toolbox used by the LIGO Scientific Collaboration to summarise and archive sundry facets of the performance of the LIGO instruments, and archive these data in a nested HTML structure.
6 |
7 | |PyPI version| |Conda version|
8 |
9 | |DOI| |License| |Supported Python versions|
10 |
11 | |Build Status| |Coverage Status| |Code Climate|
12 |
13 | https://gwsumm.readthedocs.io
14 |
15 | ------------
16 | Installation
17 | ------------
18 |
19 | GWSumm is best installed with `conda`_:
20 |
21 | .. code:: bash
22 |
23 | conda install -c conda-forge gwsumm
24 |
25 | but can also be installed with `pip`_:
26 |
27 | .. code:: bash
28 |
29 | python -m pip install gwsumm
30 |
31 | ------------
32 | Contributing
33 | ------------
34 |
35 | All code should follow the Python Style Guide outlined in `PEP 0008`_;
36 | users can use the `flake8`_ package to check their code for style issues
37 | before submitting.
38 |
39 | See `the contributions guide`_ for the recommended procedure for
40 | proposing additions/changes.
41 |
42 | .. _PEP 0008: https://www.python.org/dev/peps/pep-0008/
43 | .. _flake8: http://flake8.pycqa.org
44 | .. _the contributions guide: https://github.com/gwpy/gwsumm/blob/master/CONTRIBUTING.md
45 | .. _conda: https://conda.io
46 | .. _pip: https://pip.pypa.io/en/stable/
47 |
48 | .. |PyPI version| image:: https://badge.fury.io/py/gwsumm.svg
49 | :target: http://badge.fury.io/py/gwsumm
50 | .. |Conda version| image:: https://img.shields.io/conda/vn/conda-forge/gwsumm.svg
51 | :target: https://anaconda.org/conda-forge/gwsumm/
52 | .. |DOI| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.2647609.svg
53 | :target: https://doi.org/10.5281/zenodo.2647609
54 | .. |License| image:: https://img.shields.io/pypi/l/gwsumm.svg
55 | :target: https://choosealicense.com/licenses/gpl-3.0/
56 | .. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/gwsumm.svg
57 | :target: https://pypi.org/project/gwsumm/
58 | .. |Build Status| image:: https://github.com/gwpy/gwsumm/actions/workflows/build.yml/badge.svg?branch=master
59 | :target: https://github.com/gwpy/gwsumm/actions/workflows/build.yml
60 | .. |Coverage Status| image:: https://codecov.io/gh/gwpy/gwsumm/branch/master/graph/badge.svg
61 | :target: https://codecov.io/gh/gwpy/gwsumm
62 | .. |Code Climate| image:: https://codeclimate.com/github/gwpy/gwsumm/badges/gpa.svg
63 | :target: https://codeclimate.com/github/gwpy/gwsumm
64 |
--------------------------------------------------------------------------------
/docs/tabs/modes.rst:
--------------------------------------------------------------------------------
1 | .. _modes:
2 |
3 | .. currentmodule:: gwsumm.tabs
4 |
5 | ###########
6 | `Tab` modes
7 | ###########
8 |
9 | In its simplest form, the `Tab` is essentially a blank canvas on which to write whatever you want.
10 | However, the original mandate for GWSumm was to provide a framework in which to generate automatic summaries of LIGO data, over a given timescale.
11 |
12 | To handle data processing, rather than static HTML generation, each `Tab` has a type, based on its relation to any interval in time
13 |
14 | .. currentmodule:: gwsumm.tabs.core
15 |
16 | .. autosummary::
17 | :nosignatures:
18 | :toctree: ../api
19 |
20 | StaticTab
21 | IntervalTab
22 | EventTab
23 |
24 | The type of a `Tab` is set automatically when it is created based on the value of the :attr:`~Tab.mode` attribute, so you don't need to remember the above objects.
25 |
26 | =====
27 | Modes
28 | =====
29 |
30 | GWSumm currently support seven different `Tab` modes:
31 |
32 | ========== ==== =============================================================
33 | Mode Enum Description
34 | ========== ==== =============================================================
35 | ``STATIC`` 0 No associated time interval
36 | ``EVENT`` 1 Associated with a single GPS time, normally around an event
37 | ``GPS`` 2 Simple (arbitrary) GPS ``[start, end)`` interval
38 | ``DAY`` 10 One UTC 24-hour day
39 | ``WEEK`` 11 One 7-day week
40 | ``MONTH`` 12 One calendar month
41 | ``YEAR`` 13 One calendar year
42 | ========== ==== =============================================================
43 |
44 | ===============
45 | Assigning modes
46 | ===============
47 |
48 | Each `Tab` will be assigned a mode (unless specified as follows, the default mode
49 | is ``STATIC``). The assignment can be done on a per-tab basis by
50 | passing the `~Tab.mode` keyword argument when creating a `Tab`, or
51 | globally, by using the :meth:`gwsumm.mode.set_mode`. The latter sets
52 | the default mode for all subsequent tabs created in this session.
53 |
54 | If a :attr:`~Tab.mode` is given that associates with a GPS time or
55 | times, these must be given via the `~IntervalTab.span` or
56 | `~EventTab.gpstime` keyword arguments, otherwise a `TypeError` will be
57 | raised. The `span` tuple is the ``(GPS start time, GPS end time)``
58 |
59 | .. code-block:: python
60 |
61 | >>> tab = Tab('My first tab', mode='day', span=(0, 100))
62 | >>> print(tab.mode, tab.span)
63 | (10, Segment(0, 100))
64 | >>> tab = Tab('My first tab', mode='EVENT', gpstime=101)
65 | >>> print(tab.mode, tab.gpstime)
66 | (1, LIGOTimeGPS(101,0))
67 |
--------------------------------------------------------------------------------
/gwsumm/html/static.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2016)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """HTML helphers
20 |
21 | This module mainly declares the resources used by standard on HTML pages
22 | """
23 |
24 | from collections import OrderedDict
25 |
26 | __author__ = 'Duncan Macleod '
27 | __credits__ = ('Alex Urban ,'
28 | ' Evan Goetz ')
29 |
30 |
31 | # build collection of CSS resources
32 | CSS = OrderedDict((
33 | ('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/'
34 | 'font-awesome/6.5.1/css/fontawesome.min.css'),
35 | ('font-awesome-solid', 'https://cdnjs.cloudflare.com/ajax/libs/'
36 | 'font-awesome/6.5.1/css/solid.min.css'),
37 | ('gwbootstrap', 'https://cdn.jsdelivr.net/npm/gwbootstrap@1.3.7/'
38 | 'lib/gwbootstrap.min.css'),
39 | ))
40 |
41 | # build collection of javascript resources
42 | JS = OrderedDict((
43 | ('jquery', 'https://code.jquery.com/jquery-3.7.1.min.js'),
44 | ('jquery-ui', 'https://code.jquery.com/ui/1.13.2/jquery-ui.min.js'),
45 | ('moment', 'https://cdnjs.cloudflare.com/ajax/libs/'
46 | 'moment.js/2.30.1/moment.min.js'),
47 | ('bootstrap', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/'
48 | 'dist/js/bootstrap.bundle.min.js'),
49 | ('fancybox', 'https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/'
50 | 'dist/fancybox/fancybox.umd.js'),
51 | ('datepicker', 'https://cdnjs.cloudflare.com/ajax/libs/'
52 | 'bootstrap-datepicker/1.9.0/js/'
53 | 'bootstrap-datepicker.min.js'),
54 | ('gwbootstrap', 'https://cdn.jsdelivr.net/npm/gwbootstrap@1.3.7/'
55 | 'lib/gwbootstrap-extra.min.js'),
56 | ))
57 |
58 |
59 | # -- utilities ----------------------------------------------------------------
60 |
61 | def get_css():
62 | """Return a `dict` of CSS files to link in the HTML
63 | """
64 | return CSS
65 |
66 |
67 | def get_js():
68 | """Return a `dict` of javascript files to link in the HTML
69 | """
70 | return JS
71 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to GWSumm
2 |
3 | This is [@alurban](//github.com/alurban)'s and [@duncanmmacleod](//github.com/duncanmmacleod/)'s workflow, which might work well for others. It is merely a verbose version of the [GitHub flow](https://guides.github.com/introduction/flow/).
4 |
5 | The basic idea is to use the `master` branch of your fork as a way of updating your fork with other people's changes that have been merged into the main repo, and then working on a dedicated _feature branch_ for each piece of work:
6 |
7 | - create the fork (if needed) by clicking _Fork_ in the upper-right corner of https://github.com/gwpy/gwsumm/ - this only needs to be done once, ever
8 | - clone the fork into a new folder dedicated for this piece of work (replace `` with yout GitHub username):
9 |
10 | ```bash
11 | git clone https://github.com//gwsumm.git gwsumm-my-work # change gwsumm-my-work as appropriate
12 | cd gwsumm-my-work
13 | ```
14 |
15 | - link the fork to the upstream 'main' repo:
16 |
17 | ```bash
18 | git remote add upstream https://github.com/gwpy/gwsumm.git
19 | ```
20 |
21 | - pull changes from the upstream 'main' repo onto your fork's master branch to pick up other people's changes, then push to your remote to update your fork on github.com
22 |
23 | ```bash
24 | git pull --rebase upstream master
25 | git push
26 | ```
27 |
28 | - create a new branch on which to work
29 |
30 | ```bash
31 | git checkout -b my-new-branch
32 | ```
33 |
34 | - make commits to that branch
35 | - push changes to your remote on github.com
36 |
37 | ```bash
38 | git push -u origin my-new-branch
39 | ```
40 |
41 | - open a merge request on github.com
42 | - when the request is merged, you should 'delete the source branch' (there's a button), then just delete the clone of your fork and forget about it
43 |
44 | ```bash
45 | cd ../
46 | rm -rf ./gwsumm-my-work
47 | ```
48 |
49 | And that's it.
50 |
51 | ## Coding guidelines
52 |
53 | ### Python compatibility
54 |
55 | **GWSumm code must be compatible with Python versions >=3.6.**
56 |
57 | ### Style
58 |
59 | This package follows [PEP 8](https://www.python.org/dev/peps/pep-0008/),
60 | and all code should adhere to that as far as is reasonable.
61 |
62 | The first stage in the automated testing of pull requests is a job that runs
63 | the [`flake8`](http://flake8.pycqa.org) linter, which checks the style of code
64 | in the repo. You can run this locally before committing changes via:
65 |
66 | ```bash
67 | python -m flake8
68 | ```
69 |
70 | ### Testing
71 |
72 | GWSumm has a relatively incomplete test suite, covering less than 40% of the codebase.
73 | All code contributions should be accompanied by (unit) tests to be executed with
74 | [`pytest`](https://docs.pytest.org/en/latest/), and should cover
75 | all new or modified lines.
76 |
77 | You can run the test suite locally from the root of the repository via:
78 |
79 | ```bash
80 | python -m pytest gwsumm/
81 | ```
82 |
--------------------------------------------------------------------------------
/docs/overview.rst:
--------------------------------------------------------------------------------
1 | ###############
2 | What is GWSumm?
3 | ###############
4 |
5 | The `gwsumm` package ('the summary pages') is a python toolbox that can be
6 | used to generate a structured HTML webpage displaying graphical data that
7 | describe any and all aspects of gravitational-wave interferometer performance.
8 | The summary pages were developed in collaboration between the LIGO Laboratory
9 | and the GEO600 project with the goal of generating an automated daily summary
10 | of laser-interferometer operations and performance.
11 |
12 | The LIGO Summary Pages are used to characterize and monitor the status
13 | of the detectors and their subsystems. In addition, data products and
14 | webpages from other analysis tools are included in the Summary Pages.
15 |
16 | The output acts as a kind of daily magazine, allowing instrument scientists
17 | and data analysis teams a archived, searchable summary of the key figures of
18 | merit that will determine the sensitivity and ultimately the science output
19 | of these instruments.
20 |
21 | Those readers who are members of the LIGO Scientific Collaboration, the Virgo
22 | Collaboration, or KAGRA can view the current LIGO summary pages at the
23 | following sites:
24 |
25 | == =======================================================
26 | H1 https://ldas-jobs.ligo-wa.caltech.edu/~detchar/summary/
27 | L1 https://ldas-jobs.ligo-la.caltech.edu/~detchar/summary/
28 | == =======================================================
29 |
30 | Working model
31 | =============
32 |
33 | The GWSumm package provides an abstract set of classes from which any user
34 | can build their own python program to read, manipulate, and display data.
35 | However, for the specific purpose of the LIGO instrumental summary pages,
36 | the `gw_summary` command-line executable is used to read in a number of
37 | INI-format configuration files that define what data should be read, how it
38 | should be manipulated, and how it should all be displayed.
39 |
40 | These configuration files are made up ``[tab-xxx]`` section with the following
41 | format:
42 |
43 | .. code-block:: ini
44 |
45 | [tab-IMC]
46 | name = Input mode cleaner
47 | shortname = IMC
48 | 1 = L1:IMC-PWR_IN_OUT_DQ timeseries
49 | 1-ylim = 0,80
50 | 1-title = 'Power into IMC'
51 | [html]
52 | issues =
53 |
54 | This block defines the ``IMC`` tab, with a ``name`` (and a ``shortname``):
55 | a single ``timeseries`` plot of the ``L1:IMC-PWR_IN_OUT_DQ`` channel.
56 | The plot has been customised with a y-axis limit and a title. This
57 | also defines the required ``[html]`` section, where the required key
58 | ``issues`` is defined. This example can be saved to a file called ``imc.ini``.
59 |
60 | This tab is then generated by passing it to the `gw_summary` executable, along
61 | with some GPS times over which to run:
62 |
63 | .. code-block:: bash
64 |
65 | $ gw_summary gps 'Feb 29 2020 00:00' 'Feb 29 2020 01:00' --config-file imc.ini
66 |
67 | This minimal setup will produce the following HTML page
68 | `1266969618-1266973218/imc/index.html`:
69 |
70 | .. image:: examples/imc.png
71 | :width: 80%
72 | :align: center
73 | :alt: GWSumm example screenshot
74 |
75 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | #########################################################
2 | GWSumm: the gravitational-wave summary information system
3 | #########################################################
4 |
5 | |PyPI version| |Conda version|
6 |
7 | |DOI| |License| |Supported Python versions|
8 |
9 | The `gwsumm` package is a tool used by the
10 | `Laser Interferometer Gravitational-wave Observatory (LIGO) `_
11 | to collect, aggregate, and visually summarise the sundry data produced
12 | throughout the experiment in order to characterise instrument performance.
13 |
14 | The output of this package, known internally as the 'summary pages', give an
15 | archive of a number of figures or merit, including time-series amplitude
16 | trends, frequency spectra and spectrograms, and transient event triggers.
17 |
18 | This package includes a collection of command-line utilities and a python
19 | module:
20 |
21 | .. code:: python
22 |
23 | import gwsumm
24 |
25 | Installation
26 | ============
27 |
28 | GWSumm is best installed with `conda`_:
29 |
30 | .. code:: bash
31 |
32 | conda install -c conda-forge gwsumm
33 |
34 | but can also be installed with `pip`_:
35 |
36 | .. code:: bash
37 |
38 | python -m pip install gwsumm
39 |
40 | Note, users with `LIGO.ORG` credentials have access to a software
41 | container with a regularly-updated build of GWSumm. For more
42 | information please refer to the
43 | `LSCSoft Conda `_ documentation.
44 |
45 | Contributing
46 | ============
47 |
48 | All code should follow the Python Style Guide outlined in `PEP 0008`_;
49 | users can use the `flake8`_ package to check their code for style issues
50 | before submitting.
51 |
52 | See `the contributions guide`_ for the recommended procedure for
53 | proposing additions/changes.
54 |
55 | The GWSumm project is hosted on GitHub:
56 |
57 | * Issue tickets: https://github.com/gwpy/gwsumm/issues
58 | * Source code: https://github.com/gwpy/gwsumm
59 |
60 |
61 | License
62 | -------
63 |
64 | GWSumm is distributed under the `GNU General Public License`_.
65 |
66 | .. toctree::
67 | :maxdepth: 1
68 | :hidden:
69 |
70 | overview
71 | cli
72 | automation
73 | tabs/index
74 | tabs/websites
75 | tabs/modes
76 | tabs/api
77 | states
78 | plots
79 | modes
80 | api/index
81 |
82 | .. _PEP 0008: https://www.python.org/dev/peps/pep-0008/
83 | .. _flake8: http://flake8.pycqa.org
84 | .. _the contributions guide: https://github.com/gwpy/gwsumm/blob/master/CONTRIBUTING.md
85 | .. _conda: https://conda.io
86 | .. _pip: https://pip.pypa.io/en/stable/
87 | .. _GNU General Public License: https://github.com/gwpy/gwsumm/blob/master/LICENSE
88 |
89 | .. |PyPI version| image:: https://badge.fury.io/py/gwsumm.svg
90 | :target: http://badge.fury.io/py/gwsumm
91 | .. |Conda version| image:: https://img.shields.io/conda/vn/conda-forge/gwsumm.svg
92 | :target: https://anaconda.org/conda-forge/gwsumm/
93 | .. |DOI| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.2647609.svg
94 | :target: https://doi.org/10.5281/zenodo.2647609
95 | .. |License| image:: https://img.shields.io/pypi/l/gwsumm.svg
96 | :target: https://choosealicense.com/licenses/gpl-3.0/
97 | .. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/gwsumm.svg
98 | :target: https://pypi.org/project/gwsumm/
99 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=77.0.3",
4 | "setuptools_scm[toml]>=3.4.3",
5 | "wheel",
6 | ]
7 | build-backend = "setuptools.build_meta"
8 |
9 | [project]
10 | name = "gwsumm"
11 | description = "A python toolbox used by the LIGO Scientific Collaboration for detector characterisation"
12 | readme = "README.rst"
13 | requires-python = ">=3.10"
14 | authors = [
15 | { name = "Alex Urban", email = "alex.urban@ligo.org" },
16 | { name = "Duncan Macleod", email = "duncan.macleod@ligo.org" },
17 | ]
18 | maintainers = [
19 | { name = "Evan Goetz", email = "evan.goetz@ligo.org" },
20 | ]
21 | license = "GPL-3.0-or-later"
22 | license-files = [ "LICENSE" ]
23 | classifiers = [
24 | "Development Status :: 5 - Production/Stable",
25 | "Intended Audience :: Developers",
26 | "Intended Audience :: Science/Research",
27 | "Natural Language :: English",
28 | "Operating System :: OS Independent",
29 | "Programming Language :: Python",
30 | "Programming Language :: Python :: 3",
31 | "Programming Language :: Python :: 3.10",
32 | "Programming Language :: Python :: 3.11",
33 | "Topic :: Scientific/Engineering",
34 | "Topic :: Scientific/Engineering :: Astronomy",
35 | "Topic :: Scientific/Engineering :: Physics",
36 | ]
37 |
38 | dependencies = [
39 | "astropy >=3.0.0",
40 | "gwdatafind >=1.1.1",
41 | "gwdetchar >=2.3.2",
42 | "gwpy >=3.0.9",
43 | "gwtrigfind",
44 | "lalsuite",
45 | "lscsoft-glue >=1.60.0",
46 | "lxml",
47 | "markdown",
48 | "MarkupPy",
49 | "matplotlib >=3.5",
50 | "numpy >=1.16",
51 | "pygments >=2.7.0",
52 | "python-dateutil",
53 | "igwn-ligolw",
54 | "scipy >=1.2.0",
55 | ]
56 |
57 | dynamic = ["version"]
58 |
59 | [project.optional-dependencies]
60 | test = [
61 | "flake8",
62 | "pytest >=3.3.0",
63 | "pytest-cov >=2.4.0",
64 | ]
65 | dev = [
66 | "h5py",
67 | "ligo-gracedb >= 2.0.0",
68 | "pykerberos",
69 | ]
70 | doc = [
71 | "numpydoc",
72 | "sphinx",
73 | "sphinx-automodapi",
74 | "sphinx_bootstrap_theme",
75 | "sphinxcontrib-programoutput",
76 | ]
77 |
78 | [project.scripts]
79 | gw_summary = "gwsumm.__main__:main"
80 | gw_summary_pipe = "gwsumm.batch:main"
81 | gwsumm-plot-guardian = "gwsumm.plot.guardian.__main__:main"
82 | gwsumm-plot-triggers = "gwsumm.plot.triggers.__main__:main"
83 |
84 | [project.urls]
85 | "Documentation" = "https://gwsumm.readthedocs.io"
86 | "Source Code" = "https://github.com/gwpy/gwsumm"
87 | "Bug Tracker" = "https://github.com/gwpy/gwsumm/issues"
88 | "Discussion Forum" = "https://gwdetchar.slack.com"
89 |
90 | [tool.setuptools]
91 | include-package-data = true
92 |
93 | [tool.setuptools.packages.find]
94 | include = [ "gwsumm*" ]
95 |
96 | [tool.setuptools_scm]
97 | write_to = "gwsumm/_version.py"
98 |
99 | [tool.coverage.run]
100 | source = [ "gwsumm" ]
101 | omit = [
102 | # don't report coverage for _version.py
103 | # (generated automatically by setuptools-scm)
104 | "*/_version.py",
105 | "gwsumm/tests/*",
106 | "gwsumm/html/tests/*",
107 | # omit scripts for now, will be done in a future PR
108 | "gwsumm/__main__.py",
109 | ]
110 |
111 | [tool.coverage.report]
112 | # print report with one decimal point
113 | precision = 1
114 |
115 | [tool.pytest.ini_options]
116 | addopts = "-r a"
117 |
118 |
--------------------------------------------------------------------------------
/gwsumm/plot/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """A `Plot` is a representation of an image to be included in the HTML
20 | output a :doc:`tab `.
21 |
22 | For simple purposes, a `Plot` is just a reference to an existing image file
23 | that can be imported into an HTML page via the ```` tag.
24 |
25 | For more complicated purposes, a number of data plot classes are provided to
26 | allow users to generate images on-the-fly.
27 | The available classes are:
28 |
29 | .. autosummary::
30 | :toctree: api
31 |
32 | TimeSeriesDataPlot
33 | SpectrogramDataPlot
34 | SegmentDataPlot
35 | StateVectorDataPlot
36 | SpectrumDataPlot
37 | TimeSeriesHistogramPlot
38 | TriggerTimeSeriesDataPlot
39 | TriggerHistogramPlot
40 | TriggerRateDataPlot
41 | """
42 |
43 | __author__ = 'Duncan Macleod '
44 |
45 | from .registry import (
46 | register_plot,
47 | get_plot,
48 | )
49 | from .utils import (
50 | get_column_label,
51 | get_column_string,
52 | hash,
53 | )
54 | from .core import (
55 | format_label,
56 | SummaryPlot,
57 | DataPlot,
58 | BarPlot,
59 | PiePlot,
60 | )
61 | from .builtin import (
62 | undo_demodulation,
63 | TimeSeriesDataPlot,
64 | SpectrogramDataPlot,
65 | CoherenceSpectrogramDataPlot,
66 | SpectrumDataPlot,
67 | CoherenceSpectrumDataPlot,
68 | TimeSeriesHistogramPlot,
69 | TimeSeriesHistogram2dDataPlot,
70 | SpectralVarianceDataPlot,
71 | RayleighSpectrogramDataPlot,
72 | RayleighSpectrumDataPlot,
73 | )
74 | from .segments import (
75 | tint_hex,
76 | common_limits,
77 | SegmentDataPlot,
78 | StateVectorDataPlot,
79 | DutyDataPlot,
80 | ODCDataPlot,
81 | SegmentPiePlot,
82 | NetworkDutyPiePlot,
83 | NetworkDutyBarPlot,
84 | SegmentBarPlot,
85 | SegmentHistogramPlot,
86 | )
87 | from .triggers import (
88 | TriggerPlotMixin,
89 | TriggerDataPlot,
90 | TriggerTimeSeriesDataPlot,
91 | TriggerHistogramPlot,
92 | TriggerRateDataPlot,
93 | )
94 | from .range import (
95 | _get_params,
96 | RangePlotMixin,
97 | RangeDataPlot,
98 | RangeDataHistogramPlot,
99 | RangeSpectrogramDataPlot,
100 | RangeSpectrumDataPlot,
101 | RangeCumulativeSpectrumDataPlot,
102 | RangeCumulativeHistogramPlot,
103 | SimpleTimeVolumeDataPlot,
104 | GWpyTimeVolumeDataPlot,
105 | )
106 | from .noisebudget import (
107 | NoiseBudgetPlot,
108 | RelativeNoiseBudgetPlot,
109 | )
110 | from .guardian import GuardianStatePlot
111 | from .sei import SeiWatchDogPlot
112 |
--------------------------------------------------------------------------------
/gwsumm/plot/guardian/tests/test_main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Tests for the `gwsumm.plot.guardian` command-line interface
20 | """
21 |
22 | import os
23 | import shutil
24 |
25 | from gwpy.timeseries import (
26 | TimeSeries,
27 | TimeSeriesList,
28 | )
29 |
30 | from .... import globalv
31 | from ....archive import write_data_archive
32 | from .. import __main__ as guardian_cli
33 |
34 | __author__ = 'Alex Urban '
35 |
36 | # -- test configuration
37 |
38 | CONFIG = """
39 | [tab-ISC_LOCK]
40 | type = guardian
41 | node = ISC_LOCK
42 | name = %(node)s
43 | ; node states
44 | 600 = Low noise
45 | """
46 |
47 | # -- test data
48 |
49 | SUFFICES = [
50 | "STATE_N",
51 | "REQUEST_N",
52 | "NOMINAL_N",
53 | "OK",
54 | "MODE",
55 | "OP",
56 | ]
57 | DATA = {
58 | key: TimeSeriesList(
59 | TimeSeries([600] * 3600 * 16, sample_rate=16, name=key, channel=key)
60 | ) for key in ["L1:GRD-ISC_LOCK_{}".format(suff) for suff in SUFFICES]
61 | }
62 |
63 |
64 | # -- utils --------------------------------------------------------------------
65 |
66 | def _get_inputs(workdir):
67 | """Prepare, and return paths to, input data products
68 | """
69 | # set global timeseries data
70 | globalv.DATA = DATA
71 | # get path to data files
72 | ini = os.path.join(workdir, "config.ini")
73 | archive = os.path.abspath(os.path.join(workdir, "archive.h5"))
74 | # write to data files
75 | with open(ini, 'w') as f:
76 | f.write(CONFIG)
77 | write_data_archive(archive)
78 | # reset global data and return
79 | globalv.DATA = {}
80 | return (ini, archive)
81 |
82 |
83 | # -- cli tests ----------------------------------------------------------------
84 |
85 | def test_main(tmpdir, caplog):
86 | outdir = str(tmpdir)
87 | plot = os.path.join(outdir, "guardian.png")
88 | (ini, archive) = _get_inputs(outdir)
89 | args = [
90 | 'ISC_LOCK',
91 | '0', '3600',
92 | ini,
93 | '--plot-params', 'title=Test figure',
94 | '--output-file', plot,
95 | '--verbose',
96 | '--archive', archive,
97 | ]
98 | # test output
99 | guardian_cli.main(args)
100 | assert os.path.exists(plot)
101 | assert len(os.listdir(outdir)) == 3 # 2 inputs, 1 output
102 | assert 'Read data archive from {}'.format(archive) in caplog.text
103 | assert 'Processing:' in caplog.text
104 | assert 'Plot saved to {}'.format(plot) in caplog.text
105 | assert 'Archive recorded as {}'.format(archive) in caplog.text
106 | # clean up
107 | shutil.rmtree(outdir, ignore_errors=True)
108 |
--------------------------------------------------------------------------------
/gwsumm/state/registry.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Registry for `states `.
20 | """
21 |
22 | from .. import globalv
23 | from ..utils import re_quote
24 |
25 | __author__ = 'Duncan Macleod '
26 |
27 | __all__ = ['register_state', 'get_state', 'get_states']
28 |
29 |
30 | def register_state(state, key=None, force=False):
31 | """Register a new `SummaryState` to the given ``key``
32 |
33 | Parameters
34 | ----------
35 | state : `SummaryState`
36 | defining Class for this state type.
37 | key : `str`, optional
38 | unique descriptive name for the `SummaryState` to be registered.
39 | If ``key=None``, the :attr:`~SummaryState.key`
40 | attribute of the given state will be used.
41 | force : `bool`
42 | overwrite existing registration for this key
43 |
44 | Raises
45 | ------
46 | ValueError
47 | if key is already registered and ``force`` not given as `True`
48 | """
49 | if key is None:
50 | key = state.key
51 | key = key.lower()
52 | if key not in globalv.STATES or force:
53 | globalv.STATES[key] = state
54 | return state
55 | raise ValueError("State %r has already been registered." % key)
56 |
57 |
58 | def get_state(key):
59 | """Query the registry for the `SummaryState` registered to the given key
60 |
61 | Parameters
62 | ----------
63 | key : `str`
64 | registered key of desired `SummaryState`. This may not match the
65 | `~SummaryState.name` attribute` if the state was registered with
66 | a different key.
67 |
68 | Returns
69 | -------
70 | state : `SummaryState`
71 | the `SummaryState` registered with the given key
72 |
73 | Raises
74 | ------
75 | ValueError:
76 | if the ``key`` doesn't map to a registered `SummaryState`
77 | """
78 | key = re_quote.sub('', key)
79 | try:
80 | return globalv.STATES[key.lower()]
81 | except KeyError:
82 | raise ValueError("No SummaryState registered with name '%s'" % key)
83 |
84 |
85 | def get_states(keys=set()):
86 | """Query the registry for a list of states (defaults to all)
87 |
88 | Parameters
89 | ----------
90 | keys : `set` of `str`
91 | the set of state keys to query in the registry
92 |
93 | Returns
94 | -------
95 | states : `dict`
96 | a `dict` of (``key``, `SummaryState`) pairs
97 |
98 | Raises
99 | ------
100 | ValueError:
101 | if any of the ``keys`` doesn't map to a registered `SummaryState`
102 | """
103 | if not keys:
104 | return globalv.STATES.copy()
105 | else:
106 | return dict((key, get_state(key)) for key in keys)
107 |
--------------------------------------------------------------------------------
/docs/configuration/tabs.rst:
--------------------------------------------------------------------------------
1 | #############################
2 | Configuring a simple HTML tab
3 | #############################
4 |
5 | .. currentmodule:: gwsumm.tabs
6 |
7 | A :class:`Tab` is single HTML web-page containing some data. It can be as
8 | simple as containing some text, or can be told to generated a number of plots
9 | from GW interferometer data and display them in a specific format.
10 |
11 | For full technical details on the `Tab` classes available, please `read the
12 | tabs API page <../tabs>`_.
13 |
14 | ====
15 | Role
16 | ====
17 |
18 | A tab can take on one of two roles, depending on its configuration:
19 |
20 | ====== ======================================================================
21 | Parent The summary-page for a set of subordinate child tabs, displayed as the
22 | heading for a dropdown menu in the HTML navigation bar of the output
23 | Child A subordinate child tab of a given parent, linked under the relevant
24 | dropdown menu in the HTML navigation bar of the output
25 | ====== ======================================================================
26 |
27 | =============
28 | Configuration
29 | =============
30 |
31 | Each tab class provides a :meth:`Tab.from_ini` `classmethod`, allowing users to
32 | create a new tab from a ``[section]`` in a configuration file.
33 | Every tab should be configured with the following options:
34 |
35 | =========== ===========================================================
36 | `~Tab.type` type of tab to configure [optional, default: ``'default'``]
37 | `~Tab.name` name of this tab
38 | =========== ===========================================================
39 |
40 | The ``type`` option, whilst optional, is recommended, mainly to make the
41 | configuration more transparent to other users, who might not know which tab type
42 | is the default.
43 |
44 | Additionally, all tabs can be configured with the following keys:
45 |
46 | .. autosummary::
47 | :nosignatures:
48 |
49 | ~Tab.shortname
50 | ~Tab.parent
51 | ~Tab.group
52 |
53 | See the detailed definitions of each attribute for defaults.
54 |
55 | -------------
56 | `ExternalTab`
57 | -------------
58 |
59 | The `ExternalTab` allows users to embed any HTML page from the same domain
60 | into a GWSumm page.
61 | An `ExternalTab` can be configured as follows:
62 |
63 | .. code-block:: ini
64 |
65 | [tab-external]
66 | type = external
67 | name = 'My results'
68 | url = '/~duncan.macleod/analysis/results/summary.html'
69 |
70 | .. note::
71 |
72 | Only URLs on the same domain can be included by default on most servers.
73 | This is not a restriction of GWSumm, rather a safety measure of the Apache,
74 | and other, web server protocols.
75 |
76 | ---------
77 | `PlotTab`
78 | ---------
79 |
80 | The `PlotTab` allows users to embed any number of images into a new tab,
81 | and choose the layout.
82 | With this type of tab, users specify images to include by giving options of the
83 | form ``X = /url/to/plot``, with ``X`` an integer, increasing for each plot,
84 | and ``/url/to/plot`` the web URL at which the plot can be found.
85 | Also, users can give the following extra options:
86 |
87 | .. autosummary::
88 | :nosignatures:
89 |
90 | ~PlotTab.foreword
91 | ~PlotTab.afterword
92 |
93 | An example `PlotTab` could be configured as follows:
94 |
95 | .. literalinclude:: ../../share/examples/matplotlib.ini
96 | :language: ini
97 |
98 | Here we have imported three examples plots from the
99 | `matplotlib `_ examples with a :attr:`~PlotTab.layout`
100 | of one plot on the top row (full size), and two plots on the second row.
101 |
102 | ---------
103 | `DataTab`
104 | ---------
105 |
106 | The workhorse of the GWSumm package, at least as used by the LIGO Scientific
107 | Collaboration is the `DataTab`. Please read this page for details on
108 | configuring one of these
109 |
110 | .. toctree::
111 |
112 | data
113 |
--------------------------------------------------------------------------------
/gwsumm/config/defaults.ini:
--------------------------------------------------------------------------------
1 | ;
2 | ; GWSumm default configuration options
3 | ;
4 | ; This file provides a set of standard options for the GWSumm command-line
5 | ; interface that users can override in their own separate INI files
6 | ;
7 | ; As a result, this file probably doesn't need to be modified very often,
8 | ; just whenever a standard is set and should be followed by default
9 | ;
10 |
11 | [calendar]
12 | start-of-week = monday
13 | start-date = 2013-07-01
14 |
15 | [channels]
16 | $(ifo(1:HPI-BS_BLRMS_Z_3_10 = unit='nm/s'
17 |
18 | [states]
19 | ; 'all' is implicitly defined as the GPS [start, stop) segment with no gaps
20 | Science = %(ifo)s:DMT-SCIENCE:1
21 | IFO Locked = %(ifo)s:DMT-UP:1
22 | PSL-ODC = $(ifo)s:PSL-ODC_SUMMARY:1
23 | IMC-ODC = $(ifo)s:IMC-ODC_SUMMARY:1
24 |
25 | [state-all]
26 | name = All
27 | description = All times
28 |
29 | [general]
30 |
31 | [html]
32 | css1 = /~%(user)s/html/bootstrap/3.0.0/css/bootstrap.min.css
33 | css2 = /~%(user)s/html/datepicker/1.2.0/css/datepicker.css
34 | css3 = /~%(user)s/html/fancybox/source/jquery.fancybox.css?v=2.1.5
35 | css4 = /~%(user)s/html/gwsummary/gwsummary.css
36 | javascript1 = /~%(user)s/html/jquery-1.10.2.min.js
37 | javascript2 = /~%(user)s/html/moment.min.js
38 | javascript3 = /~%(user)s/html/bootstrap/3.0.0/js/bootstrap.min.js
39 | javascript4 = /~%(user)s/html/datepicker/1.2.0/js/bootstrap-datepicker.js
40 | javascript5 = /~%(user)s/html/fancybox/source/jquery.fancybox.pack.js?v=2.1.5
41 | javascript6 = /~%(user)s/html/gwsummary/gwsummary.js
42 |
43 | [segment-database]
44 | url = https://segdb-er.ligo.caltech.edu
45 |
46 | [fft]
47 | ; average method
48 | method = medianmean
49 | ; PSD average length and overlap (in seconds)
50 | ;fftlength = 1
51 | ;fftstride = 0.5
52 | ; spectrogram stride
53 | ;stride = 2
54 |
55 | ; -----------------------------------------------------------------------------.
56 | ; Basic Plots
57 |
58 | [plot-spectrogram]
59 | type = 'spectrogram'
60 | format = 'amplitude'
61 | logy = True
62 | logcolor = True
63 | ylabel = 'Frequency [Hz]'
64 |
65 | [plot-median-spectrogram]
66 | type = 'spectrogram'
67 | format = 'amplitude'
68 | logy = True
69 | ylabel = 'Frequency [Hz]'
70 | ratio = median
71 | clim = 0.25,4
72 | logcolor = True
73 | colorlabel = 'Amplitude relative to median'
74 |
75 |
76 | [plot-spectrum]
77 | type = 'spectrum'
78 | format = 'amplitude'
79 | xlabel = 'Frequency [Hz]'
80 | logx = True
81 | logy = True
82 | legend-loc = 'lower left'
83 |
84 | [plot-ep-time-frequency-snr]
85 | type = triggers
86 | etg = ExcessPower
87 | ; columns
88 | x = time
89 | y = central_freq
90 | color = snr
91 | ; colour bar
92 | clim = 3,100
93 | logcolor = True
94 | colorlabel = 'Signal to noise ratio (SNR)'
95 | ; plot params
96 | edgecolor = 'none'
97 | s = 16
98 | ;size_by_log = %(color)s
99 | ;size_range = %(clim)s
100 | ylabel = 'Frequency [Hz]'
101 | logy = True
102 |
103 | [plot-ep-time-frequency-amplitude]
104 | type = triggers
105 | etg = ExcessPower
106 | ; columns
107 | x = time
108 | y = central_freq
109 | color = amplitude
110 | ; colour bar
111 | logcolor = True
112 | ; plot params
113 | edgecolor = 'none'
114 | s = 16
115 | ;size_by_log = %(color)s
116 | ylabel = 'Frequency [Hz]'
117 | logy = True
118 |
119 | [plot-omicron-time-frequency-snr]
120 | type = triggers
121 | etg = Omicron
122 | ; columns
123 | x = time
124 | y = peak_frequency
125 | color = snr
126 | ; colour bar
127 | clim = 3,100
128 | logcolor = True
129 | colorlabel = 'Signal to noise ratio (SNR)'
130 | ; plot params
131 | edgecolor = 'none'
132 | s = 16
133 | ;size_by_log = %(color)s
134 | ylabel = 'Frequency [Hz]'
135 | logy = True
136 |
137 | [plot-omicron-time-frequency-amplitude]
138 | type = triggers
139 | etg = Omicron
140 | ; columns
141 | x = time
142 | y = peak_frequency
143 | color = amplitude
144 | ; colour bar
145 | logcolor = True
146 | ; plot params
147 | edgecolor = 'none'
148 | s = 16
149 | ;size_by_log = %(color)s
150 | ylabel = 'Frequency [Hz]'
151 | logy = True
152 |
--------------------------------------------------------------------------------
/gwsumm/tabs/misc.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013-2016)
3 | #
4 | # This file is part of GWSumm
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see
18 |
19 | """This module defines some utility `Tab` subclasses, including HTTP
20 | error handlers.
21 | """
22 |
23 | from MarkupPy import markup
24 |
25 | from .registry import (get_tab, register_tab)
26 |
27 | from gwdetchar.io import html
28 |
29 | __author__ = 'Duncan Macleod '
30 | __all__ = ['AboutTab', 'Error404Tab']
31 |
32 | Tab = get_tab('basic')
33 |
34 |
35 | # -- About --------------------------------------------------------------------
36 |
37 | class AboutTab(Tab):
38 | """Page describing how the containing HTML pages were generated
39 | """
40 | type = 'about'
41 |
42 | def __init__(self, name='About', **kwargs):
43 | super(AboutTab, self).__init__(name, **kwargs)
44 |
45 | def write_html(self, config=list(), prog=None, **kwargs):
46 | return super(AboutTab, self).write_html(
47 | html.about_this_page(config=config, prog=prog), **kwargs)
48 |
49 |
50 | register_tab(AboutTab)
51 |
52 |
53 | # -- HTTP errors --------------------------------------------------------------
54 |
55 | class Error404Tab(Tab):
56 | """Custom HTTP 404 error page
57 | """
58 | type = '404'
59 |
60 | def __init__(self, name='404', **kwargs):
61 | super(Error404Tab, self).__init__(name, **kwargs)
62 |
63 | def write_html(self, config=list(), top=None, **kwargs):
64 | if top is None:
65 | top = kwargs.get('base', self.path)
66 | kwargs.setdefault('title', '404: Page not found')
67 | page = markup.page()
68 | page.div(class_='alert alert-danger text-justify shadow-sm')
69 | page.p()
70 | page.strong("The page you are looking for does not exist.")
71 | page.p.close()
72 | page.p("This could be because the times for which you are looking "
73 | "were never processed (or have not happened yet), or because "
74 | "no page exists for the specific data products you want. "
75 | "Either way, if you think this is in error, please contact "
76 | "the DetChar group.")
78 | page.p("Otherwise, you might be interested in one of the following:")
79 | page.div(style="padding-top: 10px;")
80 | page.a("Take me back", role="button", class_="btn btn-lg btn-info",
81 | title="Back", href="javascript:history.back()")
82 | page.a("Take me up one level", role="button",
83 | class_="btn btn-lg btn-warning", title="Up",
84 | href="javascript:linkUp()")
85 | page.a("Take me to the top level", role="button",
86 | class_="btn btn-lg btn-success", title="Top", href=top)
87 | page.div.close()
88 | page.div.close() # alert alert-danger
89 | page.script("""
90 | function linkUp() {
91 | var url = window.location.href;
92 | if (url.substr(-1) == '/') url = url.substr(0, url.length - 2);
93 | url = url.split('/');
94 | url.pop();
95 | window.location = url.join('/');
96 | }""", type="text/javascript")
97 | return super(Error404Tab, self).write_html(page, **kwargs)
98 |
99 |
100 | register_tab(Error404Tab)
101 |
--------------------------------------------------------------------------------
/gwsumm/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Tests for `gwsumm.utils`
20 |
21 | """
22 |
23 | import time
24 | import re
25 | import sys
26 | from math import pi
27 |
28 | import pytest
29 |
30 | from .. import (utils, globalv)
31 |
32 | __author__ = 'Duncan Macleod '
33 |
34 |
35 | def test_elapsed_time():
36 | e = time.time() - globalv.START
37 | assert utils.elapsed_time() - e < .1
38 |
39 |
40 | def test_vprint(capsys):
41 | # test non-verbose
42 | globalv.VERBOSE = False
43 | utils.vprint('anything', stream=sys.stdout)
44 | out, err = capsys.readouterr()
45 | assert out == ''
46 |
47 | # test verbose
48 | globalv.VERBOSE = True
49 | utils.vprint('anything', stream=sys.stdout)
50 | out, err = capsys.readouterr()
51 | assert out == 'anything'
52 |
53 | # test profiled
54 | globalv.PROFILE = True
55 | utils.vprint('anything\n', stream=sys.stdout)
56 | out, err = capsys.readouterr()
57 | assert re.match(r'\Aanything \(\d+\.\d\d\)\n\Z', out) is not None
58 |
59 |
60 | def test_nat_sorted():
61 | # sorted strings numerically
62 | assert utils.nat_sorted(['1', '10', '2', 'a', 'B']) == [
63 | '1', '2', '10', 'B', 'a']
64 |
65 |
66 | @pytest.mark.parametrize('chan, mask', [
67 | ('L1:TEST-ODC_CHANNEL_OUT_DQ', 'L1:TEST-ODC_CHANNEL_BITMASK'),
68 | ('L1:TEST-ODC_CHANNEL_OUTMON', 'L1:TEST-ODC_CHANNEL_BITMASK'),
69 | ('L1:TEST-ODC_CHANNEL_LATCH', 'L1:TEST-ODC_CHANNEL_BITMASK'),
70 | ('L1:TEST-CHANNEL', 'L1:TEST-CHANNEL')
71 | ])
72 | def test_get_odc_bitmask(chan, mask):
73 | assert utils.get_odc_bitmask(chan) == mask
74 |
75 |
76 | @pytest.mark.parametrize('value, out', [
77 | ('my random content', 'my random content'),
78 | ('1', 1),
79 | ('1.', 1.),
80 | ('1,', (1,)),
81 | ('1,2,\'test\',4', (1, 2, 'test', 4)),
82 | ('[], [0], 1/(2*pi)', ([], [0], 1/(2*pi))),
83 | ('lambda x: x**2', lambda x: x ** 2),
84 | (pytest, pytest),
85 | ])
86 | def test_safe_eval(value, out):
87 | evalue = utils.safe_eval(value)
88 | assert type(evalue) is type(out)
89 |
90 | if not isinstance(value, str):
91 | assert evalue is out
92 | elif callable(out):
93 | assert evalue(4) == out(4)
94 | else:
95 | assert evalue == out
96 |
97 |
98 | def test_safe_eval_2():
99 | # test unsafe
100 | with pytest.raises(ValueError) as exc:
101 | utils.safe_eval("os.remove('file-that-doesnt-exist')")
102 | assert str(exc.value).startswith('Will not evaluate string containing')
103 |
104 | with pytest.raises(ValueError):
105 | utils.safe_eval("lambda x: shutil.remove('file-that-doesnt-exist')")
106 |
107 | # test locals or globals
108 | assert utils.safe_eval('test', globals_={'test': 4}) == 4
109 | assert utils.safe_eval('type(self)',
110 | locals_={'self': globalv}) == type(globalv)
111 |
112 |
113 | @pytest.mark.parametrize('ifo, host', [
114 | ('G1', 'host.atlas.aei.uni-hannover.de'),
115 | ('H1', 'host.ligo-wa.caltech.edu'),
116 | ('L1', 'host.ligo-la.caltech.edu'),
117 | ('V1', 'host.virgo.ego.it'),
118 | ])
119 | def test_get_default_ifo(ifo, host):
120 | assert utils.get_default_ifo(host) == ifo
121 |
122 | with pytest.raises(ValueError):
123 | utils.get_default_ifo('host.ligo.caltech.edu')
124 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # -----------------------
2 | #
3 | # Run a full build-and-test from the git repo
4 | # using a combination of conda and pip to install
5 | # all optional dependencies.
6 | #
7 | # This is the 'full' test suite.
8 | #
9 | # -----------------------
10 |
11 | name: Build and test
12 |
13 | on:
14 | push:
15 | branches:
16 | - main
17 | - master
18 | - release/**
19 | pull_request:
20 | branches:
21 | - main
22 | - master
23 | - release/**
24 |
25 | concurrency:
26 | group: ${{ github.workflow }}-${{ github.ref }}
27 | cancel-in-progress: true
28 |
29 | jobs:
30 | conda:
31 | name: Python ${{ matrix.python-version }} (${{ matrix.os }})
32 |
33 | strategy:
34 | fail-fast: false
35 | matrix:
36 | os:
37 | - macOS
38 | - Ubuntu
39 | python-version:
40 | - "3.10"
41 | - "3.11"
42 | runs-on: ${{ matrix.os }}-latest
43 |
44 | # this is needed for conda environments to activate automatically
45 | defaults:
46 | run:
47 | shell: bash -el {0}
48 |
49 | steps:
50 | - name: Get source code
51 | uses: actions/checkout@v5
52 | with:
53 | fetch-depth: 0
54 |
55 | - name: Cache conda packages
56 | uses: actions/cache@v4
57 | env:
58 | # increment to reset cache
59 | CACHE_NUMBER: 0
60 | with:
61 | path: ~/conda_pkgs_dir
62 | key: ${{ runner.os }}-conda-${{ matrix.python-version }}-${{ env.CACHE_NUMBER }}
63 | restore-keys: ${{ runner.os }}-conda-${{ matrix.python-version }}-
64 |
65 | - name: Configure conda
66 | uses: conda-incubator/setup-miniconda@v3
67 | with:
68 | auto-update-conda: true
69 | miniforge-version: latest
70 | python-version: ${{ matrix.python-version }}
71 |
72 | - name: Conda info
73 | run: conda info --all
74 |
75 | - name: Install dependencies
76 | run: |
77 | # parse requirements to install as much as possible with conda
78 | conda create --name pip2conda pip2conda
79 | conda run -n pip2conda pip2conda \
80 | --all \
81 | --output environment.txt \
82 | --python-version ${{ matrix.python-version }}
83 | echo "-----------------"
84 | cat environment.txt
85 | echo "-----------------"
86 | conda install --quiet --yes --name test --file environment.txt
87 |
88 | - name: Install GWSumm
89 | run: python -m pip install . --no-build-isolation -vv
90 |
91 | - name: Package list
92 | run: conda list --name test
93 |
94 | - name: Run test suite
95 | run: python -m pytest -ra --color yes --cov gwsumm --pyargs gwsumm --cov-report=xml --junitxml=pytest.xml
96 |
97 | - name: Test command-line interfaces
98 | run: |
99 | python -m coverage run --append --source gwsumm -m gwsumm --help
100 | python -m coverage run --append --source gwsumm -m gwsumm day --help
101 | python -m coverage run --append --source gwsumm -m gwsumm week --help
102 | python -m coverage run --append --source gwsumm -m gwsumm month --help
103 | python -m coverage run --append --source gwsumm -m gwsumm gps --help
104 | python -m coverage run --append --source gwsumm -m gwsumm.batch --help
105 | python -m coverage run --append --source gwsumm -m gwsumm.plot.triggers --help
106 | python -m coverage run --append --source gwsumm -m gwsumm.plot.guardian --help
107 |
108 | - name: Coverage report
109 | run: python -m coverage report --show-missing
110 |
111 | - name: Publish coverage to Codecov
112 | uses: codecov/codecov-action@v5.5.1
113 | with:
114 | files: coverage.xml
115 | flags: ${{ runner.os }}-python${{ matrix.python-version }}
116 | token: ${{ secrets.CODECOV_TOKEN }}
117 |
118 | - name: Upload test results
119 | if: always()
120 | uses: actions/upload-artifact@v5
121 | with:
122 | name: pytest-conda-${{ matrix.os }}-${{ matrix.python-version }}
123 | path: pytest.xml
124 |
--------------------------------------------------------------------------------
/gwsumm/mode.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Duncan Macleod (2013)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Job modes
20 | """
21 |
22 | import os.path
23 | from enum import (Enum, unique)
24 |
25 | from . import globalv
26 |
27 | __author__ = 'Duncan Macleod '
28 |
29 |
30 | # -- operating Mode -----------------------------------------------------------
31 |
32 | # https://docs.python.org/3/library/enum.html#orderedenum
33 | class OrderedEnum(Enum):
34 | def __ge__(self, other):
35 | if self.__class__ is other.__class__:
36 | return self.value >= other.value
37 | return NotImplemented
38 |
39 | def __gt__(self, other):
40 | if self.__class__ is other.__class__:
41 | return self.value > other.value
42 | return NotImplemented
43 |
44 | def __le__(self, other):
45 | if self.__class__ is other.__class__:
46 | return self.value <= other.value
47 | return NotImplemented
48 |
49 | def __lt__(self, other):
50 | if self.__class__ is other.__class__:
51 | return self.value < other.value
52 | return NotImplemented
53 |
54 |
55 | # set mode enum
56 | @unique
57 | class Mode(OrderedEnum):
58 | """Enumeration of valid processing 'modes'
59 |
60 | Each mode provides an association with a particular GPS interval
61 | """
62 | # no GPS associations
63 | static = 0
64 | # central GPS time (with duration)
65 | event = 1
66 | # arbitrary GPS [start, end) interval
67 | gps = 2
68 | # calendar epochs
69 | day = 10
70 | week = 11
71 | month = 12
72 | year = 13
73 |
74 | def dir_format(self):
75 | if self == Mode.day:
76 | return os.path.join('day', '%Y%m%d')
77 | elif self == Mode.week:
78 | return os.path.join('week', '%Y%m%d')
79 | elif self == Mode.month:
80 | return os.path.join('month', '%Y%m')
81 | elif self == Mode.year:
82 | return os.path.join('year', '%Y')
83 | raise ValueError("Cannot format base for Mode %s" % self)
84 |
85 | def is_calendar(self):
86 | if self >= Mode.day:
87 | return True
88 | return False
89 |
90 |
91 | # -- Mode accessors -----------------------------------------------------------
92 |
93 | def get_mode(m=None):
94 | """Return the enum for the given mode, defaults to the current mode.
95 | """
96 | if m is None:
97 | m = globalv.MODE
98 | if isinstance(m, (int, Enum)):
99 | return Mode(m)
100 | else:
101 | try:
102 | return Mode[str(m).lower()]
103 | except KeyError:
104 | raise ValueError("%s is not a valid Mode" % m)
105 |
106 |
107 | def set_mode(m):
108 | """Set the current mode.
109 | """
110 | if isinstance(m, int):
111 | m = Mode(m)
112 | elif not isinstance(m, Mode):
113 | try:
114 | m = Mode[str(m).lower()]
115 | except KeyError:
116 | raise ValueError("%s is not a valid Mode" % m)
117 | globalv.MODE = m.value
118 |
119 |
120 | # -- Mode utilities -----------------------------------------------------------
121 |
122 | def get_base(date, mode=None):
123 | """Determine the correct base attribute for the given date and mode.
124 |
125 | Parameters
126 | ----------
127 | date : :class:`datetime.datetime`
128 | formatted date
129 | mode : `int`, `str`
130 | enumerated interger code (or name) for the required mode
131 |
132 | Returns
133 | -------
134 | base : `str`
135 | the recommended base URL to have a correctly linked calendar
136 | """
137 | mode = get_mode(mode)
138 | return date.strftime(mode.dir_format())
139 |
--------------------------------------------------------------------------------
/gwsumm/plot/triggers/tests/test_main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2020)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Tests for the `gwsumm.plot.triggers` command-line interface
20 | """
21 |
22 | import os
23 | import pytest
24 | import shutil
25 |
26 | from unittest import mock
27 |
28 | from gwpy.segments import (
29 | Segment,
30 | SegmentList,
31 | DataQualityFlag,
32 | )
33 |
34 | from .... import globalv
35 | from .. import __main__ as triggers_cli
36 |
37 | __author__ = 'Alex Urban '
38 |
39 | # -- test configuration
40 |
41 | CHANNEL = "H1:GDS-CALIB_STRAIN"
42 |
43 | # -- test data
44 |
45 | LOCK = DataQualityFlag(
46 | name="H1:DMT-GRD_ISC_LOCK_NOMINAL:1",
47 | active=SegmentList([Segment(2, 2048)]),
48 | known=SegmentList([Segment(0, 3600)]),
49 | )
50 |
51 |
52 | # -- cli tests ----------------------------------------------------------------
53 |
54 | @mock.patch(
55 | 'gwpy.segments.DataQualityFlag.query_dqsegdb',
56 | return_value=LOCK,
57 | )
58 | def test_main(dqflag, tmpdir, caplog):
59 | outdir = str(tmpdir)
60 | plot = os.path.join(outdir, "triggers.png")
61 | args = [
62 | CHANNEL,
63 | '0', '3600',
64 | '--snr', '1',
65 | '--state', LOCK.name,
66 | '--output-file', plot,
67 | ]
68 | # test output
69 | with pytest.warns(UserWarning) as record:
70 | triggers_cli.main(args)
71 | assert os.path.exists(plot)
72 | assert len(os.listdir(outdir)) == 1
73 | assert 'Read 0 events' in caplog.text
74 | assert "0 events in state '{}'".format(LOCK.name) in caplog.text
75 | assert '0 events remaining with snr >= 1.0' in caplog.text
76 | assert 'Plot saved to {}'.format(plot) in caplog.text
77 | # test the `UserWarning`
78 | # FIXME: once MatplotlibDeprecationWarning about colormaps is fixed,
79 | # assert that this UserWarning is the **only** warning
80 | assert (record[0].message.args[0] ==
81 | "Caught ValueError: No channel-level directory found at "
82 | "/home/detchar/triggers/*/H1/GDS-CALIB_STRAIN_Omicron. Either "
83 | "the channel name or ETG names are wrong, or this channel is not "
84 | "configured for this ETG.")
85 | # clean up
86 | globalv.TRIGGERS = {}
87 | shutil.rmtree(outdir, ignore_errors=True)
88 |
89 |
90 | def test_main_with_cache_and_tiles(tmpdir, caplog):
91 | outdir = str(tmpdir)
92 | cache = os.path.join(outdir, "empty.cache")
93 | plot = os.path.join(outdir, "triggers.png")
94 | args = [
95 | CHANNEL,
96 | '0', '3600',
97 | '--cache-file', cache,
98 | '--snr', '1',
99 | '--plot-params', 'legend-loc="upper right"',
100 | '--tiles',
101 | '--output-file', plot,
102 | ]
103 | # write an empty cache file
104 | with open(cache, 'w') as f:
105 | f.write("")
106 | # test output
107 | triggers_cli.main(args)
108 | assert os.path.exists(plot)
109 | assert len(os.listdir(outdir)) == 2 # 1 input, 1 output
110 | assert 'Read cache of 0 files' in caplog.text
111 | assert 'Read 0 events' in caplog.text
112 | assert '0 events remaining with snr >= 1.0' in caplog.text
113 | assert 'Plot saved to {}'.format(plot) in caplog.text
114 | # clean up
115 | globalv.TRIGGERS = {}
116 | shutil.rmtree(outdir, ignore_errors=True)
117 |
118 |
119 | def test_main_invalid_columns(capsys):
120 | args = [
121 | CHANNEL,
122 | '0', '3600',
123 | '--columns', 'invalid',
124 | ]
125 | # test output
126 | with pytest.raises(SystemExit):
127 | triggers_cli.main(args)
128 | (_, err) = capsys.readouterr()
129 | assert err.endswith("--columns must receive at least two columns, got 1\n")
130 |
--------------------------------------------------------------------------------
/gwsumm/html/tests/test_bootstrap.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright (C) Alex Urban (2019)
3 | #
4 | # This file is part of GWSumm.
5 | #
6 | # GWSumm is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # GWSumm is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with GWSumm. If not, see .
18 |
19 | """Unit tests for gwsumm.html.bootstrap
20 | """
21 |
22 | __author__ = 'Alex Urban '
23 |
24 | import pytest
25 | from datetime import datetime
26 |
27 | from gwdetchar.utils.utils import parse_html
28 |
29 | from .. import bootstrap
30 |
31 | # global variables
32 | DATE = datetime.strptime('20140410', '%Y%m%d')
33 | BACKWARD = '«'
34 | CAL = ('{}')
37 | FORWARD = '»'
38 |
39 |
40 | # test utilities
41 |
42 | def test_banner():
43 | banner = bootstrap.banner('Test', subtitle='Subtest')
44 | assert parse_html(str(banner)) == parse_html(
45 | '