###############################################################################
# Copyright (c) 2012-2026 Trimble Inc.
# $Id: NoPiUT_Alert.py,v 1.7 2022/10/25 16:45:51 acartmel Exp $
###############################################################################
#
# NoPiUT_Alert.py
#
# This script is executed after the daily NoPi/NoPi display processing
# is complete. It provides a summary of the status for all of baselines
# processed. Checks whether NoPi and/or display failed for any
# specified baseline. The baselines can be specified as a separate
# config file or use a default config file. Looks for the
# diffs_summary.txt file for each baseline. Also checks for the
# NoPiDI_Top.html file. These two file confirm if NoPi and NoPi
# Display were processed completely. Either or both missing files must
# be flagged. Also reads the NoPi_status.txt file to work out the # of
# warnings in the file. 
#
###############################################################################

import sys
import os
from datetime import date
from time import strptime, mktime

from NoPiUT_Common_Meas import *
from NoPiUT_Load_Summary import *

status_file_name  = 'NoPi_status.txt'
summary_file_name = 'diffs_summary.txt'
display_webpage   = 'NoPiDI_Top.html'
nopi_zip_file     = 'nopi.zip'
data_zip_file     = 'nopi-data.zip'
data_path         = '/mnt/data_drive/'

# Local copy of cl_options class defined in NoPi_Utils/
user_opts         = cl_options()

###############################################################################
# Process data for specified date and baselines. The baselines are
# parsed from either a user specified file or from NoPiUT_Baselines.cfg
###############################################################################

# Current Date in yyyymmdd format is a required argument

argc = len( sys.argv )

if ( argc < 2 ) :
  print("Usage: python NoPi_Alerts.py <date>")
  print("  where: <date> must be specified in yyymmdd format")
  sys.exit()

if ( argc == 2 ) :
  # Ensure date is in yyyymmdd format
  if ( len( str( sys.argv[1] ) ) == 8 ) :
    cur_date = sys.argv[1]
    cur_year = cur_date[0:4]
  else :
    print("Invalid Date. Must be in yyyymmdd format")
    sys.exit()

legacy_baselines = True
if int(cur_date) >= 20171031 :
  legacy_baselines = False

# Parse default/user specified baseline file
[baseline_name, baseline_dir, norm_vals_file, start_dates, end_dates] \
  = parse_baselines_file( user_opts, legacy_baselines=legacy_baselines )

# Generate alert messages in both plain text and html formats

# Write html format message to a temporary file for each of debugging

fp = open('tmp', 'wt')

fp.write( '<html><h2>Processing Status for %s</h2><br><br><body>\n'
          % str( cur_date ) )
fp.write( '<table border="1" rules="all">\n' )
fp.write( '<tr><td align="center">%s</td>\n' % "Baseline" )
fp.write( '<td align="center">&nbsp; %s &nbsp;</td>\n' % "#Epochs" )
fp.write( '<td align="center">&nbsp; %s &nbsp;</td>\n</tr>\n' % "# Warning" )

alert_message     = 'Processing Status for %s\n' % cur_date
alert_message     += '%s %15s %s %21s' % ("Baseline",
                                          " ",
                                          "# Epochs",
                                          "# Warnings( > 1000)" )

# Generate statistics for NoPi results for each baseline over the
# period: start_date to end_date
for baseline, dir_name, start_date, end_date \
    in zip(baseline_name, baseline_dir, start_dates, end_dates) :
  process = True
  if (start_date != 'None') and (int(cur_date) < int(start_date)):
    process = False
  if (end_date != 'None') and (int(cur_date) > int(end_date)):
    process = False
  if not process:
    continue

  # Convert cur_date in yyyymmdd format string to a datetime object
  time_struct = strptime(cur_date,"%Y%m%d")
  dt = date.fromtimestamp(mktime(time_struct))

  [cur_date, cur_day_path] = get_cur_day_path( dt, data_path, baseline,
                                               dir_name, disp_date = False )
  cur_day_nopi_zip         = cur_day_path + nopi_zip_file
  cur_day_data_zip         = cur_day_path + data_zip_file

  fp.write( '<tr><td align="left">%s</td>\n' % baseline )
  alert_message += ( "\n%s" % baseline )
  #print ( "\n%s" % baseline )
  #alert_message += "\n" + "-"*( len(baseline) + 17 )

  if ( not( os.access( cur_day_nopi_zip, os.R_OK ) ) ) :
    fp.write( '<td align="center" colspan=2><b>%s</b></td>\n'
              % "NoPi Output Zip missing" )
    fp.write( '</tr>\n' )
    alert_message += "%32s" % "NoPi Output Zip missing"
    continue

  # Extract the required data files from the zip
  os.system('unzip -p '+cur_day_data_zip+' '+status_file_name+' > '+status_file_name)
  os.system('unzip -p '+cur_day_nopi_zip+' '+summary_file_name+' > '+summary_file_name)
  os.system('unzip -p '+cur_day_nopi_zip+' '+display_webpage+' > '+display_webpage)

  # If NoPi Failed, NoPi Display will also fail...
  if ( not(os.access(summary_file_name, os.R_OK))
       or os.path.getsize(summary_file_name) == 0 ) :
    fp.write( '<td align="center" colspan=2><b>%s</b></td>\n'
              % "NoPi Processing Failed" )
    fp.write( '</tr>\n' )
    alert_message += "%32s" % "NoPi Processing Failed"
    continue
  elif ( not(os.access(display_webpage, os.R_OK))
         or os.path.getsize(display_webpage) == 0 ) :
    fp.write( '<td align="center" colspan=2><b>%s</b></td>\n'
              % "NoPi Display Failed" )
    fp.write( '</tr>\n' )
    alert_message += "%32s" % "NoPi Display Failed"
    continue

  diffs_summ = cl_load_diffs_summ( summary_file_name )
  delta_time = float( diffs_summ.etime_ms ) - float( diffs_summ.stime_ms )
  delta_time = delta_time/1000.0
  fp.write( '<td align="center">%d</td>\n' % delta_time )
  alert_message += "%s %d" % (" "*(25 - len(baseline)), delta_time )

  # Count number of NoPi warnings.
  if ( os.path.getsize(status_file_name) >= 0 ) :
    warnings = 0
    for line in open(status_file_name) :
      warnings += 1
    fp.write( '<td align="center">%d</td>\n' % warnings )
    alert_message += "%16d" % warnings
  fp.write( '</tr>\n' )

fp.write( '</table></body></html>\n' )
fp.close()

# Open error log and write alert_message apart from sending an email alert
error_log = data_path + cur_year + '/' + 'NoPi_log.txt'
f = open(error_log, "a")
f.write('\n==================================================================')
f.write('\nDate:%s' % cur_date)
f.write(alert_message)
f.close()

# Send email alert if alert_message has valid alerts
if ( len( alert_message ) > 0 ) :
  import smtplib

  from email.mime.multipart import MIMEMultipart
  from email.mime.text import MIMEText

  sender = 'do_not_reply@trimble.com'
  receivers = ['andrew_cartmell@trimble.com',
               'stuart_riley@trimble.com']

  # Create message container - the correct MIME type is multipart/alternative.
  msg = MIMEMultipart('alternative')
  msg['Subject'] = "Daily Processing Alert"
  msg['From'] = sender
  msg['To']   = 'Undisclosed'

  # Record the MIME types of both parts - text/plain and text/html.
  plainMsg = MIMEText(alert_message, 'text')
  # Read in html format message from the temporary file it was written to
  fp = open('tmp', 'rt')
  htmlMsg  = MIMEText(fp.read(), 'html')

  # Attach parts into message container.
  # According to RFC 2046, the last part of a multipart message, in this case
  # the HTML message, is best and preferred.
  msg.attach(plainMsg)
  msg.attach(htmlMsg)

  # Now try sending the email alert
  try :
    smtpObj = smtplib.SMTP( 'mail.trimble.com', 25 )
    smtpObj.sendmail(sender,receivers,msg.as_string())
    print("Sent message successfully")
  except SMTPException :
    print("Error: Unable to send email")

# Clean up local directory
os.system('rm tmp')
os.system('rm '+status_file_name)
os.system('rm '+summary_file_name)
os.system('rm '+display_webpage)
