• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

desihub / desispec / 10947976957

19 Sep 2024 07:55PM UTC coverage: 30.106% (-0.06%) from 30.164%
10947976957

Pull #2376

github

web-flow
Merge 84f44afca into 97b174838
Pull Request #2376: Add script to update processing table column layout

1 of 95 new or added lines in 2 files covered. (1.05%)

59 existing lines in 1 file now uncovered.

14622 of 48569 relevant lines covered (30.11%)

0.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/py/desispec/scripts/reformat_proctables.py
1
"""
2
desispec.scripts.updateexptables
3
================================
4

5
"""
NEW
6
import os
×
NEW
7
import glob
×
NEW
8
import sys
×
NEW
9
import numpy as np
×
NEW
10
import re
×
NEW
11
import time
×
NEW
12
from astropy.table import Table
×
13

NEW
14
from desispec.io.meta import findfile
×
NEW
15
from desispec.workflow.proctable import get_processing_table_column_defs
×
NEW
16
from desispec.workflow.utils import define_variable_from_environment, listpath, \
×
17
                                    pathjoin
NEW
18
from desispec.workflow.tableio import write_table, load_table
×
NEW
19
from desispec.scripts.exposuretable import create_exposure_tables
×
20

21

22

NEW
23
def update_processing_tables(nights=None, night_range=None, orig_filetype='csv',
×
24
                           out_filetype='csv', dry_run=False):
25
    """
26
    Generates updated processing tables for the nights requested. Requires
27
    a current processing table to exist on disk.
28

29
    Args:
30
        nights: str, int, or comma separated list. The night(s) to generate
31
                                                   procesing tables for.
32
        night_range: str. comma separated pair of nights in form
33
                          YYYYMMDD,YYYYMMDD for first_night,last_night
34
                          specifying the beginning and end of a range of
35
                          nights to be generated. first_night and last_night are
36
                          inclusive.
37
        orig_filetype: str. The file extension (without the '.') of the exposure
38
                            tables.
39
        out_filetype: str. The file extension for the outputted exposure tables
40
                           (without the '.').
41

42
    Returns:
43
        Nothing
44
    """
45
    # log = get_logger()
46
    ## Make sure user specified what nights to run on
NEW
47
    if nights is None and night_range is None:
×
NEW
48
        raise ValueError("Must specify either nights or night_range."
×
49
                         +" To process all nights give nights=all")
50

51
    ## Get all nights in 2020's with data
NEW
52
    proctab_template = findfile('proctable', night=99999999)
×
NEW
53
    proctab_template = proctab_template.replace('99999999', '202[0-9][01][0-9][0-3][0-9]')
×
NEW
54
    proctab_template = proctab_template.replace('.csv', f'.{orig_filetype}')
×
NEW
55
    nights_with_proctables = list()
×
NEW
56
    for ptabfn in glob.glob(proctab_template):
×
57
        ## nights are 202YMMDD
NEW
58
        matches = re.findall('202\d{5}', os.path.basename(ptabfn))
×
NEW
59
        if len(matches) == 1:
×
NEW
60
            n = int(matches[0])
×
NEW
61
            nights_with_proctables.append(n)
×
62
        else:
NEW
63
            print(f"Couldn't parse a night from proctable file: {ptabfn}")
×
64

65
    ## If unpecified or given "all", set nights to all nights with data
NEW
66
    check_night = False
×
NEW
67
    if nights is None or nights == 'all':
×
NEW
68
        nights = nights_with_proctables
×
69
        ## No need to check nights since derived from disk
70
    else:
NEW
71
        nights = [int(val.strip()) for val in nights.split(",")]
×
72
        ## If nights are specified, make sure we check that there is actually data
NEW
73
        check_night = True
×
NEW
74
    nights = np.sort(nights)
×
75

76
    ## If user specified a night range, cut nights to that range of dates
NEW
77
    if night_range is not None:
×
NEW
78
        if ',' not in night_range:
×
NEW
79
            raise ValueError("night_range must be a comma separated pair of "
×
80
                             + "nights in form YYYYMMDD,YYYYMMDD")
NEW
81
        nightpair = night_range.split(',')
×
NEW
82
        if len(nightpair) != 2 or not nightpair[0].isnumeric() \
×
83
                or not nightpair[1].isnumeric():
NEW
84
            raise ValueError("night_range must be a comma separated pair of "
×
85
                             + "nights in form YYYYMMDD,YYYYMMDD")
NEW
86
        first_night, last_night = nightpair
×
NEW
87
        nights = nights[np.where(int(first_night) <= nights.astype(int))[0]]
×
NEW
88
        nights = nights[np.where(int(last_night) >= nights.astype(int))[0]]
×
89

90
    ## Get current set of expected columns
NEW
91
    ptab_cols, ptab_dtypes, ptab_defs = get_processing_table_column_defs(return_default_values=True)
×
NEW
92
    ptab_cols, ptab_dtypes = np.array(ptab_cols), np.array(ptab_dtypes)
×
93

94
    ## Tell user the final list of nights and starting looping over them
NEW
95
    print("Nights: ", nights)
×
NEW
96
    for night in nights:
×
NEW
97
        if check_night and night not in nights_with_proctables:
×
NEW
98
            print(f"Night {night} doesn't have a processing table: Skipping.")
×
NEW
99
            continue
×
100

101
        ## If the processing table doesn't exist, skip, since we are updating
102
        ## not generating.
NEW
103
        orig_pathname = findfile('proctable', night=night).replace('.csv', f'.{orig_filetype}')
×
NEW
104
        if not os.path.exists(orig_pathname):
×
NEW
105
            print(f'Could not find processing table for night={night} at:'
×
106
                  + f' {orig_pathname}. Skipping this night.')
NEW
107
            continue
×
108

109
        ## Load the old and new tables to compare
NEW
110
        origtable = load_table(orig_pathname, tabletype='proctab')
×
NEW
111
        curr_colnames = np.array(list(origtable.colnames))
×
NEW
112
        expected_cols = np.isin(curr_colnames, ptab_cols)
×
NEW
113
        found_cols = np.isin(ptab_cols, curr_colnames)
×
114

115
        ## If everything is present, don't try to do anything
NEW
116
        if np.all(expected_cols) and np.all(found_cols):
×
NEW
117
            print(f"{orig_pathname} has all of the expected columns, not updating this table.")
×
NEW
118
            continue
×
119

NEW
120
        unexpected = list(curr_colnames[~expected_cols])
×
NEW
121
        missing = list(ptab_cols[~found_cols])
×
NEW
122
        print(f"Found the following unexpected columns: {unexpected}")
×
NEW
123
        print(f"Found the following missing columns: {missing}")
×
124

125
        ## Solving the only cases I'm currently aware of
NEW
126
        if 'CAMWORD' in unexpected and 'PROCCAMWORD' in missing:
×
NEW
127
            print(f"CAMWORD listed instead of PROCCAMWORD. Updating that.")
×
NEW
128
            origtable.rename_column('CAMWORD', 'PROCCAMWORD')
×
NEW
129
            unexpected.remove('CAWORD')
×
NEW
130
            missing.remove('PROCCAMWORD')
×
131

NEW
132
        if len(unexpected) > 0:
×
NEW
133
            print(f"WARNING: Script detected unexpected columns. Only handle "
×
134
                  + f"the case where 'CAMWORD' is defined instead of PROCCAMWORD. "
135
                  + f"The following unexpected columns will be dropped without "
136
                  + f"using the information they contain: {unexpected}.")
NEW
137
            for colname in unexpected:
×
NEW
138
                origtable.remove_column(colname)
×
139

140
        ## Add any missing columns
NEW
141
        for colname in missing:
×
NEW
142
            if colname not in ['BADAMPS', 'LASTSTEP', 'EXPFLAG']:
×
NEW
143
                print(f"WARNING: Script didn't expect {colname} to be missing. "
×
144
                      + f"Replacing with default values, but this may have "
145
                      + f"downstream consequences.")
NEW
146
            colindex = np.where(ptab_cols==colname)[0][0]
×
NEW
147
            newdat = [ptab_defs[colindex]] * len(origtable)
×
NEW
148
            newcol = Table.Column(name=colname, data=newdat, dtype=ptab_dtypes[colindex])
×
NEW
149
            origtable.add_column(newcol)
×
150

151
        ## Finally, reorder to the current column ordering
NEW
152
        origtable = origtable[list(ptab_cols)]
×
153

154
        ## If just testing, print the table and a cell-by-cell equality test
155
        ## for the scalar columns
156
        ## If not testing, move the original table to an archived filename
157
        ## and save the updated table to the official exptable pathname
NEW
158
        if dry_run:
×
NEW
159
            print("\n\nOutput file would have been:")
×
NEW
160
            origtable.pprint_all()
×
161
        else:
NEW
162
            ftime = time.strftime("%Y%m%d_%Hh%Mm")
×
NEW
163
            replaced_pathname = orig_pathname.replace(f".{orig_filetype}",
×
164
                                                      f".replaced-{ftime}.{orig_filetype}")
NEW
165
            print(f"Moving original file from {orig_pathname} to {replaced_pathname}")
×
NEW
166
            os.rename(orig_pathname,replaced_pathname)
×
NEW
167
            time.sleep(0.1)
×
NEW
168
            out_pathname = orig_pathname.replace(f".{orig_filetype}", f".{out_filetype}")
×
NEW
169
            write_table(origtable, out_pathname)
×
NEW
170
            print(f"Updated file saved to {out_pathname}. Original archived as {replaced_pathname}")
×
171

NEW
172
            print("\n\n")
×
173

174
        ## Flush the outputs
NEW
175
        sys.stdout.flush()
×
NEW
176
        sys.stderr.flush()
×
NEW
177
    print("Exposure table regenerations complete")
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc