## Copyright 2004,2005 Pierre Martineau <pmartino@users.sourceforge.net>
# This file is part of Bibus, a bibliographic database that can
# work together with OpenOffice.org to generate bibliographic indexes.
#
# Bibus is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Bibus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bibus; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
# generated by wxGlade 0.2 on Wed May 28 22:47:35 2003
# Then edited by hand
# DO NOT EDIT WITH wxGLADE'

import wx
import urllib,ClientForm, urllib2, xml.dom.minidom, Import.PubMedXML, time, BIB
import newProgressWin
tool = 'bibus'						# used to identify the software at ncbi in case of problem
email= 'm.errami@gmail.com' # used to identify the software at ncbi in case of problem

eTBlastHTTP = "http://invention.swmed.edu"

class SearcheTBlast(wx.Dialog):
	def __init__(self,db, *args, **kwds):

		kwds["style"] = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL
		wx.Dialog.__init__(self, *args, **kwds)
		self.SearchList = [u'All Fields [ALL]', u'Text Words [TW]', u'Author [AU]', u'Title [TI]', u'Journal Title [TA]', u'Publication Date [DP]', u'Publication Type [PT]', u'Entrez Date [EDAT]', u'Pagination [PG]', u'Volume [VI]', u'Issue [IP]', u'Unique Identifiers [UID]', u'Personal Name as Subject [PS]', u'MeSH Terms [MH]', u'MeSH Date [MHDA]', u'MeSH Major Topic [MAJR]', u'MeSH Subheadings [SH]', u'Affiliation [AD]', u'EC/RN Number [RN]', u'Filter [FILTER]', u'Language [LA]', u'Secondary Source ID [SI] ', u'Subset [SB]', u'Substance Name [NM]']
		self.KeyList = ('[ALL]', '[TW]', '[AU]', '[TI]', '[TA]', '[DP]', '[PT]', '[EDAT]', '[PG]', '[VI]', '[IP]', '[UID]', '[PS]', '[MH]', '[MHDA]', '[MAJR]', '[SH]', '[AD]', '[RN]', '[FILTER]', '[LA]', '[SI]]', '[SB]', '[N]')
		self.SearchBool = ['AND','OR','NOT']
		self.db=db
		self.parent = args[0]
		# Top of the Dialog
		self.Mode = wx.RadioBox(self, -1, _("Mode"), choices=[_('Normal'), _('Expert')], majorDimension=0, style=wx.RA_SPECIFY_COLS)
		self.label_NumbRec= wx.StaticText(self, -1, _("Records with Z-Score >="))
		self.label_Paste= wx.StaticText(self, -1, _(" Paste your text below: "))
		self.label_Email= wx.StaticText(self, -1, _("Enter your email below (Optional):"))
		self.label_Cite= wx.StaticText(self, -1, _("eTBlast main page: http://invention.swmed.edu/etblast/index.shtml"))
		self.NumbRec = wx.SpinCtrl(self, -1, min=1, max=10000)
		self.NumbRec.SetValue(3)
		self.label_FirstRec = wx.StaticText(self, -1, _("Keep the best citations: "))
		self.FirstRec = wx.SpinCtrl(self, -1, "0", min=0, max=10000)
		self.FirstRec.SetValue(100)
		emailsize = wx.Size(400,20)
		self.Email = wx.TextCtrl(self, -1, u"",size = emailsize)
		#self.Email.SetValue(")
		self.static_line_1 = wx.StaticLine(self, -1)
		#
		#### TextCtrl to enter query in expert mode
		self.ExpertValue = wx.TextCtrl(self, -1, u"", style=wx.TE_MULTILINE)
		#### wx.Panel that enclose the grid
		self.PanelSearch = wx.TextCtrl(self, -1, u"", style=wx.TE_MULTILINE)
		#### Search grid
		#self.ScrollWin = wx.ScrolledWindow(self.PanelSearch,style=wx.TAB_TRAVERSAL)	# in order to be able to scroll the choices grid
		#self.ScrollWin.SetScrollRate(10,10)
		#self.SearchBoolKey=[]
		#self.SearchChoice=[]
		#self.SearchValue=[]
		#self.SearchBoolKey.append(wx.StaticText(self.ScrollWin, -1, u""))	# first line special since there is no bool choice
		#self.SearchChoice.append(wx.Choice(self.ScrollWin, -1, choices=self.SearchList))
		#self.SearchValue.append(wx.TextCtrl(self.ScrollWin, -1, u""))
		#for line in range(1,4):
		#	self.SearchBoolKey.append(wx.Choice(self.ScrollWin, -1, choices=self.SearchBool))
		#	self.SearchChoice.append(wx.Choice(self.ScrollWin, -1, choices=self.SearchList))
		#	self.SearchValue.append(wx.TextCtrl(self.ScrollWin, -1, u""))
		#### Button to add line
		#self.static_line_2 = wx.StaticLine(self.PanelSearch, -1)
		#self.button_Add = wx.Button(self.PanelSearch, -1, _("Add a line"))
		#self.static_line_3 = wx.StaticLine(self, -1)
		#
		self.button_Cancel = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
		self.button_Search = wx.Button(self, wx.ID_OK, _("Search"))
		self.button_SearchClose = wx.Button(self, -1, _("Experts' papers"))
		self.button_Count = wx.Button(self, -1, _("Count"))

		self.timeOut = 600
		self.__set_properties()
		self.__do_layout()
		self.__set_event()
		self.SetDimensions(BIB.PUBMED_X, BIB.PUBMED_Y, BIB.PUBMED_WIDTH, BIB.PUBMED_HEIGHT)
		#self.CenterOnParent()

	def __set_properties(self):
		self.SetTitle(_("eTBlast search on PubMed"))
		self.Mode.SetSelection(0)
		self.Mode.Enable(False)
		self.button_SearchClose.Enable(False)
		self.button_Count.Enable(False)
		#self.SearchChoice[0].SetSelection(0)
		#for line in range(1,4):
		#	self.SearchChoice[line].SetSelection(0)
		#	self.SearchBoolKey[line].SetSelection(0)
		self.button_Search.SetDefault()
		self.PanelSearch.SetFocus()	# we give focus to the search box

	def __do_layout(self):
		sizer_1 = wx.BoxSizer(wx.VERTICAL)	# top sizer if the dialog window
		sizer_2 = wx.BoxSizer(wx.HORIZONTAL)	# for top of the dialog
		self.grid_sizer_1 = wx.FlexGridSizer(0, 3, vgap=0, hgap=0)	# main grid
		self.sizer = wx.BoxSizer(wx.VERTICAL) # sizer enclosing scrolled window + button 'Add line' = sizer of self.PanelSearch
		sizer_3 = wx.BoxSizer(wx.HORIZONTAL)	# for buttons (bottom)
		grid_sizer_2 = wx.GridSizer(2, 2, vgap=5, hgap=5)	# for spinButtons (number of record)
		#
		# Set top of the dialog
		# number of record
		grid_sizer_2.Add(self.label_NumbRec, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL,0)
		grid_sizer_2.Add(self.NumbRec,0,0,0)
		grid_sizer_2.Add(self.label_FirstRec, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL,0)
		grid_sizer_2.Add(self.FirstRec,0,0,0)
		sizer_2.Add(self.Mode,0,wx.ALIGN_RIGHT|wx.ALL,5)
		sizer_2.Add((10,0),1,0,0)
		sizer_2.Add(grid_sizer_2,0,wx.ALIGN_RIGHT,0)
		sizer_1.Add(sizer_2,0,wx.EXPAND,0)
		sizer_1.Add(self.static_line_1,0,wx.EXPAND,0)
		#
		# Set grid
		#for line in range(4):
		#	self.grid_sizer_1.Add(self.SearchBoolKey[line],0,0,0)
		#	self.grid_sizer_1.Add(self.SearchChoice[line],0,0,0)
		#	self.grid_sizer_1.Add(self.SearchValue[line],0,wx.EXPAND,0)
		self.grid_sizer_1.AddGrowableCol(2)
		#self.ScrollWin.SetSizer(self.grid_sizer_1)
		#self.grid_sizer_1.Fit(self.ScrollWin)
		#self.grid_sizer_1.SetSizeHints(self.ScrollWin)
		#self.ScrollWin.Layout()
		#self.sizer.Add(self.ScrollWin,1,wx.EXPAND,0)
		# Add line button
		#self.sizer.Add(self.static_line_2,0,wx.EXPAND,0)
		#self.sizer.Add(self.button_Add,0,wx.EXPAND,0)
		self.PanelSearch.SetSizerAndFit(self.sizer)
		sizer_1.Add(self.label_Paste, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL,0)
		sizer_1.Add(self.PanelSearch,1,wx.EXPAND,0)
		sizer_1.Add(self.label_Email,0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL,0)
		sizer_1.Add(self.Email,0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL,0)
		sizer_1.Add(self.label_Cite,0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL,0)
		#size = self.Email.getSize()
		#print size.getWidth(), size.getHeight()
		sizer_1.Add(self.ExpertValue,1,wx.EXPAND,0)
		sizer_1.Show(self.ExpertValue,False)	# Masked at startup
		#
		#sizer_1.Add(self.static_line_3,0,wx.EXPAND,0)
		#
		# Set Bottom Buttons
		sizer_3.Add(self.button_Cancel, 0, wx.ALL,5)
		sizer_3.Add(self.button_Search, 0, wx.ALL,5)
		sizer_3.Add(self.button_SearchClose, 0, wx.ALL,5)
		sizer_3.Add(self.button_Count, 0, wx.ALL,5)
		sizer_1.Add(sizer_3,0,wx.ALIGN_CENTER_HORIZONTAL,0)
		#
		self.SetAutoLayout(1)
		self.SetSizer(sizer_1)
		sizer_1.Fit(self)
		sizer_1.SetSizeHints(self)
		self.Layout()

	def __set_event(self):
		wx.EVT_BUTTON(self,wx.ID_OK,self.onSearch)
		wx.EVT_BUTTON(self,wx.ID_CANCEL,self.onCloseWindow)
		wx.EVT_CLOSE(self, self.onCloseWindow)
		#wx.EVT_BUTTON(self,self.button_Add.GetId(),self.addLine)
		wx.EVT_BUTTON(self,self.button_SearchClose.GetId(),self.onSearchClose)
		wx.EVT_BUTTON(self,self.button_Count.GetId(),self.onCount)
		wx.EVT_RADIOBOX(self,self.Mode.GetId(),self.onMode)

	def onMode(self,event):
		selection = self.Mode.GetStringSelection()
		if selection == _('Normal'):
			self.GetSizer().Show(self.ExpertValue,False)
			self.GetSizer().Show(self.PanelSearch,True)
		else:
			self.GetSizer().Show(self.PanelSearch,False)
			self.GetSizer().Show(self.ExpertValue,True)
			self.ExpertValue.SetValue(self.__makeSearch())	# set the expert value to the current Normal search
		self.Layout()

	#def addLine(self,event):
	#	self.SearchBoolKey.append(wx.Choice(self.ScrollWin, -1, choices=self.SearchBool))
	#	self.SearchChoice.append(wx.Choice(self.ScrollWin, -1, choices=self.SearchList))
	#	self.SearchValue.append(wx.TextCtrl(self.ScrollWin, -1, u""))
	#	self.grid_sizer_1.Add(self.SearchBoolKey[-1],0,0,0)
	#	self.grid_sizer_1.Add(self.SearchChoice[-1],0,0,0)
	#	self.grid_sizer_1.Add(self.SearchValue[-1],0,wx.EXPAND,0)
	#	self.grid_sizer_1.Fit(self.ScrollWin)
	#	self.PanelSearch.Layout()
	#	self.Layout()

	def onCloseWindow(self,event):
		BIB.PUBMED_WIDTH, BIB.PUBMED_HEIGHT = self.GetSizeTuple()
		BIB.PUBMED_X, BIB.PUBMED_Y = self.GetPositionTuple()
		BIB.PUBMED_NB = self.NumbRec.GetValue()
		self.Destroy()

	def onSearchClose(self,event):
		#self.onSearch(event)
		#self.onCloseWindow(event)
		dlg=wx.MessageDialog(self, _("Experts search is not available yet!"), caption = _("Information"), style = wx.OK | wx.CENTRE | wx.ICON_ERROR, pos = wx.DefaultPosition)

	def __makeSearch(self):
		"""Return the search corresponding to the choices in Normal mode"""
		line = 0
		search = u''
		#print "%s"%self.PanelSearch.GetValue()
		#try:
		#	while self.SearchValue[line].GetValue() == u'': line = line +1
		#	search="""%s%s""" %(self.SearchValue[line].GetValue().strip(),self.KeyList[self.SearchChoice[line].GetSelection()])
		#	line = line +1
		#	while True:
		#		if self.SearchValue[line].GetValue():
		#			search = """%s %s %s%s""" %(search,self.SearchBoolKey[line].GetStringSelection(),self.SearchValue[line].GetValue().strip(),self.KeyList[self.SearchChoice[line].GetSelection()])
		#		line = line +1
		#except IndexError: pass
		return search

	def __query_medline(self,rettype):
		if self.Mode.GetStringSelection() == _('Normal'):
			search = self.__makeSearch()
		else:
			search = self.ExpertValue.GetValue().strip()
		#
		#retstart = self.FirstRec.GetValue()
		retstart = 0
		retmax = self.NumbRec.GetValue()
		#
		#query = urllib.urlencode({'db':'pubmed','term':search,'retstart':retstart,'retmax':retmax,'usehistory':'y','rettype':rettype,'retmode':'xml','tool':tool,'email':email})
		#print query
		#return urllib.urlopen("http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi", query)

	def onSearch(self,event):
		msg= newProgressWin.newProgressWin("eTBlast request is on the way...","Initializing...")
		#try:
		retstart = 0
		retmax = self.NumbRec.GetValue()
		the_url='http://invention.swmed.edu/etblast/etblast.shtml'
		try:
			req = urllib2.Request(the_url)
			response = urllib2.urlopen(req)
		except urllib2.URLError:
			msg.Destroy()
			return
		forms = ClientForm.ParseResponse(response)
		response.close()
		form = forms[0]
		asciiSearchString = self.PanelSearch.GetValue().strip()
		asciiSearchString = asciiSearchString.encode('ascii', 'ignore')
		form["paragraph_area"] = asciiSearchString
		form["email"] = self.Email.GetValue()
		request2 = form.click() # urllib2.Request object
		try:
			response2 = urllib2.urlopen(request2)
		except urllib2.HTTPError, response2:
			msg.Destroy()
			return
		result_url=self.__eTBlastParse(response2)
		response2.close()
		#print "sleeping 60 sec"
		#res_example = urllib.urlopen(url_example)
		#print self.__getTitle(res_example)
		res_to_check = urllib.urlopen(result_url)
		response_result_title=self.__getTitle(res_to_check)
		res_to_check.close()
		isTitleOK = -2
		totalTime = 0
		timeOutTime = self.timeOut
		while isTitleOK < 0 :
			if totalTime>=60:
				totalTime = 0
			if timeOutTime<=0:
				msg.Destroy()
				self.__showTimeOut()
				return
			res_to_check = urllib.urlopen(result_url)
			response_result_title=self.__getTitle(res_to_check)
			isTitleOK = response_result_title.rfind('Results of the search')
			if isTitleOK < 0 :
				res_to_check.close()
				for i in range(5):
					time.sleep(1)
					timeOutTime=timeOutTime-1
					totalTime =totalTime+1
					if not msg.Update(totalTime*0.01,"Waiting for eTBlast Reply..."):
						msg.Destroy()
						return
			#print "retrying in 5 seconds =>",response_result_title
		if not msg.Update(0.7,"Fetching eTBlast Results..."):
			msg.Destroy()
			return
		#print "query terms before"
		query_terms=self.__parseResults(res_to_check)
		if not query_terms:
			msg.Destroy()
			wx.MessageBox(_("The search did not return any result."),_("eTBlast result"),parent=self)
			return
		#print "query terms after"
		if not msg.Update(0.8,"Sending request to Medline..."):
			msg.Destroy()
			return
		query = urllib.urlencode({'db':'pubmed','term':query_terms,'retstart':retstart,'retmax':retmax,'usehistory':'y','rettype':'uilist','retmode':'xml','tool':'bibus','email':'m.errami@gmail.com'})
		#print "query to send=",query
		#return " "
		f=urllib.urlopen("http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi", query)
		#print f.read()
		try:
			mydom = xml.dom.minidom.parse(f)
			#print 'Count=',mydom.getElementsByTagName('Count')[0].firstChild.data
			#if (not mydom.getElementsByTagName('ERROR')) and (mydom.getElementsByTagName('Count')[0].firstChild.data != '0'):
			webenv = mydom.getElementsByTagName('WebEnv')[0].firstChild.data
			querykey = mydom.getElementsByTagName('QueryKey')[0].firstChild.data
			retstart = 0
			retmax = self.FirstRec.GetValue()
			myquery = urllib.urlencode({'db':'pubmed','query_key':querykey,'retstart':retstart,'retmax':retmax,'retmode':'xml','rettype':'medline','tool':'bibus','email':'m.errami@gmail.com'})
			#print "myquery =",myquery
			#print "query=", query# ,'WebEnv':webenv
			# we don't want to quote WebEnv since it seems to cause problems (% for instance)
			#print "mouWebEnv=%s&%s" %(webenv,myquery)
			f = urllib.urlopen("http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi", "WebEnv=%s&%s" %(webenv,myquery))
			if not msg.Update(0.9,"Fetching Medline Results..."):
				msg.Destroy()
				return
			self.db.deleteOnline()	# clear last online search
			#print f.read()
			records = Import.PubMedXML.importRef(f)
			for CurRecord in records:
				#print "records"
				self.db.writeRefOnline(CurRecord)
			f.close()
			## update window list
			self.parent.keytree.SelectItem(self.parent.keytree.keyOnline)
			self.parent.updateList(self.db.getAllRefOnline(BIB.LIST_DISPLAY,BIB.LIST_ORDER,BIB.LIST_HOW,short=True))
			msg.Destroy()
			#self.__showError()
		except:
			self.__showError()
			msg.Destroy()
		#except:
		#	print "An error occured when trying to submit form to eTBlast"
		#	msg.Destroy()
			
	def __parseResults(self,doc):
		#etblast_avg=0.385
		#etblast_sd=0.152
		query =""
		nbRec=0 
		noFound = "bibusNoneFoundInParsingEtblast"
		tmpQuery= noFound
		for line in doc:
			if nbRec >=self.FirstRec.GetValue() + 1:
				break
			if line.rfind('pmid=')>-1 and line.rfind('&amp;user_id=')>-1 :
				#print line[line.rfind('pmid=')+5:line.rfind('&amp;user_id=')]
				tmpQuery = line[line.rfind('pmid=')+5:line.rfind('&amp;user_id=')]
				nbRec = nbRec+1
			if line.rfind('Score:') >-1 and line.rfind(tmpQuery)==-1:
				try:
					score = float(line[line.rfind('Z-score:')+8:line.rfind('</td></tr>')])
					#print "Score ok =", score
				except:
					#print "score set to 3"
					score = 3
				if score >= self.NumbRec.GetValue() and tmpQuery != noFound :
					query = query+tmpQuery+"[UID] OR "
		return query[0:query.rfind(' OR')]

	def __getTitle(self,doc):
		isTitle = -2
		for line in doc:
			isTitle = line.rfind('<title>')
			if isTitle >-1 :
				return line
		return "NOTITLE"

	def __eTBlastParse(self,myurl):
		#print "URLS IS TITLE IS ===",
		title = self.__getTitle(myurl)
		#print title
		#print "getURL", myurl.geturl()
		#print "getInfo", myurl.info()	# headers
		#print myurl.read()	# body
		fileToParse = myurl
		for line in fileToParse:
			#isFound = line.rfind('targetURL')
			isFound =line.rfind('targetURL="http://invention.swmed.edu/cgi-bin/etblast/processing_etblast_new.pl?user-id=')
			upBrack = line.rfind('"')
			lowBrack = line.find('"') +1
			if isFound >-1 :
				result_url=line[lowBrack:upBrack]
				#v =0
				#while v < 10:
				#	v += 1
				#	print "v=",v
				#print "sleeping for 10s"
				#time.sleep(1)
				#print "slept done"
				#print "URL for Result is: \n%s"%result_url
				usernb = result_url[(result_url.rfind('='))+1:len(line)]
				actual_result_url = "http://invention.swmed.edu/etblast/user/user-%s/results.html"%usernb
				#print "ACTUAL for Result is: \n%r"%actual_result_url
				return actual_result_url
		return " "
			#example_URL http://invention.swmed.edu/etblast/user/user-1133719983/results.html
			#if line.rfind("/cgi-bin/etblast/abstract_local?pmid=")!=-1:
			#print "rfind=>%s"%line.rfind('/cgi-bin/etblast/abstract_local?pmid=')
			#print "rfind=>%s"%line.rfind('/cgi-bin/etblast/abstract_local?pmid=')

	def onCount(self,event):
		f = self.__query_medline('count')
		#print f.read()
		mydom = xml.dom.minidom.parse(f)
		if not mydom.getElementsByTagName('ERROR'):
			self.__showCount(mydom.getElementsByTagName('Count')[0].firstChild.data)
		else:
			self.__showError()

	def __showError(self):
		dlg=wx.MessageDialog(self, _("Error during query"), caption = _("Error"), style = wx.OK | wx.CENTRE | wx.ICON_ERROR, pos = wx.DefaultPosition)
		try:
			dlg.ShowModal()
		finally:
			dlg.Destroy()
	def __showTimeOut(self):
		tOut = self.timeOut/60
		dlg=wx.MessageDialog(self, _("Time Out (%s min). No answer received. eTBLAST server might be down."%tOut), caption = _("Error"), style = wx.OK | wx.CENTRE | wx.ICON_ERROR, pos = wx.DefaultPosition)
		try:
			dlg.ShowModal()
		finally:
			dlg.Destroy()

	def __showCount(self,count):
		dlg=wx.MessageDialog(self, _("There is %s such records in Medline")%count, caption = u"", style = wx.OK | wx.CENTRE | wx.ICON_INFORMATION, pos = wx.DefaultPosition)
		try:
			dlg.ShowModal()
		finally:
			dlg.Destroy()

# end of class Search_model


