#!/usr/bin/env python # python imports import sys import logging, logging.config # local imports import util from response import Response from query import Query from asker import Asker, parse_email # file config can be called in many places without a problem logging.config.fileConfig('logging.conf') logger = logging.getLogger("request_handler: ") ################################## ## start user specific variables # ################################## database_settings = '/var/sites/hm_www' # should cellphone_staging use production (hm_www) or staging database? ################################ ## end user specific variables # ################################ # set up Django environment sys.path.append(database_settings) DJANGO_SETTINGS_MODULE=database_settings from django.core.management import setup_environ import settings setup_environ(settings) from django.db.models import * # set up ThoughtAndMemory application environment from db.models import * from db.graph import * class ObjectDoesNotExist(Exception): pass def handle_request(query): """ @type query: L{Query} @param query: @rtype: L{Response} @returns: Response """ response = Response(query.request_id) response.node = get_node_from_prod_id(query.request_id, query.product_id) if response.node != None: """ Node is in TAM database """ response.found_node_in_tam = True else: """ Node is not in TAM database, try to look it up through external connectors """ try: import upcdatabase from upcdatabase import EANException #TODO: this is a broad assumption, validate the prod_id is not #None before continuing. Worst case the try/except will catch #it but still... response.upcdb_dict = upcdatabase.lookupProduct(query.product_id) if response.upcdb_dict != None and response.upcdb_dict['found'] is True: response.found_product_in_upcdb = True except EANException, e: # EANException: Please provide an EAN (13 digits) or UPCE (8 digits) #critical_problem(query.request_id, myname, 'UPCdb_lookup_error_EANException', lines_in) pass except Exception, e: #critical_problem(query.request_id, myname, 'UPCdb_lookup_error_Exception '+str(e), lines_in) pass return response def critical_problem(request_id, recipient, show_stopper, lines_in): """ @type request_id: String?! @@@@ @param request_id: @type recipient: @param recipient: @type show_stopper: @param show_stopper: @type lines_in: @param lines_in: """ logger.critical('%i %s' % (request_id, str(show_stopper))) logger.debug('%i %s' % (request_id, show_stopper)) # concatenate items in lines_in into one string and pre- and post-pend terminal strings msg = '####initiateemail####\n' + ''.join(lines_in) + "####terminateemail####" logger.debug('%i %s' % (request_id, msg)) util.sendmail('lucy@bilumi.org', 'critical.problem.'+str(recipient)+'@bilumi.org', 'sms critical error for '+str(request_id), msg) def validate_asker(asker, lines_in): """ @type asker: L{Asker} @param asker: @type lines_in: List of strings @param lines_in: @rtype: boolean @returns: True if can proceed with request handling, False if critical problem throws: TODO: throw exceptions for each kind of problem? """ logger.info('%i sender= %s isphone= %s domain= %s product_id= %s recipient= %s recipient_domain= %s' % (asker.query.request_id, str(asker.sender), False, str(asker.domain), str(asker.query.product_id), str(asker.recipient), str(asker.recipient_domain))) if asker.sender is None: critical_problem(asker.query.request_id, asker.recipient, 'no_sender', lines_in) return False elif asker.domain is None: critical_problem(asker.query.request_id, asker.recipient, 'no_domain', lines_in) return False elif asker.recipient is None: critical_problem(asker.query.request_id, asker.recipient, 'no_recipient', lines_in) return False elif asker.recipient_domain is None: critical_problem(asker.query.request_id, asker.recipient, 'no_recipient_domain', lines_in) return False elif asker.query.product_id is None: critical_problem(asker.query.request_id, asker.recipient, 'no_product_id', lines_in) # Log error, but proceed anyway so that user receives response # email (TODO: could send mail from here) return True elif asker.domain==asker.recipient_domain and asker.sender==asker.recipient: """ If the request came from this user (it shouldn't) override the response address to notify lucy instead. This prevents a malformed email from crashing the service due to an infinite email loop. """ critical_problem(asker.query.request_id, asker.recipient, 'recursive email', lines_in) return False else: return True def get_node_from_prod_id(request_id, product_id): """ The TAM db associates 'nodes' with a set of 'identifiers' Nodes include products and companies Identifiers include UPCs/EANs and ticker symbols, to name a few. Identifiers are not necessarily unique. @type request_id: String?! @@@@ @param request_id: @type product_id: @param product_id: @rtype: L{Node} @returns: the first matching node TODO: return list? """ # convert to int else pid = product_id#int(product_id) try: upc = UPC.objects.get(upc_code=pid) if upc != None: return upc.node else: return None # except ObjectDoesNotExist, e: # logger.warn('%i get_node_from_prod_id_failed_ObjectDoesNotExist' % (request_id)) # return None # except DoesNotExist, e: # logger.warn('%i get_node_from_prod_id_failed_DoesNotExist' % (request_id)) # return None except Exception, e: logger.error('%i get_node_from_prod_id_failed_Exception' % (request_id)) #critical_problem(request_id, recipient, 'get_node_from_prod_id_failed_Exception '+str(e), lines_in) return None if __name__ == '__main__': exitcode = int(0) try: # incoming email message piped through procmail # FIXME: implement a timeout here to prevent hanging forever # i.e. permission error prevent procmail from reading a file which # cause it to never pipe anything, thus the script hangs here due # to a misconfiguration elsewhere in the system # eg, http://nick.vargish.org/clues/python-tricks.html lines_in = sys.stdin.readlines() asker = parse_email(lines_in) if validate_asker(asker, lines_in): response = handle_request(asker.query) exitcode += asker.reply(response) else: exitcode = 99 except Exception, e: logger.exception(' main exception %s' % (str(e))) exitcode = 127 # finally: # requires python 2.5, but python 2.5 has issues with fastcgi and/or django ?! logging.shutdown() sys.exit(exitcode)