.. highlight:: rst .. _eqevents: ######## eqevents ######## **Static event website generator** Description =========== EQEvents is a static website generator for SeisComP XML event parameters. It can run in a spooler mode or process single files. It uses a template engine to create the HTML files. It features an index page and event detail page. More information can be added though :ref:`sec-plugins`. EQEvents is meant to run as output service for GDS (gempa dissemination server). .. _sec-plugins: Plugins ======== The different EQEvents features are implemented by various plugins which may be activated by adding a plugin to the list of :confval:`processingPlugins`, e.g., ``browser,gds,gis,leaflet,rss,search,shakemap``. The following plugins exist: .. csv-table:: :header: "Name", "Description" :widths: 15,100 ":ref:`browser `","Adds a calendar based event browser." "bulletins","Adds bulletins containing additional information generated by an external source to an event detail page. This plugin is rarely used and requires customization." "gds","Updates gempa dissemination server (GDS) database with event processing information. It is recommended to activate this plugin if EQEvents is triggered by GDS." ":ref:`gis `","Generates a detailed event map using gempa image server (GIS)." ":ref:`leaflet `","Generates a dynamic map showing the events of the current event table on the index page, in the event browser and the event search." "notification","Notifies connected clients about event and bulletin updates through a socket io interface. This plugin is rarely used and requires customization." ":ref:`plots `","Adds distance, azimuth, and polar plot to event detail page." "rss","Creates a RSS feet users may subscribe to." "search","Adds an event search. **Note**: The search function is implemented as a WSGI script and needs additional installation steps, described in :ref:`sec-dynamic`." "feedback","Adds an email feedback form. **Note**: The feedback function is implemented as a WSGI script and needs additional installation steps, described in :ref:`sec-dynamic`." "shakemap","Generates a shake map using gempa's autosigma module." System dependencies which may be required by the plugins: * python-matplotlib * python-jinja2 Processing ========== Offline ------- To process a single XML file run EQEvents and pass it an XML file: .. code:: sh eqevents -i event.xml --debug Spooler ------- In contrast to a typical GDS service EQEvents is not monitored by GDS but runs as a SeisComP module. The :file:`$SEISCOMP_ROOT/etc/gds.cfg` only defines a spool directory but no spooler nor filter command. The same directory needs to be configured in :file:`$SEISCOMP_ROOT/etc/eqevents.cfg`. If an event matches the GDS dissemination criteria or an operator manually disseminated an event the GDS will place a ``*.content`` file containing the SeisComP XML and a ``*.address`` file into the spool directory where they are found by EQEvents. Processing details: - The file name of the address and content file must match. - Files with the same name but different extension are processed as auxiliary files. - Each file found is deleted either after processing or due to wrong the file name format. - The processing starts when the content file is found. - The content file contains a match information according to the GDS dissemination rules, see :confval:`ignoreMatch`. - If multiple files for the same event are found then only the newest one is processed and the others are deleted. - The file name format is ``---[-FILTER].`` .. csv-table:: :header: "Name", "Description" :widths: 15,100 "``SECONDS.MILLIS``","Time of dissemination" "``GDSID``","ID of the dissemination procedure used to report back the dissemination status" "``EVENTID``","Event ID used to skip older files of same event" "``SERVICE``","GDS service name, unused" "``FILTER``","GDS custom filter name, unused" "``EXT``","File name extension" Reprocessing ------------ If a template was changed or new plugins have been added all static pages need to be reprocessed. As a precondition for this the event XML data must be available, see :confval:`backupXML`. To initiate reprocessing stop EQEvents and run it using the reprocess command-line option. .. code:: sh eqevents --reprocess Templates ========= EQEvents uses templates in a well defined structure. The path to the template directory can be configured with :confval:`templateDirectory`. The structure is as follows: ====================== ======================================================= base.html Base template inherited by almost all templates feed.rss RSS web feed index.html Index page leaflet-map.html Leaflet map, used in: index, browser, results results.html Search result page search.html Search parameter page table.html Event list table, used in index, browser, results browser/day.html Browser day page showing all events of a particular day browser/index.html Browser index page showing all years browser/month.html Browser month page showing all days browser/year.html Browser year page showing all months event/arrivals.html Event details arrival list event/base.html Event details base template event/overview.html Event details overview page event/quality.html Event details quality plots ====================== ======================================================= Templates are processed by `Python Jinja `_ and follow its documented syntax. Each template receives a context with several variables defined. .. py:data:: ep The EventParameters structure parsed from XML converted to a Python object. Attributes are accessible following the SeisComP ML schema names. They are mapped directly from the XML tag/attribute names. Below a simple example how to access all picks in the EventParameters object in template syntax: .. code:: jinja {% for p in ep.pick %} {{ p.waveformID.networkCode }}.{{ p.waveformID.stationCode }} {{ p.time.value|qmldate|date }} {% endfor %} The special filter qmldate is a special filter added by EQEvents to convert a date string from SeisComP ML to an Python datetime object. That object can then be further processed with the Jinja date filter. .. py:data:: event The event object currently processed. .. py:data:: origin The preferred origin of the currently processed event. .. py:data:: focalMechanism The preferred focal mechanism of the currently processed event. .. py:data:: magnitude The preferred magnitude of the currently processed event. .. py:data:: POOL The POOL that maps a publicID to an object. .. code:: jinja {% for arr in origin.arrival %} {% p = POOL[arr.pickID] %} {% if p %}
{{ p.time.value|qmldate|data }}
{% else %}
Referenced pick {{ arr.pickID }} not available
{% endif %} {% endfor %} This makes it easier to access referenced objects. Otherwise the template code must iterate over all pick objects and compare the publicID in combination with a linear search which is quite slow for larger events. .. py:data:: ID The current event ID after cleaning invalid characters. That ID is used as event folder name and all event ID depending filenames. .. py:data:: ROOT_URL The relative path to the site index file. This can be different according to the templates rendered. If paths are required this variable should be prepended. .. code:: jinja Home .. py:data:: STATIC_URL The path to the static files according to :confval:`staticURL`. .. code:: jinja .. py:data:: filename The path and filename of the current XML file being processed. Webpages ======== The following images show the different generated pages with the default templates. .. _browser-map: .. figure:: media/eqevents/browser-map.png :width: 16cm Index page with event map .. _browser-index: .. figure:: media/eqevents/index.png :width: 16cm Index page .. _browser-overview: .. figure:: media/eqevents/event-overview.png :width: 16cm Event overview .. figure:: media/eqevents/event-arrivals.png :width: 16cm Event arrivals .. _browser-quality: .. figure:: media/eqevents/event-quality.png :width: 16cm Event quality .. _browser-calendar: .. figure:: media/eqevents/browser.png :width: 16cm Browser index .. figure:: media/eqevents/browser-year.png :width: 16cm Browser page for one year .. figure:: media/eqevents/browser-month.png :width: 16cm Browser page for one month .. figure:: media/eqevents/browser-day.png :width: 16cm Browser page for one day .. figure:: media/eqevents/search.png :width: 16cm Search page Web Server Integration ====================== EQEvents has been deployed using NGINX or Apache. If unsure which web server to use then select NGINX because this is the most lightweight setup. Since EQEvents by default only consists of static pages the web server integration is straight forward. You may either decide to 1. generate the pages directly into the web server ``html`` directory by setting :confval:`outputDirectory` to e.g., :file:`/usr/share/nginx/html/eqevents` (NGINX) or :file:`/var/www/html/eqevents` (Apache) or 2. you use a softlink to point from inside the ``html`` directory to the default EQEvents output directory: .. code:: sh ln -s /home/sysop/seiscomp/var/lib/eqevents/pages /usr/share/nginx/html/eqevents In case of (1.) make sure that the EQEvents system user, probably ``sysop``, is allowed to write to the ``html`` directory. In case of (2.) the ``x`` access right must be set for the web server group and for all directories on the path. Also security modules like ``SELinux`` might prevent access of the web server process to the user directory. If this is the case use following command to grant those access rights: .. code:: sh chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/global.cfg chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/eqevents.cfg chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/defaults/global.cfg chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/defaults/eqevents.cfg chcon -Rv --type=httpd_sys_content_t /home/sysop/seiscomp/var/lib/eqevents/pages chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/var/lib/eqevents/eqevents.db chcon -Rv --type=httpd_sys_content_t /home/sysop/seiscomp/share/eqevents/templates chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/share/eqevents/search-wsgi.py chcon -Rv --type=texrel_shlib_ti /home/sysop/seiscomp/lib #setsebool -P httpd_read_user_content 1 .. _sec-dynamic: Dynamic Content - The Search and Feedback Plugins ------------------------------------------------- The search and the feedback plugin require a WSGI module to generate dynamic content. The installation depends on the web server you selected and is covered different section - :ref:`sec-dynamic-nginx` - :ref:`sec-dynamic-apache` .. _sec-dynamic-nginx: NGINX (Gunicorn) ---------------- 1. Add the search or feedback plugin to :confval:`processingPlugins`. 2. Install ``gunicorn`` .. code-block:: sh python3 -m pip install --user gunicorn 3. Copy the relevant parts of the ``server`` and ``upstream`` directives from :file:`$SEISCOMP_ROOT/share/eqevents/examples/nginx-gunicorn.conf` to your - :file:`/etc/nginx/nginx.conf` or - :file:`/etc/nginx/sites-available/YOUR-VHOST.conf` in case you are using a virtual host setup 4. Copy the Gunicorn configuration file to the EQEvents base directory: .. code-block:: sh cd $SEISCOMP_ROOT/share/eqevents/ cp examples/search-gunicorn.py . #cp examples/feedback-gunicorn.py . Adjust the configuration, e.g., logging level, path and format if necessary. 5. Copy the Gunicorn socket and service systemd configuration: .. code-block:: sh sudo cp $SEISCOMP_ROOT/share/gds/web/examples/gunicorn/gunicorn-eqevents-search.* /etc/systemd/system #sudo cp $SEISCOMP_ROOT/share/gds/web/examples/gunicorn/gunicorn-eqevents-feedback.* /etc/systemd/system Adjust the ``user``, ``group``, or ``WorkingDirectory`` variables if necessary. Change ``type`` depending on your Gunicorn version. Then enable and start the service: .. code-block:: sh sudo systemctl enable gunicorn-eqevents-search sudo systemctl start gunicorn-eqevents-search #sudo systemctl enable gunicorn-eqevents-feedback #sudo systemctl start gunicorn-eqevents-feedback 6. Test the NGINX configuration: .. code-block:: sh sudo nginx -t 7. Enable and start NGINX: .. code-block:: sh sudo systemctl enable nginx sudo systemctl start nginx Trouble shooting ................ - Check if the socket file :file:`/var/run/gunicorn-eqevents-{{search,feedback}}.sock` was created and ensure that the NGINX user (``nginx`` or ``www-data``) has write access to this file - Make a request directly at the socket: .. code-block:: sh sudo -u nginx curl --unix-socket /var/run/gunicorn-eqevents-search.sock http - By default Gunicorn logs to :file:`$HOME/.seiscomp/log/eqevents-web/{{search,feedback}}-{{access,error}}.log` - Reload gunicorn-eqvents-{search,feedback} service after modifications to your :file:`$SEISCOMP_ROOT/share/gds/{{search,feedback}}-gunicorn.py` - Reload systemd configuration after modifications to systemd service and socket files: ``sudo systemctl daemon-reload`` - Reload the NGINX server configuration: ``sudo nginx -t && sudo systemctl reload nginx`` .. _sec-dynamic-apache: Apache (WSGI) ------------- 1. Add the search or feedback plugin to :confval:`processingPlugins`. 2. Install and enable ``mod_wsgi`` .. code:: sh a2enmod wsgi 3. Configure WSGI handler, e.g., in :file:`/etc/apache2/sites-available/eqevents.conf` .. code:: apache WSGIScriptAlias /eqevents/search /home/sysop/seiscomp/share/eqevents/search-wsgi.py #WSGIScriptAlias /eqevents/search /home/sysop/seiscomp/share/eqevents/feedback-wsgi.py WSGIPythonPath /home/sysop/seiscomp/lib/python WSGIDaemonProcess eqevents processes=2 threads=1 python-path=/home/sysop/seiscomp/lib/python WSGIProcessGroup eqevents 4. Configure access to static content (optional, same file as above) .. code:: apache Options -Indexes SetHandler None Require all granted 5. Set environment variables Ubuntu: :file:`/etc/apache2/envvars` .. code:: sh export LD_LIBRARY_PATH=/home/sysop/seiscomp/lib:$LD_LIBRARY_PATH export SEISCOMP_ROOT=/home/sysop/seiscomp RHEL: :file:`/etc/sysconfig/httpd` .. code:: sh LD_LIBRARY_PATH=/home/sysop/seiscomp/lib:$LD_LIBRARY_PATH SEISCOMP_ROOT=/home/sysop/seiscomp 6. Enable site .. code:: sh a2ensite eqevents 7. Restart Apache Module Configuration ==================== | :file:`etc/defaults/global.cfg` | :file:`etc/defaults/eqevents.cfg` | :file:`etc/global.cfg` | :file:`etc/eqevents.cfg` | :file:`~/.seiscomp/global.cfg` | :file:`~/.seiscomp/eqevents.cfg` eqevents inherits :ref:`global options`. .. note:: Modules/plugins may require a license file. The default path to license files is :file:`@DATADIR@/licenses/` which can be overridden by global configuration of the parameter :confval:`gempa.licensePath`. Example: :: gempa.licensePath = @CONFIGDIR@/licenses .. confval:: database Default: ``@ROOTDIR@/var/lib/eqevents/eqevents.db`` Type: *path* Path to the sqlite3 database file. .. confval:: templateDirectory Default: ``@DATADIR@/eqevents/templates/default`` Type: *path* Directory to read templates from. .. confval:: outputDirectory Default: ``@ROOTDIR@/var/lib/eqevents/pages`` Type: *path* Target directory for generated web pages. .. confval:: spoolerDirectory Default: ``@ROOTDIR@/var/lib/eqevents/spool`` Type: *path* Spooler directory to read content files from. .. confval:: spoolerInterval Default: ``10`` Unit: *s* Type: *int* Polling interval in seconds the spooler will scan the service directory for new files. .. confval:: backupXML Default: ``false`` Type: *boolean* Defines whether to backup the input XML in the output HTML directory per event as [id].xml. The XML backup allows easy reprocessing of the Web page in case of template changes, see '\-\-reprocess' command line option. .. confval:: staticURL Default: ``static/`` Type: *string* URL prefix for static files used in the templates. Can be used as {{ STATIC_URL }} in templates. .. confval:: enableArrivals Default: ``true`` Type: *boolean* Enables arrival output as part of the event detail page. .. confval:: numberOfIndexEvents Default: ``50`` Type: *int* Number of events for the index page. .. confval:: numberOfIndexDays Type: *int* Events of this number of past days for the index page. If this parameter is not configured then numberOfIndexEvents is in effect. If this parameter is configured then it takes precedence over numberOfIndexEvents. .. confval:: maxDays Default: ``0`` Type: *int* Maximum number of days to keep events for. Older events are removed. A value of 0 or below means unlimited. .. confval:: ignoreType Default: ``false`` Type: *boolean* An event might be marked as fake event using the event type 'not existing' or 'other'. Set this parameter to 'true' to process fake events respectively to prevent them from being deleted. .. confval:: ignoreMatch Default: ``false`` Type: *boolean* The GDS uses the 'match' flag to signal whether an event solution fulfilled the dissemination criteria. E.g. an updated event revision might correct the epicenter to a point outside the area of interest. Set this parameter to 'true' to process not matching events respectively to prevent them from being deleted. .. confval:: processingPlugins Default: ``browser, plots`` Type: *list:string* List of eqevents processing plugins. .. confval:: showAuthor Default: ``false`` Type: *boolean* Include origin author information in generated web pages. .. note:: **filter.\*** *Parameters which filter the event data processed by the* *various plugins.* .. confval:: filter.minArrivalWeight Type: *double* Hides arrivals having a weight smaller than the configured threshold. To remove unused phases set a value of >0. .. note:: **gds.\*** *Configuration parameters of the GDS plugin to generate gds* *reports. To use that plugin, add 'gds' to the list* *of processing plugins.* .. confval:: gds.db Type: *string* GDS database URL used to report back the processing state, e.g. 'mysql:\/\/user:pass\@localhost\/gds'. .. note:: **gis.\*** *Configuration parameters of the GIS plugin to generate map* *files. To use that plugin, add 'gis' to the list* *of processing plugins.* .. confval:: gis.url Default: ``http://localhost:20001`` Type: *string* URL of the GIS RESTful API. .. confval:: gis.timeout Default: ``10`` Type: *int* Defines the timeout for GIS requests. If the timeout expires and GIS did not return an empty image is generated. .. confval:: gis.post Default: ``false`` Type: *boolean* By default the image is requested from the GIS using the HTTP\-GET mothod along with key event parameters. If set to 'true' the HTTP\-POST method is used instead to transfer the whole event XML. This allows the GIS to render an image with even more information, like used stations or moment tensor solutions. .. note:: **rss.\*** *Configuration parameters of the RSS plugin. To use that* *plugin, add 'rss' to the list of processing* *plugins.* .. confval:: rss.days Default: ``1`` Type: *int* Number of \(last\) days to generate the RSS feed for. .. confval:: rss.title Default: ``EQ Events RSS Channel - last ${days}`` Type: *string* Title of the RSS feed. The variable '\${days}' is replaced by the string containing the number of days configured in the 'rss.days' parameter, e.g. '2 days'. .. confval:: rss.desc Default: ``Real-time earthquake locations`` Type: *string* Description of RSS feed that is copied to the description tag of the RSS XML. .. confval:: rss.link Default: ``https://localhost`` Type: *string* URL prefix for each event detail page. EQEvents just deals with relative paths but to transform an RSS item link into an absolute URL this prefix is required. .. note:: **notification.\*** *Configuration parameters of the NOTIFICATION plugin to send* *notifications to a socket.io-server. To use that plugin, add* *'notification' to the list of processing plugins.* .. confval:: notification.server Default: ``localhost`` Type: *string* Hostname of the Socket.IO server. .. confval:: notification.port Default: ``8000`` Type: *int* Port of the Socket.IO server. .. confval:: notification.namespace Default: ``/event`` Type: *string* Socket.IO namespace. .. note:: **leaflet.\*** *Configuration parameters for the Leaflet map plugin that* *renders an interactive map on the index page.* .. note:: **leaflet.init.\*** *By default the map view is set to the bounds of the* *current event list. This configuration group allows to* *define a fix coordinate and zoom level for the initial* *map view.* .. confval:: leaflet.init.lat Unit: *degrees* Type: *double* Initial latitude to center the map view. .. confval:: leaflet.init.lon Unit: *degrees* Type: *double* Initial longitude to center the map view. .. confval:: leaflet.init.zoom Default: ``3`` Type: *int* Initial zoom level for the map view. .. note:: **leaflet.tile.\*** *Parmeters controlling the map tile layer.* .. confval:: leaflet.tile.url Default: ``http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`` Type: *string* Tile URL. See https:\/\/leafletjs.com\/reference\-1.7.1.html#tilelayer for a list of supported placeholders. .. confval:: leaflet.tile.attribution Default: ``"Map data © OpenStreetMap contributors"`` Type: *string* Tile attribution string displayed on the bottom right corner. .. confval:: leaflet.tile.maxZoom Default: ``18`` Type: *int* Maximum available zoom level. .. note:: **leaflet.control.\*** *Defines available map controls typically displayed at* *bottom left map corner.* .. confval:: leaflet.control.scaleMetric Default: ``true`` Type: *boolean* Adds a scale showing the map resolution in meter or kilometer at the map center. .. confval:: leaflet.control.scaleImperial Default: ``false`` Type: *boolean* Adds a scale showing the map resolution in feet or miles at the map center. .. confval:: leaflet.control.mousePosition Default: ``true`` Type: *boolean* Adds a control showing the geo location under the mouse cursor. .. note:: **search.\*** *The search plugin provides a page to configure a search* *filter.* .. confval:: search.resultLimit Default: ``100`` Type: *int* The maximum number of search results. .. confval:: search.distanceUnit Default: ``km`` Type: *string* The unit used for the radius of the circular region filter. This can either be 'km', 'mi' or 'deg'. Conversion constants used: 1 mile \= 1.609344 kilometers, 1 degree \= 111.195079734632 kilometers. .. confval:: search.enableMap Default: ``true`` Type: *boolean* Enables interactive map region selection. .. note:: **feedback.\*** *The feedback plugin implemets a email feedback form.* .. confval:: feedback.from Type: *string* The email's 'From' address \(mandatory\). .. confval:: feedback.to Type: *string* The email's 'To' address \(mandatory\). .. confval:: feedback.subject Default: ``EQEvents feedback`` Type: *string* The email's 'Subject'. .. confval:: feedback.host Default: ``localhost`` Type: *string* SMTP host. .. confval:: feedback.port Default: ``25`` Type: *int* SMTP port. .. confval:: feedback.user Type: *string* User in case the SMTP server requires authorization. .. confval:: feedback.password Type: *string* Password in case the SMTP server requires authorization. .. confval:: feedback.tls Default: ``false`` Type: *boolean* Use StartTLS. .. confval:: feedback.ssl Default: ``false`` Type: *boolean* Use Secure Socket Layer \(SSL\). .. note:: **shakemap.\*** *The shakemap plugin adds a shake map image to the event* *overview page. The image is generated by an external* *application, e.g. autosigma.* .. confval:: shakemap.cmd Default: ``autosigma --ep`` Type: *string* Command generating the result file, invoked with the location of the SC3 XML file as last paramter. .. confval:: shakemap.minMag Default: ``5.0`` Type: *float* Minimum magnitude to trigger shake map generation. .. confval:: shakemap.resultFile Default: ``@ROOTDIR@/var/sigma/incidents/grid-mmi.png`` Type: *string* Expected location of the result file. Command-Line Options ==================== .. program:: eqevents Generic ------- Verbosity --------- EQEvents -------- .. option:: -i, --input string Input file to be processed immediately. EQEvents will not change into spooler mode after processing this file. .. option:: --reprocess Reprocess event detail pages and index pages for events found in database. This mode requires backupXML being enabled. .. option:: --reprocess-index Reprocess index pages for events found in database.