diff --git a/schainpy/VERSION b/schainpy/VERSION
index 994ffd0..0d7b7c6 100644
--- a/schainpy/VERSION
+++ b/schainpy/VERSION
@@ -23,4 +23,7 @@ VERSIONS:
-controller_api.py: Safe access to ControllerThead
2.1.3.3:
--Colored Button Icons were added to GUI
\ No newline at end of file
+-Colored Button Icons were added to GUI
+
+2.1.4:
+-Sending error notifications to signal chain administrator
\ No newline at end of file
diff --git a/schainpy/__init__.py b/schainpy/__init__.py
index 76b27b6..54bdb1d 100644
--- a/schainpy/__init__.py
+++ b/schainpy/__init__.py
@@ -4,4 +4,4 @@ Created on Feb 7, 2012
@author $Author$
@version $Id$
'''
-__version__ = "2.1.3.3"
\ No newline at end of file
+__version__ = "2.1.4"
\ No newline at end of file
diff --git a/schainpy/admin.py b/schainpy/admin.py
new file mode 100644
index 0000000..f6eb8bc
--- /dev/null
+++ b/schainpy/admin.py
@@ -0,0 +1,346 @@
+"""The admin module contains all administrative classes relating to the schain python api.
+
+The main role of this module is to send some reports. It contains a
+notification class and a standard error handing class.
+
+$Id: admin.py 3966 2015-12-01 14:32:29Z miguel.urco $
+"""
+import os
+import smtplib
+import ConfigParser
+import StringIO
+
+from email.mime.text import MIMEText
+from email.mime.application import MIMEApplication
+from email.mime.multipart import MIMEMultipart
+
+class SchainConfigure():
+
+ __DEFAULT_SENDER_EMAIL = "notifier-schain@jro.igp.gob.pe"
+ __DEFAULT_ADMINISTRATOR_EMAIL = "miguel.urco@jro.igp.gob.pe"
+ __DEFAULT_EMAIL_SERVER = "jro-zimbra.igp.gob.pe"
+
+ __SCHAIN_ADMINISTRATOR_EMAIL = "CONTACT"
+ __SCHAIN_EMAIL_SERVER = "MAILSERVER"
+ __SCHAIN_SENDER_EMAIL = "MAILSERVER_ACCOUNT"
+
+ def __init__(self, initFile = None):
+
+ # Set configuration file
+ if (initFile == None):
+ self.__confFilePath = "/etc/schain.conf"
+ else:
+ self.__confFilePath = initFile
+
+ # open configuration file
+ try:
+ self.__confFile = open(self.__confFilePath, "r")
+ except IOError:
+ # can't read from file - use all hard-coded values
+ self.__initFromHardCode()
+ return
+
+ # create Parser using standard module ConfigParser
+ self.__parser = ConfigParser.ConfigParser()
+
+ # read conf file into a StringIO with "[madrigal]\n" section heading prepended
+ strConfFile = StringIO.StringIO("[schain]\n" + self.__confFile.read())
+
+ # parse StringIO configuration file
+ self.__parser.readfp(strConfFile)
+
+ # read information from configuration file
+ self.__readConfFile()
+
+ # close conf file
+ self.__confFile.close()
+
+
+ def __initFromHardCode(self):
+
+ self.__sender_email = self.__DEFAULT_SENDER_EMAIL
+ self.__admin_email = self.__DEFAULT_ADMINISTRATOR_EMAIL
+ self.__email_server = self.__DEFAULT_EMAIL_SERVER
+
+ def __readConfFile(self):
+ """__readConfFile is a private helper function that reads information from the parsed config file.
+
+ Inputs: None
+
+ Returns: Void.
+
+ Affects: Initializes class member variables that are found in the config file.
+
+ Exceptions: MadrigalError thrown if any key not found.
+ """
+
+ # get the sender email
+ try:
+ self.__sender_email = self.__parser.get("schain", self.__SCHAIN_SENDER_EMAIL)
+ except:
+ self.__sender_email = self.__DEFAULT_SENDER_EMAIL
+
+ # get the administrator email
+ try:
+ self.__admin_email = self.__parser.get("schain", self.__SCHAIN_ADMINISTRATOR_EMAIL)
+ except:
+ self.__admin_email = self.__DEFAULT_ADMINISTRATOR_EMAIL
+
+ # get the server email
+ try:
+ self.__email_server = self.__parser.get("schain", self.__SCHAIN_EMAIL_SERVER)
+ except:
+ self.__email_server = self.__DEFAULT_EMAIL_SERVER
+
+ def getEmailServer(self):
+
+ return self.__email_server
+
+ def getSenderEmail(self):
+
+ return self.__sender_email
+
+ def getAdminEmail(self):
+
+ return self.__admin_email
+
+class SchainNotify:
+ """SchainNotify is an object used to send messages to an administrator about a Schain software.
+
+ This object provides functions needed to send messages to an administrator about a Schain , for now
+ only sendAlert, which sends an email to the site administrator found is ADMIN_EMAIL
+
+ Usage example:
+
+ import schainpy.admin
+
+ try:
+
+ adminObj = schainpy.admin.SchainNotify()
+ adminObj.sendAlert('This is important!', 'Important Message')
+
+ except schainpy.admin.SchainError, e:
+
+ print e.getExceptionStr()
+
+
+ Non-standard Python modules used:
+ None
+
+ Exceptions thrown: None - Note that SchainNotify tries every trick it knows to avoid
+ throwing exceptions, since this is the class that will generally be called when there is a problem.
+
+ Change history:
+
+ Written by "Miguel Urco":mailto:miguel.urco@jro.igp.gob.pe Dec. 1, 2015
+ """
+
+ #constants
+
+ def __init__(self):
+ """__init__ initializes SchainNotify by getting some basic information from SchainDB and SchainSite.
+
+ Note that SchainNotify tries every trick it knows to avoid throwing exceptions, since
+ this is the class that will generally be called when there is a problem.
+
+ Inputs: Existing SchainDB object, by default = None.
+
+ Returns: void
+
+ Affects: Initializes self.__binDir.
+
+ Exceptions: None.
+ """
+
+ # note that the main configuration file is unavailable
+ # the best that can be done is send an email to root using localhost mailserver
+ confObj = SchainConfigure()
+
+ self.__emailFromAddress = confObj.getSenderEmail()
+ self.__emailToAddress = confObj.getAdminEmail()
+ self.__emailServer = confObj.getEmailServer()
+
+ def sendEmail(self, email_from, email_to, subject='Error running ...', message="", subtitle="", filename="", html_format=True):
+
+ msg = MIMEMultipart()
+ msg['Subject'] = subject
+ msg['From'] = "(Python SChain API): " + email_from
+ msg['Reply-to'] = email_from
+ msg['To'] = email_to
+
+ # That is what u see if dont have an email reader:
+ msg.preamble = 'SChainPy'
+
+ if html_format:
+ message = "
%s
" %subject + "" + subtitle.replace("\n", "
\n") + "
" + message.replace("\n", "
\n")
+ message = "\n" + message + ''
+
+ # This is the textual part:
+ part = MIMEText(message, "html")
+ else:
+ message = subject + "\n" + subtitle + "\n" + message
+ part = MIMEText(message)
+
+ msg.attach(part)
+
+ if os.path.isfile(filename):
+ # This is the binary part(The Attachment):
+ part = MIMEApplication(open(filename,"rb").read())
+ part.add_header('Content-Disposition',
+ 'attachment',
+ filename=os.path.basename(filename))
+ msg.attach(part)
+
+ # Create an instance in SMTP server
+ smtp = smtplib.SMTP(self.__emailServer)
+ # Start the server:
+# smtp.ehlo()
+# smtp.login(email_from, email_from_pass)
+
+ # Send the email
+ smtp.sendmail(msg['From'], msg['To'], msg.as_string())
+ smtp.quit()
+
+
+ def sendAlert(self, message, subject = "", subtitle="", filename=""):
+ """sendAlert sends an email with the given message and optional title.
+
+ Inputs: message (string), and optional title (string)
+
+ Returns: void
+
+ Affects: none
+
+ Exceptions: None.
+ """
+ print "***** Sending alert to %s *****" %self.__emailToAddress
+ # set up message
+
+ self.sendEmail(email_from=self.__emailFromAddress,
+ email_to=self.__emailToAddress,
+ subject=subject,
+ message=message,
+ subtitle=subtitle,
+ filename=filename)
+
+ print "***** Your system administrator has been notified *****"
+
+
+ def notify(self, email, message, subject = "", subtitle="", filename=""):
+ """notify sends an email with the given message and title to email.
+
+ Inputs: email (string), message (string), and subject (string)
+
+ Returns: void
+
+ Affects: none
+
+ Exceptions: None.
+ """
+
+ print "Notifying to %s ..." %email
+
+ self.sendEmail(email_from=self.__emailFromAddress,
+ email_to=email,
+ subject=subject,
+ message=message,
+ subtitle=subtitle,
+ filename=filename)
+
+ print "***** Your system administrator has been notified *****"
+
+class SchainError:
+ """SchainError is an exception class that is thrown for all known errors in using Schain Py lib.
+
+ Usage example:
+
+ import sys, traceback
+ import schainpy.admin
+
+ try:
+
+ test = open('ImportantFile.txt', 'r')
+
+ except:
+
+ raise schainpy.admin.SchainError('ImportantFile.txt not opened!',
+ traceback.format_exception(sys.exc_info()[0],
+ sys.exc_info()[1],
+ sys.exc_info()[2]))
+ """
+
+
+ def __init__(self, strInterpretation, exceptionList):
+ """ __init__ gathers the interpretation string along with all information from sys.exc_info().
+
+ Inputs: strIntepretation - A string representing the programmer's interpretation of
+ why the exception occurred
+
+ exceptionList - a list of strings completely describing the exception.
+ Generated by traceback.format_exception(sys.exc_info()[0],
+ sys.exc_info()[1],
+ sys.exc_info()[2])
+
+ Returns: Void.
+
+ Affects: Initializes class member variables _strInterp, _strExcList.
+
+ Exceptions: None.
+ """
+
+ self._strInterp = strInterpretation
+ self._strExcList = exceptionList
+
+
+ def getExceptionStr(self):
+ """ getExceptionStr returns a formatted string ready for printing completely describing the exception.
+
+ Inputs: None
+
+ Returns: A formatted string ready for printing completely describing the exception.
+
+ Affects: None
+
+ Exceptions: None.
+ """
+ excStr = 'The following Schain Python exception has occurred:\n'
+ excStr = excStr + self._strInterp + '\n\n'
+
+ if self._strExcList != None:
+ for item in self._strExcList:
+ excStr = excStr + str(item) + '\n'
+
+ return excStr
+
+ def __str__(self):
+ return(self.getExceptionStr())
+
+
+ def getExceptionHtml(self):
+ """ getExceptionHtml returns an Html formatted string completely describing the exception.
+
+ Inputs: None
+
+ Returns: A formatted string ready for printing completely describing the exception.
+
+ Affects: None
+
+ Exceptions: None.
+ """
+
+ excStr = '
The following Schain Python exception has occurred:\n
'
+ excStr = excStr + self._strInterp + '\n
\n'
+
+ if self._strExcList != None:
+ for item in self._strExcList:
+ excStr = excStr + str(item) + '\n
'
+
+ return excStr
+
+if __name__ == '__main__':
+
+ test = SchainNotify()
+
+ test.sendAlert('This is a message from the python module SchainNotify', 'Test from SchainNotify')
+
+ print 'Hopefully message sent - check.'
diff --git a/schainpy/controller.py b/schainpy/controller.py
index e3d0f60..cc9b5dd 100644
--- a/schainpy/controller.py
+++ b/schainpy/controller.py
@@ -2,18 +2,18 @@
Created on September , 2012
@author:
'''
-from xml.etree.ElementTree import ElementTree, Element, SubElement, tostring
-from xml.dom import minidom
-
-from model import *
-from time import sleep
import sys
import ast
import traceback
+import schainpy
+import schainpy.admin
-SCHAIN_MAIL = "miguel.urco@jro.igp.gob.pe"
-EMAIL_SERVER = "jro.igp.gob.pe"
+from xml.etree.ElementTree import ElementTree, Element, SubElement, tostring
+from xml.dom import minidom
+
+from schainpy.model import *
+from time import sleep
def prettify(elem):
"""Return a pretty-printed XML string for the Element.
@@ -325,9 +325,9 @@ class OperationConf():
return parmConfObj
- def makeXml(self, upElement):
+ def makeXml(self, procUnitElement):
- opElement = SubElement(upElement, self.ELEMENTNAME)
+ opElement = SubElement(procUnitElement, self.ELEMENTNAME)
opElement.set('id', str(self.id))
opElement.set('name', self.name)
opElement.set('type', self.type)
@@ -539,16 +539,16 @@ class ProcUnitConf():
return opConfObj
- def makeXml(self, procUnitElement):
+ def makeXml(self, projectElement):
- upElement = SubElement(procUnitElement, self.ELEMENTNAME)
- upElement.set('id', str(self.id))
- upElement.set('name', self.name)
- upElement.set('datatype', self.datatype)
- upElement.set('inputId', str(self.inputId))
+ procUnitElement = SubElement(projectElement, self.ELEMENTNAME)
+ procUnitElement.set('id', str(self.id))
+ procUnitElement.set('name', self.name)
+ procUnitElement.set('datatype', self.datatype)
+ procUnitElement.set('inputId', str(self.inputId))
for opConfObj in self.opConfObjList:
- opConfObj.makeXml(upElement)
+ opConfObj.makeXml(procUnitElement)
def readXml(self, upElement):
@@ -757,12 +757,53 @@ class ReadUnitConf(ProcUnitConf):
return opObj
+# def makeXml(self, projectElement):
+#
+# procUnitElement = SubElement(projectElement, self.ELEMENTNAME)
+# procUnitElement.set('id', str(self.id))
+# procUnitElement.set('name', self.name)
+# procUnitElement.set('datatype', self.datatype)
+# procUnitElement.set('inputId', str(self.inputId))
+#
+# for opConfObj in self.opConfObjList:
+# opConfObj.makeXml(procUnitElement)
+
+ def readXml(self, upElement):
+
+ self.id = upElement.get('id')
+ self.name = upElement.get('name')
+ self.datatype = upElement.get('datatype')
+ self.inputId = upElement.get('inputId')
+
+ if self.ELEMENTNAME == "ReadUnit":
+ self.datatype = self.datatype.replace("Reader", "")
+
+ if self.inputId == 'None':
+ self.inputId = '0'
+
+ self.opConfObjList = []
+
+ opElementList = upElement.getiterator(OperationConf().getElementName())
+
+ for opElement in opElementList:
+ opConfObj = OperationConf()
+ opConfObj.readXml(opElement)
+ self.opConfObjList.append(opConfObj)
+
+ if opConfObj.name == 'run':
+ self.path = opConfObj.getParameterValue('path')
+ self.startDate = opConfObj.getParameterValue('startDate')
+ self.endDate = opConfObj.getParameterValue('endDate')
+ self.startTime = opConfObj.getParameterValue('startTime')
+ self.endTime = opConfObj.getParameterValue('endTime')
+
class Project():
id = None
name = None
description = None
-# readUnitConfObjList = None
+ filename = None
+
procUnitConfObjDict = None
ELEMENTNAME = 'Project'
@@ -898,14 +939,26 @@ class Project():
def writeXml(self, filename):
- self.makeXml()
+ if not os.access(os.path.dirname(filename), os.W_OK):
+ return 0
- #print prettify(self.projectElement)
+ if os.path.isfile(filename) and not(os.access(filename, os.W_OK)):
+ return 0
+
+ self.makeXml()
ElementTree(self.projectElement).write(filename, method='xml')
+
+ self.filename = filename
+
+ return 1
def readXml(self, filename):
+ if not os.path.isfile(filename):
+ print "%s does not exist" %filename
+ return 0
+
self.projectElement = None
self.procUnitConfObjDict = {}
@@ -938,7 +991,9 @@ class Project():
procUnitConfObj.parentId = self.id
self.procUnitConfObjDict[procUnitConfObj.getId()] = procUnitConfObj
-
+
+ return 1
+
def printattr(self):
print "Project[%s]: name = %s, description = %s" %(self.id,
@@ -1010,7 +1065,7 @@ class Project():
print
print "*"*60
- print " Starting SIGNAL CHAIN PROCESSING "
+ print " Starting SIGNAL CHAIN PROCESSING v%s " %schainpy.__version__
print "*"*60
print
@@ -1026,28 +1081,46 @@ class Project():
procUnitConfObj = self.procUnitConfObjDict[procKey]
- message = ""
try:
sts = procUnitConfObj.run()
is_ok = is_ok or sts
except:
- print "***** Error running %s *****" %procUnitConfObj.name
- sleep(1)
+ print "***** Error occurred in %s *****" %(procUnitConfObj.name)
+
+ sleep(0.5)
err = traceback.format_exception(sys.exc_info()[0],
sys.exc_info()[1],
sys.exc_info()[2])
- for thisLine in err:
- message += thisLine
+ import socket
+
+ subject = "SChain v%s: Error running %s\n" %(schainpy.__version__, procUnitConfObj.name)
+
+ subtitle = "%s: %s\n" %(procUnitConfObj.getElementName() ,procUnitConfObj.name)
+ subtitle += "Hostname: %s\n" %socket.gethostbyname(socket.gethostname())
+ subtitle += "Working directory: %s\n" %os.path.abspath("./")
+ subtitle += "Configuration file: %s\n" %self.filename
+
+ readUnitConfObj = self.getReadUnitObj()
+ if readUnitConfObj:
+ subtitle += "Data path: %s\n" %readUnitConfObj.path
+ subtitle += "Data type: %s\n" %readUnitConfObj.datatype
+ subtitle += "Start date: %s\n" %readUnitConfObj.startDate
+ subtitle += "End date: %s\n" %readUnitConfObj.endDate
+ subtitle += "Start time: %s\n" %readUnitConfObj.startTime
+ subtitle += "End time: %s\n" %readUnitConfObj.endTime
+
+ message = "".join(err)
sys.stderr.write(message)
-# print "*"*60
-# print message
-# print "*"*60
+
+ adminObj = schainpy.admin.SchainNotify()
+ adminObj.sendAlert(message=message,
+ subject=subject,
+ subtitle=subtitle,
+ filename=self.filename)
-# self.sendReport(message)
- sleep(0.1)
is_ok = False
break
@@ -1075,23 +1148,6 @@ class Project():
self.createObjects()
self.connectObjects()
self.run()
-
- def sendReport(self, message, subject="Error occurred in Signal Chain", email=SCHAIN_MAIL):
-
- import smtplib
-
- print subject
- print "Sending report to %s ..." %email
-
- message = 'From: (Python Signal Chain API) ' + email + '\n' + \
- 'To: ' + email + '\n' + \
- 'Subject: ' + str(subject) + '\n' + \
- 'Content-type: text/html\n\n' + message
-
- server = smtplib.SMTP(EMAIL_SERVER)
- server.sendmail(email.split(',')[0],
- email.split(','), message)
- server.quit()
if __name__ == '__main__':
diff --git a/schainpy/controller_api.py b/schainpy/controller_api.py
index 39c871b..d44d9e6 100644
--- a/schainpy/controller_api.py
+++ b/schainpy/controller_api.py
@@ -1,8 +1,5 @@
import threading
-from PyQt4 import QtCore
-from PyQt4.QtCore import SIGNAL
-
from schainpy.controller import Project
class ControllerThread(threading.Thread, Project):
@@ -74,68 +71,71 @@ class ControllerThread(threading.Thread, Project):
def isFinished(self):
return not self.is_alive()
-
-class ControllerQThread(QtCore.QThread, Project):
-
- def __init__(self, filename):
-
- QtCore.QThread.__init__(self)
- Project.__init__(self)
-
- self.filename = filename
-
- self.lock = threading.Lock()
- self.control = {'stop':False, 'pause':False}
-
- def __del__(self):
-
- self.control['stop'] = True
- self.wait()
-
- def stop(self):
-
- self.lock.acquire()
-
- self.control['stop'] = True
-
- self.lock.release()
-
- def pause(self):
-
- self.lock.acquire()
-
- self.control['pause'] = not(self.control['pause'])
- paused = self.control['pause']
-
- self.lock.release()
-
- return paused
-
- def isPaused(self):
-
- self.lock.acquire()
- paused = self.control['pause']
- self.lock.release()
-
- return paused
-
- def isStopped(self):
-
- self.lock.acquire()
- stopped = self.control['stop']
- self.lock.release()
-
- return stopped
-
- def run(self):
-
- self.control['stop'] = False
- self.control['pause'] = False
-
- self.readXml(self.filename)
- self.createObjects()
- self.connectObjects()
- self.emit( SIGNAL( "jobStarted( PyQt_PyObject )" ), 1)
- Project.run(self)
- self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), 1)
-
\ No newline at end of file
+
+# from PyQt4 import QtCore
+# from PyQt4.QtCore import SIGNAL
+#
+# class ControllerQThread(QtCore.QThread, Project):
+#
+# def __init__(self, filename):
+#
+# QtCore.QThread.__init__(self)
+# Project.__init__(self)
+#
+# self.filename = filename
+#
+# self.lock = threading.Lock()
+# self.control = {'stop':False, 'pause':False}
+#
+# def __del__(self):
+#
+# self.control['stop'] = True
+# self.wait()
+#
+# def stop(self):
+#
+# self.lock.acquire()
+#
+# self.control['stop'] = True
+#
+# self.lock.release()
+#
+# def pause(self):
+#
+# self.lock.acquire()
+#
+# self.control['pause'] = not(self.control['pause'])
+# paused = self.control['pause']
+#
+# self.lock.release()
+#
+# return paused
+#
+# def isPaused(self):
+#
+# self.lock.acquire()
+# paused = self.control['pause']
+# self.lock.release()
+#
+# return paused
+#
+# def isStopped(self):
+#
+# self.lock.acquire()
+# stopped = self.control['stop']
+# self.lock.release()
+#
+# return stopped
+#
+# def run(self):
+#
+# self.control['stop'] = False
+# self.control['pause'] = False
+#
+# self.readXml(self.filename)
+# self.createObjects()
+# self.connectObjects()
+# self.emit( SIGNAL( "jobStarted( PyQt_PyObject )" ), 1)
+# Project.run(self)
+# self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), 1)
+#
\ No newline at end of file
diff --git a/schainpy/schain.conf.template b/schainpy/schain.conf.template
new file mode 100644
index 0000000..d9fced0
--- /dev/null
+++ b/schainpy/schain.conf.template
@@ -0,0 +1,5 @@
+#Copy this file to /etc/schain.conf
+[schain]
+CONTACT = miguel.urco@jro.igp.gob.pe
+MAILSERVER = jro-zimbra.igp.gob.pe
+MALSERVER_ACCOUNT = notifier-schain@jro.igp.gob.pe
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 1a70eb1..21fd8de 100644
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,9 @@ setup(name="schainpy",
'schainpy.gui.viewer.windows'},
py_modules=['schainpy.serializer.DataTranslate',
'schainpy.serializer.JROSerializer'],
- package_data={'schainpy.gui.figures': ['*.png','*.jpg']},
+ package_data={'schainpy': ['*.cfg'],
+ 'schainpy.gui.figures': ['*.png','*.jpg']
+ },
include_package_data=True,
scripts =['schainpy/gui/schainGUI'],
install_requires=["numpy >= 1.6.0",