| 1 |
"""
|
| 2 |
|
| 3 |
Relativity Library
|
| 4 |
|
| 5 |
Created by
|
| 6 |
Emanuel Borges
|
| 7 |
05.22.2014
|
| 8 |
|
| 9 |
This class will be my main Relativity library
|
| 10 |
Should DocumentTable and COTable be their own classes that subclass RelativityConnection?
|
| 11 |
|
| 12 |
|
| 13 |
TODO:
|
| 14 |
Make it so taht you van overide the site, username and pw. Right now it's created before you can overide.
|
| 15 |
|
| 16 |
"""
|
| 17 |
|
| 18 |
import os,time
|
| 19 |
import requests,json
|
| 20 |
|
| 21 |
class Relativity_Connection(object):
|
| 22 |
"""An Advanced Relativity Connection Library and Toolkit"""
|
| 23 |
version = '0.3.0'
|
| 24 |
|
| 25 |
|
| 26 |
## Prod
|
| 27 |
rootSite = 'https://review.xactdatadiscovery.com'
|
| 28 |
rootUserName = 'eborges@xactdatadiscovery.com'
|
| 29 |
rootPassword = ''
|
| 30 |
|
| 31 |
#### Prod
|
| 32 |
## rootSite = 'https://relativity5.advanceddiscovery.com'
|
| 33 |
## rootUserName = 'emanuel.borges@advanceddiscovery.com'
|
| 34 |
## rootPassword = 'M@y_1255'
|
| 35 |
|
| 36 |
|
| 37 |
## ## Dev
|
| 38 |
## #rootSite = 'http://dev.mcdermottdiscovery.com'
|
| 39 |
## #rootUserName = 'eborges@mwe.com'
|
| 40 |
## #rootPassword = 'MWEreldev2@'
|
| 41 |
##
|
| 42 |
## ## Prod
|
| 43 |
## rootSite = 'https://www.mcdermottdiscovery.com'
|
| 44 |
## rootUserName = 'eborges@mwe.com'
|
| 45 |
## rootPassword = 'QEst3kU2!'
|
| 46 |
|
| 47 |
def __init__(self, workSpace = None):
|
| 48 |
self.UpdateWorkspaceDict()
|
| 49 |
self.workSpace = workSpace
|
| 50 |
self.availableTables = None
|
| 51 |
|
| 52 |
def UpdateWorkspaceDict(self):
|
| 53 |
"""Updates the instance varible workSpaceDict with a {workspaceName:ID} dictionary"""
|
| 54 |
workSpaceDict = {}
|
| 55 |
r = requests.get(self.rootSite+'/Relativity.REST/Relativity/Workspace',auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword), headers={'X-CSRF-Header':""})
|
| 56 |
rawJson = r.json()
|
| 57 |
rawResults = rawJson['Results']
|
| 58 |
for i in rawResults:
|
| 59 |
workSpaceDict[i['Relativity Text Identifier']] = i['Artifact ID']
|
| 60 |
|
| 61 |
self.workSpaceDict = workSpaceDict
|
| 62 |
|
| 63 |
def GetAvilableTables(self):
|
| 64 |
"""updates the list of available tables"""
|
| 65 |
pass
|
| 66 |
|
| 67 |
def LoadWorkspace(self, workspaceName):
|
| 68 |
r = requests.get(self.rootSite+ '/Relativity.REST/Relativity/Workspace/%s'% self.workSpaceDict[workSpaceName],auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword), headers={'X-CSRF-Header':""})
|
| 69 |
self.workSpace = workspaceName
|
| 70 |
|
| 71 |
|
| 72 |
|
| 73 |
class DocumentTable(Relativity_Connection):
|
| 74 |
|
| 75 |
def __init__ (self,workSpace):
|
| 76 |
self.workSpace = workSpace
|
| 77 |
super(DocumentTable,self).__init__(workSpace)
|
| 78 |
r = requests.get(self.rootSite+ '/Relativity.REST/Relativity/Workspace/%s/Document'% self.workSpaceDict[self.workSpace],auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword), headers={'X-CSRF-Header':""})
|
| 79 |
self.availableTables = r
|
| 80 |
#return r.__dict__
|
| 81 |
|
| 82 |
def UpdateField(self,field):
|
| 83 |
d = {
|
| 84 |
"Artifact ID":1078252,"Artifact Type Name":"Tasks","Billing Time Used":5
|
| 85 |
}
|
| 86 |
|
| 87 |
r = requests.put('https://dev.mcdermottdiscovery.com/Relativity.REST/Workspace/1016359/Tasks/1078252',
|
| 88 |
auth=requests.auth.HTTPBasicAuth('eborges@mwe.com','MWEreldev2@'),
|
| 89 |
headers={'X-CSRF-Header':"",'Content-Type':"application/json; charset=utf-8"},
|
| 90 |
data=json.dumps(d))
|
| 91 |
|
| 92 |
|
| 93 |
|
| 94 |
|
| 95 |
class CustomObjectTable(Relativity_Connection):
|
| 96 |
'''You can just insts this object if you already know the workspace # and objectName. Otherwise insts a sep rl object
|
| 97 |
to gather the workspace number and objectName'''
|
| 98 |
def __init__ (self, workSpace, objectName):
|
| 99 |
self.workSpace = workSpace
|
| 100 |
super(CustomObjectTable,self).__init__(workSpace)
|
| 101 |
r = requests.get(self.rootSite+ '/Relativity.REST/Workspace/%s/%s'% (self.workSpaceDict[self.workSpace],objectName),
|
| 102 |
auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword),
|
| 103 |
headers={'X-CSRF-Header':""})
|
| 104 |
|
| 105 |
self.rlResponse = r.json()
|
| 106 |
|
| 107 |
rlResponseCode = r.status_code
|
| 108 |
print rlResponseCode
|
| 109 |
|
| 110 |
## This is the ID of the workspace, which is needed when you update or create new items
|
| 111 |
## Careful here, if the object that you are loading is a normal object the ID of the workspace will be the parent ID. BUT
|
| 112 |
## If the object is a child table, liek contacts is child table of client in MCP, the parent ID will be the parent ID
|
| 113 |
## of the last item in that database. So the parent id = client and not the workspace.
|
| 114 |
## This will only work if there are already items in this table, got to fix this...
|
| 115 |
self.parentID = self.rlResponse['Results'][0]['Parent Artifact']['Artifact ID']
|
| 116 |
|
| 117 |
## You wont get the field list until after you return 1 item from the RDO
|
| 118 |
self.fieldMatrix = None
|
| 119 |
|
| 120 |
## Count of the results both in total and on this page.
|
| 121 |
self.totalResultCount = self.UpdateTotalResultCount()
|
| 122 |
self.pageResultCount = self.UpdateCurrentResultCount()
|
| 123 |
|
| 124 |
## The matrix if returned items with artifactID:name. Cant do other way around, since name can dupe.
|
| 125 |
self.itemMatrix = self.UpdateItemMatrix()
|
| 126 |
## sets a matrix of the item and it's parent artifactID (name:parentArtifactID). when you grab from a table this has a
|
| 127 |
## foreign key to another table.
|
| 128 |
self.relatedItemMatrix = self.UpdateRelatedItemMatrix()
|
| 129 |
|
| 130 |
## The objectName stays the same, since you need a new instance for each table.
|
| 131 |
self.objectName = objectName
|
| 132 |
|
| 133 |
## Loading the item will update the field list and make that the active item
|
| 134 |
self.currentItems = None
|
| 135 |
|
| 136 |
## You may load 1 item using single item or query and get multiple items.
|
| 137 |
self.currentItemCount = None
|
| 138 |
|
| 139 |
|
| 140 |
|
| 141 |
def LoadSingleItem(self, itemID):
|
| 142 |
'''loads a requested item into the instance and updates the field matrix'''
|
| 143 |
r = requests.get(self.rootSite+ '/Relativity.REST/Workspace/%s/%s/%s'% (self.workSpaceDict[self.workSpace],self.objectName,itemID),
|
| 144 |
auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword),
|
| 145 |
headers={'X-CSRF-Header':""})
|
| 146 |
rlResponse = r.json()
|
| 147 |
rlResponseCode = r.status_code
|
| 148 |
print rlResponseCode
|
| 149 |
self.currentItems = [rlResponse,]
|
| 150 |
self.UpdateFieldMatrix(self.currentItems[0]['__Fields'])
|
| 151 |
self.currentItemCount = len(self.currentItems)
|
| 152 |
|
| 153 |
def QueryForItems(self,searchString):
|
| 154 |
'''takes a search string and brings back a matrix containing only those items that matched the search'''
|
| 155 |
payload = {
|
| 156 |
"condition":searchString,
|
| 157 |
"fields":["*"]
|
| 158 |
}
|
| 159 |
## Note that it will give you a max of 100 here because I'm setting the page size.
|
| 160 |
r = requests.post(self.rootSite+ '/Relativity.REST/Workspace/%s/%s/QueryResult?pagesize=100'% (self.workSpaceDict[self.workSpace],self.objectName),
|
| 161 |
auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword),
|
| 162 |
headers={'X-CSRF-Header':"",'Content-Type':"application/json; charset=utf-8"},
|
| 163 |
data=json.dumps(payload))
|
| 164 |
rlResponse = r.json()
|
| 165 |
rlResponseCode = r.status_code
|
| 166 |
print rlResponseCode
|
| 167 |
self.currentItems = rlResponse['Results']
|
| 168 |
#print self.currentItems
|
| 169 |
if self.currentItems:
|
| 170 |
self.UpdateFieldMatrix(self.currentItems[0]['__Fields'])
|
| 171 |
self.currentItemCount = len(self.currentItems)
|
| 172 |
|
| 173 |
return self.currentItems
|
| 174 |
|
| 175 |
def UpdateItemMatrix(self):
|
| 176 |
''' Updates the item matrix with ArtifactID:name and also puts all names in the itemlist'''
|
| 177 |
itemMatrix = {}
|
| 178 |
for i in self.rlResponse['Results']:
|
| 179 |
itemMatrix[i['Artifact ID']] = i['Relativity Text Identifier']
|
| 180 |
return itemMatrix
|
| 181 |
|
| 182 |
def UpdateRelatedItemMatrix(self):
|
| 183 |
''' Updates the related item matrix with parentArtifactID:[names] for related tables'''
|
| 184 |
relatedItemMatrix = {}
|
| 185 |
#for i in self.rlResponse['Results']:
|
| 186 |
# relatedItemMatrix[i['Relativity Text Identifier']] = i['Parent Artifact']['Artifact ID']
|
| 187 |
#return relatedItemMatrix
|
| 188 |
for i in self.rlResponse['Results']:
|
| 189 |
if i['Parent Artifact']['Artifact ID'] in relatedItemMatrix.keys():
|
| 190 |
relatedItemMatrix[i['Parent Artifact']['Artifact ID']][i['Relativity Text Identifier']] = i['Artifact ID']
|
| 191 |
else:
|
| 192 |
relatedItemMatrix[i['Parent Artifact']['Artifact ID']]= {i['Relativity Text Identifier']:i['Artifact ID']}
|
| 193 |
return relatedItemMatrix
|
| 194 |
|
| 195 |
def UpdateTotalResultCount(self):
|
| 196 |
'''Returns the current totalResultCount'''
|
| 197 |
return self.rlResponse['TotalResultCount']
|
| 198 |
|
| 199 |
def UpdateCurrentResultCount(self):
|
| 200 |
'''Returns the current result count for the page that you are on'''
|
| 201 |
return self.rlResponse['ResultCount']
|
| 202 |
|
| 203 |
def UpdateFieldMatrix(self,rawFieldDict):
|
| 204 |
'''Returns the list of fields in the table object'''
|
| 205 |
fieldMatrix = {}
|
| 206 |
for i in rawFieldDict:
|
| 207 |
fieldMatrix[i['Name']] = i['Field Type']
|
| 208 |
self.fieldMatrix = fieldMatrix
|
| 209 |
|
| 210 |
|
| 211 |
def CreateNewItem(self, itemFieldMatrix, parentID = None):
|
| 212 |
'''Allows you to create a new item in the DO, by passing a matrix of fields and their values. If the Object that you are
|
| 213 |
writing in is a normal object, let it inherite self.parentID. If your object is a child object, like contact table that is
|
| 214 |
a child of a client table, overide the parentID here with the ID that you want to write to. So the associated records ID.
|
| 215 |
i.e. mcguirewoods parent ID'''
|
| 216 |
if parentID:
|
| 217 |
pass
|
| 218 |
else:
|
| 219 |
parentID = self.parentID
|
| 220 |
#print self.objectName
|
| 221 |
itemFieldMatrix["Artifact Type Name"] = self.objectName
|
| 222 |
#print self.parentID
|
| 223 |
itemFieldMatrix["Parent Artifact"] = {"Artifact ID":parentID}
|
| 224 |
|
| 225 |
r = requests.post(self.rootSite+ '/Relativity.REST/Workspace/%s/%s'% (self.workSpaceDict[self.workSpace],self.objectName),
|
| 226 |
auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword),
|
| 227 |
headers={'X-CSRF-Header':"",'Content-Type':"application/json; charset=utf-8"},
|
| 228 |
data=json.dumps(itemFieldMatrix))
|
| 229 |
rlResponse = r.json()
|
| 230 |
rlResponseCode = r.status_code
|
| 231 |
print rlResponseCode
|
| 232 |
artID = rlResponse['Results'][0]['ArtifactID']
|
| 233 |
## This update wont work here because it's using the old response... if I need this one day, update.
|
| 234 |
#self.itemMatrix = self.UpdateItemMatrix()
|
| 235 |
## Returning the artifact id?, not sure about this. maybe should return status and allow you to get at artifact...
|
| 236 |
return artID
|
| 237 |
#d = {
|
| 238 |
# "Name":"TEST","Artifact Type Name":"Tasks","Billing Time Used":5,"Parent Artifact":{"Artifact ID":1003663}
|
| 239 |
#}
|
| 240 |
#
|
| 241 |
#r = requests.post('https://dev.mcdermottdiscovery.com/Relativity.REST/Workspace/1016359/Tasks',
|
| 242 |
# auth=requests.auth.HTTPBasicAuth('eborges@mwe.com','MWEreldev2@'),
|
| 243 |
# headers={'X-CSRF-Header':"",'Content-Type':"application/json; charset=utf-8"},
|
| 244 |
# data=json.dumps(d))
|
| 245 |
|
| 246 |
|
| 247 |
def UpdateField(self,field):
|
| 248 |
|
| 249 |
d = {
|
| 250 |
"Artifact ID":1078252,"Artifact Type Name":"Tasks","Billing Time Used":5,"Parent Artifact":{"Artifact ID":1003663}
|
| 251 |
}
|
| 252 |
|
| 253 |
r = requests.put(self.rootSite+ '/Relativity.REST/Workspace/1016359/Tasks/1078252',
|
| 254 |
auth=requests.auth.HTTPBasicAuth(self.rootUserName,self.rootPassword),
|
| 255 |
headers={'X-CSRF-Header':"",'Content-Type':"application/json; charset=utf-8"},
|
| 256 |
data=json.dumps(d))
|
| 257 |
|
| 258 |
def DownLoadFile(self, fileName):
|
| 259 |
"""Downloads a file to a epoch dir, under the temp dir, and returns a path to that."""
|
| 260 |
## Test that this is win
|
| 261 |
## Then
|
| 262 |
tempDir = os.getenv('TEMP')
|
| 263 |
epochTime = str(time.time())
|
| 264 |
fileTargetPath = os.path.join(tempDir,epochTime)
|
| 265 |
## Test that it worked.
|
| 266 |
## Then
|
| 267 |
fullFilePath = os.path.join(fileTargetPath,fileName)
|
| 268 |
return fullFilePath
|
| 269 |
|