File size: 13,169 Bytes
bf928ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#This script aims to get the data of Nanobodies and their binding antigens from SAbDab-nano database to 3 csv folders
# each folder contacin number of csvs based on fvs number 

import requests
import csv  
import pandas as pd
from bs4 import BeautifulSoup
import os
import re
from _bootstrap import DATA_DIR, use_project_root

use_project_root()

def getAllRecords():
    print("Calling All structures page ... ")
    url = requests.get(baseUrl+allRecordsEndPoint)
    htmltext = url.text
    data = pd.read_html(htmltext)[0]
    print(str(data.count()["PDB"]) + " PDBs is loaded now, lets save them....")
    return data

def writeDataToCSV(recordType, data):
    fvsNumber = int(data["basic_data"]["Number of Fvs"])
    empty_6_items = ["","","","","",""]
    empty_3_items = ["","",""]
    file_dir = str(DATA_DIR / recordType / (str(fvsNumber) + '.csv'))

    if recordType == "nano":
        headers = ['pdb', 'Hchain', 'Hchain_sequence', 'Heavy subgroup',
                'Species', 'In complex?','scFv?','Has constant domain?',
                'CDRH1', 'CDRH2','CDRH3',
                'Antigen chain_1', 'Antigen chain_2', 'Antigen chain_3',
                'antigen_type','antigen_name','Antigen species', 
                'Antigen sequence_1', 'Antigen sequence_2','Antigen sequence_3',
                'HL', 'HC1', 'HC2','LC1','LC2','dc','Method','Resolution','Has constant region'
                ]
    else:       
        headers = ['pdb', 'Hchain', 'Lchain', 'Hchain_sequence','Lchain_sequence', 'Heavy subgroup',
                'Light subgroup', 'Species', 'In complex?','scFv?','Has constant domain?',
                'CDRH1', 'CDRH2','CDRH3','CDRL1', 'CDRL2','CDRL3',
                'Antigen chain_1', 'Antigen chain_2', 'Antigen chain_3',
                'antigen_type','antigen_name','Antigen species', 
                'Antigen sequence_1', 'Antigen sequence_2','Antigen sequence_3',
                'HL', 'HC1', 'HC2','LC1','LC2','dc','Method','Resolution','Light chain type','Has constant region'
        ]

    with open(file_dir, 'a', encoding='UTF8', newline='') as b:
        wr = csv.writer(b)
        is_empty = os.stat(file_dir).st_size == 0
        if is_empty:
            wr.writerow(headers)

    for i in range(fvsNumber):
        cdrSec = data["fv_"+str(i)].get("CDR Sequences (chothia definition)")
        antigenDetailsSec = data["fv_"+str(i)].get("Antigen Details")
        orientationSec = data["fv_"+str(i)].get("Orientation Angles (from ABangle)")
        if recordType == "nano": 
            rowData = [
                data["basic_data"].get("PDB"), 
                data["fv_"+str(i)]["Fv Details"].get("Heavy chain"), 
                data["fv_"+str(i)].get("heavyChainSeq"), 
                data["fv_"+str(i)]["Fv Details"].get("Heavy subgroup"), 
                data["fv_"+str(i)]["Fv Details"].get("Species"), 
                data["fv_"+str(i)]["Fv Details"].get("In complex?"), 
                data["fv_"+str(i)]["Fv Details"].get("scFv?"), 
                data["fv_"+str(i)]["Fv Details"].get("Has constant domain?"), 
            ]
        else:
            rowData = [
                data["basic_data"].get("PDB"), 
                data["fv_"+str(i)]["Fv Details"].get("Heavy chain"), 
                data["fv_"+str(i)]["Fv Details"].get("Light chain"), 
                data["fv_"+str(i)].get("heavyChainSeq"), 
                data["fv_"+str(i)].get("lightChainSeq"), 
                data["fv_"+str(i)]["Fv Details"].get("Heavy subgroup"), 
                data["fv_"+str(i)]["Fv Details"].get("Light subgroup"), 
                data["fv_"+str(i)]["Fv Details"].get("Species"), 
                data["fv_"+str(i)]["Fv Details"].get("In complex?"), 
                data["fv_"+str(i)]["Fv Details"].get("scFv?"), 
                data["fv_"+str(i)]["Fv Details"].get("Has constant domain?"), 
            ]
        antiGenChain1 = antiGenChain2 = antiGenChain3 = ""
        antiGenSeq1 = antiGenSeq2 = antiGenSeq3 = ""
        if antigenDetailsSec != None:
            antiGenString =  data["fv_"+str(i)]["Antigen Details"]["Antigen chains"]
            if antiGenString != None:
                antiGenChains =  antiGenString.split(",")
                if int(len(antiGenChains)) >= 1:
                    antiGenChain1 = antiGenChains[0]
                if int(len(antiGenChains)) >= 2:
                    antiGenChain2 = antiGenChains[1]
                if int(len(antiGenChains)) >= 3:
                    antiGenChain3 = antiGenChains[2]  

            antiGenstring = data["fv_"+str(i)]["Antigen Details"]["Antigen sequence"]
            if antiGenstring != None:
                antiGenSeqs =  antiGenstring.split("/")
                if int(len(antiGenSeqs)) >= 1:
                    antiGenSeq1 = antiGenSeqs[0]
                if int(len(antiGenSeqs)) >= 2:
                    antiGenSeq2 = antiGenSeqs[1]
                if int(len(antiGenSeqs)) >= 3:
                    antiGenSeq3 = antiGenSeqs[2]  

        if cdrSec != None and recordType != "nano":
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH1")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH2")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH3")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRL1")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRL2")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRL3"))
        if cdrSec != None and recordType == "nano":
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH1")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH2")) 
            rowData.append(data["fv_"+str(i)]["CDR Sequences (chothia definition)"].get("CDRH3")) 

        if  cdrSec == None and recordType == "nano":
            rowData.extend(empty_3_items)
        if  cdrSec == None and recordType != "nano":
            rowData.extend(empty_6_items) 

        rowData.append(antiGenChain1)
        rowData.append(antiGenChain2)
        rowData.append(antiGenChain3)

        if antigenDetailsSec != None:
            rowData.append(data["fv_"+str(i)]["Antigen Details"].get("Antigen type"))
            rowData.append(data["fv_"+str(i)]["Antigen Details"].get("Antigen name"))
            rowData.append(data["fv_"+str(i)]["Antigen Details"].get("Antigen species"))
        else:
            rowData.extend(empty_3_items)

        rowData.append(antiGenSeq1)
        rowData.append(antiGenSeq2)
        rowData.append(antiGenSeq3)

        if orientationSec != None:
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("HL")))
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("HC1")))
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("HC2")))
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("LC1")))
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("LC2")))
            rowData.append(removeUnit(data["fv_"+str(i)]["Orientation Angles (from ABangle)"].get("dc")))
        else:
            rowData.extend(empty_6_items)  

        rowData.append(data["basic_data"].get("Method")) 

        rowData.append(removeUnit(data["basic_data"].get("Resolution"))) 
        
        if recordType != "nano":
            rowData.append(data["basic_data"].get("Light chain type")) 

        rowData.append(data["basic_data"].get("Has constant region"))
            
        # print(rowData)
        with open(file_dir, 'a', encoding='UTF8', newline='') as body:
            writer = csv.writer(body)
            writer.writerow(rowData)

def getFvData(pdbFvDiv):
    # tables with table-alignment classes are heavy and light chain seqs
    fvData = {}
    seqTableNumber = 0
    for table in pdbFvDiv.find_all('table', class_='table-results'):
        tableData = {}
        for i, row in enumerate(table.find_all('tr')):
            if i == 0:
                header = row.find('th').text.strip()
            else:
                tds = [row.findAll('td')]
                for td in tds :
                    tableData[td[0].string] = td[1].string
            fvData[header] = tableData

    for table in pdbFvDiv.find_all('table', class_='table-alignment'):
        for i, row in enumerate(table.find_all('tr')):
            if i == 0:
                # skip headers row
                continue
            else:
                if seqTableNumber == 0:
                    heavyChainSeq = row.find_all("td")
                    heavyChainSeqData = ""
                    for chain in heavyChainSeq:
                        heavyChainSeqData += chain.text
                    fvData["heavyChainSeq"] = heavyChainSeqData
                else:
                    lightChainSeq = row.find_all("td")
                    lightChainSeqData = ""
                    for chain in lightChainSeq:
                        lightChainSeqData += chain.text
                    fvData["lightChainSeq"] = lightChainSeqData
        seqTableNumber += 1
    return fvData

def getRecordType(data):
    heavyChainCounter = 0
    lightChainCounter = 0
    # if all fvs have heavy chain only and light chain is always none then it's nano
    # if all fvs have both heavy and light chains then anti
    # else combined 
    i = -1
    for fvs in data.items():
        i+=1
        if i == 0:
            #  skip basic_data
            continue
        for fv in fvs:
            if isinstance(fv, dict):
                if fv['Fv Details']['Heavy chain'] != None:
                    heavyChainCounter+=1
                if fv['Fv Details']['Light chain'] != None:
                    lightChainCounter+=1

    if heavyChainCounter == lightChainCounter == int(data["basic_data"]["Number of Fvs"]):
        global antiBodiesCounter
        antiBodiesCounter+=1
        return "anti"
    elif  heavyChainCounter == int(data["basic_data"]["Number of Fvs"]) and lightChainCounter == 0:
        global nanoBodiesCounter
        nanoBodiesCounter+=1
        return "nano"
    else:
        global combinedBodiesCounter
        combinedBodiesCounter+=1
        return "combined"

def removeUnit(value):
    if value == None:
        return value
    else:
        newValue = re.findall(r"[-+]?(?:\d*\.*\d+)", value)
        return newValue[0]

os.makedirs('./conf/data/nano')
os.makedirs('./conf/data/anti')
os.makedirs('./conf/data/combined')

baseUrl = "https://opig.stats.ox.ac.uk/"
allRecordsEndPoint = "webapps/sabdab-sabpred/sabdab/nanobodies/?all=true"
antiBodiesCounter = 0
nanoBodiesCounter = 0
combinedBodiesCounter = 0
failedBodiesCounter = 0  
recordsCount = 0
allRecords = getAllRecords()
for index, row in allRecords.iterrows():
    try:
        recordsCount+=1
        print("Record number "+str(recordsCount) + ": Calling  "+row['PDB']+" page to be saved ... ")
        pdbEndpoint = "webapps/sabdab-sabpred/sabdab/structureviewer/?pdb="+row['PDB']
        # pdbEndpoint = "webapps/sabdab-sabpred/sabdab/structureviewer/?pdb=1g9e"    
        url = requests.get(baseUrl+pdbEndpoint)
        soup = BeautifulSoup(url.content, "html.parser")
        # get basic details table 
        pdbDetailsTableDiv = soup.find(id="details")
        pdbDetailsTableRows = pdbDetailsTableDiv.find('table').find_all('tr')
        wholeData = {}
        tds = [row.findAll('td') for row in pdbDetailsTableRows]
        wholeData["basic_data"] = { td[0].string: td[1].string for td in tds }
        # loop on fvs to get its data
        for fvIndex in range(int(wholeData["basic_data"]["Number of Fvs"])):
            #collapse_0 is the first fv, collapse_1 is the second fv and so on
            collapseDivId = "collapse_"+str(fvIndex)
            pdbFvDiv = soup.find(id=collapseDivId)

            fvData = getFvData(pdbFvDiv)
            wholeData["fv_"+str(fvIndex)] = fvData

        recordType = getRecordType(wholeData)
        writeDataToCSV(recordType, wholeData)
    except Exception as e:
        with open('errors.txt', 'a') as log:
            log.write(row['PDB']+ " falied to be loaded > "+str(e))
            failedBodiesCounter+=1
            log.write('\n')
            print(row['PDB']+" Failed, Fail number: "+str(failedBodiesCounter)+ " till now")
            continue
print("I am done with follwing scraps")
print("Nanobodies counter is " + str(nanoBodiesCounter))
print("Antibodies counter is " + str(antiBodiesCounter))
print("Combined counter is " + str(combinedBodiesCounter))
print("Total fails counter is " + str(failedBodiesCounter))