Skip to content

Commit 8d1b6ec

Browse files
committed
[FIX] date and time now are correct and safe for schtasks.exe
1 parent e000fb6 commit 8d1b6ec

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

src/outfit.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import os
1111
import argparse
1212
import pandas as pd
13-
from datetime import datetime
13+
from datetime import datetime, timedelta
1414
from utils import *
1515
from collect_data import collect_data
1616
from process import process_data
@@ -32,6 +32,19 @@ def get_timestamp_str_from_date_time(date, time):
3232
time_str = time.Format("%H:%M:%S")
3333
return f"{date_str} {time_str}"
3434

35+
def wxDateTime_to_datetime(dt: wx.DateTime) -> datetime:
36+
if not dt.IsValid():
37+
raise ValueError("Invalid wx.DateTime object")
38+
39+
year = int(dt.GetYear())
40+
month = int(dt.GetMonth()) + 1 # wx.DateTime months are 0-based
41+
day = int(dt.GetDay())
42+
hour = int(dt.GetHour())
43+
minute = int(dt.GetMinute())
44+
second = int(dt.GetSecond())
45+
46+
return datetime(year, month, day, hour, minute, second)
47+
3548

3649
class SchedulerFrame(wx.Frame):
3750
def __init__(self):
@@ -201,7 +214,7 @@ def load_env(self):
201214
self.end_time.SetValue(ts_from)
202215

203216
self.interval_txt.SetValue(env.get(Env.INTERVAL))
204-
self.process_data_checkbox.SetValue(env.get(Env.PROCESS_DATA))
217+
# self.process_data_checkbox.SetValue(env.get(Env.PROCESS_DATA))
205218

206219
logging.info("Environment variables loaded successfully.")
207220
except Exception as e:
@@ -225,7 +238,7 @@ def store_env(self, filename: str = ".env"):
225238
env.set(Env.TIMESTAMP_FROM, ts_from_str)
226239
env.set(Env.TIMESTAMP_TO, ts_to_str)
227240
env.set(Env.INTERVAL, self.interval_txt.GetValue())
228-
env.set(Env.PROCESS_DATA, self.process_data_checkbox.GetValue())
241+
# env.set(Env.PROCESS_DATA, self.process_data_checkbox.GetValue())
229242

230243
logging.info(f"Environment variables stored to {filename}")
231244
except Exception as e:
@@ -297,10 +310,10 @@ def on_start_schedule(self, event):
297310
try:
298311
prefix = self.prefix_txt.GetValue()
299312
data_filepath = self.data_txt.GetValue()
300-
start_time = self.start_time.GetValue()
301-
start_date = self.start_date.GetValue()
302-
end_time = self.end_time.GetValue()
303-
end_date = self.end_date.GetValue()
313+
start_time = wxDateTime_to_datetime(self.start_time.GetValue())
314+
start_date = wxDateTime_to_datetime(self.start_date.GetValue())
315+
end_time = wxDateTime_to_datetime(self.end_time.GetValue())
316+
end_date = wxDateTime_to_datetime(self.end_date.GetValue())
304317
interval = self.interval_slider.GetValue()
305318

306319
start_dt = datetime(start_date.year, start_date.month, start_date.day,
@@ -315,9 +328,14 @@ def on_start_schedule(self, event):
315328

316329
env_abs_filepath = os.path.abspath(env_filepath)
317330

331+
if start_dt < datetime.now():
332+
logging.warning("Start datetime is in the past.")
333+
wx.MessageBox("Start date and time should be after the current date and time!", "Error", wx.ICON_ERROR)
334+
return
335+
318336
if end_dt <= start_dt:
319337
logging.warning("Start datetime is not earlier than end datetime.")
320-
wx.MessageBox("Start datetime should be earlier than end datetime", "Error", wx.ICON_ERROR)
338+
wx.MessageBox("Start datetime should be earlier than end datetime!", "Error", wx.ICON_ERROR)
321339
return
322340

323341
if not os.path.isfile(data_filepath):

src/utils.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from pathlib import Path
1818
from enum import Enum
1919
from dotenv import load_dotenv, set_key, unset_key
20-
from datetime import datetime
20+
from datetime import datetime, timedelta
2121
import pandas as pd
2222
import shutil
2323
import locale
@@ -180,7 +180,7 @@ def read_parquet_file(filename):
180180

181181
def read_file(filename, base_dir=None):
182182
filepath = os.path.join(base_dir, filename) if base_dir else filename
183-
183+
184184
if filename.endswith('.json'):
185185
return read_json_file(filepath)
186186
elif filename.endswith('.csv'):
@@ -247,6 +247,16 @@ def is_unix() -> bool:
247247
def is_windows() -> bool:
248248
return platform.system() == "Windows"
249249

250+
def date_windows_locale_str(dt: datetime) -> str:
251+
if locale.getlocale(locale.LC_TIME) == (None, None):
252+
locale.setlocale(locale.LC_TIME, "")
253+
return dt.strftime("%x")
254+
255+
def time_windows_locale_str(dt: datetime) -> str:
256+
if locale.getlocale(locale.LC_TIME) == (None, None):
257+
locale.setlocale(locale.LC_TIME, "")
258+
return dt.strftime("%X")
259+
250260
def schedule_on_unix(script_path, args, interval_minutes, prefix=SCHEDULE_PREFIX):
251261
job_name = f"{prefix}_{datetime.now().strftime('%Y%m%d-%H%M')}"
252262
cron_expr = f"*/{interval_minutes} * * * *"
@@ -260,19 +270,29 @@ def schedule_on_unix(script_path, args, interval_minutes, prefix=SCHEDULE_PREFIX
260270
print(f"Cron job scheduled every {interval_minutes} minutes on Unix.")
261271

262272
def schedule_on_windows(script_path, args, start_dt, end_dt, interval_minutes, prefix=SCHEDULE_PREFIX):
263-
locale.setlocale(locale.LC_TIME, '')
264-
265273
if shutil.which("schtasks") is None:
266274
raise RuntimeError("Windows Task Scheduler (schtasks) not found.")
267275

268276
if interval_minutes < 1 or interval_minutes > 1439:
269277
raise ValueError("Interval must be between 1 and 1439 minutes on Windows.")
270278

279+
# Workaround on schtasks.exe to satisfy:
280+
# (end_time - start_time) > interval
281+
# only when end_time > start_time
282+
start_time = datetime(start_dt.year, start_dt.month, start_dt.day, start_dt.hour, start_dt.minute)
283+
end_time = datetime(start_dt.year, start_dt.month, start_dt.day, end_dt.hour, end_dt.minute)
284+
285+
if end_time >= start_time:
286+
diff = end_time - start_time
287+
if diff <= timedelta(minutes=interval_minutes):
288+
compensation = (timedelta(minutes=interval_minutes + 1) - diff)
289+
end_time = end_time + compensation
290+
271291
task_name = f"{prefix}_{datetime.now().strftime('%Y%m%d-%H%M')}"
272-
start_time = start_dt.strftime("%X")
273-
start_date = start_dt.strftime("%x")
274-
end_time = end_dt.strftime("%X")
275-
end_date = end_dt.strftime("%x")
292+
start_date_str = date_windows_locale_str(start_dt)
293+
start_time_str = time_windows_locale_str(start_time)
294+
end_date_str = date_windows_locale_str(end_dt)
295+
end_time_str = time_windows_locale_str(end_time)
276296

277297
quoted_args = ' '.join(args)
278298
full_cmd = f'{script_path} {quoted_args}'
@@ -285,10 +305,10 @@ def schedule_on_windows(script_path, args, start_dt, end_dt, interval_minutes, p
285305
"/TR", full_cmd,
286306
"/SC", "MINUTE",
287307
"/MO", str(interval_minutes),
288-
"/ST", start_time,
289-
"/SD", start_date,
290-
"/ED", end_date,
291-
"/ET", end_time,
308+
"/ST", start_time_str,
309+
"/SD", start_date_str,
310+
"/ED", end_date_str,
311+
"/ET", end_time_str,
292312
"/F",
293313
"/RL", "LIMITED"
294314
]
@@ -299,7 +319,7 @@ def schedule_on_windows(script_path, args, start_dt, end_dt, interval_minutes, p
299319
except subprocess.CalledProcessError as e:
300320
print("Failed to schedule task:", e)
301321
raise Exception("Failed to schedule task")
302-
322+
303323
def remove_cron_jobs_with_prefix(prefix=SCHEDULE_PREFIX):
304324
result = subprocess.run("crontab -l", shell=True, capture_output=True, text=True)
305325
if result.returncode != 0:

0 commit comments

Comments
 (0)