mirror of
https://github.com/weewx/weewx.git
synced 2026-04-18 16:46:56 -04:00
705 lines
28 KiB
Python
705 lines
28 KiB
Python
#
|
|
# Copyright (c) 2009-2023 Tom Keffer <tkeffer@gmail.com> and Matthew Wall
|
|
#
|
|
# See the file LICENSE.txt for your rights.
|
|
#
|
|
"""Various high-level, interactive, database actions"""
|
|
|
|
import logging
|
|
import sys
|
|
import time
|
|
|
|
import weecfg
|
|
import weectllib
|
|
import weedb
|
|
import weewx.manager
|
|
from weeutil.weeutil import bcolors, y_or_n, timestamp_to_string
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def create_database(config_path,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Create a new database."""
|
|
if dry_run:
|
|
print("This is a dry run. Nothing will actually be done.")
|
|
|
|
config_path, config_dict = weecfg.read_config(config_path)
|
|
print(f"The configuration file {bcolors.BOLD}{config_path}{bcolors.ENDC} will be used.")
|
|
|
|
weectllib.initialize(config_dict)
|
|
|
|
# Try a simple open. If it succeeds, that means the database
|
|
# exists and is initialized. Otherwise, an exception will be raised.
|
|
try:
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbmanager:
|
|
print(f"Database '{dbmanager.database_name}' already exists. Nothing done.")
|
|
except weedb.OperationalError:
|
|
if not dry_run:
|
|
# Database does not exist. Try again, but allow initialization:
|
|
with weewx.manager.open_manager_with_config(config_dict,
|
|
db_binding,
|
|
initialize=True) as dbmanager:
|
|
print(f"Created database '{dbmanager.database_name}'.")
|
|
|
|
|
|
def drop_daily(config_path,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Drop the daily summary from a WeeWX database."""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
print(f"Proceeding will delete all your daily summaries from database '{database_name}'")
|
|
ans = y_or_n("Are you sure you want to proceed (y/n)? ")
|
|
if ans == 'y':
|
|
t1 = time.time()
|
|
print(f"Dropping daily summary tables from '{database_name}' ... ")
|
|
try:
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbmanager:
|
|
try:
|
|
if not dry_run:
|
|
dbmanager.drop_daily()
|
|
except weedb.OperationalError as e:
|
|
print("Error '%s'" % e, file=sys.stderr)
|
|
print(f"Drop daily summary tables failed for database '{database_name}'")
|
|
else:
|
|
tdiff = time.time() - t1
|
|
print("Daily summary tables dropped from "
|
|
f"database '{database_name}' in {tdiff:.2f} seconds")
|
|
except weedb.OperationalError:
|
|
# No daily summaries. Nothing to be done.
|
|
print(f"No daily summaries found in database '{database_name}'. Nothing done.")
|
|
else:
|
|
print("Nothing done.")
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def rebuild_daily(config_path,
|
|
date=None,
|
|
from_date=None,
|
|
to_date=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Rebuild the daily summaries."""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
# Get any dates the user might have specified.
|
|
from_d, to_d = weectllib.parse_dates(date, from_date, to_date)
|
|
|
|
# Advise the user/log what we will do
|
|
if not from_d and not to_d:
|
|
msg = "All daily summaries will be rebuilt."
|
|
elif from_d and not to_d:
|
|
msg = f"Daily summaries starting with {from_d} will be rebuilt."
|
|
elif not from_d and to_d:
|
|
msg = f"Daily summaries through {to_d} will be rebuilt."
|
|
elif from_d == to_d:
|
|
msg = f"Daily summary for {from_d} will be rebuilt."
|
|
else:
|
|
msg = f"Daily summaries from {from_d} through {to_d}, inclusive, will be rebuilt."
|
|
|
|
log.info(msg)
|
|
print(msg)
|
|
ans = y_or_n(f"Rebuild the daily summaries in the database '{database_name}'? (y/n) ")
|
|
if ans == 'n':
|
|
log.info("Nothing done.")
|
|
print("Nothing done.")
|
|
return
|
|
|
|
t1 = time.time()
|
|
|
|
log.info("Rebuilding daily summaries in database '%s' ..." % database_name)
|
|
print("Rebuilding daily summaries in database '%s' ..." % database_name)
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
return
|
|
|
|
# Open up the database. This will create the tables necessary for the daily
|
|
# summaries if they don't already exist:
|
|
with weewx.manager.open_manager_with_config(config_dict,
|
|
db_binding, initialize=True) as dbm:
|
|
# Do the actual rebuild
|
|
nrecs, ndays = dbm.backfill_day_summary(start_d=from_d,
|
|
stop_d=to_d,
|
|
trans_days=20)
|
|
tdiff = time.time() - t1
|
|
# advise the user/log what we did
|
|
log.info("Rebuild of daily summaries in database '%s' complete." % database_name)
|
|
if nrecs:
|
|
sys.stdout.flush()
|
|
# fix a bit of formatting inconsistency if less than 1000 records
|
|
# processed
|
|
if nrecs >= 1000:
|
|
print()
|
|
if ndays == 1:
|
|
msg = f"Processed {nrecs} records to rebuild 1 daily summary in {tdiff:.2f} seconds."
|
|
else:
|
|
msg = f"Processed {nrecs} records to rebuild {ndays} daily summaries in " \
|
|
f"{tdiff:.2f} seconds."
|
|
print(msg)
|
|
print(f"Rebuild of daily summaries in database '{database_name}' complete.")
|
|
else:
|
|
print(f"Daily summaries up to date in '{database_name}'.")
|
|
|
|
|
|
def add_column(config_path,
|
|
column_name=None,
|
|
column_type=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Add a single column to the database.
|
|
column_name: The name of the new column.
|
|
column_type: The type ("REAL"|"INTEGER") of the new column.
|
|
"""
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
column_type = column_type or 'REAL'
|
|
ans = y_or_n(
|
|
f"Add new column '{column_name}' of type '{column_type}' "
|
|
f"to database '{database_name}'? (y/n) ")
|
|
if ans == 'y':
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbm:
|
|
if not dry_run:
|
|
dbm.add_column(column_name, column_type)
|
|
print(f'New column {column_name} of type {column_type} added to database.')
|
|
else:
|
|
print("Nothing done.")
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def rename_column(config_path,
|
|
from_name=None,
|
|
to_name=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
ans = y_or_n(f"Rename column '{from_name}' to '{to_name}' "
|
|
f"in database {database_name}? (y/n) ")
|
|
if ans == 'y':
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbm:
|
|
if not dry_run:
|
|
dbm.rename_column(from_name, to_name)
|
|
print(f"Column '{from_name}' renamed to '{to_name}'.")
|
|
else:
|
|
print("Nothing done.")
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def drop_columns(config_path,
|
|
column_names=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Drop a set of columns from the database"""
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
ans = y_or_n(f"Drop column(s) '{', '.join(column_names)}' from the database? (y/n) ")
|
|
if ans == 'y':
|
|
drop_set = set(column_names)
|
|
# Now drop the columns. If one is missing, a NoColumnError will be raised. Be prepared
|
|
# to catch it.
|
|
print("This may take a while...")
|
|
if not dry_run:
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbm:
|
|
try:
|
|
dbm.drop_columns(drop_set)
|
|
except weedb.NoColumnError as e:
|
|
print(e, file=sys.stderr)
|
|
print("Nothing done.")
|
|
else:
|
|
print(f"Column(s) '{', '.join(column_names)}' dropped from the database.")
|
|
else:
|
|
print("Nothing done.")
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def reconfigure_database(config_path,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Create a new database, then populate it with the contents of an old database, but use
|
|
the current configuration options. The reconfigure action will create a new database with the
|
|
same name as the old, except with the suffix _new attached to the end."""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
manager_dict = weewx.manager.get_manager_dict_from_config(config_dict,
|
|
db_binding)
|
|
# Make a copy for the new database (we will be modifying it)
|
|
new_database_dict = dict(manager_dict['database_dict'])
|
|
|
|
# Now modify the database name
|
|
new_database_dict['database_name'] = manager_dict['database_dict']['database_name'] + '_new'
|
|
|
|
# First check and see if the new database already exists. If it does, check
|
|
# with the user whether it's ok to delete it.
|
|
try:
|
|
if not dry_run:
|
|
weedb.create(new_database_dict)
|
|
except weedb.DatabaseExists:
|
|
ans = y_or_n("New database '%s' already exists. "
|
|
"Delete it first? (y/n) " % new_database_dict['database_name'])
|
|
if ans == 'y':
|
|
weedb.drop(new_database_dict)
|
|
else:
|
|
print("Nothing done.")
|
|
return
|
|
|
|
# Get the unit system of the old archive:
|
|
with weewx.manager.Manager.open(manager_dict['database_dict']) as old_dbmanager:
|
|
old_unit_system = old_dbmanager.std_unit_system
|
|
|
|
if old_unit_system is None:
|
|
print("Old database has not been initialized. Nothing to be done.")
|
|
return
|
|
|
|
# Get the unit system of the new archive:
|
|
try:
|
|
target_unit_nickname = config_dict['StdConvert']['target_unit']
|
|
except KeyError:
|
|
target_unit_system = None
|
|
else:
|
|
target_unit_system = weewx.units.unit_constants[target_unit_nickname.upper()]
|
|
|
|
print("Copying database '%s' to '%s'" % (manager_dict['database_dict']['database_name'],
|
|
new_database_dict['database_name']))
|
|
if target_unit_system is None or old_unit_system == target_unit_system:
|
|
print("The new database will use the same unit system as the old ('%s')." %
|
|
weewx.units.unit_nicknames[old_unit_system])
|
|
else:
|
|
print("Units will be converted from the '%s' system to the '%s' system." %
|
|
(weewx.units.unit_nicknames[old_unit_system],
|
|
weewx.units.unit_nicknames[target_unit_system]))
|
|
|
|
ans = y_or_n("Are you sure you wish to proceed? (y/n) ")
|
|
if ans == 'y':
|
|
t1 = time.time()
|
|
weewx.manager.reconfig(manager_dict['database_dict'],
|
|
new_database_dict,
|
|
new_unit_system=target_unit_system,
|
|
new_schema=manager_dict['schema'],
|
|
dry_run=dry_run)
|
|
tdiff = time.time() - t1
|
|
print("Database '%s' copied to '%s' in %.2f seconds."
|
|
% (manager_dict['database_dict']['database_name'],
|
|
new_database_dict['database_name'],
|
|
tdiff))
|
|
else:
|
|
print("Nothing done.")
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def transfer_database(config_path,
|
|
dest_binding=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Transfer 'archive' data from one database to another"""
|
|
|
|
# do we have enough to go on, must have a dest binding
|
|
if not dest_binding:
|
|
print("Destination binding not specified. Nothing Done. Aborting.", file=sys.stderr)
|
|
return
|
|
|
|
if dry_run:
|
|
print("This is a dry run. Nothing will actually be done.")
|
|
|
|
config_path, config_dict = weecfg.read_config(config_path)
|
|
weectllib.initialize(config_dict)
|
|
|
|
print(f"The configuration file {bcolors.BOLD}{config_path}{bcolors.ENDC} will be used.")
|
|
|
|
# get manager dict for our source binding
|
|
src_manager_dict = weewx.manager.get_manager_dict_from_config(config_dict,
|
|
db_binding)
|
|
# get manager dict for our dest binding
|
|
try:
|
|
dest_manager_dict = weewx.manager.get_manager_dict_from_config(config_dict,
|
|
dest_binding)
|
|
except weewx.UnknownBinding:
|
|
# if we can't find the binding display a message then return
|
|
print(f"Unknown destination binding '{dest_binding}'. "
|
|
f"Please confirm the destination binding.")
|
|
print("Nothing Done. Aborting.", file=sys.stderr)
|
|
return
|
|
except weewx.UnknownDatabase as e:
|
|
# if we can't find the database display a message then return
|
|
print(f"Error accessing destination database: {e}", file=sys.stderr)
|
|
print("Nothing Done. Aborting.", file=sys.stderr)
|
|
return
|
|
except (ValueError, AttributeError):
|
|
# maybe a schema issue
|
|
print("Error accessing destination database.", file=sys.stderr)
|
|
print("Maybe the destination schema is incorrectly specified "
|
|
"in binding '%s' in weewx.conf?" % dest_binding, file=sys.stderr)
|
|
print("Nothing Done. Aborting.", file=sys.stderr)
|
|
return
|
|
except weewx.UnknownDatabaseType:
|
|
# maybe a [Databases] issue
|
|
print("Error accessing destination database.", file=sys.stderr)
|
|
print("Maybe the destination database is incorrectly defined in weewx.conf?",
|
|
file=sys.stderr)
|
|
print("Nothing Done. Aborting.", file=sys.stderr)
|
|
return
|
|
|
|
# All looks good. Get a manager for our source
|
|
with weewx.manager.Manager.open(src_manager_dict['database_dict']) as src_manager:
|
|
# How many source records?
|
|
num_recs = src_manager.getSql("SELECT COUNT(dateTime) from %s;"
|
|
% src_manager.table_name)[0]
|
|
if not num_recs:
|
|
# we have no source records to transfer so abort with a message
|
|
print(f"No records found in source database '{src_manager.database_name}'.")
|
|
print("Nothing done. Aborting.")
|
|
return
|
|
|
|
# not a dry run, actually do the transfer
|
|
ans = y_or_n("Transfer %s records from source database '%s' "
|
|
"to destination database '%s'? (y/n) "
|
|
% (num_recs, src_manager.database_name,
|
|
dest_manager_dict['database_dict']['database_name']))
|
|
if ans == 'n':
|
|
print("Nothing done.")
|
|
return
|
|
|
|
t1 = time.time()
|
|
nrecs = 0
|
|
# wrap in a try..except in case we have an error
|
|
try:
|
|
with weewx.manager.Manager.open_with_create(
|
|
dest_manager_dict['database_dict'],
|
|
table_name=dest_manager_dict['table_name'],
|
|
schema=dest_manager_dict['schema']) as dest_manager:
|
|
print("Transferring, this may take a while.... ")
|
|
sys.stdout.flush()
|
|
|
|
if not dry_run:
|
|
# This could generate a *lot* of log entries. Temporarily disable logging
|
|
# for events at or below INFO
|
|
logging.disable(logging.INFO)
|
|
|
|
# do the transfer, should be quick as it's done as a
|
|
# single transaction
|
|
nrecs = dest_manager.addRecord(src_manager.genBatchRecords(),
|
|
progress_fn=weewx.manager.show_progress)
|
|
|
|
# Remove the temporary restriction
|
|
logging.disable(logging.NOTSET)
|
|
|
|
tdiff = time.time() - t1
|
|
print("\nCompleted.")
|
|
print("%s records transferred from source database '%s' to "
|
|
"destination database '%s' in %.2f seconds."
|
|
% (nrecs, src_manager.database_name,
|
|
dest_manager.database_name, tdiff))
|
|
except ImportError as e:
|
|
# Probably when trying to load db driver
|
|
print("Error accessing destination database '%s'."
|
|
% (dest_manager_dict['database_dict']['database_name'],),
|
|
file=sys.stderr)
|
|
print("Nothing done. Aborting.", file=sys.stderr)
|
|
raise
|
|
except (OSError, weedb.OperationalError):
|
|
# probably a weewx.conf typo or MySQL db not created
|
|
print("Error accessing destination database '%s'."
|
|
% dest_manager_dict['database_dict']['database_name'], file=sys.stderr)
|
|
print("Maybe it does not exist (MySQL) or is incorrectly "
|
|
"defined in weewx.conf?", file=sys.stderr)
|
|
print("Nothing done. Aborting.", file=sys.stderr)
|
|
raise
|
|
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|
|
|
|
def calc_missing(config_path,
|
|
date=None,
|
|
from_date=None,
|
|
to_date=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Calculate any missing derived observations and save to database."""
|
|
import weecfg.database
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
log.info("Preparing to calculate missing derived observations...")
|
|
|
|
# get a db manager dict given the config dict and binding
|
|
manager_dict = weewx.manager.get_manager_dict_from_config(config_dict,
|
|
db_binding)
|
|
# Get the table_name used by the binding, it could be different to the
|
|
# default 'archive'. If for some reason it is not specified then fail hard.
|
|
table_name = manager_dict['table_name']
|
|
# get the first and last good timestamps from the archive, these represent
|
|
# our overall bounds for calculating missing derived obs
|
|
with weewx.manager.Manager.open(manager_dict['database_dict'],
|
|
table_name=table_name) as dbmanager:
|
|
first_ts = dbmanager.firstGoodStamp()
|
|
last_ts = dbmanager.lastGoodStamp()
|
|
# process any command line options that may limit the period over which
|
|
# missing derived obs are calculated
|
|
start_dt, stop_dt = weectllib.parse_dates(date,
|
|
from_date=from_date, to_date=to_date,
|
|
as_datetime=True)
|
|
# we now have a start and stop date for processing, we need to obtain those
|
|
# as epoch timestamps, if we have no start and/or stop date then use the
|
|
# first or last good timestamp instead
|
|
start_ts = time.mktime(start_dt.timetuple()) if start_dt is not None else first_ts - 1
|
|
stop_ts = time.mktime(stop_dt.timetuple()) if stop_dt is not None else last_ts
|
|
# notify if this is a dry run
|
|
if dry_run:
|
|
msg = "This is a dry run, missing derived observations will be calculated but not saved"
|
|
log.info(msg)
|
|
print(msg)
|
|
_head = "Missing derived observations will be calculated "
|
|
# advise the user/log what we will do
|
|
if start_dt is None and stop_dt is None:
|
|
_tail = "for all records."
|
|
elif start_dt and not stop_dt:
|
|
_tail = "from %s through to the end (%s)." % (timestamp_to_string(start_ts),
|
|
timestamp_to_string(stop_ts))
|
|
elif not start_dt and stop_dt:
|
|
_tail = "from the beginning (%s) through to %s." % (timestamp_to_string(start_ts),
|
|
timestamp_to_string(stop_ts))
|
|
else:
|
|
_tail = "from %s through to %s inclusive." % (timestamp_to_string(start_ts),
|
|
timestamp_to_string(stop_ts))
|
|
msg = "%s%s" % (_head, _tail)
|
|
log.info(msg)
|
|
print(msg)
|
|
ans = y_or_n("Proceed? (y/n) ")
|
|
if ans == 'n':
|
|
msg = "Nothing done."
|
|
log.info(msg)
|
|
print(msg)
|
|
return
|
|
|
|
t1 = time.time()
|
|
|
|
# construct a CalcMissing config dict
|
|
calc_missing_config_dict = {'name': 'Calculate Missing Derived Observations',
|
|
'binding': db_binding,
|
|
'start_ts': start_ts,
|
|
'stop_ts': stop_ts,
|
|
'trans_days': 20,
|
|
'dry_run': dry_run}
|
|
|
|
# obtain a CalcMissing object
|
|
calc_missing_obj = weecfg.database.CalcMissing(config_dict,
|
|
calc_missing_config_dict)
|
|
log.info("Calculating missing derived observations...")
|
|
print("Calculating missing derived observations...")
|
|
# Calculate and store any missing observations. Be prepared to
|
|
# catch any exceptions from CalcMissing.
|
|
try:
|
|
calc_missing_obj.run()
|
|
except weewx.UnknownBinding as e:
|
|
# We have an unknown binding, this could occur if we are using a
|
|
# non-default binding and StdWXCalculate has not been told (via
|
|
# weewx.conf) to use the same binding. Log it and notify the user then
|
|
# exit.
|
|
msg = "Error: '%s'" % e
|
|
print(msg)
|
|
log.error(msg)
|
|
print("Perhaps StdWXCalculate is using a different binding. Check "
|
|
"configuration file [StdWXCalculate] stanza")
|
|
sys.exit("Nothing done. Aborting.")
|
|
else:
|
|
msg = "Missing derived observations calculated in %0.2f seconds" % (time.time() - t1)
|
|
log.info(msg)
|
|
print(msg)
|
|
|
|
|
|
def check(config_path, db_binding='wx_binding'):
|
|
"""Check the database for any issues."""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run=False)
|
|
weectllib.initialize(config_dict)
|
|
|
|
print("Checking daily summary tables version...")
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbm:
|
|
daily_summary_version = dbm._read_metadata('Version')
|
|
|
|
msg = f"Daily summary tables are at version {daily_summary_version}."
|
|
log.info(msg)
|
|
print(msg)
|
|
|
|
if daily_summary_version is not None and daily_summary_version >= '2.0':
|
|
# interval weighting fix has been applied
|
|
msg = "Interval Weighting Fix is not required."
|
|
log.info(msg)
|
|
print(msg)
|
|
else:
|
|
print("Recommend running --update to recalculate interval weightings.")
|
|
|
|
|
|
def update_database(config_path,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Apply any required database fixes.
|
|
|
|
Applies the following fixes:
|
|
- checks if database version is 3.0, if not interval weighting fix is
|
|
applied
|
|
- recalculates windSpeed daily summary max and maxtime fields from
|
|
archive
|
|
"""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
ans = y_or_n("The update process does not affect archive data, "
|
|
"but does alter the database.\nContinue (y/n)? ")
|
|
if ans == 'n':
|
|
log.info("Update cancelled.")
|
|
print("Update cancelled.")
|
|
return
|
|
|
|
log.info("Preparing interval weighting fix...")
|
|
print("Preparing interval weighting fix...")
|
|
|
|
# Get a database manager object
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbm:
|
|
# check the daily summary version
|
|
msg = f"Daily summary tables are at version {dbm.version}."
|
|
log.info(msg)
|
|
print(msg)
|
|
|
|
if dbm.version is not None and dbm.version >= '4.0':
|
|
# interval weighting fix has been applied
|
|
log.info("Interval weighting fix is not required.")
|
|
print("Interval weighting fix is not required.")
|
|
else:
|
|
# apply the interval weighting
|
|
log.info("Calculating interval weights...")
|
|
print("Calculating interval weights. This could take awhile.")
|
|
t1 = time.time()
|
|
if not dry_run:
|
|
dbm.update()
|
|
msg = "Interval Weighting Fix completed in %0.2f seconds." % (time.time() - t1)
|
|
print()
|
|
print(msg)
|
|
sys.stdout.flush()
|
|
log.info(msg)
|
|
|
|
# recalc the max/maxtime windSpeed values
|
|
_fix_wind(config_dict, db_binding, dry_run)
|
|
|
|
|
|
def _fix_wind(config_dict, db_binding, dry_run):
|
|
"""Recalculate the windSpeed daily summary max and maxtime fields.
|
|
|
|
Create a WindSpeedRecalculation object and call its run() method to
|
|
recalculate the max and maxtime fields from archive data. This process is
|
|
idempotent, so it can be called repeatedly with no ill effect.
|
|
"""
|
|
import weecfg.database
|
|
|
|
msg = "Preparing maximum windSpeed fix..."
|
|
log.info(msg)
|
|
print(msg)
|
|
|
|
# notify if this is a dry run
|
|
if dry_run:
|
|
print("This is a dry run: maximum windSpeed will be calculated but not saved.")
|
|
|
|
# construct a windSpeed recalculation config dict
|
|
wind_config_dict = {'name': 'Maximum windSpeed fix',
|
|
'binding': db_binding,
|
|
'trans_days': 100,
|
|
'dry_run': dry_run}
|
|
|
|
# create a windSpeedRecalculation object
|
|
wind_obj = weecfg.database.WindSpeedRecalculation(config_dict,
|
|
wind_config_dict)
|
|
# perform the recalculation, wrap in a try..except to catch any db errors
|
|
t1 = time.time()
|
|
|
|
try:
|
|
wind_obj.run()
|
|
except weedb.NoTableError:
|
|
msg = "Maximum windSpeed fix applied: no windSpeed found"
|
|
log.info(msg)
|
|
print(msg)
|
|
else:
|
|
msg = "Maximum windSpeed fix completed in %0.2f seconds" % (time.time() - t1)
|
|
log.info(msg)
|
|
print(msg)
|
|
|
|
|
|
def reweight_daily(config_path,
|
|
date=None,
|
|
from_date=None,
|
|
to_date=None,
|
|
db_binding='wx_binding',
|
|
dry_run=False):
|
|
"""Recalculate the weighted sums in the daily summaries."""
|
|
|
|
config_path, config_dict, database_name = weectllib.prepare(config_path, db_binding, dry_run)
|
|
weectllib.initialize(config_dict)
|
|
|
|
# Determine the period over which we are rebuilding from any command line date parameters
|
|
from_d, to_d = weectllib.parse_dates(date,
|
|
from_date=from_date, to_date=to_date)
|
|
|
|
# advise the user/log what we will do
|
|
if from_d is None and to_d is None:
|
|
msg = "The weighted sums in all the daily summaries will be recalculated."
|
|
elif from_d and not to_d:
|
|
msg = "The weighted sums in the daily summaries from %s through the end " \
|
|
"will be recalculated." % from_d
|
|
elif not from_d and to_d:
|
|
msg = "The weighted sums in the daily summaries from the beginning through %s" \
|
|
"will be recalculated." % to_d
|
|
elif from_d == to_d:
|
|
msg = "The weighted sums in the daily summary for %s will be recalculated." % from_d
|
|
else:
|
|
msg = "The weighted sums in the daily summaries from %s through %s, " \
|
|
"inclusive, will be recalculated." % (from_d, to_d)
|
|
|
|
log.info(msg)
|
|
print(msg)
|
|
ans = y_or_n("Proceed (y/n)? ")
|
|
if ans == 'n':
|
|
log.info("Nothing done.")
|
|
print("Nothing done.")
|
|
return
|
|
|
|
t1 = time.time()
|
|
|
|
# Open up the database.
|
|
with weewx.manager.open_manager_with_config(config_dict, db_binding) as dbmanager:
|
|
msg = f"Recalculating the weighted summaries in database '{database_name}' ..."
|
|
log.info(msg)
|
|
print(msg)
|
|
if not dry_run:
|
|
# Do the actual recalculations
|
|
dbmanager.recalculate_weights(start_d=from_d, stop_d=to_d)
|
|
|
|
msg = "Finished reweighting in %.1f seconds." % (time.time() - t1)
|
|
log.info(msg)
|
|
print()
|
|
print(msg)
|
|
if dry_run:
|
|
print("This was a dry run. Nothing was actually done.")
|
|
|