Merge from dev branch (#125)

* updating Tethys help

* added help details server connection & calibration

* Reporting outputs to user

Use Pamguard WarnOnce system to show a message of success or failure
after attempting to output to Tethys.

* Removing empty fields

Start of implementation of code that can remove empty fields from
objects before they are written.

* More help documentation

Continued writing general export help.
Added new css style
Added image

* Fix type in Deployment export

* Prevent single deployment export

For now, disable the option to export a single Deployment document for
multiple recording periods.

* System to search species codes

Search the ITIS species codes in Tethys to find ITIS codes. Search can
be on a common or scientific name.

* Data blocks help + species codes

More help documentation on exporting detections/localization as well as mapping detection species codes to TSNs.  Includes supporting images

* Improved dialog for finding species codes

Now has a progress bar to block GUI while searching database.

* Add species codes TOC in help

* Documented new species search in help

* Nilus Checker

Can check a Nilus class to see if it's missing required objects and be
used to remove empty fields (e.g. zero length strings) from an object.

* fixed anchor html problem

* Messing around with checking of required fields in nilus objects.

* split Tethys help files into smaller chunks

* Update button enablers

And reinstate output of a single deployment document, with non-recording
periods stores in the QA section

And update help TOC for latest help documents.

---------

Co-authored-by: Marie <marie.roch@sdsu.edu>
This commit is contained in:
Douglas Gillespie 2024-01-30 11:55:34 +00:00 committed by GitHub
parent cb0d191c08
commit 5466914d4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 2270 additions and 165 deletions

View File

@ -1,7 +1,5 @@
package PamView.wizard;
import java.io.Serializable;
import javax.swing.JPanel;

View File

@ -15,7 +15,7 @@ public class ClickBlockSpeciesManager extends DataBlockSpeciesManager<ClickDetec
public ClickBlockSpeciesManager(ClickControl clickControl, ClickDataBlock clickDataBlock) {
super(clickDataBlock);
this.clickControl = clickControl;
setDefaultDefaultSpecies(new SpeciesMapItem(ITISTypes.UNKNOWN, "Unknown", "Unknown"));
setDefaultDefaultSpecies(new SpeciesMapItem(ITISTypes.OTHER, "Unknown", "Unknown"));
setDefaultSpeciesCode("Unknown");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,2 +1,2 @@
JavaSearch 1.0
TMAP bs=2048 rt=1 fl=-1 id1=6727 id2=1
TMAP bs=2048 rt=1 fl=-1 id1=6882 id2=1

Binary file not shown.

View File

@ -54,6 +54,8 @@
<mapID target="classifiers.whistleClassifierHelp.docs.whistleClassifier_Output" url="classifiers/whistleClassifierHelp/docs/whistleClassifier_Output.html"/>
<mapID target="utilities.tethys.docs.calibrations" url="utilities/tethys/docs/calibrations.html"/>
<mapID target="utilities.hydrophoneArrayManagerHelp.docs.Array_Configuration_sec1" url="utilities/hydrophoneArrayManagerHelp/docs/Array_Configuration.html#sec1"/>
<mapID target="sound_processing.fftManagerHelp.docs.noise_removal_medianfilter" url="sound_processing/fftManagerHelp/docs/noise_removal.html#medianfilter"/>
@ -138,6 +140,8 @@
<mapID target="sound_processing.NoiseBands.Docs.NoiseBands" url="sound_processing/NoiseBands/Docs/NoiseBands.html"/>
<mapID target="utilities.tethys.docs.detect_localize" url="utilities/tethys/docs/detect_localize.html"/>
<mapID target="visual_methods.videoRangeHelp.docs.videoRange_Calibrating" url="visual_methods/videoRangeHelp/docs/videoRange_Calibrating.html"/>
<mapID target="detectors.gpl.docs.detection" url="detectors/gpl/docs/detection.html"/>
@ -198,6 +202,8 @@
<mapID target="displays.userDisplayHelp.docs.userDisplayPanel" url="displays/userDisplayHelp/docs/userDisplayPanel.html"/>
<mapID target="utilities.tethys.docs.tethys_speciescodes_TSNLookup" url="utilities/tethys/docs/tethys_speciescodes.html#TSNLookup"/>
<mapID target="classifiers.roccaHelp.docs.rocca_Configure_SchoolStats" url="classifiers/roccaHelp/docs/rocca_Configure.html#SchoolStats"/>
<mapID target="visual_methods.loggerFormsHelp.docs.SUBTABS" url="visual_methods/loggerFormsHelp/docs/SUBTABS.html"/>
@ -276,8 +282,6 @@
<mapID target="detectors.clickDetectorHelp.docs.offline_Tools_AmplSel" url="detectors/clickDetectorHelp/docs/offline_Tools.html#AmplSel"/>
<mapID target="utilities.tethys.docs.tethys_quickstart" url="utilities/tethys/docs/tethys_quickstart.html"/>
<mapID target="displays.spectrogramDisplayHelp.docs.UserDisplay_Spectrogram_Overlays" url="displays/spectrogramDisplayHelp/docs/UserDisplay_Spectrogram_Overlays.html"/>
<mapID target="displays.radarDisplayHelp.docs.UserDisplay_Radar_Creating" url="displays/radarDisplayHelp/docs/UserDisplay_Radar_Creating.html"/>
@ -290,6 +294,8 @@
<mapID target="classifiers.roccaHelp.docs.rocca_Spectrogram_rocca_RecalcContour" url="classifiers/roccaHelp/docs/rocca_Spectrogram.html#rocca_RecalcContour"/>
<mapID target="utilities.tethys.docs.tethys_speciescodes" url="utilities/tethys/docs/tethys_speciescodes.html"/>
<mapID target="overview.PamMasterHelp.docs.performanceTests" url="overview/PamMasterHelp/docs/performanceTests.html"/>
<mapID target="overview.PamMasterHelp.docs.xmloutput" url="overview/PamMasterHelp/docs/xmloutput.html"/>
@ -388,6 +394,8 @@
<mapID target="localisation.group3d.docs.3dsimplex" url="localisation/group3d/docs/3dsimplex.html"/>
<mapID target="utilities.tethys.docs.connection" url="utilities/tethys/docs/connection.html"/>
<mapID target="visual_methods.loggerFormsHelp.docs.NOCLEAR" url="visual_methods/loggerFormsHelp/docs/NOCLEAR.html"/>
<mapID target="sound_processing.beamformer.docs.Beamformer_Settings_BF_SourceTab" url="sound_processing/beamformer/docs/Beamformer_Settings.html#BF_SourceTab"/>
@ -456,6 +464,8 @@
<mapID target="sound_processing.seismicveto.docs.veto_configuration" url="sound_processing/seismicveto/docs/veto_configuration.html"/>
<mapID target="utilities.tethys.docs.deployments" url="utilities/tethys/docs/deployments.html"/>
<mapID target="detectors.whistleMoanHelp.docs.whistleMoan_ConfigNoise_averagesubtraction" url="detectors/whistleMoanHelp/docs/whistleMoan_ConfigNoise.html#averagesubtraction"/>
<mapID target="localisation.largeAperture3D.docs.meal_exercise3_localise" url="localisation/largeAperture3D/docs/meal_exercise3_localise.html"/>
@ -592,6 +602,8 @@
<mapID target="detectors.gpl.docs.notes" url="detectors/gpl/docs/notes.html"/>
<mapID target="utilities.tethys.docs.tethys_module" url="utilities/tethys/docs/tethys_module.html"/>
<mapID target="sound_processing.fftManagerHelp.docs.FFTEngine_Creating" url="sound_processing/fftManagerHelp/docs/FFTEngine_Creating.html"/>
<mapID target="sound_processing.fftManagerHelp.docs.noise_removal_clickremoval" url="sound_processing/fftManagerHelp/docs/noise_removal.html#clickremoval"/>

View File

@ -2,6 +2,8 @@
<!DOCTYPE index PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp Index Version 1.0//EN" "http://java.sun.com/products/javahelp/index_2_0.dtd"><!--generated by JHelpDev Version: 0.63, 14 May 2008, see jhelpdev.sourceforge.net-->
<index version="1.0">
<indexitem text="ITIS Species Codes " target="utilities.tethys.docs.tethys_speciescodes"/>
<indexitem text="Tethys server " target="utilities.tethys.docs.tethys_server"/>
<indexitem text="Tethys database interface " target="utilities.tethys.docs.tethys_overview"/>

View File

@ -198,10 +198,20 @@
<tocitem text="Tethys Interface ">
<tocitem text="Tethys Overview " target="utilities.tethys.docs.tethys_overview" image="topic"/>
<tocitem text="Quick Start " target="utilities.tethys.docs.tethys_quickstart" image="topic"/>
<tocitem text="Tethys Module " target="utilities.tethys.docs.tethys_module" image="topic"/>
<tocitem text="Tethys Server " target="utilities.tethys.docs.tethys_server" image="topic"/>
<tocitem text="Connecting to Tethys " target="utilities.tethys.docs.connection" image="topic"/>
<tocitem text="Instrument Calibrations " target="utilities.tethys.docs.calibrations" image="topic"/>
<tocitem text="Instrument Deployments " target="utilities.tethys.docs.deployments" image="topic"/>
<tocitem text="Detections and Localizations " target="utilities.tethys.docs.detect_localize" image="topic"/>
<tocitem text="ITIS Species Codes " target="utilities.tethys.docs.tethys_speciescodes" image="topic"/>
</tocitem>
</tocitem>
<tocitem text="Sensors ">

View File

@ -55,6 +55,14 @@ ol {
img.wrap {float: left}
img.wrapright {float: right}
img.wrapcenter {float: center}
.center {
display: block;
margin-left: auto;
margin-right: auto;
width: 85%;
}
table, th, td {
border: 1px solid black;

View File

@ -0,0 +1,147 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<link href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Instrument Calibration Information</title>
<style type="text/css">
img.wrap {
float: left
}
img.wrapright {
float: right
}
img {
float: right;
}
</style>
</head>
<body>
<h1>Instrument calibration information</h1>
<p>
Most of the calibration data is taken from the array manager and
from the sound acquisition module. However, PAMGuard will ask a
few questions about HOW the instrument was calibrated, when it was
done and who is responsible.
</p>
<p>
There are two dialogs associated with calibration. The first
asks for a calibration method and has the following fields:
</p>
<ul>
<li>
Method: Must be one of the following options:
<ul>
<li>Reference hydrophone</li>
<li>Manufacturers specification</li>
<li>Piston phone</li>
<li>Other calibrated source</li>
<li>Unknown</li>
</ul>
</li>
<li>
Serial number: Hydrophone serial number
</li>
<li>
Quality: Quality assurance value:
<ul>
<li> unverified: The calibration has not been verified </li>
<li> valid: The calibration has been validated as per the quality assurance process </li>
<li> invalid: The calibration was found to be invalid during quality assurance </li>
</ul>
</li>
<li>
QA Comment: Textual description of the quality assurance
process.
</li>
<li>
Calibration method: Textual description of the Method.
</li>
</ul>
<p>
The second calibrations dialog asks for:
</p>
<ul>
<li>
Calibration date: Date the calibration was performed.
</li>
<li>
Update frequency: Must be one of the following:
<ul>
<li>
as-needed: No updates are planned, but if a change is needed the calibration will be updated (defaul)t
</li>
<li>
unplanned: There are no plans to ever update the record.
</li>
<li>
yearly: A yearly review will be conducted to ensure that the record is valid.
</li>
</ul>
</li>
<li>
Technical Person / Data Manager: These two types of data have
the same fields and detail who was responsible for the
calibration and who is responsible for maintaining the record
of the calibration. In many cases, this may be the same
person and copy buttons allow the fields to be duplicated.
<ul>
<li>
Name: Responsible partys naem
</li>
<li>
Organisation: Organisation to which the party reports
</li>
<li>
Position: Responsible partys title
</li>
<li>
Email: Email contact information
</li>
</ul>
</li>
</ul>
<p>
Fill in as much information as you can!
If the export is successful, a record will show for each
hydrophone (or sensor) in your instrument array in the
calibration information table:
</p>
<div>
<img src="./images/calibration_information.png" class=".center"
alt="Panel with information about instrument calibration"/>
</div>
<br>
<br>
<p class="prevLink"><a href="tethys_module.html">Previous: Module overview
<p class="nextLink"><a href="deployments.html">Next: Instrument deployments
</a></p>
<br>
</body>
</html>

View File

@ -0,0 +1,121 @@
<html>
<head>
<LINK href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Tethys Connection and Project Details </title>
</head>
<body>
<h1> Connection and Project Details </h1>
<p>
Make sure you have a <a href = "tethys_server.html">Tethys Server</a> running. The PAMGuard interface will
only work with <a href="https://tethys.sdsu.edu/tethys3/">Tethys
3</a> or later. The section below specifies how to set the
address of Tethys server address as well as determine if
PAMGuard can communicate successfully with Tethys. (The top
panel will be orange if communication is not working.
</p>
<h2>Tethys Server</h2>
<p>
The Tethys Server field next to the picture of the goddess Tethys
indicates the address of the Tehtys server. To change this field,
click on the gear icon. You will be prompted to provide a computer
address (URL) and a port. The address should start with http://
followed by the machine name or internet protocol address unless
the Tethys server has been configured to use an encrypted
connection. In this case, start the address with https://.
</p>
<p>
By default, PAMGuard will use http://localhost:9779 which
assumes that your Tethys server is running on the same computer as
PAMGuard and that it expects communication on port 9779, the
default port (administrators may change this).
</p>
<p>
If PAMGuard can communicate with the Tethys server, the
Connection and Project panel will be light grey. If
communication is not possible, the panel will be colored
orange. Likely causes for communication failure are:
</p>
<ul>
<li>
The server address or port is incorrect.
</li>
<li>
Tethys has not been started on the server machine. One of the
main reasons that we see this is when the administrator has
not configured Tethys to run automatically as a service. In
that case, when a machine reboots (e.g., for automatic
operaing system updates) the server will not start
automatically. The Tethys manual explains how to configure
Tethys as a service that starts automatically when the machine
boots.
</li>
<li>
Firewall rules do not permit traffic between the machine
executing PAMGuard and the one hosting Tethys on the selected
port. If you do not have adminstrative privileges, you will
need to contact your support team for help.
</li>
</ul>
<h2>Project and Instrument Information</h2>
<p>
Projects are names used by Tethys to help track work that should
be considered together, such as a series of deployments designed
to answer a specific question or funded under a specific
grant. If you do not already have a project defined in your
PAMGuard database, you can click the "New Project" button in the
"Connection and Project details" section of the Tethys
module. This will start a dialog that asks for a case-sensitive
project name and a geographic region. The geographic region is
for convenience, PAMGuard and Tethys track information by
longitude and latitude, but sometimes it is helpful to query for
information with respect to a geographic name such as Channel
Islands National Marine Sanctuary.
</p>
<p>
The array instrumentation is selected from a drop-down menu next
to the Instruments label. A dropdown menu next to the label
“Instruments” shows the list of hydrophone arrays. These are
likely to have been previously established prior to starting
analysis of your data by using the menu Settings -> Hydrophone
Array.
</p>
<p>
If you are a long-time user of PAMGuard, you will notice
additional fields are required for instrumentation: Instrument
Type and Instrument Id. The type indicates what type of
instrument is being used and may be generic such as a mooring or
array or denote a specific instrument such as a HARP, Rock
Hopper, SoundTrap, etc. The Id is a unique identifier for the
instrument such as a serial number. Note that if you are using
an older PAMGuard database, you may see a blank entry in the
instruments list as these new fields will not have been
populated. Press new/edit to access the instrument settings
from the Tethys module page.
</p>
<br>
<br>
<p class="prevLink"><a href="tethys_module.html">Previous:
Tethys module</a></p>
<p class="nextLink"><a href="calibrations.html">Next: Instrument calibrations</a></p>
<br>
</body>

View File

@ -0,0 +1,143 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<link href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Deployments</title>
<style type="text/css">
img.wrap {
float: left
}
img.wrapright {
float: right
}
img {
float: right;
}
</style>
</head>
<body>
<h1>Deployments</h1>
<p>
Tethys uses deployment records to register information about
when instruments have been deployed as well as their
characteristics. Examples of characteristics that are recorded
include sample rate and the number of quantization bits,
description of duty cycles (if applicable), hydrophone geometry,
and enough details to be able find calibration data for specific
hydrophones.
</p>
<p>
PAMGuard will examine the
<a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard
database</a>
and <a href="../../BinaryStore/docs/binarystore_overview.html">
binary Store</a> to determine what records should be generated for
your instrumentation.
</p>
<p>
Where data were collected continuously or on a regular duty
cycle, PAMGuard will create a single deployment record. If data
were collected on a more "ad-hoc" basis, where the instrument
has been deployed multiple times or has irregular recording,
PAMGuard will generate a deployment record for each period of
recording.
</p>
<p>
The figure below shows an example of ad-hoc recording periods
identified by PAMGuard:
</p>
<div>
<img src="./images/deploymentspanel.png" class=".center"
alt="Panel showing recording times/deployments for this PAMGuard database"/>
</div>
<p>
Occasionally, there may be short recording periods (e.g. while
you were testing kit on deck) that you do not want to export. Use
the select checkbox to pick all of the rows that you wish to
export, or right click on the table and "Select All."
</p>
<p>
When one or more deployment records are selected, the
"Export..." button will become available. Selecting the export
button will start a dialog that asks for additional information
about the deployments and then write records to Tethys.
</p>
<p>
The first page of the dialog asks for the project and geographic
region which will be automatically populated if they have been
previously specified. In addition, the following fields are
requested:
</p>
<ul>
<li>Cruise name - Optional name of the deployment cruise</li>
<li>Site - Case-sensitive name for the deployment site,
e.g. "Tanner Banks" or a letter designation "T". This can
provide a simple way to identify multiple deployments at the
same general location.
</li>
<li>Responsible Party - A set of fields describing who was
responsible for the deployment and how they may be
contacted.
</li>
</ul>
<p>
The next page of the dialog asks whether you would like to
export a single deployment document or multiple deployments.
<strong>todo: add more detail here</strong>
</p>
<p>
Finally, you are prompted to provide optional textual descriptions of:
<ul>
<li> Objectives - What were your objectives when deploying the
instrument? Example: Determine population estimates for
critically endangered populations of vaquita (<em>Phocoena
sinus</em>).
</li>
<li> Abstract - A textual description of the deployment.
Example: A set of high frequency recorders were deployed across the
northern portion of the Sea of Cortez in the historical range
of the vaquita (<em>Phocoena sinus</em>). These recordings will
support detection and density estimation efforts.
</li>
<li>
Method - A description of the methods used. Example:
Small boat deployment of bottom moored SoundTrap recorders
with acoustic releases.
</li>
</ul>
Press Finish to export the records. Once the
document(s) have been successuflly exported, the document name
associated with each recording period will be shown in the
Tethys Deployment column.
</p>
<br></br>
<br></br>
<p class="prevLink">
<a href="calibrations.html">Previous: Calibrations</a>
</p>
<p class="nextLink">
<a href="detect_localize.html">Next: Detections & Localizations</a>
</p>
</body>
</html>

View File

@ -0,0 +1,161 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<link href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Detections/Localizations</title>
<style type="text/css">
img.wrap {
float: left
}
img.wrapright {
float: right
}
img {
float: right;
}
</style>
</head>
<body>
<h1>Exporting Detections/Localizations (PAMGuard data blocks)</h1>
<p>
The bottom left panel of the Tethys module shows a list of
different types of PAMGuard data that can be exported. The data
in this list correspond to the various PAMGuard modules that have
been configured. See the
<a href="../../../overview/PamMasterHelp/docs/modelViewer.html">
data model viewer</a>
help for an example of how PAMGuard might be configured.
</p>
<h2>Species information</h2>
<p>
Some of these data represent detections of specific species or
phenomena that must be translated to Tethys. A context menu
(right-click on most computers) will show the option "Species
info..." that will allow you to specify the
<a href="./tethys_speciescodes.html">translation of events</a>
to species identifiers and call/sound types. If you try to export
without having done this, the species info dialog will be started
automatically prior to export.
</p>
<h2>Selecting data blocks for import</h2>
<p>
In the sample data blocks below, four modules have been
configured, but only one of them has been run. Column "N Pam
Data" indicates the number of data records that have been
produced, and "PAMGuard Time" tells us when the data were
processed. "Tethys Documents" indicates how many Tethys records
have been produced, and should be 0 until the data are exported.
</p>
<div>
<img src="./images/data_blocks.png" class=".center"
alt="List of results showing detections and other module processing events"/>
</div>
<p>
Select the data blocks to be exported by clicking on them.
Multiple lines can be selected by using keyboard modifiers such as
holding the shift while clicking to select all data blocks between
the last clicked block and where you click. Holding the alternate
(ALT) key will allow selection or de-selection of a single item
without affecting the selection state of other blocks.
<p>
<p>
<em>
SUGGESTION: It looks like we have to highlight these by clicking
on them. As we use select boxes for recording periods, we might
want to do the same thing here... We might want to rename
N PAM Datas to N PAM Data as data are already plural.
</em>
</p>
<h2> Exporting data blocks </h2>
<p>
Once the data blocks have been selected, press export. A series
of dialogs will guide you through the export process. The first
set of dialogs simply display a summary of information about what
will be exported.
</p>
<div>
<img src="./images/stream_algo_info.png" class=".center"
alt="List of details describing the mechanism, parameters, and version of modules used in processing"/>
</div>
<p>
There is nothing to change in this summary. Press Next once you
have reviewed it. The second panel allows specification of your
objectives, abstract, and method. Many modules will have
pre-populated the method for you. While it is recommended to populate
the objectives and abstract, these fields are optional.
</p>
<p>
Press Next to proceed to the next step of the dialog. You will be asked
what details you wish to store within the parameters that were used to
produce these data. Your must select one of the following:
</p>
<ul>
<li>
None - Do not report any of parameters used to produce these
detections. This option is <strong>not recommended</strong> as
it severely limits your ability to reproduce your results at a
later date or know whether or not the results of different
studies can be used together.
</li>
<li> Data selector only - <strong>not sure what this is</strong> </li>
<li>
Module only - Report the parameters that were set with this
module. Only parameters associated with the specific module
will be reported. Examples include score and duration thresholds
as well as any other type of criterion used to determine whether
or not an event is associated with a specific phenomenon or species.
</li>
<li>
Full process chain (default) - This is the most verbose option. It includes the module
parameters as well as anything else that is part of the signal processing chain that leads
to the module. As an example, a module only setting would not report the parameters that were
used to generate a spectrogram that was presented to a module for classification, but the
full process chain would record these details as well. Use this option will dramatically
increase the potential to reproduce your results, but it will generate a large amount of data
about the signal processing chain, much of which might not be useful.
</li>
</ul>
<p>
The final page of the dialog has an "Export data" button. Press
this to export the data. The system will begin generating the
Tethys document and the "Export data" button will be relabeled "Export complete"
once it is done. At this point, you can press "Finish" to close
the dialog.
<em>Would it make more sense to export when the user presses
Finish (or change the Finish button Export)?</em>
</p>
<p class="prevLink">
<a href="deployments.html">Previous: Deployments</a>
</p>
<p class="nextLink">
<a href="./tethys_speciescodes.html">Translating species to
taxonomic serial numbers</a> (species encoding)
</p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,92 @@
<html>
<head>
<LINK href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Tethys Module Overview</title>
</head>
<body>
<h1>Tethys Module Overview</h1>
<p>
It is assumed that you are familiar with PAMGuard and have some
knowledge about Tethys.
<a href="https://tethys.sdsu.edu/documentation">Documentation</a>
and <a href="https://tethys.sdsu.edu/tutorials/">tutorials</a>
are available at the Tethys
<a href="https://tethys.sdsu.edu">web site</a>.
</p>
<h1>Launch PAMGuard in viewer mode</h1>
<p>
Tethys export is only available in
<a href="../../../overview/PamMasterHelp/docs/viewerMode.html">PAMGuard
Viewer mode</a>
and is used to archive project data to a centralised
database. It is NOT a replacement for the
existing <a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard
database</a>. Open the PAMGuard database that you wish to use
in viewer mode.
</p>
<h1>PAMGuard Tethys Module</h1>
<p>
Add a Tethys module to PAMGuard from the File / Add Modules /
Utilities menu. A new tab panel will show the Tethys interface
</p>
<div>
<img src = "./images/TethysGUI_1.png" class=".center"/>
</div>
<p>
The tab shows a number of panes for the connection to the server
and the various types of data that will be output to the
database. Some of these panes summarize information on what's in
the current PAMGuard dataset (consisting of your PAMGuard
database and binary store), others may be empty until you start
to export to Tethys.
</p>
<p>
The Tethys module consists of several panels:
</p>
<ul>
<li>
<a href="connection.html">Connection and Project Details</a> -
Specifies the location of the Tethys server and metadata about
the project.
</li>
<br>
<li>Data Export - There are several panes that are responsible
for exporting information about instrument deployments, their
calibrations, and what they detected/localized.
<ul>
<li>
Instrument <a href="calibrations.html">calibration</a>
information
</li>
<li>
Recording periods
and <a href="deployments.html">deployments</a> information.
</li>
<li>
<a href="detect_localize.html">Detections and
localizations</a>. A final panel next to this one lists
exported detection/localization documents.
</li>
</ul>
</li>
</ul>
<br>
<br>
<p class="prevLink"><a href="tethys_overview.html">Previous: Overview</a></p>
<p class="nextLink"><a href="connection.html">Next: Connection
& Project Details</a></p>
<br>
</body>
</html>

View File

@ -22,47 +22,67 @@ img {
</head>
<body>
<h1>Tethys Interface</h1>
<p></p>
<h2>Overview</h2>
<p align="right">
<img class="wrap" SRC="images/Tethys-200.png" alt="Tethys mosaic">
</p>
<div>
<img SRC="images/Tethys-200.png" alt="Tethys mosaic"
style="float: right; margin 10px;"/>
</div>
<p>
PAMGuard is compatible
with <a href="https://tethys.sdsu.edu/tethys3/">Tethys 3.0</a> or
later.
<a href="https://tethys.sdsu.edu/">Tethys</a> is a freely
available open source temporal-spatial database for metadata related
to acoustic recordings. The database is intended to house the metadata
from marine mammal detection and localization studies, allowing the
user to perform meta analyses or to aggregate data from many
experimental efforts based on a common attribute. This resulting
database can then be queried based on time, space, or any desired
attribute and the results can be integrated with external datasets
such as NASA's Ocean Color, lunar illumination, etc. in a consistent
manner. While Tethys is designed primarily for acoustic metadata from
marine mammals, the design is general enough to permit use in other
areas as well.
</p>
<p>PAMGuard is compatible with <a href="https://tethys.sdsu.edu/tethys3/">Tethys 3.0</a> or above, released early in 2024. </p>
<p>The Tethys database is not a replacement for the existing
<a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard Database.</a>
Where the PAMGuard database only contains data from a single instrument or cruise, the Tethys
database contains data from many cruises and projects and can be used to hold a summary of all data
from a lab or organisation.
available open source temporal-spatial database for metadata
related to acoustic recordings. The database is intended to house
the metadata from marine mammal detection and localization
studies, allowing the user to perform meta analyses or to
aggregate data from many experimental efforts based on a common
attribute. This resulting database can then be queried based on
time, space, or any desired attribute and the results can be
integrated with external datasets such as NASA's Ocean Color,
lunar illumination, etc. in a consistent manner. While Tethys is
designed primarily for acoustic metadata from marine mammals, the
design is general enough to permit use in other areas as well.
</p>
<p>Before using the module in PAMGuard, you should install the Tethys Server, which runs under
Windows. <a href="https://tethys.sdsu.edu/install/">
Instructions for installing the Tethys Server can be found here.</a></p>
<p>
The Tethys database is not a replacement for the existing
<a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard
Database.</a> Where the PAMGuard database only contains data from
a single instrument or cruise, the Tethys database contains data
from many cruises and projects and can be used to hold a summary
of all data from a lab or organisation. PAMGuard's Tethys module
provides an interface for exporting detailed or summary
information about acoustic detections to the Tethys database.
</p>
<p>
Before using the module in PAMGuard, you should install the Tethys
Server. While clients that communicate with Tethys can run on a
variety of computer operating systems, there are a small number of
dependencies on Microsoft technologies that require the server to
be installed on a Microsoft Windows machine.
<a href="https://tethys.sdsu.edu/install/"> Instructions
for installing the Tethys Server can be found here.</a></p>
<br><br>
<a href="tethys_quickstart.html">Next: Quick Start</a>
<p>
Module help:
</p>
<br>
<br>
<br>
<ol>
<li> The <a href="tethys_module.html">Tethys module</a> (start here)</li>
<li> The <a href="connection.html">Connecting to Tethys</a> </li>
<li> The <a href="calibrations.html">Instrument calibrations</a> </li>
<li> The <a href="deployments.html">Instrument deployments</a> </li>
<li> The <a href="detect_localize.html">Detections & Localizations</a> </li>
<li> Guide to specifying <a href="tethys_speciescodes.html">species names</li>
</ol>
</body>
</html>

View File

@ -1,80 +0,0 @@
<html>
<head>
<LINK href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Tethys</title>
</head>
<body>
<h1>Tethys Quick Start</h1>
<p>This 'Quick Start' guide is aimed at people who are already familiar with both Tethys and PAMGuard</p>
<p>The Tethys database is only used in
<a href="../../../overview/PamMasterHelp/docs/viewerMode.html">PAMGUard Viewer mode</a> and is only used to archive project data to a single
centralised database.
It is NOT a replacement for the existing <a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard database</a>.</p>
<h3>PAMGuard Tethys Module</h3>
<p>Launch PAMGuard in Viewer Mode with an existing set of data.</p>
<p>Add a Tethys module to PAMGuard from the File / Add Modules / Utilities menu. </p>
<p>A new tab panel will show the Tethys interface</p>
<center><img src = "./images/TethysGUI_1.png" width="100%" height="auto"></center>
<p>The tab shows a number of panels for the connection to the server and the various types
of data that will be output to the database. Some of these should summary information on what's in the
current PAMGuard dataset (consisting of your PAMGuard database and binary store), others may be empty until you start to
export to Tethys. </p>
<h3>Tethys Server</h3>
<p>Make sure you have a <a href = "tethys_server.html">Tethys Server</a> running. The PAMGuard interface will
only work with <a href="https://tethys.sdsu.edu/tethys3/">Tethys 3.</a> </p>
<p>Check the server connection. If PAMGuard has correctly connected to the Tethys server, the top panel of the display will be a normal
grey colour. If the connection cannot be made, the panel will be orange.</p>
<p>If required, change the server settings using the "Select Sever" button</p>
<h4>Project and Instrument Information</h4>
<p>Either select an existing "Project", or create a new one.</p>
<p>You also need to give Tethys more information about the instrument(s) or arrays you've deployed than in previous PAMGuard versions. Again, select an
existing instrument, or set up a new one. You'll notice that this information is held with the rest of the PAMGuard array management information.</p>
<h3>Data Export</h3>
<p>It's best to export data in the sequence the panels are laid out in on the PAMGuard display, i.e. Calibrations, then Deployments, and finally Detections.</p>
<h4>Calibrations</h4>
<p>To export the calibration data, press the "Export..." button at the top of the "Instrument Calibration Information" panel.
<p>Most of the calibration data is taken from the array manager and from the sound acquisition module. However, PAMGuard will ask a few questions about
HOW the instrument was calibrated, when it was done and who is responsible. Fill in as much information as you can!
IF the export is successful, a record will show for each hydrophone (or sensor) in your instrument array in the calibration information table:</p>
<center><img src="./images/calibration_information.png"></center>
<h4>Deployments</h4>
<p>PAMGuard will have done it's best to work out the temporal extent of your data by looking in the
<a href="../../generalDatabaseHelp/docs/database_database.html">PAMGuard database</a> and
<a href= "../../BinaryStore/docs/binarystore_overview.html">binary Store</a>. Where data were collected on a regular duty
cycle, or continuously, there should be a single record in the table of recording periods. If data were collected on a more
"ad-hoc" basis, for instance during a boat based survey, there might be many different records in the table.
Occasionally, there may be short recording periods (e.g. while you were testing kit on deck) that you don't want to export. Either select
individual rows that you want to export, or right click on the table and "Select All".</p>
<p>Press the "Export..." button and work through the questions to provide additional information about your data, why it was collected, etc. </p>
<p>Once the document(s) have been exported, the document names will be shown alongside each PAMGuard deployment period</p>
<center><img src="./images/deploymentspanel.png"></center>
<h4>Detections</h4>
<p>The bottom left panel shows a list of different types of PAMGuard data that can be exported and should also show the total numbers of each type of data that are available
within the PAMGuard storage systems. Select the datablock you want to export from and press "Export...". </p>
<p><strong>You really don't want to try to export zillions of data to Tethys</strong> , in the options that will appear you can opt to just export certain types of detections
or summary counts.</p>
<br>
<br>
<p class="prevLink"><a href="tethys_overview.html">Previous: Overview</a></p>
<p class="nextLink"><a href="tethys_server.html">Next: Tethys Server</a></p>
<br>
<br>
<br>
</body>
</html>

View File

@ -0,0 +1,192 @@
<html>
<head>
<LINK href="../../../pamHelpStylesheet.css" type="text/css"
rel="STYLESHEET">
<title>Species coding</title>
</head>
<body>
<h1> Species and Call Type Names </h1>
When exporting data from PAMGuard to Tethys, some PAMGuard records
will require additional information indicating what type of animal
or phenomena were detected. If a specific call-type was detected,
e.g. "Clicks" or "Whistles", these should be noted as well.
<h2> Species Names </h2>
<p>
Tethys uses the <a href="https:itis.gov">Integrated Taxonomic
Information System</a> (ITIS) to encode species names as taxonomic
serial numbers (TSNs), unique numeric identifiers for species.
These data conform with several international coding systems which
are described on the <a href="https://itis.gov/standard.html">ITIS
standards</a> page.
</p>
<p>
It is not uncommon to be unable to describe a call to the genus
level. In such cases, one can use a higher taxonomic level. For
example, beaked whale echolocation clicks are distinctive from the
clicks of other toothed whales as their pulses have a
frequency-modulated component. While they can frequently be
associated with the family Hyperodontidae, it is not always
possible to associate a click to a specific species as many of the
at least twenty-two species remain understudied. In such a case,
we would use the TSN for Hyperodontidae, 770799. While not
currently supported by PAMGuard, each species identifier has an
optional Group attribute that can be used in an ad-hoc manner to
provide additional information. This can be used to add
population markers, tentative genus groups, etc.
</p>
<p>
ITIS does not describe abiotic sounds, Tethys records such sounds as follows:
<ol>
<li>
For anthropogenic signals, the Tethys convention is to
use <em>Homo sapiens</em>, TSN 180092, for the species code
and describe the human-generated signal via a call type,
e.g. ship, mid-frequency active sonar, etc.
</li>
<li>
Tethys reserves the TSN -10 for geophonic signals.
The call type is used to describe the source. Examples
include ambient sound, earthquake, rain, etc. Note that negative TSNs
are not part of the ITIS standard.
</li>
</ol>
</p>
Note that in general, you do not have to worry about remembering
TSNs. Tethys uses TSNs internally, but will translate TSNs to/from
Latin names or user-defined abbreviations both when querying and
presenting results.
<h2> Call types </h2>
<p>
Some detectors identify specific call types. When this is the
case, users will need to specify the call type name. While
species names are standardized in Tethys, call names do not have a
well-defined standard and experts frequently use different names
for the same type of call. Consequently, Tethys does not provide a
standard coding for call types and users are free to choose the call
type names with which they feel most comfortable.
</p>
<p>
That said, the authors of Tethys do however provide a list of recommend call
types for many species. These recommendations can be accessed in the
<a href="https://ars.els-cdn.com/content/image/1-s2.0-S1574954115001983-mmc1.docx">
supplemental information</a> of the open access article "Management of acoustic metadata
for bioacoustics," Roch et al. (2016),
(<a href="https://doi.org/10.1016/j.ecoinf.2015.12.002">DOI:10.1016/j.ecoinf.2015.12.002</a>).
</p>
<h2>
Export dialog
</h2>
<p>
During export of records that are species-specific, a dialog will
appear that lists the types of events that were found by PAMGuard
modules. This dialog permits users to specify how the ad-hoc species
species/call encoding scheme used by PAMGuard modules can be systematically
translated to the TSNs and call types are stored in Tethys.
</p>
<p>
PAMGuard events typically are a short name that represents the
species and/or potentially a call. Knowledge of the PAMGuard
modules that were run and the data on which they executed will let
a user infer what should be recorded.
The dialog below shows a sample set of events produced by one or more PAMGuard modules
using the names: HP, DO, SON, KW, UNK, and PHP:
<div>
<img src="./images/species_codes.png" class=".center"/>
</div>
The dialog above was produced from detections on data that were
recorded near the mouth of the River Tay in Scotland. Consequently,
we can infer that the harbour porpoise that is denoted by "HP" is
<em>Phocena phocena</em>, the only harbour porpoise endemic to
Scottish waters.
</p>
An ITIS code and call/sound type can be associated with each event. The dialog lists:
<ul>
<li>
Name - The ad-hoc name given by the PAMGuard module. This may not be changed.
</li>
<li>
ITIS code -This is the ITIS TSN that is associated with PAMGuard
event code. Click on Find to inovke the TSN
search dialog whose behavior is described in the <a href="#TSNLookup">next
section</a>.
<p>
If you happen to know the TSN, you
can enter it directly. Pressing Find after typing the TNS will
populate the Latin and English vernauclar names so that you may
verify your TSN was entered correctly.
</p>
</li>
<li> Call / sound type - This will default to the code provided by
the module but should be updated to specify an appropriate call
type or left blank to indicate that the detection is not related
to a specific call type. In the case of a porpoise detector,
the likely call type would be "Clicks".
<strong>We should double check that no Call element is generated when this is blank.</strong>
</li>
</ul>
<p>
Once all species names and call/sound types have been identified, press the Okay button.
</p>
<a name="TSNLookup"></a>
<h2> Searching for ITIS Taxonomic Serial Numbers (TSNs) </h2>
<p>
As noted above, pressing Find without typing a TSN will bring up a
search dialog:
</p>
<div>
<image src="./images/species_search.png" class=".center"/>
</div>
<p>
The top of this dialog has a search box where one can enter either
a Latin name or the common name for a species. The Tethys server
will search for all species that match the search-box contents.
For many species, there are common name entries in languages
other than English, and these are searched as well.
</p>
<p>
Once you press the search button, a list will appear with all
matches for your search term. If there are too many, a scroll bar
will permit you to look through the list. Select the entry that
you wish and press OK. The TSN on the species dialog will be
populated along with the Latin name and common names from the ITIS
database.
</p>
<br></br>
<br></br>
<p class="prevLink">
<a href="detect_localize.html">Previous: Detections & Localizations</a>
</p>
<p class="nextLink">
<a href="tethys_overview.html">Returrn to overview</a>
</p>
</body>

View File

@ -491,9 +491,10 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
public ServerStatus checkServer() {
ServerStatus serverState = dbxmlConnect.pingServer();
if (lastServerStatus == null || lastServerStatus.ok != serverState.ok) {
lastServerStatus = serverState; // set before sending notification!
sendStateUpdate(new TethysState(StateType.UPDATESERVER));
}
lastServerStatus = serverState;
// lastServerStatus = serverState;
return serverState;
}
@ -699,5 +700,23 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
return calibrationHandler;
}
/**
* @return the lastServerStatus
*/
public ServerStatus getLastServerStatus() {
return lastServerStatus;
}
/**
* Quick way for any controls to see that the server is probably OK
* without actually pinging it.
* @return true if last ping of server was OK
*/
public boolean isServerOk() {
if (lastServerStatus == null) {
return false;
}
return lastServerStatus.ok;
}
}

View File

@ -1,5 +1,6 @@
package tethys.calibration;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -45,9 +46,11 @@ import tethys.TethysTimeFuncs;
import tethys.calibration.swing.CalibrationsExportWizard;
import tethys.dbxml.DBXMLConnect;
import tethys.dbxml.TethysException;
import tethys.niluswraps.NilusChecker;
import tethys.niluswraps.NilusSettingsWrapper;
import tethys.niluswraps.NilusUnpacker;
import tethys.pamdata.AutoTethysProvider;
import tethys.reporter.TethysReporter;
public class CalibrationHandler implements TethysStateObserver {
@ -186,6 +189,7 @@ public class CalibrationHandler implements TethysStateObserver {
int nExport = 0;
boolean overwrite = false;
boolean exists;
TethysReporter.getTethysReporter().clear();
for (int i = 0; i < nPhone; i++) {
// String docName = getHydrophoneId(i);
NilusSettingsWrapper<Calibration> clonedWrap = wrappedSample.clone();
@ -195,11 +199,24 @@ public class CalibrationHandler implements TethysStateObserver {
calDoc.setMetadataInfo(sampleCal.getMetadataInfo());
calDoc.setProcess(sampleCal.getProcess());
calDoc.setQualityAssurance(sampleCal.getQualityAssurance());
if (NilusChecker.isEmpty(sampleCal.getResponsibleParty()) == false) {
calDoc.setResponsibleParty(sampleCal.getResponsibleParty());
}
calDoc.setTimeStamp(sampleCal.getTimeStamp());
}
// check the contact info in the metadata.
// can't so because it's required.
// MetadataInfo metaData = calDoc.getMetadataInfo();
// if (metaData != null) {
// if (NilusChecker.isEmpty(metaData.getContact())) {
// metaData.setContact(null);
// }
// }
addParameterDetails(calDoc, i);
// run some checks of completeness of the data
NilusChecker.removeEmptyFields(calDoc);
// ArrayList<Field> emptyFields = NilusChecker.checkEmptyFields(calDoc);
String calDocName = createDocumentName(calDoc, i);
exists = calDocumentExists(calDocName);
@ -233,6 +250,7 @@ public class CalibrationHandler implements TethysStateObserver {
}
}
tethysControl.sendStateUpdate(new TethysState(TethysState.StateType.EXPORTRDATA, Collection.Calibrations));
TethysReporter.getTethysReporter().showReport(true);
return nExport;
}
@ -405,6 +423,10 @@ public class CalibrationHandler implements TethysStateObserver {
hz.add(Double.valueOf(0));
db.add(Double.valueOf(hSens+preampGain));
if (NilusChecker.isEmpty(calibration.getResponsibleParty())) {
calibration.setResponsibleParty(null);
}
MetadataInfo metaInf = calibration.getMetadataInfo();
if (metaInf == null) {
metaInf = new MetadataInfo();
@ -417,6 +439,12 @@ public class CalibrationHandler implements TethysStateObserver {
contact = new ResponsibleParty();
metaInf.setContact(contact);
}
if (NilusChecker.isEmpty(metaInf.getContact())) {
metaInf.setContact(null);
}
if (NilusChecker.isEmpty(metaInf)) {
calibration.setMetadataInfo(null);
}
contact.setIndividualName("Unknown");
contact.setOrganizationName("unknown");

View File

@ -4,6 +4,8 @@ import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComboBox;
@ -15,6 +17,7 @@ import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import PamView.dialog.PamDialog;
import PamView.dialog.PamGridBagContraints;
import PamView.panel.WestAlignedPanel;
import PamView.wizard.PamWizard;
@ -23,9 +26,17 @@ import nilus.AlgorithmType.Parameters;
import nilus.AlgorithmType.SupportSoftware;
import nilus.Calibration;
import nilus.Calibration.QualityAssurance;
import nilus.Helper;
import nilus.QualityValueBasic;
import tethys.calibration.CalibrationHandler;
import tethys.niluswraps.NilusChecker;
/**
* Calibrations Process card attempts to fill in the
* calibration data for the Quality Assurance and Process fields.
* @author dg50
*
*/
public class CalibrationProcessCard extends CalibrationsCard {
private JPanel processPanel;
@ -123,9 +134,13 @@ public class CalibrationProcessCard extends CalibrationsCard {
}
process.setMethod((String) calMethod.getSelectedItem());
process.setVersion(version.getText());
process.setSoftware(software.getText());
String soft = warnNotNull(getPamWizard(), software, "Calibration Method");
if (soft == null) {
return false;
}
process.setSoftware(soft);
if (software.getText() == null) {
getPamWizard().showWarning("You must specify the calibratin method used");
getPamWizard().showWarning("You must specify the calibration method used");
}
QualityAssurance qa = calibration.getQualityAssurance();
@ -133,7 +148,11 @@ public class CalibrationProcessCard extends CalibrationsCard {
qa = new QualityAssurance();
calibration.setQualityAssurance(qa);
}
qa.setComment(qaComment.getText());
String t = warnNotNull(getPamWizard(), qaComment, "QA Comment");
if (t == null) {
return false;
}
qa.setComment(t);
qa.setQuality(QualityValueBasic.fromValue((String) qaQuality.getSelectedItem()));
// need to add a few fixed things for this to work...
@ -142,11 +161,27 @@ public class CalibrationProcessCard extends CalibrationsCard {
if (params == null) {
params = new Parameters();
process.setParameters(params);
// params.getAny().
}
try {
Helper.createRequiredElements(params);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// if (checkEmptyFields(qa) == false) {
// return false;
// }
// if (checkEmptyFields(process) == false) {
//// return false;
// }
return true;
}
@Override
public void setParams(Calibration calibration) {
if (calibration == null) {

View File

@ -3,8 +3,9 @@ package tethys.calibration.swing;
import PamView.wizard.PamWizard;
import PamView.wizard.PamWizardCard;
import nilus.Calibration;
import tethys.swing.TethysWizardCard;
abstract public class CalibrationsCard extends PamWizardCard<Calibration> {
abstract public class CalibrationsCard extends TethysWizardCard<Calibration> {
public CalibrationsCard(PamWizard pamWizard, String title) {
super(pamWizard, title);

View File

@ -28,6 +28,7 @@ import nilus.MetadataInfo;
import nilus.ResponsibleParty;
import tethys.TethysTimeFuncs;
import tethys.calibration.CalibrationHandler;
import tethys.niluswraps.NilusChecker;
import tethys.swing.export.ResponsiblePartyPanel;
public class CalibrationsContactCard extends CalibrationsCard {
@ -125,6 +126,11 @@ public class CalibrationsContactCard extends CalibrationsCard {
}
metaData.setContact(checkRPChildren(metaData.getContact()));
dataManager.getParams(metaData.getContact());
ResponsibleParty metaContact = metaData.getContact();
NilusChecker.removeEmptyFields(metaData);
if (metaData.getContact() == null) {
return PamDialog.showWarning(getPamWizard(), "Missing data", "The Data Manager fields must be completed");
}
metaData.setUpdateFrequency((String) updateInterval.getSelectedItem());
metaData.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis()));
@ -136,6 +142,10 @@ public class CalibrationsContactCard extends CalibrationsCard {
long millis = date.getTime();
cardParams.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(millis));
checkEmptyFields(rp);
checkEmptyFields(metaData);
return true;
}

View File

@ -13,6 +13,7 @@ import javax.swing.border.TitledBorder;
import PamView.panel.PamPanel;
import tethys.TethysControl;
import tethys.TethysState;
import tethys.calibration.CalibrationHandler;
import tethys.swing.TethysGUIPanel;
@ -63,4 +64,14 @@ public class CalibrationsMainPanel extends TethysGUIPanel {
return mainPanel;
}
@Override
public void updateState(TethysState tethysState) {
super.updateState(tethysState);
enableControls();
}
private void enableControls() {
exportButton.setEnabled(getTethysControl().isServerOk());
}
}

View File

@ -22,7 +22,10 @@ import tethys.Collection;
import tethys.TethysControl;
import tethys.database.TethysActions;
import tethys.database.TethysLogger;
import tethys.niluswraps.NilusChecker;
import tethys.output.TethysExportParams;
import tethys.reporter.TethysReport;
import tethys.reporter.TethysReporter;
/**
* Class containing functions for managing the database connection. Opening, closing,
@ -144,6 +147,9 @@ public class DBXMLConnect {
*/
public boolean postAndLog(Object nilusObject, String documentName) throws TethysException
{
boolean ok = NilusChecker.warnEmptyFields(tethysControl.getGuiFrame(), nilusObject);
TethysException e = null;
boolean success = false;
try {
@ -207,6 +213,8 @@ public class DBXMLConnect {
*/
boolean error = importReturn.contains("<Error");
boolean success = importReturn.contains("<Success>");
String name = tempFile.getName();
TethysReporter.getTethysReporter().addReport(new TethysReport(success, collection, name, name));
// error = !success; might be a better options.
if (error) {
throw new TethysException("Error posting to Tethys", importReturn);

View File

@ -45,6 +45,9 @@ import dataMap.OfflineDataMapPoint;
import generalDatabase.DBControlUnit;
import metadata.MetaDataContol;
import metadata.PamguardMetaData;
import nilus.AcousticDataQAType;
import nilus.AcousticDataQAType.Quality;
import nilus.AcousticDataQAType.Quality.FrequencyRange;
import nilus.Audio;
import nilus.ChannelInfo;
import nilus.ChannelInfo.DutyCycle;
@ -84,6 +87,7 @@ import tethys.deployment.swing.RecordingGapDialog;
import tethys.niluswraps.PDeployment;
import tethys.output.TethysExportParams;
import tethys.pamdata.AutoTethysProvider;
import tethys.reporter.TethysReporter;
import tethys.swing.DeploymentTableObserver;
/**
@ -387,12 +391,14 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
* @param selectedDeployments
*/
public void exportDeployments(ArrayList<RecordingPeriod> selectedDeployments) {
TethysReporter.getTethysReporter().clear();
if (deploymentExportOptions.separateDeployments) {
exportSeparateDeployments(selectedDeployments);
}
else {
exportOneDeploymnet(selectedDeployments);
}
TethysReporter.getTethysReporter().showReport(tethysControl.getGuiFrame(), true);
}
/**
@ -400,6 +406,14 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
*/
private void exportOneDeploymnet(ArrayList<RecordingPeriod> selectedDeployments) {
// do the lot, whatever ...
Float sampleRate = null;
AcquisitionControl daq = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.class, null);
if (daq != null) {
DaqSystem system = daq.findDaqSystem(null);
AcquisitionParameters daqParams = daq.acquisitionParameters;
sampleRate = daqParams.sampleRate;
}
selectedDeployments = getDeploymentOverview().getRecordingPeriods();
int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId();
RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(0).getRecordStart(),
@ -412,11 +426,37 @@ public class DeploymentHandler implements TethysStateObserver, DeploymentTableOb
deployment.setCruise(globalMeta.getCruise());
deployment.setSite(globalMeta.getSite());
if (selectedDeployments.size() > 1) {
// now need to remove the
SamplingDetails samplingDetails = deployment.getSamplingDetails();
samplingDetails.getChannel().clear();
for (int i = 0; i < selectedDeployments.size(); i++) {
addSamplingDetails(deployment, selectedDeployments.get(i));
// // now need to remove the sampling details - don't though, add invalid periods instead.
// SamplingDetails samplingDetails = deployment.getSamplingDetails();
// samplingDetails.getChannel().clear();
// for (int i = 0; i < selectedDeployments.size(); i++) {
// addSamplingDetails(deployment, selectedDeployments.get(i));
// }
/*
* Instead, we're putting invalid periods into the QA section.
*/
AcousticDataQAType qa = deployment.getQualityAssurance();
if (qa == null) {
deployment.setQualityAssurance(qa = new AcousticDataQAType());
}
List<Quality> qualityList = qa.getQuality();
for (int i = 1; i < selectedDeployments.size(); i++) {
long end = selectedDeployments.get(i-1).getRecordStop();
long start = selectedDeployments.get(i).getRecordStart();
Quality q = new Quality();
q.setStart(TethysTimeFuncs.xmlGregCalFromMillis(end));
q.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(start));
q.setCategory("unusable");
if (sampleRate != null) {
FrequencyRange f = q.getFrequencyRange();
if (f == null) {
q.setFrequencyRange(f = new FrequencyRange());
}
f.setLowHz(0);
f.setHighHz(sampleRate/2);
}
q.setComment("No data (probably off or out of water)");
qualityList.add(q);
}
}
DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect();

View File

@ -39,11 +39,12 @@ public class DeploymentDataCard extends PamWizardCard {
this.tethysControl = tethysControl;
deploymentHandler = tethysControl.getDeploymentHandler();
ButtonGroup bg = new ButtonGroup();
exportOne = new JRadioButton("Export a single detection document for all data");
exportMany = new JRadioButton("Export separate documents for each ad-hoc recording period");
exportOne = new JRadioButton("Export a single deployment document for all data");
exportMany = new JRadioButton("Export separate deployment documents for each ad-hoc recording period");
bg.add(exportOne);
bg.add(exportMany);
JPanel optsPanel = new JPanel(new GridBagLayout());
optsPanel.setBorder(new TitledBorder("Number of documents"));
GridBagConstraints c = new PamGridBagContraints();
@ -95,6 +96,15 @@ public class DeploymentDataCard extends PamWizardCard {
}
public void setParams(DeploymentExportOpts exportOptions, Deployment deployment) {
/*
* temp code to only allow export of multiple documents.
*/
// exportOptions.separateDeployments = true;
// exportOne.setEnabled(false);
// exportOne.setToolTipText("Feature not yet enabled");
exportOne.setSelected(exportOptions.separateDeployments == false);
exportMany.setSelected(exportOptions.separateDeployments == true);
setParams(deployment);

View File

@ -43,6 +43,7 @@ import tethys.output.DatablockSynchInfo;
import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams;
import tethys.pamdata.TethysDataProvider;
import tethys.reporter.TethysReporter;
import tethys.species.DataBlockSpeciesManager;
import tethys.swing.export.DetectionsExportWizard;
@ -587,6 +588,7 @@ public class DetectionsHandler {
this.dataBlock = dataBlock;
this.exportParams = exportParams;
this.exportObserver = exportObserver;
TethysReporter.getTethysReporter().clear();
}
public void publish(DetectionExportProgress exportProgress) {
@ -617,6 +619,7 @@ public class DetectionsHandler {
DetectionExportProgress prog = new DetectionExportProgress(null, null, 0, 0, 0, 0, DetectionExportProgress.STATE_COMPLETE);
tethysControl.exportedDetections(dataBlock);
exportObserver.update(prog);
TethysReporter.getTethysReporter().showReport(tethysControl.getGuiFrame(), true);
}
@Override

View File

@ -0,0 +1,417 @@
package tethys.niluswraps;
import java.awt.Window;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.renjin.methods.Methods;
import PamView.dialog.warn.WarnOnce;
import nilus.Calibration;
import nilus.Calibration.QualityAssurance;
import nilus.Helper;
import nilus.ResponsibleParty;
/**
* A few static checks of some nilus classes to see if it's
* worth writing them or not.
* @author dg50
*
*/
public class NilusChecker {
public static void main(String args[]) {
Calibration cal = new Calibration();
try {
Helper.createRequiredElements(cal);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
QualityAssurance qa;
cal.setQualityAssurance(qa = new QualityAssurance());
qa.setComment("Nothing to comment on ");
int removed = removeEmptyFields(cal);
System.out.printf("%d fields removed from object %s\n", removed, cal);
ArrayList<Field> missing = checkEmptyFields(cal);
for (Field field : missing) {
System.out.printf("Field %s is required but empty in %s\n", field.getName(), field.getDeclaringClass().toString());
}
}
public static boolean warnEmptyFields(Window owner, Object nilusObject) {
ArrayList<Field> emptyFields = findEmptyFields(nilusObject, true);
if (emptyFields == null || emptyFields.size() == 0) {
return true;
}
String msg = String.format("<html>One or more fields in the nilus object %s are required but empty:<br>", nilusObject.getClass().getName());
for (Field f : emptyFields) {
msg += String.format("<br>Field %s in object %s", f.getName(), f.getDeclaringClass().getName());
}
msg += "<br><br>It is likely that this document will fail to write to the Tethys database.</html>";
String tit = "Incomplete Tethys data";
WarnOnce.showWarning(owner, tit, msg, WarnOnce.WARNING_MESSAGE);
return false;
}
/**
* Find empty fields
* @param nilusObject object to search
* @param onlyRequired only list required fields.
* @return list of empty, and optionally also required, fields.
*/
public static ArrayList<Field> findEmptyFields(Object nilusObject, boolean onlyRequired) {
return findEmptyFields(nilusObject, new ArrayList<Field>(), onlyRequired);
}
private static ArrayList<Field> findEmptyFields(Object nilusObject, ArrayList<Field> found, boolean onlyRequired) {
if (nilusObject == null) {
return found;
}
int removed = 0;
Class<? extends Object> nilusClass = nilusObject.getClass();
if (nilusClass.getCanonicalName().contains("java.lang")) {
return found;
}
Method[] methods = nilusClass.getDeclaredMethods();
Field[] fields = nilusClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Method getter = findGetter(fields[i], methods);
if (getter == null) {
// System.out.printf("Unable to find getter for field %s in %s\n", fields[i].getName(), nilusClass.getName());
continue;
}
boolean required = isRequired(fields[i]);
// System.out.printf("Field %30s is %s required\n", fields[i].getName(), required ? " " : "NOT");
Object gotObj = null;
try {
gotObj = getter.invoke(nilusObject, new Object[0]);
} catch (IllegalAccessException | InvocationTargetException e) {
// System.out.printf("Unable to invoce getter %s on %s\n", getter.getName(), nilusObject);
continue;
}
boolean empty = isEmpty(gotObj);
if (empty) {
if (required || !onlyRequired) {
found.add(fields[i]);
}
}
else {
found = findEmptyFields(gotObj, found, onlyRequired);
}
}
return found;
}
/**
* Remove empty fields from a nilus object. <br>
* An empty field is a field that is null, or has a String that is empty, or
* only contains elements which are all themselves empty. i.e. an object that references
* empty objects will be considered empty.
* @param nilusObject
* @return number of empty fields removed.
*/
public static int removeEmptyFields(Object nilusObject) {
if (nilusObject == null) {
return 0;
}
int removed = 0;
Class<? extends Object> nilusClass = nilusObject.getClass();
if (nilusClass.getCanonicalName().contains("java.lang")) {
return 0;
}
Method[] methods = nilusClass.getDeclaredMethods();
Field[] fields = nilusClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Method getter = findGetter(fields[i], methods);
Method setter = findSetter(fields[i], methods);
if (getter == null) {
// System.out.printf("Unable to find getter for field %s in %s\n", fields[i].getName(), nilusClass.getName());
continue;
}
if (setter == null) {
// System.out.printf("Unable to find setter for field %s in %s\n", fields[i].getName(), nilusClass.getName());
continue;
}
boolean required = isRequired(fields[i]);
// System.out.printf("Field %30s is %s required\n", fields[i].getName(), required ? " " : "NOT");
Object gotObj = null;
try {
gotObj = getter.invoke(nilusObject, null);
} catch (IllegalAccessException | InvocationTargetException e) {
// System.out.printf("Unable to invoce getter %s on %s\n", getter.getName(), nilusObject);
continue;
}
boolean empty = isEmpty(gotObj);
if (empty && gotObj != null && canRemove(fields[i])) {
try {
// System.out.printf("Removing empty field %s in object %s\n", fields[i].getName(), nilusObject);
// Object args = new Object[1];
setter.invoke(nilusObject, new Object[1]);
removed++;
} catch (IllegalAccessException | InvocationTargetException e) {
System.out.printf("Unable to invoce setter %s on %s\n", getter.getName(), nilusObject);
continue;
}
}
else {
removed += removeEmptyFields(gotObj);
}
}
return removed;
}
/**
* Fields that can be removed.
* @param field
* @return
*/
private static boolean canRemove(Field field) {
if (field == null) {
return true;
}
Class fClass = field.getType();
if (fClass == String.class) {
return true;
}
if (List.class.isAssignableFrom(fClass)) {
return false;
}
if (fClass.isPrimitive()) {
return false;
}
String className = fClass.getCanonicalName();
if (className.contains("nilus.")) {
return true;
}
return false;
}
/**
* Check an object for empty and required fields.
* @param nilusObject
* @return a list of required empty fields in the nilusObjec and any objects references by that object.
*/
public static ArrayList<Field> checkEmptyFields(Object nilusObject) {
return checkEmptyFields(nilusObject, new ArrayList<Field>());
}
/**
* Check an object for empty and required fields.
* @param nilusObject
* @param emptyFields
* @return
*/
private static ArrayList<Field> checkEmptyFields(Object nilusObject, ArrayList<Field> emptyFields) {
if (nilusObject == null) {
return emptyFields;
}
Class<? extends Object> nilusClass = nilusObject.getClass();
if (nilusClass.isPrimitive()) {
return emptyFields;
}
if (nilusClass.getCanonicalName().contains("java.lang")) {
return emptyFields;
}
Method[] methods = nilusClass.getDeclaredMethods();
Field[] fields = nilusClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Method getter = findGetter(fields[i], methods);
Method setter = findSetter(fields[i], methods);
if (getter == null) {
// System.out.printf("Unable to find getter for field %s in %s\n", fields[i].getName(), nilusClass.getName());
continue;
}
if (setter == null) {
// System.out.printf("Unable to find setter for field %s in %s\n", fields[i].getName(), nilusClass.getName());
continue;
}
boolean required = isRequired(fields[i]);
// System.out.printf("Field %30s is %s required\n", fields[i].getName(), required ? " " : "NOT");
Object gotObj = null;
try {
gotObj = getter.invoke(nilusObject, null);
} catch (IllegalAccessException | InvocationTargetException e) {
// System.out.printf("Unable to invoce getter %s on %s\n", getter.getName(), nilusObject);
continue;
}
boolean empty = isEmpty(gotObj);
if (empty) {
if (required) {
emptyFields.add(fields[i]);
}
}
else {
checkEmptyFields(gotObj, emptyFields);
}
// if (required == true && empty == true) {
// System.out.printf("Field %s is required but empty in %s\n", fields[i].getName(), nilusObject.toString());
// }
}
return emptyFields;
}
/**
* See if a field has an annotation that indicates it's required.
* @param field field
* @return required
*/
private static boolean isRequired(Field field) {
Annotation[] annots = field.getAnnotations();
for (int a = 0; a < annots.length; a++) {
// System.out.printf("Field %s has annotation %d %s\n", fields[i].getName(), a, annots[a].toString());
String str = annots[a].toString();
if (str.contains("required=true")) {
return true;
}
}
return false;
}
/**
* Find a getter for a field. This will either be get... or is...
* @param field
* @param methods list of methods to search
* @return found method or null
*/
private static Method findGetter(Field field, Method[] methods) {
String name = field.getName();
String poss = "get"+name;
Method found = findMethod(poss, methods);
if (found != null) {
return found;
}
poss = "is" + name;
return findMethod(poss, methods);
}
/**
* Fine a setter for a field. This will always be set...
* @param field field
* @param methods list of methods to search
* @return found method or null
*/
private static Method findSetter(Field field, Method[] methods) {
String name = field.getName();
String poss = "set" + name;
return findMethod(poss, methods);
}
/**
* Find a method based on it's name (case insensitive).
* @param name name of method
* @param methods list of methods to search
* @return found method or null
*/
private static Method findMethod(String name, Method[] methods) {
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equalsIgnoreCase(name)) {
return methods[i];
}
}
return null;
}
/**
* Test if an object is empty. <br>
* An object is considered empty if any of the following criteria are met:
* <ul>
* <li>The object is null</li>
* <li>The object is a zero length string</li>
* <li>The object is not null, but all of it's fields satisfy this same criteria of being empty</li>
* <li>The object is a list which has no elements</li>
* </ul>
* Primitive types are never empty.
* @param nilusObject
* @return true if it's empty
*/
public static boolean isEmpty(Object nilusObject) {
if (nilusObject == null) {
return true;
}
if (nilusObject instanceof String) {
String str = (String) nilusObject;
return (str.length() == 0);
}
if (nilusObject instanceof List) {
return isEmptyList((List) nilusObject);
}
if (nilusObject.getClass().isPrimitive()) {
return false;
}
boolean empty = true;
// and check all getters
Class<? extends Object> nilusClass = nilusObject.getClass();
Method[] methods = nilusClass.getDeclaredMethods();
// searching for getters.
int nGet = 0;
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getName().startsWith("get") && method.getParameterCount() == 0) {
nGet ++;
try {
Object got = method.invoke(nilusObject, null);
if (got != null) {
if (got instanceof String) {
if (isEmptyString((String) got) == false) {
empty = false;
}
}
else if (got instanceof List<?>) {
if (isEmptyList((List) got) == false) {
empty = false;
}
}
else if (isEmpty(got) == false) {// it's some other class type, so recurecively ask back here.
empty = false;
}
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
return false; // better save than sorry if we don't understand.
}
}
}
if (nGet == 0) {
// there weren't any understandable getters, so assume not empty. May be some other primitive type.
empty = false;
}
return empty;
}
/**
* A list is considered empty if it has no elements
* @param list
* @return true if empty
*/
private static boolean isEmptyList(List list) {
if (list == null) {
return true;
}
return list.size() == 0;
}
/**
* A String is empty if it is null or of zero length
* @param string string
* @return true if empty
*/
public static boolean isEmptyString(String string) {
if (string == null || string.length() == 0) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,56 @@
package tethys.reporter;
import tethys.Collection;
public class TethysReport {
private boolean success;
private Collection collection;
private String docName;
private String docId;
/**
* @param success
* @param collection
* @param docName
* @param docId
*/
public TethysReport(boolean success, Collection collection, String docName, String docId) {
this.success = success;
this.collection = collection;
this.docName = docName;
this.docId = docId;
}
/**
* @return the success
*/
public boolean isSuccess() {
return success;
}
/**
* @return the collection
*/
public Collection getCollection() {
return collection;
}
/**
* @return the docName
*/
public String getDocName() {
return docName;
}
/**
* @return the docId
*/
public String getDocId() {
return docId;
}
}

View File

@ -0,0 +1,118 @@
package tethys.reporter;
import java.awt.Window;
import java.util.ArrayList;
import PamController.PamController;
import PamView.dialog.warn.WarnOnce;
/**
* Set of functions to provide mesage reports on Tethys output. This
* will work with the existing WarnOnce type pop-up, the primary purpose
* of the functions here being to collate information, possibly from
* several document writes, before issuing an overall report.
* @author dg50
*
*/
public class TethysReporter {
private static TethysReporter singleInstance;
private ArrayList<TethysReport> tethysReports;
private TethysReporter() {
tethysReports = new ArrayList<TethysReport>();
}
/**
* Get the reporter.
* @return
*/
public static final TethysReporter getTethysReporter() {
if (singleInstance == null) {
singleInstance = new TethysReporter();
}
return singleInstance;
}
/**
* Clear all reports
*/
synchronized public void clear() {
tethysReports.clear();
}
/**
* Add a report after attempting to write a document
* @param report
*/
synchronized public void addReport(TethysReport report) {
tethysReports.add(report);
}
/**
* Get the current number of reports
* @return number of reports
*/
synchronized public int getSize() {
return tethysReports.size();
}
/**
* Get a summary string of all reported writes using html to separate each ont a separat eline
* @return
*/
synchronized public String getReportString() {
if (tethysReports.size() == 0) {
return "No reports";
}
String str = "<html>";
for (int i = 0; i < tethysReports.size(); i++) {
TethysReport aReport = tethysReports.get(i);
String res = aReport.isSuccess() ? "Success" : "Failure";
if (i > 0) {
str += "<br>";
}
str += String.format("%s writing %s document %s to Tethys", res, aReport.getCollection().collectionName(), aReport.getDocName());
}
str += "</html>";
return str;
}
/**
* Get a count of failed document writes
* @return failure count
*/
public int countFails() {
int fails = 0;
for (TethysReport aReport : tethysReports) {
if (aReport.isSuccess() == false) {
fails++;
}
}
return fails;
}
/**
* Show a report in a popup window
* @param clear clear the list of reports afterwards
*/
public void showReport(boolean clear) {
showReport(PamController.getMainFrame(), clear);
}
/**
* Show a report on a popup window
* @param window parent frame
* @param clear clear the list of reports afterwards
*/
public void showReport(Window window, boolean clear) {
boolean probs = countFails() > 0;
WarnOnce.showNamedWarning("TethysReporter", window, "Tethys Document Writer", getReportString(), WarnOnce.WARNING_MESSAGE);
if (clear) {
clear();
}
}
}

View File

@ -21,7 +21,7 @@ public class DataBlockSpeciesCodes {
/**
* Probably only to be used when there are no defined names, but helpful if it's set.
*/
private int itisDefault = ITISTypes.UNKNOWN;
private int itisDefault = ITISTypes.OTHER;
/**
* A default sound type, which can be used for all 'species', but can get

View File

@ -1,14 +1,23 @@
package tethys.species;
import java.io.StringReader;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import PamController.settings.output.xml.PAMGuardXMLPreview;
import PamController.settings.output.xml.PamguardXMLWriter;
import dbxml.Queries;
import tethys.TethysControl;
import tethys.dbxml.DBQueryResult;
import tethys.dbxml.DBXMLConnect;
import tethys.dbxml.DBXMLQueries;
import tethys.dbxml.TethysQueryException;
@ -81,4 +90,83 @@ public class ITISFunctions {
return new TethysITISResult(itisCode, taxunit, latin, vernacular);
}
public ArrayList<SpeciesMapItem> searchSpecies(String searchTerm) {
ArrayList<SpeciesMapItem> items = new ArrayList<SpeciesMapItem>();
String xQ = "let $target := \"thespeciessearchterm\" \r\n"
+ "return\r\n"
+ "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> {\r\n"
+ "\r\n"
+ " for $ranks0 in collection(\"ITIS_ranks\")/ranks/rank[\r\n"
+ " dbxml:contains(completename, $target) or \r\n"
+ " vernacular[dbxml:contains(name,$target)]]\r\n"
+ "return\r\n"
+ " <Record> {\r\n"
+ " $ranks0/tsn,\r\n"
+ " $ranks0/completename,\r\n"
+ " <vernacular>\r\n"
+ " {string-join($ranks0/vernacular/name, \", \")}\r\n"
+ " </vernacular>\r\n"
+ " } </Record>\r\n"
+ "} </Result>\r\n"
+ "";
xQ = xQ.replace("thespeciessearchterm", searchTerm);
DBXMLConnect dbXMLConnect = tethysControl.getDbxmlConnect();
DBXMLQueries dbxmlQueries = tethysControl.getDbxmlQueries();
Queries queries = dbXMLConnect.getTethysQueries();
String queryResult = null;
try {
queryResult = queries.QueryTethys(xQ);
} catch (Exception e) {
// TODO Auto-generated catch block
// e.printStackTrace();
return items;
}
DocumentBuilder builder = null;
Document doc = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
//Create DocumentBuilder with default configuration
builder = factory.newDocumentBuilder();
//Parse the content to Document object
doc = builder.parse(new InputSource(new StringReader(queryResult)));
} catch (Exception e) {
// e.printStackTrace();
// System.out.println(queryResult);
return items;
}
// print for now, then comment this out..
// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter();
// String fDoc = pamXMLWriter.getAsString(doc, true);
// System.out.println(fDoc);
// now unpack the xml document.
NodeList els = doc.getElementsByTagName("Record");
int n = els.getLength();
for (int i = 0; i < n; i++) {
Node aNode = els.item(i);
if (aNode instanceof Element) {
Element anEl = (Element) aNode;
String tsn = dbxmlQueries.getElementData(anEl, "tsn");
int nTSN = 0;
try {
nTSN = Integer.valueOf(tsn);
}
catch (NumberFormatException ex) {
System.out.println("Invalid TSN read from Tethys: " + tsn);
continue;
}
String completeName = dbxmlQueries.getElementData(anEl, "completename");
String vernacular = dbxmlQueries.getElementData(anEl, "vernacular");
SpeciesMapItem mapItem = new SpeciesMapItem(nTSN, "", "", completeName, vernacular);
items.add(mapItem);
}
}
return items;
}
}

View File

@ -7,12 +7,12 @@ package tethys.species;
*/
public class ITISTypes {
public static final int UNKNOWN = 0;
public static final int OTHER = 0;
public static final int ANTHROPOGENIC = 1;
public static final String getName(int code) {
switch (code) {
case UNKNOWN:
case OTHER:
return "Unknown";
case ANTHROPOGENIC:
return "Anthropogenic";

View File

@ -21,11 +21,11 @@ public class SpeciesTest {
public static void main(String[] args) {
SpeciesTest st = new SpeciesTest();
st.runJson();
// st.runJson();
// int spermWhale = 180488;
// st.getCodeInfo(spermWhale);
// st.runXQuery();
st.runXQuery();
}
private void getCodeInfo(int itisCode) {
@ -86,14 +86,31 @@ public class SpeciesTest {
// + " }</Deployment>\r\n"
// + "} </Result>";
String xQ = "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> {\r\n"
+ " for $rank0 in collection(\"ITIS_ranks\")/rank[tsn = \"180488\"]\r\n"
+ " return\r\n"
+ " <rank>{\r\n"
+ " $rank0/completename\r\n"
+ " }</rank>\r\n"
+ "} </Result>";
// String xQ = "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> {\r\n"
// + " for $rank0 in collection(\"ITIS_ranks\")/rank[tsn = \"180488\"]\r\n"
// + " return\r\n"
// + " <rank>{\r\n"
// + " $rank0/completename\r\n"
// + " }</rank>\r\n"
// + "} </Result>";
String xQ = "let $target := \"physeter\" \r\n"
+ "return\r\n"
+ "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> {\r\n"
+ "\r\n"
+ " for $ranks0 in collection(\"ITIS_ranks\")/ranks/rank[\r\n"
+ " dbxml:contains(completename, $target) or \r\n"
+ " vernacular[dbxml:contains(name,$target)]]\r\n"
+ "return\r\n"
+ " <Record> {\r\n"
+ " $ranks0/tsn,\r\n"
+ " $ranks0/completename,\r\n"
+ " <vernacular>\r\n"
+ " {string-join($ranks0/vernacular/name, \", \")}\r\n"
+ " </vernacular>\r\n"
+ " } </Record>\r\n"
+ "} </Result>\r\n"
+ "";
System.out.println(xQ);
JerseyClient jerseyClient = new JerseyClient(uri);
Queries queries = new Queries(jerseyClient);

View File

@ -7,12 +7,14 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
import PamController.PamController;
import PamView.PamGui;
import PamView.dialog.PamDialog;
import PamView.panel.PamNorthPanel;
import PamguardMVC.PamDataBlock;
import tethys.species.SpeciesMapManager;
@ -38,7 +40,16 @@ public class DataBlockSpeciesDialog extends PamDialog {
});
JPanel nPanel = new JPanel(new BorderLayout());
nPanel.setBorder(new TitledBorder("Code management"));
nPanel.add(BorderLayout.EAST, itisButton);
nPanel.add(BorderLayout.EAST, new PamNorthPanel(itisButton));
String otherMsg =
"<html>Specify an ITIS taxonomic serial number (coding)."
+ "<br>Press the Find button to look up TSNs by Latin or common name. "
+ "<br>Anthropogenic signals should be coded as Homo sapiens (180092). "
+ "<br>Noise Measurements and geophonic sounds should be coded as "
+ "\"Other Phenomena\" (-10). "
+ "<br>When known, a call or sound type should "
+ "be specified (see help for more information).</html>";
nPanel.add(BorderLayout.CENTER, new JLabel(otherMsg , JLabel.LEFT));
// JPanel nwBit = new JPanel(new FlowLayout());
// JButton exportButton = new JButton("Export");
// exportButton.addActionListener(SpeciesMapManager.getInstance().getExportAction(parentFrame));
@ -52,6 +63,7 @@ public class DataBlockSpeciesDialog extends PamDialog {
mainPanel.add(BorderLayout.NORTH, nPanel);
setDialogComponent(mainPanel);
setResizable(true);
setHelpPoint("utilities.tethys.docs.tethys_speciescodes");
}
protected void gotoITIS() {

View File

@ -0,0 +1,282 @@
package tethys.species.swing;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel;
import PamUtils.worker.PamWorkDialog;
import PamUtils.worker.PamWorkProgressMessage;
import PamView.dialog.PamDialog;
import PamView.dialog.PamGridBagContraints;
import PamView.dialog.warn.WarnOnce;
import PamView.tables.SwingTableColumnWidths;
import PamView.tables.TableColumnWidthData;
import tethys.TethysControl;
import tethys.species.ITISFunctions;
import tethys.species.SpeciesMapItem;
public class SpeciesSearchDialog extends PamDialog {
private static final long serialVersionUID = 1L;
private TethysControl tethysControl;
private SpeciesMapItem selectedItem;
private static SpeciesSearchDialog singleInstance;
private JTextField searchText;
private JButton searchButton;
private JTable resultTable;
private ArrayList<SpeciesMapItem> speciesMapItems;
private DataModel tableModel;
private volatile PamWorkDialog workDialog;
private Object synch = new Object();
private SpeciesSearchDialog(Window parentFrame, TethysControl tethysControl) {
super(parentFrame, "Species search", false);
this.tethysControl = tethysControl;
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new TitledBorder("Search Term"));
JPanel topPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new PamGridBagContraints();
topPanel.add(new JLabel("Latin or common name ", JLabel.RIGHT), c);
c.gridx++;
topPanel.add(searchText = new JTextField(12), c);
c.gridx++;
topPanel.add(searchButton = new JButton("search"), c);
mainPanel.add(BorderLayout.NORTH, topPanel);
searchButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
searchTethys();
}
});
searchText.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
searchTethys();
}
});
tableModel = new DataModel();
resultTable = new JTable(tableModel);
JPanel centPanel = new JPanel(new BorderLayout());
centPanel.add(BorderLayout.NORTH, new JLabel("Possible matches (select one)", JLabel.LEFT));
JScrollPane scrollPane = new JScrollPane(resultTable);
centPanel.add(BorderLayout.CENTER, scrollPane);
mainPanel.add(BorderLayout.CENTER, centPanel);
resultTable.addMouseListener(new TableMouse());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SwingTableColumnWidths("Species Search Dialog Table", resultTable);
}
});
setResizable(true);
setDialogComponent(mainPanel);
}
public static SpeciesMapItem showDialog(Window parentFrame, TethysControl tethysControl) {
if (singleInstance == null) {
singleInstance = new SpeciesSearchDialog(parentFrame, tethysControl);
}
singleInstance.setParams();
singleInstance.setVisible(true);
return singleInstance.selectedItem;
}
private void searchTethys() {
clearResults();
String str = searchText.getText();
if (str == null || str.length() == 0) {
return;
}
SearchWorker searchWorker = new SearchWorker(str);
searchWorker.execute();
// then open the dialog to block this thread.
synchronized (synch) {
workDialog = new PamWorkDialog(getOwner(), 1, "Searching Tethys Database");
workDialog.setVisible(true);
}
}
public void setMapItems(ArrayList<SpeciesMapItem> newMapItems) {
this.speciesMapItems = newMapItems;
tableModel.fireTableDataChanged();
}
private class SearchWorker extends SwingWorker<Integer, PamWorkProgressMessage> {
private String searchString;
private ArrayList<SpeciesMapItem> newMapItems;
public SearchWorker(String searchString) {
this.searchString = searchString;
}
@Override
protected Integer doInBackground() throws Exception {
String msg = String.format("Searching database for names containing \"%s\"", searchString);
PamWorkProgressMessage pm = new PamWorkProgressMessage(null, msg);
publish(pm);
try {
ITISFunctions itisFunctions = tethysControl.getItisFunctions();
this.newMapItems = itisFunctions.searchSpecies(searchString);
}
catch (Exception e) {
e.printStackTrace();
}
if (newMapItems == null) {
return 0;
}
if (workDialog != null) {
workDialog.setVisible(false);
workDialog.dispose();
}
return newMapItems.size();
}
@Override
protected void done() {
if (newMapItems == null || newMapItems.size() == 0) {
String msg = String.format("No matching ITIS types for search term %s", searchString);
WarnOnce.showNamedWarning("ITIS Lookup failure", getOwner(), "ITIS Code search", msg, WarnOnce.WARNING_MESSAGE);
}
setMapItems(newMapItems);
}
@Override
protected void process(List<PamWorkProgressMessage> chunks) {
for (PamWorkProgressMessage msg : chunks) {
synchronized (synch) {
if (workDialog != null) {
workDialog.update(msg);
}
}
}
}
}
private void setParams() {
searchText.setText(null);
clearResults();
}
private void clearResults() {
speciesMapItems = null;
selectedItem = null;
}
@Override
public boolean getParams() {
if (selectedItem == null) {
return showWarning("You must select a row from the table of species");
}
return selectedItem != null;
}
@Override
public void cancelButtonPressed() {
clearResults();
}
@Override
public void restoreDefaultSettings() {
// TODO Auto-generated method stub
}
private class TableMouse extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
if (speciesMapItems == null) {
return;
}
int selectedRow = resultTable.getSelectedRow();
if (selectedRow >= 0 && selectedRow < speciesMapItems.size()) {
selectedItem = speciesMapItems.get(selectedRow);
}
tableModel.fireTableDataChanged();
}
}
private class DataModel extends AbstractTableModel {
private String[] colNames = {"Select", "TSN", "Name", "Common Name"};
@Override
public int getRowCount() {
if (speciesMapItems == null) {
return 0;
}
return speciesMapItems.size();
}
@Override
public int getColumnCount() {
return colNames.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
SpeciesMapItem mapItem = speciesMapItems.get(rowIndex);
switch (columnIndex) {
case 0:
return mapItem == selectedItem;
case 1:
return mapItem.getItisCode();
case 2:
return mapItem.getLatinName();
case 3:
return mapItem.getCommonName();
}
return null;
}
@Override
public String getColumnName(int column) {
return colNames[column];
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return Boolean.class;
}
return super.getColumnClass(columnIndex);
}
}
}

View File

@ -85,7 +85,8 @@ public class SpeciesSubPanel {
pamguardName.setToolTipText("Internal name within PAMGuard module");
itisCode.setToolTipText("ITIS species code");
searchButton.setToolTipText("Search for species code");
searchButton.setToolTipText("<html>Either enter a code manually and press \"Find\" for additional information,<br>"
+ "or leave the code empty and press \"Find\" to search the Tethys database using common or scientific names.</html>" );
callType.setToolTipText("Descriptive name for call type or measurement");
latinName.setToolTipText("Scientific name");
commonName.setToolTipText("Common name");
@ -112,6 +113,17 @@ public class SpeciesSubPanel {
return;
}
ITISFunctions itisFunctions = tethysControl.getItisFunctions();
String itisString = this.itisCode.getText();
if (itisString == null || itisString.length() == 0) {
searchForCode(tethysControl, itisFunctions);
}
else {
getCodeInformation(tethysControl, itisFunctions, itisString);
}
// System.out.println(itisInfo);
}
private void getCodeInformation(TethysControl tethysControl, ITISFunctions itisFunctions, String itisString) {
int itisCode = 0;
try {
itisCode = Integer.valueOf(this.itisCode.getText());
@ -129,7 +141,15 @@ public class SpeciesSubPanel {
commonName.setText(itisInfo.getVernacular());
}
}
// System.out.println(itisInfo);
}
private void searchForCode(TethysControl tethysControl, ITISFunctions itisFunctions) {
SpeciesMapItem speciesItem = SpeciesSearchDialog.showDialog(tethysControl.getGuiFrame(), tethysControl);
if (speciesItem != null) {
itisCode.setText(String.format("%d", speciesItem.getItisCode()));
latinName.setText(speciesItem.getLatinName());
commonName.setText(speciesItem.getCommonName());
}
}
public JComponent getDialogComponent() {

View File

@ -147,7 +147,7 @@ public class DatablockSynchPanel extends TethysGUIPanel {
if (deployments == null || deployments.size() == 0) {
en = false;
}
exportButton.setEnabled(en);
exportButton.setEnabled(getTethysControl().isServerOk() & en);
}
public void showPopup(MouseEvent e, int row) {
@ -181,6 +181,9 @@ public class DatablockSynchPanel extends TethysGUIPanel {
// dataBlockSynchInfo = null;
// getSychInfos();
// getTethysControl().coun
break;
case UPDATESERVER:
enableExportButton();
}
synchTableModel.fireTableDataChanged();

View File

@ -154,6 +154,9 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT
case UPDATEMETADATA:
setInternal();
break;
case UPDATESERVER:
enableControls();
break;
}
}
@ -244,7 +247,7 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT
private void enableControls() {
boolean enable = selectedDeployments != null && selectedDeployments.size() > 0;
bigExportButton.setEnabled(enable);
bigExportButton.setEnabled(getTethysControl().isServerOk() & enable);
}
}

View File

@ -15,6 +15,7 @@ import javax.swing.border.TitledBorder;
import PamView.panel.PamPanel;
import tethys.TethysControl;
import tethys.TethysState;
import tethys.deployment.DeploymentHandler;
import tethys.deployment.RecordingPeriod;
@ -97,23 +98,37 @@ public class DeploymentsPanel extends TethysGUIPanel implements DeploymentTableO
enableExportButton();
}
private void enableExportButton() {
ArrayList<RecordingPeriod> selected = pamDeploymentsTable.getSelectedPeriods();
// and see if any warnings are needed: basically if anything selected has an output.
if (selected == null) {
exportButton.setEnabled(false);
return;
}
boolean existing = false;
if (selected != null) {
// and see if any warnings are needed: basically if anything selected has an output.
for (RecordingPeriod aPeriod: selected) {
if (aPeriod.getMatchedTethysDeployment() != null) {
existing = true;
break;
}
}
}
String warning = null;
if (existing) {
warning = " One or more deployment documents already exist. These must be deleted prior to exporting new documents";
exportWarning.setText(warning);
}
exportButton.setEnabled(selected.size()>0 & existing == false);
exportButton.setEnabled(selected.size()>0 & existing == false && getTethysControl().isServerOk());
}
@Override
public void updateState(TethysState tethysState) {
super.updateState(tethysState);
enableExportButton();
}

View File

@ -326,6 +326,9 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel {
* @return
*/
public ArrayList<RecordingPeriod> getSelectedPeriods() {
if (deploymentOverview == null) {
return null;
}
ArrayList<RecordingPeriod> allPeriods = deploymentOverview.getRecordingPeriods();
ArrayList<RecordingPeriod> selPeriods = new ArrayList();
int n = allPeriods.size();

View File

@ -0,0 +1,80 @@
package tethys.swing;
import java.awt.Window;
import java.lang.reflect.Field;
import java.util.ArrayList;
import javax.swing.JTextField;
import javax.swing.text.JTextComponent;
import PamView.dialog.PamDialog;
import PamView.dialog.warn.WarnOnce;
import PamView.wizard.PamWizard;
import PamView.wizard.PamWizardCard;
import tethys.niluswraps.NilusChecker;
/**
* Wizard card with a few extra functions for Tethys.
* @author dg50
*
* @param <T>
*/
abstract public class TethysWizardCard<T extends Object> extends PamWizardCard<T> {
private static final long serialVersionUID = 1L;
public TethysWizardCard(PamWizard pamWizard, String title) {
super(pamWizard, title);
}
/**
* Get a string out of a text component, but if the field is
* empty, return null rather than an empty, 0 length, string.
* @param textField
* @return string, or null if it's empty.
*/
public String getText(JTextComponent textField) {
String str = textField.getText();
if (str == null) {
return null;
}
if (str.length() == 0) {
return null;
}
return str;
}
/**
* Get a string from a text component, and warn if it is empty or null.
* @param owner parent window for warning
* @param textComponent text component
* @param name control name for warning text
* @return String if there was one, or null if it was null or empty.
*/
public String warnNotNull(Window owner, JTextComponent textComponent, String name) {
String str = getText(textComponent);
if (str != null) {
return str;
}
String warn = String.format("The field %s cannot be empty", name);
PamDialog.showWarning(owner, "Empty or missing data", warn);
return null;
}
/**
* Check for required empty fields.
* @param nilusObject
* @return true if all OK, false if there are required fields
*/
public boolean checkEmptyFields(Object nilusObject) {
NilusChecker.removeEmptyFields(nilusObject);
ArrayList<Field> emptyList = NilusChecker.checkEmptyFields(nilusObject);
if (emptyList.size() == 0) {
return true;
}
Field first = emptyList.get(0);
String msg = String.format("The field \"%s\" in \"%s\" is required by Tethys and must be completed", first.getName(), first.getDeclaringClass().getCanonicalName());
return PamDialog.showWarning(getPamWizard(), "Missing required data", msg);
}
}