lstosa 0.10.7__py3-none-any.whl → 0.10.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lstosa
3
- Version: 0.10.7
3
+ Version: 0.10.9
4
4
  Summary: Onsite analysis pipeline for the CTA LST-1
5
5
  Author: María Láinez, José Enrique Ruiz, Lab Saha, Andrés Baquero, José Luis Contreras, Maximilian Linhoff
6
6
  Author-email: Daniel Morcuende <dmorcuen@ucm.es>
@@ -20,7 +20,7 @@ Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: astropy ~=5.0
23
- Requires-Dist: lstchain >=0.10.5
23
+ Requires-Dist: lstchain >=0.10.7
24
24
  Requires-Dist: matplotlib
25
25
  Requires-Dist: numpy
26
26
  Requires-Dist: pandas
@@ -1,9 +1,9 @@
1
1
  osa/__init__.py,sha256=crotf1NMTfNdZuCua_5T_jk3kvZrAAwVw4FPrfxv994,193
2
- osa/_version.py,sha256=Tqz9jfXJ9oRFN5_F1Y4Bx09mLTPY4KYQNRrHQQNprBE,413
3
- osa/conftest.py,sha256=b_26FciV8NZAdHj2S10E8cE4bettinLJY83Roe1a5L4,19358
4
- osa/job.py,sha256=acdiaRlHsc2GLyz2Oz_RBiawJo9QKK-tsajYdM7X0_Q,25393
2
+ osa/_version.py,sha256=8jtDtmBITB3xhjNoKEFLlUOHmnfoHfF78GzDAEdlgvA,413
3
+ osa/conftest.py,sha256=_NERtB9t-Oi7ykW4QDYy9ZbgsrYxXMmQ3QkDScH5JyE,19601
4
+ osa/job.py,sha256=OnjF88kTVdURcrIR9iPenATNx2HteDFlAKtOX4fD144,26603
5
5
  osa/osadb.py,sha256=pkCuYbEG-moHG0uQHxwB7giQAv2XTld4HJ5gdn1F1hA,2422
6
- osa/paths.py,sha256=nkXPUdQAzIlvB71lOERiPjgET58fj5DEbWazWqKbIJw,11869
6
+ osa/paths.py,sha256=28FJuyr9yDcxL6oA10TINr0CRVDBleQZU8rnqwlHNq4,12905
7
7
  osa/raw.py,sha256=ZNIsuqfx5ljoz_hwhSuafdKf-wr8-cxRJmel-A2endg,1337
8
8
  osa/report.py,sha256=sL2V7n8Y_UUaSDbWJY2o4UxDb4FU5AaFIRR8R25DB8o,4634
9
9
  osa/version.py,sha256=9T2TtuGBQeOy5PJDxMCeGlqx5baxLaq47VmFTDc09z8,796
@@ -36,7 +36,7 @@ osa/provenance/config/logger.yaml,sha256=hy_lH3DfbRFh2VM_iawI-c-3wE0cjTRHy465C2e
36
36
  osa/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  osa/scripts/autocloser.py,sha256=j7nRvglIv_Ol_GJzbcfqPHquBIWwQU515GkDRfc6_ho,14540
38
38
  osa/scripts/calibration_pipeline.py,sha256=g9o1chqCRRSo7GNuQZRABjGnJYjZyfhkPgRLPmuV994,5703
39
- osa/scripts/closer.py,sha256=eZLUlqot4EXwL9IlU21tKRZ2GvZ4i8ill0c2QdEkoDY,15219
39
+ osa/scripts/closer.py,sha256=FPqUHaZcKyI20u9PhBCSqO-IXa1El3w_0DRXTKBTVTg,17778
40
40
  osa/scripts/copy_datacheck.py,sha256=tfDs6oTdPbii4BOXp6bTHuED0xNJeqaPFrv6Ed7ZnWc,3104
41
41
  osa/scripts/datasequence.py,sha256=gXAp8arbLPEK-sca9VnME6-2XfUzBFIoEFchlUZYrXI,9260
42
42
  osa/scripts/gain_selection.py,sha256=yaz2fKicuCDgLbD3fhb3l9LBlXLEEO3yuZO-oDc2IPo,8084
@@ -50,7 +50,7 @@ osa/scripts/show_run_summary_tcu.py,sha256=SoDLVKdQHOJkfenFguBOfXf10Gyv7heXSQAFn
50
50
  osa/scripts/simulate_processing.py,sha256=NiRVYiwZENt_mnKncytgJT23_-tJMb1B5PswM12nnX4,6941
51
51
  osa/scripts/update_source_catalog.py,sha256=GHwWFc-y6S4KkUJxUVM5drdAnVDD0-n3D-Tv3CCmh4E,7218
52
52
  osa/scripts/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- osa/scripts/tests/test_osa_scripts.py,sha256=xgBic0sW-eao2MSgYgnP-MB6QbmDK6IIY69m0SGdzaA,12588
53
+ osa/scripts/tests/test_osa_scripts.py,sha256=1B2UFc3idLGSyqeuzf4IEvb6sEbtfaV8ogj1LoEoEyQ,12851
54
54
  osa/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  osa/tests/test_jobs.py,sha256=F0jsHZ9BYB_cCHHxlXyO9v1E5_-mBJhuFtshtsAtnXo,15260
56
56
  osa/tests/test_osa.py,sha256=QCOsjUgPuNMHoef3Ym2sDXVjun2LaBrfKyroAIH-os8,415
@@ -75,9 +75,9 @@ osa/workflow/dl3.py,sha256=kz7L5jcKHFJ--UdQ8HQKLzWO6nxc2LLOTz42ExcqzTk,9921
75
75
  osa/workflow/stages.py,sha256=WYgUM2XDIaUjCc4_Zs_VSGW6gk73EaKcHk6ZMnPds74,6692
76
76
  osa/workflow/tests/test_dl3.py,sha256=aY5bb-8OcZGAXG3JPCZihChzkA_GsWjRIa31BHZn3Dg,299
77
77
  osa/workflow/tests/test_stages.py,sha256=TmC00XFACWZp740TQeFaokWi3C50ovj_XGiySWrrdZk,3944
78
- lstosa-0.10.7.dist-info/LICENSE,sha256=h6iWot11EtMvaDaS_AvCHKLTNByO5wEbMyNj1c90y1c,1519
79
- lstosa-0.10.7.dist-info/METADATA,sha256=v28ofFiZTcCliCrNe6aGtQ-C9GxvHlJm__FfAdKTKfQ,7348
80
- lstosa-0.10.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
81
- lstosa-0.10.7.dist-info/entry_points.txt,sha256=e5x7xddaqZhfdZPsErhHInqR4UGHsxXIlylEbTie0_8,928
82
- lstosa-0.10.7.dist-info/top_level.txt,sha256=_Tj8zVHdrOoWZuuWTHbDpNofxW0imUmKdlXhnxsXJek,4
83
- lstosa-0.10.7.dist-info/RECORD,,
78
+ lstosa-0.10.9.dist-info/LICENSE,sha256=h6iWot11EtMvaDaS_AvCHKLTNByO5wEbMyNj1c90y1c,1519
79
+ lstosa-0.10.9.dist-info/METADATA,sha256=iUVQ_IpWWQ4G57LjU50aR0miy3TjqRS11aKsTxM6BD4,7348
80
+ lstosa-0.10.9.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
81
+ lstosa-0.10.9.dist-info/entry_points.txt,sha256=e5x7xddaqZhfdZPsErhHInqR4UGHsxXIlylEbTie0_8,928
82
+ lstosa-0.10.9.dist-info/top_level.txt,sha256=_Tj8zVHdrOoWZuuWTHbDpNofxW0imUmKdlXhnxsXJek,4
83
+ lstosa-0.10.9.dist-info/RECORD,,
osa/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.10.7'
16
- __version_tuple__ = version_tuple = (0, 10, 7)
15
+ __version__ = version = '0.10.9'
16
+ __version_tuple__ = version_tuple = (0, 10, 9)
osa/conftest.py CHANGED
@@ -482,7 +482,14 @@ def datacheck_dl1_files(base_test_dir):
482
482
 
483
483
  @pytest.fixture(scope="session")
484
484
  def longterm_dir(base_test_dir):
485
- directory = base_test_dir / "OSA" / "DL1DataCheck_LongTerm" / "v0.1.0" / "20200117"
485
+ directory = base_test_dir / "OSA" / "DL1DataCheck_LongTerm" / prod_id / date_to_dir(date)
486
+ directory.mkdir(parents=True, exist_ok=True)
487
+ return directory
488
+
489
+
490
+ @pytest.fixture(scope="session")
491
+ def longterm_link_latest_dir(base_test_dir):
492
+ directory = base_test_dir / "OSA" / "DL1DataCheck_LongTerm" / "night_wise" / "all"
486
493
  directory.mkdir(parents=True, exist_ok=True)
487
494
  return directory
488
495
 
osa/job.py CHANGED
@@ -702,6 +702,37 @@ def get_sacct_output(sacct_output: StringIO) -> pd.DataFrame:
702
702
  return sacct_output
703
703
 
704
704
 
705
+ def get_closer_sacct_output(sacct_output) -> pd.DataFrame:
706
+ """
707
+ Fetch the information of jobs in the queue launched by AUTOCLOSER using the sacct
708
+ SLURM output and store it in a pandas dataframe.
709
+
710
+ Returns
711
+ -------
712
+ queue_list: pd.DataFrame
713
+ """
714
+ sacct_output = pd.read_csv(sacct_output, names=FORMAT_SLURM)
715
+
716
+ # Keep only the jobs corresponding to AUTOCLOSER sequences
717
+ # Until the merging of muon files is fixed, check all jobs except "lstchain_merge_muon_files"
718
+ sacct_output = sacct_output[
719
+ (sacct_output["JobName"].str.contains("lstchain_merge_hdf5_files"))
720
+ | (sacct_output["JobName"].str.contains("lstchain_check_dl1"))
721
+ | (sacct_output["JobName"].str.contains("lstchain_longterm_dl1_check"))
722
+ | (sacct_output["JobName"].str.contains("lstchain_cherenkov_transparency"))
723
+ | (sacct_output["JobName"].str.contains("provproces"))
724
+ ]
725
+
726
+ try:
727
+ sacct_output["JobID"] = sacct_output["JobID"].apply(lambda x: x.split("_")[0])
728
+ sacct_output["JobID"] = sacct_output["JobID"].str.strip(".batch").astype(int)
729
+
730
+ except AttributeError:
731
+ log.debug("No job info could be obtained from sacct")
732
+
733
+ return sacct_output
734
+
735
+
705
736
  def filter_jobs(job_info: pd.DataFrame, sequence_list: Iterable):
706
737
  """Filter the job info list to get the values of the jobs in the current queue."""
707
738
  sequences_info = pd.DataFrame([vars(seq) for seq in sequence_list])
osa/paths.py CHANGED
@@ -18,6 +18,7 @@ from osa.configs.datamodel import Sequence
18
18
  from osa.utils import utils
19
19
  from osa.utils.logging import myLogger
20
20
 
21
+
21
22
  log = myLogger(logging.getLogger(__name__))
22
23
 
23
24
  __all__ = [
@@ -346,3 +347,26 @@ def create_source_directories(source_list: list, cuts_dir: Path):
346
347
  if source is not None:
347
348
  source_dir = cuts_dir / source
348
349
  source_dir.mkdir(parents=True, exist_ok=True)
350
+
351
+
352
+ def get_latest_version_file(longterm_files: List[str]) -> Path:
353
+ """Get the latest version path of the produced longterm DL1 datacheck files for a given date."""
354
+ return max(
355
+ longterm_files,
356
+ key=lambda path: int(path.parents[1].name.split(".")[1])
357
+ if path.parents[1].name.startswith("v")
358
+ else "",
359
+ )
360
+
361
+
362
+ def create_longterm_symlink():
363
+ """If the created longterm DL1 datacheck file corresponds to the latest
364
+ version available, make symlink to it in the "all" common directory."""
365
+ nightdir = utils.date_to_dir(options.date)
366
+ longterm_dir = Path(cfg.get("LST1", "LONGTERM_DIR"))
367
+ linked_longterm_file = longterm_dir / f"night_wise/all/DL1_datacheck_{nightdir}.h5"
368
+ all_longterm_files = longterm_dir.rglob(f"v*/{nightdir}/DL1_datacheck_{nightdir}.h5")
369
+ latest_version_file = get_latest_version_file(all_longterm_files)
370
+
371
+ log.info("Symlink the latest version longterm DL1 datacheck file in the common directory.")
372
+ linked_longterm_file.symlink_to(latest_version_file)
osa/scripts/closer.py CHANGED
@@ -8,6 +8,7 @@ import re
8
8
  import shutil
9
9
  import subprocess
10
10
  import sys
11
+ import time
11
12
  from datetime import datetime, timedelta
12
13
  from pathlib import Path
13
14
  from typing import Tuple, Iterable, List
@@ -15,15 +16,21 @@ from typing import Tuple, Iterable, List
15
16
  from osa import osadb
16
17
  from osa.configs import options
17
18
  from osa.configs.config import cfg
18
- from osa.job import are_all_jobs_correctly_finished, save_job_information
19
+ from osa.job import (
20
+ are_all_jobs_correctly_finished,
21
+ save_job_information,
22
+ run_sacct,
23
+ get_closer_sacct_output
24
+ )
19
25
  from osa.nightsummary.extract import extract_runs, extract_sequences
20
26
  from osa.nightsummary.nightsummary import run_summary_table
21
- from osa.paths import destination_dir
27
+ from osa.paths import destination_dir, create_longterm_symlink
22
28
  from osa.raw import is_raw_data_available
23
29
  from osa.report import start
24
30
  from osa.utils.cliopts import closercliparsing
25
31
  from osa.utils.logging import myLogger
26
32
  from osa.utils.register import register_found_pattern
33
+ from osa.utils.mail import send_warning_mail
27
34
  from osa.utils.utils import (
28
35
  night_finished_flag,
29
36
  is_day_closed,
@@ -152,10 +159,13 @@ def post_process(seq_tuple):
152
159
  post_process_files(seq_list)
153
160
 
154
161
  # Merge DL1 datacheck files and produce PDFs. It also produces
155
- # the daily datacheck report using the longterm script.
162
+ # the daily datacheck report using the longterm script, and updates
163
+ # the longterm DL1 datacheck file with the cherenkov_transparency script.
156
164
  if cfg.getboolean("lstchain", "merge_dl1_datacheck"):
157
165
  list_job_id = merge_dl1_datacheck(seq_list)
158
- daily_datacheck(daily_longterm_cmd(list_job_id))
166
+ longterm_job_id = daily_datacheck(daily_longterm_cmd(list_job_id))
167
+ cherenkov_transparency(cherenkov_transparency_cmd(longterm_job_id))
168
+ create_longterm_symlink()
159
169
 
160
170
  # Extract the provenance info
161
171
  extract_provenance(seq_list)
@@ -169,6 +179,24 @@ def post_process(seq_tuple):
169
179
  if not options.no_dl2:
170
180
  merge_files(seq_list, data_level="DL2")
171
181
 
182
+ time.sleep(600)
183
+
184
+ # Check if all jobs launched by autocloser finished correctly
185
+ # before creating the NightFinished.txt file
186
+ n_max = 6
187
+ n = 0
188
+ while not all_closer_jobs_finished_correctly() & n <= n_max:
189
+ log.info(
190
+ "All jobs launched by autocloser did not finished correctly yet. "
191
+ "Checking again in 10 minutes..."
192
+ )
193
+ time.sleep(600)
194
+ n += 1
195
+
196
+ if n > n_max:
197
+ send_warning_mail(date=options.date)
198
+ return False
199
+
172
200
  if options.seqtoclose is None:
173
201
  database = cfg.get("database", "path")
174
202
  if database:
@@ -466,6 +494,7 @@ def daily_longterm_cmd(parent_job_ids: List[str]) -> List[str]:
466
494
 
467
495
  return [
468
496
  "sbatch",
497
+ "--parsable",
469
498
  "-D",
470
499
  options.directory,
471
500
  "-o",
@@ -484,11 +513,60 @@ def daily_datacheck(cmd: List[str]):
484
513
  log.info("Daily dl1 checks using longterm script.")
485
514
  log.debug(f"Executing {stringify(cmd)}")
486
515
 
516
+ if not options.simulate and not options.test and shutil.which("sbatch") is not None:
517
+ job = subprocess.run(
518
+ cmd,
519
+ encoding="utf-8",
520
+ capture_output=True,
521
+ text=True,
522
+ check=True,
523
+ )
524
+ job_id = job.stdout.strip()
525
+ return job_id
526
+ else:
527
+ log.debug("Simulate launching scripts")
528
+
529
+
530
+ def cherenkov_transparency_cmd(longterm_job_id: str) -> List[str]:
531
+ """Build the cherenkov transparency command."""
532
+ nightdir = date_to_dir(options.date)
533
+ datacheck_dir = destination_dir("DATACHECK", create_dir=False)
534
+ longterm_dir = Path(cfg.get("LST1", "LONGTERM_DIR")) / options.prod_id / nightdir
535
+ longterm_datacheck_file = longterm_dir / f"DL1_datacheck_{nightdir}.h5"
536
+
537
+ return [
538
+ "sbatch",
539
+ "-D",
540
+ options.directory,
541
+ "-o",
542
+ "log/cherenkov_transparency_%j.log",
543
+ f"--dependency=afterok:{longterm_job_id}",
544
+ "lstchain_cherenkov_transparency",
545
+ f"--update-datacheck-file={longterm_datacheck_file}",
546
+ f"--input-dir={datacheck_dir}",
547
+ ]
548
+
549
+
550
+ def cherenkov_transparency(cmd: List[str]):
551
+ """Update longterm dl1 check file with cherenkov transparency information."""
552
+ log.info("Update longterm dl1 check file with cherenkov_transparency script.")
553
+ log.debug(f"Executing {stringify(cmd)}")
554
+
487
555
  if not options.simulate and not options.test and shutil.which("sbatch") is not None:
488
556
  subprocess.run(cmd, check=True)
489
557
  else:
490
558
  log.debug("Simulate launching scripts")
491
559
 
492
560
 
561
+ def all_closer_jobs_finished_correctly():
562
+ """Check if all the jobs launched by autocloser finished correctly."""
563
+ sacct_output = run_sacct()
564
+ jobs_closer = get_closer_sacct_output(sacct_output)
565
+ if len(jobs_closer[jobs_closer["State"]!="COMPLETED"])==0:
566
+ return True
567
+ else:
568
+ return False
569
+
570
+
493
571
  if __name__ == "__main__":
494
572
  main()
@@ -179,6 +179,9 @@ def test_closer(
179
179
  drs4_time_calibration_files,
180
180
  systematic_correction_files,
181
181
  merged_run_summary,
182
+ longterm_dir,
183
+ longterm_link_latest_dir,
184
+ daily_datacheck_dl1_files,
182
185
  ):
183
186
  # First assure that the end of night flag is not set and remove it otherwise
184
187
  night_finished_flag = Path(
@@ -198,6 +201,10 @@ def test_closer(
198
201
  for obs_file in test_observed_data:
199
202
  assert obs_file.exists()
200
203
  assert merged_run_summary.exists()
204
+ assert longterm_dir.exists()
205
+ assert longterm_link_latest_dir.exists()
206
+ for check_file in daily_datacheck_dl1_files:
207
+ assert check_file.exists()
201
208
 
202
209
  run_program("closer", "-y", "-v", "-t", "-d", "2020-01-17", "LST1")
203
210
  closed_seq_file = running_analysis_dir / "sequence_LST1_01809.closed"
@@ -319,6 +326,7 @@ def test_daily_longterm_cmd():
319
326
 
320
327
  expected_cmd = [
321
328
  "sbatch",
329
+ "--parsable",
322
330
  "-D",
323
331
  options.directory,
324
332
  "-o",