Repository: cta-observatory/lstosa

Issue:
Use onsite scripts from lstchain
We need to adapt lstosa calibration sequence to run these two files on a daily basis for a whole-dataset reprocessing:
- [onsite_create_calibration_file.py](https://github.com/cta-observatory/cta-lstchain/blob/master/lstchain/scripts/onsite/onsite_create_calibration_file.py)
- [onsite_create_drs4_pedestal_file.py](https://github.com/cta-observatory/cta-lstchain/blob/master/lstchain/scripts/onsite/onsite_create_drs4_pedestal_file.py)

Currently, we run directly the scripts/Tools from lstchain so it needs to be adapted.

Maybe it would be good to implement a separate script to not leave out the current `calibrationsequence.py`

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index e487c73..503b4f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ test_osa
 testfiles
 prov.log
 docs/prov.log
+*.log
 
 # Compiled files
 *.py[co]
diff --git a/cfg/sequencer.cfg b/cfg/sequencer.cfg
index b04bc86..2538d19 100644
--- a/cfg/sequencer.cfg
+++ b/cfg/sequencer.cfg
@@ -7,15 +7,16 @@
 # Directories to replicate the LST1 directory tree.
 # The BASE and MONITORING need to be set by the user.
 BASE: test_osa/test_files0
-MONITORING: extra/monitoring
+MONITORING: %(BASE)s/monitoring
 # The directories below can be left untouched.
 R0_DIR: %(BASE)s/R0
 DRIVE_DIR: %(MONITORING)s/DrivePositioning
 RUN_SUMMARY_DIR: %(MONITORING)s/RunSummary
 ANALYSIS_DIR: %(BASE)s/running_analysis
-CALIB_DIR: %(BASE)s/calibration 
-PEDESTAL_DIR: %(BASE)s/calibration
-TIMECALIB_DIR: %(BASE)s/calibration
+CALIB_BASE_DIR: %(MONITORING)s/PixelCalibration/LevelA
+CALIB_DIR: %(CALIB_BASE_DIR)s/calibration
+PEDESTAL_DIR: %(CALIB_BASE_DIR)s/drs4_baseline
+TIMECALIB_DIR: %(CALIB_BASE_DIR)s/drs4_time_sampling_from_FF
 DL1_DIR: %(BASE)s/DL1
 DL1AB_DIR: %(BASE)s/DL1
 MUON_DIR: %(BASE)s/DL1
@@ -33,14 +34,12 @@ CALIB_PROD_ID: v01
 DL1_PROD_ID: tailcut84
 DL2_PROD_ID: tailcut84_model1
 
-
 [lstchain]
-# Calibration
-drs4_baseline: lstchain_data_create_drs4_pedestal_file
-time_calibration: lstchain_data_create_time_calibration_file
-charge_calibration: lstchain_create_calibration_file
+# Calibration steps in calibration pipeline script
+drs4_baseline: onsite_create_drs4_pedestal_file
+charge_calibration: onsite_create_calibration_file
 
-# Data processing
+# Data processing steps in datasequence script
 r0_to_dl1: lstchain_data_r0_to_dl1
 dl1ab: lstchain_dl1ab
 check_dl1: lstchain_check_dl1
diff --git a/docs/scripts/index.rst b/docs/scripts/index.rst
index bf13885..010957b 100644
--- a/docs/scripts/index.rst
+++ b/docs/scripts/index.rst
@@ -5,7 +5,7 @@ Scripts
 Scrips to be executed from the command line are described below:
 
 * `sequencer.py`_
-* `calibrationsequence.py`_
+* `calibration_pipeline.py`_
 * `datasequence.py`_
 * `closer.py`_
 * `provprocess.py`_
@@ -27,7 +27,7 @@ In the analysis folders you will find several types of ``sequence_*`` files:
 
 ``sequence_*.sh``
    File submitted to the working nodes.
-   It calls either the :ref:`calibrationsequence.py` and the
+   It calls either the :ref:`calibration_pipeline.py` and the
    :ref:`datasequence.py` depending on the arguments given to
    the sequencer and the type of sequence/run. You can submit
    these jobs manually by executing ``sbatch sequence_*.py``.
@@ -63,10 +63,10 @@ API/References
     :no-heading:
 
 
-.. _calibrationsequence.py:
+.. _calibration_pipeline.py:
 
-calibrationsequence.py
-++++++++++++++++++++++
+calibration_pipeline.py
++++++++++++++++++++++++
 
 It produces the calibration products.
 
@@ -74,13 +74,13 @@ Usage
 -----
 .. argparse::
    :module: osa.utils.cliopts
-   :func: calibration_sequence_argparser
-   :prog: calibrationsequence.py
+   :func: calibration_pipeline_argparser
+   :prog: calibration_pipeline.py
 
 API/References
 --------------
 
-.. automodapi:: osa.scripts.calibrationsequence
+.. automodapi:: osa.scripts.calibration_pipeline
     :no-heading:
 
 
diff --git a/extra/history_files/sequence_LST1_04183.history b/extra/history_files/sequence_LST1_04183.history
index 06ef21a..12d437c 100644
--- a/extra/history_files/sequence_LST1_04183.history
+++ b/extra/history_files/sequence_LST1_04183.history
@@ -1,3 +1,2 @@
-04179 lstchain_data_create_drs4_pedestal_file v0.7.0 Thu Mar 25 12:08:59 UTC 2021 drs4_pedestal.Run04179.0000.fits None 0
-04183 lstchain_data_create_time_calibration_file v0.7.0 Thu Mar 25 12:15:33 UTC 2021 time_calibration.Run04183.0000.hdf5 onsite_camera_calibration_param.json 0
-04183 lstchain_create_calibration_file v0.7.0 Thu Mar 25 12:21:57 UTC 2021 calibration.Run04183.0000.hdf5 onsite_camera_calibration_param.json 0
\ No newline at end of file
+04179 onsite_create_drs4_pedestal_file v0.7.0 2021-12-19 22:11 None None 0
+04183 onsite_create_calibration_file v0.7.0 2021-12-19 22:11 None None 0
\ No newline at end of file
diff --git a/extra/monitoring/RunSummary/RunSummary_20200117.ecsv b/extra/monitoring/RunSummary/RunSummary_20200117.ecsv
deleted file mode 100644
index 4fc69eb..0000000
--- a/extra/monitoring/RunSummary/RunSummary_20200117.ecsv
+++ /dev/null
@@ -1,24 +0,0 @@
-# %ECSV 0.9
-# ---
-# datatype:
-# - {name: run_id, datatype: int64}
-# - {name: n_subruns, datatype: int64}
-# - {name: run_type, datatype: string}
-# - {name: ucts_timestamp, datatype: int64}
-# - {name: run_start, datatype: int64}
-# - {name: dragon_reference_time, datatype: int64}
-# - {name: dragon_reference_module_id, datatype: int16}
-# - {name: dragon_reference_module_index, datatype: int16}
-# - {name: dragon_reference_counter, datatype: uint64}
-# - {name: dragon_reference_source, datatype: string}
-# delimiter: ','
-# meta: !!omap
-# - {date: '2020-01-17'}
-# - {lstchain_version: 0.7.0}
-# schema: astropy-2.0
-run_id,n_subruns,run_type,ucts_timestamp,run_start,dragon_reference_time,dragon_reference_module_id,dragon_reference_module_index,dragon_reference_counter,dragon_reference_source
-1804,6,DRS4,1579289727863850890,1579289712000000000,1579289727863850890,90,0,5863850400,ucts
-1805,5,PEDCALIB,1579291426030146503,1579291413000000000,1579291426030146503,90,0,2030146000,ucts
-1806,5,PEDCALIB,1579291932080485703,1579291917000000000,1579291932080485703,90,0,5080485200,ucts
-1807,11,DATA,1579292477145904430,1579292461000000000,1579292477145904430,90,0,6145904000,ucts
-1808,9,DATA,1579292985532016507,1579292975000000000,1579292985532016507,90,0,2532016000,ucts
\ No newline at end of file
diff --git a/osa/job.py b/osa/job.py
index b19da91..74acdc6 100644
--- a/osa/job.py
+++ b/osa/job.py
@@ -9,7 +9,7 @@ import time
 from io import StringIO
 from pathlib import Path
 from textwrap import dedent
-from typing import List, Iterable
+from typing import List, Iterable, Optional
 
 import matplotlib.pyplot as plt
 import pandas as pd
@@ -43,6 +43,7 @@ __all__ = [
     "filter_jobs",
     "run_sacct",
     "run_squeue",
+    "get_time_calibration_file",
 ]
 
 TAB = "\t".expandtabs(4)
@@ -55,7 +56,7 @@ FORMAT_SLURM = [
     "TotalCPU",
     "MaxRSS",
     "State",
-    "ExitCode"
+    "ExitCode",
 ]
 
 
@@ -73,7 +74,7 @@ def are_all_jobs_correctly_finished(sequence_list):
     -------
     flag: bool
     """
-    # FIXME: check based on squence.jobid exit status
+    # FIXME: check based on sequence.jobid exit status
     flag = True
     analysis_directory = Path(options.directory)
     for sequence in sequence_list:
@@ -144,38 +145,45 @@ def historylevel(history_file: Path, data_type: str):
     Returns the level from which the analysis should begin and
     the rc of the last executable given a certain history file.
 
+    Notes
+    -----
     Workflow for PEDCALIB sequences:
-     - DRS4->time calib is level 3->2
-     - time calib->charge calib is level 2->1
-     - charge calib 1->0 (sequence completed)
+     - Creation of DRS4 pedestal file, level 2->1
+     - Creation of charge calibration file, level 1->0
+     - Sequence completed when reaching level 0
 
     Workflow for DATA sequences:
-     - R0->DL1 is level 4->3
-     - DL1->DL1AB is level 3->2
-     - DATACHECK is level 2->1
-     - DL1->DL2 is level 1->0 (sequence completed)
-
-     TODO: Create a dict with the program exit status and prod id to
-      take into account not only the last history line but also the others.
+     - R0->DL1, level 4->3
+     - DL1->DL1AB, level 3->2
+     - DATACHECK, level 2->1
+     - DL1->DL2, level 1->0
+     - Sequence completed when reaching level 0
 
     Parameters
     ----------
     history_file: pathlib.Path
     data_type: str
-        Either 'DATA' or 'CALIBRATION'
+        Type of the sequence, either 'DATA' or 'PEDCALIB'
 
     Returns
     -------
-    level, exit_status: int, int
+    level : int
+    exit_status : int
     """
+
+    # TODO: Create a dict with the program exit status and prod id to take
+    #  into account not only the last history line but also the others.
+
     if data_type == "DATA":
         level = 4
     elif data_type == "PEDCALIB":
-        level = 3
+        level = 2
     else:
-        log.error("Type {data_type} not expected")
+        log.error(f"Type {data_type} not expected")
         sys.exit(1)
+
     exit_status = 0
+
     if history_file.exists():
         for line in history_file.read_text().splitlines():
             words = line.split()
@@ -189,7 +197,13 @@ def historylevel(history_file: Path, data_type: str):
             except (IndexError, ValueError) as err:
                 log.exception(f"Malformed history file {history_file}, {err}")
             else:
-                if program == cfg.get("lstchain", "r0_to_dl1"):
+                # Calibration sequence
+                if program == cfg.get("lstchain", "drs4_baseline"):
+                    level = 1 if exit_status == 0 else 2
+                elif program == cfg.get("lstchain", "charge_calibration"):
+                    level = 0 if exit_status == 0 else 1
+                # Data sequence
+                elif program == cfg.get("lstchain", "r0_to_dl1"):
                     level = 3 if exit_status == 0 else 4
                 elif program == cfg.get("lstchain", "dl1ab"):
                     if (exit_status == 0) and (prod_id == options.dl1_prod_id):
@@ -212,12 +226,7 @@ def historylevel(history_file: Path, data_type: str):
                     else:
                         level = 1
                         log.debug(f"DL2 prod ID: {options.dl2_prod_id} not produced yet")
-                elif program == cfg.get("lstchain", "drs4_baseline"):
-                    level = 2 if exit_status == 0 else 3
-                elif program == cfg.get("lstchain", "time_calibration"):
-                    level = 1 if exit_status == 0 else 2
-                elif program == cfg.get("lstchain", "charge_calibration"):
-                    level = 0 if exit_status == 0 else 1
+
                 else:
                     log.warning(f"Program name not identified {program}")
 
@@ -233,7 +242,7 @@ def prepare_jobs(sequence_list):
 
 def setrunfromparent(sequence_list):
     """
-    Create a dictionary with run number and its the parent run number.
+    Create a dictionary with run number and its parent run number.
 
     Parameters
     ----------
@@ -241,7 +250,7 @@ def setrunfromparent(sequence_list):
 
     Returns
     -------
-    dictionary with run number and its the parent run number
+    dictionary with run number and its parent run number
     """
     dictionary = {}
     for s1 in sequence_list:
@@ -266,29 +275,59 @@ def sequence_filenames(sequence):
     sequence.history = Path(options.directory) / f"{basename}{history_suffix}"
 
 
+def get_time_calibration_file(run_id: int) -> Path:
+    """
+    Return the time calibration file corresponding to a calibration run taken before
+    the run id given. If run_id is smaller than the first run id from the time
+    calibration files, return the first time calibration file available, which
+    corresponds to 1625.
+    """
+
+    time_calibration_dir = Path(cfg.get("LST1", "TIMECALIB_DIR"))
+    file_list = sorted(time_calibration_dir.rglob("pro/time_calibration.Run*.h5"))
+
+    if not file_list:
+        raise IOError("No time calibration file found")
+
+    for file in file_list:
+        run_in_list = int(file.name.split(".")[1].strip("Run"))
+        if run_id < 1625:
+            time_calibration_file = file_list[0]
+        elif run_in_list <= run_id:
+            time_calibration_file = file
+        else:
+            break
+
+    return time_calibration_file.resolve()
+
+
 def sequence_calibration_filenames(sequence_list):
     """Build names of the calibration and drive files."""
     nightdir = lstdate_to_dir(options.date)
     yy_mm_dd = date_in_yymmdd(nightdir)
-    drivefile = f"drive_log_{yy_mm_dd}.txt"
+    drive_file = f"drive_log_{yy_mm_dd}.txt"
 
     for sequence in sequence_list:
+
         if not sequence.parent_list:
-            cal_run_string = str(sequence.run).zfill(5)
-            calfile = f"calibration.Run{cal_run_string}.0000.h5"
-            timecalfile = f"time_calibration.Run{cal_run_string}.0000.h5"
-            ped_run_string = str(sequence.previousrun).zfill(5)
+            drs4_pedestal_run_id = sequence.previousrun
+            calibration_run_id = sequence.run
         else:
-            run_string = str(sequence.parent_list[0].run).zfill(5)
-            ped_run_string = str(sequence.parent_list[0].previousrun).zfill(5)
-            calfile = f"calibration.Run{run_string}.0000.h5"
-            timecalfile = f"time_calibration.Run{run_string}.0000.h5"
-        pedfile = f"drs4_pedestal.Run{ped_run_string}.0000.fits"
+            drs4_pedestal_run_id = sequence.parent_list[0].previousrun
+            calibration_run_id = sequence.parent_list[0].run
+
+        drs4_pedestal_file = f"drs4_pedestal.Run{drs4_pedestal_run_id:05d}.0000.h5"
+        calibration_file = f"calibration_filters_52.Run{calibration_run_id:05d}.0000.h5"
+
         # Assign the calibration and drive files to the sequence object
-        sequence.drive = drivefile
-        sequence.calibration = Path(options.directory) / calfile
-        sequence.time_calibration = Path(options.directory) / timecalfile
-        sequence.pedestal = Path(options.directory) / pedfile
+        sequence.drive = drive_file
+        sequence.pedestal = (
+            Path(cfg.get("LST1", "PEDESTAL_DIR")) / nightdir / "pro" / drs4_pedestal_file
+        )
+        sequence.calibration = (
+            Path(cfg.get("LST1", "CALIB_DIR")) / nightdir / "pro" / calibration_file
+        )
+        sequence.time_calibration = get_time_calibration_file(calibration_run_id)
 
 
 def plot_job_statistics(sacct_output: pd.DataFrame, directory: Path):
@@ -312,15 +351,12 @@ def plot_job_statistics(sacct_output: pd.DataFrame, directory: Path):
     sacct_output_filter = sacct_output.copy()
     sacct_output_filter = sacct_output_filter.dropna()
     # Remove the G from MaxRSS value and convert to float
-    sacct_output_filter["MaxRSS"] = sacct_output_filter["MaxRSS"].\
-        str.strip("G").astype(float)
+    sacct_output_filter["MaxRSS"] = (
+        sacct_output_filter["MaxRSS"].str.strip("G").astype(float)
+    )
 
     plt.figure()
-    plt.hist2d(
-        sacct_output_filter.MaxRSS,
-        sacct_output_filter.CPUTimeRAW / 3600,
-        bins=50
-    )
+    plt.hist2d(sacct_output_filter.MaxRSS, sacct_output_filter.CPUTimeRAW / 3600, bins=50)
     plt.xlabel("MaxRSS [GB]")
     plt.ylabel("Elapsed time [h]")
     directory.mkdir(exist_ok=True, parents=True)
@@ -434,7 +470,7 @@ def create_job_template(sequence, get_content=False):
     run_summary_dir = cfg.get("LST1", "RUN_SUMMARY_DIR")
 
     if sequence.type == "PEDCALIB":
-        command = "calibrationsequence"
+        command = "calibration_pipeline"
     elif sequence.type == "DATA":
         command = "datasequence"
     else:
@@ -445,32 +481,28 @@ def create_job_template(sequence, get_content=False):
 
     if options.verbose:
         commandargs.append("-v")
+    if options.simulate:
+        commandargs.append("-s")
     if options.configfile:
-        commandargs.append("-c")
-        commandargs.append(Path(options.configfile).resolve())
+        commandargs.append("--config")
+        commandargs.append(f"{Path(options.configfile).resolve()}")
     if sequence.type == "DATA" and options.no_dl2:
         commandargs.append("--no-dl2")
 
-    commandargs.append("-d")
-    commandargs.append(options.date)
-    commandargs.append("--prod-id")
-    commandargs.append(options.prod_id)
+    commandargs.append(f"--date={options.date}")
 
     if sequence.type == "PEDCALIB":
-        commandargs.append(sequence.pedestal)
-        commandargs.append(sequence.calibration)
-        ped_run_number = str(sequence.previousrun).zfill(5)
-        cal_run_number = str(sequence.run).zfill(5)
-        commandargs.append(ped_run_number)
-        commandargs.append(cal_run_number)
-        commandargs.append(Path(run_summary_dir) / f"RunSummary_{nightdir}.ecsv")
+        commandargs.append(f"--drs4-pedestal-run={sequence.previousrun:05d}")
+        commandargs.append(f"--pedcal-run={sequence.run:05d}")
 
     if sequence.type == "DATA":
-        commandargs.append(sequence.calibration.resolve())
-        commandargs.append(sequence.pedestal.resolve())
-        commandargs.append(sequence.time_calibration.resolve())
-        commandargs.append(Path(drivedir) / sequence.drive)
-        commandargs.append(Path(run_summary_dir) / f"RunSummary_{nightdir}.ecsv")
+        run_summary_file = Path(run_summary_dir) / f"RunSummary_{nightdir}.ecsv"
+        commandargs.append(f"--prod-id={options.prod_id}")
+        commandargs.append(f"--drs4-pedestal-file={sequence.pedestal.resolve()}")
+        commandargs.append(f"--time-calib-file={sequence.time_calibration.resolve()}")
+        commandargs.append(f"--pedcal-file={sequence.calibration.resolve()}")
+        commandargs.append(f"--drive-file={Path(drivedir).resolve() / sequence.drive}")
+        commandargs.append(f"--run-summary={run_summary_file.resolve()}")
 
     python_imports = dedent(
         """\
@@ -553,7 +585,7 @@ def submit_jobs(sequence_list, batch_command="sbatch"):
     for sequence in sequence_list:
         commandargs = [batch_command, "--parsable", no_display_backend]
         if sequence.type == "PEDCALIB":
-            commandargs.append(sequence.script)
+            commandargs.append(str(sequence.script))
             if options.simulate or options.no_calib or options.test:
                 log.debug("SIMULATE Launching scripts")
             else:
@@ -566,66 +598,20 @@ def submit_jobs(sequence_list, batch_command="sbatch"):
                     rc = error.returncode
                     log.exception(f"Command '{batch_command}' not found, error {rc}")
 
-            log.debug(commandargs)
+            log.debug(stringify(commandargs))
+
+        # Here sequence.jobid has not been redefined, so it keeps the one
+        # from previous time sequencer was launched.
 
-            # FIXME here s.jobid has not been redefined se it keeps the one
-            #  from previous time sequencer was launched
         # Add the job dependencies after calibration sequence
         if sequence.parent_list and sequence.type == "DATA":
             if not options.simulate and not options.no_calib and not options.test:
                 log.debug("Adding dependencies to job submission")
                 depend_string = f"--dependency=afterok:{parent_jobid}"
                 commandargs.append(depend_string)
-            # Old MAGIC style:
-            # for pseq in s.parent_list:
-            #     if pseq.jobid is not None:
-            #         if int(pseq.jobid) > 0:
-            #             depend_string += ":{0}".format(pseq.jobid)
-            #        """ Skip vetoed """
-            #        if s.action == 'Veto':
-            #            log.debug("job {0} has been vetoed".format(s.jobname))
-            #        elif s.action == 'Closed':
-            #            log.debug("job {0} is already closed".format(s.jobname))
-            #        elif s.action == 'Check' and s.state != 'C':
-            #            log.debug("job {0} checked to be dispatched but not completed yet".format(s.jobname))
-            #            if s.state == 'H' or s.state == 'R':
-            #                # Reset values
-            #                s.exit = None
-            #                if s.state == 'H':
-            #                    s.jobhost = None
-            #                    s.cputime = None
-            #                    s.walltime = None
-            #        elif s.action == 'Check' and s.state == 'C' and s.exit == 0:
-            #            log.debug("job {0} checked to be successful".format(s.jobname))
-            #        else:
-            #            if options.simulate == True:
-            #                commandargs.insert(0, 'echo')
-            #                s.action = 'Simulate'
-            #                # This jobid is negative showing it belongs to a simulated environment (not real jobid)
-            #                s.jobid = -1 - s.seq
-            #            else:
-            #                s.action = 'Submit'
-            #                # Reset the values to avoid misleading info from previous jobs
-            #                s.jobhost = None
-            #                s.state = 'Q'
-            #                s.cputime = None
-            #                s.walltime = None
-            #                s.exit = None
-            #            try:
-            #                stdout = subprocess.check_output(commandargs)
-            #            except subprocess.CalledProcessError as Error:
-            #                log.exception(Error, 2)
-            #            except OSError (ValueError, NameError):
-            #                log.exception("Command {0}, {1}".format(stringify(commandargs), NameError), ValueError)
-            #            else:
-            #                if options.simulate == False:
-            #                    try:
-            #                        s.jobid = int(stdout.split('.', 1)[0])
-            #                    except ValueError as e:
-            #                        log.warning("Wrong parsing of jobid {0} not being an integer, {1}".format(stdout.split('.', 1)[0], e))
-            #        job_list.append(s.jobid)
-            #        log.debug("{0} {1}".format(s.action, stringify(commandargs)))
+
             commandargs.append(sequence.script)
+
             if options.simulate:
                 log.debug("SIMULATE Launching scripts")
             elif options.test:
@@ -641,7 +627,9 @@ def submit_jobs(sequence_list, batch_command="sbatch"):
                     subprocess.check_output(commandargs, shell=False)
                 except subprocess.CalledProcessError as error:
                     log.exception(error)
-            log.debug(commandargs)
+
+            log.debug(stringify(commandargs))
+
         job_list.append(sequence.script)
 
     return job_list
@@ -649,14 +637,12 @@ def submit_jobs(sequence_list, batch_command="sbatch"):
 
 def run_squeue() -> StringIO:
     """Run squeue command to get the status of the jobs."""
-    if shutil.which('squeue') is None:
+    if shutil.which("squeue") is None:
         log.warning("No job info available since sacct command is not available")
         return StringIO()
 
     out_fmt = "%i,%j,%T,%M"  # JOBID, NAME, STATE, TIME
-    return StringIO(
-        subprocess.check_output(["squeue", "--me", "-o", out_fmt]).decode()
-    )
+    return StringIO(subprocess.check_output(["squeue", "--me", "-o", out_fmt]).decode())
 
 
 def get_squeue_output(squeue_output: StringIO) -> pd.DataFrame:
@@ -667,19 +653,22 @@ def get_squeue_output(squeue_output: StringIO) -> pd.DataFrame:
     df = pd.read_csv(squeue_output)
     # Remove the job array part of the jobid
     df["JOBID"] = df["JOBID"].apply(lambda x: x.split("_")[0]).astype("int")
-    df.rename(inplace=True, columns={
-        'STATE': 'State',
-        'JOBID': 'JobID',
-        'NAME': 'JobName',
-        'TIME': 'CPUTime'
-    })
+    df.rename(
+        inplace=True,
+        columns={
+            "STATE": "State",
+            "JOBID": "JobID",
+            "NAME": "JobName",
+            "TIME": "CPUTime",
+        },
+    )
     df["CPUTimeRAW"] = df["CPUTime"].apply(time_to_seconds)
     return df
 
 
 def run_sacct() -> StringIO:
     """Run sacct to obtain the job information."""
-    if shutil.which('sacct') is None:
+    if shutil.which("sacct") is None:
         log.warning("No job info available since sacct command is not available")
         return StringIO()
 
@@ -693,7 +682,7 @@ def run_sacct() -> StringIO:
         "--starttime",
         start_date,
         "-o",
-        ",".join(FORMAT_SLURM)
+        ",".join(FORMAT_SLURM),
     ]
     return StringIO(subprocess.check_output(sacct_cmd).decode())
 
@@ -717,7 +706,7 @@ def filter_jobs(job_info: pd.DataFrame, sequence_list: Iterable):
     """Filter the job info list to get the values of the jobs in the current queue."""
     sequences_info = pd.DataFrame([vars(seq) for seq in sequence_list])
     # Keep the jobs in the sacct output that are present in the sequence list
-    return job_info[job_info['JobName'].isin(sequences_info['jobname'])]
+    return job_info[job_info["JobName"].isin(sequences_info["jobname"])]
 
 
 def set_queue_values(
@@ -744,9 +733,7 @@ def set_queue_values(
     job_info_filtered = filter_jobs(job_info, sequence_list)
 
     for sequence in sequence_list:
-        df_jobname = job_info_filtered[
-            job_info_filtered["JobName"] == sequence.jobname
-        ]
+        df_jobname = job_info_filtered[job_info_filtered["JobName"] == sequence.jobname]
         sequence.tries = df_jobname["JobID"].nunique()
         sequence.action = "Check"
 
@@ -755,9 +742,8 @@ def set_queue_values(
             df_jobid_filtered = df_jobname[df_jobname["JobID"] == sequence.jobid]
             try:
                 sequence.cputime = time.strftime(
-                    "%H:%M:%S", time.gmtime(
-                        df_jobid_filtered["CPUTimeRAW"].median(skipna=False)
-                    )
+                    "%H:%M:%S",
+                    time.gmtime(df_jobid_filtered["CPUTimeRAW"].median(skipna=False)),
                 )
             except ValueError:
                 sequence.cputime = None
@@ -781,12 +767,12 @@ def update_sequence_state(sequence, filtered_job_info: pd.DataFrame) -> None:
         sequence.state = "PENDING"
     elif any("FAIL" in job for job in filtered_job_info.State):
         sequence.state = "FAILED"
-        sequence.exit = filtered_job_info[
-            filtered_job_info.State.values == "FAILED"
-            ]["ExitCode"].iloc[0]
+        sequence.exit = filtered_job_info[filtered_job_info.State.values == "FAILED"][
+            "ExitCode"
+        ].iloc[0]
     elif any("CANCELLED" in job for job in filtered_job_info.State):
         sequence.state = "CANCELLED"
-        mask = ['CANCELLED' in job for job in filtered_job_info.State]
+        mask = ["CANCELLED" in job for job in filtered_job_info.State]
         sequence.exit = filtered_job_info[mask]["ExitCode"].iloc[0]
     elif any("TIMEOUT" in job for job in filtered_job_info.State):
         sequence.state = "TIMEOUT"
@@ -801,8 +787,8 @@ def run_program_with_history_logging(
         run: str,
         prod_id: str,
         command: str,
-        input_file: Path,
-        config_file: Path
+        input_file: Optional[str] = None,
+        config_file: Optional[str] = None,
 ):
     """
     Run the program and log the output in the history file
@@ -814,8 +800,8 @@ def run_program_with_history_logging(
     run: str
     prod_id: str
     command: str
-    input_file: pathlib.Path
-    config_file: pathlib.Path
+    input_file: str, optional
+    config_file: str, optional
 
     Returns
     -------
@@ -827,30 +813,19 @@ def run_program_with_history_logging(
         rc = subprocess.run(command_args, check=True).returncode
     except subprocess.CalledProcessError as error:
         rc = error.returncode
-        history(
-            run,
-            prod_id,
-            command,
-            input_file.name,
-            config_file.name,
-            rc,
-            history_file
-        )
-        log.exception(
-            f"Could not execute {stringify(command_args)}, error: {error}"
-        )
-    else:
-        history(
-            run,
-            prod_id,
-            command,
-            input_file.name,
-            config_file.name,
-            rc,
-            history_file
-        )
-        if rc != 0:
-            sys.exit(rc)
-        return rc
+        log.exception(f"Could not execute {stringify(command_args)}, error: {error}")
+
+    history(
+        run=run,
+        prod_id=prod_id,
+        stage=command,
+        return_code=rc,
+        history_file=history_file,
+        input_file=input_file,
+        config_file=config_file
+    )
+
+    if rc != 0:
+        sys.exit(rc)
 
-    return None
+    return rc
diff --git a/osa/report.py b/osa/report.py
index 6f2892f..fcf53fc 100644
--- a/osa/report.py
+++ b/osa/report.py
@@ -3,6 +3,7 @@ from datetime import datetime
 from fnmatch import fnmatchcase
 from glob import glob
 from os.path import basename, getsize, join
+from pathlib import Path
 
 from osa.configs import config, options
 from osa.configs.config import cfg
@@ -11,7 +12,7 @@ from osa.utils.iofile import append_to_file
 
 log = logging.getLogger(__name__)
 
-__all__ = ["history", "start", "rule", "finished_assignments"]
+__all__ = ["history", "start", "finished_assignments"]
 
 
 def start(parent_tag: str):
@@ -40,11 +41,6 @@ def header(message):
     log.info(f"{prettyframe} {message} {prettyframe}")
 
 
-def rule():
-    prettyframe = int(config.cfg.get("OUTPUT", "REPORTWIDTH")) * "-"
-    log.info(prettyframe)
-
-
 def finished_assignments(sequence_list):
     """
     Report that the files have been produced and the night closed.
@@ -124,7 +120,15 @@ def finished_assignments(sequence_list):
     return dictionary
 
 
-def history(run, prod_id, program, input_file, input_card, rc, history_file) -> None:
+def history(
+        run: str,
+        prod_id: str,
+        stage: str,
+        return_code: int,
+        history_file: Path,
+        input_file=None,
+        config_file=None,
+) -> None:
     """
     Appends a history line to the history file. A history line
     reports the outcome of the execution of a lstchain executable.
@@ -135,20 +139,20 @@ def history(run, prod_id, program, input_file, input_card, rc, history_file) ->
         Run/sequence analyzed.
     prod_id : str
         Prod ID of the run analyzed.
-    program : str
-        Mars executable used.
-    input_file : str
-        If needed, some input file used for the lstchain executable
-    input_card : str
-        Input card used for the lstchain executable.
-    rc : str or int
+    stage : str
+        Stage of the analysis pipeline.
+    return_code : int
         Return code of the lstchain executable.
     history_file : pathlib.Path
         The history file that keeps track of the analysis steps.
+    input_file : str, optional
+        If needed, input file used for the lstchain executable
+    config_file : str, optional
+        Input card used for the lstchain executable.
     """
-    now = datetime.utcnow()
-    date_string = now.strftime("%a %b %d %X UTC %Y")
+    date_string = datetime.utcnow().isoformat(sep=" ", timespec="minutes")
     string_to_write = (
-        f"{run} {program} {prod_id} {date_string} {input_file} {input_card} {rc}\n"
+        f"{run} {stage} {prod_id} {date_string} "
+        f"{input_file} {config_file} {return_code}\n"
     )
     append_to_file(history_file, string_to_write)
diff --git a/osa/scripts/calibration_pipeline.py b/osa/scripts/calibration_pipeline.py
new file mode 100644
index 0000000..2d2b869
--- /dev/null
+++ b/osa/scripts/calibration_pipeline.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+
+"""
+Calibration pipeline
+
+Script to process the pedestal and calibration runs to produce the
+DRS4 pedestal and charge calibration files. It pipes together the two
+onsite calibration scripts from lstchain.
+"""
+
+import logging
+import sys
+from pathlib import Path
+
+from osa.configs import options
+from osa.configs.config import cfg
+from osa.job import historylevel
+from osa.job import run_program_with_history_logging
+from osa.utils.cliopts import calibration_pipeline_cliparsing
+from osa.utils.logging import myLogger
+
+__all__ = [
+    "calibration_sequence",
+    "calibrate_charge",
+    "drs4_pedestal",
+    "drs4_pedestal_command",
+    "calibration_file_command",
+]
+
+log = myLogger(logging.getLogger())
+
+
+def drs4_pedestal_command(drs4_pedestal_run_id: str) -> list:
+    """Build the create_drs4_pedestal command."""
+    base_dir = Path(cfg.get("LST1", "BASE")).resolve()
+    return [
+        'onsite_create_drs4_pedestal_file',
+        f'--run_number={drs4_pedestal_run_id}',
+        f'--base_dir={base_dir}',
+        '--no-progress',
+        '--yes'
+    ]
+
+
+def calibration_file_command(pedcal_run_id: str) -> list:
+    """Build the create_calibration_file command."""
+    base_dir = Path(cfg.get("LST1", "BASE")).resolve()
+    return [
+        'onsite_create_calibration_file',
+        f'--run_number={pedcal_run_id}',
+        f'--base_dir={base_dir}',
+        '--yes',
+        '--filters=52'
+    ]
+
+
+def calibration_sequence(drs4_pedestal_run_id: str, pedcal_run_id: str) -> int:
+    """
+    Handle the two stages for creating the daily calibration products:
+    DRS4 pedestal and charge calibration files.
+
+    Parameters
+    ----------
+    drs4_pedestal_run_id : str
+    pedcal_run_id : str
+
+    Returns
+    -------
+    rc : int
+        Return code
+    """
+    analysis_dir = Path(options.directory)
+    history_file = analysis_dir / f"sequence_LST1_{pedcal_run_id}.history"
+
+    level, rc = (2, 0) if options.simulate else historylevel(history_file, "PEDCALIB")
+
+    log.info(f"Going to level {level}")
+
+    if level == 2:
+        rc = drs4_pedestal(drs4_pedestal_run_id, history_file)
+        level -= 1
+        log.info(f"Going to level {level}")
+    if level == 1:
+        rc = calibrate_charge(pedcal_run_id, history_file)
+        level -= 1
+        log.info(f"Going to level {level}")
+    if level == 0:
+        log.info(f"Job for sequence {pedcal_run_id} finished without fatal errors")
+
+    return rc
+
+
+def drs4_pedestal(drs4_pedestal_run_id: str, history_file: Path) -> int:
+    """
+    Create a DRS4 pedestal file for baseline correction.
+
+    Parameters
+    ----------
+    drs4_pedestal_run_id : str
+        String with run number of the pedestal run
+    history_file : `pathlib.Path`
+        Path to the history file
+
+    Returns
+    -------
+    rc : int
+        Return code
+    """
+    if options.simulate:
+        return 0
+
+    cmd = drs4_pedestal_command(drs4_pedestal_run_id)
+
+    return run_program_with_history_logging(
+        command_args=cmd,
+        history_file=history_file,
+        run=drs4_pedestal_run_id,
+        prod_id=options.calib_prod_id,
+        command=cmd[0]
+    )
+
+
+def calibrate_charge(calibration_run: str, history_file: Path) -> int:
+    """
+    Create the calibration file to transform from ADC counts to photo-electrons
+
+    Parameters
+    ----------
+    calibration_run
+    history_file
+
+    Returns
+    -------
+    rc: int
+        Return code
+    """
+    if options.simulate:
+        return 0
+
+    cmd = calibration_file_command(pedcal_run_id=calibration_run)
+
+    return run_program_with_history_logging(
+        command_args=cmd,
+        history_file=history_file,
+        run=calibration_run,
+        prod_id=options.calib_prod_id,
+        command=cmd[0]
+    )
+
+
+def main():
+    """
+    Performs the calibration steps (obtain the drs4 baseline correction
+    and ADC to photo-electron coefficients)
+    """
+    drs4_pedestal_run, pedcal_run = calibration_pipeline_cliparsing()
+
+    if options.verbose:
+        log.setLevel(logging.DEBUG)
+    else:
+        log.setLevel(logging.INFO)
+
+    rc = calibration_sequence(drs4_pedestal_run, pedcal_run)
+    sys.exit(rc)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/osa/scripts/calibrationsequence.py b/osa/scripts/calibrationsequence.py
deleted file mode 100644
index aac8e59..0000000
--- a/osa/scripts/calibrationsequence.py
+++ /dev/null
@@ -1,455 +0,0 @@
-"""
-Script to process the calibration runs and produce the
-DRS4 pedestal, charge and time calibration files.
-"""
-
-import logging
-import os
-import subprocess
-import sys
-from pathlib import Path
-
-import lstchain.visualization.plot_calib as calib
-import lstchain.visualization.plot_drs4 as drs4
-import matplotlib.pyplot as plt
-
-from osa.configs import options
-from osa.configs.config import cfg
-from osa.job import historylevel, run_program_with_history_logging
-from osa.report import history
-from osa.utils.cliopts import calibration_sequence_cliparsing
-from osa.utils.logging import myLogger
-from osa.utils.utils import stringify, get_input_file
-
-__all__ = [
-    "calibration_sequence",
-    "calibrate_charge",
-    "calibrate_time",
-    "drs4_pedestal",
-    "plot_calibration_checks",
-    "plot_drs4_pedestal_check",
-    "drs4_pedestal_command",
-    "calibration_file_command",
-    "time_calibration_command"
-]
-
-log = myLogger(logging.getLogger())
-
-
-def calibration_sequence(
-        pedestal_filename: Path,
-        calibration_filename: Path,
-        ped_run_number: str,
-        cal_run_number: str,
-        run_summary_file: Path,
-) -> int:
-    """
-    Handle the three steps for creating the calibration products:
-    DRS4 pedestal, charge calibration and time calibration files
-
-    Parameters
-    ----------
-    pedestal_filename: pathlib.Path
-    calibration_filename: pathlib.Path
-    ped_run_number: str
-    cal_run_number: str
-    run_summary_file: pathlib.Path
-
-    Returns
-    -------
-    rc: int
-        Return code
-    """
-    history_file = \
-        Path(options.directory) / f"sequence_{options.tel_id}_{cal_run_number}.history"
-
-    level, rc = (3, 0) if options.simulate else historylevel(history_file, "PEDCALIB")
-
-    log.info(f"Going to level {level}")
-    if level == 3:
-        rc = drs4_pedestal(ped_run_number, pedestal_filename, history_file)
-        level -= 1
-        log.info(f"Going to level {level}")
-    if level == 2:
-        rc = calibrate_time(
-            cal_run_number,
-            pedestal_filename,
-            calibration_filename,
-            run_summary_file,
-            history_file,
-        )
-        level -= 1
-        log.info(f"Going to level {level}")
-    if level == 1:
-        rc = calibrate_charge(
-            ped_run_number,
-            cal_run_number,
-            pedestal_filename,
-            calibration_filename,
-            run_summary_file,
-            history_file,
-        )
-        level -= 1
-        log.info(f"Going to level {level}")
-    if level == 0:
-        log.info(f"Job for sequence {ped_run_number} finished without fatal errors")
-    return rc
-
-
-def drs4_pedestal_command(
-        input_file: Path,
-        output_file: Path,
-        max_events=20000
-) -> list:
-    """Build the command to run the drs4 pedestal calibration."""
-    return [
-        cfg.get("lstchain", "drs4_baseline"),
-        f"--input-file={input_file}",
-        f"--output-file={output_file}",
-        f"--max-events={max_events}",
-        "--overwrite"
-    ]
-
-
-def calibration_file_command(
-        calibration_run: str,
-        calibration_configfile: Path,
-        drs4_pedestal_path: Path,
-        calibration_output_file: Path,
-        run_summary_file: Path,
-) -> list:
-    """Build the command to run the calibration file creation."""
-    calibration_run_file = get_input_file(calibration_run)
-    ffactor_systematics = Path(cfg.get("lstchain", "ffactor_systematics"))
-    time_file = Path(options.directory) / f"time_{calibration_output_file.name}"
-    log_dir = Path(options.directory) / "log"
-    log_file = log_dir / f"calibration.Run{calibration_run}.0000.log"
-
-    return [
-        cfg.get("lstchain", "charge_calibration"),
-        f"--input_file={calibration_run_file}",
-        f"--output_file={calibration_output_file}",
-        "--EventSource.default_trigger_type=tib",
-        f"--LSTCalibrationCalculator.systematic_correction_path={ffactor_systematics}",
-        f"--LSTEventSource.EventTimeCalculator.run_summary_path={run_summary_file}",
-        f"--LSTEventSource.LSTR0Corrections.drs4_time_calibration_path={time_file}",
-        f"--LSTEventSource.LSTR0Corrections.drs4_pedestal_path={drs4_pedestal_path}",
-        f"--log-file={log_file}",
-        f"--config={calibration_configfile}"
-    ]
-
-
-def time_calibration_command(
-        calibration_run: str,
-        time_calibration_file: Path,
-        drs4_pedestal_file: Path,
-        run_summary: Path,
-) -> list:
-    """Build the command to run the time calibration."""
-    r0_path = Path(cfg.get("LST1", "R0_DIR")).resolve()
-    calibration_data_file = f"{r0_path}/*/LST-1.1.Run{calibration_run}.000*.fits.fz"
-
-    return [
-        cfg.get("lstchain", "time_calibration"),
-        f"--input-file={calibration_data_file}",
-        f"--output-file={time_calibration_file}",
-        f"--pedestal-file={drs4_pedestal_file}",
-        f"--run-summary-path={run_summary}",
-        "--max-events=53000"
-    ]
-
-
-def drs4_pedestal(
-        run_ped: str,
-        pedestal_output_file: Path,
-        history_file: Path,
-        max_events=20000
-):
-    """
-    Create a DRS4 pedestal file for baseline correction.
-
-    Parameters
-    ----------
-    run_ped: str
-        String with run number of the pedestal run
-    pedestal_output_file: pathlib.Path
-        Path to the output file to be created
-    history_file: pathlib.Path
-        Path to the history file
-    max_events: int
-
-    Returns
-    -------
-    Return code
-    """
-    if options.simulate:
-        return 0
-
-    input_file = get_input_file(run_ped)
-    output_file = Path(options.directory) / pedestal_output_file
-    calib_configfile = Path(cfg.get("lstchain", "calibration_config"))
-
-    command_args = drs4_pedestal_command(input_file, output_file, max_events)
-
-    rc = run_program_with_history_logging(
-        command_args,
-        history_file,
-        run_ped,
-        options.calib_prod_id,
-        command_args[0],
-        pedestal_output_file,
-        calib_configfile,
-    )
-
-    plot_drs4_pedestal_check(input_file, output_file, run_ped)
-
-    return rc
-
-
-def plot_drs4_pedestal_check(
-        input_file: Path,
-        output_file: Path,
-        drs4_run: str
-) -> None:
-    """Plot the check for the drs4 baseline correction."""
-    analysis_log_directory = Path(options.directory) / "log"
-    plot_file = analysis_log_directory / f"drs4_pedestal.Run{drs4_run}.0000.pdf"
-    log.info(f"Producing plots in {plot_file}")
-    drs4.plot_pedestals(input_file, output_file, drs4_run, plot_file)
-    plt.close("all")
-
-
-def calibrate_charge(
-        run_ped: str,
-        calibration_run: str,
-        pedestal_file: Path,
-        calibration_output_file: Path,
-        run_summary: Path,
-        history_file: Path,
-) -> int:
-    """
-    Create a charge calibration file to transform from ADC counts to photo-electrons
-
-    Parameters
-    ----------
-    run_ped
-    calibration_run
-    pedestal_file
-    calibration_output_file
-    history_file
-    run_summary: pathlib.Path
-        Path name of the run summary file
-
-    Returns
-    -------
-    rc: str
-        Return code
-    """
-    if options.simulate:
-        return 0
-
-    calib_configfile = Path(cfg.get("lstchain", "calibration_config"))
-    command_args = calibration_file_command(
-        calibration_run,
-        calib_configfile,
-        pedestal_file,
-        calibration_output_file,
-        run_summary
-    )
-
-    rc = run_program_with_history_logging(
-        command_args,
-        history_file,
-        calibration_run,
-        options.calib_prod_id,
-        command_args[0],
-        calibration_output_file,
-        calib_configfile,
-    )
-
-    if rc != 0:
-        sys.exit(rc)
-
-    plot_calibration_checks(calibration_run, run_ped, calibration_output_file)
-
-    return rc
-
-
-def plot_calibration_checks(
-        calibration_run: str,
-        drs4_run: str,
-        calibration_output_file: Path
-) -> None:
-    """Produce check plots."""
-    analysis_log_directory = Path(options.directory) / "log"
-    plot_file = analysis_log_directory / f"calibration.Run{calibration_run}." \
-                                         f"0000.pedestal.Run{drs4_run}.0000.pdf"
-    calib.read_file(calibration_output_file, tel_id=1)
-    log.info(f"Producing plots in {plot_file}")
-    calib.plot_all(
-        calib.ped_data, calib.ff_data, calib.calib_data, calibration_run, plot_file
-    )
-    plt.close("all")
-
-
-def calibrate_time(
-        calibration_run: str,
-        pedestal_file: Path,
-        calibration_output_file: Path,
-        run_summary: Path,
-        history_file: Path
-) -> int:
-    """
-    Create a time calibration file
-
-    Parameters
-    ----------
-    calibration_run: str
-        Run number of the calibration run
-    pedestal_file: pathlib.Path
-        Path to the pedestal file
-    calibration_output_file: pathlib.Path
-        Path to the output calibration file
-    run_summary: pathlib.Path
-        Path to the run summary file
-    history_file: pathlib.Path
-        Path to the history file
-
-    Returns
-    -------
-    rc: int
-        Return code
-    """
-    time_calibration_file = Path(options.directory) / \
-        f"time_{calibration_output_file.name}"
-
-    command_args = time_calibration_command(
-        calibration_run,
-        time_calibration_file,
-        pedestal_file,
-        run_summary
-    )
-
-    calib_configfile = "Default"
-
-    if options.simulate:
-        return 0
-
-    try:
-        log.info(f"Executing {stringify(command_args)}")
-        rc = subprocess.run(command_args, check=True).returncode
-    except subprocess.CalledProcessError as error:
-        rc = error.returncode
-        history(
-            calibration_run,
-            options.calib_prod_id,
-            command_args[0],
-            time_calibration_file.name,
-            calib_configfile,
-            rc,
-            history_file,
-        )
-        log.exception(f"Could not execute {stringify(command_args)}, error: {error}")
-    else:
-        history(
-            calibration_run,
-            options.calib_prod_id,
-            command_args[0],
-            time_calibration_file.name,
-            calib_configfile,
-            rc,
-            history_file,
-        )
-
-    if rc != 0:
-        log.warning(
-            "Not able to create time calibration file. Trying to use an existing file"
-        )
-        rc = link_ref_time_calibration(
-            time_calibration_file,
-            calibration_run,
-            history_file
-        )
-
-    return rc
-
-
-def link_ref_time_calibration(
-        time_calibration_file: Path,
-        calibration_run: str,
-        history_file: Path,
-        command="link_time_calibration",
-        calibration_configfile="Default",
-) -> int:
-    """
-    Link the reference time calibration file to the current time calibration file.
-    The reference time calibration file is defined in the cfg file.
-    """
-
-    ref_time_calibration_run = int(
-        cfg.get("lstchain", "reference_time_calibration_run")
-    )
-    calibration_path = Path(cfg.get("LST1", "CALIB_DIR"))
-    output_file = time_calibration_file
-    log.info(
-        f"Searching for file "
-        f"*/{options.calib_prod_id}/time_calibration.Run{ref_time_calibration_run:05d}*"
-    )
-    file_list = list(
-        calibration_path.rglob(
-            f'*/{options.calib_prod_id}/'
-            f'time_calibration.Run{ref_time_calibration_run:05d}*'
-        )
-    )
-
-    if file_list:
-        log.info(
-            f"Creating a symlink to an already produce time calibration "
-            f"file corresponding to run {ref_time_calibration_run:05d}"
-        )
-        ref_time_calibration_file = file_list[0]
-        os.symlink(ref_time_calibration_file, output_file)
-        rc = 0
-        history(
-            calibration_run,
-            options.calib_prod_id,
-            command,
-            time_calibration_file.name,
-            calibration_configfile,
-            rc,
-            history_file,
-        )
-    else:
-        log.error("Default time calibration file not found. Create it first.")
-        sys.exit(1)
-
-    return rc
-
-
-def main():
-    """
-    Performs the calibration steps (obtain the drs4 baseline correction,
-    drs4 time correction and ADC to photo-electron coefficients).
-    """
-    (
-        pedoutfile,
-        caloutfile,
-        calib_run_number,
-        ped_run_number,
-        run_summary,
-    ) = calibration_sequence_cliparsing()
-
-    if options.verbose:
-        log.setLevel(logging.DEBUG)
-    else:
-        log.setLevel(logging.INFO)
-
-    # run the routine
-    rc = calibration_sequence(
-        pedoutfile, caloutfile, calib_run_number, ped_run_number, run_summary
-    )
-    sys.exit(rc)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/osa/scripts/closer.py b/osa/scripts/closer.py
index 062b5fa..fc1e8d3 100644
--- a/osa/scripts/closer.py
+++ b/osa/scripts/closer.py
@@ -32,7 +32,6 @@ from osa.utils.utils import (
     gettag,
 )
 
-
 __all__ = [
     "use_night_summary",
     "is_raw_data_available",
@@ -52,8 +51,6 @@ log = myLogger(logging.getLogger())
 
 def main():
     """Main function in charge of closing the sequences."""
-
-    # set the options through cli parsing
     closercliparsing()
 
     if options.verbose:
@@ -125,12 +122,14 @@ def is_raw_data_available():
     return answer
 
 
-def is_sequencer_successful(seq_tuple: list):
+def is_sequencer_successful(seq_tuple: [bool, list]):
+    """Return a bool assessing whether the sequencer has successfully finished or not."""
     return seq_tuple[0]
 
 
 def ask_for_closing():
-    """Ask to the user whether sequences should be closed or not.
+    """
+    Ask the user whether sequences should be closed or not.
     A True (Y/y) closes, while False(N/n) answer stops the program.
 
     Returns
@@ -210,9 +209,6 @@ def post_process_files(seq_list: list):
     DL2_RE = re.compile(f"{options.dl2_prod_id}" + r"/dl2.*.(?:h5|hdf5|hdf)")
     MUONS_RE = re.compile(r"muons.*.fits")
     DATACHECK_RE = re.compile(r"datacheck_dl1.*.(?:h5|hdf5|hdf)")
-    CALIB_RE = re.compile(r"/calibration.*.(?:h5|hdf5|hdf)")
-    TIMECALIB_RE = re.compile(r"/time_calibration.*.(?:h5|hdf5|hdf)")
-    PEDESTAL_RE = re.compile(r"drs4.*.fits")
 
     pattern_files = dict(
         [
@@ -220,9 +216,6 @@ def post_process_files(seq_list: list):
             ("DL2", DL2_RE),
             ("MUON", MUONS_RE),
             ("DATACHECK", DATACHECK_RE),
-            ("PEDESTAL", PEDESTAL_RE),
-            ("CALIB", CALIB_RE),
-            ("TIMECALIB", TIMECALIB_RE),
         ]
     )
 
@@ -267,12 +260,14 @@ def is_finished_check(run_summary):
 
     Returns
     -------
-    seq_finished, seq_list: tuple
+    seq_finished: bool
+        True if all sequences are finished, False otherwise.
+    seq_list: list
     """
 
     sequence_success = False
     if run_summary is not None:
-        # building the sequences (the same way than the sequencer)
+        # building the sequences (the same way as the sequencer)
         subrun_list = extractsubruns(run_summary)
         run_list = extractruns(subrun_list)
         sequence_list = extractsequences(run_list)
@@ -296,7 +291,7 @@ def is_finished_check(run_summary):
         # empty file (no sensible data)
         sequence_success = True
         sequence_list = []
-    return sequence_success, sequence_list
+    return [sequence_success, sequence_list]
 
 
 def merge_dl1_datacheck(seq_list):
@@ -411,8 +406,7 @@ def extract_provenance(seq_list):
 
 
 def merge_dl2(sequence_list):
-    """Merge DL2 h5 files run-wise"""
-
+    """Merge DL2 h5 files run-wise."""
     log.info("Looping over the sequences and merging the dl2 files")
 
     dl2_dir = destination_dir("DL2", create_dir=False)
diff --git a/osa/scripts/datasequence.py b/osa/scripts/datasequence.py
index c9394be..2848c5c 100644
--- a/osa/scripts/datasequence.py
+++ b/osa/scripts/datasequence.py
@@ -131,7 +131,7 @@ def r0_to_dl1(
     r0_dir = Path(cfg.get("LST1", "R0_DIR")) / night_dir
     r0_file = r0_dir / f"LST-1.1.Run{run_str}.fits.fz"
 
-    command_args = [
+    cmd = [
         command,
         f"--input-file={r0_file}",
         f"--output-dir={options.directory}",
@@ -146,13 +146,13 @@ def r0_to_dl1(
         return 0
 
     return run_program_with_history_logging(
-        command_args,
-        history_file,
-        run_str,
-        options.prod_id,
-        command,
-        calibration_file,
-        pedestal_file,
+        command_args=cmd,
+        history_file=history_file,
+        run=run_str,
+        prod_id=options.prod_id,
+        command=command,
+        input_file=calibration_file.name,
+        config_file=pedestal_file.name,
     )
 
 
@@ -160,7 +160,7 @@ def r0_to_dl1(
 def dl1ab(run_str: str, history_file: Path):
     """
     Prepare and launch the actual lstchain script that is performing
-    the the image cleaning considering the interleaved pedestal information
+    the image cleaning considering the interleaved pedestal information
     and obtains shower parameters. It keeps the shower images.
 
     Parameters
@@ -184,7 +184,7 @@ def dl1ab(run_str: str, history_file: Path):
 
     # Prepare and launch the actual lstchain script
     command = cfg.get("lstchain", "dl1ab")
-    command_args = [
+    cmd = [
         command,
         f"--input-file={input_dl1_datafile}",
         f"--output-file={output_dl1_datafile}",
@@ -192,19 +192,19 @@ def dl1ab(run_str: str, history_file: Path):
         f"--config={dl1ab_config_file}",
     ]
     if not cfg.getboolean("lstchain", "store_image_dl1ab"):
-        command_args.append("--no-image=True")
+        cmd.append("--no-image=True")
 
     if options.simulate:
         return 0
 
     return run_program_with_history_logging(
-        command_args,
-        history_file,
-        run_str,
-        options.dl1_prod_id,
-        command,
-        input_dl1_datafile,
-        dl1ab_config_file,
+        command_args=cmd,
+        history_file=history_file,
+        run=run_str,
+        prod_id=options.dl1_prod_id,
+        command=command,
+        input_file=input_dl1_datafile.name,
+        config_file=dl1ab_config_file.name,
     )
 
 
@@ -231,7 +231,7 @@ def dl1_datacheck(run_str: str, history_file: Path):
 
     # Prepare and launch the actual lstchain script
     command = cfg.get("lstchain", "check_dl1")
-    command_args = [
+    cmd = [
         command,
         f"--input-file={input_dl1_datafile}",
         f"--output-dir={output_directory}",
@@ -244,13 +244,13 @@ def dl1_datacheck(run_str: str, history_file: Path):
         return 0
 
     return run_program_with_history_logging(
-        command_args,
-        history_file,
-        run_str,
-        options.dl1_prod_id,
-        command,
-        input_dl1_datafile,
-        dl1ab_config_file,
+        command_args=cmd,
+        history_file=history_file,
+        run=run_str,
+        prod_id=options.dl1_prod_id,
+        command=command,
+        input_file=input_dl1_datafile.name,
+        config_file=dl1ab_config_file.name,
     )
 
 
@@ -277,7 +277,7 @@ def dl1_to_dl2(run_str: str, history_file: Path):
     dl1_file = dl1ab_subdirectory / f"dl1_LST-1.Run{run_str}.h5"
 
     command = cfg.get("lstchain", "dl1_to_dl2")
-    command_args = [
+    cmd = [
         command,
         f"--input-file={dl1_file}",
         f"--output-dir={dl2_subdirectory}",
@@ -289,13 +289,13 @@ def dl1_to_dl2(run_str: str, history_file: Path):
         return 0
 
     return run_program_with_history_logging(
-        command_args,
-        history_file,
-        run_str,
-        options.dl2_prod_id,
-        command,
-        dl1_file,
-        dl2_config_file,
+        command_args=cmd,
+        history_file=history_file,
+        run=run_str,
+        prod_id=options.dl2_prod_id,
+        command=command,
+        input_file=dl1_file.name,
+        config_file=dl2_config_file.name,
     )
 
 
diff --git a/osa/scripts/sequencer.py b/osa/scripts/sequencer.py
index 4e34f6d..c2075b6 100644
--- a/osa/scripts/sequencer.py
+++ b/osa/scripts/sequencer.py
@@ -29,7 +29,7 @@ from osa.nightsummary.extract import (
     extractsubruns,
 )
 from osa.nightsummary.nightsummary import run_summary_table
-from osa.report import rule, start
+from osa.report import start
 from osa.utils.cliopts import sequencer_cli_parsing, set_default_directory_if_needed
 from osa.utils.logging import myLogger
 from osa.utils.utils import is_day_closed, gettag
@@ -127,7 +127,6 @@ def single_process(telescope):
     if is_report_needed:
         # insert_if_new_activity_db(sequence_list)
         # updatesequencedb(sequence_list)
-        # rule()
         report_sequences(sequence_list)
 
     return sequence_list
@@ -167,7 +166,6 @@ def stereo_process(telescope, s1_list, s2_list):
     update_sequence_status(sequence_list)
     submit_jobs(sequence_list)
     # finalizing report
-    rule()
     report_sequences(sequence_list)
     # cleaning
     options.directory = None
diff --git a/osa/scripts/simulate_processing.py b/osa/scripts/simulate_processing.py
index e1f92e1..956373f 100644
--- a/osa/scripts/simulate_processing.py
+++ b/osa/scripts/simulate_processing.py
@@ -119,10 +119,8 @@ def parse_template(template, idx):
             line = line.replace("'", "")
             line = line.replace(",", "")
             line = line.replace(r"{0}.format(str(subruns).zfill(4))", str(idx).zfill(4))
-            if "--stdout=" in line or "--stderr" in line or "srun" in line:
+            if "--stdout=" in line or "--stderr" in line:
                 continue
-            if "--prod-id" in line:
-                args.append("-s")
             args.append(line.strip())
         if "subprocess.run" in line:
             keep = True
@@ -138,7 +136,7 @@ def simulate_calibration(args):
 
 def simulate_subrun_processing(args):
     """Simulate subrun processing."""
-    run_str, subrun_idx = args[13].split(".")
+    run_str, subrun_idx = args[-2].split(".")
     log.info(f"Simulating process call for run {run_str} subrun {subrun_idx}")
     subprocess.run(args)
 
@@ -156,12 +154,12 @@ def simulate_processing():
     for sequence in sequence_list:
         processed = False
         for sub_list in sequence.subrun_list:
-            if sub_list.runobj.type != "DATA":
+            if sub_list.runobj.type == "PEDCALIB":
                 args_cal = parse_template(
                     create_job_template(sequence, get_content=True), 0
                 )
                 simulate_calibration(args_cal)
-            else:
+            elif sub_list.runobj.type == "DATA":
                 with mp.Pool() as poolproc:
                     args_proc = [
                         parse_template(
diff --git a/osa/utils/cliopts.py b/osa/utils/cliopts.py
index a755f19..f854730 100644
--- a/osa/utils/cliopts.py
+++ b/osa/utils/cliopts.py
@@ -19,8 +19,6 @@ from osa.utils.utils import (
 )
 
 __all__ = [
-    "calibration_sequence_cliparsing",
-    "calibration_sequence_argparser",
     "closer_argparser",
     "closercliparsing",
     "copy_datacheck_parsing",
@@ -39,6 +37,8 @@ __all__ = [
     "get_dl1_prod_id",
     "get_dl2_prod_id",
     "get_calib_prod_id",
+    "calibration_pipeline_cliparsing",
+    "calibration_pipeline_argparser",
 ]
 
 log = logging.getLogger(__name__)
@@ -70,14 +70,6 @@ def closer_argparser():
         default=False,
         help="rely on existing nightsumary file",
     )
-    parser.add_argument(
-        "-o",
-        "--outputdir",
-        action="store",
-        type=str,
-        dest="directory",
-        help="analysis output directory",
-    )
     parser.add_argument(
         "-r",
         "--reason",
@@ -119,14 +111,6 @@ def closer_argparser():
         default=False,
         help="make lots of noise for debugging",
     )
-    parser.add_argument(
-        "-w",
-        "--warnings",
-        action="store_true",
-        dest="warning",
-        default=False,
-        help="show useful warnings",
-    )
     parser.add_argument(
         "--stderr",
         action="store",
@@ -168,7 +152,6 @@ def closercliparsing():
     options.stderr = opts.stderr
     options.stdout = opts.stdout
     options.date = opts.date
-    options.directory = opts.directory
     options.noninteractive = opts.noninteractive
     options.simulate = opts.simulate
     options.test = opts.test
@@ -201,7 +184,8 @@ def closercliparsing():
         options.dl2_prod_id = options.prod_id
 
 
-def calibration_sequence_argparser():
+def calibration_pipeline_argparser():
+    """Command line parser for the calibration pipeline."""
     parser = ArgumentParser()
     parser.add_argument(
         "-c",
@@ -219,14 +203,6 @@ def calibration_sequence_argparser():
         dest="date",
         help="observation ending date YYYY_MM_DD [default today]",
     )
-    parser.add_argument(
-        "-o",
-        "--outputdir",
-        action="store",
-        type=str,
-        dest="directory",
-        help="write files to output directory",
-    )
     parser.add_argument(
         "-v",
         "--verbose",
@@ -235,14 +211,6 @@ def calibration_sequence_argparser():
         default=False,
         help="make lots of noise for debugging",
     )
-    parser.add_argument(
-        "-w",
-        "--warnings",
-        action="store_true",
-        dest="warning",
-        default=False,
-        help="show useful warnings",
-    )
     parser.add_argument(
         "--stderr",
         action="store",
@@ -273,51 +241,38 @@ def calibration_sequence_argparser():
         help="do not submit sequences as jobs",
     )
     parser.add_argument(
-        "drs4_pedestal_file",
-        type=Path,
-        help="Full path of the DRS4 pedestal file to be created"
-    )
-    parser.add_argument(
-        "calibration_file",
-        type=Path,
-        help="Full path of the calibration file to be created"
-    )
-    parser.add_argument(
-        "calibration_run_number",
-        type=str,
-        help="Calibration run number"
-    )
-    parser.add_argument(
-        "drs4_pedestal_run_number",
+        "--drs4-pedestal-run",
         type=str,
         help="DRS4 pedestal run number"
     )
     parser.add_argument(
-        "run_summary_file",
-        type=Path,
-        help="Run summary file"
+        "--pedcal-run",
+        type=str,
+        help="Calibration run number"
     )
+
     parser.add_argument("tel_id", choices=["LST1"])
 
     return parser
 
 
-def calibration_sequence_cliparsing():
-    opts = calibration_sequence_argparser().parse_args()
+def calibration_pipeline_cliparsing():
+    """
+    Set the global variables and parse the command
+    line arguments for the calibration pipeline
+    """
+    opts = calibration_pipeline_argparser().parse_args()
 
     # set global variables
     options.configfile = opts.configfile
     options.stderr = opts.stderr
     options.stdout = opts.stdout
     options.date = opts.date
-    options.directory = opts.directory
     options.verbose = opts.verbose
     options.prod_id = opts.prod_id
     options.tel_id = opts.tel_id
     options.simulate = opts.simulate
 
-    log.debug(f"The options and arguments are {opts}")
-
     # setting the default date and directory if needed
     options.date = set_default_date_if_needed()
     options.directory = set_default_directory_if_needed()
@@ -325,13 +280,7 @@ def calibration_sequence_cliparsing():
         options.calib_prod_id = get_calib_prod_id()
     else:
         options.calib_prod_id = options.prod_id
-    return (
-        opts.drs4_pedestal_file,
-        opts.calibration_file,
-        opts.calibration_run_number,
-        opts.drs4_pedestal_run_number,
-        opts.run_summary_file,
-    )
+    return opts.drs4_pedestal_run, opts.pedcal_run
 
 
 def data_sequence_argparser():
@@ -352,14 +301,6 @@ def data_sequence_argparser():
         dest="date",
         help="observation ending date YYYY_MM_DD [default today]",
     )
-    parser.add_argument(
-        "-o",
-        "--outputdir",
-        action="store",
-        type=str,
-        dest="directory",
-        help="analysis working directory",
-    )
     parser.add_argument(
         "-v",
         "--verbose",
@@ -404,27 +345,27 @@ def data_sequence_argparser():
         help="Do not produce DL2 files (default False)",
     )
     parser.add_argument(
-        "calib_file",
+        "--pedcal-file",
         type=Path,
         help="Path of the calibration file"
     )
     parser.add_argument(
-        "drs4_ped_file",
+        "--drs4-pedestal-file",
         type=Path,
         help="Path of the DRS4 pedestal file"
     )
     parser.add_argument(
-        "time_calib_file",
+        "--time-calib-file",
         type=Path,
         help="Path of the time calibration file"
     )
     parser.add_argument(
-        "drive_log_file",
+        "--drive-file",
         type=Path,
         help="Path of drive log file with pointing information"
     )
     parser.add_argument(
-        "run_summary_file",
+        "--run-summary",
         type=Path,
         help="Path of run summary file with time reference information",
     )
@@ -443,7 +384,6 @@ def data_sequence_cli_parsing():
     options.stderr = opts.stderr
     options.stdout = opts.stdout
     options.date = opts.date
-    options.directory = opts.directory
     options.verbose = opts.verbose
     options.simulate = opts.simulate
     options.prod_id = opts.prod_id
@@ -472,11 +412,11 @@ def data_sequence_cli_parsing():
         options.dl2_prod_id = options.prod_id
 
     return (
-        opts.calib_file,
-        opts.drs4_ped_file,
+        opts.pedcal_file,
+        opts.drs4_pedestal_file,
         opts.time_calib_file,
-        opts.drive_log_file,
-        opts.run_summary_file,
+        opts.drive_file,
+        opts.run_summary,
         opts.run_number,
     )
 
@@ -502,14 +442,6 @@ def sequencer_argparser():
         help="observation ending date YYYY_MM_DD [default today]",
     )
     # boolean options
-    parser.add_argument(
-        "-o",
-        "--outputdir",
-        action="store",
-        type=str,
-        dest="directory",
-        help="analysis output directory",
-    )
     parser.add_argument(
         "-s",
         "--simulate",
@@ -551,14 +483,6 @@ def sequencer_argparser():
         default=False,
         help="Increase verbosity for debugging",
     )
-    parser.add_argument(
-        "-w",
-        "--warnings",
-        action="store_true",
-        dest="warning",
-        default=False,
-        help="show useful warnings",
-    )
     parser.add_argument(
         "--stderr",
         action="store",
@@ -591,7 +515,6 @@ def sequencer_cli_parsing():
     options.stderr = opts.stderr
     options.stdout = opts.stdout
     options.date = opts.date
-    options.directory = opts.directory
     options.simulate = opts.simulate
     options.test = opts.test
     options.no_submit = opts.no_submit
@@ -600,8 +523,6 @@ def sequencer_cli_parsing():
     options.verbose = opts.verbose
     options.tel_id = opts.tel_id
 
-    # the standardhandle has to be declared before here,
-    # since verbose and warnings are options from the cli
     log.debug(f"the options are {opts}")
 
     options.prod_id = get_prod_id()
@@ -623,6 +544,7 @@ def sequencer_cli_parsing():
 
     # setting the default date and directory if needed
     options.date = set_default_date_if_needed()
+    options.directory = set_default_directory_if_needed()
 
 
 def rawcopycliparsing():
@@ -658,14 +580,6 @@ def rawcopycliparsing():
         default=False,
         help="make lots of noise for debugging",
     )
-    parser.add_argument(
-        "-w",
-        "--warnings",
-        action="store_true",
-        dest="warning",
-        default=False,
-        help="show useful warnings",
-    )
     parser.add_argument(
         "--stderr",
         action="store",
@@ -692,8 +606,6 @@ def rawcopycliparsing():
     options.nocheck = opts.nocheck
     options.verbose = opts.verbose
 
-    # the standardhandle has to be declared here,
-    # since verbose and warnings are options from the cli
     log.debug(f"the options are {opts}")
     log.debug(f"the argument is {args}")
 
diff --git a/osa/utils/iofile.py b/osa/utils/iofile.py
index 24c195d..3624661 100644
--- a/osa/utils/iofile.py
+++ b/osa/utils/iofile.py
@@ -15,7 +15,7 @@ __all__ = [
 ]
 
 
-def write_to_file(file, content):
+def write_to_file(file: pathlib.Path, content: str):
     """Check if the file already exists and write the content in it."""
     file_temp = f"{file}.tmp"
     try:
diff --git a/osa/utils/utils.py b/osa/utils/utils.py
index 75d5ed6..026f382 100644
--- a/osa/utils/utils.py
+++ b/osa/utils/utils.py
@@ -321,7 +321,7 @@ def destination_dir(concept, create_dir=True) -> Path:
         Expected: MUON, DL1AB, DATACHECK, DL2, PEDESTAL, CALIB, TIMECALIB
     create_dir : bool
         Set it to True (default) if you want to create the directory.
-        Otherwise it just return the path
+        Otherwise, it just returns the path
 
     Returns
     -------
@@ -417,11 +417,11 @@ def copy_files_datacheck_web(host, datedir, file_list) -> None:
 
     Parameters
     ----------
-    host: str
+    host : str
         Hostname of the webserver
-    datedir: str
+    datedir : str
         Date directory in the webserver
-    file_list: list
+    file_list : list
         List of files to be copied
     """
     # FIXME: Check if files exists already at webserver CHECK HASH
diff --git a/setup.py b/setup.py
index 3c829f0..7683030 100644
--- a/setup.py
+++ b/setup.py
@@ -9,13 +9,13 @@ entry_points = {
     "console_scripts": [
         "sequencer = osa.scripts.sequencer:main",
         "closer = osa.scripts.closer:main",
-        "calibrationsequence = osa.scripts.calibrationsequence:main",
         "copy_datacheck = osa.scripts.copy_datacheck:main",
         "datasequence = osa.scripts.datasequence:main",
         "sequencer_webmaker = osa.scripts.sequencer_webmaker:main",
         "show_run_summary = osa.scripts.show_run_summary:main",
         "provprocess = osa.scripts.provprocess:main",
         "simulate_processing = osa.scripts.simulate_processing:main",
+        "calibration_pipeline = osa.scripts.calibration_pipeline:main",
     ]
 }
 


Test patch:
diff --git a/osa/conftest.py b/osa/conftest.py
index b558949..2d48cdd 100644
--- a/osa/conftest.py
+++ b/osa/conftest.py
@@ -1,3 +1,22 @@
+"""
+Mock test data set for testing OSA.
+
+R0 files are in BASE_DIR/R0/YYYYMMDD
+
+Calibration files follow the structure:
+BASE_DIR/monitoring/PixelCalibration/LevelA/<calibration_product>/YYYYMMDD/<version>
+Where <calibration_product> can be: drs4_baseline, calibration,
+drs4_time_sampling_from_FF, ffactor_systematics
+<version> will usually be "pro" (for production)
+
+RunSummary file: BASE_DIR/monitoring/RunSummary/RunSummary_YYYMMDD.ecsv
+
+Analysis products (dl1, dl2, muons and datacheck) are produced in the analysis directory:
+BASE_DIR/running_analysis/YYYYMMDD/<prod_id>
+"""
+
+from textwrap import dedent
+
 import pytest
 
 from osa.configs import options
@@ -20,6 +39,59 @@ def base_test_dir(tmp_path_factory):
     return tmp_path_factory.mktemp("test_files")
 
 
+@pytest.fixture(scope="session")
+def monitoring_dir(base_test_dir):
+    monitoring_dir = base_test_dir / "monitoring"
+    monitoring_dir.mkdir(parents=True, exist_ok=True)
+    return monitoring_dir
+
+
+@pytest.fixture(scope="session")
+def run_summary_dir(monitoring_dir):
+    summary_dir = monitoring_dir / "RunSummary"
+    summary_dir.mkdir(parents=True, exist_ok=True)
+    return summary_dir
+
+
+@pytest.fixture(scope="session")
+def calibration_base_dir(monitoring_dir):
+    base_dir = monitoring_dir / "PixelCalibration" / "LevelA"
+    base_dir.mkdir(parents=True, exist_ok=True)
+    return base_dir
+
+
+@pytest.fixture(scope="session")
+def calibration_dir(calibration_base_dir):
+    directory = calibration_base_dir / "calibration" / nightdir / "pro"
+    directory.mkdir(parents=True, exist_ok=True)
+    return directory
+
+
+@pytest.fixture(scope="session")
+def drs4_baseline_dir(calibration_base_dir):
+    directory = calibration_base_dir / "drs4_baseline" / nightdir / "pro"
+    directory.mkdir(parents=True, exist_ok=True)
+    return directory
+
+
+@pytest.fixture(scope="session")
+def drs4_time_calibration_files(calibration_base_dir):
+    directory = calibration_base_dir / "drs4_time_sampling_from_FF"
+    directory1 = directory / "20191124" / "pro"
+    directory2 = directory / "20210321" / "pro"
+    directory3 = directory / "20210902" / "pro"
+    directory1.mkdir(parents=True, exist_ok=True)
+    directory2.mkdir(parents=True, exist_ok=True)
+    directory3.mkdir(parents=True, exist_ok=True)
+    file1 = directory1 / "time_calibration.Run01625.0000.h5"
+    file2 = directory2 / "time_calibration.Run04211.0000.h5"
+    file3 = directory3 / "time_calibration.Run05979.0000.h5"
+    time_file_list = [file1, file2, file3]
+    for file in time_file_list:
+        file.touch()
+    return time_file_list
+
+
 @pytest.fixture(scope="session")
 def running_analysis_dir(base_test_dir):
     analysis_dir = base_test_dir / "running_analysis" / nightdir / prod_id
@@ -59,15 +131,19 @@ def dl2_subdir(running_analysis_dir):
 
 
 @pytest.fixture(scope="session")
-def test_calibration_data(running_analysis_dir):
+def calibration_file(calibration_dir):
     """Mock calibration files for testing."""
-    calib_file = running_analysis_dir / "calibration.Run01805.0000.h5"
-    drs4_file = running_analysis_dir / "drs4_pedestal.Run01804.0000.fits"
-    time_file = running_analysis_dir / "time_calibration.Run01805.0000.h5"
+    calib_file = calibration_dir / "calibration_filters_52.Run01805.0000.h5"
     calib_file.touch()
+    return calib_file
+
+
+@pytest.fixture(scope="session")
+def drs4_baseline_file(drs4_baseline_dir):
+    """Mock calibration files for testing."""
+    drs4_file = drs4_baseline_dir / "drs4_pedestal.Run01804.0000.h5"
     drs4_file.touch()
-    time_file.touch()
-    return calib_file, drs4_file, time_file
+    return drs4_file
 
 
 @pytest.fixture(scope="session")
@@ -87,16 +163,54 @@ def test_observed_data(running_analysis_dir, dl1b_subdir, dl2_subdir):
 
 
 @pytest.fixture(scope="session")
-def run_summary():
+def run_summary_file(run_summary_dir):
+
+    summary_content = dedent("""\
+    # %ECSV 0.9
+    # ---
+    # datatype:
+    # - {name: run_id, datatype: int64}
+    # - {name: n_subruns, datatype: int64}
+    # - {name: run_type, datatype: string}
+    # - {name: ucts_timestamp, datatype: int64}
+    # - {name: run_start, datatype: int64}
+    # - {name: dragon_reference_time, datatype: int64}
+    # - {name: dragon_reference_module_id, datatype: int16}
+    # - {name: dragon_reference_module_index, datatype: int16}
+    # - {name: dragon_reference_counter, datatype: uint64}
+    # - {name: dragon_reference_source, datatype: string}
+    # delimiter: ','
+    # meta: !!omap
+    # - {date: '2020-01-17'}
+    # - {lstchain_version: 0.7.0}
+    # schema: astropy-2.0
+    run_id,n_subruns,run_type,ucts_timestamp,run_start,dragon_reference_time,dragon_reference_module_id,dragon_reference_module_index,dragon_reference_counter,dragon_reference_source
+    1804,6,DRS4,1579289727863850890,1579289712000000000,1579289727863850890,90,0,5863850400,ucts
+    1805,5,PEDCALIB,1579291426030146503,1579291413000000000,1579291426030146503,90,0,2030146000,ucts
+    1806,5,PEDCALIB,1579291932080485703,1579291917000000000,1579291932080485703,90,0,5080485200,ucts
+    1807,11,DATA,1579292477145904430,1579292461000000000,1579292477145904430,90,0,6145904000,ucts
+    1808,9,DATA,1579292985532016507,1579292975000000000,1579292985532016507,90,0,2532016000,ucts""")
+
+    summary_file = run_summary_dir / 'RunSummary_20200117.ecsv'
+    summary_file.touch()
+    summary_file.write_text(summary_content)
+    return summary_file
+
+
+@pytest.fixture(scope="session")
+def run_summary(run_summary_file):
     """Creates a sequence list from a run summary file."""
+    assert run_summary_file.exists()
     return run_summary_table(date)
 
 
 @pytest.fixture(scope="session")
-def sequence_list(running_analysis_dir, run_summary):
+def sequence_list(running_analysis_dir, run_summary, drs4_time_calibration_files):
     """Creates a sequence list from a run summary file."""
     options.directory = running_analysis_dir
     options.simulate = True
+    for file in drs4_time_calibration_files:
+        assert file.exists()
 
     subrun_list = extractsubruns(run_summary)
     run_list = extractruns(subrun_list)
@@ -104,7 +218,11 @@ def sequence_list(running_analysis_dir, run_summary):
 
 
 @pytest.fixture(scope="session")
-def sequence_file_list(running_analysis_dir):
+def sequence_file_list(running_analysis_dir, run_summary_file, drs4_time_calibration_files):
+    for file in drs4_time_calibration_files:
+        assert file.exists()
+    assert run_summary_file.exists()
+
     run_program("sequencer", "-d", "2020_01_17", "--no-submit", "-t", "LST1")
     return [
         running_analysis_dir / "sequence_LST1_01805.py",
@@ -116,6 +234,8 @@ def sequence_file_list(running_analysis_dir):
 @pytest.fixture(scope="session")
 def txt_file_test(running_analysis_dir):
     from osa.utils.iofile import write_to_file
-    file_name = running_analysis_dir / "test.txt"
-    write_to_file(file_name, 'This is a test')
-    return file_name
+    options.simulate = False
+    file = running_analysis_dir / "test.txt"
+    write_to_file(file, 'This is a test')
+    options.simulate = True
+    return file
diff --git a/osa/nightsummary/tests/test_nightsummary.py b/osa/nightsummary/tests/test_nightsummary.py
index cd2aafb..a7e3f2a 100644
--- a/osa/nightsummary/tests/test_nightsummary.py
+++ b/osa/nightsummary/tests/test_nightsummary.py
@@ -11,9 +11,11 @@ def test_get_nightsummary_file():
            "RunSummary_20200101.ecsv"
 
 
-def test_run_summary_table():
+def test_run_summary_table(run_summary_file):
     from osa.nightsummary.nightsummary import run_summary_table
 
+    assert run_summary_file.exists()
+
     date = "2020_01_17"
     summary = run_summary_table(date)
 
diff --git a/osa/scripts/tests/test_osa_scripts.py b/osa/scripts/tests/test_osa_scripts.py
index 4c52fde..f2d4297 100644
--- a/osa/scripts/tests/test_osa_scripts.py
+++ b/osa/scripts/tests/test_osa_scripts.py
@@ -8,20 +8,21 @@ import pytest
 import yaml
 
 from osa.configs import options
-from osa.configs.config import cfg
 from osa.scripts.closer import is_sequencer_successful, is_finished_check
 
 ALL_SCRIPTS = [
     "sequencer",
     "closer",
-    "calibrationsequence",
     "copy_datacheck",
     "datasequence",
+    "calibration_pipeline",
     "show_run_summary",
     "provprocess",
     "simulate_processing",
 ]
 
+options.date = "2020_01_17"
+
 
 def remove_provlog():
     log_file = Path("prov.log")
@@ -47,7 +48,12 @@ def test_all_help(script):
     run_program(script, "--help")
 
 
-def test_simulate_processing():
+def test_simulate_processing(drs4_time_calibration_files, run_summary_file):
+
+    for file in drs4_time_calibration_files:
+        assert file.exists()
+
+    assert run_summary_file.exists()
 
     remove_provlog()
     rc = run_program("simulate_processing", "-p", "--force")
@@ -89,7 +95,10 @@ def test_simulate_processing():
     assert rc.returncode == 0
 
 
-def test_simulated_sequencer():
+def test_simulated_sequencer(drs4_time_calibration_files, run_summary_file):
+    assert run_summary_file.exists()
+    for file in drs4_time_calibration_files:
+        assert file.exists()
     rc = run_program("sequencer", "-c", "cfg/sequencer.cfg", "-d", "2020_01_17", "-s", "-t", "LST1")
     assert rc.returncode == 0
     now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M")
@@ -127,7 +136,7 @@ def test_autocloser(running_analysis_dir):
     )
 
 
-def test_closer(r0_dir, running_analysis_dir, test_observed_data, test_calibration_data):
+def test_closer(r0_dir, running_analysis_dir, test_observed_data):
     # First assure that the end of night flag is not set and remove it otherwise
     night_finished_flag = Path(
         "./test_osa/test_files0/OSA/Closer/20200117/v0.1.0/NightFinished.txt"
@@ -137,8 +146,6 @@ def test_closer(r0_dir, running_analysis_dir, test_observed_data, test_calibrati
 
     assert r0_dir.exists()
     assert running_analysis_dir.exists()
-    for cal_file in test_calibration_data:
-        assert cal_file.exists()
     for obs_file in test_observed_data:
         assert obs_file.exists()
 
@@ -163,18 +170,6 @@ def test_closer(r0_dir, running_analysis_dir, test_observed_data, test_calibrati
         "./test_osa/test_files0/DL2/20200117/v0.1.0/tailcut84_model1/"
         "dl2_LST-1.Run01808.0011.h5"
     )
-    assert os.path.exists(
-        "./test_osa/test_files0/calibration/20200117/v01/"
-        "drs4_pedestal.Run01804.0000.fits"
-    )
-    assert os.path.exists(
-        "./test_osa/test_files0/calibration/20200117/v01/"
-        "calibration.Run01805.0000.h5"
-    )
-    assert os.path.exists(
-        "./test_osa/test_files0/calibration/20200117/"
-        "v01/time_calibration.Run01805.0000.h5"
-    )
     # Assert that the link to dl1 and muons files have been created
     assert os.path.islink(
         "./test_osa/test_files0/running_analysis/20200117/"
@@ -202,39 +197,30 @@ def test_datasequence(running_analysis_dir):
 
     output = run_program(
         "datasequence",
-        "-c",
+        "--config",
         "cfg/sequencer.cfg",
-        "-d",
-        "2020_01_17",
-        "-s",
-        "--prod-id",
-        prod_id,
-        drs4_file,
-        calib_file,
-        timecalib_file,
-        drive_file,
-        runsummary_file,
+        "--date=2020_01_17",
+        "--simulate",
+        f"--prod-id={prod_id}",
+        f"--drs4-pedestal-file={drs4_file}",
+        f"--pedcal-file={calib_file}",
+        f"--time-calib-file={timecalib_file}",
+        f"--drive-file={drive_file}",
+        f"--run-summary={runsummary_file}",
         run_number,
         "LST1",
     )
     assert output.returncode == 0
 
 
-def test_calibrationsequence(r0_data, running_analysis_dir):
-    drs4_file = "drs4_pedestal.Run01805.0000.fits"
-    calib_file = "calibration.Run01806.0000.hdf5"
-    runsummary_file = "RunSummary_20200117.ecsv"
+def test_calibration_pipeline(running_analysis_dir):
     prod_id = "v0.1.0"
     drs4_run_number = "01805"
     pedcal_run_number = "01806"
     options.directory = running_analysis_dir
 
-    # Check that the R0 files corresponding to calibration run exists
-    for files in r0_data:
-        assert os.path.exists(files)
-
     output = run_program(
-        "calibrationsequence",
+        "calibration_pipeline",
         "-c",
         "cfg/sequencer.cfg",
         "-d",
@@ -242,99 +228,64 @@ def test_calibrationsequence(r0_data, running_analysis_dir):
         "-s",
         "--prod-id",
         prod_id,
-        drs4_file,
-        calib_file,
+        "--drs4-pedestal-run",
         drs4_run_number,
+        "--pedcal-run",
         pedcal_run_number,
-        runsummary_file,
         "LST1",
     )
     assert output.returncode == 0
 
 
-def test_is_sequencer_successful(run_summary):
+def test_is_sequencer_successful(run_summary, running_analysis_dir):
+    options.directory = running_analysis_dir
     seq_tuple = is_finished_check(run_summary)
     assert is_sequencer_successful(seq_tuple) is True
 
 
-def test_drs4_pedestal_command(r0_data, test_calibration_data):
-    from osa.scripts.calibrationsequence import drs4_pedestal_command
-    input_file = r0_data[0]
-    output_file = test_calibration_data[1]
-    command = drs4_pedestal_command(input_file, output_file)
+def test_drs4_pedestal_cmd(base_test_dir):
+    from osa.scripts.calibration_pipeline import drs4_pedestal_command
+    cmd = drs4_pedestal_command(drs4_pedestal_run_id="01804")
     expected_command = [
-        "lstchain_data_create_drs4_pedestal_file",
-        f"--input-file={input_file}",
-        f"--output-file={output_file}",
-        "--max-events=20000",
-        "--overwrite"
+        'onsite_create_drs4_pedestal_file',
+        '--run_number=01804',
+        f'--base_dir={base_test_dir}',
+        '--no-progress',
+        '--yes'
     ]
-    assert command == expected_command
+    assert cmd == expected_command
 
 
-def test_calibration_file_command(r0_data, test_calibration_data, running_analysis_dir):
-    from osa.scripts.calibrationsequence import calibration_file_command
-    options.directory = running_analysis_dir
-    calibration_run = "01806"
-    input_file = r0_data[2]  # Corresponds to run 01806
-    calibration_output_file = Path(test_calibration_data[0])
-    drs4_pedestal_file = Path(test_calibration_data[1])
-    ffactor_systematics = cfg.get("lstchain", "ffactor_systematics")
-    calib_config = cfg.get("lstchain", "calibration_config")
-    run_summary = Path("extra/monitoring/RunSummary") / "RunSummary_20200117.ecsv"
-    time_file_basename = Path(calibration_output_file).name.replace("calibration", "time_calibration")
-    time_file = Path(running_analysis_dir) / time_file_basename
-    log_dir = running_analysis_dir / "log"
-    log_dir.mkdir(parents=True, exist_ok=True)
-    log_file = log_dir / f"calibration.Run{calibration_run}.0000.log"
-
-    command = calibration_file_command(
-        calibration_run,
-        calib_config,
-        drs4_pedestal_file,
-        calibration_output_file,
-        run_summary
-    )
-
+def test_calibration_file_cmd(base_test_dir):
+    from osa.scripts.calibration_pipeline import calibration_file_command
+    cmd = calibration_file_command(pedcal_run_id="01805")
     expected_command = [
-        "lstchain_create_calibration_file",
-        f"--input_file={input_file}",
-        f"--output_file={calibration_output_file}",
-        "--EventSource.default_trigger_type=tib",
-        f"--LSTCalibrationCalculator.systematic_correction_path={ffactor_systematics}",
-        f"--LSTEventSource.EventTimeCalculator.run_summary_path={run_summary}",
-        f"--LSTEventSource.LSTR0Corrections.drs4_time_calibration_path={time_file}",
-        f"--LSTEventSource.LSTR0Corrections.drs4_pedestal_path={drs4_pedestal_file}",
-        f"--log-file={log_file}",
-        f"--config={calib_config}"
+        'onsite_create_calibration_file',
+        '--run_number=01805',
+        f'--base_dir={base_test_dir}',
+        '--yes',
+        '--filters=52'
     ]
-    assert command == expected_command
+    assert cmd == expected_command
 
 
-def test_time_calibration_command(r0_dir, test_calibration_data, running_analysis_dir):
-    from osa.scripts.calibrationsequence import time_calibration_command
-    options.directory = running_analysis_dir
-    calibration_run = "01806"
-    input_file = r0_dir.parent / "*/LST-1.1.Run01806.000*.fits.fz"
-    calibration_output_file = Path(test_calibration_data[0])
-    drs4_pedestal_file = Path(test_calibration_data[1])
-    run_summary = Path("extra/monitoring/RunSummary") / "RunSummary_20200117.ecsv"
-    time_file_basename = Path(calibration_output_file).name.replace("calibration", "time_calibration")
-    time_calibration_file = Path(running_analysis_dir) / time_file_basename
-
-    command = time_calibration_command(
-        calibration_run,
-        time_calibration_file,
-        drs4_pedestal_file,
-        run_summary
-    )
+def test_drs4_pedestal(running_analysis_dir):
+    from osa.scripts.calibration_pipeline import drs4_pedestal
+    history_file = running_analysis_dir / "calibration_sequence.history"
+    with pytest.raises(SystemExit):
+        rc = drs4_pedestal(
+            drs4_pedestal_run_id="01804",
+            history_file=history_file
+        )
+        assert rc != 0
 
-    expected_command = [
-        "lstchain_data_create_time_calibration_file",
-        f"--input-file={input_file}",
-        f"--output-file={time_calibration_file}",
-        f"--pedestal-file={drs4_pedestal_file}",
-        f"--run-summary-path={run_summary}",
-        "--max-events=53000"
-    ]
-    assert command == expected_command
+
+def test_calibrate_charge(running_analysis_dir):
+    from osa.scripts.calibration_pipeline import calibrate_charge
+    history_file = running_analysis_dir / "calibration_sequence.history"
+    with pytest.raises(SystemExit):
+        rc = calibrate_charge(
+            calibration_run="01805",
+            history_file=history_file
+        )
+        assert rc != 0
diff --git a/osa/tests/test_jobs.py b/osa/tests/test_jobs.py
index 1e86d2a..1484779 100644
--- a/osa/tests/test_jobs.py
+++ b/osa/tests/test_jobs.py
@@ -16,6 +16,7 @@ calibration_history_file = (
 )
 options.date = "2020_01_17"
 options.tel_id = "LST1"
+options.prod_id = "v0.1.0"
 
 
 def test_historylevel():
@@ -125,9 +126,16 @@ def test_job_header_template(sequence_list, running_analysis_dir):
     assert header == output_string2
 
 
-def test_create_job_template_scheduler(sequence_list):
+def test_create_job_template_scheduler(
+        sequence_list,
+        drs4_time_calibration_files,
+        drs4_baseline_file,
+        calibration_file,
+        run_summary_file
+):
     from osa.job import create_job_template
     options.test = False
+    options.simulate = False
     content = create_job_template(sequence_list[1], get_content=True)
     expected_content = dedent(f"""\
     #!/bin/env python
@@ -156,17 +164,15 @@ def test_create_job_template_scheduler(sequence_list):
         os.environ['NUMBA_CACHE_DIR'] = tmpdirname
         proc = subprocess.run([
             'datasequence',
-            '-c',
+            '--config',
             '{Path.cwd()}/cfg/sequencer.cfg',
-            '-d',
-            '2020_01_17',
-            '--prod-id',
-            'v0.1.0',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/calibration.Run01805.0000.h5',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/drs4_pedestal.Run01804.0000.fits',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/time_calibration.Run01805.0000.h5',
-            'extra/monitoring/DrivePositioning/drive_log_20_01_17.txt',
-            'extra/monitoring/RunSummary/RunSummary_20200117.ecsv',
+            '--date=2020_01_17',
+            '--prod-id=v0.1.0',
+            '--drs4-pedestal-file={drs4_baseline_file}',
+            '--time-calib-file={drs4_time_calibration_files[0]}',
+            '--pedcal-file={calibration_file}',
+            '--drive-file={Path.cwd()}/test_osa/test_files0/monitoring/DrivePositioning/drive_log_20_01_17.txt',
+            '--run-summary={run_summary_file}',
             '--stderr=log/sequence_LST1_01807.{{0}}_{{1}}.err'.format(str(subruns).zfill(4), str(job_id)),
             '--stdout=log/sequence_LST1_01807.{{0}}_{{1}}.out'.format(str(subruns).zfill(4), str(job_id)),
             '01807.{{0}}'.format(str(subruns).zfill(4)),
@@ -174,13 +180,21 @@ def test_create_job_template_scheduler(sequence_list):
         ])
 
     sys.exit(proc.returncode)""")
+    options.simulate = True
     assert content == expected_content
 
 
-def test_create_job_template_local(sequence_list):
+def test_create_job_template_local(
+        sequence_list,
+        drs4_time_calibration_files,
+        drs4_baseline_file,
+        calibration_file,
+        run_summary_file
+):
     """Check the job file in local mode (assuming no scheduler)."""
     from osa.job import create_job_template
     options.test = True
+    options.simulate = False
     content = create_job_template(sequence_list[1], get_content=True)
     expected_content = dedent(f"""\
     #!/bin/env python
@@ -196,22 +210,54 @@ def test_create_job_template_local(sequence_list):
         os.environ['NUMBA_CACHE_DIR'] = tmpdirname
         proc = subprocess.run([
             'datasequence',
-            '-c',
+            '--config',
             '{Path.cwd()}/cfg/sequencer.cfg',
-            '-d',
-            '2020_01_17',
-            '--prod-id',
-            'v0.1.0',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/calibration.Run01805.0000.h5',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/drs4_pedestal.Run01804.0000.fits',
-            '{Path.cwd()}/test_osa/test_files0/running_analysis/20200117/v0.1.0/time_calibration.Run01805.0000.h5',
-            'extra/monitoring/DrivePositioning/drive_log_20_01_17.txt',
-            'extra/monitoring/RunSummary/RunSummary_20200117.ecsv',
+            '--date=2020_01_17',
+            '--prod-id=v0.1.0',
+            '--drs4-pedestal-file={drs4_baseline_file}',
+            '--time-calib-file={drs4_time_calibration_files[0]}',
+            '--pedcal-file={calibration_file}',
+            '--drive-file={Path.cwd()}/test_osa/test_files0/monitoring/DrivePositioning/drive_log_20_01_17.txt',
+            '--run-summary={run_summary_file}',
             '01807.{{0}}'.format(str(subruns).zfill(4)),
             'LST1'
         ])
 
     sys.exit(proc.returncode)""")
+    options.simulate = True
+    assert content == expected_content
+
+
+def test_create_job_scheduler_calibration(sequence_list):
+    """Check the pilot job file for the calibration pipeline."""
+    from osa.job import create_job_template
+    options.test = True
+    options.simulate = False
+    content = create_job_template(sequence_list[0], get_content=True)
+    expected_content = dedent(f"""\
+    #!/bin/env python
+
+    import os
+    import subprocess
+    import sys
+    import tempfile
+
+    subruns = 0
+
+    with tempfile.TemporaryDirectory() as tmpdirname:
+        os.environ['NUMBA_CACHE_DIR'] = tmpdirname
+        proc = subprocess.run([
+            'calibration_pipeline',
+            '--config',
+            '{Path.cwd()}/cfg/sequencer.cfg',
+            '--date=2020_01_17',
+            '--drs4-pedestal-run=01804',
+            '--pedcal-run=01805',
+            'LST1'
+        ])
+
+    sys.exit(proc.returncode)""")
+    options.simulate = True
     assert content == expected_content
 
 
@@ -229,9 +275,8 @@ def test_set_cache_dirs():
 def test_calibration_history_level():
     from osa.job import check_history_level
     levels = {
-        "lstchain_data_create_drs4_pedestal_file": 2,
-        "lstchain_data_create_time_calibration_file": 1,
-        "lstchain_create_calibration_file": 0
+        "onsite_create_drs4_pedestal_file": 1,
+        "onsite_create_calibration_file": 0
     }
     level, exit_status = check_history_level(
         calibration_history_file, levels
@@ -296,3 +341,62 @@ def test_plot_job_statistics(sacct_output, running_analysis_dir):
     plot_job_statistics(sacct_output, log_dir)
     plot_file = log_dir / "job_statistics.pdf"
     assert plot_file.exists()
+
+
+def test_get_time_calibration_file(drs4_time_calibration_files):
+    from osa.job import get_time_calibration_file
+    for file in drs4_time_calibration_files:
+        assert file.exists()
+
+    run = 1616
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[0]
+
+    run = 1625
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[0]
+
+    run = 1900
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[0]
+
+    run = 4211
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[1]
+
+    run = 5000
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[1]
+
+    run = 5979
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[2]
+
+    run = 6000
+    time_file = get_time_calibration_file(run)
+    assert time_file == drs4_time_calibration_files[2]
+
+
+def test_run_program_with_history_logging(running_analysis_dir):
+    from osa.job import run_program_with_history_logging
+
+    options.simulate = False
+
+    cmd = ["echo", "Testing"]
+    history_file = running_analysis_dir / "test.history"
+    run = "01140"
+    prod_id = "v0.2.0"
+    command = "echo"
+    config_file = "config_test.json"
+
+    rc = run_program_with_history_logging(
+        command_args=cmd,
+        history_file=history_file,
+        run=run,
+        prod_id=prod_id,
+        command=command,
+        config_file=config_file,
+    )
+    options.simulate = True
+    assert rc == 0
+    assert history_file.exists()
diff --git a/osa/tests/test_osa.py b/osa/tests/test_osa.py
index 589afe0..f3c24bb 100644
--- a/osa/tests/test_osa.py
+++ b/osa/tests/test_osa.py
@@ -2,9 +2,21 @@ import pytest
 
 
 @pytest.mark.tryfirst
-def test_basedir_available(base_test_dir, r0_data):
+def test_basedir_available(
+        base_test_dir,
+        r0_data,
+        drs4_time_calibration_files,
+        calibration_file,
+        drs4_baseline_file
+):
 
     assert base_test_dir.is_dir()
 
     for data_file in r0_data:
         assert data_file.exists()
+
+    for file in drs4_time_calibration_files:
+        assert file.exists()
+
+    assert drs4_baseline_file.exists()
+    assert calibration_file.exists()
diff --git a/osa/tests/test_report.py b/osa/tests/test_report.py
index bfebe40..49d52ab 100644
--- a/osa/tests/test_report.py
+++ b/osa/tests/test_report.py
@@ -1,4 +1,5 @@
 from datetime import datetime
+from osa.configs import options
 
 
 def test_finished_assignments(sequence_list):
@@ -25,9 +26,21 @@ def test_history(base_test_dir):
     input_card = "r0_dl1.config"
     rc = 0
     history_file = base_test_dir / "r0_to_dl1_01800.history"
-    date_string = datetime.utcnow().strftime("%a %b %d %X UTC %Y")
+    date_string = datetime.utcnow().isoformat(sep=" ", timespec="minutes")
 
-    history(run, prod_id, program, input_file, input_card, rc, history_file)
+    options.simulate = False
+
+    history(
+        run=run,
+        prod_id=prod_id,
+        stage=program,
+        return_code=rc,
+        history_file=history_file,
+        input_file=input_file,
+        config_file=input_card
+    )
+
+    options.simulate = True
 
     logged_string = f"01800 r0_to_dl1 v1.0.0 {date_string} " \
         "r0_to_dl1_01800.fits r0_dl1.config 0\n"
diff --git a/osa/utils/tests/test_iofile.py b/osa/utils/tests/test_iofile.py
index d83d830..3b5a0ac 100644
--- a/osa/utils/tests/test_iofile.py
+++ b/osa/utils/tests/test_iofile.py
@@ -1,12 +1,11 @@
 from osa.utils.iofile import append_to_file
-from pathlib import Path
+from osa.configs import options
 
 
-def test_write_to_file(txt_file_test):
+def test_write_and_append_to_file(txt_file_test):
     assert txt_file_test.exists()
-    assert isinstance(txt_file_test, Path)
-
-
-def test_append_to_file(txt_file_test):
+    assert txt_file_test.read_text() == 'This is a test'
+    options.simulate = False
     append_to_file(txt_file_test, '\nAnother line')
+    options.simulate = True
     assert txt_file_test.read_text() == 'This is a test\nAnother line'
diff --git a/osa/utils/tests/test_utils.py b/osa/utils/tests/test_utils.py
index 0948d52..cd7fd57 100644
--- a/osa/utils/tests/test_utils.py
+++ b/osa/utils/tests/test_utils.py
@@ -45,6 +45,12 @@ def test_get_prod_id():
     assert get_prod_id() == prod_id
 
 
+def test_get_calib_prod_id():
+    from osa.utils.utils import get_calib_prod_id
+    prod_id = cfg.get(options.tel_id, "CALIB_PROD_ID")
+    assert get_calib_prod_id() == prod_id
+
+
 def test_date_in_yymmdd():
     from osa.utils.utils import date_in_yymmdd
 
@@ -65,7 +71,6 @@ def test_destination_dir():
     datedir = lstdate_to_dir(options.date)
     options.dl1_prod_id = cfg.get("LST1", "DL1_PROD_ID")
     options.dl2_prod_id = cfg.get("LST1", "DL2_PROD_ID")
-    options.calib_prod_id = cfg.get("LST1", "CALIB_PROD_ID")
     options.prod_id = cfg.get("LST1", "PROD_ID")
     base_directory = cfg.get("LST1", "BASE")
     base_path = Path(base_directory)
@@ -73,9 +78,6 @@ def test_destination_dir():
     data_types = {
         "DL1AB": "DL1",
         "DATACHECK": "DL1",
-        "PEDESTAL": "calibration",
-        "CALIB": "calibration",
-        "TIMECALIB": "calibration",
         "MUON": "DL1",
         "DL2": "DL2",
     }
@@ -83,13 +85,13 @@ def test_destination_dir():
     for concept, dst_dir in data_types.items():
         directory = destination_dir(concept, create_dir=False)
         if concept in ["DL1AB", "DATACHECK"]:
-            expected_directory = base_path / dst_dir / datedir /\
-                                 options.prod_id / options.dl1_prod_id
+            expected_directory = (
+                base_path / dst_dir / datedir / options.prod_id / options.dl1_prod_id
+            )
         elif concept == "DL2":
-            expected_directory = base_path / dst_dir / datedir /\
-                                 options.prod_id / options.dl2_prod_id
-        elif concept in ["PEDESTAL", "CALIB", "TIMECALIB"]:
-            expected_directory = base_path / dst_dir / datedir / options.calib_prod_id
+            expected_directory = (
+                base_path / dst_dir / datedir / options.prod_id / options.dl2_prod_id
+            )
         else:
             expected_directory = base_path / dst_dir / datedir / options.prod_id