completor 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- completor/constants.py +19 -0
- completor/create_output.py +12 -3
- completor/icv_file_handling.py +362 -0
- completor/icv_functions.py +845 -0
- completor/initialization.py +563 -0
- completor/input_validation.py +63 -0
- completor/logger.py +1 -1
- completor/main.py +88 -19
- completor/parse.py +5 -5
- completor/read_casefile.py +429 -48
- completor/utils.py +133 -2
- {completor-1.4.0.dist-info → completor-1.5.0.dist-info}/METADATA +11 -3
- {completor-1.4.0.dist-info → completor-1.5.0.dist-info}/RECORD +16 -13
- {completor-1.4.0.dist-info → completor-1.5.0.dist-info}/LICENSE +0 -0
- {completor-1.4.0.dist-info → completor-1.5.0.dist-info}/WHEEL +0 -0
- {completor-1.4.0.dist-info → completor-1.5.0.dist-info}/entry_points.txt +0 -0
completor/constants.py
CHANGED
|
@@ -266,6 +266,7 @@ class _Keywords:
|
|
|
266
266
|
MAP_FILE = "MAPFILE"
|
|
267
267
|
SCHEDULE_FILE = "SCHFILE"
|
|
268
268
|
OUT_FILE = "OUTFILE"
|
|
269
|
+
ICVC_KEYWORD = "ICVCONTROL"
|
|
269
270
|
|
|
270
271
|
# Note: Alphabetically sorted, which matters for check vs. missing keys in input data.
|
|
271
272
|
main_keywords = [COMPLETION_DATA, COMPLETION_SEGMENTS, WELL_SEGMENTS, WELL_SPECIFICATION]
|
|
@@ -347,3 +348,21 @@ class Method(Enum):
|
|
|
347
348
|
elif isinstance(other, str):
|
|
348
349
|
return self.name == other
|
|
349
350
|
return False
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class ICVMethod(Enum):
|
|
354
|
+
"""Enum class representing the possible states for an ICV valve."""
|
|
355
|
+
|
|
356
|
+
OPEN = "OPEN"
|
|
357
|
+
OPEN_WAIT = "OPEN_WAIT"
|
|
358
|
+
OPEN_READY = "OPEN_READY"
|
|
359
|
+
OPEN_STOP = "OPEN_STOP"
|
|
360
|
+
OPEN_WAIT_STOP = "OPEN_WAIT_STOP"
|
|
361
|
+
|
|
362
|
+
CHOKE = "CHOKE"
|
|
363
|
+
CHOKE_READY = "CHOKE_READY"
|
|
364
|
+
CHOKE_WAIT = "CHOKE_WAIT"
|
|
365
|
+
CHOKE_STOP = "CHOKE_STOP"
|
|
366
|
+
CHOKE_WAIT_STOP = "CHOKE_WAIT_STOP"
|
|
367
|
+
|
|
368
|
+
UDQ = "UDQ"
|
completor/create_output.py
CHANGED
|
@@ -21,7 +21,9 @@ from completor.visualize_well import visualize_well
|
|
|
21
21
|
from completor.wells import Lateral, Well
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def format_output(
|
|
24
|
+
def format_output(
|
|
25
|
+
well: Well, case: ReadCasefile, pdf: PdfPages | None = None
|
|
26
|
+
) -> tuple[str, str, str, str, pd.DataFrame]:
|
|
25
27
|
"""Formats the finished output string to be written to a file.
|
|
26
28
|
|
|
27
29
|
Args:
|
|
@@ -49,7 +51,7 @@ def format_output(well: Well, case: ReadCasefile, pdf: PdfPages | None = None) -
|
|
|
49
51
|
|
|
50
52
|
start_segment = 2
|
|
51
53
|
start_branch = 1
|
|
52
|
-
|
|
54
|
+
df_inflow_control_output = pd.DataFrame()
|
|
53
55
|
header_written = False
|
|
54
56
|
first = True
|
|
55
57
|
for lateral in well.active_laterals:
|
|
@@ -128,6 +130,7 @@ def format_output(well: Well, case: ReadCasefile, pdf: PdfPages | None = None) -
|
|
|
128
130
|
case.completion_icv_tubing,
|
|
129
131
|
case.wsegicv_table,
|
|
130
132
|
)
|
|
133
|
+
df_inflow_control_output = pd.concat([df_inflow_control_output, df_inflow_control_valve], ignore_index=True)
|
|
131
134
|
completion_data_list.append(
|
|
132
135
|
_format_completion_data(well.well_name, lateral.lateral_number, df_completion_data, first)
|
|
133
136
|
)
|
|
@@ -241,7 +244,13 @@ def format_output(well: Well, case: ReadCasefile, pdf: PdfPages | None = None) -
|
|
|
241
244
|
)
|
|
242
245
|
bonus.append(metadata + print_density_driven_include + "\n\n\n\n")
|
|
243
246
|
|
|
244
|
-
return
|
|
247
|
+
return (
|
|
248
|
+
print_completion_data,
|
|
249
|
+
print_well_segments,
|
|
250
|
+
print_completion_segments,
|
|
251
|
+
"".join(bonus),
|
|
252
|
+
df_inflow_control_output,
|
|
253
|
+
)
|
|
245
254
|
|
|
246
255
|
|
|
247
256
|
def _check_well_segments_header(welsegs_header: pd.DataFrame, start_measured_depths: pd.Series) -> pd.DataFrame:
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"""A file handling class for the icv-control algorithm"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from completor import icv_functions
|
|
10
|
+
from completor.constants import ICVMethod
|
|
11
|
+
from completor.get_version import get_version
|
|
12
|
+
from completor.initialization import Initialization
|
|
13
|
+
from completor.logger import logger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IcvFileHandling:
|
|
17
|
+
"""Create paths, directories, and output files."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, file_data: dict, initials: Initialization):
|
|
20
|
+
self.initials = initials
|
|
21
|
+
self.icv_functions = icv_functions.IcvFunctions(initials)
|
|
22
|
+
self.current_working_directory = Path.cwd()
|
|
23
|
+
self.output_file_name = Path(file_data["output_file_name"])
|
|
24
|
+
self.output_directory = Path(file_data["output_directory"])
|
|
25
|
+
self.input_case_file = Path(file_data["input_case_file"])
|
|
26
|
+
self.create_ordered_filenames()
|
|
27
|
+
self.create_include_files()
|
|
28
|
+
self.create_main_schedule_file(Path(file_data["schedule_file_path"]))
|
|
29
|
+
self.include_file_path = None
|
|
30
|
+
self.schedule_include_file_path = None
|
|
31
|
+
|
|
32
|
+
def create_ordered_filenames(self):
|
|
33
|
+
"""Create a dict with datetime keys and include file name values."""
|
|
34
|
+
|
|
35
|
+
self.ordered_filenames = {}
|
|
36
|
+
for icv_name in self.initials.icv_names:
|
|
37
|
+
icv_date = self.initials.icv_dates[icv_name]
|
|
38
|
+
if icv_date not in self.ordered_filenames:
|
|
39
|
+
self.ordered_filenames[icv_date] = {}
|
|
40
|
+
if icv_name not in self.ordered_filenames[icv_date]:
|
|
41
|
+
self.ordered_filenames[icv_date][icv_name] = []
|
|
42
|
+
|
|
43
|
+
def create_include_file_content(self, icv_name: str, file_type: ICVMethod, criteria: int | None = None) -> str:
|
|
44
|
+
"""Create include file content for icv functions.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
icv_name: Icv name. One or two symbols, I.E: A or Z9.
|
|
48
|
+
file_type: File type CHOKE_WAIT, OPEN_WAIT, CHOKE, OPEN.
|
|
49
|
+
criteria: Integer value denoting the criteria.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Content of the file_type include file.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
ValueError: If criteria is None for CHOKE or OPEN file type.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
trigger_number_times = 10000
|
|
59
|
+
file_content = ""
|
|
60
|
+
actionx_repeater = self.initials.case.step_table[icv_name][0]
|
|
61
|
+
if actionx_repeater > 9999 or len(str(actionx_repeater)) > 4:
|
|
62
|
+
logger.warning(f"MAX 9999 steps:'{actionx_repeater}'steps on ICV '{icv_name}'")
|
|
63
|
+
if file_type == ICVMethod.CHOKE_WAIT:
|
|
64
|
+
trigger_minimum_interval = 10
|
|
65
|
+
file_content += self.icv_functions.create_choke_wait(
|
|
66
|
+
icv_name, trigger_number_times, trigger_minimum_interval, actionx_repeater, criteria
|
|
67
|
+
)
|
|
68
|
+
return file_content
|
|
69
|
+
|
|
70
|
+
if file_type == ICVMethod.OPEN_WAIT:
|
|
71
|
+
file_content += self.icv_functions.create_open_wait(icv_name, actionx_repeater, criteria)
|
|
72
|
+
return file_content
|
|
73
|
+
|
|
74
|
+
if file_type == ICVMethod.CHOKE:
|
|
75
|
+
trigger_minimum_interval_str = ""
|
|
76
|
+
file_content = self.icv_functions.create_choke_ready(
|
|
77
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str
|
|
78
|
+
)
|
|
79
|
+
file_content += self.icv_functions.create_choke_wait_stop(icv_name, criteria)
|
|
80
|
+
file_content += self.icv_functions.create_choke_stop(
|
|
81
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str
|
|
82
|
+
)
|
|
83
|
+
trigger_number_times = 1
|
|
84
|
+
file_content += self.icv_functions.create_choke(
|
|
85
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str, actionx_repeater
|
|
86
|
+
)
|
|
87
|
+
return file_content
|
|
88
|
+
|
|
89
|
+
if file_type == ICVMethod.OPEN:
|
|
90
|
+
trigger_minimum_interval_str = ""
|
|
91
|
+
file_content += self.icv_functions.create_open_ready(
|
|
92
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str
|
|
93
|
+
)
|
|
94
|
+
file_content += self.icv_functions.create_open_wait_stop(icv_name, criteria)
|
|
95
|
+
file_content += self.icv_functions.create_open_stop(
|
|
96
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str
|
|
97
|
+
)
|
|
98
|
+
trigger_number_times = 1
|
|
99
|
+
file_content += self.icv_functions.create_open(
|
|
100
|
+
icv_name, criteria, trigger_number_times, trigger_minimum_interval_str, actionx_repeater
|
|
101
|
+
)
|
|
102
|
+
return file_content
|
|
103
|
+
|
|
104
|
+
raise ValueError(f"The file type '{file_type}' is not recognized.")
|
|
105
|
+
|
|
106
|
+
def append_control_criteria_to_file(
|
|
107
|
+
self, file_path: Path, well_name: str, icv_name: str, file_type: ICVMethod, icv_date: str, criteria: int | None
|
|
108
|
+
):
|
|
109
|
+
"""Append to the file.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
file_path: Path to the file
|
|
113
|
+
well_name: Well name.
|
|
114
|
+
icv_name: Icv name, one or two symbols, I.E: A or Z9.
|
|
115
|
+
file_type: Type of icv function file.
|
|
116
|
+
icv_date: Date of Icv change.
|
|
117
|
+
criteria: Integer value denoting the criteria.
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
self.ordered_filenames[icv_date][icv_name].append(file_path)
|
|
121
|
+
content = self.create_include_file_content(icv_name, file_type, criteria)
|
|
122
|
+
content = self.add_section_header(content, f"{well_name} {icv_name} {file_type}")
|
|
123
|
+
self.append_content_to_file(file_path, content)
|
|
124
|
+
|
|
125
|
+
def append_content_to_file(self, file_path: Path, content: str):
|
|
126
|
+
"""Append content to the file. Create the file if it does not exist.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
file_path: Path to the file.
|
|
130
|
+
content: Content to append to the file.
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
file_path.touch(exist_ok=True)
|
|
134
|
+
|
|
135
|
+
if not content.endswith("\n\n"):
|
|
136
|
+
content += "\n\n"
|
|
137
|
+
|
|
138
|
+
with open(file_path, "a", encoding="utf-8") as file:
|
|
139
|
+
file.write(content)
|
|
140
|
+
|
|
141
|
+
def add_section_header(self, content: str, header: str) -> str:
|
|
142
|
+
"""Add a section header to the content.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
content: Content to add the header to.
|
|
146
|
+
header: Header to add to the content.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Content with the header added.
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
return f"--- {header} ---\n{content}"
|
|
153
|
+
|
|
154
|
+
def create_include_files(self):
|
|
155
|
+
"""Create the include files for icv-control.
|
|
156
|
+
|
|
157
|
+
The include files are as follows:
|
|
158
|
+
- include.sch
|
|
159
|
+
- icv-control tables
|
|
160
|
+
- init
|
|
161
|
+
- input
|
|
162
|
+
- control critieria for each well and icv.
|
|
163
|
+
- summary.sch
|
|
164
|
+
"""
|
|
165
|
+
base_folder = Path(self.output_directory)
|
|
166
|
+
fmu_path = Path("eclipse/include/")
|
|
167
|
+
if str(fmu_path) in str(base_folder):
|
|
168
|
+
base_include_path = Path("../include/schedule")
|
|
169
|
+
else:
|
|
170
|
+
base_include_path = Path("")
|
|
171
|
+
|
|
172
|
+
base_folder.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
|
|
174
|
+
summary_file_path = Path(base_folder / "summary_icvc.sch")
|
|
175
|
+
self.include_file_path = Path(base_folder / "include_icvc.sch")
|
|
176
|
+
self.schedule_include_file_path = Path(base_include_path / "include_icvc.sch")
|
|
177
|
+
# case_file_path = Path(base_folder / "case_icvc.case")
|
|
178
|
+
|
|
179
|
+
summary_file_path.unlink(missing_ok=True)
|
|
180
|
+
self.include_file_path.unlink(missing_ok=True)
|
|
181
|
+
# case_file_path.unlink(missing_ok=True)
|
|
182
|
+
|
|
183
|
+
content = self.add_section_header(self.initials.init_icvcontrol, "INIT")
|
|
184
|
+
self.append_content_to_file(self.include_file_path, content)
|
|
185
|
+
|
|
186
|
+
content = self.add_section_header(self.initials.input_icvcontrol, "INPUT")
|
|
187
|
+
self.append_content_to_file(self.include_file_path, content)
|
|
188
|
+
|
|
189
|
+
content = self.add_section_header(self.initials.summary, "SUMMARY")
|
|
190
|
+
self.append_content_to_file(summary_file_path, content)
|
|
191
|
+
logger.info(f"Created summary file: '{summary_file_path}'.")
|
|
192
|
+
|
|
193
|
+
content = self.add_section_header(content, f"Completor version: {get_version()}")
|
|
194
|
+
# self.append_content_to_file(case_file_path, content)
|
|
195
|
+
|
|
196
|
+
for icv_name in self.initials.icv_names:
|
|
197
|
+
well_name = self.initials.well_names[icv_name]
|
|
198
|
+
icv_date = self.initials.icv_dates[icv_name]
|
|
199
|
+
|
|
200
|
+
for file_type in [ICVMethod.CHOKE_WAIT, ICVMethod.CHOKE, ICVMethod.OPEN_WAIT, ICVMethod.OPEN]:
|
|
201
|
+
if (
|
|
202
|
+
self.icv_functions.custom_conditions is not None
|
|
203
|
+
and file_type in self.icv_functions.custom_conditions
|
|
204
|
+
and icv_name in self.icv_functions.custom_conditions[file_type]
|
|
205
|
+
):
|
|
206
|
+
custom_criteria = self.icv_functions.custom_conditions[file_type][icv_name].keys()
|
|
207
|
+
for criteria in custom_criteria:
|
|
208
|
+
if criteria != "map":
|
|
209
|
+
self.append_control_criteria_to_file(
|
|
210
|
+
self.include_file_path, well_name, icv_name, file_type, icv_date, criteria
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
logger.warning(
|
|
214
|
+
f"No criteria found for well '{well_name}' icv '{icv_name}' " f"function '{file_type}'."
|
|
215
|
+
)
|
|
216
|
+
self.append_control_criteria_to_file(
|
|
217
|
+
self.include_file_path, well_name, icv_name, file_type, icv_date, None
|
|
218
|
+
)
|
|
219
|
+
logger.info(f"Created include file: '{self.include_file_path}'.")
|
|
220
|
+
|
|
221
|
+
def create_main_schedule_file(self, input_schedule: Path):
|
|
222
|
+
"""Creates the main output schedule file from the input schedule file.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
input_schedule: Input schedule file name.
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
main_schedule_file = Path(self.output_directory / self.output_file_name)
|
|
229
|
+
|
|
230
|
+
dates = [datetime.datetime.strptime(d, "%d %b %Y") for d in set(self.initials.icv_dates.values())]
|
|
231
|
+
with open(input_schedule, encoding="utf-8") as fh:
|
|
232
|
+
sch_files = fh.readlines()
|
|
233
|
+
for index, line in enumerate(sch_files):
|
|
234
|
+
line = " ".join(line.split())
|
|
235
|
+
if "DATES" in line:
|
|
236
|
+
sch_files[index] = line.replace("DATES\n", "DATES \n")
|
|
237
|
+
|
|
238
|
+
sch_data = "".join(sch_files).split("DATES")
|
|
239
|
+
sch_data_before_date = sch_data[0]
|
|
240
|
+
sch_data_after_first_date, date_comment = format_date(sch_data[1:])
|
|
241
|
+
for date in sch_data_after_first_date:
|
|
242
|
+
try:
|
|
243
|
+
dates.append(datetime.datetime.strptime(date, "%d %b %Y %H %M %S"))
|
|
244
|
+
except ValueError:
|
|
245
|
+
dates.append(datetime.datetime.strptime(date, "%d %b %Y"))
|
|
246
|
+
sorted_dates = []
|
|
247
|
+
# Sort dates chronologically and remove duplicate date entries.
|
|
248
|
+
for date in sorted(set(dates)):
|
|
249
|
+
# When the timestamp is zero, the timestamp is not written.
|
|
250
|
+
if date.hour == 0 and date.minute == 0 and date.second == 0:
|
|
251
|
+
sorted_dates.append(date.strftime("%d %b %Y").upper())
|
|
252
|
+
else:
|
|
253
|
+
sorted_dates.append(date.strftime("%d %b %Y %H:%M:%S").upper())
|
|
254
|
+
|
|
255
|
+
lines_path_to_include = list(sch_data_before_date)
|
|
256
|
+
lines_path = self.format_sch_file(sorted_dates, sch_data_after_first_date, date_comment)
|
|
257
|
+
lines_path_to_include += lines_path
|
|
258
|
+
main_schedule_lines = [line.replace("//", "/") for line in lines_path_to_include]
|
|
259
|
+
|
|
260
|
+
with open(main_schedule_file, "w", encoding="utf-8") as file:
|
|
261
|
+
file.write("".join(main_schedule_lines))
|
|
262
|
+
logger.info(f"Created main schedule file: '{main_schedule_file}'.")
|
|
263
|
+
|
|
264
|
+
def format_sch_file(
|
|
265
|
+
self, sorted_dates: list[str], sch_data_after_first_date: dict[str, str], date_comment: dict[str, str]
|
|
266
|
+
) -> list[str]:
|
|
267
|
+
"""
|
|
268
|
+
Formats the SCH file by including relevant paths and
|
|
269
|
+
content based on sorted dates and SCH data.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
sorted_dates: A list of sorted dates.
|
|
273
|
+
sch_data_after_first_date: Containing SCH data after the first date.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
lines_path_to_include: Formatted paths to include in the SCH file.
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
lines_path_to_include = []
|
|
280
|
+
base_include = "INCLUDE\n"
|
|
281
|
+
|
|
282
|
+
for icvdate in sorted_dates:
|
|
283
|
+
try:
|
|
284
|
+
lines_path_to_include.append(f"DATES\n {icvdate} /{date_comment[icvdate]}\n/\n")
|
|
285
|
+
except KeyError:
|
|
286
|
+
lines_path_to_include.append(f"DATES\n {icvdate} /\n/\n")
|
|
287
|
+
try:
|
|
288
|
+
# Append content from SCH file after the first date.
|
|
289
|
+
lines_path_to_include.append(sch_data_after_first_date[icvdate].strip() + "\n\n")
|
|
290
|
+
except KeyError:
|
|
291
|
+
# Skip if SCH data for the icvdate is not found.
|
|
292
|
+
pass
|
|
293
|
+
try:
|
|
294
|
+
self.ordered_filenames[icvdate].keys()
|
|
295
|
+
except KeyError:
|
|
296
|
+
continue
|
|
297
|
+
# The includes of 'init.udq' and 'input.udq' should be placed close to the
|
|
298
|
+
# include 'well' statement.
|
|
299
|
+
lines_path_to_include.append(f"{base_include} '{self.schedule_include_file_path}' /\n\n".replace("//", "/"))
|
|
300
|
+
return lines_path_to_include
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def format_date(date_contents: list) -> tuple[dict, dict]:
|
|
304
|
+
"""
|
|
305
|
+
This function takes a list of dates in the format "day/month/year time" or
|
|
306
|
+
"day/month/year" and returns a dictionary of the formatted dates as keys and
|
|
307
|
+
the corresponding schedule data as values.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
sch_dates: List of strings representing dates and schedule data.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
sch_data: Dictionary with keys as formatted dates and values as schedule data.
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
ValueError: if the input date string is not in the format of
|
|
317
|
+
"day/month/year time" or "day/month/year".
|
|
318
|
+
|
|
319
|
+
"""
|
|
320
|
+
formatted_data = {}
|
|
321
|
+
date_comment = {}
|
|
322
|
+
# iterate over the list of schedule dates
|
|
323
|
+
for date_content in date_contents:
|
|
324
|
+
if date_content:
|
|
325
|
+
date_line, content = date_content.split("\n", 1)
|
|
326
|
+
date, comment = date_line.split("/", 1)
|
|
327
|
+
comment = comment.lstrip()
|
|
328
|
+
date = date.strip()
|
|
329
|
+
if comment:
|
|
330
|
+
comment = " " + comment
|
|
331
|
+
if content.lstrip().startswith("/"):
|
|
332
|
+
content = content.replace("/", "", 1)
|
|
333
|
+
# Remove special characters from the date string, replacing "JLY" with "JUL"
|
|
334
|
+
date_formated = re.sub(r"[^a-zA-Z0-9]", " ", date).strip()
|
|
335
|
+
date_formated = date_formated.replace("JLY", "JUL")
|
|
336
|
+
try:
|
|
337
|
+
# Try to parse the date using the expected format
|
|
338
|
+
date = datetime.datetime.strptime(date_formated, "%d %b %Y %H %M %S")
|
|
339
|
+
except ValueError:
|
|
340
|
+
try:
|
|
341
|
+
date = datetime.datetime.strptime(date_formated, "%d %b %Y")
|
|
342
|
+
except ValueError:
|
|
343
|
+
try:
|
|
344
|
+
date = datetime.datetime.strptime(date_formated, "%d%b%Y")
|
|
345
|
+
except ValueError:
|
|
346
|
+
logger.warning(
|
|
347
|
+
"Date format seems to be wrong in the schedule file.\n"
|
|
348
|
+
"Format: 1 JAN 2030. The day is an integer between 1-31.\n"
|
|
349
|
+
"The months are JAN, FEB, MAR, APR, MAY, JUN, JLY/JUL, "
|
|
350
|
+
"AUG, SEP, OCT, NOV or DEC.\nThe year is a 4 digit integer."
|
|
351
|
+
f" See line that states:'{date}'."
|
|
352
|
+
)
|
|
353
|
+
try:
|
|
354
|
+
if date.hour == 0 and date.minute == 0 and date.second == 0:
|
|
355
|
+
date = date.strftime("%d %b %Y").upper()
|
|
356
|
+
else:
|
|
357
|
+
date = date.strftime("%d %b %Y %H %M %S").upper()
|
|
358
|
+
except AttributeError:
|
|
359
|
+
logger.warning(f"Something wrong in the date format in line: {date}")
|
|
360
|
+
formatted_data[date] = content
|
|
361
|
+
date_comment[date] = comment
|
|
362
|
+
return formatted_data, date_comment
|