import urllib import urllib2 import itertools import mimetools import mimetypes from cStringIO import StringIO import urllib import urllib2 class MultiPartForm(object): """Accumulate the data to be used when posting a form.""" def __init__(self): super(MultiPartForm, self).__init__() self.form_fields = [] self.files = [] self.boundary = mimetools.choose_boundary() return def get_content_type(self): return 'multipart/form-data; boundary=%s' % self.boundary def add_field(self, name, value): """Add a simple field to the form data.""" self.form_fields.append((name, value)) return def add_file(self, fieldname, filename, fileHandle, mimetype=None): """Add a file to be uploaded.""" body = fileHandle.read() if mimetype is None: mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' self.files.append((fieldname, filename, mimetype, body)) return def __str__(self): """Return a string representing the form data, including attached files.""" # Build a list of lists, each containing "lines" of the # request. Each part is separated by a boundary string. # Once the list is built, return a string where each # line is separated by '\r\n'. parts = [] part_boundary = '--' + self.boundary # Add the form fields parts.extend( [ part_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', value, ] for name, value in self.form_fields ) # Add the files to upload parts.extend( [ part_boundary, 'Content-Disposition: file; name="%s"; filename="%s"' % \ (field_name, filename), 'Content-Type: %s' % content_type, '', body, ] for field_name, filename, content_type, body in self.files ) # Flatten the list and add closing boundary marker, # then return CR+LF separated data flattened = list(itertools.chain(*parts)) flattened.append('--' + self.boundary + '--') flattened.append('') return '\r\n'.join(flattened) class DjangoMultiPartForm(MultiPartForm): def __init__(self, baseURL): super (DjangoMultiPartForm,self).__init__() self.baseURL = baseURL def get_request(self): # Build the request request = urllib2.Request(self.baseURL) request.add_header('User-agent', 'PyMOTW (http://www.doughellmann.com/PyMOTW/)') body = str(self) request.add_header('Content-type', self.get_content_type()) request.add_header('Content-length', len(body)) request.add_data(body) return request class DjangoSession (object): def __init__(self, *args, **kws): self.baseURL = args[0] self.username = args[1] self.password=args[2] self.baseURL = args[0] self.cookieLookup = {} handlers = self.createHandlers() self.opener = urllib2.build_opener(*handlers) self.openSession() def createHandlers(self): handlers = [urllib2.HTTPHandler(), urllib2.HTTPSHandler()] handlers += [self.basicAuthHandler()] handlers += [self.cookieHandler()] return handlers def basicAuthHandler(self): password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() top_level_url = "" password_mgr.add_password(None, top_level_url, "username","PASSWORD") handler = urllib2.HTTPhandler = urllib2.HTTPBasicAuthHandler(password_mgr) return handler def cookieHandler(self): import cookielib self.cookies = cookielib.LWPCookieJar() handler = urllib2.HTTPCookieProcessor(self.cookies) return handler def openRequest(self, request): print self.cookieLookup response = self.opener.open(request) for cookie in self.cookies: self.cookieLookup[cookie.name] = cookie.value return response def openURL(self, url): return self.openRequest(url) def postURLWithValues(self, url, values): data = urllib.urlencode(values) req = urllib2.Request(url, data) response = self.openURL(req) return response def getURLWithValues(self, url, values): print 'url',url url_values = urllib.urlencode(values) full_url = url + '?' + url_values print 'full_url',full_url req = urllib2.Request(full_url) response = self.openURL(req) return response def openSession(self): response = self.openURL(self.baseURL) responseHTML=response.read() formURL = response.geturl() #Here check that this looks like a properly crafted login form from lxml import etree parser = etree.HTMLParser() responseAsEtree = etree.parse(StringIO(responseHTML), parser) if len(responseAsEtree.xpath("//input[@name='username']")) == 0: raise Exception("No username field in login page retrieved from ", response.geturl()) if len(responseAsEtree.xpath("//input[@name='password']")) == 0: raise Exception("No password field in login page retrieved from ", response.geturl()) else: print 'Retrieved login form with url', formURL #Now post to this url, using the provided csrftoken and our stored credentials values = {'username':self.username,'password':self.password,'csrfmiddlewaretoken':self.cookieLookup['csrftoken'],next:self.baseURL} secondResponse = self.postURLWithValues(formURL, values) html = secondResponse.read() #print html def multiPartForm(self, baseURL): form = DjangoMultiPartForm(baseURL) form.add_field('csrfmiddlewaretoken',self.cookieLookup['csrftoken']) return form class CCP4i2DjangoSession(DjangoSession): def __init__(self, *args, **kws): super(CCP4i2DjangoSession, self).__init__(*args, **kws) self.pm = self.myStartProjectsManager() def myStartProjectsManager(self): import os CCP4=os.environ['CCP4'] CCP4I2_TOP = os.path.join(CCP4,'share','ccp4i2') sys.path.append(os.path.join(CCP4I2_TOP,'utils')) from startup import setupEnvironment, setupPythonpath, startProjectsManager setupEnvironment(path=CCP4I2_TOP) setupPythonpath(top=CCP4I2_TOP,mode='qtgui') pm = startProjectsManager() return pm def projectIdForName(self, projectName=None, strict=False): try: if strict: projectIdList = [projectTuple[0] for projectTuple in self.pm.db().listProjects() if projectName == projectTuple[1]] else: projectIdList = [projectTuple[0] for projectTuple in self.pm.db().listProjects() if projectName in projectTuple[1]] return projectIdList[0] except: return None def slugify(self, value): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. """ import unicodedata, re value = unicodedata.normalize('NFKD', value.decode('unicode-escape')).encode('ascii', 'ignore') value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) result = re.sub('[-\s]+', '-', value) return result def pushProject(self,projectName): projectId = self.projectIdForName(projectName=projectName, strict=False) import CCP4NonGuiProjectUtils projectList = self.pm.db().listProjects() projectNameList = [projectTuple[1] for projectTuple in projectList if projectId in projectTuple[0]] projectName = self.slugify(projectNameList[-1]) import tempfile tmpArchive = tempfile.NamedTemporaryFile(suffix='.ccp4_project.zip',delete=False) tmpArchive.close() fullPath=tmpArchive.name self.pm.compressProject(projectId,after=None,excludeI2files=False,fileName=fullPath,blocking=True) form = self.multiPartForm(self.baseURL+'/importProject') import os with open(fullPath,"r") as fileHandle: form.add_file('file', os.path.split(fullPath)[1], fileHandle) request = form.get_request() response = self.openRequest(request) print response.read() def pushZip(self,zipName): form = self.multiPartForm(self.baseURL+'/importProject') import os with open(zipName,"r") as fileHandle: form.add_file('file', os.path.split(zipName)[1], fileHandle) request = form.get_request() response = self.openRequest(request) print response.read() def fetchProject(self, projectName): response = self.getURLWithValues(self.baseURL+"/ProjectZipForProjName/"+projectName,{}) import tempfile tmpArchive = tempfile.NamedTemporaryFile(suffix='.ccp4_project.zip',delete=False) print tmpArchive.name CHUNK = 16 * 1024 while True: chunk = response.read(CHUNK) if not chunk: break tmpArchive.write(chunk) tmpArchive.close() pm = self.myStartProjectsManager() import CCP4NonGuiProjectUtils importer = CCP4NonGuiProjectUtils.CCP4NonGuiProjectUtils(tmpArchive.name) pm.db().commit() pm.db().close() return tmpArchive.name def queryDb(self, command, values=None): print 'command',command,'values',values response = self.getURLWithValues(self.baseURL+'?'+command, values) import json return json.loads(response.read()) if __name__ == '__main__': def exerciseGet(): response = djangoSession.getURLWithValues(sys.argv[1]+"?listProjects",{}) import json print json.loads(response.read()) def exerciseCreateProject(projectName): form = djangoSession.multiPartForm(sys.argv[1]+'/createProject') form.add_field('title',projectName) print form.__str__() request = form.get_request() print print 'OUTGOING DATA:' print request.get_data() response = djangoSession.openRequest(request) print print 'SERVER RESPONSE:' print response.read() baseURL = None username=None password=None projectName=None mode='Push' #First check for old syntax: a list contaiing password etc import sys, getopt dashedArgs = [arg for arg in sys.argv[1:] if arg.startswith('-')] if len(sys.argv) == 5 and len(dashedArgs) == 4: parameterNamespace = Namespace(server=sys.argv[1], username=sys.argv[2], password=sys.argv[3],depositProject=[sys.argv[4]],fetchProject=[],depositZip=[]) else: import argparse parser = argparse.ArgumentParser(description='Interact with CCP4i2 Archive.') parser.add_argument('-s','--server',help='URL of server e.g. http://myserver.local:8080/ManageCCP4i2Archive') parser.add_argument('-u','--username','--user',help='username of CCP4i2Archive e.g. djangouser. Defaults to login of current session') parser.add_argument('-p','--password','--pass',help='Password of CCP4i2Archive e.g. djangopassword') parser.add_argument('-f','--fetchProject',help='Fetch project from archive',action='append') parser.add_argument('-d','--depositProject',help='Deposit project in archive',action='append') parser.add_argument('-q','--queryDb',help='Perform database request',action='append') parser.add_argument('-z','--depositZip',help='Deposit zipped project in archive',action='append') parameterNamespace = parser.parse_args() print parameterNamespace import getpass if parameterNamespace.username is None: parameterNamespace.username = getpass.getuser() if parameterNamespace.password is None: parameterNamespace.password = getpass.getpass() ccp4i2DjangoSession = CCP4i2DjangoSession(parameterNamespace.server, parameterNamespace.username, parameterNamespace.password) if parameterNamespace.depositProject is not None: for depositProject in parameterNamespace.depositProject: ccp4i2DjangoSession.pushProject(depositProject) if parameterNamespace.fetchProject is not None: for fetchProject in parameterNamespace.fetchProject: ccp4i2DjangoSession.fetchProject(fetchProject) if parameterNamespace.depositZip is not None: for depositZip in parameterNamespace.depositZip: ccp4i2DjangoSession.pushZip(depositZip) if parameterNamespace.queryDb is not None: for queryDb in parameterNamespace.queryDb: tokens = queryDb.split('?') print 'tokens',tokens command = tokens[0] values = {} for valuePair in tokens[1:]: values[valuePair.split('=')[0]] = valuePair.split('=')[1] print ccp4i2DjangoSession.queryDb(command,values) ''' ccp4i2DjangoSession = CCP4i2DjangoSession(sys.argv[1],sys.argv[2],sys.argv[3]) #exerciseCreateProject('AAAnEmptyProject') #exerciseGet() ccp4i2DjangoSession.pushProject(sys.argv[4]) '''