def __init__(self, api_key: str, api_path: str, move_from: str, move_to: str, age: int):
     """
     Move Issues Init
     :param api_key: The api key for auth user
     :param api_path: Path to the api
     :param move_from: ID string of project to move tasks from
     :param move_to: ID string of project to move tasks from
     :param age: Find issues with an actual completion date greater than this value in months.
     """
     self.api_key = api_key
     self.api_path = api_path
     self.move_from = move_from
     self.move_to = move_to
     self.age = age
     # Can't bluk move more than 100 elements as per API restrictions
     self.max_results = 100
     self.completion_date_str = "$$TODAY-{age}m".format(age=self.age)
     self.client = StreamClient(self.api_path, self.api_key)
def example():
    client = StreamClient('http://localhost:8080/attask/api')
    print 'Logging in...'
    client.login('admin', 'user')
    print 'Done'

    print 'Retrieving user...'
    user = AtTaskObject(
        client.get(ObjCode.USER, client.user_id,
                   ['ID', 'homeGroupID', 'emailAddr']))
    print 'Done'
    print user

    print 'Searching projects...'
    results = client.search(ObjCode.PROJECT, {'groupID': user.homeGroupID})
    print 'Done'

    print '%s project(s) found' % len(results)
    for p in results:
        project = AtTaskObject(p)
        print ' - %s' % project.name

    print 'Creating project...'
    project = AtTaskObject(
        client.post(ObjCode.PROJECT, {
            'name': 'My Project',
            'groupID': user.homeGroupID
        }))
    print 'Done'
    print project

    print 'Retrieving project...'
    project = AtTaskObject(client.get(ObjCode.PROJECT, project.ID))
    print 'Done'
    print project

    print 'Editing project...'
    project = AtTaskObject(
        client.put(ObjCode.PROJECT, project.ID, {'ownerID': user.ID}), client)
    print 'Done'
    print project

    print 'Deleting project...'
    client.delete(ObjCode.PROJECT, project.ID)
    print 'Done'

    print 'Creating another project...'
    project = AtTaskObject({}, client)
    project.objCode = ObjCode.PROJECT
    project.name = 'My New Project'
    project.groupID = user.homeGroupID
    project.save()
    print 'Done'
    print project

    print 'Editing another project...'
    project.ownerID = user.ID
    project.save()
    print 'Done'
    print project

    print 'Deleting another project...'
    client.delete(ObjCode.PROJECT, project.ID)
    print 'Done'

    print 'Logging out...'
    client.logout()
    print 'Done'
Ejemplo n.º 3
0
                item['projID'] + ' | ' + item['projName'] + '\n')
            templistforemail.append(
                str(datetime.now().strftime("%Y-%m-%d %H:%M")) + ' | ' +
                item['projID'] + ' | ' + item['projName'] + '\n')
        # Sends email to each person in email list
        names = ['*****@*****.**', '*****@*****.**']
        for name in names:
            mailsend.sendmail(''.join(templistforemail), name)
    outFile.close()


    # Because it is awesome and we can
def wizardCall():
    with open('wizard.txt', 'r') as fin:
        print fin.read()
    fin.close()


################################################################################

# Main()
if __name__ == '__main__':
    wizardCall()
    username = '******'
    password = '******'  #an actual password has would go here
    client = StreamClient('https://somedomain.attask-ondemand.com/attask/api/')
    client.login(username, masking.unmask(password))
    getProject()
    updateProjectPCD()
    writeToFile(projIDlist)
Ejemplo n.º 4
0
CATEGORY_BACKLOG_ITEM = '4c78aaa7000c4bc23722d1bebdf9d77f'

class TShirtSize:
    SMALL = 5
    MEDIUM = 10
    LARGE = 15

#get the command line parameters and make sense of them
parser = argparse.ArgumentParser(description='Issue To Task')
parser.add_argument('issueID', action='store', type=str, help="This is the issue GUID on dot8")
parser.add_argument('-b', action='store_true', default=False, dest='isBlocking', help='Whether to set it as a blocking task')
parser.add_argument('-p', nargs="?", dest='points', default=0, type=int, required=False, help='How many points the task is worth')

args = parser.parse_args() #we will refer to this object later to key off the properties set on it

connection = StreamClient(API_URL)

try:
    connection.login(USERNAME,PASSWORD)
except Exception, e:
    print "Error connecting to %s" % connection.url
    print e
    exit(0)

def printProject(project):
    print "%s [Project]" % project['name']
    tasks = sorted(project['tasks'],key=lambda k: k['taskNumber'])
    for task in tasks:
        if task['parentID'] is None: #parent task
            print "\tTask %d: %s" % (task['taskNumber'],task['name'])
    line()
Ejemplo n.º 5
0
from Tkinter import *

#Global Variables
username = '******'
password = '******'
carrieridlist = 'carrieridlist'
periodid = '0'
taskname = 'taskname'
Builder = 'Builder'
projIDlist = []
taskIDlist = []
assignIDlist = []

#this will need to be updated with the actual subdomain to be valid
#also 'CoName' needs to be find and replaced with the company name
client = StreamClient(
    'https://entersubdomainhere.attask-ondemand.com/attask/api/')

#start TKinter build#
root = Tk()
root.resizable(width=False, height=False)

mainframe = Frame(root)
mainframe.pack()

photo = PhotoImage(file="broadsword.gif")
test = Label(mainframe, image=photo, bd=5)
test.image = photo
test.grid(column=0, row=0, columnspan=2)

unL = Label(mainframe, text='Batch edit WF assignments | Written by BE')
unL.grid(column=0, row=1, columnspan=2)
Ejemplo n.º 6
0
from api import StreamClient, ObjCode
from local_credentials import API_URL, USERNAME, PASSWORD

PROJECT_ID = "50047866000005b833d7a1da52e5f69c"

connection = StreamClient(API_URL)

try:
    connection.login(USERNAME,PASSWORD)
except Exception, e:
    print "Error connecting to %s" % connection.url
    print e
    exit(0)

def createTask(name):
    result = connection.post(ObjCode.TASK,{'name': name, 'projectID':PROJECT_ID})

project = connection.get(ObjCode.PROJECT,PROJECT_ID,['tasks:*']) #AtTaskObject(connection.get(ObjCode.PROJECT,PROJECT_ID,['tasks:*']), connection)

print "%s [Project]" % project['name']

for task in project['tasks']:
    print "\tTask %d: %s" % (task['taskNumber'],task['name'])


createTask("bobbo's Task!!")
class MoveIssues(object):
    def __init__(self, api_key: str, api_path: str, move_from: str, move_to: str, age: int):
        """
        Move Issues Init
        :param api_key: The api key for auth user
        :param api_path: Path to the api
        :param move_from: ID string of project to move tasks from
        :param move_to: ID string of project to move tasks from
        :param age: Find issues with an actual completion date greater than this value in months.
        """
        self.api_key = api_key
        self.api_path = api_path
        self.move_from = move_from
        self.move_to = move_to
        self.age = age
        # Can't bluk move more than 100 elements as per API restrictions
        self.max_results = 100
        self.completion_date_str = "$$TODAY-{age}m".format(age=self.age)
        self.client = StreamClient(self.api_path, self.api_key)

    def go(self):
        """
        Starts the moving process.
        """
        results_len = 1  # Just something to get us started
        while results_len > 0:
            issues = self.find_issues()
            results_len = len(issues)
            iss_move_count = self.move_issues(issues)
            if results_len > 0:
                results_str = "Moved {result_count} items".format(result_count=iss_move_count)
                print(results_str)

        print("Operation complete")

    def find_issues(self):
        """
        Finds issues in 'from' project to be moved.
        :return: a list of issues that meet the criteria
        """
        params = {
            "$$LIMIT": self.max_results,
            "projectID": self.move_from,
            "projectID_Mod": "in",
            "actualCompletionDate": self.completion_date_str,
            "actualCompletionDate_Mod": "lte"
        }
        fields = {}
        results = AtTaskObject(self.client.search(ObjCode.ISSUE, params, fields))
        return results.data

    def move_issues(self, data) -> int:
        """
        Moves issues between projects
        :param data: list of issues
        :return: count of issues moved
        """
        counter = 0
        for item in data:
            # Get the ID number
            uid = item['ID']
            params = {'projectID': self.move_to}
            AtTaskObject(self.client.action(ObjCode.ISSUE, 'move', params, objid=uid))
            print(".", end="", flush=True)
            counter += 1
        return counter

    def get_proj_name(self, proj_id: str):
        """
        Gets the name of a project from ID
        :param proj_id: ID number of the project to get the name of
        :return: Name of the project
        """
        results = AtTaskObject(self.client.get(ObjCode.PROJECT, proj_id))
        return results.data['name']

    def get_number_of_issues_to_move(self):
        """
        Get's a count of the number of issues to be moved
        :return: a count of the number of issues to be moved
        """
        params = {
            "projectID": self.move_from,
            "projectID_Mod": "in",
            "actualCompletionDate": self.completion_date_str,
            "actualCompletionDate_Mod": "lte"
        }
        results = AtTaskObject(self.client.report(ObjCode.ISSUE, params, 'sum'))
        return results.data['dcount_ID']
	def do_GET(self):

		def do_FAIL():
			self.send_response(200)
			self.send_header("Content-type", "text/html")	
			self.end_headers()
			self.wfile.write("<!DOCTYPE html>\n")
			self.wfile.write("<html>\n")
			self.wfile.write("<head>\n")
			self.wfile.write("\t<meta>\n")
			self.wfile.write("\t<title>Fail</title>\n")
			self.wfile.write("\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n")
			self.wfile.write("\t<link rel=\"stylesheet\" type=\"text/css\" href=\"http://some.linux.server/css/bootstrap.min.css\">\n")
			self.wfile.write("</head>\n")
			self.wfile.write("<body>\n")
			self.wfile.write("\t<script src=\"http://code.jquery.com/jquery.min.js\"></script>\n")
			self.wfile.write("\t<script src=\"http://some.linux.server/js/bootstrap.min.js\"></script>\n")
			self.wfile.write("\n\t<div class=\"container\">\n")
			self.wfile.write("\n\t<h1>Sorry :(</h1>\n")
			self.wfile.write("\n\tThere's nothing to see here.\n")
			self.wfile.write("</body>\n")
			self.wfile.write("</html>\n")
			return

		# log the thread id
		message =  threading.currentThread().getName()
		
		# parse the URL
		parsed_path = urlparse.urlparse(self.path)	
		params = urlparse.parse_qs(parsed_path.query)
#		print params

		# authenticate to Workfront API
		client = StreamClient('https://DOMAIN.attask-ondemand.com/attask/api')
		client.login('ADMINUSERNAME','PASSWORD')
		
		# get the object type from the parameters
		objectTypeParam = params['type']
#		print objectTypeParam[0]

		thisObjectCode =  params['object']

		if (objectTypeParam[0] == 'TASK'):
			try:
				thisObject = AtTaskObject(client.get(ObjCode.TASK,thisObjectCode[0],{'referenceNumber','DE:JIRA'}))
				keyFieldQuote = 'qd.Character02' # QuoteDtl.Character02 is used for JIRA Link (Workfront Link)
				keyFieldOrder = 'od.Character02' # OrderDtl.Character02 is used for JIRA Link (Workfront Link)
				keyFieldJob = 'od.Character02' # JobHead.Character02 is used for JIRA Link (Workfront Link)
				keyFieldInvoice = 'od.Character02' # JobHead.Character02 is used for JIRA Link (Workfront Link)
			except:
				do_FAIL()
				return

		elif (objectTypeParam[0] == 'ISSUE'):
			try:
				thisObject = AtTaskObject(client.get(ObjCode.ISSUE,thisObjectCode[0],{'referenceNumber','DE:JIRA'}))
				keyFieldQuote = 'qd.Character02' # QuoteDtl.Character02 is used for JIRA Link (Workfront Link)
				keyFieldOrder = 'od.Character02' # OrderDtl.Character02 is used for JIRA Link (Workfront Link)
				keyFieldJob = 'od.Character02' # OrderDtl.Character02 is used for JIRA Link (Workfront Link)
				keyFieldInvoice = 'od.Character02' # OrderDtl.Character02 is used for JIRA Link (Workfront Link)
			except:
				do_FAIL()
				return

		elif (objectTypeParam[0] == 'PROJ'):
			try:
				thisObject = AtTaskObject(client.get(ObjCode.PROJECT,thisObjectCode[0],{'referenceNumber','DE:JIRA','DE:Epicor Code'}))
				keyFieldQuote = 'qh.ShortChar05' # QuoteHed.ShortChar05 is used for Project Code
				keyFieldOrder = 'oh.ShortChar05' # OrderHed.ShortChar05 is used for Project Code
				keyFieldJob = 'jh.ShortChar05' # JobHead.ShortChar05 is used for ProjectCode
				keyFieldInvoice = 'oh.ShortChar05' # OrderHed.ShortChar05 is used for ProjectCode
			except:
				do_FAIL()
				return

		else:
			do_FAIL()
			return

		# get the object code from the URL parser, and create an AtTask object for the related task
		
		
#		print thisObject

		# get the object type, referenceNumber, and JIRA code for the task
		objectType = thisObject.data.get('objCode')
		refNum = thisObject.referenceNumber
		jiraCode = thisObject.data.get('DE:JIRA','none')
		projectCode = thisObject.data.get('DE:Epicor Code','none')

#		print objectType
#		print refNum
#		print jiraCode
#		print projectCode

		# manage Epicor link transition from JIRA code to Workfront referenceNum
		# if the JIRA custom field is present, use its contents as the idCode
		# otherwise, use the referenceNumber
		if '-' in jiraCode:
			idCode = jiraCode
		else:
			idCode = refNum

		# if we're looking for a project, search on the Project Code, not the JIRA code / Workfront ID
		if (objectType == 'PROJ'):
			idCode = projectCode

		# if there is no project code defined, idCode will be set to 'none', so trap for that
		if (idCode == 'none'):
			do_FAIL()
			return

		# connect to MS SQL server
		conn = _mssql.connect(server='MSSQLSERVER.domain.tld',user='******',password='******',database='Epicor905')

		# string containing SQL query for Quote details
		quoteSQL = """
		SELECT
		  LEFT(qh.ShortChar05, 5) AS ProjID,
		  qd.ProdCode,
		  qh.DateQuoted,
		  (CAST(qd.QuoteNum AS varchar) + ' / ' + CAST(qd.QuoteLine AS varchar)) AS QuoteLine,
		  (CAST(qd.SellingExpectedQty AS int)) AS Qty,
		  (CAST(qd.DocExtPriceDtl AS money)) AS LineCharges,
		  (CAST(ISNULL(aggregateMisc.MiscTotal, 0) AS money)) AS MiscCharges,
		  (CAST((ISNULL(SUM(aggregateMisc.MiscTotal), 0) + qd.DocExtPriceDtl) AS money)) AS Total,
		  (CAST(qd.PartNum AS varchar) + ' / ' + LEFT(CAST(qd.LineDesc AS varchar),50) ) AS PNDesc
		FROM Epicor905.dbo.QuoteDtl AS qd
		LEFT OUTER JOIN Epicor905.dbo.QuoteHed AS qh
		  ON qd.QuoteNum = qh.QuoteNum
		LEFT OUTER JOIN (SELECT
		  SUM(qm.DocMiscAmt) AS MiscTotal,
		  QuoteNum,
		  QuoteLine
		FROM Epicor905.dbo.QuoteMsc AS qm
		GROUP BY QuoteNum,
		         QuoteLine) AS aggregateMisc
		  ON (qd.QuoteNum = aggregateMisc.QuoteNum)
		  AND (qd.QuoteLine = aggregateMisc.QuoteLine)
		WHERE (%s LIKE \'%s%%\')
		GROUP BY qd.QuoteNum,
		         qd.QuoteLine,
		         qd.SellingExpectedQty,
		         qd.DocExtPriceDtl,
		         aggregateMisc.MiscTotal,
		         qh.ShortChar05,
		         qd.ProdCode,
		         qh.DateQuoted,
		         qd.PartNum,
		         qd.LineDesc
		ORDER BY qd.QuoteNum DESC, qd.QuoteLine ASC
		""" % (keyFieldQuote, idCode)

		# string containing SQL query for Order details
		orderSQL ="""
		SELECT
		  oh.OrderDate,
		  (CAST(od.OrderNum AS varchar) + ' / ' + CAST(od.OrderLine AS varchar) + ' / ' + oh.PONum) AS OrderLinePO,
		  (CAST(od.OrderQty AS int)) AS Qty,
		  (CAST(od.DocExtPriceDtl AS money)) AS LineCharges,
		  (CAST(ISNULL(aggregateMisc.MiscTotal, 0) AS money)) AS MiscCharges,
		  (CAST((ISNULL(SUM(aggregateMisc.MiscTotal), 0) + od.DocExtPriceDtl) AS money)) AS Total,
		  (CAST(od.PartNum AS varchar) + ' / ' + LEFT(CAST(od.LineDesc AS varchar),50) ) AS PNDesc
		FROM Epicor905.dbo.OrderDtl AS od
		LEFT OUTER JOIN (SELECT
		  SUM(om.DocMiscAmt) AS MiscTotal,
		  OrderNum,
		  OrderLine
		FROM Epicor905.dbo.OrderMsc AS om
		GROUP BY OrderNum,
		         OrderLine) AS aggregateMisc
		  ON (od.OrderNum = aggregateMisc.OrderNum)
		  AND (od.OrderLine = aggregateMisc.OrderLine)
		INNER JOIN OrderHed AS oh
		  ON oh.Company = od.Company
		  AND oh.OrderNum = od.OrderNum
		WHERE (%s LIKE \'%s%%\')
		GROUP BY od.OrderNum,
		         oh.PONum,
		         od.OrderLine,
		         od.OrderQty,
		         od.DocExtPriceDtl,
		         aggregateMisc.MiscTotal,
		         oh.OrderDate,
		         od.PartNum,
		         od.LineDesc
		ORDER BY od.OrderNum DESC, od.OrderLine ASC
		""" % (keyFieldOrder, idCode)

		# string containing SQL query for Job details
		jobSQL = """
		SELECT
		  jh.CreateDate,
		  od.OrderNum,
		  jh.JobNum,
		  jh.ProdQty,
		  jh.QtyCompleted,
		  jh.DueDate,
		  jh.JobCompletionDate,
		  (CAST(od.PartNum AS varchar) + ' / ' + LEFT(CAST(od.LineDesc AS varchar),50) ) AS PNDesc
		FROM Epicor905.dbo.OrderDtl AS od
		INNER JOIN Epicor905.dbo.JobProd AS jp
		  ON od.Company = jp.Company
		  AND od.OrderNum = jp.OrderNum
		  AND od.OrderLine = jp.OrderLine
		INNER JOIN Epicor905.dbo.JobHead AS jh
		  ON jp.Company = jh.Company
		  AND jp.JobNum = jh.JobNum
		WHERE UPPER(%s) LIKE \'%s\'
		  AND jh.JobReleased = 1
		ORDER BY jh.CreateDate DESC
		""" % (keyFieldJob, idCode)

		# string containing SQL query for Invoice details
		invoiceSQL = """
		SELECT
		  InvoiceNum,
		  InvoiceDate,
		  DocInvoiceAmt,
		  OpenInvoice,
		  OrderNum,
		  PONum
		FROM Epicor905.dbo.InvcHead ih
		WHERE ih.OrderNum IN (SELECT DISTINCT
		  od.ORDERNUM
		FROM Epicor905.dbo.OrderDtl od
		LEFT OUTER JOIN Epicor905.dbo.OrderHed oh
		  ON od.Company = oh.Company
		  AND od.OrderNum = oh.OrderNum
		WHERE %s LIKE \'%s\')
		ORDER BY ih.InvoiceDate DESC
		""" % (keyFieldInvoice, idCode)

		# send HTTP 200 OK status code, headers
		self.send_response(200)
		self.send_header("Content-type", "text/html")	
		self.end_headers()

		# write HTML top matter
		self.wfile.write("<!DOCTYPE html>\n")
		self.wfile.write("<html>\n")
		self.wfile.write("<head>\n")
		self.wfile.write("\t<meta>\n")
		self.wfile.write("\t<title>Epicor Integration</title>\n")
		self.wfile.write("\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n")
		self.wfile.write("\t<link rel=\"stylesheet\" type=\"text/css\" href=\"http://some.linux.server/css/bootstrap.min.css\">\n")
		self.wfile.write("</head>\n")
		self.wfile.write("<body>\n")
		self.wfile.write("\t<script src=\"http://code.jquery.com/jquery.min.js\"></script>\n")
		self.wfile.write("\t<script src=\"http://some.linux.server/js/bootstrap.min.js\"></script>\n")

		self.wfile.write("\n\t<div class=\"container\">\n")

		if (objectType == 'PROJ'):
			self.wfile.write("\n\t<h2>Epicor Reports for Project {0}</h2>\n".format(idCode))
			self.wfile.write("\n\t<p>In Workfront, the Project Code is selected in \
				Project Details > Standard Project Form > Epicor Code. In Epicor, the Project code is \
				selected from the dropdown menu on Epicor Quotes, Orders, and Jobs. This report shows Epicor\
				details matching the selected Project Code. Click on each panel to display details.</p>\n")
		else:
			self.wfile.write("\n\t<h2>Epicor Reports for Task {0}</h2>\n".format(idCode))
			self.wfile.write("\n\t<p>This report provides details related to the Task ID that is entered in the \
				\"JIRA Link\" field on Epicor quotes and orders. For old entries, the Task ID is the \
				JIRA code (\"PROTO-123\"). For new entries, the Task ID is the Workfront Task Reference Number \
				(\"12345\"). Click on each panel to hide details.</p>\n")

		self.wfile.write("\n\t\t<div class=\"panel-group\" id=\"accordian\">\n")

		# execute SQL query for quote detail
		conn.execute_query(quoteSQL)

		# create an empty list for rows of strings to be joined later
		tempList = []

		# create table for quote details
		self.wfile.write("\n\t\t\t<div class=\"panel panel-primary\">\n")
		self.wfile.write("\n\t\t\t\t<div class=\"panel panel-heading\">\n")
		self.wfile.write("\t\t\t\t\t<h3 class=\"panel-title\">\n")
		self.wfile.write("\t\t\t\t\t\t<a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#accordionOne\">\n")
		self.wfile.write("\t\t\t\t\t\t\tQuote Details\n")
		self.wfile.write("\t\t\t\t\t\t</a>\n")
		self.wfile.write("\t\t\t\t\t</h3>\n")
		self.wfile.write("\t\t\t\t</div> <!-- close panel-heading -->\n")
		# for PROJ reports, there's usually lots of data, so start with the panels collapsed
		# for TASK/ISSUE reports, there is usually less data, so start with the panels expanded
		if (objectType == 'PROJ'):
			self.wfile.write("\n\t\t\t\t<div id=\"accordionOne\" class=\"panel-collapse collapse\">\n")
		else:
			self.wfile.write("\n\t\t\t\t<div id=\"accordionOne\" class=\"panel-collapse collapse in\">\n")
		self.wfile.write("\t\t\t\t\t<div class=\"panel-body\">\n")
		self.wfile.write("\t\t\t\t\t\t<table class=\"table table-striped\">\n")
		tempList.append('\t\t\t\t\t\t\t<tr><thead>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Date Quoted</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Quote / Line</th>\n')
#		tempList.append('\t\t\t\t\t\t\t\t<th>Project Code</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>PN / Description</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Product Group</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Quantity</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Line Charges</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Misc Charges</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Total Value</th>\n')
		tempList.append('\t\t\t\t\t\t\t</tr></thead>\n')
		for row in conn:
			tempList.append('\t\t\t\t\t\t\t\t<tr>\n')
			try:
				dateString = (row['DateQuoted']).strftime('%d %b %Y')
			except:
				# dateString = str(None) did this cause a problem?
				dateString = 'None'
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-left\">', dateString, '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['QuoteLine'], '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['PNDesc'], '</td>\n'))
#			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['ProjID'], '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['ProdCode'], '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['Qty']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['LineCharges']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['MiscCharges']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['Total']), '</td>\n'))
			tempList.append('\t\t\t\t\t\t\t\t</tr>\n')
		tempList.append('\t\t\t\t\t\t</table>\n')
		tempList.append('\t\t\t\t\t</div> <!-- close panel body -->\n')
		tempList.append('\t\t\t\t</div> <!-- close accordianOne -->\n')
		tempList.append('\t\t\t</div> <!-- close panel-primary -->\n')

		# join rows of list into a new strting, and write it to the page
		quoteString = ''.join(tempList).encode('utf-8')
		
		try:
			self.wfile.write(quoteString)
		except:
			self.wfile.write('sorry, i choked on something i found in the list of quotes\n')

		self.wfile.write("\t\t\t<p></p>\n")

		#repeat to order details
		conn.execute_query(orderSQL)
		tempList = []
		self.wfile.write("\n\t\t\t<div class=\"panel panel-primary\">\n")
		self.wfile.write("\n\t\t\t\t<div class=\"panel panel-heading\">\n")
		self.wfile.write("\t\t\t\t\t<h3 class=\"panel-title\">\n")
		# for PROJ reports, there's usually lots of data, so start with the panels collapsed
		# for TASK/ISSUE reports, there is usually less data, so start with the panels expanded
		self.wfile.write("\t\t\t\t\t\t<a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#accordionTwo\">\n")
		self.wfile.write("\t\t\t\t\t\t\tOrder Details\n")
		self.wfile.write("\t\t\t\t\t\t</a>\n")
		self.wfile.write("\t\t\t\t\t</h3>\n")
		self.wfile.write("\t\t\t\t</div> <!-- close panel-heading -->\n")
		if (objectType == 'PROJ'):
			self.wfile.write("\n\t\t\t\t<div id=\"accordionTwo\" class=\"panel-collapse collapse\">\n")
		else:
			self.wfile.write("\n\t\t\t\t<div id=\"accordionTwo\" class=\"panel-collapse collapse in\">\n")
		self.wfile.write("\t\t\t\t\t<div class=\"panel-body\">\n")
		self.wfile.write("\t\t\t\t\t\t<table class=\"table table-striped\">\n")
		tempList.append('\t\t\t\t\t\t\t<tr><thead>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Order Date\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>SO / Line / PO</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>PN / Description</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Quantity</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Line Charges</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Misc Charges</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Total Value</th>\n')
		tempList.append('\t\t\t\t\t\t\t</tr></thead>\n')
		for row in conn:
			tempList.append('\t\t\t\t\t\t\t<tr>\n')
			try:
				dateString = (row['OrderDate']).strftime('%d %b %Y')
			except:
				dateString = str(None)
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-left\">', dateString, '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['OrderLinePO'], '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', row['PNDesc'], '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['Qty']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['LineCharges']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['MiscCharges']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['Total']), '</td>\n'))
			tempList.append('\t\t\t\t\t\t\t</tr>\n')
		tempList.append('\t\t\t\t\t\t</table>\n')
		tempList.append('\t\t\t\t\t</div> <!-- close panel body -->\n')
		tempList.append('\t\t\t\t</div> <!-- close accordianTwo -->\n')
		tempList.append('\t\t\t</div> <!-- close panel-primary -->\n')

		orderString = ''.join(tempList).encode('utf-8')
		self.wfile.write(orderString)

		self.wfile.write("\t\t\t<p></p>\n")


		# repeat for Job details
		conn.execute_query(jobSQL)
		tempList = []
		self.wfile.write("\n\t\t\t<div class=\"panel panel-primary\">\n")
		self.wfile.write("\n\t\t\t\t<div class=\"panel panel-heading\">\n")
		self.wfile.write("\t\t\t\t\t<h3 class=\"panel-title\">\n")
		self.wfile.write("\t\t\t\t\t\t<a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#accordionThree\">\n")
		self.wfile.write("\t\t\t\t\t\t\tJob Details\n")
		self.wfile.write("\t\t\t\t\t\t</a>\n")
		self.wfile.write("\t\t\t\t\t</h3>\n")
		self.wfile.write("\t\t\t\t</div> <!-- close panel-heading -->\n")
		if (objectType == 'PROJ'):
			self.wfile.write("\n\t\t\t\t<div id=\"accordionThree\" class=\"panel-collapse collapse\">\n")
		else:
			self.wfile.write("\n\t\t\t\t<div id=\"accordionThree\" class=\"panel-collapse collapse in\">\n")
		self.wfile.write("\t\t\t\t\t<div class=\"panel-body\">\n")
		self.wfile.write("\t\t\t\t\t\t<table class=\"table table-striped\">\n")
		tempList.append('\t\t\t\t\t\t\t<tr><thead>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Created</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Job Number</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>SO Number</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>PN / Description</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Start Qty</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Complete Qty</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Due Date</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Job Completion Date</th>\n')
		tempList.append('\t\t\t\t\t\t\t</tr></thead>\n')
		for row in conn:
			tempList.append('\t\t\t\t\t\t\t<tr>\n')
			try:
				dateString = (row['CreateDate']).strftime('%d %b %Y')
			except:
				dateString = 'None'
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-left\">', dateString, '</td>\n'))
			try:
				jobString = "%s" % row['JobNum']
				jobString = jobString.encode('utf-8')
			except:
				jobString = 'Unicode'
				print "I just choked on a Unicode character in Job number field"
				print row['JobNum']
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', jobString, '</td>\n'))
			try:
				orderString = "%d" % row['OrderNum']
			except:
				orderString = "Choked on Unicode"
				print "I just choked on a Unicode character in Order number field"
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', orderString, '</td>\n'))
			try:
				pnString = "%s" % row['PNDesc']
			except:
				pnString = "Choked on Unicode"
				print "I just choked on a Unicode character in PNDesc field"
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', str(row['PNDesc']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['ProdQty']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['QtyCompleted']), '</td>\n'))
			try:
				dateString = (row['DueDate']).strftime('%d %b %Y')
			except:
				dateString = 'None'
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', dateString, '</td>\n'))
			try:
				dateString = (row['JobCompletionDate']).strftime('%d %b %Y')	
			except:
				dateString = 'None'
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', dateString, '</td>\n'))
			tempList.append('\t\t\t\t\t\t\t</tr>\n')
		tempList.append('\t\t\t\t\t\t</table>\n')
		tempList.append('\t\t\t\t\t</div> <!-- close panel body -->\n')
		tempList.append('\t\t\t\t</div> <!-- close accordianThree -->\n')
		tempList.append('\t\t\t</div> <!-- close panel-primary -->\n')

		#newTempList = unicode(tempList, 'utf-8')
		#jobString = u' '.join(tempList).encode('utf-8')
		jobString = u' '.join(tempList).strip()
		
		self.wfile.write(jobString)

		self.wfile.write("\t\t\t<p></p>\n")


		# repeat for Invoice details
		conn.execute_query(invoiceSQL)
		tempList = []
		self.wfile.write("\n\t\t\t<div class=\"panel panel-primary\">\n")
		self.wfile.write("\n\t\t\t\t<div class=\"panel panel-heading\">\n")
		self.wfile.write("\t\t\t\t\t<h3 class=\"panel-title\">\n")
		self.wfile.write("\t\t\t\t\t\t<a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#accordionFour\">\n")
		self.wfile.write("\t\t\t\t\t\t\tInvoice Details\n")
		self.wfile.write("\t\t\t\t\t\t</a>\n")
		self.wfile.write("\t\t\t\t\t</h3>\n")
		self.wfile.write("\t\t\t\t</div> <!-- close panel-heading -->\n")
		if (objectType == 'PROJ'):
			self.wfile.write("\n\t\t\t\t<div id=\"accordionFour\" class=\"panel-collapse collapse\">\n")
		else:
			self.wfile.write("\n\t\t\t\t<div id=\"accordionFour\" class=\"panel-collapse collapse in\">\n")
		self.wfile.write("\t\t\t\t\t<div class=\"panel-body\">\n")
		self.wfile.write("\t\t\t\t\t\t<table class=\"table table-striped\">\n")
		tempList.append('\t\t\t\t\t\t\t<tr><thead>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Invoice Number</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Invoice Date</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Sales Order</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th>Customer PO</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Open</th>\n')
		tempList.append('\t\t\t\t\t\t\t\t<th class=\"text-right\">Invoice Amount</th>\n')
		tempList.append('\t\t\t\t\t\t\t</tr></thead>\n')
		for row in conn:
			tempList.append('\t\t\t\t\t\t\t<tr>\n')
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', str(row['InvoiceNum']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', (row['InvoiceDate']).strftime('%d %b %Y'), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', str(row['OrderNum']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td>', str(row['PONum']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['OpenInvoice']), '</td>\n'))
			tempList.extend(('\t\t\t\t\t\t\t\t<td class=\"text-right\">', "{:,.0f}".format(row['DocInvoiceAmt']), '</td>\n'))
			tempList.append('\t\t\t\t\t\t\t</tr>\n')
		tempList.append('\t\t\t\t\t\t</table>\n')
		tempList.append('\t\t\t\t\t</div> <!-- close panel body -->\n')
		tempList.append('\t\t\t\t</div> <!-- close accordianThree -->\n')
		tempList.append('\t\t\t</div> <!-- close panel-primary -->\n')

		invString = ''.join(tempList).encode('utf-8')
		
		self.wfile.write(invString)

		# close accordian panel group
		self.wfile.write("\t\t</div> <!-- close panel-group -->\n")
		
		# footnotes
		self.wfile.write("\n\t\t\t<h3>Footnotes</h3>\n")
		self.wfile.write("\t\t\t<ul>\n")
		self.wfile.write("\t\t\t\t<li>These data are current as of last night.</li>\n")
		self.wfile.write("\t\t\t\t<li>The reference number for this %s is: %s</li>\n" % (objectType, refNum) )
		self.wfile.write("\t\t\t\t<li>The JIRA Link for this %s is: %s</li>\n" % (objectType, jiraCode) )
		self.wfile.write("\t\t\t\t<li>The Project Code for this %s is: %s</li>\n" % (objectType, projectCode) )
		self.wfile.write("\t\t\t\t<li>Quote query searched Epicor field %s for: %s</li>\n" % (keyFieldQuote, idCode) )
		self.wfile.write("\t\t\t\t<li>Order query searched Epicor field %s for: %s</li>\n" % (keyFieldOrder, idCode) )
		self.wfile.write("\t\t\t\t<li>Order query searched Epicor field %s for: %s</li>\n" % (keyFieldJob, idCode) )
		self.wfile.write("\t\t\t\t<li>Order query searched Epicor field %s for: %s</li>\n" % (keyFieldInvoice, idCode) )
		self.wfile.write("\t\t\t\t<li>Served by %s</li>\n" % message)
		self.wfile.write("\t\t\t</ul>\n")

		# close container, body, and html
		self.wfile.write("\n\t</div> <!-- outer container -->\n")
		self.wfile.write("</body>\n</html>\n")

		# close the MSSQL connection
		conn.close()

		return