Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
01869cb
remove unused imports, formatting
laura-iris Oct 23, 2025
047b0d7
fix pd.concat() warning
laura-iris Oct 23, 2025
6b78c4d
convert match to raw for string literal
laura-iris Oct 29, 2025
e8f6c7c
replace deprecated allow_stretch and keep_ratio with fit_mode
laura-iris Oct 29, 2025
21ef4ab
use service.earthscope.org, formatting updates
laura-iris Nov 12, 2025
3bf154d
update to match for string literal
laura-iris Nov 12, 2025
c85c6eb
add infer_objects at warning suggestion, supress warning for Downcast…
laura-iris Nov 12, 2025
c76b55b
fix datetime adapter deprecation
laura-iris Nov 13, 2025
8349642
fix deprecated .ix syntax
laura-iris Nov 13, 2025
b78f992
fix: move off deprecated df append
laura-iris Nov 13, 2025
1748f6f
fix df append deprecated behavior when generating report
laura-iris Nov 13, 2025
9693259
remove temporary logging
laura-iris Nov 13, 2025
dcbd7cd
better catch for empty ticket list, fix improper negation
laura-iris Nov 18, 2025
66ce6a1
update references from IRIS to EarthScope
laura-iris Nov 18, 2025
4e544bf
update reference from IRIS to EarthScope
laura-iris Nov 18, 2025
6c2941d
use new QA email [email protected]
laura-iris Nov 18, 2025
673746d
fix typo in link variable name
laura-iris Nov 18, 2025
eddf62c
Fix more earthscope naming, fix bug related to empty metadata
laura-iris Nov 19, 2025
f529f68
clean up old, commented code
laura-iris Nov 19, 2025
3c1e196
fix bug that caused averaging to fail
laura-iris Nov 20, 2025
c0a9d21
Update QuARG version to 1.1.2
laura-iris Nov 20, 2025
f4f6925
update dependency versions
laura-iris Nov 20, 2025
dd947ae
update install instructions
laura-iris Nov 20, 2025
9f2d22b
bump minor version to 1.2.0
laura-iris Nov 20, 2025
f6e0cc3
cleanup old comments
laura-iris Nov 20, 2025
91f9925
order the horizontal channels alphanumerically for thresholds that co…
laura-iris Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
553 changes: 254 additions & 299 deletions QuARG.py

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
For detailed documentation, check out [EarthScope.github.io/quarg/](https://EarthScope.github.io/quarg/DOCUMENTATION.html)
<br />

**QuARG**, the Quality Assurance Report Generator, is a Python client that allows network operators to generate quality assurance (QA) reports from start to finish. These reports utilize EarthScope’s database of [MUSTANG](http://service.iris.edu/mustang/) data quality metrics to find and highlight potential issues in the data, reducing the amount of time that analysts need to spend scanning the data for problems.
**QuARG**, the Quality Assurance Report Generator, is a Python client that allows network operators to generate quality assurance (QA) reports from start to finish. These reports utilize EarthScope’s database of [MUSTANG](http://service.earthscope.org/mustang/) data quality metrics to find and highlight potential issues in the data, reducing the amount of time that analysts need to spend scanning the data for problems.

Over the years that IRIS produced Quality Assurance Reports, we refined the process of generating a report into four primary steps:

Expand Down Expand Up @@ -97,16 +97,18 @@ Instructions for Linux or macOS (Intel chip)
```
cd quarg
conda update conda
conda create --name quarg -c conda-forge --file quarg-conda-install.txt
conda create --name quarg -c conda-forge python=3.12
conda activate quarg
conda install -c conda-forge --file quarg-conda-install.txt
```

Instructions for macOS (Apple M1 or M2 chip):
```
cd quarg
conda update conda
CONDA_SUBDIR=osx-64 conda create --name quarg -c conda-forge --file quarg-conda-install.txt
CONDA_SUBDIR=osx-64 conda create --name quarg -c conda-forge python=3.12
conda activate quarg
CONDA_SUBDIR=osx-64 conda install -c conda-forge --file quarg-conda-install.txt
```

See what is installed in our (quarg) environment with:
Expand Down
66 changes: 33 additions & 33 deletions docs/DOCUMENTATION.html

Large diffs are not rendered by default.

180 changes: 123 additions & 57 deletions findIssues.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,78 @@
import pandas as pd



# TODO: If ts_ metrics are used, must propagate through into the thresholds file

# ============================#
# LOAD INPUT ARGUMENTS
network = ''; station = ''; location = ''; channels = ''; start = '';end = ''; outfile = ''
network = ""
station = ""
location = ""
channels = ""
start = ""
end = ""
outfile = ""

args = reportUtils.getArgs()
start= args.start
start = args.start
end = args.end
# month = args.month

preferenceFile = args.preference_file

if not preferenceFile:
# If no preference file included, run everything
thresholdGroups = ['Completeness','Amplitudes','Timing','State of Health','Metadata']
groupsDict = {'Completeness':['avgGaps','gapsRatioGt12','noData'],
'Amplitudes' : ['flat','lowRms','hiAmp','lowAmp','badResp',
'avgSpikes','pegged','dead','noise1','noise2',
'medianUnique','rmsRatio','xTalk',
'gainRatio','nonCoher','polarity',
'dcOffsets','nSpikes','rmsRatio'],
'Timing' : ['poorTQual','suspectTime','noTime'],
'State of Health' : ['ampSat','filtChg','clip',
'spikes','glitch','padding','tSync'],
'Metadata' : ['zDip','horDip','zeroZ','lowScale','nonMSUnits']}

thresholdGroups = [
"Completeness",
"Amplitudes",
"Timing",
"State of Health",
"Metadata",
]
groupsDict = {
"Completeness": ["avgGaps", "gapsRatioGt12", "noData"],
"Amplitudes": [
"flat",
"lowRms",
"hiAmp",
"lowAmp",
"badResp",
"avgSpikes",
"pegged",
"dead",
"noise1",
"noise2",
"medianUnique",
"rmsRatio",
"xTalk",
"gainRatio",
"nonCoher",
"polarity",
"dcOffsets",
"nSpikes",
"rmsRatio",
],
"Timing": ["poorTQual", "suspectTime", "noTime"],
"State of Health": [
"ampSat",
"filtChg",
"clip",
"spikes",
"glitch",
"padding",
"tSync",
],
"Metadata": ["zDip", "horDip", "zeroZ", "lowScale", "nonMSUnits"],
}

else:
try:
with open(preferenceFile) as f:
exec(compile(f.read(), preferenceFile, "exec"))
except OSError:
print('Cannot open', preferenceFile)
print("Cannot open", preferenceFile)
quit()

# Commandline arguments override preference file values, if provided
if args.network:
network = args.network
Expand All @@ -71,7 +107,7 @@
if args.locations:
location = args.locations
if args.channels:
channels= args.channels
channels = args.channels
if args.outfile:
outfile = args.outfile
if args.metricsource:
Expand Down Expand Up @@ -102,53 +138,64 @@

if os.path.isfile(outfile):
resp1 = input("This file already exists - overwrite?[y/n]: ")
if (resp1.upper() == 'Y') or (resp1.upper() == 'YES'):
print('Removing existing file')
if (resp1.upper() == "Y") or (resp1.upper() == "YES"):
print("Removing existing file")
os.remove(outfile)
elif (resp1.upper() == 'N') or (resp1.upper()== 'NO'):
resp2= input('Should I append to the existing file?[y/n]: ')
if (not resp2.upper() == 'Y') and (not resp2.upper() == 'YES'):

elif (resp1.upper() == "N") or (resp1.upper() == "NO"):
resp2 = input("Should I append to the existing file?[y/n]: ")
if (not resp2.upper() == "Y") and (not resp2.upper() == "YES"):
quit("Exiting")
else:
print('Input not recognized, cancelling')
print("Input not recognized, cancelling")
quit()

# Load up list of metrics and metadata, for reference later on
if os.path.isfile(metrics_file):
with open(metrics_file,'r') as f:
with open(metrics_file, "r") as f:
metricsList = f.read().splitlines()
else:
# This should not happen unless running outside of QuARG since QuARG.py has a check before running findIssues.py
print("WARNING: Could not find list of MUSTANG metrics in file %s - does it exist?" % metrics_file)
print(" You can create this list by entering the Thresholds Editor - it will automatically generate there")
print(
"WARNING: Could not find list of MUSTANG metrics in file %s - does it exist?"
% metrics_file
)
print(
" You can create this list by entering the Thresholds Editor - it will automatically generate there"
)
quit()

if os.path.isfile(metadata_file):
with open(metadata_file,'r') as f:
with open(metadata_file, "r") as f:
metadataList = f.read().splitlines()
else:
# This should not happen unless running outside of QuARG since QuARG.py has a check before running findIssues.py
print("WARNING: Could not find list of IRIS metadata fields in file %s - does it exist?" % metadata_file)
print(" You can create this list by entering the Thresholds Editor - it will automatically generate there")
print(
"WARNING: Could not find list of EarthScope metadata fields in file %s - does it exist?"
% metadata_file
)
print(
" You can create this list by entering the Thresholds Editor - it will automatically generate there"
)
quit()


# ============================#
# GO THROUGH THRESHOLDS

# Add the header to the file
with open(outfile, 'w') as f:
with open(outfile, "w") as f:
f.write("# Threshold|Target|Start|End|Ndays|Status|Value|Notes\n")
f.close()
f.close()

# Get metadata dataframe at the beginning to use wherever necessary, since it is always the same
metadataDF = reportUtils.getMetadata(network, station, location, channels, start, end, metadataSource)
metadataDF = reportUtils.getMetadata(
network, station, location, channels, start, end, metadataSource
)


failedMetricsAll = list()
failedThresholdsAll = list()
# thresholdFile = './groupsTEST.txt'
for thresholdGroup in thresholdGroups:
print()
print("Running %s Thresholds" % thresholdGroup)
Expand All @@ -157,46 +204,65 @@
except:
print(" Could not find any thresholds for %s" % thresholdGroup)
continue

thresholdsList.sort()

allMetrics, failedThresholds = thresholds.get_threshold_metrics(thresholdsList, thresholdFile)

allMetrics, failedThresholds = thresholds.get_threshold_metrics(
thresholdsList, thresholdFile
)
metadatas = [e for e in metadataList if e in allMetrics]
metrics = [e for e in metricsList if e in allMetrics]

# hasMetadata = False;
hasMetrics = False
# if len(metadatas) > 0:
# print("This thresholds Group contains some metadata fields")
# hasMetadata = True
if len(metrics) > 0:
hasMetrics = True


if hasMetrics:
metricDF, failedMetrics = reportUtils.mergeMetricDF(network, station, location, channels, start, end, metrics, metricSource)
metricDF, failedMetrics = reportUtils.mergeMetricDF(
network, station, location, channels, start, end, metrics, metricSource
)
else:
metricDF = pd.DataFrame(columns=['value','target','start','end','network','station','location','channel'])
metricDF = pd.DataFrame(
columns=[
"value",
"target",
"start",
"end",
"network",
"station",
"location",
"channel",
]
)
failedMetrics = list()

for failedThreshold in failedThresholds:
if not failedThreshold in failedThresholdsAll:
failedThresholdsAll.append(failedThreshold)

for failedMetric in failedMetrics:
if not failedMetric in failedMetricsAll:
failedMetricsAll.append(failedMetric)

# if hasMetrics == True and not metricDF.empty:

for threshold in thresholdsList:
if not threshold in failedThresholds:
thresholds.do_threshold(threshold, thresholdFile, metricDF, metadataDF, outfile, instruments, start, end, hasMetrics, chanTypes)
thresholds.do_threshold(
threshold,
thresholdFile,
metricDF,
metadataDF,
outfile,
instruments,
start,
end,
hasMetrics,
chanTypes,
)

with open('failedMetrics.txt','w') as f:
with open("failedMetrics.txt", "w") as f:
for failedThreshold in failedThresholdsAll:
f.write('threshold: %s\n' % failedThreshold)
f.write("threshold: %s\n" % failedThreshold)
for failedMetric in failedMetricsAll:
f.write('metric: %s\n' % failedMetric)
f.write("metric: %s\n" % failedMetric)

print("INFO: Completed generating issue file")

Loading